@jjh32456/dsh-longlive 1.6.7 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/CHANGELOG.md +70 -1508
  2. package/LICENSE +1 -1
  3. package/QUICKSTART.md +34 -13
  4. package/README.en.md +18 -7
  5. package/README.md +34 -9
  6. package/SPEC.md +5 -5
  7. package/cordis.patch.yml +23 -9
  8. package/docs/ANSWER-GUARD.md +92 -0
  9. package/docs/ARCHITECTURE.md +4 -3
  10. package/docs/AUTO-RECALL-WIRING.md +1 -1
  11. package/docs/AUTO-RECALL.md +15 -9
  12. package/docs/CONFIG.md +168 -35
  13. package/docs/ERRORS.md +17 -5
  14. package/docs/FAQ.md +7 -7
  15. package/docs/FEATURES.md +96 -28
  16. package/docs/MIGRATION-1.6.0.md +2 -2
  17. package/docs/OPERATIONS.md +6 -6
  18. package/docs/PANEL-INTEGRATION.md +6 -0
  19. package/docs/PRIVACY.md +88 -0
  20. package/docs/QUICKSTART.md +5 -5
  21. package/docs/README.en.md +4 -2
  22. package/docs/RESIDENT-WATCHER.md +1 -1
  23. package/docs/SESSION-WATCH.md +6 -6
  24. package/docs/TROUBLESHOOTING.md +4 -4
  25. package/package.json +47 -117
  26. package/runbook.md +2 -2
  27. package/scripts/backfill-auto-cards.mjs +98 -0
  28. package/scripts/install-surgery-task.ps1 +5 -2
  29. package/scripts/lib/session-watch-fixture.mjs +1 -1
  30. package/scripts/longlive-doctor.mjs +6 -0
  31. package/scripts/migrate-180.mjs +163 -0
  32. package/scripts/recall-index-build.mjs +128 -0
  33. package/scripts/session-watch-drill.mjs +1 -1
  34. package/scripts/surgery-run.cmd +1 -1
  35. package/scripts/unattended-surgery.ps1 +5 -2
  36. package/scripts/verify-card-anchors.mjs +138 -0
  37. package/src/actions/audit.js +125 -0
  38. package/src/actions/evidence.js +122 -0
  39. package/src/actions/fs.js +162 -0
  40. package/src/activity/index.js +760 -0
  41. package/src/activity/model.js +351 -0
  42. package/src/activity/store.js +644 -0
  43. package/src/activity/thinking.js +176 -0
  44. package/src/adapters/constants.js +11 -0
  45. package/src/adapters/engram/data/aliases.json +51 -0
  46. package/src/adapters/engram/data/policy.json +8 -0
  47. package/src/adapters/engram/data/routing-rules.json +546 -0
  48. package/src/adapters/engram/index.js +235 -0
  49. package/src/adapters/registry.js +315 -0
  50. package/src/answer/audit.js +193 -0
  51. package/src/answer/evidence.js +30 -0
  52. package/src/answer/guard.js +1370 -0
  53. package/src/answer/index.js +1067 -0
  54. package/src/answer/query.js +111 -0
  55. package/src/answer/second-hop.mjs +101 -0
  56. package/src/auto-truncate.js +1 -1
  57. package/src/config.js +171 -47
  58. package/src/deps.js +6 -4
  59. package/src/features.js +182 -6
  60. package/src/heartbeat.js +156 -23
  61. package/src/identity/handover.js +52 -1
  62. package/src/identity/index.js +13 -5
  63. package/src/identity/inject-position.js +2 -2
  64. package/src/index.js +97 -7
  65. package/src/lifecycle/gap-clocks.js +328 -0
  66. package/src/lifecycle/index.js +269 -0
  67. package/src/live.js +22 -2
  68. package/src/memory/activity-capture.js +229 -0
  69. package/src/memory/anchors.js +194 -0
  70. package/src/memory/audit-records.js +222 -0
  71. package/src/memory/auto-cards.js +401 -0
  72. package/src/memory/capture.js +442 -0
  73. package/src/memory/card-anchors.js +203 -0
  74. package/src/memory/conflicts.js +106 -0
  75. package/src/memory/decay.js +311 -0
  76. package/src/memory/distill.js +471 -0
  77. package/src/memory/event-index.js +917 -0
  78. package/src/memory/evidence-chain.js +295 -0
  79. package/src/memory/evidence.js +145 -0
  80. package/src/memory/filter-runner.js +1 -1
  81. package/src/memory/filter.js +5 -1
  82. package/src/memory/forget-v2.js +410 -0
  83. package/src/memory/forget.js +71 -0
  84. package/src/memory/frames-activation.js +247 -0
  85. package/src/memory/fts-query.js +174 -0
  86. package/src/memory/governance.js +604 -0
  87. package/src/memory/index.js +1 -0
  88. package/src/memory/l2-gate.js +169 -0
  89. package/src/memory/lock.js +121 -0
  90. package/src/memory/migrate.js +139 -0
  91. package/src/memory/recall-v2.js +223 -0
  92. package/src/memory/recall.js +65 -2
  93. package/src/memory/records.js +425 -0
  94. package/src/memory/retention.js +128 -0
  95. package/src/memory/review.js +124 -0
  96. package/src/memory/trust.js +88 -0
  97. package/src/memory/version-chain.js +264 -0
  98. package/src/memory/wal.js +137 -0
  99. package/src/memory/working-frame.js +516 -0
  100. package/src/metrics/classify.mjs +5 -3
  101. package/src/metrics/index.mjs +21 -1
  102. package/src/metrics/prestep-latency.mjs +300 -0
  103. package/src/metrics/store.mjs +25 -0
  104. package/src/policy-profile.js +18 -3
  105. package/src/recall/anaphora.mjs +87 -0
  106. package/src/recall/cards.mjs +7 -0
  107. package/src/recall/embedding.mjs +8 -8
  108. package/src/recall/entities.mjs +112 -0
  109. package/src/recall/index.js +235 -19
  110. package/src/recall/inject-order.mjs +10 -8
  111. package/src/recall/privacy.mjs +105 -16
  112. package/src/recall/recall-core.mjs +116 -10
  113. package/src/recall/shared-visibility.js +302 -0
  114. package/src/recall/time-normalizer.mjs +196 -0
  115. package/src/role/activation.js +191 -0
  116. package/src/role/attention.js +318 -0
  117. package/src/role/base.js +36 -0
  118. package/src/role/channels.js +423 -0
  119. package/src/role/index.js +425 -0
  120. package/src/role/interfaces.js +115 -0
  121. package/src/role/personality.js +370 -0
  122. package/src/role/readers.js +151 -0
  123. package/src/role/relationships.js +477 -0
  124. package/src/role/store.js +202 -0
  125. package/src/role/writeprotect.js +373 -0
  126. package/src/router/aliases.json +176 -25
  127. package/src/router/background/agent-runner.mjs +1 -1
  128. package/src/router/background/anchor.mjs +1 -1
  129. package/src/router/background/audit.mjs +2 -2
  130. package/src/router/background/budget-ledger.mjs +2 -2
  131. package/src/router/background/host-sim.mjs +1 -1
  132. package/src/router/background/memory.mjs +4 -4
  133. package/src/router/background/queue.mjs +1 -1
  134. package/src/router/background/retry.mjs +1 -1
  135. package/src/router/background/router.mjs +1 -1
  136. package/src/router/background/stream.mjs +1 -1
  137. package/src/router/background/tool-policy.mjs +1 -1
  138. package/src/router/design-headings.json +5 -5
  139. package/src/router/face/config.mjs +1 -1
  140. package/src/router/face/identity.mjs +3 -3
  141. package/src/router/judge.mjs +8 -7
  142. package/src/router/policies/generic.json +21 -4
  143. package/src/router/route.js +109 -22
  144. package/src/router/router.mjs +10 -6
  145. package/src/router/routing-rules.json +260 -445
  146. package/src/router/run-sim.mjs +8 -8
  147. package/src/router/runtime.js +23 -12
  148. package/src/router/service/board.mjs +95 -15
  149. package/src/router/service/config.mjs +5 -5
  150. package/src/router/service/context.mjs +1 -1
  151. package/src/router/service/hint.mjs +14 -0
  152. package/src/router/service/queue.mjs +1 -1
  153. package/src/router/service/service.mjs +27 -11
  154. package/src/router/shadow.mjs +4 -3
  155. package/src/surgery-recovery.js +2 -2
  156. package/src/tool-output.js +2 -2
  157. package/src/tools.js +39 -7
  158. package/src/truncate-visible.js +2 -2
  159. package/src/ui/actions.js +1312 -0
  160. package/src/ui/panel-api.js +367 -0
  161. package/src/ui/panel-auth.js +251 -0
  162. package/src/ui/panel-integrity.js +243 -0
  163. package/src/ui/routes-readonly.js +695 -0
  164. package/src/ui/routes.js +3 -0
  165. package/docs/DEV.md +0 -279
  166. package/docs/PRESENCE-BUS-WIRING.md +0 -491
  167. package/docs/PRESENCE-BUS.md +0 -694
  168. package/docs/SURGERY.md +0 -186
  169. package/scripts/mark-core-cards.mjs +0 -289
  170. package/scripts/pack.mjs +0 -392
  171. package/scripts/router-proposals.mjs +0 -386
  172. package/src/router/fixtures/M1.json +0 -16
  173. package/src/router/fixtures/M2.json +0 -16
  174. package/src/router/fixtures/M3.json +0 -20
  175. package/src/router/fixtures/M4.json +0 -20
  176. package/src/router/fixtures/M5.json +0 -45
  177. package/src/router/fixtures/M6.json +0 -53
  178. package/src/router/fixtures/M7.json +0 -46
  179. package/src/router/fixtures/S1.json +0 -20
  180. package/src/router/fixtures/S2.json +0 -23
  181. package/src/router/fixtures/S3.json +0 -20
  182. package/src/router/fixtures/S4.json +0 -34
  183. package/src/router/fixtures/S5.json +0 -43
  184. package/src/router/fixtures/S6.json +0 -47
  185. package/src/router/fixtures/T1.json +0 -23
  186. package/src/router/fixtures/T2.json +0 -16
  187. package/src/router/fixtures/T3.json +0 -19
  188. package/src/router/fixtures/T4.json +0 -16
  189. package/src/router/fixtures/X1.json +0 -17
  190. package/src/router/fixtures/X2.json +0 -20
  191. package/src/router/gen-design-headings.mjs +0 -107
  192. package/src/router/policies/owner-rulings-2026-11-03.json +0 -62
  193. package/test/auto-recall.test.mjs +0 -474
  194. package/test/auto-truncate.test.mjs +0 -931
  195. package/test/autobind.test.mjs +0 -385
  196. package/test/bus.test.mjs +0 -1572
  197. package/test/cli-guard.test.mjs +0 -198
  198. package/test/conclusion.test.mjs +0 -408
  199. package/test/config-schema.test.mjs +0 -204
  200. package/test/d6-output.test.mjs +0 -194
  201. package/test/d6.test.mjs +0 -395
  202. package/test/deps.test.mjs +0 -342
  203. package/test/doctor-setup.test.mjs +0 -297
  204. package/test/embedding-recall.test.mjs +0 -347
  205. package/test/feature-flags.test.mjs +0 -568
  206. package/test/filter-shadow.test.mjs +0 -170
  207. package/test/fixtures/baseline-0.8.0-registration.json +0 -556
  208. package/test/fixtures/d6-header-tools.json +0 -1
  209. package/test/fixtures/lock-child.mjs +0 -67
  210. package/test/fixtures/pre-step-container.mjs +0 -100
  211. package/test/fixtures/privacy-memory/topics/pii-unmarked.md +0 -9
  212. package/test/fixtures/privacy-memory/topics/secret-local-only.md +0 -11
  213. package/test/fixtures/profile-session-longlive-config.json +0 -20
  214. package/test/fixtures/proposal-cas-child.mjs +0 -66
  215. package/test/host-engine-status.test.mjs +0 -307
  216. package/test/hot-truncate.test.mjs +0 -1349
  217. package/test/identity-card-marker.test.mjs +0 -307
  218. package/test/identity-router/run.mjs +0 -721
  219. package/test/integration-pre-step.test.mjs +0 -553
  220. package/test/live.test.mjs +0 -1020
  221. package/test/maintenance-schedule.test.mjs +0 -538
  222. package/test/memory.test.mjs +0 -2828
  223. package/test/metrics.test.mjs +0 -478
  224. package/test/orchestrator.test.mjs +0 -266
  225. package/test/p1_probe.mjs +0 -97
  226. package/test/packaging.test.mjs +0 -182
  227. package/test/portability.test.mjs +0 -343
  228. package/test/rc4-regressions.test.mjs +0 -562
  229. package/test/rc5-regressions.test.mjs +0 -197
  230. package/test/rc6-chinese.test.mjs +0 -394
  231. package/test/router-1100/apply-off.test.mjs +0 -175
  232. package/test/router-1100/budget-reconcile.test.mjs +0 -215
  233. package/test/router-1100/config-context.test.mjs +0 -211
  234. package/test/router-1100/helpers.mjs +0 -129
  235. package/test/router-1100/hint-envelope.test.mjs +0 -150
  236. package/test/router-1100/lock-crossproc.test.mjs +0 -206
  237. package/test/router-1100/proposal-cas-crossproc.test.mjs +0 -184
  238. package/test/router-1100/router-apply.test.mjs +0 -372
  239. package/test/router-1100/router-fixb.test.mjs +0 -602
  240. package/test/router-1100/router-proposals.test.mjs +0 -405
  241. package/test/router-1100/router-queue-lease-wal.test.mjs +0 -355
  242. package/test/router-1100/router-security-cr.test.mjs +0 -333
  243. package/test/router-1100/service-lifecycle.test.mjs +0 -220
  244. package/test/router-1100/tools-contract.test.mjs +0 -164
  245. package/test/router-1100/turn-reflect-tick.test.mjs +0 -89
  246. package/test/router-1100/v6-contract.test.mjs +0 -158
  247. package/test/router-1100/v6b-contract.test.mjs +0 -293
  248. package/test/router-1100/v6c-contract.test.mjs +0 -249
  249. package/test/router-1100/wal-rotation.test.mjs +0 -375
  250. package/test/router-1100/wp1-config.test.mjs +0 -170
  251. package/test/router-1100/wp1-default-off.test.mjs +0 -77
  252. package/test/router-1100/wp1-model-status.test.mjs +0 -90
  253. package/test/router-1100/wp1-status.test.mjs +0 -220
  254. package/test/router-1100/wp1-tools.test.mjs +0 -146
  255. package/test/router-1100/wp5-cli.test.mjs +0 -240
  256. package/test/router-1100/wp5-model-auto.test.mjs +0 -418
  257. package/test/router-1100/wp6-merge.test.mjs +0 -386
  258. package/test/router-1100/wp6-wake.test.mjs +0 -502
  259. package/test/router-background/audit.test.mjs +0 -37
  260. package/test/router-background/budget-ledger.test.mjs +0 -330
  261. package/test/router-background/cross-process.test.mjs +0 -222
  262. package/test/router-background/fixtures/cross-child.mjs +0 -85
  263. package/test/router-background/fixtures/real-shapes.mjs +0 -239
  264. package/test/router-background/router.test.mjs +0 -1044
  265. package/test/router-background/stream-retry.test.mjs +0 -358
  266. package/test/router-background/tool-agent.test.mjs +0 -384
  267. package/test/router-face/bridge.test.mjs +0 -169
  268. package/test/router-face/face.test.mjs +0 -316
  269. package/test/router-failclosed.test.mjs +0 -466
  270. package/test/router-identity.test.mjs +0 -384
  271. package/test/router-l1-l2.contract.test.mjs +0 -1075
  272. package/test/router-memory-tools.test.mjs +0 -262
  273. package/test/router-policy.test.mjs +0 -225
  274. package/test/router-runtime.test.mjs +0 -438
  275. package/test/router-shadow.test.mjs +0 -287
  276. package/test/router.test.mjs +0 -903
  277. package/test/run-rc.mjs +0 -299
  278. package/test/run.mjs +0 -2469
  279. package/test/safety-defaults.test.mjs +0 -179
  280. package/test/seeded-load.test.mjs +0 -436
  281. package/test/session-original.test.mjs +0 -338
  282. package/test/surgery-recovery.test.mjs +0 -936
  283. package/test/t7_drive.mjs +0 -151
  284. package/test/tool-output.test.mjs +0 -393
  285. package/test/tool-schema.test.mjs +0 -177
  286. package/test/tools_contract.test.mjs +0 -716
  287. package/test/truncate-visible.test.mjs +0 -1150
  288. package/test/ui-flags.test.mjs +0 -477
  289. package/test/v4-hardening.test.mjs +0 -427
