Files
next-tjwater-agent/tests/runtime/opencode2Events.test.ts
T
jiang 7383ed6d13 feat(agent): migrate OpenCode 2 runtime and sandbox tools
Upgrade the runtime and native plugin API to OpenCode 2.0.24. Restore
session-scoped learning, credential renewal, typed project queries and
controlled frontend actions from the legacy Agent.

Replace host Shell execution with an approved Landlock/seccomp executor
and persistent per-session workspaces. Add typed Bocha search and Tianditu
geocoding bridges using the owning session credential. Require a working
kernel sandbox before startup and persist workspace files in deployments.

Validation completed before this commit: TypeScript checks, 150 Bun tests,
Docker build and UID 10001 sandbox execution, native Shell approval and
session recovery. Tianditu succeeded; Bocha currently reports exhausted
account quota. Unrelated investigation-report contract work is excluded.
2026-10-08 15:46:37 +08:00

49 lines
3.6 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import type { V2Event, FormInfo } from "@opencode/client";
import { formAnswers, Opencode2EventNormalizer } from "../../src/runtime/opencode2-events.js";
import { normalizeQuestionToolPayload } from "../../src/routes/chatStreamEvents.js";
const event = (type: string, data: unknown) => ({ type, data, created: 123 }) as V2Event;
describe("OpenCode 2 event contract", () => {
it("ends the product stream on native execution completion", () => {
const normalizer = new Opencode2EventNormalizer();
expect(normalizer.normalize(event("session.execution.succeeded", { sessionID: "s" }))).toMatchObject([
{ type: "session.status", properties: { sessionID: "s", status: { type: "idle" } } },
{ type: "session.idle", properties: { sessionID: "s" } },
]);
});
it("preserves tool identity, structured output and todo updates", () => {
const normalizer = new Opencode2EventNormalizer();
const data = { sessionID: "s", assistantMessageID: "m", id: "call-1" };
normalizer.normalize(event("session.tool.input.started", { ...data, name: "todowrite" }));
normalizer.normalize(event("session.tool.called", { ...data, input: { todos: [{ content: "check", status: "pending" }] } }));
const result = normalizer.normalize(event("session.tool.success", { ...data, content: [{ type: "text", text: "[]" }] }));
expect(result[0]).toMatchObject({ properties: { part: { callID: "call-1", tool: "todowrite", state: { status: "completed", output: "[]" } } } });
expect(result[1]).toMatchObject({ type: "todo.updated", properties: { sessionID: "s", todos: [{ content: "check" }] } });
});
it("keeps permissions tied to the native session and call", () => {
const normalizer = new Opencode2EventNormalizer();
expect(normalizer.normalize(event("permission.asked", { id: "p", sessionID: "s", action: "shell", resources: ["echo ok"], source: { type: "tool", id: "call-2", messageID: "m" } }))[0]).toMatchObject({
type: "permission.v2.asked", properties: { sessionID: "s", action: "shell", tool: { callID: "call-2", messageID: "m" } },
});
});
it("waits for the native form ID instead of exposing a tool call ID as a question request", () => {
const normalizer = new Opencode2EventNormalizer();
const data = { sessionID: "s", assistantMessageID: "m", id: "call-q" };
normalizer.normalize(event("session.tool.input.started", { ...data, name: "question" }));
const events = normalizer.normalize(event("session.tool.called", { ...data, input: { questions: [{ header: "验证", question: "是否通过?", options: [] }] } }));
const part = (events[0] as any).properties.part;
expect(normalizeQuestionToolPayload(part, part.state.input, "client")).toBeUndefined();
const forms = normalizer.normalize(event("form.created", { form: { id: "frm_test", sessionID: "s", title: "验证", fields: [{ key: "answer", type: "string", title: "是否通过?", options: [{ value: "ok", label: "通过" }] }] } }));
expect(forms[0]).toMatchObject({ type: "question.v2.asked", properties: { id: "frm_test", questions: [{ question: "是否通过?" }] } });
});
it("maps displayed choices back to form values and preserves multiple selections", () => {
const form = { id: "form-1", sessionID: "s", title: "选项", fields: [
{ key: "mode", type: "string", options: [{ label: "快速", value: "fast" }] },
{ key: "areas", type: "multiselect", options: [{ label: "东区", value: "east" }], custom: true },
] } as FormInfo;
expect(formAnswers(form, [["快速"], ["东区", "自选区域"]])).toEqual({ mode: "fast", areas: ["east", "自选区域"] });
});
});