/** * context.ts — Extract parent conversation context for subagent inheritance. */ import type { ExtensionContext } from "@earendil-works/pi-coding-agent"; interface TextContentBlock { type: "text"; text?: string; } function isTextContentBlock(value: unknown): value is TextContentBlock { return ( typeof value === "object" && value !== null && "type" in value && value.type === "text" ); } /** Extract text from a message content block array. */ export function extractText(content: unknown[]): string { return content .filter(isTextContentBlock) .map((c) => c.text ?? "") .join("\n"); } /** * Build a text representation of the parent conversation context. * Used when context is "fork" to give the subagent visibility * into what has been discussed/done so far. */ export function buildParentContext(ctx: ExtensionContext): string { const entries = ctx.sessionManager.getBranch(); if (!entries || entries.length === 0) { return ""; } const parts: string[] = []; for (const entry of entries) { if (entry.type === "message") { const msg = entry.message; if (msg.role === "user") { const text = typeof msg.content === "string" ? msg.content : extractText(msg.content); if (text.trim()) { parts.push(`[User]: ${text.trim()}`); } } else if (msg.role === "assistant") { const text = extractText(msg.content); if (text.trim()) { parts.push(`[Assistant]: ${text.trim()}`); } } // Skip toolResult messages — too verbose for context } else if (entry.type === "compaction" && entry.summary) { // Include compaction summaries — they're already condensed parts.push(`[Summary]: ${entry.summary}`); } } if (parts.length === 0) { return ""; } return `# Parent Conversation Context The following is the conversation history from the parent session that spawned you. Use this context to understand what has been discussed and decided so far. ${parts.join("\n\n")} --- # Your Task (below) `; }