fix(agent): preserve runtime request context

OpenCode tools run in a child service, so they cannot read the Agent process-local session map. Hydrate a sanitized context through the authenticated internal bridge and centralize backend project headers to prevent context loss across both boundaries.
This commit is contained in:
2026-08-05 18:39:09 +08:00
parent a53839e157
commit 2dc37e3fd8
7 changed files with 291 additions and 19 deletions
@@ -0,0 +1,83 @@
import { afterEach, describe, expect, it } from "bun:test";
import {
readBridgedRuntimeSessionContext,
serializeRuntimeSessionContext,
} from "../../src/runtime/internalSessionContextBridge.js";
import { removeRuntimeSessionContext } from "../../src/runtime/sessionContext.js";
describe("readBridgedRuntimeSessionContext", () => {
const sessionId = "remote-opencode-session";
afterEach(() => {
removeRuntimeSessionContext(sessionId);
});
it("hydrates a child-process context through the authenticated internal bridge", async () => {
const calls: Array<{ input: string; init?: RequestInit }> = [];
const context = await readBridgedRuntimeSessionContext(sessionId, {
baseUrl: "http://127.0.0.1:8787",
internalToken: "internal-secret",
fetchImpl: async (input, init) => {
calls.push({ input: String(input), init });
return Response.json({
actor_key: "actor-1",
allow_learning_write: true,
client_session_id: "client-session-1",
project_key: "project-1",
session_id: sessionId,
trace_id: "trace-1",
});
},
});
expect(context).toMatchObject({
actorKey: "actor-1",
allowLearningWrite: true,
clientSessionId: "client-session-1",
projectKey: "project-1",
sessionId,
traceId: "trace-1",
});
expect(calls).toHaveLength(1);
expect(calls[0]?.input).toBe(
"http://127.0.0.1:8787/internal/tools/session-context",
);
expect(calls[0]?.init?.headers).toEqual({
"Content-Type": "application/json",
"x-agent-internal-token": "internal-secret",
});
expect(JSON.parse(String(calls[0]?.init?.body))).toEqual({
session_id: sessionId,
});
});
it("does not expose backend credentials to the opencode child process", () => {
const context = {
accessToken: "backend-secret",
actorKey: "actor-1",
clientSessionId: "client-session-1",
network: "network-1",
projectId: "project-id-1",
projectKey: "project-key-1",
sessionId,
traceId: "trace-1",
};
expect(context).toHaveProperty("accessToken", "backend-secret");
const payload = serializeRuntimeSessionContext(context);
expect(payload).toEqual({
actor_key: "actor-1",
allow_learning_write: undefined,
client_session_id: "client-session-1",
memory_list_read_scopes: undefined,
project_key: "project-key-1",
session_id: sessionId,
trace_id: "trace-1",
});
expect(payload).not.toHaveProperty("access_token");
expect(payload).not.toHaveProperty("project_id");
expect(payload).not.toHaveProperty("network");
});
});