@@ -1,170 +0,0 @@
1
- // 1.2.0 acceptance: the two passive observers write the panel contract shape.
2
- //
3
- // These are integration tests over real files in a temp memoryRoot. They never
4
- // touch the owner's real memory root, never call an LLM, and never import a
5
- // host package.
6
-
7
- import test from 'node:test';
8
- import assert from 'node:assert/strict';
9
- import fs from 'node:fs';
10
- import os from 'node:os';
11
- import path from 'node:path';
12
-
13
- import { runFilter, parseJournal } from '../src/memory/filter-runner.js';
14
- import { createRouterRuntime } from '../src/router/runtime.js';
15
-
16
- const NOW = new Date('2026-11-05T10:00:00+08:00');
17
- const SECRET = 'password: abcdef123456';
18
-
19
- const JOURNAL = [
20
- '\uFEFF---',
21
- 'title: 2026-11',
22
- 'date: 2026-11-05',
23
- '---',
24
- '',
25
- '## 2026-11-05 · 过滤器与影子验收',
26
- '',
27
- '老大要求过滤器只读记忆文件,写 decisions jsonl,异常只记 warning,绝不阻断工具调用。',
28
- '',
29
- '- **影子记录必须只记录不执行**:route() 与 guard() 的判定追加到 router-runtime.jsonl,工具原值原样返回。',
30
- '',
31
- SECRET,
32
- '',
33
- ].join('\n');
34
-
35
- function tempMemory() {
36
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'longlive-filter-shadow-'));
37
- fs.mkdirSync(path.join(root, 'journal'), { recursive: true });
38
- fs.mkdirSync(path.join(root, 'topics'), { recursive: true });
39
- fs.writeFileSync(path.join(root, 'journal', '2026-11.md'), JOURNAL, 'utf8');
40
- fs.writeFileSync(
41
- path.join(root, 'topics', 'card.md'),
42
- '---\ntitle: card\n---\n\n影子记录是只读审计,不改变工具行为。\n',
43
- 'utf8',
44
- );
45
- return root;
46
- }
47
-
48
- function readJsonl(file) {
49
- return fs.readFileSync(file, 'utf8').split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line));
50
- }
51
-
52
- test('parseJournal: BOM is stripped and line numbers point at the journal body', () => {
53
- const units = parseJournal(JOURNAL);
54
- assert.equal(units.length, 3);
55
- assert.equal(units[0].lineStart, 8);
56
- assert.equal(units[0].lineEnd, 8);
57
- assert.equal(units[0].heading, '2026-11-05 · 过滤器与影子验收');
58
- assert.equal(units[0].date, '2026-11-05');
59
- assert.equal(units[1].lineStart, 10);
60
- });
61
-
62
- test('filter: writes decisions jsonl with every §5 field and never echoes a secret', () => {
63
- const root = tempMemory();
64
- const result = runFilter({ memoryRoot: root, now: NOW });
65
- assert.equal(result.ok, true);
66
- assert.equal(result.units, 3);
67
- assert.equal(result.appended, 3);
68
-
69
- const file = path.join(root, '.longlive', 'filter', 'decisions-2026-11-05.jsonl');
70
- assert.equal(fs.existsSync(file), true);
71
- const rows = readJsonl(file);
72
- assert.equal(rows.length, 3);
73
-
74
- const allowed = new Set(['remember', 'archive_only', 'human', 'local_only']);
75
- for (const row of rows) {
76
- for (const key of ['ts', 'unitId', 'turn', 'target', 'score', 'dims', 'hardSignals', 'privacy',
77
- 'needsHuman', 'human', 'source', 'excerpt', 'dedup']) {
78
- assert.equal(Object.prototype.hasOwnProperty.call(row, key), true, `missing ${key}`);
79
- }
80
- assert.match(row.ts, /^\d{4}-\d{2}-\d{2}T/);
81
- assert.match(row.unitId, /^[0-9a-f]{16}$/);
82
- assert.equal(row.turn, null);
83
- assert.equal(allowed.has(row.target), true);
84
- assert.equal(typeof row.score, 'number');
85
- assert.equal(row.dims.length, 8);
86
- assert.deepEqual(row.dims.map((dim) => dim.id), ['R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8']);
87
- for (const dim of row.dims) {
88
- assert.equal(typeof dim.name, 'string');
89
- assert.equal(typeof dim.score, 'number');
90
- assert.equal(typeof dim.max, 'number');
91
- assert.equal(typeof dim.why, 'string');
92
- }
93
- assert.equal(typeof row.privacy.verdict, 'string');
94
- assert.equal(typeof row.needsHuman, 'boolean');
95
- assert.deepEqual(Object.keys(row.human).sort(), ['note', 'verdict']);
96
- assert.equal(row.source.file, 'journal/2026-11.md');
97
- assert.equal(Number.isInteger(row.source.lineStart), true);
98
- assert.equal(Number.isInteger(row.source.lineEnd), true);
99
- assert.equal(row.excerpt, null);
100
- assert.equal(typeof row.dedup.delta, 'number');
101
- assert.equal(row.dedup.delta === -3 || row.dedup.delta === 0 || row.dedup.delta === 1, true);
102
- if (row.dedup.nearestUnitId !== null) assert.equal(typeof row.dedup.nearestUnitId, 'string');
103
- }
104
-
105
- const blocked = rows.find((row) => row.target === 'local_only');
106
- assert.ok(blocked, 'the privacy paragraph must become local_only');
107
- assert.equal(blocked.privacy.verdict, 'blocked');
108
- assert.equal(blocked.privacy.marker, 'V1_PRIVACY_BLOCK');
109
- assert.equal(blocked.excerpt, null);
110
- assert.equal(JSON.stringify(blocked).includes('abcdef123456'), false, 'secret leaked into the audit row');
111
-
112
- // Audit trail exists too; the panel only needs decisions-*.jsonl.
113
- assert.equal(fs.existsSync(path.join(root, '.longlive', 'filter', 'queue', '2026-11-05.json')), true);
114
- assert.equal(fs.existsSync(path.join(root, '.longlive', 'filter', 'state.json')), true);
115
- const runs = fs.readdirSync(path.join(root, '.longlive', 'filter', 'runs'));
116
- assert.equal(runs.length, 1);
117
- const run = JSON.parse(fs.readFileSync(path.join(root, '.longlive', 'filter', 'runs', runs[0]), 'utf8'));
118
- assert.equal(run.schemaVersion, 'maf-1');
119
- assert.equal(run.policy.version, 'maf-2');
120
- assert.equal(run.units.length, 3);
121
- assert.equal(run.counts.remember + run.counts.archive_only + run.counts.human + run.counts.local_only, 3);
122
-
123
- // Same day, second startup: nothing is appended twice.
124
- const again = runFilter({ memoryRoot: root, now: NOW });
125
- assert.equal(again.ok, true);
126
- assert.equal(again.appended, 0);
127
- assert.equal(readJsonl(file).length, 3);
128
- });
129
-
130
- test('shadow: recordAllTools writes the §6 contract fields and returns the tool value unchanged', async () => {
131
- const root = tempMemory();
132
- const runtime = createRouterRuntime({ mode: 'shadow', memoryRoot: root, recordAllTools: true });
133
- const expected = { ok: true, status: 'healthy' };
134
- const value = await runtime.intercept('session_health', { sessionId: 'test' }, async () => expected);
135
- assert.equal(value, expected, 'the exact object comes back');
136
-
137
- const file = path.join(root, '.longlive', 'router-runtime.jsonl');
138
- assert.equal(fs.existsSync(file), true);
139
- const [row] = readJsonl(file);
140
- assert.equal(row.mode, 'shadow');
141
- assert.equal(row.tool, 'session_health');
142
- assert.equal(row.subject, 'session_health');
143
- assert.equal(['allow', 'deny', 'ask'].includes(row.action), true);
144
- assert.equal(row.action, 'allow');
145
- assert.equal(row.status, 'ok');
146
- assert.equal(typeof row.callText, 'string');
147
- assert.equal(Array.isArray(row.errors), true);
148
- assert.equal(row.guard.verdict, 'allow');
149
- assert.equal(row.outcome.status, 'ok');
150
- assert.equal(typeof row.totalMs, 'number');
151
- assert.equal(typeof row.decision, 'object');
152
- assert.equal(fs.existsSync(path.join(root, 'router-runtime.jsonl')), false, 'must not write the legacy top-level path');
153
- });
154
-
155
- test('shadow: a failing record sink never changes the tool call', async () => {
156
- const root = tempMemory();
157
- // Make `.longlive` a file: creating the record directory must fail, but the
158
- // tool result is still returned and the error is swallowed into onError.
159
- fs.writeFileSync(path.join(root, '.longlive'), 'not a directory', 'utf8');
160
- const errors = [];
161
- const runtime = createRouterRuntime({
162
- mode: 'shadow',
163
- memoryRoot: root,
164
- recordAllTools: true,
165
- onError: (error) => errors.push(error),
166
- });
167
- const value = await runtime.intercept('memory_recall', { query: '过滤器' }, async () => 'cards');
168
- assert.equal(value, 'cards');
169
- assert.equal(errors.length > 0, true);
170
- });