fix(coding-agent): add OpenCode session headers

closes #4847
This commit is contained in:
Armin Ronacher
2026-05-22 22:22:55 +02:00
parent 9b62f1f87c
commit 42379a37bd
3 changed files with 43 additions and 3 deletions

View File

@@ -13,6 +13,7 @@
### Fixed
- Fixed OpenCode Zen/Go requests to send per-session OpenCode routing headers ([#4847](https://github.com/earendil-works/pi/issues/4847)).
- Fixed Amazon Bedrock provider loading under strict package managers by inheriting the declared `@smithy/node-http-handler` dependency from `@earendil-works/pi-ai` ([#4842](https://github.com/earendil-works/pi/issues/4842)).
- Fixed exported session HTML to escape quote characters in attribute values ([#4832](https://github.com/earendil-works/pi/issues/4832)).
- Fixed GitHub Copilot device-code login to keep opening the verification URL in browser-capable environments while ignoring browser launch failures for headless use.

View File

@@ -129,7 +129,15 @@ function getDefaultAgentDir(): string {
function getAttributionHeaders(
model: Model<any>,
settingsManager: SettingsManager,
sessionId?: string,
): Record<string, string> | undefined {
if (
sessionId &&
(model.provider === "opencode" || model.provider === "opencode-go" || model.baseUrl.includes("opencode.ai"))
) {
return { "x-opencode-session": sessionId, "x-opencode-client": "pi" };
}
if (!isInstallTelemetryEnabled(settingsManager)) {
return undefined;
}
@@ -332,7 +340,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
throw new Error(auth.error);
}
const providerRetrySettings = settingsManager.getProviderRetrySettings();
const attributionHeaders = getAttributionHeaders(model, settingsManager);
const attributionHeaders = getAttributionHeaders(model, settingsManager, options?.sessionId);
return streamSimple(model, context, {
...options,
apiKey: auth.apiKey,

View File

@@ -86,6 +86,7 @@ describe("createAgentSession OpenRouter attribution headers", () => {
telemetryEnabled?: boolean;
providerHeaders?: Record<string, string>;
requestHeaders?: Record<string, string>;
sessionId?: string;
} = {},
): Promise<Record<string, string> | undefined> {
const settingsManager = SettingsManager.create(cwd, agentDir);
@@ -112,6 +113,11 @@ describe("createAgentSession OpenRouter attribution headers", () => {
registeredProviders.push(model.provider);
}
const sessionManager = SessionManager.inMemory(cwd);
if (options.sessionId) {
sessionManager.newSession({ id: options.sessionId });
}
const { session } = await createAgentSession({
cwd,
agentDir,
@@ -119,14 +125,17 @@ describe("createAgentSession OpenRouter attribution headers", () => {
authStorage,
modelRegistry,
settingsManager,
sessionManager: SessionManager.inMemory(cwd),
sessionManager,
});
try {
await session.agent.streamFn(
model,
{ messages: [] },
options.requestHeaders ? { headers: options.requestHeaders } : undefined,
{
sessionId: session.sessionId,
...(options.requestHeaders ? { headers: options.requestHeaders } : {}),
},
);
return capturedOptions?.headers;
} finally {
@@ -178,4 +187,26 @@ describe("createAgentSession OpenRouter attribution headers", () => {
expect(headers?.["X-OpenRouter-Title"]).toBe("request-title");
expect(headers?.["X-OpenRouter-Categories"]).toBe("provider-category");
});
it("adds OpenCode session headers", async () => {
const headers = await captureHeaders(createModel("opencode", "https://opencode.ai/zen/v1"), {
sessionId: "opencode-session",
});
expect(headers?.["x-opencode-session"]).toBe("opencode-session");
expect(headers?.["x-opencode-client"]).toBe("pi");
});
it("lets configured OpenCode headers override the defaults", async () => {
const headers = await captureHeaders(createModel("opencode", "https://opencode.ai/zen/v1"), {
sessionId: "opencode-session",
providerHeaders: {
"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");
});
});