declare const Deno: { test(name: string, fn: () => void | Promise): void; }; import { createSubjektivSubject, parseMemoryStagingListResponse, parseSubjektivMemoryListChangesResponse, parseSubjektivMemoryQueryResponse, parseSubjektivMemoryReadResponse, parseSubjektivResidentSurfaceResponse, parseSubjektivSubjectListResponse, parseSubjektivSubjectResponse, SubjektivSubjectCreateError, updateSubjektivSubjectBehavior, validateSubjektivSubjectCreateRequest, } from "../src/lib/workspace/memory/api.ts"; function stable(value: unknown): unknown { if (Array.isArray(value)) return value.map(stable); if (typeof value === "object" && value !== null) { return Object.fromEntries( Object.entries(value as Record) .sort(([left], [right]) => left.localeCompare(right)) .map(([key, item]) => [key, stable(item)]), ); } return value; } function assertEquals(actual: unknown, expected: unknown): void { if (JSON.stringify(stable(actual)) !== JSON.stringify(stable(expected))) { throw new Error( `expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`, ); } } function assertThrows(fn: () => void, expectedMessage: string): void { try { fn(); } catch (error) { if (error instanceof Error && error.message.includes(expectedMessage)) { return; } throw error; } throw new Error(`expected function to throw ${expectedMessage}`); } async function assertSubjectCreateRejects( fn: () => Promise, expectedKind: SubjektivSubjectCreateError["kind"], expectedMessage: string, expectedStatus: number | null = null, ): Promise { try { await fn(); } catch (error) { if (!(error instanceof SubjektivSubjectCreateError)) throw error; assertEquals(error.kind, expectedKind); assertEquals(error.status, expectedStatus); if (!error.message.includes(expectedMessage)) { throw new Error( `expected ${JSON.stringify(error.message)} to include ${JSON.stringify(expectedMessage)}`, ); } return; } throw new Error(`expected Subject creation to reject with ${expectedKind}`); } function subject(id = "subject-1") { return { id, role: "Release coordinator", behavior_md: "Prefer explicit evidence.", state: "active", memory_fingerprint: "fingerprint-12", created_at: "2026-09-01T00:00:00Z", updated_at: "2026-09-02T00:00:00Z", }; } function currentWorker() { return { runtime_id: "runtime-1", worker_id: "worker-1", host_id: "host-1", display_name: "Release Worker", label: "Release Worker", profile: "builtin:companion", singleton_key: "subjektiv:subject-1", tags: [], workspace: { visibility: "workspace", identity: "workspace-1", workspace_id: "workspace-1", }, state: "idle", last_seen_at: "2026-09-02T00:00:00Z", pinned: false, retention_state: "retained", implementation: { kind: "embedded", display_hint: "Embedded" }, diagnostics: [], }; } function queryItem(id = "memory-1") { return { id, change_id: "change-current", kind: "decision", state: "active", claim: "Keep provenance typed.", excerpt: "Preserve source and derivation references.", updated_at: "2026-09-02T03:04:05Z", }; } function detailFixture() { const origin = { kind: "flow_instruction", workspace_id: "workspace-1", runtime_id: "runtime-1", worker_id: "worker-1", flow_selector: "builtin:coder-review", flow_definition_id: "flow-1", }; return { memory_id: "memory-1", change_id: "change-earlier", current_change_id: "change-current", kind: "decision", state: "resolved", claim: "Keep provenance typed.", body_md: "# Committed Memory\n\nBounded body segment.", why_useful: "Prevents origin loss.", staleness: null, change_reason: "Resolved after implementation.", created_at: "2026-09-01T00:00:00Z", updated_at: "2026-09-02T03:04:05Z", body_offset: 0, body_byte_offset: 0, body_next_offset: 2, body_next_byte_offset: 0, body_truncated: true, source_candidate_ids: ["candidate-1"], source_candidates: [{ candidate_id: "candidate-1", evidence: [{ id: "evidence-1", kind: "message", entry_range: [10, 12], origin, excerpt: "Use exact DTOs.", summary: "The decision was explicit.", }], evidence_total: 2, evidence_truncated: true, source_refs: [{ session_id: "session-1", segment_id: "segment-1", entry_range: [10, 12], evidence_id: "evidence-1", origin, evidence_kind: "message", label: "Decision discussion", summary: "Bounded host summary.", }], source_refs_total: 2, source_refs_truncated: true, }], derived_from: [{ memory_id: "memory-parent", change_id: "change-parent" }], evidence_next_cursor: "evidence-page-2", evidence_has_more: true, }; } Deno.test("Subject parsers enforce identity, exact enums, bounded fingerprints, and bounds", () => { assertEquals( parseSubjektivSubjectResponse(subject(), "subject-1"), subject(), ); assertThrows( () => parseSubjektivSubjectResponse(subject(), "another-subject"), "identity does not match", ); assertThrows( () => parseSubjektivSubjectResponse({ ...subject(), state: "paused" }), "unknown Subject state", ); assertThrows( () => parseSubjektivSubjectResponse({ ...subject(), memory_fingerprint: 12, }), "bounded identifier", ); assertThrows( () => parseSubjektivSubjectResponse({ ...subject(), id: "x".repeat(513) }), "bounded identifier", ); assertThrows( () => parseSubjektivSubjectResponse({ ...subject(), future: true }), "unknown field", ); assertThrows( () => parseSubjektivSubjectListResponse({ limit: 100, items: Array.from({ length: 101 }, (_, index) => subject(`subject-${index}`)), has_more: false, }), "bounded array", ); }); Deno.test("Subject parser preserves live Worker connection state", () => { assertEquals( parseSubjektivSubjectResponse({ ...subject(), current_worker: currentWorker(), }), { ...subject(), current_worker: currentWorker() }, ); assertEquals( parseSubjektivSubjectResponse({ ...subject(), current_worker: null }), { ...subject(), current_worker: null }, ); assertThrows( () => parseSubjektivSubjectResponse({ ...subject(), current_worker: { display_name: "Incomplete Worker" }, }), "current worker.runtime_id", ); }); Deno.test("Subject list parser preserves the exact bounded response", () => { const response = { limit: 100, items: [subject()], has_more: false }; assertEquals(parseSubjektivSubjectListResponse(response), response); const nextPage = { limit: 1, items: [subject()], next_cursor: "subjektiv.subjects.next", has_more: true, }; assertEquals(parseSubjektivSubjectListResponse(nextPage), nextPage); assertThrows( () => parseSubjektivSubjectListResponse({ limit: 1, items: [subject()], has_more: true, }), "cursor does not match has_more", ); assertThrows( () => parseSubjektivSubjectListResponse({ limit: 100, items: [subject("duplicate"), subject("duplicate")], has_more: false, }), "Subject ids must be unique", ); }); Deno.test("Subject create request mirrors Backend role validation without rewriting input", () => { assertEquals( validateSubjektivSubjectCreateRequest({ role: " Release coordinator " }), { role: " Release coordinator ", behavior_md: "" }, ); assertThrows( () => validateSubjektivSubjectCreateRequest({ role: " " }), "must not be empty", ); assertThrows( () => validateSubjektivSubjectCreateRequest({ role: "line\nbreak" }), "control characters", ); assertThrows( () => validateSubjektivSubjectCreateRequest({ role: "界".repeat(86) }), "at most 256 bytes", ); assertThrows( () => validateSubjektivSubjectCreateRequest({ role: "Reviewer", behavior_md: " ".repeat(3), }), "empty or contain non-whitespace", ); assertThrows( () => validateSubjektivSubjectCreateRequest({ role: "Reviewer", behavior_md: "x".repeat(16 * 1024 + 1), }), "at most 16384 bytes", ); }); Deno.test("Subject behavior update uses CAS and preserves exact text", async () => { const requests: Array<{ path: string; init?: RequestInit }> = []; const fetchFn = (async (path: string | URL | Request, init?: RequestInit) => { requests.push({ path: String(path), init }); return Response.json({ ...subject("subject/one"), behavior_md: "Line one.\nLine two.", }); }) as typeof fetch; const updated = await updateSubjektivSubjectBehavior( fetchFn, "workspace one", "subject/one", { expected_behavior_md: "Prefer explicit evidence.", behavior_md: "Line one.\nLine two." }, ); assertEquals(updated.behavior_md, "Line one.\nLine two."); assertEquals( requests[0].path, "/api/w/workspace%20one/subjektiv/subjects/subject%2Fone/behavior", ); assertEquals(requests[0].init?.method, "PATCH"); assertEquals( requests[0].init?.body, '{"expected_behavior_md":"Prefer explicit evidence.","behavior_md":"Line one.\\nLine two."}', ); }); Deno.test("Subject behavior update reports conflicts without claiming Worker application", async () => { const conflictFetch = (async () => Response.json({ message: "behavior content conflict" }, { status: 409, })) as typeof fetch; await assertSubjectCreateRejects( () => updateSubjektivSubjectBehavior(conflictFetch, "workspace", "subject", { expected_behavior_md: "old", behavior_md: "new", }), "rejected", "changed elsewhere", 409, ); }); Deno.test("Subject create client sends the exact typed body once and parses the created Subject", async () => { const requests: Array<{ path: string; init?: RequestInit }> = []; const fetchFn = (async (path: string | URL | Request, init?: RequestInit) => { requests.push({ path: String(path), init }); return Response.json({ ...subject("subject-created"), role: " Release coordinator ", behavior_md: "Stay concise.", }, { status: 201 }); }) as typeof fetch; const created = await createSubjektivSubject(fetchFn, "workspace one", { role: " Release coordinator ", behavior_md: "Stay concise.", }); assertEquals(created, { ...subject("subject-created"), role: " Release coordinator ", behavior_md: "Stay concise.", }); assertEquals(requests.length, 1); assertEquals(requests[0].path, "/api/w/workspace%20one/subjektiv/subjects"); assertEquals(requests[0].init?.method, "POST"); assertEquals( requests[0].init?.body, '{"role":" Release coordinator ","behavior_md":"Stay concise."}', ); }); Deno.test("Subject create client distinguishes rejected and unknown outcomes without retrying", async () => { let rejectedCalls = 0; const rejectedFetch = (async () => { rejectedCalls += 1; return Response.json( { error: "Forbidden", message: "workspace permission denied", diagnostics: [], }, { status: 403 }, ); }) as typeof fetch; await assertSubjectCreateRejects( () => createSubjektivSubject(rejectedFetch, "workspace-1", { role: "Reviewer", }), "rejected", "workspace permission denied", 403, ); assertEquals(rejectedCalls, 1); let serverErrorCalls = 0; const serverErrorFetch = (async () => { serverErrorCalls += 1; return Response.json({ error: "Internal Server Error" }, { status: 500 }); }) as typeof fetch; await assertSubjectCreateRejects( () => createSubjektivSubject(serverErrorFetch, "workspace-1", { role: "Reviewer", }), "unknown_outcome", "may have been created", 500, ); assertEquals(serverErrorCalls, 1); let mismatchCalls = 0; const mismatchFetch = (async () => { mismatchCalls += 1; return Response.json(subject("subject-created"), { status: 201 }); }) as typeof fetch; await assertSubjectCreateRejects( () => createSubjektivSubject(mismatchFetch, "workspace-1", { role: "Reviewer", }), "unknown_outcome", "could not be confirmed", ); assertEquals(mismatchCalls, 1); let unknownCalls = 0; const unknownFetch = (async () => { unknownCalls += 1; throw new TypeError("connection reset"); }) as typeof fetch; await assertSubjectCreateRejects( () => createSubjektivSubject(unknownFetch, "workspace-1", { role: "Reviewer" }), "unknown_outcome", "may have been created", ); assertEquals(unknownCalls, 1); }); Deno.test("Resident surface parser accepts ready-empty and enforces snapshot invariants", () => { const readyEmpty = { subject_id: "subject-1", availability: "ready", snapshot: { snapshot_id: "snapshot-empty", body_md: "", memory_refs: [], built_from_memory_fingerprint: "fingerprint-0", created_at: "2026-09-02T00:00:00Z", }, }; assertEquals( parseSubjektivResidentSurfaceResponse(readyEmpty, "subject-1"), readyEmpty, ); assertEquals( parseSubjektivResidentSurfaceResponse({ subject_id: "subject-1", availability: "ungenerated", }), { subject_id: "subject-1", availability: "ungenerated" }, ); assertThrows( () => parseSubjektivResidentSurfaceResponse({ subject_id: "subject-1", availability: "ready", }), "requires a snapshot", ); for (const availability of ["ungenerated", "stale", "failed"]) { assertThrows( () => parseSubjektivResidentSurfaceResponse({ ...readyEmpty, availability, }), "must not include a snapshot", ); } assertThrows( () => parseSubjektivResidentSurfaceResponse({ subject_id: "subject-1", availability: "refreshing", }), "unknown resident surface availability", ); assertThrows( () => parseSubjektivResidentSurfaceResponse(readyEmpty, "subject-2"), "identity does not match", ); }); Deno.test("Current Memory list parser rejects unknown variants, invalid change IDs, cursors, and oversized pages", () => { const response = { items: [queryItem()], has_more: true, next_cursor: "memory-page-2", }; assertEquals(parseSubjektivMemoryQueryResponse(response), response); assertThrows( () => parseSubjektivMemoryQueryResponse({ ...response, items: [{ ...queryItem(), state: "pending" }], }), "unknown Memory state", ); assertThrows( () => parseSubjektivMemoryQueryResponse({ ...response, items: [{ ...queryItem(), kind: "future_kind" }], }), "unknown Memory candidate kind", ); assertThrows( () => parseSubjektivMemoryQueryResponse({ ...response, items: [{ ...queryItem(), change_id: "" }], }), "bounded identifier", ); assertThrows( () => parseSubjektivMemoryQueryResponse({ items: [queryItem()], has_more: true, }), "cursor does not match", ); assertThrows( () => parseSubjektivMemoryQueryResponse({ items: Array.from({ length: 101 }, (_, index) => queryItem(`memory-${index}`)), has_more: false, }), "bounded array", ); }); Deno.test("Memory detail parser preserves body/evidence continuation, sources, origins, and derivation", () => { const fixture = detailFixture(); const parsed = parseSubjektivMemoryReadResponse(fixture, "memory-1"); assertEquals(parsed, fixture); assertEquals(parsed.body_next_offset, 2); assertEquals(parsed.body_next_byte_offset, 0); assertEquals(parsed.evidence_next_cursor, "evidence-page-2"); assertEquals( parsed.source_candidates[0].evidence[0].origin, fixture.source_candidates[0].evidence[0].origin, ); assertEquals( parsed.source_candidates[0].source_refs, fixture.source_candidates[0].source_refs, ); assertEquals(parsed.derived_from, [{ memory_id: "memory-parent", change_id: "change-parent", }]); }); Deno.test("Memory detail parser fails closed on identity, variants, bounds, and continuation mismatch", () => { const fixture = detailFixture(); assertThrows( () => parseSubjektivMemoryReadResponse(fixture, "memory-2"), "identity does not match", ); assertThrows( () => parseSubjektivMemoryReadResponse({ ...fixture, current_change_id: 3, }), "bounded identifier", ); const missingBodyContinuation = structuredClone(fixture) as Record< string, unknown >; delete missingBodyContinuation.body_next_byte_offset; assertThrows( () => parseSubjektivMemoryReadResponse(missingBodyContinuation), "body continuation fields", ); assertThrows( () => parseSubjektivMemoryReadResponse({ ...fixture, source_candidate_ids: ["different-candidate"], }), "source candidate ids do not match", ); assertThrows( () => parseSubjektivMemoryReadResponse({ ...fixture, source_candidates: [{ ...fixture.source_candidates[0], evidence: [{ ...fixture.source_candidates[0].evidence[0], entry_range: [12, 10], }], }], }), "must be ordered", ); const unknownOrigin = structuredClone(fixture); unknownOrigin.source_candidates[0].evidence[0].origin.kind = "future_origin"; assertThrows( () => parseSubjektivMemoryReadResponse(unknownOrigin), "unknown Memory evidence origin kind", ); assertThrows( () => parseSubjektivMemoryReadResponse({ ...fixture, evidence_has_more: false, }), "cursor does not match", ); }); Deno.test("Memory change parser preserves immutable states and checks response identity", () => { const response = { memory_id: "memory-1", current_change_id: "change-current", items: [{ change_id: "change-current", kind: "decision", state: "active", claim: "Current claim.", change_reason: "Clarified wording.", updated_at: "2026-09-03T00:00:00Z", }, { change_id: "change-earlier", kind: "decision", state: "resolved", claim: "Earlier claim.", change_reason: "Resolved.", updated_at: "2026-09-02T00:00:00Z", }, { change_id: "change-original", kind: "decision", state: "retracted", claim: "Original claim.", change_reason: "Retracted.", updated_at: "2026-09-01T00:00:00Z", }], has_more: false, }; assertEquals( parseSubjektivMemoryListChangesResponse(response, "memory-1"), response, ); assertThrows( () => parseSubjektivMemoryListChangesResponse(response, "memory-2"), "identity does not match", ); assertThrows( () => parseSubjektivMemoryListChangesResponse({ ...response, items: [{ ...response.items[0], state: "archived" }], }), "unknown Memory state", ); assertThrows( () => parseSubjektivMemoryListChangesResponse({ ...response, items: [response.items[0], response.items[0]], }), "Memory changes must be unique", ); }); Deno.test("Memory changes preserve server order without interpreting opaque identities", () => { const item = { kind: "decision", state: "active", claim: "Claim", change_reason: "Reason", updated_at: "2026-09-01T00:00:00Z" }; const response = { memory_id: "memory-1", current_change_id: "a/current", items: [{ ...item, change_id: "z/earlier" }, { ...item, change_id: "a/current" }], has_more: false, }; assertEquals(parseSubjektivMemoryListChangesResponse(response), response); assertEquals(parseSubjektivMemoryReadResponse({ ...detailFixture(), change_id: "z/earlier", current_change_id: "a/current" }).change_id, "z/earlier"); }); Deno.test("Memory identities reject numeric counters, empty and oversized strings, and unknown fields", () => { for (const value of [3, "", "x".repeat(513)]) { assertThrows(() => parseSubjektivMemoryReadResponse({ ...detailFixture(), change_id: value }), "bounded identifier"); assertThrows(() => parseSubjektivMemoryReadResponse({ ...detailFixture(), current_change_id: value }), "bounded identifier"); assertThrows(() => parseSubjektivSubjectResponse({ ...subject(), memory_fingerprint: value }), "bounded identifier"); } assertThrows(() => parseSubjektivMemoryReadResponse({ ...detailFixture(), unexpected_field: true }), "unknown field"); assertThrows(() => parseSubjektivSubjectResponse({ ...subject(), unexpected_field: true }), "unknown field"); }); Deno.test("Behavior CAS sends the observed text byte-for-byte including empty content", async () => { for (const expected of ["", " Previous\nbehavior.\n"]) { let body: unknown; const fetchFn = (async (_path: string | URL | Request, init?: RequestInit) => { body = JSON.parse(String(init?.body)); return Response.json({ ...subject(), behavior_md: "Replacement" }); }) as typeof fetch; await updateSubjektivSubjectBehavior(fetchFn, "workspace", "subject-1", { expected_behavior_md: expected, behavior_md: "Replacement" }); assertEquals(body, { expected_behavior_md: expected, behavior_md: "Replacement" }); } }); Deno.test("Behavior saves with mismatched response text have unknown outcome and are not retried", async () => { let calls = 0; const fetchFn = (async () => { calls += 1; return Response.json(subject()); }) as typeof fetch; await assertSubjectCreateRejects(() => updateSubjektivSubjectBehavior(fetchFn, "workspace", "subject-1", { expected_behavior_md: "Prefer explicit evidence.", behavior_md: "Replacement", }), "unknown_outcome", "could not be confirmed"); assertEquals(calls, 1); }); Deno.test("Legacy staging parser remains strict for deprecated compatibility", () => { const origin = { kind: "worker_input", worker_id: "worker-1" }; const response = { limit: 100, returned_count: 1, total_valid_count: 1, invalid_count: 0, truncated: false, order: "imported_at_desc_candidate_id_asc", record_authority: "sqlite_workspace_authority.memory_staging", items: [{ id: "candidate-1", byte_len: 128, record: { schema_version: 2, id: "candidate-1", extract_run_id: "extract-run-1", source: { segment_id: "segment-1", range: [10, 20] }, kind: "decision", claim: "Keep provenance typed.", why_useful: "Prevents origin loss.", staleness: null, evidence: [{ id: "evidence-1", kind: "message", entry_range: [10, 10], origin, excerpt: null, summary: "bounded summary", }], source_refs: [{ session_id: "session-1", segment_id: "segment-1", entry_range: [10, 10], evidence_id: "evidence-1", origin, evidence_kind: "message", label: "source", summary: null, }], }, }], diagnostics: [], }; assertEquals(parseMemoryStagingListResponse(response), response); assertThrows( () => parseMemoryStagingListResponse({ ...response, items: [{ ...response.items[0], record: { ...response.items[0].record, source: { segment_id: "segment-1", range: [20, 10] }, }, }], }), "must be ordered", ); });