@elyracode/agent-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +488 -0
- package/dist/agent-loop.d.ts +24 -0
- package/dist/agent-loop.d.ts.map +1 -0
- package/dist/agent-loop.js +479 -0
- package/dist/agent-loop.js.map +1 -0
- package/dist/agent.d.ts +118 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +402 -0
- package/dist/agent.js.map +1 -0
- package/dist/harness/agent-harness.d.ts +78 -0
- package/dist/harness/agent-harness.d.ts.map +1 -0
- package/dist/harness/agent-harness.js +602 -0
- package/dist/harness/agent-harness.js.map +1 -0
- package/dist/harness/compaction/branch-summarization.d.ts +88 -0
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/harness/compaction/branch-summarization.js +243 -0
- package/dist/harness/compaction/branch-summarization.js.map +1 -0
- package/dist/harness/compaction/compaction.d.ts +122 -0
- package/dist/harness/compaction/compaction.d.ts.map +1 -0
- package/dist/harness/compaction/compaction.js +616 -0
- package/dist/harness/compaction/compaction.js.map +1 -0
- package/dist/harness/compaction/utils.d.ts +38 -0
- package/dist/harness/compaction/utils.d.ts.map +1 -0
- package/dist/harness/compaction/utils.js +153 -0
- package/dist/harness/compaction/utils.js.map +1 -0
- package/dist/harness/env/nodejs.d.ts +44 -0
- package/dist/harness/env/nodejs.d.ts.map +1 -0
- package/dist/harness/env/nodejs.js +348 -0
- package/dist/harness/env/nodejs.js.map +1 -0
- package/dist/harness/execution-env.d.ts +4 -0
- package/dist/harness/execution-env.d.ts.map +1 -0
- package/dist/harness/execution-env.js +3 -0
- package/dist/harness/execution-env.js.map +1 -0
- package/dist/harness/messages.d.ts +51 -0
- package/dist/harness/messages.d.ts.map +1 -0
- package/dist/harness/messages.js +102 -0
- package/dist/harness/messages.js.map +1 -0
- package/dist/harness/prompt-templates.d.ts +45 -0
- package/dist/harness/prompt-templates.d.ts.map +1 -0
- package/dist/harness/prompt-templates.js +200 -0
- package/dist/harness/prompt-templates.js.map +1 -0
- package/dist/harness/session/repo/jsonl.d.ts +20 -0
- package/dist/harness/session/repo/jsonl.d.ts.map +1 -0
- package/dist/harness/session/repo/jsonl.js +92 -0
- package/dist/harness/session/repo/jsonl.js.map +1 -0
- package/dist/harness/session/repo/memory.d.ts +18 -0
- package/dist/harness/session/repo/memory.d.ts.map +1 -0
- package/dist/harness/session/repo/memory.js +42 -0
- package/dist/harness/session/repo/memory.js.map +1 -0
- package/dist/harness/session/repo/shared.d.ts +10 -0
- package/dist/harness/session/repo/shared.d.ts.map +1 -0
- package/dist/harness/session/repo/shared.js +31 -0
- package/dist/harness/session/repo/shared.js.map +1 -0
- package/dist/harness/session/session.d.ts +32 -0
- package/dist/harness/session/session.d.ts.map +1 -0
- package/dist/harness/session/session.js +196 -0
- package/dist/harness/session/session.js.map +1 -0
- package/dist/harness/session/storage/jsonl.d.ts +30 -0
- package/dist/harness/session/storage/jsonl.d.ts.map +1 -0
- package/dist/harness/session/storage/jsonl.js +170 -0
- package/dist/harness/session/storage/jsonl.js.map +1 -0
- package/dist/harness/session/storage/memory.d.ts +26 -0
- package/dist/harness/session/storage/memory.d.ts.map +1 -0
- package/dist/harness/session/storage/memory.js +90 -0
- package/dist/harness/session/storage/memory.js.map +1 -0
- package/dist/harness/skills.d.ts +41 -0
- package/dist/harness/skills.d.ts.map +1 -0
- package/dist/harness/skills.js +259 -0
- package/dist/harness/skills.js.map +1 -0
- package/dist/harness/system-prompt.d.ts +3 -0
- package/dist/harness/system-prompt.d.ts.map +1 -0
- package/dist/harness/system-prompt.js +30 -0
- package/dist/harness/system-prompt.js.map +1 -0
- package/dist/harness/types.d.ts +497 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +16 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/utils/shell-output.d.ts +14 -0
- package/dist/harness/utils/shell-output.d.ts.map +1 -0
- package/dist/harness/utils/shell-output.js +97 -0
- package/dist/harness/utils/shell-output.js.map +1 -0
- package/dist/harness/utils/truncate.d.ts +70 -0
- package/dist/harness/utils/truncate.d.ts.map +1 -0
- package/dist/harness/utils/truncate.js +205 -0
- package/dist/harness/utils/truncate.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.d.ts +69 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +278 -0
- package/dist/proxy.js.map +1 -0
- package/dist/types.d.ts +386 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-harness.js","sourceRoot":"","sources":["../../src/harness/agent-harness.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage, ImageContent, Model, UserMessage } from \"@elyracode/ai\";\nimport { Agent, type QueueMode } from \"../agent.js\";\nimport type { AgentEvent, AgentMessage, AgentTool, ThinkingLevel } from \"../types.js\";\nimport { collectEntriesForBranchSummary, generateBranchSummary } from \"./compaction/branch-summarization.js\";\nimport { compact, DEFAULT_COMPACTION_SETTINGS, prepareCompaction } from \"./compaction/compaction.js\";\nimport { formatPromptTemplateInvocation } from \"./prompt-templates.js\";\nimport { formatSkillInvocation } from \"./skills.js\";\nimport type {\n\tAbortResult,\n\tAgentHarnessEvent,\n\tAgentHarnessEventResultMap,\n\tAgentHarnessOptions,\n\tAgentHarnessOwnEvent,\n\tAgentHarnessPhase,\n\tAgentHarnessResources,\n\tAgentHarnessTurnState,\n\tExecutionEnv,\n\tNavigateTreeResult,\n\tPendingSessionWrite,\n\tPromptTemplate,\n\tSession,\n\tSkill,\n} from \"./types.js\";\n\nfunction createUserMessage(text: string, images?: ImageContent[]): UserMessage {\n\tconst content: Array<{ type: \"text\"; text: string } | ImageContent> = [{ type: \"text\", text }];\n\tif (images) content.push(...images);\n\treturn { role: \"user\", content, timestamp: Date.now() };\n}\n\nexport class AgentHarness<\n\tTSkill extends Skill = Skill,\n\tTPromptTemplate extends PromptTemplate = PromptTemplate,\n\tTTool extends AgentTool = AgentTool,\n> {\n\treadonly agent: Agent;\n\treadonly env: ExecutionEnv;\n\tprivate session: Session;\n\tprivate model: Model<any>;\n\tprivate thinkingLevel: ThinkingLevel;\n\tprivate activeToolNames: string[];\n\tprivate nextTurnQueue: AgentMessage[] = [];\n\tprivate phase: AgentHarnessPhase = \"idle\";\n\tprivate steerQueue: UserMessage[] = [];\n\tprivate followUpQueue: UserMessage[] = [];\n\tprivate pendingSessionWrites: PendingSessionWrite[] = [];\n\tprivate resources: AgentHarnessResources<TSkill, TPromptTemplate>;\n\tprivate systemPrompt: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>[\"systemPrompt\"];\n\tprivate getApiKeyAndHeaders?: AgentHarnessOptions[\"getApiKeyAndHeaders\"];\n\tprivate tools = new Map<string, TTool>();\n\tprivate listeners = new Set<\n\t\t(event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal) => Promise<void> | void\n\t>();\n\tprivate hooks = new Map<keyof AgentHarnessEventResultMap, Set<(event: any) => Promise<any> | any>>();\n\n\tconstructor(options: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>) {\n\t\tthis.agent = new Agent({\n\t\t\tinitialState: {\n\t\t\t\tmodel: options.model,\n\t\t\t\tthinkingLevel: options.thinkingLevel,\n\t\t\t\ttools: options.tools ?? [],\n\t\t\t},\n\t\t\tsteeringMode: options.steeringMode,\n\t\t\tfollowUpMode: options.followUpMode,\n\t\t});\n\t\tthis.env = options.env;\n\t\tthis.session = options.session;\n\t\tthis.resources = options.resources ?? {};\n\t\tthis.systemPrompt = options.systemPrompt;\n\t\tthis.getApiKeyAndHeaders = options.getApiKeyAndHeaders;\n\t\tfor (const tool of options.tools ?? []) {\n\t\t\tthis.tools.set(tool.name, tool);\n\t\t}\n\t\tthis.model = options.model;\n\t\tthis.thinkingLevel = options.thinkingLevel ?? this.agent.state.thinkingLevel;\n\t\tthis.activeToolNames = options.activeToolNames ?? (options.tools ?? []).map((tool) => tool.name);\n\t\tthis.agent.state.model = this.model;\n\t\tthis.agent.state.thinkingLevel = this.thinkingLevel;\n\t\tthis.agent.getApiKey = async (provider) => {\n\t\t\tconst model = this.model;\n\t\t\tif (!this.getApiKeyAndHeaders || model.provider !== provider) return undefined;\n\t\t\treturn (await this.getApiKeyAndHeaders(model))?.apiKey;\n\t\t};\n\t\tthis.agent.transformContext = async (messages) => {\n\t\t\tconst result = await this.emitHook({ type: \"context\", messages: [...messages] });\n\t\t\treturn result?.messages ?? messages;\n\t\t};\n\t\tthis.agent.beforeToolCall = async ({ toolCall, args }) => {\n\t\t\tconst result = await this.emitHook({\n\t\t\t\ttype: \"tool_call\",\n\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\ttoolName: toolCall.name,\n\t\t\t\tinput: args as Record<string, unknown>,\n\t\t\t});\n\t\t\treturn result ? { block: result.block, reason: result.reason } : undefined;\n\t\t};\n\t\tthis.agent.afterToolCall = async ({ toolCall, args, result, isError }) => {\n\t\t\tconst patch = await this.emitHook({\n\t\t\t\ttype: \"tool_result\",\n\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\ttoolName: toolCall.name,\n\t\t\t\tinput: args as Record<string, unknown>,\n\t\t\t\tcontent: result.content,\n\t\t\t\tdetails: result.details,\n\t\t\t\tisError,\n\t\t\t});\n\t\t\treturn patch\n\t\t\t\t? { content: patch.content, details: patch.details, isError: patch.isError, terminate: patch.terminate }\n\t\t\t\t: undefined;\n\t\t};\n\t\tthis.agent.onPayload = async (payload) => {\n\t\t\tconst result = await this.emitHook({ type: \"before_provider_request\", payload });\n\t\t\treturn result?.payload ?? payload;\n\t\t};\n\t\tthis.agent.onResponse = async (response) => {\n\t\t\tconst headers = { ...(response.headers as Record<string, string>) };\n\t\t\tawait this.emitOwn({ type: \"after_provider_response\", status: response.status, headers }, this.agent.signal);\n\t\t};\n\t\tthis.agent.prepareNextTurn = async () => {\n\t\t\tawait this.flushPendingSessionWrites();\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\tthis.applyTurnState(turnState);\n\t\t\treturn {\n\t\t\t\tcontext: {\n\t\t\t\t\tsystemPrompt: turnState.systemPrompt,\n\t\t\t\t\tmessages: turnState.messages.slice(),\n\t\t\t\t\ttools: turnState.activeTools.slice(),\n\t\t\t\t},\n\t\t\t\tmodel: turnState.model,\n\t\t\t\tthinkingLevel: turnState.thinkingLevel,\n\t\t\t};\n\t\t};\n\t\tthis.agent.subscribe(async (event, signal) => {\n\t\t\tawait this.handleAgentEvent(event, signal);\n\t\t});\n\t}\n\n\tprivate async emitOwn(event: AgentHarnessOwnEvent<TSkill, TPromptTemplate>, signal?: AbortSignal): Promise<void> {\n\t\tfor (const listener of this.listeners) {\n\t\t\tawait listener(event, signal);\n\t\t}\n\t}\n\n\tprivate async emitAny(event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal): Promise<void> {\n\t\tfor (const listener of this.listeners) {\n\t\t\tawait listener(event, signal);\n\t\t}\n\t}\n\n\tprivate async emitHook<TType extends keyof AgentHarnessEventResultMap>(\n\t\tevent: Extract<AgentHarnessOwnEvent, { type: TType }>,\n\t): Promise<AgentHarnessEventResultMap[TType] | undefined> {\n\t\tconst handlers = this.hooks.get(event.type as TType);\n\t\tif (!handlers || handlers.size === 0) return undefined;\n\t\tlet lastResult: AgentHarnessEventResultMap[TType] | undefined;\n\t\tfor (const handler of handlers) {\n\t\t\tconst result = await handler(event);\n\t\t\tif (result !== undefined) {\n\t\t\t\tlastResult = result;\n\t\t\t}\n\t\t}\n\t\treturn lastResult;\n\t}\n\n\tprivate async emitQueueUpdate(): Promise<void> {\n\t\tawait this.emitOwn({\n\t\t\ttype: \"queue_update\",\n\t\t\tsteer: [...this.steerQueue],\n\t\t\tfollowUp: [...this.followUpQueue],\n\t\t\tnextTurn: [...this.nextTurnQueue],\n\t\t});\n\t}\n\n\tprivate async createTurnState(): Promise<AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>> {\n\t\tconst context = await this.session.buildContext();\n\t\tconst resources = this.getResources();\n\t\tconst tools = [...this.tools.values()];\n\t\tconst activeTools = this.activeToolNames\n\t\t\t.map((name) => this.tools.get(name))\n\t\t\t.filter((tool): tool is TTool => tool !== undefined);\n\t\tlet systemPrompt = \"You are a helpful assistant.\";\n\t\tif (typeof this.systemPrompt === \"string\") {\n\t\t\tsystemPrompt = this.systemPrompt;\n\t\t} else if (this.systemPrompt) {\n\t\t\tsystemPrompt = await this.systemPrompt({\n\t\t\t\tenv: this.env,\n\t\t\t\tsession: this.session,\n\t\t\t\tmodel: this.model,\n\t\t\t\tthinkingLevel: this.thinkingLevel,\n\t\t\t\tactiveTools,\n\t\t\t\tresources,\n\t\t\t});\n\t\t}\n\t\treturn {\n\t\t\tmessages: context.messages,\n\t\t\tresources,\n\t\t\tsystemPrompt,\n\t\t\tmodel: this.model,\n\t\t\tthinkingLevel: this.thinkingLevel,\n\t\t\ttools,\n\t\t\tactiveTools,\n\t\t};\n\t}\n\n\tprivate applyTurnState(turnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>): void {\n\t\tthis.agent.state.messages = turnState.messages;\n\t\tthis.agent.state.systemPrompt = turnState.systemPrompt;\n\t\tthis.agent.state.model = turnState.model;\n\t\tthis.agent.state.thinkingLevel = turnState.thinkingLevel;\n\t\tthis.agent.state.tools = turnState.activeTools;\n\t}\n\n\tprivate validateToolNames(toolNames: string[]): void {\n\t\tconst missing = toolNames.filter((name) => !this.tools.has(name));\n\t\tif (missing.length > 0) throw new Error(`Unknown tool(s): ${missing.join(\", \")}`);\n\t}\n\n\tprivate async flushPendingSessionWrites(): Promise<void> {\n\t\tconst writes = this.pendingSessionWrites;\n\t\tthis.pendingSessionWrites = [];\n\t\tfor (const write of writes) {\n\t\t\tif (write.type === \"message\") {\n\t\t\t\tawait this.session.appendMessage(write.message);\n\t\t\t} else if (write.type === \"model_change\") {\n\t\t\t\tawait this.session.appendModelChange(write.provider, write.modelId);\n\t\t\t} else if (write.type === \"thinking_level_change\") {\n\t\t\t\tawait this.session.appendThinkingLevelChange(write.thinkingLevel);\n\t\t\t} else if (write.type === \"custom\") {\n\t\t\t\tawait this.session.appendCustomEntry(write.customType, write.data);\n\t\t\t} else if (write.type === \"custom_message\") {\n\t\t\t\tawait this.session.appendCustomMessageEntry(write.customType, write.content, write.display, write.details);\n\t\t\t} else if (write.type === \"label\") {\n\t\t\t\tawait this.session.appendLabel(write.targetId, write.label);\n\t\t\t} else if (write.type === \"session_info\") {\n\t\t\t\tawait this.session.appendSessionName(write.name ?? \"\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleAgentEvent(event: AgentEvent, signal?: AbortSignal): Promise<void> {\n\t\tawait this.emitAny(event, signal);\n\t\tif (event.type === \"message_start\" && event.message.role === \"user\") {\n\t\t\tconst steerIndex = this.steerQueue.indexOf(event.message);\n\t\t\tif (steerIndex !== -1) {\n\t\t\t\tthis.steerQueue.splice(steerIndex, 1);\n\t\t\t\tawait this.emitQueueUpdate();\n\t\t\t} else {\n\t\t\t\tconst followUpIndex = this.followUpQueue.indexOf(event.message);\n\t\t\t\tif (followUpIndex !== -1) {\n\t\t\t\t\tthis.followUpQueue.splice(followUpIndex, 1);\n\t\t\t\t\tawait this.emitQueueUpdate();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (event.type === \"message_end\") {\n\t\t\tawait this.session.appendMessage(event.message);\n\t\t}\n\t\tif (event.type === \"turn_end\") {\n\t\t\tconst hadPendingMutations = this.pendingSessionWrites.length > 0;\n\t\t\tawait this.flushPendingSessionWrites();\n\t\t\tawait this.emitOwn({\n\t\t\t\ttype: \"save_point\",\n\t\t\t\thadPendingMutations,\n\t\t\t});\n\t\t}\n\t\tif (event.type === \"agent_end\") {\n\t\t\tawait this.flushPendingSessionWrites();\n\t\t\tthis.phase = \"idle\";\n\t\t\tawait this.emitOwn({ type: \"settled\", nextTurnCount: this.nextTurnQueue.length }, signal);\n\t\t}\n\t}\n\n\tprivate async executeTurn(\n\t\tturnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>,\n\t\ttext: string,\n\t\toptions?: { images?: ImageContent[] },\n\t): Promise<AssistantMessage> {\n\t\tthis.applyTurnState(turnState);\n\t\tconst beforeLength = this.agent.state.messages.length;\n\t\tlet messages: AgentMessage[] = [createUserMessage(text, options?.images)];\n\t\tif (this.nextTurnQueue.length > 0) {\n\t\t\tmessages = [...this.nextTurnQueue, messages[0]!];\n\t\t\tthis.nextTurnQueue = [];\n\t\t\tawait this.emitQueueUpdate();\n\t\t}\n\t\tconst beforeResult = await this.emitHook({\n\t\t\ttype: \"before_agent_start\",\n\t\t\tprompt: text,\n\t\t\timages: options?.images,\n\t\t\tsystemPrompt: turnState.systemPrompt,\n\t\t\tresources: turnState.resources,\n\t\t});\n\t\tif (beforeResult?.messages) messages = [...beforeResult.messages, ...messages];\n\t\tif (beforeResult?.systemPrompt) this.agent.state.systemPrompt = beforeResult.systemPrompt;\n\t\ttry {\n\t\t\tawait this.agent.prompt(messages);\n\t\t} finally {\n\t\t\tawait this.flushPendingSessionWrites();\n\t\t}\n\t\tlet response: AssistantMessage | undefined;\n\t\tconst newMessages = this.agent.state.messages.slice(beforeLength);\n\t\tfor (let i = newMessages.length - 1; i >= 0; i--) {\n\t\t\tconst message = newMessages[i]!;\n\t\t\tif (message.role === \"assistant\") {\n\t\t\t\tresponse = message;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!response) throw new Error(\"AgentHarness prompt completed without an assistant message\");\n\t\treturn response;\n\t}\n\n\tasync prompt(text: string, options?: { images?: ImageContent[] }): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new Error(\"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\treturn await this.executeTurn(turnState, text, options);\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync skill(name: string, additionalInstructions?: string): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new Error(\"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\tconst skill = (turnState.resources.skills ?? []).find((candidate) => candidate.name === name);\n\t\t\tif (!skill) throw new Error(`Unknown skill: ${name}`);\n\t\t\treturn await this.executeTurn(turnState, formatSkillInvocation(skill, additionalInstructions));\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync promptFromTemplate(name: string, args: string[] = []): Promise<AssistantMessage> {\n\t\tif (this.phase !== \"idle\") throw new Error(\"AgentHarness is busy\");\n\t\tthis.phase = \"turn\";\n\t\ttry {\n\t\t\tconst turnState = await this.createTurnState();\n\t\t\tconst template = (turnState.resources.promptTemplates ?? []).find((candidate) => candidate.name === name);\n\t\t\tif (!template) throw new Error(`Unknown prompt template: ${name}`);\n\t\t\treturn await this.executeTurn(turnState, formatPromptTemplateInvocation(template, args));\n\t\t} catch (error) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tsteer(text: string, options?: { images?: ImageContent[] }): void {\n\t\tif (this.phase === \"idle\") throw new Error(\"Cannot steer while idle\");\n\t\tconst message = createUserMessage(text, options?.images);\n\t\tthis.steerQueue.push(message);\n\t\tthis.agent.steer(message);\n\t\tvoid this.emitQueueUpdate();\n\t}\n\n\tfollowUp(text: string, options?: { images?: ImageContent[] }): void {\n\t\tif (this.phase === \"idle\") throw new Error(\"Cannot follow up while idle\");\n\t\tconst message = createUserMessage(text, options?.images);\n\t\tthis.followUpQueue.push(message);\n\t\tthis.agent.followUp(message);\n\t\tvoid this.emitQueueUpdate();\n\t}\n\n\tnextTurn(text: string, options?: { images?: ImageContent[] }): void {\n\t\tthis.nextTurnQueue.push(createUserMessage(text, options?.images));\n\t\tvoid this.emitQueueUpdate();\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<void> {\n\t\tif (this.phase === \"idle\") {\n\t\t\tawait this.session.appendMessage(message);\n\t\t} else {\n\t\t\tthis.pendingSessionWrites.push({ type: \"message\", message });\n\t\t}\n\t}\n\n\tasync compact(\n\t\tcustomInstructions?: string,\n\t): Promise<{ summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown }> {\n\t\tif (this.phase !== \"idle\") throw new Error(\"compact() requires idle harness\");\n\t\tthis.phase = \"compaction\";\n\t\tconst model = this.model;\n\t\tif (!model) throw new Error(\"No model set for compaction\");\n\t\tconst auth = await this.getApiKeyAndHeaders?.(model);\n\t\tif (!auth) throw new Error(\"No auth available for compaction\");\n\t\tconst branchEntries = await this.session.getBranch();\n\t\tconst preparation = prepareCompaction(branchEntries, DEFAULT_COMPACTION_SETTINGS);\n\t\tif (!preparation) throw new Error(\"Nothing to compact\");\n\t\tconst hookResult = await this.emitHook({\n\t\t\ttype: \"session_before_compact\",\n\t\t\tpreparation,\n\t\t\tbranchEntries,\n\t\t\tcustomInstructions,\n\t\t\tsignal: new AbortController().signal,\n\t\t});\n\t\tif (hookResult?.cancel) {\n\t\t\tthis.phase = \"idle\";\n\t\t\tthrow new Error(\"Compaction cancelled\");\n\t\t}\n\t\tconst provided = hookResult?.compaction;\n\t\tconst result =\n\t\t\tprovided ??\n\t\t\t(await compact(\n\t\t\t\tpreparation,\n\t\t\t\tmodel,\n\t\t\t\tauth.apiKey,\n\t\t\t\tauth.headers,\n\t\t\t\tcustomInstructions,\n\t\t\t\tundefined,\n\t\t\t\tthis.thinkingLevel,\n\t\t\t));\n\t\tconst entryId = await this.session.appendCompaction(\n\t\t\tresult.summary,\n\t\t\tresult.firstKeptEntryId,\n\t\t\tresult.tokensBefore,\n\t\t\tresult.details,\n\t\t\tprovided !== undefined,\n\t\t);\n\t\tconst entry = await this.session.getEntry(entryId);\n\t\tif (entry?.type === \"compaction\") {\n\t\t\tawait this.emitOwn({ type: \"session_compact\", compactionEntry: entry, fromHook: provided !== undefined });\n\t\t}\n\t\tthis.phase = \"idle\";\n\t\treturn result;\n\t}\n\n\tasync navigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<NavigateTreeResult> {\n\t\tif (this.phase !== \"idle\") throw new Error(\"navigateTree() requires idle harness\");\n\t\tthis.phase = \"branch_summary\";\n\t\tconst oldLeafId = await this.session.getLeafId();\n\t\tif (oldLeafId === targetId) {\n\t\t\tthis.phase = \"idle\";\n\t\t\treturn { cancelled: false };\n\t\t}\n\t\tconst targetEntry = await this.session.getEntry(targetId);\n\t\tif (!targetEntry) throw new Error(`Entry ${targetId} not found`);\n\t\tconst { entries, commonAncestorId } = await collectEntriesForBranchSummary(this.session, oldLeafId, targetId);\n\t\tconst preparation = {\n\t\t\ttargetId,\n\t\t\toldLeafId,\n\t\t\tcommonAncestorId,\n\t\t\tentriesToSummarize: entries,\n\t\t\tuserWantsSummary: options?.summarize ?? false,\n\t\t\tcustomInstructions: options?.customInstructions,\n\t\t\treplaceInstructions: options?.replaceInstructions,\n\t\t\tlabel: options?.label,\n\t\t};\n\t\tconst signal = new AbortController().signal;\n\t\tconst hookResult = await this.emitHook({\n\t\t\ttype: \"session_before_tree\",\n\t\t\tpreparation,\n\t\t\tsignal,\n\t\t});\n\t\tif (hookResult?.cancel) {\n\t\t\tthis.phase = \"idle\";\n\t\t\treturn { cancelled: true };\n\t\t}\n\t\tlet summaryEntry: any | undefined;\n\t\tlet summaryText: string | undefined = hookResult?.summary?.summary;\n\t\tlet summaryDetails: unknown = hookResult?.summary?.details;\n\t\tif (!summaryText && options?.summarize && entries.length > 0) {\n\t\t\tconst model = this.model;\n\t\t\tif (!model) throw new Error(\"No model set for branch summary\");\n\t\t\tconst auth = await this.getApiKeyAndHeaders?.(model);\n\t\t\tif (!auth) throw new Error(\"No auth available for branch summary\");\n\t\t\tconst branchSummary = await generateBranchSummary(entries, {\n\t\t\t\tmodel,\n\t\t\t\tapiKey: auth.apiKey,\n\t\t\t\theaders: auth.headers,\n\t\t\t\tsignal: new AbortController().signal,\n\t\t\t\tcustomInstructions: hookResult?.customInstructions ?? options?.customInstructions,\n\t\t\t\treplaceInstructions: hookResult?.replaceInstructions ?? options?.replaceInstructions,\n\t\t\t});\n\t\t\tif (branchSummary.aborted) {\n\t\t\t\tthis.phase = \"idle\";\n\t\t\t\treturn { cancelled: true };\n\t\t\t}\n\t\t\tif (branchSummary.error) throw new Error(branchSummary.error);\n\t\t\tsummaryText = branchSummary.summary;\n\t\t\tsummaryDetails = {\n\t\t\t\treadFiles: branchSummary.readFiles ?? [],\n\t\t\t\tmodifiedFiles: branchSummary.modifiedFiles ?? [],\n\t\t\t};\n\t\t}\n\t\tlet editorText: string | undefined;\n\t\tlet newLeafId: string | null;\n\t\tif (targetEntry.type === \"message\" && targetEntry.message.role === \"user\") {\n\t\t\tnewLeafId = targetEntry.parentId;\n\t\t\tconst content = targetEntry.message.content;\n\t\t\teditorText =\n\t\t\t\ttypeof content === \"string\"\n\t\t\t\t\t? content\n\t\t\t\t\t: content\n\t\t\t\t\t\t\t.filter((c): c is { readonly type: \"text\"; readonly text: string } => c.type === \"text\")\n\t\t\t\t\t\t\t.map((c) => c.text)\n\t\t\t\t\t\t\t.join(\"\");\n\t\t} else if (targetEntry.type === \"custom_message\") {\n\t\t\tnewLeafId = targetEntry.parentId;\n\t\t\teditorText =\n\t\t\t\ttypeof targetEntry.content === \"string\"\n\t\t\t\t\t? targetEntry.content\n\t\t\t\t\t: targetEntry.content\n\t\t\t\t\t\t\t.filter((c): c is { readonly type: \"text\"; readonly text: string } => c.type === \"text\")\n\t\t\t\t\t\t\t.map((c) => c.text)\n\t\t\t\t\t\t\t.join(\"\");\n\t\t} else {\n\t\t\tnewLeafId = targetId;\n\t\t}\n\t\tconst summaryId = await this.session.moveTo(\n\t\t\tnewLeafId,\n\t\t\tsummaryText\n\t\t\t\t? {\n\t\t\t\t\t\tsummary: summaryText,\n\t\t\t\t\t\tdetails: summaryDetails,\n\t\t\t\t\t\tfromHook: hookResult?.summary !== undefined,\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t);\n\t\tif (summaryId) {\n\t\t\tsummaryEntry = await this.session.getEntry(summaryId);\n\t\t}\n\t\tawait this.emitOwn({\n\t\t\ttype: \"session_tree\",\n\t\t\tnewLeafId: await this.session.getLeafId(),\n\t\t\toldLeafId,\n\t\t\tsummaryEntry,\n\t\t\tfromHook: hookResult?.summary !== undefined,\n\t\t});\n\t\tthis.phase = \"idle\";\n\t\treturn { cancelled: false, editorText, summaryEntry };\n\t}\n\n\tasync setModel(model: Model<any>): Promise<void> {\n\t\tconst previousModel = this.model;\n\t\tthis.model = model;\n\t\tif (this.phase === \"idle\") {\n\t\t\tthis.agent.state.model = model;\n\t\t\tawait this.session.appendModelChange(model.provider, model.id);\n\t\t} else {\n\t\t\tthis.pendingSessionWrites.push({ type: \"model_change\", provider: model.provider, modelId: model.id });\n\t\t}\n\t\tawait this.emitOwn({ type: \"model_select\", model, previousModel, source: \"set\" });\n\t}\n\n\tasync setThinkingLevel(level: ThinkingLevel): Promise<void> {\n\t\tconst previousLevel = this.thinkingLevel;\n\t\tthis.thinkingLevel = level;\n\t\tif (this.phase === \"idle\") {\n\t\t\tthis.agent.state.thinkingLevel = level;\n\t\t\tawait this.session.appendThinkingLevelChange(level);\n\t\t} else {\n\t\t\tthis.pendingSessionWrites.push({ type: \"thinking_level_change\", thinkingLevel: level });\n\t\t}\n\t\tawait this.emitOwn({ type: \"thinking_level_select\", level, previousLevel });\n\t}\n\n\tasync setActiveTools(toolNames: string[]): Promise<void> {\n\t\tthis.validateToolNames(toolNames);\n\t\tthis.activeToolNames = [...toolNames];\n\t\tif (this.phase === \"idle\") {\n\t\t\tthis.agent.state.tools = this.activeToolNames.map((name) => this.tools.get(name)!);\n\t\t}\n\t}\n\n\tget steeringMode(): QueueMode {\n\t\treturn this.agent.steeringMode;\n\t}\n\n\tset steeringMode(mode: QueueMode) {\n\t\tthis.agent.steeringMode = mode;\n\t}\n\n\tget followUpMode(): QueueMode {\n\t\treturn this.agent.followUpMode;\n\t}\n\n\tset followUpMode(mode: QueueMode) {\n\t\tthis.agent.followUpMode = mode;\n\t}\n\n\tgetResources(): AgentHarnessResources<TSkill, TPromptTemplate> {\n\t\treturn {\n\t\t\tskills: this.resources.skills?.slice(),\n\t\t\tpromptTemplates: this.resources.promptTemplates?.slice(),\n\t\t};\n\t}\n\n\tasync setResources(resources: AgentHarnessResources<TSkill, TPromptTemplate>): Promise<void> {\n\t\tconst previousResources = this.getResources();\n\t\tthis.resources = {\n\t\t\tskills: resources.skills?.slice(),\n\t\t\tpromptTemplates: resources.promptTemplates?.slice(),\n\t\t};\n\t\tawait this.emitOwn({ type: \"resources_update\", resources: this.getResources(), previousResources });\n\t}\n\n\tasync setTools(tools: TTool[], activeToolNames?: string[]): Promise<void> {\n\t\tthis.tools = new Map(tools.map((tool) => [tool.name, tool]));\n\t\tif (activeToolNames) {\n\t\t\tthis.validateToolNames(activeToolNames);\n\t\t\tthis.activeToolNames = [...activeToolNames];\n\t\t} else {\n\t\t\tthis.validateToolNames(this.activeToolNames);\n\t\t}\n\t\tif (this.phase === \"idle\") {\n\t\t\tthis.agent.state.tools = this.activeToolNames.map((name) => this.tools.get(name)!);\n\t\t}\n\t}\n\n\tasync abort(): Promise<AbortResult> {\n\t\tconst clearedSteer = [...this.steerQueue];\n\t\tconst clearedFollowUp = [...this.followUpQueue];\n\t\tthis.steerQueue = [];\n\t\tthis.followUpQueue = [];\n\t\tthis.agent.clearAllQueues();\n\t\tawait this.emitQueueUpdate();\n\t\tthis.agent.abort();\n\t\tawait this.agent.waitForIdle();\n\t\tawait this.emitOwn({ type: \"abort\", clearedSteer, clearedFollowUp });\n\t\treturn { clearedSteer, clearedFollowUp };\n\t}\n\n\tasync waitForIdle(): Promise<void> {\n\t\tawait this.agent.waitForIdle();\n\t}\n\n\tsubscribe(\n\t\tlistener: (event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal) => Promise<void> | void,\n\t): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\ton<TType extends keyof AgentHarnessEventResultMap>(\n\t\ttype: TType,\n\t\thandler: (\n\t\t\tevent: Extract<AgentHarnessOwnEvent, { type: TType }>,\n\t\t) => Promise<AgentHarnessEventResultMap[TType]> | AgentHarnessEventResultMap[TType],\n\t): () => void {\n\t\tlet handlers = this.hooks.get(type);\n\t\tif (!handlers) {\n\t\t\thandlers = new Set();\n\t\t\tthis.hooks.set(type, handlers);\n\t\t}\n\t\thandlers.add(handler as any);\n\t\treturn () => handlers!.delete(handler as any);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch summarization for tree navigation.
|
|
3
|
+
*
|
|
4
|
+
* When navigating to a different point in the session tree, this generates
|
|
5
|
+
* a summary of the branch being left so context isn't lost.
|
|
6
|
+
*/
|
|
7
|
+
import type { Model } from "@elyracode/ai";
|
|
8
|
+
import type { AgentMessage } from "../../types.js";
|
|
9
|
+
import type { Session, SessionTreeEntry } from "../types.js";
|
|
10
|
+
import { type FileOperations } from "./utils.js";
|
|
11
|
+
export interface BranchSummaryResult {
|
|
12
|
+
summary?: string;
|
|
13
|
+
readFiles?: string[];
|
|
14
|
+
modifiedFiles?: string[];
|
|
15
|
+
aborted?: boolean;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Details stored in BranchSummaryEntry.details for file tracking */
|
|
19
|
+
export interface BranchSummaryDetails {
|
|
20
|
+
readFiles: string[];
|
|
21
|
+
modifiedFiles: string[];
|
|
22
|
+
}
|
|
23
|
+
export type { FileOperations } from "./utils.js";
|
|
24
|
+
export interface BranchPreparation {
|
|
25
|
+
/** Messages extracted for summarization, in chronological order */
|
|
26
|
+
messages: AgentMessage[];
|
|
27
|
+
/** File operations extracted from tool calls */
|
|
28
|
+
fileOps: FileOperations;
|
|
29
|
+
/** Total estimated tokens in messages */
|
|
30
|
+
totalTokens: number;
|
|
31
|
+
}
|
|
32
|
+
export interface CollectEntriesResult {
|
|
33
|
+
/** Entries to summarize, in chronological order */
|
|
34
|
+
entries: SessionTreeEntry[];
|
|
35
|
+
/** Common ancestor between old and new position, if any */
|
|
36
|
+
commonAncestorId: string | null;
|
|
37
|
+
}
|
|
38
|
+
export interface GenerateBranchSummaryOptions {
|
|
39
|
+
/** Model to use for summarization */
|
|
40
|
+
model: Model<any>;
|
|
41
|
+
/** API key for the model */
|
|
42
|
+
apiKey: string;
|
|
43
|
+
/** Request headers for the model */
|
|
44
|
+
headers?: Record<string, string>;
|
|
45
|
+
/** Abort signal for cancellation */
|
|
46
|
+
signal: AbortSignal;
|
|
47
|
+
/** Optional custom instructions for summarization */
|
|
48
|
+
customInstructions?: string;
|
|
49
|
+
/** If true, customInstructions replaces the default prompt instead of being appended */
|
|
50
|
+
replaceInstructions?: boolean;
|
|
51
|
+
/** Tokens reserved for prompt + LLM response (default 16384) */
|
|
52
|
+
reserveTokens?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Collect entries that should be summarized when navigating from one position to another.
|
|
56
|
+
*
|
|
57
|
+
* Walks from oldLeafId back to the common ancestor with targetId, collecting entries
|
|
58
|
+
* along the way. Does NOT stop at compaction boundaries - those are included and their
|
|
59
|
+
* summaries become context.
|
|
60
|
+
*
|
|
61
|
+
* @param session - Session manager (read-only access)
|
|
62
|
+
* @param oldLeafId - Current position (where we're navigating from)
|
|
63
|
+
* @param targetId - Target position (where we're navigating to)
|
|
64
|
+
* @returns Entries to summarize and the common ancestor
|
|
65
|
+
*/
|
|
66
|
+
export declare function collectEntriesForBranchSummary(session: Session, oldLeafId: string | null, targetId: string): Promise<CollectEntriesResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Prepare entries for summarization with token budget.
|
|
69
|
+
*
|
|
70
|
+
* Walks entries from NEWEST to OLDEST, adding messages until we hit the token budget.
|
|
71
|
+
* This ensures we keep the most recent context when the branch is too long.
|
|
72
|
+
*
|
|
73
|
+
* Also collects file operations from:
|
|
74
|
+
* - Tool calls in assistant messages
|
|
75
|
+
* - Existing branch_summary entries' details (for cumulative tracking)
|
|
76
|
+
*
|
|
77
|
+
* @param entries - Entries in chronological order
|
|
78
|
+
* @param tokenBudget - Maximum tokens to include (0 = no limit)
|
|
79
|
+
*/
|
|
80
|
+
export declare function prepareBranchEntries(entries: SessionTreeEntry[], tokenBudget?: number): BranchPreparation;
|
|
81
|
+
/**
|
|
82
|
+
* Generate a summary of abandoned branch entries.
|
|
83
|
+
*
|
|
84
|
+
* @param entries - Session entries to summarize (chronological order)
|
|
85
|
+
* @param options - Generation options
|
|
86
|
+
*/
|
|
87
|
+
export declare function generateBranchSummary(entries: SessionTreeEntry[], options: GenerateBranchSummaryOptions): Promise<BranchSummaryResult>;
|
|
88
|
+
//# sourceMappingURL=branch-summarization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-summarization.d.ts","sourceRoot":"","sources":["../../../src/harness/compaction/branch-summarization.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAgB,KAAK,EAAe,MAAM,eAAe,CAAC;AAEtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAOnD,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAIN,KAAK,cAAc,EAInB,MAAM,YAAY,CAAC;AAMpB,MAAM,WAAW,mBAAmB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IACjC,mEAAmE;IACnE,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,gDAAgD;IAChD,OAAO,EAAE,cAAc,CAAC;IACxB,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACpC,mDAAmD;IACnD,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,4BAA4B;IAC5C,qCAAqC;IACrC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oCAAoC;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,qDAAqD;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wFAAwF;IACxF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,8BAA8B,CACnD,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,CAAC,CAkC/B;AA0CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,WAAW,GAAE,MAAU,GAAG,iBAAiB,CAoD5G;AAwCD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE,gBAAgB,EAAE,EAC3B,OAAO,EAAE,4BAA4B,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAqE9B","sourcesContent":["/**\n * Branch summarization for tree navigation.\n *\n * When navigating to a different point in the session tree, this generates\n * a summary of the branch being left so context isn't lost.\n */\n\nimport type { ImageContent, Model, TextContent } from \"@elyracode/ai\";\nimport { completeSimple } from \"@elyracode/ai\";\nimport type { AgentMessage } from \"../../types.js\";\nimport {\n\tconvertToLlm,\n\tcreateBranchSummaryMessage,\n\tcreateCompactionSummaryMessage,\n\tcreateCustomMessage,\n} from \"../messages.js\";\nimport type { Session, SessionTreeEntry } from \"../types.js\";\nimport { estimateTokens } from \"./compaction.js\";\nimport {\n\tcomputeFileLists,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n\tSUMMARIZATION_SYSTEM_PROMPT,\n\tserializeConversation,\n} from \"./utils.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BranchSummaryResult {\n\tsummary?: string;\n\treadFiles?: string[];\n\tmodifiedFiles?: string[];\n\taborted?: boolean;\n\terror?: string;\n}\n\n/** Details stored in BranchSummaryEntry.details for file tracking */\nexport interface BranchSummaryDetails {\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\nexport type { FileOperations } from \"./utils.js\";\n\nexport interface BranchPreparation {\n\t/** Messages extracted for summarization, in chronological order */\n\tmessages: AgentMessage[];\n\t/** File operations extracted from tool calls */\n\tfileOps: FileOperations;\n\t/** Total estimated tokens in messages */\n\ttotalTokens: number;\n}\n\nexport interface CollectEntriesResult {\n\t/** Entries to summarize, in chronological order */\n\tentries: SessionTreeEntry[];\n\t/** Common ancestor between old and new position, if any */\n\tcommonAncestorId: string | null;\n}\n\nexport interface GenerateBranchSummaryOptions {\n\t/** Model to use for summarization */\n\tmodel: Model<any>;\n\t/** API key for the model */\n\tapiKey: string;\n\t/** Request headers for the model */\n\theaders?: Record<string, string>;\n\t/** Abort signal for cancellation */\n\tsignal: AbortSignal;\n\t/** Optional custom instructions for summarization */\n\tcustomInstructions?: string;\n\t/** If true, customInstructions replaces the default prompt instead of being appended */\n\treplaceInstructions?: boolean;\n\t/** Tokens reserved for prompt + LLM response (default 16384) */\n\treserveTokens?: number;\n}\n\n// ============================================================================\n// Entry Collection\n// ============================================================================\n\n/**\n * Collect entries that should be summarized when navigating from one position to another.\n *\n * Walks from oldLeafId back to the common ancestor with targetId, collecting entries\n * along the way. Does NOT stop at compaction boundaries - those are included and their\n * summaries become context.\n *\n * @param session - Session manager (read-only access)\n * @param oldLeafId - Current position (where we're navigating from)\n * @param targetId - Target position (where we're navigating to)\n * @returns Entries to summarize and the common ancestor\n */\nexport async function collectEntriesForBranchSummary(\n\tsession: Session,\n\toldLeafId: string | null,\n\ttargetId: string,\n): Promise<CollectEntriesResult> {\n\t// If no old position, nothing to summarize\n\tif (!oldLeafId) {\n\t\treturn { entries: [], commonAncestorId: null };\n\t}\n\n\t// Find common ancestor (deepest node that's on both paths)\n\tconst oldPath = new Set((await session.getBranch(oldLeafId)).map((e) => e.id));\n\tconst targetPath = await session.getBranch(targetId);\n\n\t// targetPath is root-first, so iterate backwards to find deepest common ancestor\n\tlet commonAncestorId: string | null = null;\n\tfor (let i = targetPath.length - 1; i >= 0; i--) {\n\t\tif (oldPath.has(targetPath[i].id)) {\n\t\t\tcommonAncestorId = targetPath[i].id;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Collect entries from old leaf back to common ancestor\n\tconst entries: SessionTreeEntry[] = [];\n\tlet current: string | null = oldLeafId;\n\n\twhile (current && current !== commonAncestorId) {\n\t\tconst entry = await session.getEntry(current);\n\t\tif (!entry) break;\n\t\tentries.push(entry as SessionTreeEntry);\n\t\tcurrent = entry.parentId;\n\t}\n\n\t// Reverse to get chronological order\n\tentries.reverse();\n\n\treturn { entries, commonAncestorId };\n}\n\n// ============================================================================\n// Entry to Message Conversion\n// ============================================================================\n\n/**\n * Extract AgentMessage from a session entry.\n * Similar to getMessageFromEntry in compaction.ts but also handles compaction entries.\n */\nfunction getMessageFromEntry(entry: SessionTreeEntry): AgentMessage | undefined {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\t// Skip tool results - context is in assistant's tool call\n\t\t\tif (entry.message.role === \"toolResult\") return undefined;\n\t\t\treturn entry.message as AgentMessage;\n\n\t\tcase \"custom_message\":\n\t\t\treturn createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content as string | (TextContent | ImageContent)[],\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t);\n\n\t\tcase \"branch_summary\":\n\t\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\n\t\tcase \"compaction\":\n\t\t\treturn createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);\n\n\t\t// These don't contribute to conversation content\n\t\tcase \"thinking_level_change\":\n\t\tcase \"model_change\":\n\t\tcase \"custom\":\n\t\tcase \"label\":\n\t\tcase \"session_info\":\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Prepare entries for summarization with token budget.\n *\n * Walks entries from NEWEST to OLDEST, adding messages until we hit the token budget.\n * This ensures we keep the most recent context when the branch is too long.\n *\n * Also collects file operations from:\n * - Tool calls in assistant messages\n * - Existing branch_summary entries' details (for cumulative tracking)\n *\n * @param entries - Entries in chronological order\n * @param tokenBudget - Maximum tokens to include (0 = no limit)\n */\nexport function prepareBranchEntries(entries: SessionTreeEntry[], tokenBudget: number = 0): BranchPreparation {\n\tconst messages: AgentMessage[] = [];\n\tconst fileOps = createFileOps();\n\tlet totalTokens = 0;\n\n\t// First pass: collect file ops from ALL entries (even if they don't fit in token budget)\n\t// This ensures we capture cumulative file tracking from nested branch summaries\n\t// Only extract from pi-generated summaries (fromHook !== true), not extension-generated ones\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"branch_summary\" && !entry.fromHook && entry.details) {\n\t\t\tconst details = entry.details as BranchSummaryDetails;\n\t\t\tif (Array.isArray(details.readFiles)) {\n\t\t\t\tfor (const f of details.readFiles) fileOps.read.add(f);\n\t\t\t}\n\t\t\tif (Array.isArray(details.modifiedFiles)) {\n\t\t\t\t// Modified files go into both edited and written for proper deduplication\n\t\t\t\tfor (const f of details.modifiedFiles) {\n\t\t\t\t\tfileOps.edited.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second pass: walk from newest to oldest, adding messages until token budget\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tconst message = getMessageFromEntry(entry);\n\t\tif (!message) continue;\n\n\t\t// Extract file ops from assistant messages (tool calls)\n\t\textractFileOpsFromMessage(message, fileOps);\n\n\t\tconst tokens = estimateTokens(message);\n\n\t\t// Check budget before adding\n\t\tif (tokenBudget > 0 && totalTokens + tokens > tokenBudget) {\n\t\t\t// If this is a summary entry, try to fit it anyway as it's important context\n\t\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t\tif (totalTokens < tokenBudget * 0.9) {\n\t\t\t\t\tmessages.unshift(message);\n\t\t\t\t\ttotalTokens += tokens;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Stop - we've hit the budget\n\t\t\tbreak;\n\t\t}\n\n\t\tmessages.unshift(message);\n\t\ttotalTokens += tokens;\n\t}\n\n\treturn { messages, fileOps, totalTokens };\n}\n\n// ============================================================================\n// Summary Generation\n// ============================================================================\n\nconst BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.\nSummary of that exploration:\n\n`;\n\nconst BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.\n\nUse this EXACT format:\n\n## Goal\n[What was the user trying to accomplish in this branch?]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Work that was started but not finished]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [What should happen next to continue this work]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Generate a summary of abandoned branch entries.\n *\n * @param entries - Session entries to summarize (chronological order)\n * @param options - Generation options\n */\nexport async function generateBranchSummary(\n\tentries: SessionTreeEntry[],\n\toptions: GenerateBranchSummaryOptions,\n): Promise<BranchSummaryResult> {\n\tconst { model, apiKey, headers, signal, customInstructions, replaceInstructions, reserveTokens = 16384 } = options;\n\n\t// Token budget = context window minus reserved space for prompt + response\n\tconst contextWindow = model.contextWindow || 128000;\n\tconst tokenBudget = contextWindow - reserveTokens;\n\n\tconst { messages, fileOps } = prepareBranchEntries(entries, tokenBudget);\n\n\tif (messages.length === 0) {\n\t\treturn { summary: \"No content to summarize\" };\n\t}\n\n\t// Transform to LLM-compatible messages, then serialize to text\n\t// Serialization prevents the model from treating it as a conversation to continue\n\tconst llmMessages = convertToLlm(messages);\n\tconst conversationText = serializeConversation(llmMessages);\n\n\t// Build prompt\n\tlet instructions: string;\n\tif (replaceInstructions && customInstructions) {\n\t\tinstructions = customInstructions;\n\t} else if (customInstructions) {\n\t\tinstructions = `${BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t} else {\n\t\tinstructions = BRANCH_SUMMARY_PROMPT;\n\t}\n\tconst promptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n${instructions}`;\n\n\tconst summarizationMessages = [\n\t\t{\n\t\t\trole: \"user\" as const,\n\t\t\tcontent: [{ type: \"text\" as const, text: promptText }],\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t];\n\n\t// Call LLM for summarization\n\tconst response = await completeSimple(\n\t\tmodel,\n\t\t{ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages },\n\t\t{ apiKey, headers, signal, maxTokens: 2048 },\n\t);\n\n\t// Check if aborted or errored\n\tif (response.stopReason === \"aborted\") {\n\t\treturn { aborted: true };\n\t}\n\tif (response.stopReason === \"error\") {\n\t\treturn { error: response.errorMessage || \"Summarization failed\" };\n\t}\n\n\tlet summary = response.content\n\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t.map((c) => c.text)\n\t\t.join(\"\\n\");\n\n\t// Prepend preamble to provide context about the branch summary\n\tsummary = BRANCH_SUMMARY_PREAMBLE + summary;\n\n\t// Compute file lists and append to summary\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tsummary += formatFileOperations(readFiles, modifiedFiles);\n\n\treturn {\n\t\tsummary: summary || \"No summary generated\",\n\t\treadFiles,\n\t\tmodifiedFiles,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch summarization for tree navigation.
|
|
3
|
+
*
|
|
4
|
+
* When navigating to a different point in the session tree, this generates
|
|
5
|
+
* a summary of the branch being left so context isn't lost.
|
|
6
|
+
*/
|
|
7
|
+
import { completeSimple } from "@elyracode/ai";
|
|
8
|
+
import { convertToLlm, createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage, } from "../messages.js";
|
|
9
|
+
import { estimateTokens } from "./compaction.js";
|
|
10
|
+
import { computeFileLists, createFileOps, extractFileOpsFromMessage, formatFileOperations, SUMMARIZATION_SYSTEM_PROMPT, serializeConversation, } from "./utils.js";
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Entry Collection
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Collect entries that should be summarized when navigating from one position to another.
|
|
16
|
+
*
|
|
17
|
+
* Walks from oldLeafId back to the common ancestor with targetId, collecting entries
|
|
18
|
+
* along the way. Does NOT stop at compaction boundaries - those are included and their
|
|
19
|
+
* summaries become context.
|
|
20
|
+
*
|
|
21
|
+
* @param session - Session manager (read-only access)
|
|
22
|
+
* @param oldLeafId - Current position (where we're navigating from)
|
|
23
|
+
* @param targetId - Target position (where we're navigating to)
|
|
24
|
+
* @returns Entries to summarize and the common ancestor
|
|
25
|
+
*/
|
|
26
|
+
export async function collectEntriesForBranchSummary(session, oldLeafId, targetId) {
|
|
27
|
+
// If no old position, nothing to summarize
|
|
28
|
+
if (!oldLeafId) {
|
|
29
|
+
return { entries: [], commonAncestorId: null };
|
|
30
|
+
}
|
|
31
|
+
// Find common ancestor (deepest node that's on both paths)
|
|
32
|
+
const oldPath = new Set((await session.getBranch(oldLeafId)).map((e) => e.id));
|
|
33
|
+
const targetPath = await session.getBranch(targetId);
|
|
34
|
+
// targetPath is root-first, so iterate backwards to find deepest common ancestor
|
|
35
|
+
let commonAncestorId = null;
|
|
36
|
+
for (let i = targetPath.length - 1; i >= 0; i--) {
|
|
37
|
+
if (oldPath.has(targetPath[i].id)) {
|
|
38
|
+
commonAncestorId = targetPath[i].id;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Collect entries from old leaf back to common ancestor
|
|
43
|
+
const entries = [];
|
|
44
|
+
let current = oldLeafId;
|
|
45
|
+
while (current && current !== commonAncestorId) {
|
|
46
|
+
const entry = await session.getEntry(current);
|
|
47
|
+
if (!entry)
|
|
48
|
+
break;
|
|
49
|
+
entries.push(entry);
|
|
50
|
+
current = entry.parentId;
|
|
51
|
+
}
|
|
52
|
+
// Reverse to get chronological order
|
|
53
|
+
entries.reverse();
|
|
54
|
+
return { entries, commonAncestorId };
|
|
55
|
+
}
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Entry to Message Conversion
|
|
58
|
+
// ============================================================================
|
|
59
|
+
/**
|
|
60
|
+
* Extract AgentMessage from a session entry.
|
|
61
|
+
* Similar to getMessageFromEntry in compaction.ts but also handles compaction entries.
|
|
62
|
+
*/
|
|
63
|
+
function getMessageFromEntry(entry) {
|
|
64
|
+
switch (entry.type) {
|
|
65
|
+
case "message":
|
|
66
|
+
// Skip tool results - context is in assistant's tool call
|
|
67
|
+
if (entry.message.role === "toolResult")
|
|
68
|
+
return undefined;
|
|
69
|
+
return entry.message;
|
|
70
|
+
case "custom_message":
|
|
71
|
+
return createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);
|
|
72
|
+
case "branch_summary":
|
|
73
|
+
return createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);
|
|
74
|
+
case "compaction":
|
|
75
|
+
return createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);
|
|
76
|
+
// These don't contribute to conversation content
|
|
77
|
+
case "thinking_level_change":
|
|
78
|
+
case "model_change":
|
|
79
|
+
case "custom":
|
|
80
|
+
case "label":
|
|
81
|
+
case "session_info":
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Prepare entries for summarization with token budget.
|
|
87
|
+
*
|
|
88
|
+
* Walks entries from NEWEST to OLDEST, adding messages until we hit the token budget.
|
|
89
|
+
* This ensures we keep the most recent context when the branch is too long.
|
|
90
|
+
*
|
|
91
|
+
* Also collects file operations from:
|
|
92
|
+
* - Tool calls in assistant messages
|
|
93
|
+
* - Existing branch_summary entries' details (for cumulative tracking)
|
|
94
|
+
*
|
|
95
|
+
* @param entries - Entries in chronological order
|
|
96
|
+
* @param tokenBudget - Maximum tokens to include (0 = no limit)
|
|
97
|
+
*/
|
|
98
|
+
export function prepareBranchEntries(entries, tokenBudget = 0) {
|
|
99
|
+
const messages = [];
|
|
100
|
+
const fileOps = createFileOps();
|
|
101
|
+
let totalTokens = 0;
|
|
102
|
+
// First pass: collect file ops from ALL entries (even if they don't fit in token budget)
|
|
103
|
+
// This ensures we capture cumulative file tracking from nested branch summaries
|
|
104
|
+
// Only extract from pi-generated summaries (fromHook !== true), not extension-generated ones
|
|
105
|
+
for (const entry of entries) {
|
|
106
|
+
if (entry.type === "branch_summary" && !entry.fromHook && entry.details) {
|
|
107
|
+
const details = entry.details;
|
|
108
|
+
if (Array.isArray(details.readFiles)) {
|
|
109
|
+
for (const f of details.readFiles)
|
|
110
|
+
fileOps.read.add(f);
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(details.modifiedFiles)) {
|
|
113
|
+
// Modified files go into both edited and written for proper deduplication
|
|
114
|
+
for (const f of details.modifiedFiles) {
|
|
115
|
+
fileOps.edited.add(f);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Second pass: walk from newest to oldest, adding messages until token budget
|
|
121
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
122
|
+
const entry = entries[i];
|
|
123
|
+
const message = getMessageFromEntry(entry);
|
|
124
|
+
if (!message)
|
|
125
|
+
continue;
|
|
126
|
+
// Extract file ops from assistant messages (tool calls)
|
|
127
|
+
extractFileOpsFromMessage(message, fileOps);
|
|
128
|
+
const tokens = estimateTokens(message);
|
|
129
|
+
// Check budget before adding
|
|
130
|
+
if (tokenBudget > 0 && totalTokens + tokens > tokenBudget) {
|
|
131
|
+
// If this is a summary entry, try to fit it anyway as it's important context
|
|
132
|
+
if (entry.type === "compaction" || entry.type === "branch_summary") {
|
|
133
|
+
if (totalTokens < tokenBudget * 0.9) {
|
|
134
|
+
messages.unshift(message);
|
|
135
|
+
totalTokens += tokens;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Stop - we've hit the budget
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
messages.unshift(message);
|
|
142
|
+
totalTokens += tokens;
|
|
143
|
+
}
|
|
144
|
+
return { messages, fileOps, totalTokens };
|
|
145
|
+
}
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// Summary Generation
|
|
148
|
+
// ============================================================================
|
|
149
|
+
const BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.
|
|
150
|
+
Summary of that exploration:
|
|
151
|
+
|
|
152
|
+
`;
|
|
153
|
+
const BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.
|
|
154
|
+
|
|
155
|
+
Use this EXACT format:
|
|
156
|
+
|
|
157
|
+
## Goal
|
|
158
|
+
[What was the user trying to accomplish in this branch?]
|
|
159
|
+
|
|
160
|
+
## Constraints & Preferences
|
|
161
|
+
- [Any constraints, preferences, or requirements mentioned]
|
|
162
|
+
- [Or "(none)" if none were mentioned]
|
|
163
|
+
|
|
164
|
+
## Progress
|
|
165
|
+
### Done
|
|
166
|
+
- [x] [Completed tasks/changes]
|
|
167
|
+
|
|
168
|
+
### In Progress
|
|
169
|
+
- [ ] [Work that was started but not finished]
|
|
170
|
+
|
|
171
|
+
### Blocked
|
|
172
|
+
- [Issues preventing progress, if any]
|
|
173
|
+
|
|
174
|
+
## Key Decisions
|
|
175
|
+
- **[Decision]**: [Brief rationale]
|
|
176
|
+
|
|
177
|
+
## Next Steps
|
|
178
|
+
1. [What should happen next to continue this work]
|
|
179
|
+
|
|
180
|
+
Keep each section concise. Preserve exact file paths, function names, and error messages.`;
|
|
181
|
+
/**
|
|
182
|
+
* Generate a summary of abandoned branch entries.
|
|
183
|
+
*
|
|
184
|
+
* @param entries - Session entries to summarize (chronological order)
|
|
185
|
+
* @param options - Generation options
|
|
186
|
+
*/
|
|
187
|
+
export async function generateBranchSummary(entries, options) {
|
|
188
|
+
const { model, apiKey, headers, signal, customInstructions, replaceInstructions, reserveTokens = 16384 } = options;
|
|
189
|
+
// Token budget = context window minus reserved space for prompt + response
|
|
190
|
+
const contextWindow = model.contextWindow || 128000;
|
|
191
|
+
const tokenBudget = contextWindow - reserveTokens;
|
|
192
|
+
const { messages, fileOps } = prepareBranchEntries(entries, tokenBudget);
|
|
193
|
+
if (messages.length === 0) {
|
|
194
|
+
return { summary: "No content to summarize" };
|
|
195
|
+
}
|
|
196
|
+
// Transform to LLM-compatible messages, then serialize to text
|
|
197
|
+
// Serialization prevents the model from treating it as a conversation to continue
|
|
198
|
+
const llmMessages = convertToLlm(messages);
|
|
199
|
+
const conversationText = serializeConversation(llmMessages);
|
|
200
|
+
// Build prompt
|
|
201
|
+
let instructions;
|
|
202
|
+
if (replaceInstructions && customInstructions) {
|
|
203
|
+
instructions = customInstructions;
|
|
204
|
+
}
|
|
205
|
+
else if (customInstructions) {
|
|
206
|
+
instructions = `${BRANCH_SUMMARY_PROMPT}\n\nAdditional focus: ${customInstructions}`;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
instructions = BRANCH_SUMMARY_PROMPT;
|
|
210
|
+
}
|
|
211
|
+
const promptText = `<conversation>\n${conversationText}\n</conversation>\n\n${instructions}`;
|
|
212
|
+
const summarizationMessages = [
|
|
213
|
+
{
|
|
214
|
+
role: "user",
|
|
215
|
+
content: [{ type: "text", text: promptText }],
|
|
216
|
+
timestamp: Date.now(),
|
|
217
|
+
},
|
|
218
|
+
];
|
|
219
|
+
// Call LLM for summarization
|
|
220
|
+
const response = await completeSimple(model, { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, { apiKey, headers, signal, maxTokens: 2048 });
|
|
221
|
+
// Check if aborted or errored
|
|
222
|
+
if (response.stopReason === "aborted") {
|
|
223
|
+
return { aborted: true };
|
|
224
|
+
}
|
|
225
|
+
if (response.stopReason === "error") {
|
|
226
|
+
return { error: response.errorMessage || "Summarization failed" };
|
|
227
|
+
}
|
|
228
|
+
let summary = response.content
|
|
229
|
+
.filter((c) => c.type === "text")
|
|
230
|
+
.map((c) => c.text)
|
|
231
|
+
.join("\n");
|
|
232
|
+
// Prepend preamble to provide context about the branch summary
|
|
233
|
+
summary = BRANCH_SUMMARY_PREAMBLE + summary;
|
|
234
|
+
// Compute file lists and append to summary
|
|
235
|
+
const { readFiles, modifiedFiles } = computeFileLists(fileOps);
|
|
236
|
+
summary += formatFileOperations(readFiles, modifiedFiles);
|
|
237
|
+
return {
|
|
238
|
+
summary: summary || "No summary generated",
|
|
239
|
+
readFiles,
|
|
240
|
+
modifiedFiles,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=branch-summarization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-summarization.js","sourceRoot":"","sources":["../../../src/harness/compaction/branch-summarization.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Branch summarization for tree navigation.\n *\n * When navigating to a different point in the session tree, this generates\n * a summary of the branch being left so context isn't lost.\n */\n\nimport type { ImageContent, Model, TextContent } from \"@elyracode/ai\";\nimport { completeSimple } from \"@elyracode/ai\";\nimport type { AgentMessage } from \"../../types.js\";\nimport {\n\tconvertToLlm,\n\tcreateBranchSummaryMessage,\n\tcreateCompactionSummaryMessage,\n\tcreateCustomMessage,\n} from \"../messages.js\";\nimport type { Session, SessionTreeEntry } from \"../types.js\";\nimport { estimateTokens } from \"./compaction.js\";\nimport {\n\tcomputeFileLists,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n\tSUMMARIZATION_SYSTEM_PROMPT,\n\tserializeConversation,\n} from \"./utils.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BranchSummaryResult {\n\tsummary?: string;\n\treadFiles?: string[];\n\tmodifiedFiles?: string[];\n\taborted?: boolean;\n\terror?: string;\n}\n\n/** Details stored in BranchSummaryEntry.details for file tracking */\nexport interface BranchSummaryDetails {\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\nexport type { FileOperations } from \"./utils.js\";\n\nexport interface BranchPreparation {\n\t/** Messages extracted for summarization, in chronological order */\n\tmessages: AgentMessage[];\n\t/** File operations extracted from tool calls */\n\tfileOps: FileOperations;\n\t/** Total estimated tokens in messages */\n\ttotalTokens: number;\n}\n\nexport interface CollectEntriesResult {\n\t/** Entries to summarize, in chronological order */\n\tentries: SessionTreeEntry[];\n\t/** Common ancestor between old and new position, if any */\n\tcommonAncestorId: string | null;\n}\n\nexport interface GenerateBranchSummaryOptions {\n\t/** Model to use for summarization */\n\tmodel: Model<any>;\n\t/** API key for the model */\n\tapiKey: string;\n\t/** Request headers for the model */\n\theaders?: Record<string, string>;\n\t/** Abort signal for cancellation */\n\tsignal: AbortSignal;\n\t/** Optional custom instructions for summarization */\n\tcustomInstructions?: string;\n\t/** If true, customInstructions replaces the default prompt instead of being appended */\n\treplaceInstructions?: boolean;\n\t/** Tokens reserved for prompt + LLM response (default 16384) */\n\treserveTokens?: number;\n}\n\n// ============================================================================\n// Entry Collection\n// ============================================================================\n\n/**\n * Collect entries that should be summarized when navigating from one position to another.\n *\n * Walks from oldLeafId back to the common ancestor with targetId, collecting entries\n * along the way. Does NOT stop at compaction boundaries - those are included and their\n * summaries become context.\n *\n * @param session - Session manager (read-only access)\n * @param oldLeafId - Current position (where we're navigating from)\n * @param targetId - Target position (where we're navigating to)\n * @returns Entries to summarize and the common ancestor\n */\nexport async function collectEntriesForBranchSummary(\n\tsession: Session,\n\toldLeafId: string | null,\n\ttargetId: string,\n): Promise<CollectEntriesResult> {\n\t// If no old position, nothing to summarize\n\tif (!oldLeafId) {\n\t\treturn { entries: [], commonAncestorId: null };\n\t}\n\n\t// Find common ancestor (deepest node that's on both paths)\n\tconst oldPath = new Set((await session.getBranch(oldLeafId)).map((e) => e.id));\n\tconst targetPath = await session.getBranch(targetId);\n\n\t// targetPath is root-first, so iterate backwards to find deepest common ancestor\n\tlet commonAncestorId: string | null = null;\n\tfor (let i = targetPath.length - 1; i >= 0; i--) {\n\t\tif (oldPath.has(targetPath[i].id)) {\n\t\t\tcommonAncestorId = targetPath[i].id;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Collect entries from old leaf back to common ancestor\n\tconst entries: SessionTreeEntry[] = [];\n\tlet current: string | null = oldLeafId;\n\n\twhile (current && current !== commonAncestorId) {\n\t\tconst entry = await session.getEntry(current);\n\t\tif (!entry) break;\n\t\tentries.push(entry as SessionTreeEntry);\n\t\tcurrent = entry.parentId;\n\t}\n\n\t// Reverse to get chronological order\n\tentries.reverse();\n\n\treturn { entries, commonAncestorId };\n}\n\n// ============================================================================\n// Entry to Message Conversion\n// ============================================================================\n\n/**\n * Extract AgentMessage from a session entry.\n * Similar to getMessageFromEntry in compaction.ts but also handles compaction entries.\n */\nfunction getMessageFromEntry(entry: SessionTreeEntry): AgentMessage | undefined {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\t// Skip tool results - context is in assistant's tool call\n\t\t\tif (entry.message.role === \"toolResult\") return undefined;\n\t\t\treturn entry.message as AgentMessage;\n\n\t\tcase \"custom_message\":\n\t\t\treturn createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content as string | (TextContent | ImageContent)[],\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t);\n\n\t\tcase \"branch_summary\":\n\t\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\n\t\tcase \"compaction\":\n\t\t\treturn createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);\n\n\t\t// These don't contribute to conversation content\n\t\tcase \"thinking_level_change\":\n\t\tcase \"model_change\":\n\t\tcase \"custom\":\n\t\tcase \"label\":\n\t\tcase \"session_info\":\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Prepare entries for summarization with token budget.\n *\n * Walks entries from NEWEST to OLDEST, adding messages until we hit the token budget.\n * This ensures we keep the most recent context when the branch is too long.\n *\n * Also collects file operations from:\n * - Tool calls in assistant messages\n * - Existing branch_summary entries' details (for cumulative tracking)\n *\n * @param entries - Entries in chronological order\n * @param tokenBudget - Maximum tokens to include (0 = no limit)\n */\nexport function prepareBranchEntries(entries: SessionTreeEntry[], tokenBudget: number = 0): BranchPreparation {\n\tconst messages: AgentMessage[] = [];\n\tconst fileOps = createFileOps();\n\tlet totalTokens = 0;\n\n\t// First pass: collect file ops from ALL entries (even if they don't fit in token budget)\n\t// This ensures we capture cumulative file tracking from nested branch summaries\n\t// Only extract from pi-generated summaries (fromHook !== true), not extension-generated ones\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"branch_summary\" && !entry.fromHook && entry.details) {\n\t\t\tconst details = entry.details as BranchSummaryDetails;\n\t\t\tif (Array.isArray(details.readFiles)) {\n\t\t\t\tfor (const f of details.readFiles) fileOps.read.add(f);\n\t\t\t}\n\t\t\tif (Array.isArray(details.modifiedFiles)) {\n\t\t\t\t// Modified files go into both edited and written for proper deduplication\n\t\t\t\tfor (const f of details.modifiedFiles) {\n\t\t\t\t\tfileOps.edited.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second pass: walk from newest to oldest, adding messages until token budget\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tconst message = getMessageFromEntry(entry);\n\t\tif (!message) continue;\n\n\t\t// Extract file ops from assistant messages (tool calls)\n\t\textractFileOpsFromMessage(message, fileOps);\n\n\t\tconst tokens = estimateTokens(message);\n\n\t\t// Check budget before adding\n\t\tif (tokenBudget > 0 && totalTokens + tokens > tokenBudget) {\n\t\t\t// If this is a summary entry, try to fit it anyway as it's important context\n\t\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t\tif (totalTokens < tokenBudget * 0.9) {\n\t\t\t\t\tmessages.unshift(message);\n\t\t\t\t\ttotalTokens += tokens;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Stop - we've hit the budget\n\t\t\tbreak;\n\t\t}\n\n\t\tmessages.unshift(message);\n\t\ttotalTokens += tokens;\n\t}\n\n\treturn { messages, fileOps, totalTokens };\n}\n\n// ============================================================================\n// Summary Generation\n// ============================================================================\n\nconst BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.\nSummary of that exploration:\n\n`;\n\nconst BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.\n\nUse this EXACT format:\n\n## Goal\n[What was the user trying to accomplish in this branch?]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Work that was started but not finished]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [What should happen next to continue this work]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Generate a summary of abandoned branch entries.\n *\n * @param entries - Session entries to summarize (chronological order)\n * @param options - Generation options\n */\nexport async function generateBranchSummary(\n\tentries: SessionTreeEntry[],\n\toptions: GenerateBranchSummaryOptions,\n): Promise<BranchSummaryResult> {\n\tconst { model, apiKey, headers, signal, customInstructions, replaceInstructions, reserveTokens = 16384 } = options;\n\n\t// Token budget = context window minus reserved space for prompt + response\n\tconst contextWindow = model.contextWindow || 128000;\n\tconst tokenBudget = contextWindow - reserveTokens;\n\n\tconst { messages, fileOps } = prepareBranchEntries(entries, tokenBudget);\n\n\tif (messages.length === 0) {\n\t\treturn { summary: \"No content to summarize\" };\n\t}\n\n\t// Transform to LLM-compatible messages, then serialize to text\n\t// Serialization prevents the model from treating it as a conversation to continue\n\tconst llmMessages = convertToLlm(messages);\n\tconst conversationText = serializeConversation(llmMessages);\n\n\t// Build prompt\n\tlet instructions: string;\n\tif (replaceInstructions && customInstructions) {\n\t\tinstructions = customInstructions;\n\t} else if (customInstructions) {\n\t\tinstructions = `${BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t} else {\n\t\tinstructions = BRANCH_SUMMARY_PROMPT;\n\t}\n\tconst promptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n${instructions}`;\n\n\tconst summarizationMessages = [\n\t\t{\n\t\t\trole: \"user\" as const,\n\t\t\tcontent: [{ type: \"text\" as const, text: promptText }],\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t];\n\n\t// Call LLM for summarization\n\tconst response = await completeSimple(\n\t\tmodel,\n\t\t{ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages },\n\t\t{ apiKey, headers, signal, maxTokens: 2048 },\n\t);\n\n\t// Check if aborted or errored\n\tif (response.stopReason === \"aborted\") {\n\t\treturn { aborted: true };\n\t}\n\tif (response.stopReason === \"error\") {\n\t\treturn { error: response.errorMessage || \"Summarization failed\" };\n\t}\n\n\tlet summary = response.content\n\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t.map((c) => c.text)\n\t\t.join(\"\\n\");\n\n\t// Prepend preamble to provide context about the branch summary\n\tsummary = BRANCH_SUMMARY_PREAMBLE + summary;\n\n\t// Compute file lists and append to summary\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tsummary += formatFileOperations(readFiles, modifiedFiles);\n\n\treturn {\n\t\tsummary: summary || \"No summary generated\",\n\t\treadFiles,\n\t\tmodifiedFiles,\n\t};\n}\n"]}
|