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();