From 1fc8ba54f17311d54641a142400477d1d1f5414d Mon Sep 17 00:00:00 2001 From: Claudia Date: Mon, 2 Feb 2026 10:52:56 +0100 Subject: [PATCH] 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! --- src/agents/pi-embedded-runner/run/attempt.ts | 29 +++++++++++++++++++ .../pi-embedded-runner/system-prompt.ts | 3 ++ 2 files changed, 32 insertions(+) diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index 9a295c8b6..efd2427a2 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -7,6 +7,7 @@ import os from "node:os"; import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js"; import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js"; import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js"; +import { buildEventContext, formatContextForPrompt } from "../../../infra/event-context.js"; import { getMachineDisplayName } from "../../../infra/machine-name.js"; import { MAX_IMAGE_BYTES } from "../../../media/constants.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; + // 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({ workspaceDir: effectiveWorkspace, defaultThinkLevel: params.thinkLevel, @@ -366,6 +394,7 @@ export async function runEmbeddedAttempt( userTime, userTimeFormat, contextFiles, + eventContextHint, }); const systemPromptReport = buildSystemPromptReport({ source: "run", diff --git a/src/agents/pi-embedded-runner/system-prompt.ts b/src/agents/pi-embedded-runner/system-prompt.ts index 70f85a74a..451175769 100644 --- a/src/agents/pi-embedded-runner/system-prompt.ts +++ b/src/agents/pi-embedded-runner/system-prompt.ts @@ -46,6 +46,8 @@ export function buildEmbeddedSystemPrompt(params: { userTime?: string; userTimeFormat?: ResolvedTimeFormat; contextFiles?: EmbeddedContextFile[]; + /** Event-sourced context from NATS (formatted text block). */ + eventContextHint?: string; }): string { return buildAgentSystemPrompt({ workspaceDir: params.workspaceDir, @@ -71,6 +73,7 @@ export function buildEmbeddedSystemPrompt(params: { userTime: params.userTime, userTimeFormat: params.userTimeFormat, contextFiles: params.contextFiles, + eventContextHint: params.eventContextHint, }); }