From 9ba481c76c416f5b76493198d2757c48fc384492 Mon Sep 17 00:00:00 2001 From: Altair Li <137127498+Altairpaca@users.noreply.github.com> Date: Thu, 10 Sep 2026 15:34:23 +0800 Subject: [PATCH 1/8] refactor(core): expose provider session attribution headers --- packages/coding-agent/src/core/provider-attribution.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/coding-agent/src/core/provider-attribution.ts b/packages/coding-agent/src/core/provider-attribution.ts index e7a0fbf759..df5e82538e 100644 --- a/packages/coding-agent/src/core/provider-attribution.ts +++ b/packages/coding-agent/src/core/provider-attribution.ts @@ -65,7 +65,10 @@ function getDefaultAttributionHeaders( return undefined; } -function getSessionHeaders(model: Model, sessionId: string | undefined): Record | undefined { +export function getProviderSessionHeaders( + model: Model, + sessionId: string | undefined, +): Record | undefined { if (!sessionId) return undefined; if ( model.provider !== "opencode" && @@ -84,7 +87,7 @@ export function mergeProviderAttributionHeaders( ...headerSources: Array ): ProviderHeaders | undefined { const merged: ProviderHeaders = { - ...getSessionHeaders(model, sessionId), + ...getProviderSessionHeaders(model, sessionId), ...getDefaultAttributionHeaders(model, settingsManager), }; From b69c3cdc39afbfdca05eb3fe593a92becffae4f2 Mon Sep 17 00:00:00 2001 From: Altair Li <137127498+Altairpaca@users.noreply.github.com> Date: Thu, 10 Sep 2026 15:34:51 +0800 Subject: [PATCH 2/8] fix(compaction): preserve provider session attribution --- .../builtin/compaction/speculative-summary.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts b/packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts index a69d284bd1..967411a61b 100644 --- a/packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts +++ b/packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts @@ -18,6 +18,7 @@ import { summarizationMaxDurationMs, } from "../../../compaction/stream-watchdog.ts"; import { convertToLlm } from "../../../messages.ts"; +import { getProviderSessionHeaders } from "../../../provider-attribution.ts"; import type { buildPrompt } from "./prompts.ts"; import { repairOrphanedToolResults } from "./repair-tool-pairs.ts"; import type { SpeculativeCompactionContext, SpeculativeCompactionSnapshot } from "./speculative.ts"; @@ -143,9 +144,15 @@ export async function generateSummaryMessage(options: { ), ...(options.snapshot.tools && options.snapshot.tools.length > 0 ? { tools: options.snapshot.tools } : {}), }; + const requestHeaders: ProviderHeaders = { + ...getProviderSessionHeaders(options.snapshot.model, options.context.sessionManager.getSessionId()), + ...(options.auth.headers ?? {}), + }; const headers = providerRequest - ? await providerRequest.transformHeaders(options.auth.headers ?? {}) - : options.auth.headers; + ? await providerRequest.transformHeaders(requestHeaders) + : Object.keys(requestHeaders).length > 0 + ? requestHeaders + : undefined; const responseStream = summarizationStream(options.context, options.snapshot.model, requestContext, { apiKey: options.auth.apiKey, headers, From 8c4c69a4d3b2b5b314c4268c2b1683bc673dc980 Mon Sep 17 00:00:00 2001 From: Altair Li <137127498+Altairpaca@users.noreply.github.com> Date: Thu, 10 Sep 2026 15:36:32 +0800 Subject: [PATCH 3/8] test(compaction): cover OpenCode session attribution --- ...-1523-opencode-session-attribution.test.ts | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 packages/coding-agent/test/compaction/issue-1523-opencode-session-attribution.test.ts diff --git a/packages/coding-agent/test/compaction/issue-1523-opencode-session-attribution.test.ts b/packages/coding-agent/test/compaction/issue-1523-opencode-session-attribution.test.ts new file mode 100644 index 0000000000..8d23d306a8 --- /dev/null +++ b/packages/coding-agent/test/compaction/issue-1523-opencode-session-attribution.test.ts @@ -0,0 +1,117 @@ +import { + createAssistantMessageEventStream, + fauxAssistantMessage, + type Model, + type ProviderHeaders, +} from "@earendil-works/pi-ai"; +import { describe, expect, it } from "vitest"; +import { generateSummaryMessage } from "../../src/core/extensions/builtin/compaction/speculative-summary.ts"; +import type { + SpeculativeCompactionContext, + SpeculativeCompactionSnapshot, +} from "../../src/core/extensions/builtin/compaction/speculative.ts"; +import type { ModelRegistry } from "../../src/core/model-registry.ts"; +import { SessionManager } from "../../src/core/session-manager.ts"; + +function createModel(provider = "opencode-go", baseUrl = "https://opencode.ai/zen/go/v1/messages"): Model { + return { + id: "test-model", + name: "Test Model", + api: "openai-completions", + provider, + baseUrl, + reasoning: false, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128_000, + maxTokens: 4_096, + }; +} + +function completedStream(model: Model) { + const stream = createAssistantMessageEventStream(); + stream.end({ + ...fauxAssistantMessage("summary"), + api: model.api, + provider: model.provider, + model: model.id, + }); + return stream; +} + +async function captureSummaryHeaders(options: { + model: Model; + transformHeaders?: (headers: ProviderHeaders) => ProviderHeaders | Promise; +}): Promise { + let capturedHeaders: ProviderHeaders | undefined; + const sessionManager = SessionManager.inMemory(); + sessionManager.newSession({ id: "session-1523" }); + + const modelRuntime = { + stream: (model: Model, _context: unknown, streamOptions?: { headers?: ProviderHeaders }) => { + capturedHeaders = streamOptions?.headers; + return completedStream(model); + }, + }; + const context: SpeculativeCompactionContext = { + model: options.model, + modelRegistry: { modelRuntime } as unknown as ModelRegistry, + sessionManager, + getContextUsage: () => undefined, + getMessageRevision: () => 1, + prepareProviderRequest: async (messages) => ({ + messages, + transformPayload: async (payload) => payload, + transformHeaders: async (headers) => + options.transformHeaders ? await options.transformHeaders(headers) : headers, + }), + applyCompaction: async () => ({ applied: true, reason: "ok" }), + }; + const snapshot = { + model: options.model, + contextWindow: options.model.contextWindow, + systemPrompt: "system", + tools: [], + } as unknown as SpeculativeCompactionSnapshot; + + await generateSummaryMessage({ + context, + messages: [{ role: "user", content: [{ type: "text", text: "history" }], timestamp: 1 }], + prompt: { system: "system", user: "summarize" } as never, + snapshot, + auth: {}, + }); + return capturedHeaders; +} + +describe("issue #1523: compaction provider session attribution", () => { + it("sends the OpenCode session headers on builtin summarization", async () => { + const headers = await captureSummaryHeaders({ model: createModel() }); + + expect(headers?.["x-opencode-session"]).toBe("session-1523"); + expect(headers?.["x-opencode-client"]).toBe("pi"); + }); + + it("keeps before-provider header transforms authoritative", async () => { + const headers = await captureSummaryHeaders({ + model: createModel(), + transformHeaders: (input) => ({ + ...input, + "x-opencode-session": "extension-session", + "x-opencode-client": "extension-client", + }), + }); + + expect(headers?.["x-opencode-session"]).toBe("extension-session"); + expect(headers?.["x-opencode-client"]).toBe("extension-client"); + }); + + it("does not add OpenCode session headers for unrelated providers", async () => { + const headers = await captureSummaryHeaders({ + model: createModel("example", "https://example.test/v1"), + }); + + expect(headers?.["x-opencode-session"]).toBeUndefined(); + expect(headers?.["x-opencode-client"]).toBeUndefined(); + }); +}); From 5a5a09f3e43c16bcc900232eca68da77774a44c6 Mon Sep 17 00:00:00 2001 From: Altair Li <137127498+Altairpaca@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:23:55 +0800 Subject: [PATCH 4/8] refactor(coding-agent): name OpenCode session attribution explicitly --- packages/coding-agent/src/core/provider-attribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/coding-agent/src/core/provider-attribution.ts b/packages/coding-agent/src/core/provider-attribution.ts index df5e82538e..26bcf45d89 100644 --- a/packages/coding-agent/src/core/provider-attribution.ts +++ b/packages/coding-agent/src/core/provider-attribution.ts @@ -65,7 +65,7 @@ function getDefaultAttributionHeaders( return undefined; } -export function getProviderSessionHeaders( +export function getOpenCodeSessionHeaders( model: Model, sessionId: string | undefined, ): Record | undefined { @@ -87,7 +87,7 @@ export function mergeProviderAttributionHeaders( ...headerSources: Array ): ProviderHeaders | undefined { const merged: ProviderHeaders = { - ...getProviderSessionHeaders(model, sessionId), + ...getOpenCodeSessionHeaders(model, sessionId), ...getDefaultAttributionHeaders(model, settingsManager), }; From b9073cc2dcbce61ec6786057258e58bd39cf8b19 Mon Sep 17 00:00:00 2001 From: Altair Li <137127498+Altairpaca@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:24:23 +0800 Subject: [PATCH 5/8] fix(compaction): document OpenCode attribution boundary --- .../extensions/builtin/compaction/speculative-summary.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts b/packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts index 967411a61b..8ad36a62c5 100644 --- a/packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts +++ b/packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts @@ -18,7 +18,7 @@ import { summarizationMaxDurationMs, } from "../../../compaction/stream-watchdog.ts"; import { convertToLlm } from "../../../messages.ts"; -import { getProviderSessionHeaders } from "../../../provider-attribution.ts"; +import { getOpenCodeSessionHeaders } from "../../../provider-attribution.ts"; import type { buildPrompt } from "./prompts.ts"; import { repairOrphanedToolResults } from "./repair-tool-pairs.ts"; import type { SpeculativeCompactionContext, SpeculativeCompactionSnapshot } from "./speculative.ts"; @@ -144,8 +144,11 @@ export async function generateSummaryMessage(options: { ), ...(options.snapshot.tools && options.snapshot.tools.length > 0 ? { tools: options.snapshot.tools } : {}), }; + // Builtin compaction bypasses AgentSession's sdk.ts stream wrapper. Seed only + // the OpenCode routing defaults here, then keep resolved auth/configured + // headers and the provider-request transform authoritative as on normal turns. const requestHeaders: ProviderHeaders = { - ...getProviderSessionHeaders(options.snapshot.model, options.context.sessionManager.getSessionId()), + ...getOpenCodeSessionHeaders(options.snapshot.model, options.context.sessionManager.getSessionId()), ...(options.auth.headers ?? {}), }; const headers = providerRequest From 0d5b8cb0f1355730e0551a4b94eb085d2b881163 Mon Sep 17 00:00:00 2001 From: Altair Li <137127498+Altairpaca@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:25:05 +0800 Subject: [PATCH 6/8] test(compaction): harden OpenCode session attribution regression --- ...-1523-opencode-session-attribution.test.ts | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 packages/coding-agent/test/suite/regressions/issue-1523-opencode-session-attribution.test.ts diff --git a/packages/coding-agent/test/suite/regressions/issue-1523-opencode-session-attribution.test.ts b/packages/coding-agent/test/suite/regressions/issue-1523-opencode-session-attribution.test.ts new file mode 100644 index 0000000000..9c58e10a0f --- /dev/null +++ b/packages/coding-agent/test/suite/regressions/issue-1523-opencode-session-attribution.test.ts @@ -0,0 +1,148 @@ +import { + createAssistantMessageEventStream, + fauxAssistantMessage, + type Model, + type ProviderHeaders, +} from "@earendil-works/pi-ai"; +import { describe, expect, it } from "vitest"; +import { generateSummaryMessage } from "../../../src/core/extensions/builtin/compaction/speculative-summary.ts"; +import type { + SpeculativeCompactionContext, + SpeculativeCompactionSnapshot, +} from "../../../src/core/extensions/builtin/compaction/speculative.ts"; +import type { ModelRegistry } from "../../../src/core/model-registry.ts"; +import { SessionManager } from "../../../src/core/session-manager.ts"; + +type TestModel = Model<"openai-completions">; + +function createModel(provider = "opencode-go", baseUrl = "https://opencode.ai/zen/go/v1/messages"): TestModel { + return { + id: "test-model", + name: "Test Model", + api: "openai-completions", + provider, + baseUrl, + reasoning: false, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128_000, + maxTokens: 4_096, + }; +} + +function completedStream(model: TestModel) { + const stream = createAssistantMessageEventStream(); + stream.end({ + ...fauxAssistantMessage("summary"), + api: model.api, + provider: model.provider, + model: model.id, + }); + return stream; +} + +async function captureSummaryHeaders(options: { + model: TestModel; + authHeaders?: ProviderHeaders; + transformHeaders?: (headers: ProviderHeaders) => ProviderHeaders | Promise; +}): Promise { + let capturedHeaders: ProviderHeaders | undefined; + const sessionManager = SessionManager.inMemory(); + sessionManager.newSession({ id: "session-1523" }); + + const modelRuntime = { + stream: (model: TestModel, _context: unknown, streamOptions?: { headers?: ProviderHeaders }) => { + capturedHeaders = streamOptions?.headers; + return completedStream(model); + }, + }; + const context: SpeculativeCompactionContext = { + model: options.model, + modelRegistry: { modelRuntime } as unknown as ModelRegistry, + sessionManager, + getContextUsage: () => undefined, + getMessageRevision: () => 1, + prepareProviderRequest: async (messages) => ({ + messages, + transformPayload: async (payload) => payload, + transformHeaders: async (headers) => + options.transformHeaders ? await options.transformHeaders(headers) : headers, + }), + applyCompaction: async () => ({ applied: true, reason: "ok" }), + }; + const snapshot = { + model: options.model, + contextWindow: options.model.contextWindow, + systemPrompt: "system", + tools: [], + } as unknown as SpeculativeCompactionSnapshot; + + await generateSummaryMessage({ + context, + messages: [{ role: "user", content: [{ type: "text", text: "history" }], timestamp: 1 }], + prompt: { system: "system", user: "summarize" } as never, + snapshot, + auth: { headers: options.authHeaders }, + }); + return capturedHeaders; +} + +// Regression for #1523: builtin compaction bypasses sdk.ts's normal AgentSession +// provider-attribution wrapper but must preserve the same OpenCode routing semantics. +describe("issue #1523: compaction provider session attribution", () => { + it("sends the OpenCode session headers on builtin summarization", async () => { + const headers = await captureSummaryHeaders({ model: createModel() }); + + expect(headers?.["x-opencode-session"]).toBe("session-1523"); + expect(headers?.["x-opencode-client"]).toBe("pi"); + }); + + it("recognizes OpenCode routing by opencode.ai host", async () => { + const headers = await captureSummaryHeaders({ + model: createModel("custom-openai", "https://opencode.ai/zen/go/v1/messages"), + }); + + expect(headers?.["x-opencode-session"]).toBe("session-1523"); + expect(headers?.["x-opencode-client"]).toBe("pi"); + }); + + it("keeps resolved auth or configured headers above generated session defaults", async () => { + const headers = await captureSummaryHeaders({ + model: createModel(), + authHeaders: { + "x-opencode-session": "configured-session", + "x-opencode-client": "configured-client", + }, + }); + + expect(headers?.["x-opencode-session"]).toBe("configured-session"); + expect(headers?.["x-opencode-client"]).toBe("configured-client"); + }); + + it("keeps before-provider header transforms authoritative", async () => { + const headers = await captureSummaryHeaders({ + model: createModel(), + authHeaders: { + "x-opencode-session": "configured-session", + "x-opencode-client": "configured-client", + }, + transformHeaders: (input) => ({ + ...input, + "x-opencode-session": "extension-session", + "x-opencode-client": "extension-client", + }), + }); + + expect(headers?.["x-opencode-session"]).toBe("extension-session"); + expect(headers?.["x-opencode-client"]).toBe("extension-client"); + }); + + it("does not add OpenCode session headers for unrelated providers", async () => { + const headers = await captureSummaryHeaders({ + model: createModel("example", "https://example.test/v1"), + }); + + expect(headers?.["x-opencode-session"]).toBeUndefined(); + expect(headers?.["x-opencode-client"]).toBeUndefined(); + }); +}); From 573c100df623db35bbf6d1ce784d86f111e86be9 Mon Sep 17 00:00:00 2001 From: Altair Li <137127498+Altairpaca@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:25:14 +0800 Subject: [PATCH 7/8] test(compaction): move issue 1523 regression to suite --- ...-1523-opencode-session-attribution.test.ts | 117 ------------------ 1 file changed, 117 deletions(-) delete mode 100644 packages/coding-agent/test/compaction/issue-1523-opencode-session-attribution.test.ts diff --git a/packages/coding-agent/test/compaction/issue-1523-opencode-session-attribution.test.ts b/packages/coding-agent/test/compaction/issue-1523-opencode-session-attribution.test.ts deleted file mode 100644 index 8d23d306a8..0000000000 --- a/packages/coding-agent/test/compaction/issue-1523-opencode-session-attribution.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { - createAssistantMessageEventStream, - fauxAssistantMessage, - type Model, - type ProviderHeaders, -} from "@earendil-works/pi-ai"; -import { describe, expect, it } from "vitest"; -import { generateSummaryMessage } from "../../src/core/extensions/builtin/compaction/speculative-summary.ts"; -import type { - SpeculativeCompactionContext, - SpeculativeCompactionSnapshot, -} from "../../src/core/extensions/builtin/compaction/speculative.ts"; -import type { ModelRegistry } from "../../src/core/model-registry.ts"; -import { SessionManager } from "../../src/core/session-manager.ts"; - -function createModel(provider = "opencode-go", baseUrl = "https://opencode.ai/zen/go/v1/messages"): Model { - return { - id: "test-model", - name: "Test Model", - api: "openai-completions", - provider, - baseUrl, - reasoning: false, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 128_000, - maxTokens: 4_096, - }; -} - -function completedStream(model: Model) { - const stream = createAssistantMessageEventStream(); - stream.end({ - ...fauxAssistantMessage("summary"), - api: model.api, - provider: model.provider, - model: model.id, - }); - return stream; -} - -async function captureSummaryHeaders(options: { - model: Model; - transformHeaders?: (headers: ProviderHeaders) => ProviderHeaders | Promise; -}): Promise { - let capturedHeaders: ProviderHeaders | undefined; - const sessionManager = SessionManager.inMemory(); - sessionManager.newSession({ id: "session-1523" }); - - const modelRuntime = { - stream: (model: Model, _context: unknown, streamOptions?: { headers?: ProviderHeaders }) => { - capturedHeaders = streamOptions?.headers; - return completedStream(model); - }, - }; - const context: SpeculativeCompactionContext = { - model: options.model, - modelRegistry: { modelRuntime } as unknown as ModelRegistry, - sessionManager, - getContextUsage: () => undefined, - getMessageRevision: () => 1, - prepareProviderRequest: async (messages) => ({ - messages, - transformPayload: async (payload) => payload, - transformHeaders: async (headers) => - options.transformHeaders ? await options.transformHeaders(headers) : headers, - }), - applyCompaction: async () => ({ applied: true, reason: "ok" }), - }; - const snapshot = { - model: options.model, - contextWindow: options.model.contextWindow, - systemPrompt: "system", - tools: [], - } as unknown as SpeculativeCompactionSnapshot; - - await generateSummaryMessage({ - context, - messages: [{ role: "user", content: [{ type: "text", text: "history" }], timestamp: 1 }], - prompt: { system: "system", user: "summarize" } as never, - snapshot, - auth: {}, - }); - return capturedHeaders; -} - -describe("issue #1523: compaction provider session attribution", () => { - it("sends the OpenCode session headers on builtin summarization", async () => { - const headers = await captureSummaryHeaders({ model: createModel() }); - - expect(headers?.["x-opencode-session"]).toBe("session-1523"); - expect(headers?.["x-opencode-client"]).toBe("pi"); - }); - - it("keeps before-provider header transforms authoritative", async () => { - const headers = await captureSummaryHeaders({ - model: createModel(), - transformHeaders: (input) => ({ - ...input, - "x-opencode-session": "extension-session", - "x-opencode-client": "extension-client", - }), - }); - - expect(headers?.["x-opencode-session"]).toBe("extension-session"); - expect(headers?.["x-opencode-client"]).toBe("extension-client"); - }); - - it("does not add OpenCode session headers for unrelated providers", async () => { - const headers = await captureSummaryHeaders({ - model: createModel("example", "https://example.test/v1"), - }); - - expect(headers?.["x-opencode-session"]).toBeUndefined(); - expect(headers?.["x-opencode-client"]).toBeUndefined(); - }); -}); From 27fcf328cde6c1ec015cdbe76f07913f3c421840 Mon Sep 17 00:00:00 2001 From: Altair Li <137127498+Altairpaca@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:13:35 +0800 Subject: [PATCH 8/8] docs(changes): track OpenCode compaction attribution --- packages/coding-agent/src/core/changes.md | 18 ++++++++++++++++++ .../extensions/builtin/compaction/changes.md | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/packages/coding-agent/src/core/changes.md b/packages/coding-agent/src/core/changes.md index eb39744dc9..9a626e58de 100644 --- a/packages/coding-agent/src/core/changes.md +++ b/packages/coding-agent/src/core/changes.md @@ -1,5 +1,23 @@ # changes +## 2026-09-11 - Preserve OpenCode session attribution for direct compaction summaries + +### What changed + +- `packages/coding-agent/src/core/provider-attribution.ts`: exports the existing OpenCode session-header predicate as `getOpenCodeSessionHeaders()` so non-AgentSession request paths can reuse the provider/host test without duplicating it. `mergeProviderAttributionHeaders()` continues to apply the same generated defaults and caller override order. + +### Why + +- Builtin compaction dispatches its summary directly through `ModelRuntime.stream()` and therefore cannot reuse the AgentSession wrapper that normally attaches `x-opencode-session`; exposing the narrow helper lets that alternate request boundary preserve the same routing identity. + +### Why an extension could not handle it + +- The session-attribution predicate is core provider request policy used before or beneath extension header transforms; duplicating it in an extension would allow the provider/host rules to drift from normal AgentSession traffic. + +### Expected merge conflict zones + +- LOW: `packages/coding-agent/src/core/provider-attribution.ts` around the OpenCode session-header helper and `mergeProviderAttributionHeaders()`. + ## 2026-09-10 - Atomic account display-name metadata with shape-keyed sentinel guard (senpi#1495) ### What changed diff --git a/packages/coding-agent/src/core/extensions/builtin/compaction/changes.md b/packages/coding-agent/src/core/extensions/builtin/compaction/changes.md index b932e2e493..f95614c9b1 100644 --- a/packages/coding-agent/src/core/extensions/builtin/compaction/changes.md +++ b/packages/coding-agent/src/core/extensions/builtin/compaction/changes.md @@ -1,5 +1,24 @@ # changes.md — builtin compaction policy +## 2026-09-11 - Preserve OpenCode session routing on builtin summary requests + +### What changed + +- `packages/coding-agent/src/core/extensions/builtin/compaction/speculative-summary.ts`: seeds the current `SessionManager` id through `getOpenCodeSessionHeaders()` before resolved auth/configured headers and the existing provider-request header transform. +- `packages/coding-agent/test/suite/regressions/issue-1523-opencode-session-attribution.test.ts`: pins provider-id and host routing, configured-header precedence, extension-transform precedence, and non-OpenCode neutrality. + +### Why + +- Issue #1523: the compaction summarizer bypasses `sdk.ts`'s normal AgentSession stream wrapper, so OpenCode Go requests could omit `x-opencode-session` and fail with `400 MissingSessionID` while ordinary turns succeeded. + +### Why an extension could not handle it + +- The omission occurs at the builtin compaction request-construction boundary before `ModelRuntime.stream()`. The fix must seed the normal routing defaults before the already-supported provider header transform, which remains authoritative. + +### Expected merge conflict zones + +- LOW: `speculative-summary.ts` at request-header construction immediately before `summarizationStream()`. + ## Deterministic resume slice for an over-window restored context (2026-09-10) ### What changed