diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index c4c2d366..5083e60f 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -399,7 +399,7 @@ export class AgentSession { throw new Error(formatNoApiKeyFoundMessage(model.provider)); } - private async _getCompactionRequestAuth(model: Model): Promise<{ + private async _getSummarizationRequestAuth(model: Model): Promise<{ apiKey?: string; headers?: Record; env?: Record; @@ -1744,7 +1744,7 @@ export class AgentSession { throw new Error(formatNoModelSelectedMessage()); } - const { apiKey, headers, env } = await this._getCompactionRequestAuth(this.model); + const { apiKey, headers, env } = await this._getSummarizationRequestAuth(this.model); const pathEntries = this.sessionManager.getBranch(); const settings = this.settingsManager.getCompactionSettings(); @@ -2012,7 +2012,7 @@ export class AgentSession { headers = authResult.headers; env = authResult.env; } else { - ({ apiKey, headers, env } = await this._getCompactionRequestAuth(this.model)); + ({ apiKey, headers, env } = await this._getSummarizationRequestAuth(this.model)); } const pathEntries = this.sessionManager.getBranch(); @@ -2881,7 +2881,7 @@ export class AgentSession { let summaryDetails: unknown; if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) { const model = this.model!; - const { apiKey, headers, env } = await this._getRequiredRequestAuth(model); + const { apiKey, headers, env } = await this._getSummarizationRequestAuth(model); const branchSummarySettings = this.settingsManager.getBranchSummarySettings(); const result = await generateBranchSummary(entriesToSummarize, { model, diff --git a/packages/coding-agent/src/core/compaction/branch-summarization.ts b/packages/coding-agent/src/core/compaction/branch-summarization.ts index 3f557c01..a96bb202 100644 --- a/packages/coding-agent/src/core/compaction/branch-summarization.ts +++ b/packages/coding-agent/src/core/compaction/branch-summarization.ts @@ -66,7 +66,7 @@ export interface GenerateBranchSummaryOptions { /** Model to use for summarization */ model: Model; /** API key for the model */ - apiKey: string; + apiKey?: string; /** Request headers for the model */ headers?: Record; /** Provider-scoped environment values for the model */ diff --git a/packages/coding-agent/test/suite/regressions/6324-branch-summary-ambient-auth.test.ts b/packages/coding-agent/test/suite/regressions/6324-branch-summary-ambient-auth.test.ts new file mode 100644 index 00000000..121cc006 --- /dev/null +++ b/packages/coding-agent/test/suite/regressions/6324-branch-summary-ambient-auth.test.ts @@ -0,0 +1,61 @@ +import { createAssistantMessageEventStream } from "@earendil-works/pi-ai"; +import { afterEach, describe, expect, it } from "vitest"; +import { assistantMsg, userMsg } from "../../utilities.ts"; +import { createHarness, type Harness } from "../harness.ts"; + +describe("issue #6324 branch summary ambient auth", () => { + const harnesses: Harness[] = []; + + afterEach(() => { + while (harnesses.length > 0) { + harnesses.pop()?.cleanup(); + } + }); + + it("summarizes tree branches when request auth has no API key", async () => { + const harness = await createHarness({ withConfiguredAuth: false }); + harnesses.push(harness); + + let streamCallCount = 0; + harness.session.agent.streamFn = (model, _context, options) => { + streamCallCount++; + expect(options?.apiKey).toBeUndefined(); + + const stream = createAssistantMessageEventStream(); + stream.push({ + type: "done", + reason: "stop", + message: { + role: "assistant", + content: [{ type: "text", text: "branch summary text" }], + api: model.api, + provider: model.provider, + model: model.id, + usage: { + input: 1, + output: 1, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 2, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }, + stopReason: "stop", + timestamp: Date.now(), + }, + }); + return stream; + }; + + const targetId = harness.sessionManager.appendMessage(userMsg("first branch")); + harness.sessionManager.appendMessage(assistantMsg("first reply")); + harness.sessionManager.appendMessage(userMsg("abandoned branch work")); + harness.sessionManager.appendMessage(assistantMsg("abandoned reply")); + + const result = await harness.session.navigateTree(targetId, { summarize: true }); + + expect(result.cancelled).toBe(false); + expect(streamCallCount).toBe(1); + expect(result.summaryEntry?.type).toBe("branch_summary"); + expect(result.summaryEntry?.summary).toContain("branch summary text"); + }); +});