From a1d361b9470ffb19b1220f095e1dbd1d71716a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 26 Mar 2026 23:05:16 -0400 Subject: [PATCH] feat(06-02): wire session handoff injection into prompt builder and orchestration dispatch - Add sessionHandoff field to PromptContext interface and Session Handoff section in buildSystemPrompt - Add getSessionHandoff dependency to orchestration service, load handoff before prompt build - Update orchestration to prefer task_sessions table over legacy tasks.session_json (SESS-01) - Wire getSessionHandoff through ServiceGraph into createOrchestrationService Co-Authored-By: Claude Opus 4.6 (1M context) --- src/main/orchestration-service.ts | 22 +++++++++++++++++++++- src/main/prompt-builder.ts | 11 +++++++++++ src/main/service-graph.ts | 7 +++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/orchestration-service.ts b/src/main/orchestration-service.ts index c306542b..b6af8ef8 100644 --- a/src/main/orchestration-service.ts +++ b/src/main/orchestration-service.ts @@ -66,6 +66,7 @@ interface OrchestrationDependencies { isQuitting: () => boolean; listRuntimeSkillEntries: (companyId: string, agentId: string, connectorId: string) => Array<{ runtimeName: string; sourcePath: string }>; budgetService: ReturnType; + getSessionHandoff?: (taskId: string) => string | null; } export function createOrchestrationService({ @@ -81,6 +82,7 @@ export function createOrchestrationService({ isQuitting, listRuntimeSkillEntries, budgetService, + getSessionHandoff, }: OrchestrationDependencies) { let heartbeatTimer: ReturnType | null = null; const scheduleLastFiredMap = new Map(); @@ -229,6 +231,16 @@ export function createOrchestrationService({ createdAt: comment.createdAt, })); + // D-07: Load handoff from previous rotated session + let sessionHandoff: string | null = null; + if (getSessionHandoff) { + try { + sessionHandoff = getSessionHandoff(task.id); + } catch (err) { + logger.warn(`[run] Failed to load session handoff for task ${task.id}: ${err instanceof Error ? err.message : String(err)}`); + } + } + const systemPrompt = apiUrl ? buildTaskPrompt({ agent, apiUrl, @@ -280,6 +292,7 @@ export function createOrchestrationService({ .filter((document) => document.companyId === companyId && (document.authorAgentId === agent.id || document.reviewerAgentId === agent.id || document.status === "in_review")) .slice(0, 5), allConnectors: snapshot.connectors.map((c) => ({ id: c.id, label: c.label, status: c.status })), + sessionHandoff, }) : null; if (apiUrl) { @@ -292,7 +305,14 @@ export function createOrchestrationService({ env.AGENT_COMPANY_WAKE_REASON = trigger; } - const taskSession = db.getTaskSession(task.id); + // SESS-01: Prefer task_sessions table, fall back to legacy tasks.session_json + const activeSession = db.getActiveTaskSession(task.id); + const taskSession = activeSession + ? { + sessionParams: activeSession.sessionParamsJson ? JSON.parse(activeSession.sessionParamsJson) as Record : null, + sessionDisplayId: activeSession.sessionDisplayId, + } + : db.getTaskSession(task.id); let thinkingEffort: string | null = null; try { const meta = JSON.parse(agent.metadataJson || "{}"); diff --git a/src/main/prompt-builder.ts b/src/main/prompt-builder.ts index eea1b880..e8de727e 100644 --- a/src/main/prompt-builder.ts +++ b/src/main/prompt-builder.ts @@ -31,6 +31,8 @@ interface PromptContext { activeSprint?: SprintRecord | null; recentDocuments?: DocumentRecord[]; allConnectors?: Array<{ id: string; label: string; status: string }>; + /** D-07: Handoff markdown from a rotated session, injected as context for the agent */ + sessionHandoff?: string | null; } export function buildSystemPrompt(ctx: PromptContext): string { @@ -195,6 +197,15 @@ ${relevantProjects.map((p) => `- [${p.id}] ${p.name}: ${p.description || ""} (st `); } + // D-07: Inject handoff context from previous rotated session + if (ctx.sessionHandoff) { + sections.push(`# Session Handoff +The following context was preserved from your previous session on this task: + +${ctx.sessionHandoff} +`); + } + if (ctx.task) { sections.push(`# Current Task - ID: ${ctx.task.id} diff --git a/src/main/service-graph.ts b/src/main/service-graph.ts index 70e87bd9..cb93fbe2 100644 --- a/src/main/service-graph.ts +++ b/src/main/service-graph.ts @@ -234,6 +234,13 @@ export function createServiceGraph({ }; })(), budgetService: getOrCreateBudgetEnforcementService(), + getSessionHandoff: (taskId: string) => { + try { + return getOrCreateSessionManagementService().getHandoffContext(taskId); + } catch { + return null; + } + }, }); workerService.createWorker();