| 1 | import assert from "node:assert/strict"; |
| 2 | import { readFileSync } from "node:fs"; |
| 3 | import { canonicalMessage } from "../lib/canonicalTranscriptBackend"; |
| 4 | import { transientUserTags } from "../lib/canonicalUserDisplay"; |
| 5 | import { applyResolvedField, entryToRecord } from "../lib/transcriptRecordProjection"; |
| 6 | import { historyMessagesToItems } from "../lib/useController"; |
| 7 | import type { HistoryContentRef } from "../lib/types"; |
| 8 | |
| 9 | const persistent = { messageId: "user-1", position: 2, version: 1, role: "user", eventSequence: 2, visibleTurn: 1 }; |
| 10 | const context = '<session-context version="1">\nThis host-generated snapshot supersedes every earlier session-context snapshot.\nEnvironment: private host context\n</session-context>'; |
| 11 | const wrapped = "<response-language>prefer Chinese</response-language>\n<reasoning-language>中文</reasoning-language>\n你是谁"; |
| 12 | const visible = (raw: Record<string, unknown>) => canonicalMessage(persistent, raw); |
| 13 | const items = (raw: Record<string, unknown>) => historyMessagesToItems([visible(raw)], "test").items; |
| 14 | |
| 15 | // The existing Go display vocabulary remains authoritative for host wrappers. |
| 16 | const goSource = readFileSync(new URL("../../../../internal/agent/preview.go", import.meta.url), "utf8"); |
| 17 | const goTags = [...goSource.match(/var TransientUserBlockTags = \[\]string\{([\s\S]*?)\n\}/)![1].matchAll(/"([\w-]+)"/g)].map(match => match[1]); |
| 18 | assert.deepEqual([...transientUserTags], goTags); |
| 19 | for (const tag of transientUserTags) { |
| 20 | assert.equal(visible({ role: "user", content: `<${tag} version="1">internal</${tag}>\n你是谁` }).content, "你是谁"); |
| 21 | } |
| 22 | assert.equal(visible({ role: "user", content: wrapped }).content, "你是谁"); |
| 23 | assert.equal(visible({ role: "user", content: wrapped, raw_content: "用户原文" }).content, "用户原文"); |
| 24 | assert.equal(visible({ role: "user", raw_content: "只有原文" }).content, "只有原文"); |
| 25 | for (const raw of [ |
| 26 | { role: "user", origin: "host", content: context }, |
| 27 | { role: "user", origin: "host", content: "background job protocol", raw_content: "also internal" }, |
| 28 | { role: "user", content: context }, |
| 29 | { role: "user", content: "<response-language>internal only</response-language>" }, |
| 30 | ]) assert.equal(items(raw).length, 0, JSON.stringify(raw)); |
| 31 | for (const literal of [context, "<response-language>user example</response-language>", "```xml\n" + context + "\n```", "Explain <reasoning-language> in XML"]) { |
| 32 | assert.equal(visible({ role: "user", origin: "user", content: wrapped, raw_content: literal }).content, literal); |
| 33 | } |
| 34 | for (const role of ["assistant", "tool"]) assert.equal(canonicalMessage({ ...persistent, role }, { role, content: context }).content, context); |
| 35 | const steerPrefix = "[Mid-turn steer queued by the user. Do not treat this as a new task; use it only as additional guidance for the current task after completing the current step.]"; |
| 36 | assert.equal(visible({ role: "user", content: `${steerPrefix}\n补充说明` }).content, "↪ 补充说明"); |
| 37 | assert.equal(visible({ role: "user", content: `${steerPrefix}\n补充说明` }).role, "notice"); |
| 38 | assert.equal(items({ role: "user", content: `${steerPrefix}\nA tool failed. Use read-only diagnosis as needed` }).length, 0); |
| 39 | const compiler = `<memory-compiler-execution>${JSON.stringify({ planner_ir: { source_event: wrapped } })}</memory-compiler-execution>`; |
| 40 | assert.equal(visible({ role: "user", content: compiler }).content, "你是谁"); |
| 41 | for (const suffix of ["<execution-policy>policy</execution-policy>", "<memory-recall>memory</memory-recall>"]) { |
| 42 | assert.equal(visible({ role: "user", content: `你是谁\n${suffix}` }).content, "你是谁"); |
| 43 | } |
| 44 | assert.equal(visible({ role: "user", content: `# Reasonix executor handoff\n\nOriginal task:\n${wrapped}\n\nPlanner output:\nprivate plan` }).content, "你是谁"); |
| 45 | |
| 46 | // Lazy full-body hydration must use the same projection as inline history. |
| 47 | for (const [origin, expected] of [["host", ""], ["user", "你是谁"]]) { |
| 48 | const raw = { id: persistent.messageId, role: "user", origin, content: `<capability-route>${"x".repeat(40000)}</capability-route>\n你是谁`, raw_content: origin === "user" ? "你是谁" : undefined }; |
| 49 | const original = JSON.stringify(raw); |
| 50 | const ref: HistoryContentRef = { entryId: "m:user-1", field: "canonicalMessage", size: original.length, chunks: 1, revision: 1, digest: "fixture" }; |
| 51 | const record = entryToRecord({ entryId: "m:user-1", turn: 1, order: 2, message: canonicalMessage({ ...persistent, preview: expected }, undefined), refs: [ref] }); |
| 52 | const data = [...new TextEncoder().encode(original)].map(byte => String.fromCharCode(byte)).join(""); |
| 53 | assert.equal(applyResolvedField(record, ref, data), true); |
| 54 | assert.equal(record.message.content, expected); |
| 55 | assert.equal(record.message.messageId, persistent.messageId); |
| 56 | assert.equal(JSON.stringify(raw), original, "display never mutates source content"); |
| 57 | assert.equal(historyMessagesToItems([record.message], "test").items.length, origin === "host" ? 0 : 1); |
| 58 | } |
| 59 | console.log("PASS authored chat text, host filtering, literal markup, legacy wrappers, steers and lazy hydration"); |
| 60 |