fix(opencode): stamp platformSource on worker posts (refs #3678) (#3767)

* fix(opencode): stamp platform source on worker posts (#3678)

* fix(opencode): scope platform source stamping to issue slice (#3678)
This commit is contained in:
Rod Boev
2026-09-10 23:40:33 -04:00
committed by GitHub
parent d1ae0cb29d
commit 82a18e9292
2 changed files with 74 additions and 1 deletions
+5 -1
View File
@@ -1,6 +1,7 @@
import { z } from "zod";
import { join } from "node:path";
import { SettingsDefaultsManager } from "../../shared/SettingsDefaultsManager.js";
import { normalizePlatformSource } from "../../shared/platform-source.js";
/**
* OpenCode plugin event contract.
@@ -116,7 +117,10 @@ function workerPostFireAndForget(
fetch(`${WORKER_BASE_URL}${path}`, {
method: "POST",
headers: JSON_HEADERS,
body: JSON.stringify(body),
body: JSON.stringify({
...body,
platformSource: normalizePlatformSource("opencode"),
}),
}).catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
if (!message.includes("ECONNREFUSED")) {
@@ -8,6 +8,7 @@ import {
REGISTERED_OPENCODE_HOOKS,
REAL_OPENCODE_EVENT_TYPES,
} from "../../src/integrations/opencode-plugin/index";
import { normalizePlatformSource } from "../../src/shared/platform-source";
/**
* Regression guard for plan-08 (OpenCode event-contract correctness).
@@ -176,6 +177,74 @@ describe("OpenCode plugin event contract", () => {
expect(obsBody.tool_name).toBe("read");
expect(obsBody.tool_input).toEqual({ path: "/a" });
expect(obsBody.tool_response).toBe("file contents");
expect(obsBody.platformSource).toBe(normalizePlatformSource("opencode"));
} finally {
globalThis.fetch = originalFetch;
}
});
it("stamps every session-write POST and leaves GET and deletion unchanged", async () => {
const requests: Array<{ method: string; url: string; body: Record<string, unknown> | null }> = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = (async (url: string | URL | Request, init?: RequestInit) => {
requests.push({
method: init?.method || "GET",
url: String(url),
body: init?.body ? JSON.parse(String(init.body)) : null,
});
return new Response(JSON.stringify({ content: [{ type: "text", text: "No observations found" }] }), {
status: 200,
});
}) as typeof fetch;
try {
const plugin = await ClaudeMemPlugin(pluginCtx);
const expectedPlatformSource = normalizePlatformSource("opencode");
const postHookInvocations: Record<string, () => Promise<void>> = {
"tool.execute.after": () => plugin["tool.execute.after"](
{ tool: "read", sessionID: "ses_contract_tool", callID: "c1" },
{ title: "Read", output: "tool output", metadata: {}, args: {} },
),
"chat.message": () => plugin["chat.message"](
{},
{
message: { role: "assistant", sessionID: "ses_contract_chat" },
parts: [{ type: "text", text: "assistant output" }],
},
),
"experimental.session.compacting": () => plugin["experimental.session.compacting"]({ sessionID: "ses_contract_compact" }),
event: () => plugin.event({ event: { type: "session.idle", properties: { sessionID: "ses_contract_idle" } } }),
};
for (const hook of REGISTERED_OPENCODE_HOOKS) {
const invoke = postHookInvocations[hook];
expect(invoke, `registered hook "${hook}" must have a POST contract case`).toBeDefined();
await invoke!();
}
const posts = requests.filter((request) => request.method === "POST");
expect(posts).toHaveLength(8);
expect(posts.map((request) => request.url)).toEqual([
expect.stringContaining("/api/sessions/init"),
expect.stringContaining("/api/sessions/observations"),
expect.stringContaining("/api/sessions/init"),
expect.stringContaining("/api/sessions/observations"),
expect.stringContaining("/api/sessions/init"),
expect.stringContaining("/api/sessions/summarize"),
expect.stringContaining("/api/sessions/init"),
expect.stringContaining("/api/sessions/summarize"),
]);
for (const post of posts) {
expect(post.body?.platformSource).toBe(expectedPlatformSource);
}
const postCountBeforeSearchAndDeletion = posts.length;
await plugin.tool.claude_mem_search.execute({ query: "auth" });
await plugin.event({ event: { type: "session.deleted", properties: { sessionID: "ses_contract_idle" } } });
expect(requests.filter((request) => request.method === "POST")).toHaveLength(
postCountBeforeSearchAndDeletion,
);
expect(requests.at(-1)?.method).toBe("GET");
} finally {
globalThis.fetch = originalFetch;
}