import assert from "node:assert/strict"; import test from "node:test"; import { createBrowserRuntimeFactory, withRuntimeOriginPolicy } from "./runtime.ts"; import type { BrowserOperation, BrowserRuntimeOptions, BrowserTransport, BrowserTransportRequest, BrowserTransportResultFor, PrivateArtifactMetadata, PrivateArtifactStore, RuntimeOperationContext, } from "./contracts.ts"; const tab = (id: string, url = "https://example.test/form") => ({ tabId: id as never, kind: "page" as const, selected: true, owned: true as const, url, origin: new URL(url).origin, title: "Fixture", }); function artifacts(): { store: PrivateArtifactStore; committed: PrivateArtifactMetadata[] } { const committed: PrivateArtifactMetadata[] = []; const store: PrivateArtifactStore = { async open(owner, kind) { let bytes = 0; let done = false; return { owner, kind, async write(chunk) { if (done) throw new Error("closed"); bytes += chunk.byteLength; }, async commit() { done = true; const metadata: PrivateArtifactMetadata = { artifactId: `${kind}-1`, kind, contentType: kind === "screenshot" ? "image/png" : "application/json", path: `/private/${kind}-1`, bytes, owner, permissions: "0600", localOnly: true, modelForwarded: false, }; committed.push(metadata); return metadata; }, async abort() { done = true; }, }; }, async close() {}, }; return { store, committed }; } function transportFixture() { const calls: BrowserTransportRequest[] = []; let transportClosed = 0; const transport: BrowserTransport = { async dispatch(request: BrowserTransportRequest, signal: AbortSignal): Promise> { calls.push(request as BrowserTransportRequest); const op = request.operation; if (op.op === "observe") return { op: "observe", result: { observationId: "o1", url: "https://example.test/form", title: "Fixture", candidates: [] } } as unknown as BrowserTransportResultFor; if (op.op === "read") return { op: "read", result: { tab: tab("tab-1"), documentId: "doc-1", url: "https://example.test/form", title: "Fixture", visibleText: "Hello", visibleTextTruncated: false } } as unknown as BrowserTransportResultFor; if (op.op === "listTabs") return { op: "listTabs", result: [tab("tab-1"), tab("tab-2", "https://other.test/")] } as unknown as BrowserTransportResultFor; if (op.op === "navigate") return { op: "navigate", result: { status: "committed", mutationState: "verified", tab: tab("tab-1", op.request.url), documentId: "doc-2" } } as unknown as BrowserTransportResultFor; if (op.op === "narrowOriginPolicy") return { op: "narrowOriginPolicy", result: { allowedOrigins: op.request.allowedOrigins } } as unknown as BrowserTransportResultFor; if (op.op === "captureEvidence") return { op: "captureEvidence", result: { status: "complete", console: [{ level: "error", message: "password=secret https://example.test/?token=secret", count: 1, truncated: false }], failedRequests: [], screenshot: { kind: "screenshot", contentType: "image/png", encoding: "base64", dataBase64: "aGk=", byteLength: 2, truncated: false }, } } as unknown as BrowserTransportResultFor; if (op.op === "close") return { op: "close", result: { closed: true } } as unknown as BrowserTransportResultFor; if (op.op === "assert") return { op: "assert", result: { status: "satisfied", readOnly: true, tab: tab("tab-1"), documentId: "doc-1" } } as unknown as BrowserTransportResultFor; if (op.op === "reload") return { op: "reload", result: { status: "committed", mutationState: "verified", tab: tab("tab-1"), documentId: "doc-2" } } as unknown as BrowserTransportResultFor; if (op.op === "selectTab") return { op: "selectTab", result: { selected: true, tab: tab(op.request.tabId) } } as unknown as BrowserTransportResultFor; if (op.op === "waitPopup") return { op: "waitPopup", result: { status: "timed_out", autoSwitched: false } } as unknown as BrowserTransportResultFor; if (op.op === "closeTab") return { op: "closeTab", result: { closed: true, tabId: op.request.tabId, mutationState: "verified" } } as unknown as BrowserTransportResultFor; throw new Error("unexpected operation"); }, async close() { transportClosed++; }, }; return { transport, calls, transportClosed: () => transportClosed }; } function options(artifactsStore: PrivateArtifactStore, transport: BrowserTransport): BrowserRuntimeOptions { return { sessionId: "session-1", cdpUrl: "http://127.0.0.1:9222", allowedOrigins: "*", artifacts: artifactsStore, transportFactory: { create() { return transport; } } }; } test("factory creates one persistent transport and serializes same-session calls", async () => { const store = artifacts(); const fixture = transportFixture(); const runtime = createBrowserRuntimeFactory(options(store.store, fixture.transport).transportFactory).create(options(store.store, fixture.transport)); await Promise.all([runtime.observe(new AbortController().signal), runtime.read({}, new AbortController().signal)]); assert.equal(fixture.calls.filter((call) => call.operation.op === "observe" || call.operation.op === "read").length, 2); assert.deepEqual(Object.keys(fixture.calls[0] ?? {}).sort(), ["operation"]); assert.equal((fixture.calls[0]?.operation as any).op, "observe"); assert.equal((fixture.calls[0] as any)?.op, undefined); assert.equal(fixture.calls[1]?.operation.op, "read"); await runtime.close(); await runtime.close(); assert.equal(fixture.transportClosed(), 1); }); test("aborted operations still make an independent bounded evidence attempt", async () => { const store = artifacts(); const fixture = transportFixture(); const runtime = createBrowserRuntimeFactory({ create() { return fixture.transport; } }).create(options(store.store, fixture.transport)); const controller = new AbortController(); controller.abort(new Error("operation deadline exceeded")); await assert.rejects(runtime.observe(controller.signal), (error: any) => { assert.equal(error.details.code, "aborted"); assert.deepEqual(error.failureEvidence, { status: "complete" }); return true; }); assert.equal(fixture.calls.filter((call) => call.operation.op === "captureEvidence").length, 1); await runtime.close(); }); test("already-aborted parent does not create transport during failure evidence", async () => { const store = artifacts(); const parent = new AbortController(); parent.abort(new Error("browser session closed")); let created = 0; const runtime = createBrowserRuntimeFactory({ create() { created++; return transportFixture().transport; } }).create({ ...options(store.store, transportFixture().transport) }, parent.signal); await assert.rejects(runtime.observe(new AbortController().signal), (error: any) => { assert.equal(error.details.code, "browser_closed"); assert.deepEqual(error.failureEvidence, { status: "unavailable", reason: "browser session is unavailable" }); return true; }); assert.equal(created, 0); await runtime.close(); }); test("destroyed transports report unavailable evidence without replacing the failure", async () => { const store = artifacts(); const transport: BrowserTransport = { async dispatch(request: BrowserTransportRequest): Promise> { if (request.operation.op === "captureEvidence") throw new Error("browser bridge is closed"); throw new Error("browser worker request timed out; worker was torn down"); }, async close() {}, }; const runtime = createBrowserRuntimeFactory({ create() { return transport; } }).create(options(store.store, transport)); const controller = new AbortController(); controller.abort(new Error("operation deadline exceeded")); await assert.rejects(runtime.observe(controller.signal), (error: any) => { assert.equal(error.details.code, "aborted"); assert.deepEqual(error.failureEvidence, { status: "unavailable", reason: "browser session is unavailable" }); return true; }); await runtime.close(); }); test("factory forwards pinned Python selection to the transport", async () => { const store = artifacts(); const fixture = transportFixture(); let received: unknown; const runtime = createBrowserRuntimeFactory({ create(transportOptions) { received = transportOptions; return fixture.transport; } }).create({ ...options(store.store, fixture.transport), python: "/tmp/pinned-python" }); await runtime.observe(new AbortController().signal); assert.equal((received as any).python, "/tmp/pinned-python"); await runtime.close(); }); test("wildcard narrowing is scoped while restricted bases cannot broaden", async () => { const first = artifacts(); const fixture = transportFixture(); const runtime = createBrowserRuntimeFactory(options(first.store, fixture.transport).transportFactory).create(options(first.store, fixture.transport)); await withRuntimeOriginPolicy(runtime, ["https://example.test"], (context) => runtime.read({}, new AbortController().signal, context)); const reads = fixture.calls.filter((call) => call.operation.op === "read"); assert.deepEqual((reads[0]?.context as RuntimeOperationContext).allowedOrigins, ["https://example.test"]); await runtime.read({}, new AbortController().signal); assert.equal(fixture.calls.at(-1)?.context, undefined); const restricted = createBrowserRuntimeFactory({ create() { return fixture.transport; } }).create({ ...options(first.store, fixture.transport), allowedOrigins: ["https://example.test"] }); await assert.rejects(restricted.narrowOriginPolicy({ allowedOrigins: "*" }, new AbortController().signal), /broadened/); await runtime.close(); await restricted.close(); }); test("policy is enforced before navigation and tabs are origin filtered", async () => { const store = artifacts(); const fixture = transportFixture(); const runtime = createBrowserRuntimeFactory(options(store.store, fixture.transport).transportFactory).create(options(store.store, fixture.transport)); const context = await runtime.narrowOriginPolicy({ allowedOrigins: ["https://example.test"] }, new AbortController().signal); await assert.rejects(runtime.navigate({ url: "https://other.test/out" }, new AbortController().signal, context), (error: any) => error.details.code === "policy_denied"); const restricted = createBrowserRuntimeFactory({ create() { return fixture.transport; } }).create({ ...options(store.store, fixture.transport), allowedOrigins: ["https://example.test"] }); await assert.rejects(restricted.navigate({ url: "https://example.test/out" }, new AbortController().signal, { allowedOrigins: "*" }), (error: any) => { assert.equal(error.details.code, "policy_denied"); assert.equal(error.details.mutationState, "not_attempted"); assert.equal(error.details.retry, "never"); return true; }); const tabs = await runtime.listTabs(new AbortController().signal, context); assert.deepEqual(tabs.map((item) => item.origin), ["https://example.test"]); await runtime.close(); await restricted.close(); }); test("requested evidence commits local metadata and redacts untrusted console text", async () => { const store = artifacts(); const fixture = transportFixture(); const runtime = createBrowserRuntimeFactory(options(store.store, fixture.transport).transportFactory).create(options(store.store, fixture.transport)); const result = await runtime.captureEvidence({ runId: "run-1", reason: "requested" }, new AbortController().signal); assert.equal(result.screenshot?.bytes, 2); assert.equal(result.screenshot?.modelForwarded, false); assert.equal(result.console[0]?.message.includes("secret"), false); assert.equal(store.committed.length, 1); await runtime.close(); }); test("transport failures become safe errors and are not mutation retried", async () => { const store = artifacts(); let actions = 0; const transport: BrowserTransport = { async dispatch(request: BrowserTransportRequest): Promise> { actions++; if (request.operation.op === "captureEvidence") return { op: "captureEvidence", result: { status: "unavailable", console: [], failedRequests: [] } } as unknown as BrowserTransportResultFor; throw new Error("private page secret and mutation may have run"); }, async close() {}, }; const runtime = createBrowserRuntimeFactory({ create() { return transport; } }).create(options(store.store, transport)); await assert.rejects(runtime.act({ observationId: "o", candidateId: "c", action: "click", postcondition: { kind: "text", value: "done" } }, new AbortController().signal), (error: any) => { assert.equal(error.details.code, "worker_failure"); assert.equal(error.details.mutationState, "unknown"); assert.equal(String(error).includes("private"), false); return true; }); assert.equal(actions, 2, "one action dispatch plus one best-effort evidence request; no mutation retry"); await runtime.close(); });