feat(core): Auto-load Event Context on session start (Phase 3)

- Load event context from NATS JetStream on every run
- Inject formatted context into system prompt
- Configurable via gateway.eventStore settings
- Graceful fallback if NATS unavailable

Now every session starts with recent event history!
This commit is contained in:
Claudia 2026-02-02 10:52:56 +01:00
parent aea4c823ed
commit d40865e2c2
2 changed files with 32 additions and 0 deletions

View file

@ -7,6 +7,7 @@ import os from "node:os";
import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js"; import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js";
import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js"; import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js";
import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js"; import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js";
import { buildEventContext, formatContextForPrompt } from "../../../infra/event-context.js";
import { getMachineDisplayName } from "../../../infra/machine-name.js"; import { getMachineDisplayName } from "../../../infra/machine-name.js";
import { MAX_IMAGE_BYTES } from "../../../media/constants.js"; import { MAX_IMAGE_BYTES } from "../../../media/constants.js";
import { getGlobalHookRunner } from "../../../plugins/hook-runner-global.js"; import { getGlobalHookRunner } from "../../../plugins/hook-runner-global.js";
@ -341,6 +342,33 @@ export async function runEmbeddedAttempt(
}); });
const ttsHint = params.config ? buildTtsSystemPromptHint(params.config) : undefined; const ttsHint = params.config ? buildTtsSystemPromptHint(params.config) : undefined;
// Load event-sourced context if enabled
let eventContextHint: string | undefined;
const eventStoreConfig = params.config?.gateway?.eventStore;
if (eventStoreConfig?.enabled) {
try {
const eventContext = await buildEventContext(
{
natsUrl: eventStoreConfig.natsUrl || "nats://localhost:4222",
streamName: eventStoreConfig.streamName || "openclaw-events",
subjectPrefix: eventStoreConfig.subjectPrefix || "openclaw.events",
},
{
agent: "agent",
sessionKey: params.sessionKey,
hoursBack: 2,
maxEvents: 100,
},
);
if (eventContext.eventsProcessed > 0) {
eventContextHint = formatContextForPrompt(eventContext);
log.info(`[event-context] Loaded ${eventContext.eventsProcessed} events for context`);
}
} catch (err) {
log.warn(`[event-context] Failed to load: ${err}`);
}
}
const appendPrompt = buildEmbeddedSystemPrompt({ const appendPrompt = buildEmbeddedSystemPrompt({
workspaceDir: effectiveWorkspace, workspaceDir: effectiveWorkspace,
defaultThinkLevel: params.thinkLevel, defaultThinkLevel: params.thinkLevel,
@ -366,6 +394,7 @@ export async function runEmbeddedAttempt(
userTime, userTime,
userTimeFormat, userTimeFormat,
contextFiles, contextFiles,
eventContextHint,
}); });
const systemPromptReport = buildSystemPromptReport({ const systemPromptReport = buildSystemPromptReport({
source: "run", source: "run",

View file

@ -46,6 +46,8 @@ export function buildEmbeddedSystemPrompt(params: {
userTime?: string; userTime?: string;
userTimeFormat?: ResolvedTimeFormat; userTimeFormat?: ResolvedTimeFormat;
contextFiles?: EmbeddedContextFile[]; contextFiles?: EmbeddedContextFile[];
/** Event-sourced context from NATS (formatted text block). */
eventContextHint?: string;
}): string { }): string {
return buildAgentSystemPrompt({ return buildAgentSystemPrompt({
workspaceDir: params.workspaceDir, workspaceDir: params.workspaceDir,
@ -71,6 +73,7 @@ export function buildEmbeddedSystemPrompt(params: {
userTime: params.userTime, userTime: params.userTime,
userTimeFormat: params.userTimeFormat, userTimeFormat: params.userTimeFormat,
contextFiles: params.contextFiles, contextFiles: params.contextFiles,
eventContextHint: params.eventContextHint,
}); });
} }