@remnic/core 9.4.2 → 9.6.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 (379) hide show
  1. package/dist/access-admin-ops-surface.d.ts +6 -6
  2. package/dist/access-admin-ops-surface.js +28 -28
  3. package/dist/access-boundary.d.ts +27 -8
  4. package/dist/access-boundary.js +33 -29
  5. package/dist/access-cli.js +83 -81
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-http.d.ts +52 -6
  8. package/dist/access-http.js +40 -38
  9. package/dist/access-identity-continuity-surface.d.ts +5 -5
  10. package/dist/access-identity-continuity-surface.js +28 -28
  11. package/dist/access-lcm-surface.d.ts +6 -6
  12. package/dist/access-lcm-surface.js +28 -28
  13. package/dist/access-mcp.d.ts +16 -6
  14. package/dist/access-mcp.js +37 -35
  15. package/dist/access-observe-write-surface.d.ts +6 -6
  16. package/dist/access-observe-write-surface.js +28 -28
  17. package/dist/access-operations-batch.js +33 -32
  18. package/dist/access-operations.d.ts +9 -9
  19. package/dist/access-operations.js +35 -34
  20. package/dist/access-recall-surface.d.ts +6 -6
  21. package/dist/access-recall-surface.js +28 -28
  22. package/dist/access-schema.d.ts +4 -4
  23. package/dist/access-schema.js +4 -4
  24. package/dist/{access-service-m-2VO_Ig.d.ts → access-service-CXJaql3A.d.ts} +4 -4
  25. package/dist/access-service.d.ts +6 -6
  26. package/dist/access-service.js +28 -28
  27. package/dist/access-surface-catalog.d.ts +6 -6
  28. package/dist/access-surface-catalog.js +1 -0
  29. package/dist/access-surface-catalog.js.map +1 -1
  30. package/dist/access-token-capabilities.d.ts +174 -0
  31. package/dist/access-token-capabilities.js +33 -0
  32. package/dist/action-confidence.d.ts +1 -1
  33. package/dist/active-memory-bridge.d.ts +1 -1
  34. package/dist/active-recall.d.ts +1 -1
  35. package/dist/active-recall.js +2 -2
  36. package/dist/adapters/index.js +4 -4
  37. package/dist/adapters/registry.js +2 -2
  38. package/dist/{auto-sync-HMHQHUAG.js → auto-sync-4A5YBWYO.js} +4 -4
  39. package/dist/behavior-learner.d.ts +1 -1
  40. package/dist/behavior-signals.d.ts +1 -1
  41. package/dist/bootstrap.d.ts +5 -5
  42. package/dist/briefing.d.ts +2 -2
  43. package/dist/briefing.js +9 -9
  44. package/dist/buffer-surprise-report.d.ts +1 -1
  45. package/dist/buffer.d.ts +2 -2
  46. package/dist/calibration.d.ts +1 -1
  47. package/dist/capabilities.d.ts +1 -1
  48. package/dist/{capsule-crypto-CZJSLEFG.js → capsule-crypto-RAARDY3M.js} +2 -2
  49. package/dist/capsule-crypto-RAARDY3M.js.map +1 -0
  50. package/dist/{catalog-BxWokdC2.d.ts → catalog-C9cu67Ig.d.ts} +1 -1
  51. package/dist/causal-behavior.d.ts +1 -1
  52. package/dist/causal-behavior.js +5 -5
  53. package/dist/causal-chain.js +3 -3
  54. package/dist/causal-consolidation.d.ts +1 -1
  55. package/dist/causal-consolidation.js +13 -13
  56. package/dist/causal-retrieval.js +3 -3
  57. package/dist/causal-trajectory-graph.d.ts +1 -1
  58. package/dist/causal-trajectory.js +1 -1
  59. package/dist/{chunk-6ZIETXZ2.js → chunk-23JFRB73.js} +2 -2
  60. package/dist/{chunk-IC6QX5TC.js → chunk-3XIY7MDQ.js} +6 -6
  61. package/dist/{chunk-FZGGZAAU.js → chunk-4GNEDXDI.js} +146 -2
  62. package/dist/chunk-4GNEDXDI.js.map +1 -0
  63. package/dist/{chunk-LOR6ZM5R.js → chunk-4LV23CHK.js} +2 -2
  64. package/dist/{chunk-E2SPGGUI.js → chunk-4OKLXWDZ.js} +13 -6
  65. package/dist/chunk-4OKLXWDZ.js.map +1 -0
  66. package/dist/{chunk-AER6MT24.js → chunk-6SXVCD7W.js} +2 -2
  67. package/dist/{chunk-OHDJ34CC.js → chunk-72WG5QKN.js} +2 -2
  68. package/dist/{chunk-CZAZX7JP.js → chunk-7MW3CVLD.js} +4 -4
  69. package/dist/{chunk-CXLNZXG5.js → chunk-7XGSIUP5.js} +1 -1
  70. package/dist/{chunk-CZAA5N5S.js → chunk-AYZJID4S.js} +2 -2
  71. package/dist/{chunk-M7XQSUBB.js → chunk-B3ABVQ36.js} +116 -9
  72. package/dist/chunk-B3ABVQ36.js.map +1 -0
  73. package/dist/{chunk-6BISDEN6.js → chunk-BEM4D2QG.js} +14 -14
  74. package/dist/{chunk-ZPOROUTX.js → chunk-CYWA2CXR.js} +5 -5
  75. package/dist/{chunk-M5QKGHCR.js → chunk-CYWCKFCL.js} +5 -5
  76. package/dist/chunk-D7FOKIY5.js +173 -0
  77. package/dist/chunk-D7FOKIY5.js.map +1 -0
  78. package/dist/{chunk-4BFRBP45.js → chunk-DEG5ULFJ.js} +2 -2
  79. package/dist/{chunk-JLR3JSTL.js → chunk-DS7MM2ES.js} +3 -3
  80. package/dist/{chunk-2BFLDH5F.js → chunk-EEIROWEG.js} +2 -2
  81. package/dist/{chunk-5QZDMSSN.js → chunk-FKCRGLRD.js} +237 -82
  82. package/dist/{chunk-5QZDMSSN.js.map → chunk-FKCRGLRD.js.map} +1 -1
  83. package/dist/{chunk-X4A62ET7.js → chunk-H2DXDQCQ.js} +17 -17
  84. package/dist/{chunk-27FLVMDC.js → chunk-HLN7RROI.js} +7 -7
  85. package/dist/chunk-IC2TCZJ2.js +54 -0
  86. package/dist/chunk-IC2TCZJ2.js.map +1 -0
  87. package/dist/{chunk-2U2A5HF4.js → chunk-J3XHPFEU.js} +6 -6
  88. package/dist/{chunk-AKUCU7CI.js → chunk-JI5AVXYO.js} +5 -5
  89. package/dist/{chunk-KCJ5H6XQ.js → chunk-JSNVWNGX.js} +2 -2
  90. package/dist/{chunk-7Z2DZNWN.js → chunk-KL4UAXUY.js} +43 -2
  91. package/dist/{chunk-7Z2DZNWN.js.map → chunk-KL4UAXUY.js.map} +1 -1
  92. package/dist/{chunk-FLVL4L3L.js → chunk-KTULX4YO.js} +4 -4
  93. package/dist/{chunk-ILFJ7AVS.js → chunk-LADFWYHY.js} +35 -21
  94. package/dist/chunk-LADFWYHY.js.map +1 -0
  95. package/dist/{chunk-437S3G37.js → chunk-LNQUAJNC.js} +1 -1
  96. package/dist/{chunk-EPGWWCZF.js → chunk-LR5DZ56F.js} +6 -6
  97. package/dist/{chunk-YDSWRD33.js → chunk-N3JDIRBW.js} +4 -4
  98. package/dist/{chunk-OUMTBFSQ.js → chunk-NKCPUHCI.js} +5 -5
  99. package/dist/{chunk-7QWMTOIF.js → chunk-P454KXD6.js} +112 -34
  100. package/dist/chunk-P454KXD6.js.map +1 -0
  101. package/dist/{chunk-DR6PEJU3.js → chunk-PCLIWBGD.js} +44 -59
  102. package/dist/chunk-PCLIWBGD.js.map +1 -0
  103. package/dist/{chunk-BGTAL46Y.js → chunk-PNLFN2PZ.js} +2 -2
  104. package/dist/{chunk-P6SYWE4G.js → chunk-PVUJ6WVV.js} +10 -10
  105. package/dist/{chunk-YPEMNNNY.js → chunk-RGK27ELN.js} +6 -6
  106. package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
  107. package/dist/{chunk-66FLU5BT.js → chunk-SIRPLF54.js} +6 -6
  108. package/dist/{chunk-M7GGMGMZ.js → chunk-SVOHGUMI.js} +1202 -126
  109. package/dist/chunk-SVOHGUMI.js.map +1 -0
  110. package/dist/{chunk-HRCACVHL.js → chunk-TLUDFPZL.js} +2 -2
  111. package/dist/{chunk-2FOD2YY3.js → chunk-UFUKHGZD.js} +236 -103
  112. package/dist/chunk-UFUKHGZD.js.map +1 -0
  113. package/dist/{chunk-2MR3MFQB.js → chunk-UPSAT4PY.js} +2 -2
  114. package/dist/{chunk-4P4626RY.js → chunk-UTRJFDP2.js} +4 -4
  115. package/dist/{chunk-CGS7UKWM.js → chunk-VCW74PNH.js} +2 -2
  116. package/dist/{chunk-5GPPACXK.js → chunk-VSMRDBFT.js} +2 -1
  117. package/dist/{chunk-NG7YBCJO.js → chunk-XCL57M6Q.js} +4 -4
  118. package/dist/{chunk-CLX5VDNY.js → chunk-Y4RKAQZ7.js} +4 -4
  119. package/dist/{chunk-DGYCWCQ7.js → chunk-Y7UNNAZD.js} +2 -2
  120. package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
  121. package/dist/{chunk-XN3TUUX2.js → chunk-YQB32INI.js} +2 -2
  122. package/dist/{cli-EiBBOt3r.d.ts → cli-CqdPA6z9.d.ts} +3 -3
  123. package/dist/cli.d.ts +7 -7
  124. package/dist/cli.js +61 -59
  125. package/dist/compounding/engine.d.ts +2 -2
  126. package/dist/compounding/engine.js +9 -9
  127. package/dist/compounding/preference-consolidator.d.ts +1 -1
  128. package/dist/compression-optimizer.d.ts +1 -1
  129. package/dist/config.d.ts +1 -1
  130. package/dist/config.js +2 -2
  131. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  132. package/dist/connectors/codex-materialize-runner.js +9 -9
  133. package/dist/connectors/codex-materialize.d.ts +1 -1
  134. package/dist/connectors/index.d.ts +1 -1
  135. package/dist/connectors/index.js +13 -11
  136. package/dist/consolidation-provenance-check.d.ts +2 -2
  137. package/dist/consolidation-undo.d.ts +2 -2
  138. package/dist/contradiction/index.d.ts +2 -2
  139. package/dist/contradiction/index.js +4 -4
  140. package/dist/conversation-index/backend.d.ts +1 -1
  141. package/dist/conversation-index/backend.js +2 -2
  142. package/dist/conversation-index/chunker.d.ts +1 -1
  143. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  144. package/dist/conversation-index/indexer.d.ts +1 -1
  145. package/dist/conversation-index/search.d.ts +1 -1
  146. package/dist/dashboard-runtime.js +2 -2
  147. package/dist/day-summary.d.ts +1 -1
  148. package/dist/delinearize.d.ts +1 -1
  149. package/dist/direct-answer-wiring.d.ts +1 -1
  150. package/dist/direct-answer.d.ts +1 -1
  151. package/dist/embedding-fallback.d.ts +1 -1
  152. package/dist/enrichment/index.d.ts +1 -1
  153. package/dist/entity-retrieval.d.ts +2 -2
  154. package/dist/entity-retrieval.js +9 -9
  155. package/dist/entity-schema.d.ts +1 -1
  156. package/dist/explicit-capture.d.ts +5 -5
  157. package/dist/extraction-faithfulness.d.ts +1 -1
  158. package/dist/extraction-judge-telemetry.d.ts +1 -1
  159. package/dist/extraction-judge-training.d.ts +1 -1
  160. package/dist/extraction-judge.d.ts +1 -1
  161. package/dist/extraction.d.ts +1 -1
  162. package/dist/fallback-llm.d.ts +1 -1
  163. package/dist/{first-start-migration-DY6YCRC2.js → first-start-migration-VBJQL6OI.js} +5 -5
  164. package/dist/graph-dashboard-diff.d.ts +1 -1
  165. package/dist/graph-dashboard-key.d.ts +1 -1
  166. package/dist/graph-dashboard-parser.d.ts +1 -1
  167. package/dist/graph-edge-reinforcement.d.ts +1 -1
  168. package/dist/graph-snapshot.d.ts +1 -1
  169. package/dist/graph.d.ts +1 -1
  170. package/dist/identity-continuity.d.ts +1 -1
  171. package/dist/importance.d.ts +1 -1
  172. package/dist/index.d.ts +271 -17
  173. package/dist/index.js +163 -99
  174. package/dist/intent.d.ts +1 -1
  175. package/dist/lcm/engine.d.ts +1 -1
  176. package/dist/lcm/engine.js +2 -2
  177. package/dist/lcm/index.d.ts +1 -1
  178. package/dist/lcm/index.js +2 -2
  179. package/dist/lcm/tools.d.ts +1 -1
  180. package/dist/lifecycle.d.ts +1 -1
  181. package/dist/live-connectors-runner.d.ts +1 -1
  182. package/dist/local-llm.d.ts +1 -1
  183. package/dist/local-model-endpoint.d.ts +1 -1
  184. package/dist/maintenance/memory-governance.d.ts +1 -1
  185. package/dist/maintenance/memory-governance.js +10 -10
  186. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +9 -9
  187. package/dist/maintenance/rebuild-memory-projection.js +11 -11
  188. package/dist/mcp-memory-inspector-app.d.ts +6 -6
  189. package/dist/mcp-read-only-tools.d.ts +16 -0
  190. package/dist/mcp-read-only-tools.js +8 -0
  191. package/dist/mcp-read-only-tools.js.map +1 -0
  192. package/dist/memory-action-policy.d.ts +1 -1
  193. package/dist/memory-cache.d.ts +1 -1
  194. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  195. package/dist/memory-projection-store.d.ts +1 -1
  196. package/dist/memory-provenance.d.ts +1 -1
  197. package/dist/memory-worth-outcomes.d.ts +2 -2
  198. package/dist/models-json.d.ts +1 -1
  199. package/dist/namespaces/migrate.d.ts +3 -3
  200. package/dist/namespaces/migrate.js +19 -19
  201. package/dist/namespaces/principal.d.ts +1 -1
  202. package/dist/namespaces/search.d.ts +1 -1
  203. package/dist/namespaces/search.js +10 -10
  204. package/dist/namespaces/storage.d.ts +3 -3
  205. package/dist/namespaces/storage.js +9 -9
  206. package/dist/native-knowledge.d.ts +1 -1
  207. package/dist/operator-toolkit.d.ts +2 -2
  208. package/dist/operator-toolkit.js +25 -25
  209. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  210. package/dist/orchestration/maintenance.d.ts +2 -2
  211. package/dist/orchestration/maintenance.js +11 -11
  212. package/dist/{orchestrator-B2Y28Z9t.d.ts → orchestrator-CWFrT8AF.d.ts} +91 -60
  213. package/dist/orchestrator.d.ts +5 -5
  214. package/dist/orchestrator.js +85 -83
  215. package/dist/patterns-cli.d.ts +1 -1
  216. package/dist/policy-runtime.d.ts +1 -1
  217. package/dist/provenance.d.ts +1 -1
  218. package/dist/{qmd-Db9mOkzB.d.ts → qmd--fnPMK60.d.ts} +1 -1
  219. package/dist/qmd-preflight.d.ts +2 -2
  220. package/dist/qmd-recall-cache.d.ts +1 -1
  221. package/dist/qmd.d.ts +2 -2
  222. package/dist/recall-disclosure-escalation.d.ts +1 -1
  223. package/dist/recall-explain-renderer.d.ts +1 -1
  224. package/dist/recall-planner-llm.d.ts +1 -1
  225. package/dist/recall-state.d.ts +1 -1
  226. package/dist/recall-tag-filter.d.ts +1 -1
  227. package/dist/recall-timings.d.ts +1 -1
  228. package/dist/recall-xray-cli.d.ts +1 -1
  229. package/dist/recall-xray-renderer.d.ts +1 -1
  230. package/dist/recall-xray.d.ts +1 -1
  231. package/dist/resolve-auth-token.d.ts +1 -1
  232. package/dist/resume-bundles.js +4 -4
  233. package/dist/retrieval-agents.d.ts +2 -2
  234. package/dist/retrieval-tiers.d.ts +1 -1
  235. package/dist/routing/engine.d.ts +1 -1
  236. package/dist/routing/store.d.ts +1 -1
  237. package/dist/schemas.d.ts +22 -22
  238. package/dist/search/document-scanner.js +2 -2
  239. package/dist/search/embed-helper.d.ts +1 -1
  240. package/dist/search/factory.d.ts +1 -1
  241. package/dist/search/factory.js +9 -9
  242. package/dist/search/index.d.ts +1 -1
  243. package/dist/search/index.js +13 -13
  244. package/dist/search/lancedb-backend.d.ts +1 -1
  245. package/dist/search/lancedb-backend.js +3 -3
  246. package/dist/search/meilisearch-backend.d.ts +1 -1
  247. package/dist/search/meilisearch-backend.js +3 -3
  248. package/dist/search/noop-backend.d.ts +1 -1
  249. package/dist/search/orama-backend.d.ts +1 -1
  250. package/dist/search/orama-backend.js +3 -3
  251. package/dist/search/port.d.ts +1 -1
  252. package/dist/search/remote-backend.d.ts +1 -1
  253. package/dist/{semantic-consolidation-BDVIbLRw.d.ts → semantic-consolidation-O5lMZ4LG.d.ts} +1 -1
  254. package/dist/semantic-consolidation.d.ts +2 -2
  255. package/dist/semantic-consolidation.js +11 -11
  256. package/dist/semantic-rule-promotion.js +9 -9
  257. package/dist/semantic-rule-verifier.d.ts +1 -1
  258. package/dist/semantic-rule-verifier.js +10 -10
  259. package/dist/session-observer-bands.d.ts +1 -1
  260. package/dist/session-observer-state.d.ts +1 -1
  261. package/dist/shared-context/manager.d.ts +1 -1
  262. package/dist/signal.d.ts +1 -1
  263. package/dist/storage-C1maCNbc.d.ts +1591 -0
  264. package/dist/storage.d.ts +5 -1239
  265. package/dist/storage.js +8 -8
  266. package/dist/summarizer.d.ts +1 -1
  267. package/dist/summary-snapshot.d.ts +1 -1
  268. package/dist/temporal-supersession.d.ts +2 -2
  269. package/dist/temporal-validity.d.ts +1 -1
  270. package/dist/threading.d.ts +1 -1
  271. package/dist/tier-migration.d.ts +2 -2
  272. package/dist/tier-routing.d.ts +1 -1
  273. package/dist/{tier-stats-TG6GUZ6N.js → tier-stats-5XF4UBRQ.js} +4 -4
  274. package/dist/tokens.d.ts +17 -2
  275. package/dist/tokens.js +3 -1
  276. package/dist/topics.d.ts +1 -1
  277. package/dist/transcript.d.ts +1 -1
  278. package/dist/transfer/backup.js +2 -2
  279. package/dist/transfer/capsule-export.js +3 -3
  280. package/dist/transfer/capsule-import.js +3 -3
  281. package/dist/transfer/types.d.ts +12 -12
  282. package/dist/trust-score-stage.d.ts +1 -1
  283. package/dist/trust-score.d.ts +1 -1
  284. package/dist/{types-ljkd82rn.d.ts → types-CGgcNWCG.d.ts} +31 -1
  285. package/dist/types.d.ts +1 -1
  286. package/dist/utility-runtime.d.ts +1 -1
  287. package/dist/verified-recall.js +10 -10
  288. package/package.json +2 -2
  289. package/src/access-boundary.test.ts +62 -0
  290. package/src/access-boundary.ts +165 -126
  291. package/src/access-http.test.ts +781 -0
  292. package/src/access-http.ts +334 -108
  293. package/src/access-mcp.test.ts +264 -0
  294. package/src/access-mcp.ts +45 -70
  295. package/src/access-operations-batch.ts +35 -12
  296. package/src/access-surface-catalog.test.ts +22 -14
  297. package/src/access-surface-catalog.ts +6 -3
  298. package/src/access-token-capabilities.test.ts +844 -0
  299. package/src/access-token-capabilities.ts +391 -0
  300. package/src/chat/chat-mcp.test.ts +232 -0
  301. package/src/chat/chat-session.ts +34 -0
  302. package/src/cli-wearables-fuse.test.ts +197 -0
  303. package/src/cli.ts +25 -0
  304. package/src/index.ts +14 -0
  305. package/src/mcp-read-only-tools.ts +61 -0
  306. package/src/storage.ts +16 -0
  307. package/src/tokens.ts +32 -3
  308. package/src/wearables/cli.test.ts +102 -0
  309. package/src/wearables/cli.ts +65 -0
  310. package/src/wearables/config.test.ts +32 -0
  311. package/src/wearables/config.ts +52 -0
  312. package/src/wearables/day-store.test.ts +246 -0
  313. package/src/wearables/day-store.ts +272 -3
  314. package/src/wearables/fusion/cluster.ts +230 -0
  315. package/src/wearables/fusion/fuse.ts +218 -0
  316. package/src/wearables/fusion/fusion.test.ts +2575 -0
  317. package/src/wearables/fusion/index.ts +50 -0
  318. package/src/wearables/fusion/reconcile.ts +831 -0
  319. package/src/wearables/fusion/reconstruct.ts +251 -0
  320. package/src/wearables/fusion/store.ts +480 -0
  321. package/src/wearables/fusion/types.ts +228 -0
  322. package/src/wearables/index.ts +41 -0
  323. package/src/wearables/service.test.ts +1030 -2
  324. package/src/wearables/service.ts +258 -16
  325. package/src/wearables/speakers.test.ts +128 -0
  326. package/src/wearables/speakers.ts +58 -4
  327. package/src/wearables/storage-io.test.ts +146 -0
  328. package/src/wearables/types.ts +31 -0
  329. package/dist/chunk-2FOD2YY3.js.map +0 -1
  330. package/dist/chunk-7QWMTOIF.js.map +0 -1
  331. package/dist/chunk-DR6PEJU3.js.map +0 -1
  332. package/dist/chunk-E2SPGGUI.js.map +0 -1
  333. package/dist/chunk-FZGGZAAU.js.map +0 -1
  334. package/dist/chunk-ILFJ7AVS.js.map +0 -1
  335. package/dist/chunk-M7GGMGMZ.js.map +0 -1
  336. package/dist/chunk-M7XQSUBB.js.map +0 -1
  337. /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-token-capabilities.js.map} +0 -0
  338. /package/dist/{auto-sync-HMHQHUAG.js.map → auto-sync-4A5YBWYO.js.map} +0 -0
  339. /package/dist/{chunk-6ZIETXZ2.js.map → chunk-23JFRB73.js.map} +0 -0
  340. /package/dist/{chunk-IC6QX5TC.js.map → chunk-3XIY7MDQ.js.map} +0 -0
  341. /package/dist/{chunk-LOR6ZM5R.js.map → chunk-4LV23CHK.js.map} +0 -0
  342. /package/dist/{chunk-AER6MT24.js.map → chunk-6SXVCD7W.js.map} +0 -0
  343. /package/dist/{chunk-OHDJ34CC.js.map → chunk-72WG5QKN.js.map} +0 -0
  344. /package/dist/{chunk-CZAZX7JP.js.map → chunk-7MW3CVLD.js.map} +0 -0
  345. /package/dist/{chunk-CXLNZXG5.js.map → chunk-7XGSIUP5.js.map} +0 -0
  346. /package/dist/{chunk-CZAA5N5S.js.map → chunk-AYZJID4S.js.map} +0 -0
  347. /package/dist/{chunk-6BISDEN6.js.map → chunk-BEM4D2QG.js.map} +0 -0
  348. /package/dist/{chunk-ZPOROUTX.js.map → chunk-CYWA2CXR.js.map} +0 -0
  349. /package/dist/{chunk-M5QKGHCR.js.map → chunk-CYWCKFCL.js.map} +0 -0
  350. /package/dist/{chunk-4BFRBP45.js.map → chunk-DEG5ULFJ.js.map} +0 -0
  351. /package/dist/{chunk-JLR3JSTL.js.map → chunk-DS7MM2ES.js.map} +0 -0
  352. /package/dist/{chunk-2BFLDH5F.js.map → chunk-EEIROWEG.js.map} +0 -0
  353. /package/dist/{chunk-X4A62ET7.js.map → chunk-H2DXDQCQ.js.map} +0 -0
  354. /package/dist/{chunk-27FLVMDC.js.map → chunk-HLN7RROI.js.map} +0 -0
  355. /package/dist/{chunk-2U2A5HF4.js.map → chunk-J3XHPFEU.js.map} +0 -0
  356. /package/dist/{chunk-AKUCU7CI.js.map → chunk-JI5AVXYO.js.map} +0 -0
  357. /package/dist/{chunk-KCJ5H6XQ.js.map → chunk-JSNVWNGX.js.map} +0 -0
  358. /package/dist/{chunk-FLVL4L3L.js.map → chunk-KTULX4YO.js.map} +0 -0
  359. /package/dist/{chunk-437S3G37.js.map → chunk-LNQUAJNC.js.map} +0 -0
  360. /package/dist/{chunk-EPGWWCZF.js.map → chunk-LR5DZ56F.js.map} +0 -0
  361. /package/dist/{chunk-YDSWRD33.js.map → chunk-N3JDIRBW.js.map} +0 -0
  362. /package/dist/{chunk-OUMTBFSQ.js.map → chunk-NKCPUHCI.js.map} +0 -0
  363. /package/dist/{chunk-BGTAL46Y.js.map → chunk-PNLFN2PZ.js.map} +0 -0
  364. /package/dist/{chunk-P6SYWE4G.js.map → chunk-PVUJ6WVV.js.map} +0 -0
  365. /package/dist/{chunk-YPEMNNNY.js.map → chunk-RGK27ELN.js.map} +0 -0
  366. /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
  367. /package/dist/{chunk-66FLU5BT.js.map → chunk-SIRPLF54.js.map} +0 -0
  368. /package/dist/{chunk-HRCACVHL.js.map → chunk-TLUDFPZL.js.map} +0 -0
  369. /package/dist/{chunk-2MR3MFQB.js.map → chunk-UPSAT4PY.js.map} +0 -0
  370. /package/dist/{chunk-4P4626RY.js.map → chunk-UTRJFDP2.js.map} +0 -0
  371. /package/dist/{chunk-CGS7UKWM.js.map → chunk-VCW74PNH.js.map} +0 -0
  372. /package/dist/{chunk-5GPPACXK.js.map → chunk-VSMRDBFT.js.map} +0 -0
  373. /package/dist/{chunk-NG7YBCJO.js.map → chunk-XCL57M6Q.js.map} +0 -0
  374. /package/dist/{chunk-CLX5VDNY.js.map → chunk-Y4RKAQZ7.js.map} +0 -0
  375. /package/dist/{chunk-DGYCWCQ7.js.map → chunk-Y7UNNAZD.js.map} +0 -0
  376. /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
  377. /package/dist/{chunk-XN3TUUX2.js.map → chunk-YQB32INI.js.map} +0 -0
  378. /package/dist/{first-start-migration-DY6YCRC2.js.map → first-start-migration-VBJQL6OI.js.map} +0 -0
  379. /package/dist/{tier-stats-TG6GUZ6N.js.map → tier-stats-5XF4UBRQ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/access-boundary.ts"],"sourcesContent":["/**\n * Single input-validation and error boundary for the CLI/MCP/HTTP access\n * surfaces (issue #1525, epic #1520 Phase 1).\n *\n * Every operation that crosses the access-service facade passes through ONE\n * registry entry: a zod-validated request envelope, a shared error mapper,\n * and the \"reject invalid input and list valid options\" behavior that\n * CLAUDE.md rules 14/17/24/28/36/48/51 previously had to be re-implemented\n * per handler. The three surfaces become thin adapters — one operation\n * definition, three transports — so a validation fix lands everywhere at\n * once.\n *\n * Host-agnostic (rule 31): operation names carry no `openclaw-*`/`engram-*`\n * prefix. Session/namespace tenancy stays in the handler layer (resolved via\n * ScopePlan #1521); the boundary validates SHAPE, not tenancy.\n */\n\nimport { z } from \"zod\";\nimport { EngramAccessInputError, type EngramAccessService } from \"./access-service.js\";\nimport { assertFleetWideOperationAllowed, assertOperationAllowed, tokenCapabilityStore } from \"./access-token-capabilities.js\";\nimport { expandTildePath } from \"./utils/path.js\";\n\n// ---------------------------------------------------------------------------\n// Canonical operation names — host-agnostic (rule 31)\n// ---------------------------------------------------------------------------\n\n/**\n * Canonical operation ids. One id is shared by the MCP tool, the HTTP route,\n * and the CLI command that expose the same operation. Add to this union as\n * each domain-group migration PR (memory ops → connectors → namespaces …)\n * lands; the fitness test in `access-surface-catalog.test.ts` treats the\n * registered set as the migration state.\n */\nexport const OPERATION_NAMES = [\n \"memory_get\",\n \"memory_search\",\n \"memory_store\",\n \"coding_decision\",\n \"coding_architecture\",\n // codegraph parity tools (issue #1554) -- each maps to one codegraph_*\n // boundary operation that delegates to the surface handler in\n // coding/codegraph-surfaces.ts. Names match the external tool suffixes.\n \"codegraph_index\",\n \"codegraph_list_projects\",\n \"codegraph_delete_project\",\n \"codegraph_index_status\",\n \"codegraph_search_graph\",\n \"codegraph_trace_path\",\n \"codegraph_detect_changes\",\n \"codegraph_query_graph\",\n \"codegraph_get_schema\",\n \"codegraph_get_snippet\",\n \"codegraph_get_architecture\",\n \"codegraph_search_code\",\n \"codegraph_manage_adr\",\n \"codegraph_ingest_traces\",\n \"memory_correct_plan\",\n \"memory_correct_apply\",\n \"coding_delta\",\n // Remaining MCP/HTTP handlers migrated through the boundary (issue #1525).\n \"recall\",\n \"recall_explain\",\n \"set_coding_context\",\n \"recall_tier_explain\",\n \"recall_xray\",\n \"wearables_status\",\n \"wearables_sync\",\n \"transcript_day\",\n \"transcript_search\",\n \"transcript_memories\",\n \"action_confidence\",\n \"chatgpt_memory_inspector\",\n \"day_summary\",\n \"capsule_export\",\n \"capsule_import\",\n \"capsule_list\",\n \"memory_governance_run\",\n \"procedure_mining_run\",\n \"pattern_reinforcement_run\",\n \"procedural_stats\",\n \"memory_timeline\",\n \"suggestion_submit\",\n \"entity_get\",\n \"review_queue_list\",\n \"correction_pending\",\n \"observe\",\n \"lcm_search\",\n \"lcm_compaction_flush\",\n \"lcm_compaction_record\",\n \"continuity_audit_generate\",\n \"continuity_incident_open\",\n \"continuity_incident_close\",\n \"continuity_incident_list\",\n \"continuity_loop_add_or_update\",\n \"continuity_loop_review\",\n \"identity_anchor_get\",\n \"identity_anchor_update\",\n \"memory_identity\",\n \"work_task\",\n \"work_project\",\n \"work_board\",\n \"shared_context_write_output\",\n \"shared_feedback_record\",\n \"shared_priorities_append\",\n \"shared_context_cross_signals_run\",\n \"shared_context_curate_daily\",\n \"compounding_weekly_synthesize\",\n \"compounding_promote_candidate\",\n \"compression_guidelines_optimize\",\n \"compression_guidelines_activate\",\n \"memory_profile\",\n \"memory_entities_list\",\n \"memory_questions\",\n \"memory_last_recall\",\n \"memory_intent_debug\",\n \"memory_qmd_debug\",\n \"memory_graph_explain\",\n \"graph_snapshot\",\n \"memory_feedback\",\n \"memory_promote\",\n \"memory_outcome\",\n \"memory_action_apply\",\n \"context_checkpoint\",\n \"briefing\",\n \"review_list\",\n \"review_resolve\",\n \"contradiction_scan_run\",\n \"memory_summarize_hourly\",\n \"conversation_index_update\",\n \"profiling_report\",\n \"graph_edge_decay_run\",\n \"live_connectors_run\",\n \"peer_list\",\n \"peer_get\",\n \"peer_set\",\n \"peer_delete\",\n \"peer_profile_get\",\n \"peer_forget\",\n \"console_state\",\n \"dreams_status\",\n \"dreams_run\",\n // HTTP-only routes (no direct MCP tool equivalent).\n \"adapters_status\",\n \"offline_sync_snapshot\",\n \"offline_sync_snapshot_stream\",\n \"offline_sync_files\",\n \"offline_sync_file_content\",\n \"offline_sync_apply_file_content\",\n \"offline_sync_apply\",\n \"lcm_status\",\n \"memory_list\",\n \"recall_timings\",\n \"entity_list\",\n \"maintenance_status\",\n \"quality_status\",\n \"trust_zones_status\",\n \"trust_zones_records\",\n \"review_disposition\",\n \"trust_zones_promote\",\n \"trust_zones_demo_seed\",\n \"citations_observed\",\n \"contradiction_detail\",\n \"graph_events\",\n // Chat surface — conversational memory inspection/correction (issue #1583).\n \"chat_message\",\n \"chat_events\",\n] as const;\n\n/**\n * Canonical operation ids. One id is shared by the MCP tool, the HTTP route,\n * and the CLI command that expose the same operation. Derived from\n * {@link OPERATION_NAMES} (single source of truth) so the runtime catalog\n * used for token-capability validation cannot drift from the type. The\n * fitness test in `access-surface-catalog.test.ts` treats the registered set\n * as the migration state.\n */\nexport type OperationName = (typeof OPERATION_NAMES)[number];\n\n// ---------------------------------------------------------------------------\n// Operation context — what every handler receives\n// ---------------------------------------------------------------------------\n\n/**\n * Per-call context. `service` is the facade the handler delegates to; the\n * boundary never reaches past it. `authenticatedPrincipal` is resolved by\n * the SURFACE (MCP header / HTTP identity / CLI flag) before the boundary\n * runs, so handlers stay principal-source-agnostic. `hooks` carries\n * transport-level callbacks (e.g. HTTP write-quota enforcement) that must\n * fire atomically inside the service call; surfaces that have no such hook\n * leave it undefined.\n */\nexport interface OperationContext {\n readonly service: EngramAccessService;\n readonly authenticatedPrincipal?: string;\n readonly operatorPrincipal?: string;\n readonly hooks?: OperationHooks;\n}\n\n/**\n * Transport-level callbacks a handler forwards into the service call. Kept\n * narrow on purpose: the boundary owns validation + dispatch shape, not\n * transport policy. Add fields here only when a surface genuinely needs a\n * callback the service itself consumes.\n */\nexport interface OperationHooks {\n /** HTTP write-quota gate; throws to reject the write when exhausted. */\n readonly enforceWriteQuota?: () => void | Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Operation spec + bound operation\n// ---------------------------------------------------------------------------\n\nexport interface OperationSpec<In, Out> {\n /** Canonical operation id; matches an {@link OperationName}. */\n readonly name: OperationName;\n readonly description: string;\n /** Zod schema validating the raw request envelope. */\n readonly schema: z.ZodType<In>;\n /** Handler invoked with the parsed input; throws EngramAccessInputError for domain faults. */\n readonly handler: (input: In, ctx: OperationContext) => Promise<Out>;\n /**\n * Marks an operation as inherently FLEET-WIDE / global — it runs across ALL\n * namespaces (or against a single non-namespaced global layer) and carries no\n * `namespace` argument, so the MCP `tools/call` effective-namespace chokepoint\n * never applies. When set, {@link defineOperation}'s run wrapper rejects any\n * namespace-SCOPED token (fail closed) via assertFleetWideOperationAllowed\n * BEFORE the handler — no side effect on denial. Unrestricted/legacy tokens\n * (cron, internal callers, no capability record) are unaffected (issue #1850).\n */\n readonly fleetWide?: boolean;\n}\n\nexport interface BoundOperation<In = unknown, Out = unknown> {\n readonly spec: OperationSpec<In, Out>;\n /** Validate the raw envelope, then invoke the handler. Throws EngramAccessInputError on any validation failure. */\n readonly run: (rawInput: unknown, ctx: OperationContext) => Promise<Out>;\n}\n\n// ---------------------------------------------------------------------------\n// Shared normalizers the boundary owns (rules 17, 28, 36, 48, 51)\n// ---------------------------------------------------------------------------\n\n/**\n * Coerce boolean-like strings at the edge (rule 36). Accepts actual booleans\n * and the string spellings clients send (\"true\"/\"false\"/\"1\"/\"0\"/\"yes\"/\"no\"/\n * \"on\"/\"off\", case-insensitive). Rejects anything else loudly — `Boolean(\"false\")`\n * would silently be `true`, which is the bug rule 36 exists to prevent.\n *\n * `undefined`/`null`/`\"\"` → `undefined`, so callers can keep treating an\n * absent flag as \"use the default\" without a separate presence check.\n */\nexport function coerceBooleanLike(value: unknown): boolean | undefined {\n if (value === undefined || value === null || value === \"\") return undefined;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const lower = value.trim().toLowerCase();\n if (lower === \"true\" || lower === \"1\" || lower === \"yes\" || lower === \"on\") return true;\n if (lower === \"false\" || lower === \"0\" || lower === \"no\" || lower === \"off\") return false;\n }\n throw new EngramAccessInputError(\n `expected a boolean-like value (true|false|1|0|yes|no|on|off); got ${JSON.stringify(value)}`,\n );\n}\n\n/**\n * Coerce + validate a positive integer from a numeric string or number\n * (rule 28). Loosely-typed MCP/CLI clients send `\"5\"`; `typeof saved === \"number\"`\n * on read-back would reject it later, so we coerce at the edge and reject\n * booleans/objects loudly (`Number(true) === 1` would silently pass otherwise).\n *\n * `undefined`/`null`/`\"\"` → `undefined`.\n */\nexport function coercePositiveInteger(value: unknown, label: string): number | undefined {\n if (value === undefined || value === null || value === \"\") return undefined;\n if (typeof value === \"number\") {\n if (!Number.isFinite(value) || value <= 0 || !Number.isInteger(value)) {\n throw new EngramAccessInputError(`${label} expects a positive integer; got ${JSON.stringify(value)}`);\n }\n return value;\n }\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (!/^[+-]?\\d+$/.test(trimmed)) {\n throw new EngramAccessInputError(`${label} expects a positive integer; got ${JSON.stringify(value)}`);\n }\n const parsed = Number(trimmed);\n if (!Number.isSafeInteger(parsed) || parsed <= 0) {\n throw new EngramAccessInputError(`${label} expects a positive integer; got ${JSON.stringify(value)}`);\n }\n return parsed;\n }\n throw new EngramAccessInputError(`${label} expects a positive integer; got ${JSON.stringify(value)}`);\n}\n\n/**\n * Expand `~` in a path-shaped input (rule 17). Node `fs` does NOT expand `~`;\n * ad-hoc regex drifts. `undefined`/`null`/`\"\"` → `undefined`.\n */\nexport function normalizeOptionalPath(value: unknown): string | undefined {\n if (value === undefined || value === null || value === \"\") return undefined;\n if (typeof value !== \"string\") {\n throw new EngramAccessInputError(`expected a path string; got ${JSON.stringify(value)}`);\n }\n return expandTildePath(value);\n}\n\n// ---------------------------------------------------------------------------\n// Error formatting — rule 51: list valid options, never silently default\n// ---------------------------------------------------------------------------\n\n/**\n * Turn a zod failure into an {@link EngramAccessInputError} whose message\n * names the offending field and — for enum/union issues — lists the valid\n * options, so the caller can correct rather than guess (rule 51).\n */\nexport function formatZodIssues(error: z.ZodError): string {\n const parts: string[] = [];\n for (const issue of error.issues) {\n // Collapse array-index paths (e.g. [\"currentContextScopes\", 1]) to\n // the field name so the message reads \"field: must be ...\" not\n // \"field.1: must be ...\".\n const hasNumeric = issue.path.some((p) => typeof p === \"number\");\n const path = hasNumeric\n ? issue.path.filter((p) => typeof p === \"string\").join(\".\") || \"(root)\"\n : issue.path.length > 0 ? issue.path.join(\".\") : \"(root)\";\n const options = enumOptionsFromIssue(issue);\n const suffix = options ? `. Valid: ${options.join(\", \")}` : \"\";\n parts.push(`${path}: ${issue.message}${suffix}`);\n }\n return parts.length > 0\n ? `request validation failed: ${parts.join(\"; \")}`\n : \"request validation failed\";\n}\n\nfunction enumOptionsFromIssue(issue: z.ZodIssue): readonly string[] | undefined {\n // zod exposes accepted enum values on the issue for ZodEnum / ZodNativeEnum\n // and on the options of invalid_union discriminators. Reading them here\n // keeps \"list valid options\" in ONE place rather than per handler.\n if (issue.code === z.ZodIssueCode.invalid_enum_value) {\n const rawOptions = (issue as { options?: unknown }).options;\n if (Array.isArray(rawOptions)) {\n return rawOptions.map((opt) => String(opt));\n }\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\nconst registry = new Map<OperationName, BoundOperation>();\n\n/**\n * Register an operation. Throws if the name is already registered — duplicate\n * registration is a programming error, not a runtime input fault, so it throws\n * a plain Error (not the input-error class surfaces translate for clients).\n */\n/**\n * Error map that converts Zod's generic type-mismatch messages into\n * human-readable \"must be a <type>\" messages (rule 51). Applied at the\n * boundary chokepoint so every operation gets consistent messaging\n * without per-handler boilerplate.\n */\nconst humanReadableErrorMap: z.ZodErrorMap = (issue, ctx) => {\n if (issue.code === z.ZodIssueCode.invalid_type) {\n if (issue.received === \"undefined\") return { message: ctx.defaultError };\n // Array-element type errors — collapse to field-level message.\n if (issue.path.some((p) => typeof p === \"number\")) {\n const arrField = issue.path.filter((p) => typeof p === \"string\").join(\".\");\n return { message: `${arrField} must be an array of ${issue.expected}s` };\n }\n const fieldName = issue.path.length > 0 ? String(issue.path[issue.path.length - 1]) : \"value\";\n switch (issue.expected) {\n case \"string\": return { message: `${fieldName} must be a string` };\n case \"boolean\": return { message: `${fieldName} must be a boolean` };\n case \"number\": return { message: `${fieldName} must be a number` };\n case \"array\": return { message: `${fieldName} must be an array` };\n case \"object\": return { message: `${fieldName} must be an object` };\n default: return { message: ctx.defaultError };\n }\n }\n return { message: ctx.defaultError };\n};\n\nexport function defineOperation<In, Out>(spec: OperationSpec<In, Out>): BoundOperation<In, Out> {\n if (registry.has(spec.name)) {\n throw new Error(`access-boundary: operation already registered: ${spec.name}`);\n }\n const bound: BoundOperation<In, Out> = {\n spec,\n run: async (rawInput, ctx) => {\n const parseResult = spec.schema.safeParse(rawInput, { errorMap: humanReadableErrorMap });\n if (!parseResult.success) {\n throw new EngramAccessInputError(formatZodIssues(parseResult.error));\n }\n // Per-token capability enforcement (issue #1837): when the presenting\n // token carries an ops allow-list, reject operations not in it. The\n // capability record is carried via AsyncLocalStorage, set once per\n // authorized HTTP/MCP request; absent (CLI/tests/direct callers) ⇒\n // unrestricted. Default-deny ONLY when an ops axis is present — legacy\n // tokens (absent record) and explicit-unrestricted new tokens are\n // unaffected.\n assertOperationAllowed(tokenCapabilityStore.getStore(), spec.name);\n // Fleet-wide / global maintenance ops (issue #1850 round 10): these run\n // across all namespaces with no `namespace` arg, so the tools/call\n // effective-namespace gate never fires. A namespace-SCOPED token must not\n // trigger cross-namespace maintenance — fail closed BEFORE the handler so\n // a denial never leaks a partial mutation. No-op for unrestricted/legacy.\n if (spec.fleetWide) {\n assertFleetWideOperationAllowed(tokenCapabilityStore.getStore());\n }\n return spec.handler(parseResult.data, ctx);\n },\n };\n // Store under the canonical name; the cast is safe because In/Out are\n // erased at the registry boundary and recovered by callers via getOperation.\n registry.set(spec.name, bound as unknown as BoundOperation);\n return bound;\n}\n\n/** Look up a registered operation by canonical name. */\nexport function getOperation(name: OperationName): BoundOperation | undefined {\n return registry.get(name);\n}\n\n/** All registered operation names. */\nexport function listRegisteredOperations(): readonly OperationName[] {\n return [...registry.keys()];\n}\n\n/** Test-only: clear the registry so pilot definitions can be re-registered. */\nexport function __resetRegistryForTest(): void {\n registry.clear();\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  resolveDefaultNamespaceRoot,
3
3
  resolveNamespaceStorageRoot
4
- } from "./chunk-BGTAL46Y.js";
4
+ } from "./chunk-PNLFN2PZ.js";
5
5
  import {
6
6
  getConfiguredNamespaces
7
7
  } from "./chunk-BAVVI5GP.js";
@@ -2042,4 +2042,4 @@ export {
2042
2042
  runNamespaceMaintenancePlan,
2043
2043
  runNamespaceMaintenanceBatchPlan
2044
2044
  };
2045
- //# sourceMappingURL=chunk-LOR6ZM5R.js.map
2045
+ //# sourceMappingURL=chunk-4LV23CHK.js.map
@@ -1,3 +1,7 @@
1
+ import {
2
+ TOKEN_CAPABILITIES_VERSION,
3
+ normalizeCapabilities
4
+ } from "./chunk-D7FOKIY5.js";
1
5
  import {
2
6
  resolveHomeDir
3
7
  } from "./chunk-JUC24CTX.js";
@@ -55,10 +59,12 @@ function validateTokenEntry(raw, index) {
55
59
  if (typeof entry.connector !== "string" || entry.connector.length === 0) {
56
60
  throw new Error(`invalid token entry at index ${index}: connector must be a non-empty string`);
57
61
  }
62
+ const capabilities = entry.capabilities !== void 0 ? normalizeCapabilities(entry.capabilities) : void 0;
58
63
  return {
59
64
  token: entry.token,
60
65
  connector: entry.connector,
61
- createdAt: typeof entry.createdAt === "string" && entry.createdAt.length > 0 ? entry.createdAt : (/* @__PURE__ */ new Date(0)).toISOString()
66
+ createdAt: typeof entry.createdAt === "string" && entry.createdAt.length > 0 ? entry.createdAt : (/* @__PURE__ */ new Date(0)).toISOString(),
67
+ ...capabilities ? { capabilities } : {}
62
68
  };
63
69
  }
64
70
  function parseTokenStore(rawText, filePath) {
@@ -170,13 +176,14 @@ function saveTokenStore(store, tokensPath) {
170
176
  throw error;
171
177
  }
172
178
  }
173
- function buildTokenEntry(connector) {
179
+ function buildTokenEntry(connector, capabilities) {
174
180
  const prefix = TOKEN_PREFIXES[connector] ?? "remnic_xx_";
175
181
  const token = prefix + randomBytes(24).toString("hex");
176
182
  return {
177
183
  token,
178
184
  connector,
179
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
185
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
186
+ capabilities: capabilities ?? { version: TOKEN_CAPABILITIES_VERSION }
180
187
  };
181
188
  }
182
189
  function commitTokenEntry(entry, tokensPath) {
@@ -185,10 +192,10 @@ function commitTokenEntry(entry, tokensPath) {
185
192
  store.tokens.push(entry);
186
193
  saveTokenStore(store, tokensPath);
187
194
  }
188
- function generateToken(connector, tokensPath) {
195
+ function generateToken(connector, tokensPath, capabilities) {
189
196
  const store = loadTokenStore(tokensPath);
190
197
  store.tokens = store.tokens.filter((t) => t.connector !== connector);
191
- const entry = buildTokenEntry(connector);
198
+ const entry = buildTokenEntry(connector, capabilities);
192
199
  store.tokens.push(entry);
193
200
  saveTokenStore(store, tokensPath);
194
201
  return entry;
@@ -246,4 +253,4 @@ export {
246
253
  getAllValidTokensCached,
247
254
  resolveConnectorFromToken
248
255
  };
249
- //# sourceMappingURL=chunk-E2SPGGUI.js.map
256
+ //# sourceMappingURL=chunk-4OKLXWDZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/**\n * Token management for Remnic multi-connector auth.\n *\n * Manages per-connector tokens in ~/.remnic/tokens.json.\n * Each connector gets a unique token with a recognizable prefix.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\nimport { resolveHomeDir } from \"./runtime/env.js\";\nimport {\n TOKEN_CAPABILITIES_VERSION,\n normalizeCapabilities,\n type TokenCapabilities,\n} from \"./access-token-capabilities.js\";\n\nexport interface TokenEntry {\n token: string;\n connector: string;\n createdAt: string;\n /**\n * Versioned capabilities record (issue #1837). PRESENT ⇒ the token was\n * minted by the capability-aware path; its ops/namespaces axes are\n * enforced (absent axis ⇒ unrestricted on that axis; empty array ⇒ deny\n * all; non-empty ⇒ allow-list). ABSENT ⇒ a pre-feature legacy entry ⇒\n * unrestricted (full access), purely for backward compatibility — this is\n * the ONLY path by which full access is granted by omission. Every new\n * token carries an explicit record (even `{ version: 1 }` for a deliberate\n * unrestricted mint), so a new token can never silently gain full access.\n */\n capabilities?: TokenCapabilities;\n}\n\nexport interface TokenStore {\n tokens: TokenEntry[];\n}\n\nconst TOKEN_PREFIXES: Record<string, string> = {\n \"openclaw\": \"remnic_oc_\",\n \"claude-code\": \"remnic_cc_\",\n \"codex-cli\": \"remnic_cx_\",\n \"codex\": \"remnic_cx_\",\n \"hermes\": \"remnic_hm_\",\n \"pi\": \"remnic_pi_\",\n \"omp\": \"remnic_op_\",\n \"replit\": \"remnic_rl_\",\n \"cursor\": \"remnic_cu_\",\n \"cline\": \"remnic_cl_\",\n \"github-copilot\": \"remnic_gh_\",\n \"roo-code\": \"remnic_rc_\",\n \"windsurf\": \"remnic_ws_\",\n \"amp\": \"remnic_am_\",\n \"generic-mcp\": \"remnic_gm_\",\n \"chatgpt\": \"remnic_cg_\",\n};\n\nfunction defaultTokensPath(): string {\n return path.join(resolveHomeDir(), \".remnic\", \"tokens.json\");\n}\n\nfunction legacyTokensPath(): string {\n return path.join(resolveHomeDir(), \".engram\", \"tokens.json\");\n}\n\nfunction resolveReadPath(tokensPath?: string): string {\n const primary = tokensPath ?? defaultTokensPath();\n if (tokensPath) return primary;\n if (fs.existsSync(primary)) return primary;\n const legacy = legacyTokensPath();\n return fs.existsSync(legacy) ? legacy : primary;\n}\n\nfunction ensureDir(filePath: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction isEnoent(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && (error as NodeJS.ErrnoException).code === \"ENOENT\";\n}\n\nfunction validateTokenEntry(raw: unknown, index: number): TokenEntry {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new Error(`invalid token entry at index ${index}: expected object`);\n }\n const entry = raw as Record<string, unknown>;\n if (typeof entry.token !== \"string\" || entry.token.length === 0) {\n throw new Error(`invalid token entry at index ${index}: token must be a non-empty string`);\n }\n if (typeof entry.connector !== \"string\" || entry.connector.length === 0) {\n throw new Error(`invalid token entry at index ${index}: connector must be a non-empty string`);\n }\n const capabilities = entry.capabilities !== undefined\n ? normalizeCapabilities(entry.capabilities)\n : undefined;\n return {\n token: entry.token,\n connector: entry.connector,\n createdAt:\n typeof entry.createdAt === \"string\" && entry.createdAt.length > 0\n ? entry.createdAt\n : new Date(0).toISOString(),\n ...(capabilities ? { capabilities } : {}),\n };\n}\n\nfunction parseTokenStore(rawText: string, filePath: string): { store: TokenStore; migratedLegacy: boolean } {\n let raw: unknown;\n try {\n raw = JSON.parse(rawText);\n } catch (error) {\n throw new Error(\n `failed to parse token store at ${filePath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n if (typeof raw === \"object\" && raw !== null && !Array.isArray(raw)) {\n const record = raw as Record<string, unknown>;\n if (record.tokens !== undefined) {\n if (!Array.isArray(record.tokens)) {\n throw new Error(`invalid token store at ${filePath}: tokens must be an array`);\n }\n return {\n store: {\n tokens: record.tokens.map((entry, index) => validateTokenEntry(entry, index)),\n },\n migratedLegacy: false,\n };\n }\n\n // Migrate legacy flat-map format: { \"connector\": \"token_value\", ... }\n const migrated: TokenEntry[] = [];\n for (const [key, value] of Object.entries(record)) {\n if (typeof value === \"string\" && value.length > 0) {\n migrated.push(\n validateTokenEntry(\n { token: value, connector: key, createdAt: new Date().toISOString() },\n migrated.length,\n ),\n );\n }\n }\n if (migrated.length > 0) {\n return { store: { tokens: migrated }, migratedLegacy: true };\n }\n }\n\n throw new Error(`invalid token store at ${filePath}: expected token array or legacy connector map`);\n}\n\nexport function loadTokenStore(tokensPath?: string): TokenStore {\n const p = resolveReadPath(tokensPath);\n try {\n const rawText = fs.readFileSync(p, \"utf8\");\n const { store, migratedLegacy } = parseTokenStore(rawText, p);\n if (migratedLegacy) {\n // Auto-migrate legacy flat-map stores in new format. This is best-effort:\n // a migration write failure must not hide the successfully parsed tokens.\n try {\n saveTokenStore(store, tokensPath);\n } catch {\n // Migration write failed (e.g., read-only fs) — still return parsed tokens.\n }\n }\n return store;\n } catch (error) {\n if (isEnoent(error)) {\n return { tokens: [] };\n }\n throw error;\n }\n}\n\nfunction fsyncDirectoryBestEffort(dirPath: string): void {\n let dirFd: number | null = null;\n try {\n dirFd = fs.openSync(dirPath, \"r\");\n fs.fsyncSync(dirFd);\n } catch {\n // Directory fsync is not supported on every platform/filesystem.\n } finally {\n if (dirFd !== null) {\n try { fs.closeSync(dirFd); } catch { /* ignore */ }\n }\n }\n}\n\nexport function saveTokenStore(store: TokenStore, tokensPath?: string): void {\n const p = tokensPath ?? defaultTokensPath();\n ensureDir(p);\n const validated: TokenStore = {\n tokens: store.tokens.map((entry, index) => validateTokenEntry(entry, index)),\n };\n const dir = path.dirname(p);\n const tmpPath = path.join(dir, `.${path.basename(p)}.${process.pid}.${randomUUID()}.tmp`);\n let fd: number | null = null;\n try {\n fd = fs.openSync(tmpPath, \"w\", 0o600);\n fs.writeFileSync(fd, JSON.stringify(validated, null, 2) + \"\\n\", \"utf8\");\n fs.fsyncSync(fd);\n fs.closeSync(fd);\n fd = null;\n fs.renameSync(tmpPath, p);\n // Tighten permissions on pre-existing files after atomic replacement.\n try { fs.chmodSync(p, 0o600); } catch { /* ignore on platforms without chmod */ }\n fsyncDirectoryBestEffort(dir);\n invalidateTokenCache();\n } catch (error) {\n if (fd !== null) {\n try { fs.closeSync(fd); } catch { /* ignore */ }\n }\n try { fs.rmSync(tmpPath, { force: true }); } catch { /* ignore cleanup failure */ }\n throw error;\n }\n}\n\n/**\n * Build a TokenEntry candidate WITHOUT saving it to the store.\n * Callers use this when they need to defer the save until after a\n * dependent write (e.g. Hermes config.yaml) succeeds — see\n * commitTokenEntry() to persist the candidate.\n */\nexport function buildTokenEntry(connector: string, capabilities?: TokenCapabilities): TokenEntry {\n const prefix = TOKEN_PREFIXES[connector] ?? \"remnic_xx_\";\n const token = prefix + randomBytes(24).toString(\"hex\");\n // Every newly-built entry carries an EXPLICIT capabilities record (issue\n // #1837). When none is requested, record a deliberate unrestricted\n // `{ version: 1 }` — never an omitted field — so \"full access via omission\"\n // is reachable only by pre-feature legacy entries lacking the marker.\n return {\n token,\n connector,\n createdAt: new Date().toISOString(),\n capabilities: capabilities ?? { version: TOKEN_CAPABILITIES_VERSION },\n };\n}\n\n/**\n * Persist a pre-built TokenEntry into the store, replacing any existing\n * entry for the same connector. Used together with buildTokenEntry() when\n * the caller wants to defer the save until after a dependent write succeeds.\n *\n * For transactional rollback, callers should snapshot the full store via\n * loadTokenStore() BEFORE calling commitTokenEntry() and restore it with\n * saveTokenStore() on failure. A full-store snapshot handles partial writes\n * of tokens.json atomically — single-entry restore via the return value is\n * insufficient because if this function throws during saveTokenStore, the\n * return statement never executes (UXJI/UXJT fix).\n */\nexport function commitTokenEntry(entry: TokenEntry, tokensPath?: string): void {\n const store = loadTokenStore(tokensPath);\n store.tokens = store.tokens.filter((t) => t.connector !== entry.connector);\n store.tokens.push(entry);\n saveTokenStore(store, tokensPath);\n}\n\nexport function generateToken(\n connector: string,\n tokensPath?: string,\n capabilities?: TokenCapabilities,\n): TokenEntry {\n const store = loadTokenStore(tokensPath);\n\n // Remove existing token for this connector\n store.tokens = store.tokens.filter((t) => t.connector !== connector);\n\n const entry = buildTokenEntry(connector, capabilities);\n store.tokens.push(entry);\n saveTokenStore(store, tokensPath);\n return entry;\n}\n\nexport function listTokens(tokensPath?: string): TokenEntry[] {\n return loadTokenStore(tokensPath).tokens;\n}\n\nexport function revokeToken(connector: string, tokensPath?: string): boolean {\n const store = loadTokenStore(tokensPath);\n const before = store.tokens.length;\n store.tokens = store.tokens.filter((t) => t.connector !== connector);\n if (store.tokens.length < before) {\n saveTokenStore(store, tokensPath);\n return true;\n }\n return false;\n}\n\nexport function getAllValidTokens(tokensPath?: string): string[] {\n return loadTokenStore(tokensPath).tokens.map((t) => t.token);\n}\n\n// Cached token-entry snapshot to avoid synchronous disk I/O on every HTTP\n// request. Re-reads tokens.json at most once per TTL interval (default 5s).\n// There is deliberately exactly ONE cache: validation (token strings) and\n// identity (connector ids) are derived from the SAME snapshot, so a token\n// can never validate against a fresher snapshot than the one that resolves\n// its connector. saveTokenStore() invalidates on every mutation, so a\n// freshly minted or revoked token is coherent immediately.\nconst TOKEN_CACHE_TTL_MS = 5_000;\nlet _cachedEntries: TokenEntry[] = [];\nlet _cachedAt = 0;\nlet _cachedPath: string | undefined;\n\nfunction invalidateTokenCache(): void {\n _cachedEntries = [];\n _cachedAt = 0;\n _cachedPath = undefined;\n}\n\n/** Cached token-entry snapshot ({token, connector} pairs). */\nexport function getAllValidTokenEntriesCached(tokensPath?: string): readonly TokenEntry[] {\n const now = Date.now();\n if (now - _cachedAt < TOKEN_CACHE_TTL_MS && tokensPath === _cachedPath) return _cachedEntries;\n _cachedEntries = loadTokenStore(tokensPath).tokens;\n _cachedAt = now;\n _cachedPath = tokensPath;\n return _cachedEntries;\n}\n\nexport function getAllValidTokensCached(tokensPath?: string): string[] {\n return getAllValidTokenEntriesCached(tokensPath).map((entry) => entry.token);\n}\n\nexport function resolveConnectorFromToken(token: string, tokensPath?: string): string | undefined {\n return loadTokenStore(tokensPath).tokens.find((t) => t.token === token)?.connector;\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  assertPathInsideRoot
3
- } from "./chunk-5GPPACXK.js";
3
+ } from "./chunk-VSMRDBFT.js";
4
4
  import {
5
5
  RECALL_FALLBACK_DIRS
6
6
  } from "./chunk-VS2IYZRU.js";
@@ -92,4 +92,4 @@ async function scanMemoryDir(memoryDir) {
92
92
  export {
93
93
  scanMemoryDir
94
94
  };
95
- //# sourceMappingURL=chunk-AER6MT24.js.map
95
+ //# sourceMappingURL=chunk-6SXVCD7W.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-M7GGMGMZ.js";
3
+ } from "./chunk-SVOHGUMI.js";
4
4
 
5
5
  // src/semantic-rule-promotion.ts
6
6
  import { createHash, randomUUID } from "crypto";
@@ -529,4 +529,4 @@ export {
529
529
  setSemanticRulePromotionTestHooks,
530
530
  promoteSemanticRuleFromMemory
531
531
  };
532
- //# sourceMappingURL=chunk-OHDJ34CC.js.map
532
+ //# sourceMappingURL=chunk-72WG5QKN.js.map
@@ -1,10 +1,10 @@
1
+ import {
2
+ scanMemoryDir
3
+ } from "./chunk-6SXVCD7W.js";
1
4
  import {
2
5
  isSearchAborted,
3
6
  throwIfSearchAborted
4
7
  } from "./chunk-CINZGPSJ.js";
5
- import {
6
- scanMemoryDir
7
- } from "./chunk-AER6MT24.js";
8
8
  import {
9
9
  reportSearchDegradation,
10
10
  resolveEnsureCollectionArgs
@@ -613,4 +613,4 @@ export {
613
613
  resolveOramaCollectionDbFilePath,
614
614
  OramaBackend
615
615
  };
616
- //# sourceMappingURL=chunk-CZAZX7JP.js.map
616
+ //# sourceMappingURL=chunk-7MW3CVLD.js.map
@@ -216,4 +216,4 @@ export {
216
216
  getCausalTrajectoryStoreStatus,
217
217
  searchCausalTrajectories
218
218
  };
219
- //# sourceMappingURL=chunk-CXLNZXG5.js.map
219
+ //# sourceMappingURL=chunk-7XGSIUP5.js.map
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-UI4FVU6T.js";
5
5
  import {
6
6
  StorageManager
7
- } from "./chunk-M7GGMGMZ.js";
7
+ } from "./chunk-SVOHGUMI.js";
8
8
  import {
9
9
  parseContinuityImprovementLoops
10
10
  } from "./chunk-LDXUBPMO.js";
@@ -1276,4 +1276,4 @@ export {
1276
1276
  defaultTierMigrationCycleBudget,
1277
1277
  CompoundingEngine
1278
1278
  };
1279
- //# sourceMappingURL=chunk-CZAA5N5S.js.map
1279
+ //# sourceMappingURL=chunk-AYZJID4S.js.map
@@ -2,6 +2,16 @@
2
2
  import { promises as fsPromises } from "fs";
3
3
  import * as path from "path";
4
4
  var DEFAULT_SELF_NAME = "Me";
5
+ var SELF_MARKER_SUFFIX = " (you)";
6
+ var SELF_MARKER = "(you)";
7
+ function stripSelfMarker(name) {
8
+ const trimmed = name.trimEnd();
9
+ if (!trimmed.endsWith(SELF_MARKER)) return trimmed;
10
+ return trimmed.slice(0, -SELF_MARKER.length).trimEnd();
11
+ }
12
+ function hasSelfMarker(label) {
13
+ return label.trimEnd().endsWith(SELF_MARKER);
14
+ }
5
15
  function emptySpeakerRegistry() {
6
16
  return { version: 1, selfName: DEFAULT_SELF_NAME, speakers: {} };
7
17
  }
@@ -63,18 +73,22 @@ function resolveSpeaker(sourceId, segment, registry) {
63
73
  const override = registry.speakers[speakerRegistryKey(sourceId, segment.speakerKey)];
64
74
  if (override) {
65
75
  const isSelf = override.isSelf === true;
76
+ const base = stripSelfMarker(override.name);
66
77
  return {
67
- label: isSelf ? `${override.name} (you)` : override.name,
78
+ label: isSelf ? `${base}${SELF_MARKER_SUFFIX}` : base,
68
79
  isSelf
69
80
  };
70
81
  }
71
82
  if (segment.isWearer === true) {
72
- return { label: `${registry.selfName} (you)`, isSelf: true };
83
+ return {
84
+ label: `${stripSelfMarker(registry.selfName)}${SELF_MARKER_SUFFIX}`,
85
+ isSelf: true
86
+ };
73
87
  }
74
88
  if (typeof segment.speakerName === "string" && segment.speakerName.trim().length > 0) {
75
- return { label: segment.speakerName.trim(), isSelf: false };
89
+ return { label: stripSelfMarker(segment.speakerName.trim()), isSelf: false };
76
90
  }
77
- const key = segment.speakerKey.trim();
91
+ const key = stripSelfMarker(segment.speakerKey.trim());
78
92
  if (/^\d+$/.test(key)) {
79
93
  return { label: `Speaker ${key}`, isSelf: false };
80
94
  }
@@ -97,13 +111,46 @@ function distinctSpeakerLabels(sourceId, segments, registry) {
97
111
  import { createHash } from "crypto";
98
112
  var WEARABLES_DIR_NAME = "wearables";
99
113
  var DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
114
+ var TRANSCRIPT_FORMAT_VERSION = 2;
115
+ function isRegexWhitespace(code) {
116
+ return code >= 9 && code <= 13 || code === 32 || code === 160 || code === 5760 || code >= 8192 && code <= 8202 || code === 8232 || code === 8233 || code === 8239 || code === 8287 || code === 12288 || code === 65279;
117
+ }
118
+ function parseTranscriptSegmentLine(line) {
119
+ if (!line.startsWith("**")) return null;
120
+ let labelEnd = -1;
121
+ for (let i = 3; i < line.length; i++) {
122
+ if (line.charCodeAt(i) !== 42 || i + 3 >= line.length || line.charCodeAt(i + 1) !== 42 || !isRegexWhitespace(line.charCodeAt(i + 2)) || line.charCodeAt(i + 3) !== 91) {
123
+ continue;
124
+ }
125
+ labelEnd = i;
126
+ break;
127
+ }
128
+ if (labelEnd === -1) return null;
129
+ const label = line.slice(2, labelEnd);
130
+ const clockStart = labelEnd + 4;
131
+ let clockEnd = -1;
132
+ for (let i = clockStart + 1; i < line.length; i++) {
133
+ if (line.charCodeAt(i) !== 93 || i + 2 >= line.length || line.charCodeAt(i + 1) !== 58 || !isRegexWhitespace(line.charCodeAt(i + 2))) {
134
+ continue;
135
+ }
136
+ clockEnd = i;
137
+ break;
138
+ }
139
+ if (clockEnd === -1) return null;
140
+ return {
141
+ label,
142
+ clock: line.slice(clockStart, clockEnd),
143
+ text: line.slice(clockEnd + 3)
144
+ };
145
+ }
100
146
  function isValidTranscriptDate(date) {
101
147
  if (!DATE_PATTERN.test(date)) return false;
102
148
  const parsed = /* @__PURE__ */ new Date(`${date}T00:00:00Z`);
103
149
  return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === date;
104
150
  }
105
151
  function hashTranscriptBody(body) {
106
- return createHash("sha256").update(body, "utf-8").digest("hex");
152
+ return createHash("sha256").update(`v${TRANSCRIPT_FORMAT_VERSION}
153
+ ${body}`, "utf-8").digest("hex");
107
154
  }
108
155
  function formatClockTime(iso, timezone) {
109
156
  if (!iso) return "--:--";
@@ -126,6 +173,48 @@ function conversationDurationMinutes(conversation) {
126
173
  if (Number.isNaN(start) || Number.isNaN(end) || end <= start) return 0;
127
174
  return (end - start) / 6e4;
128
175
  }
176
+ function escapeSegmentText(text) {
177
+ return text.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
178
+ }
179
+ function unescapeSegmentText(text) {
180
+ return text.replace(/\\(.)/g, (_match, ch) => {
181
+ if (ch === "n") return "\n";
182
+ if (ch === "r") return "\r";
183
+ if (ch === "\\") return "\\";
184
+ return "\\" + ch;
185
+ });
186
+ }
187
+ function escapeSpeakerLabel(label) {
188
+ return label.replace(/\\/g, "\\\\").replace(/\*/g, "\\*").replace(/\[/g, "\\[").replace(/\]/g, "\\]").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
189
+ }
190
+ function unescapeSpeakerLabel(label) {
191
+ return label.replace(/\\(.)/g, (_match, ch) => {
192
+ if (ch === "\\") return "\\";
193
+ if (ch === "*") return "*";
194
+ if (ch === "[") return "[";
195
+ if (ch === "]") return "]";
196
+ if (ch === "n") return "\n";
197
+ if (ch === "r") return "\r";
198
+ return "\\" + ch;
199
+ });
200
+ }
201
+ function bodyIsEscaped(meta) {
202
+ return meta !== null && meta !== void 0 && typeof meta.formatVersion === "number" && meta.formatVersion >= TRANSCRIPT_FORMAT_VERSION;
203
+ }
204
+ function decodeTranscriptBody(body, escaped = false) {
205
+ if (!escaped) return body;
206
+ const lines = body.split("\n");
207
+ for (let i = 0; i < lines.length; i++) {
208
+ const match = parseTranscriptSegmentLine(lines[i]);
209
+ if (match === null) continue;
210
+ const { label, clock, text: rawText } = match;
211
+ const decodedText = unescapeSegmentText(rawText);
212
+ const decodedLabel = unescapeSpeakerLabel(label);
213
+ if (decodedText === rawText && decodedLabel === label) continue;
214
+ lines[i] = `**${decodedLabel}** [${clock}]: ${decodedText}`;
215
+ }
216
+ return lines.join("\n");
217
+ }
129
218
  function composeDayTranscriptBody(sourceId, date, timezone, conversations, registry) {
130
219
  const lines = [];
131
220
  lines.push(`# ${sourceId} transcript \u2014 ${date}`);
@@ -150,7 +239,9 @@ function composeDayTranscriptBody(sourceId, date, timezone, conversations, regis
150
239
  for (const segment of conversation.segments) {
151
240
  const { label } = resolveSpeaker(sourceId, segment, registry);
152
241
  const at = formatClockTime(segment.startIso, timezone);
153
- lines.push(`**${label}** [${at}]: ${segment.text}`);
242
+ lines.push(
243
+ `**${escapeSpeakerLabel(label)}** [${at}]: ${escapeSegmentText(segment.text)}`
244
+ );
154
245
  }
155
246
  lines.push("");
156
247
  }
@@ -172,12 +263,16 @@ function composeDayTranscriptMeta(sourceId, date, timezone, conversations, regis
172
263
  speakers: distinctSpeakerLabels(sourceId, allSegments, registry),
173
264
  durationMinutes,
174
265
  contentHash: hashTranscriptBody(body),
175
- syncedAt
266
+ syncedAt,
267
+ formatVersion: TRANSCRIPT_FORMAT_VERSION
176
268
  };
177
269
  }
178
270
  function serializeDayTranscript(meta, body) {
179
271
  const lines = ["---"];
180
272
  lines.push(`kind: ${meta.kind}`);
273
+ if (typeof meta.formatVersion === "number") {
274
+ lines.push(`formatVersion: ${meta.formatVersion}`);
275
+ }
181
276
  lines.push(`source: ${JSON.stringify(meta.source)}`);
182
277
  lines.push(`date: ${JSON.stringify(meta.date)}`);
183
278
  lines.push(`timezone: ${JSON.stringify(meta.timezone)}`);
@@ -230,9 +325,13 @@ function parseDayTranscript(raw) {
230
325
  if (!source || !date) return null;
231
326
  const meta = {
232
327
  kind: "wearable-transcript",
328
+ ...scalars.has("formatVersion") ? { formatVersion: parseNonNegativeInt(scalars.get("formatVersion")) } : {},
233
329
  source,
234
330
  date,
235
- timezone: scalars.get("timezone") ?? "UTC",
331
+ // Preserve a missing timezone field as "" rather than coercing to a
332
+ // default: the fusion identity guard must see "no resolvable tz id"
333
+ // and skip, never silently match a known zone.
334
+ timezone: scalars.get("timezone") ?? "",
236
335
  conversationCount: parseNonNegativeInt(scalars.get("conversationCount")),
237
336
  segmentCount: parseNonNegativeInt(scalars.get("segmentCount")),
238
337
  speakers,
@@ -262,6 +361,7 @@ function parseNonNegativeInt(value) {
262
361
 
263
362
  export {
264
363
  DEFAULT_SELF_NAME,
364
+ hasSelfMarker,
265
365
  emptySpeakerRegistry,
266
366
  speakersFilePath,
267
367
  loadSpeakerRegistry,
@@ -270,11 +370,18 @@ export {
270
370
  resolveSpeaker,
271
371
  distinctSpeakerLabels,
272
372
  WEARABLES_DIR_NAME,
373
+ TRANSCRIPT_FORMAT_VERSION,
374
+ parseTranscriptSegmentLine,
273
375
  isValidTranscriptDate,
274
376
  hashTranscriptBody,
377
+ escapeSegmentText,
378
+ unescapeSegmentText,
379
+ unescapeSpeakerLabel,
380
+ bodyIsEscaped,
381
+ decodeTranscriptBody,
275
382
  composeDayTranscriptBody,
276
383
  composeDayTranscriptMeta,
277
384
  serializeDayTranscript,
278
385
  parseDayTranscript
279
386
  };
280
- //# sourceMappingURL=chunk-M7XQSUBB.js.map
387
+ //# sourceMappingURL=chunk-B3ABVQ36.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wearables/speakers.ts","../src/wearables/day-store.ts"],"sourcesContent":["/**\n * Wearable speaker registry — maps provider diarization labels to\n * human names, persistently, per source.\n *\n * Providers expose speakers differently (Limitless: names + a \"user\"\n * marker; Bee: opaque labels like \"0\"/\"1\"; Omi: \"SPEAKER_00\" + is_user\n * + optional person ids). The registry stores operator-confirmed\n * mappings keyed `<sourceId>:<speakerKey>` in\n * `state/wearables/speakers.json`, plus the wearer's display name.\n *\n * Resolution precedence (most-authoritative first):\n * 1. registry override for `<sourceId>:<speakerKey>`\n * 2. provider-identified wearer -> selfName\n * 3. provider-supplied speaker name\n * 4. the raw speaker key, prefixed \"Speaker\" when it is bare digits\n */\n\nimport { promises as fsPromises } from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport interface SpeakerOverride {\n name: string;\n /** Mark this speaker as the wearer (their words become \"you\"). */\n isSelf?: boolean;\n updatedAt: string;\n}\n\nexport interface SpeakerRegistry {\n version: 1;\n /** Display name used for the wearer across all sources. */\n selfName: string;\n /** Overrides keyed `<sourceId>:<speakerKey>`. */\n speakers: Record<string, SpeakerOverride>;\n}\n\nexport const DEFAULT_SELF_NAME = \"Me\";\n\n/**\n * Reserved self marker appended to a wearer's display label so the\n * rendered transcript carries explicit, parseable self metadata. It is\n * RESERVED: a non-self display label can never carry it (see\n * `stripSelfMarker`), so when `reconstructFusionInputs` reads it back it\n * is authoritative self metadata — never a coincidental display-name\n * suffix that would misattribute another speaker as the wearer\n * (issue #1849).\n */\nexport const SELF_MARKER_SUFFIX = \" (you)\";\nconst SELF_MARKER = \"(you)\";\n\n/**\n * Remove a reserved `(you)` self marker from a display name. Applied to\n * every base name before it becomes a label so the marker can only ever\n * appear as the renderer-appended self flag, never as part of a stored\n * non-self display name.\n *\n * Implemented with plain string ops instead of a regex so the leading\n * `\\s*` (no anchor) cannot cause the O(n²) backtracking CodeQL flags\n * on whitespace-only input (js/polynomial-redos).\n */\nexport function stripSelfMarker(name: string): string {\n const trimmed = name.trimEnd();\n if (!trimmed.endsWith(SELF_MARKER)) return trimmed;\n return trimmed.slice(0, -SELF_MARKER.length).trimEnd();\n}\n\n/**\n * Whether a rendered label carries the reserved self marker. This is the\n * single authoritative self-status detector shared by the renderer and\n * the fusion reconstructor (issue #1849).\n */\nexport function hasSelfMarker(label: string): boolean {\n return label.trimEnd().endsWith(SELF_MARKER);\n}\n\nexport function emptySpeakerRegistry(): SpeakerRegistry {\n return { version: 1, selfName: DEFAULT_SELF_NAME, speakers: {} };\n}\n\nexport function speakersFilePath(memoryDir: string): string {\n return path.join(memoryDir, \"state\", \"wearables\", \"speakers.json\");\n}\n\nexport async function loadSpeakerRegistry(\n memoryDir: string,\n): Promise<SpeakerRegistry> {\n const filePath = speakersFilePath(memoryDir);\n let raw: string;\n try {\n raw = await fsPromises.readFile(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return emptySpeakerRegistry();\n }\n throw err;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `wearables speakers file is not valid JSON (state/wearables/speakers.json): ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n Array.isArray(parsed) ||\n typeof (parsed as SpeakerRegistry).speakers !== \"object\" ||\n (parsed as SpeakerRegistry).speakers === null\n ) {\n throw new Error(\n 'wearables speakers file has an unexpected shape (state/wearables/speakers.json); expected {\"version\":1,\"selfName\":\"...\",\"speakers\":{}}',\n );\n }\n const registry = parsed as SpeakerRegistry;\n return {\n version: 1,\n selfName:\n typeof registry.selfName === \"string\" && registry.selfName.trim().length > 0\n ? registry.selfName.trim()\n : DEFAULT_SELF_NAME,\n speakers: registry.speakers,\n };\n}\n\nexport async function saveSpeakerRegistry(\n memoryDir: string,\n registry: SpeakerRegistry,\n): Promise<void> {\n const filePath = speakersFilePath(memoryDir);\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now().toString(36)}`;\n await fsPromises.writeFile(\n tmpPath,\n `${JSON.stringify(registry, null, 2)}\\n`,\n \"utf-8\",\n );\n try {\n await fsPromises.rename(tmpPath, filePath);\n } catch (err) {\n await fsPromises.unlink(tmpPath).catch(() => undefined);\n throw err;\n }\n}\n\nexport function speakerRegistryKey(\n sourceId: string,\n speakerKey: string,\n): string {\n return `${sourceId}:${speakerKey}`;\n}\n\nexport interface ResolvedSpeaker {\n /** Display label used in transcripts, e.g. \"Jane\" or \"Me (you)\". */\n label: string;\n /** Whether this speaker is the wearer. */\n isSelf: boolean;\n}\n\nexport function resolveSpeaker(\n sourceId: string,\n segment: { speakerKey: string; speakerName?: string; isWearer?: boolean },\n registry: SpeakerRegistry,\n): ResolvedSpeaker {\n const override = registry.speakers[speakerRegistryKey(sourceId, segment.speakerKey)];\n if (override) {\n const isSelf = override.isSelf === true;\n // Reserve the `(you)` marker: strip any pre-existing marker from the\n // base name so a NON-self label can never carry it. Without this, a\n // speaker override like \"Pat (you)\" set WITHOUT --self would render\n // as \"Pat (you)\" and the fusion reconstructor would read the suffix\n // as self metadata, attributing Pat's words to the wearer (issue\n // #1849).\n const base = stripSelfMarker(override.name);\n return {\n label: isSelf ? `${base}${SELF_MARKER_SUFFIX}` : base,\n isSelf,\n };\n }\n if (segment.isWearer === true) {\n return {\n label: `${stripSelfMarker(registry.selfName)}${SELF_MARKER_SUFFIX}`,\n isSelf: true,\n };\n }\n if (\n typeof segment.speakerName === \"string\" &&\n segment.speakerName.trim().length > 0\n ) {\n // Provider-supplied names are never self here; strip a stray marker\n // so a provider label cannot masquerade as the reserved self flag.\n return { label: stripSelfMarker(segment.speakerName.trim()), isSelf: false };\n }\n // Strip the reserved `(you)` marker from the raw key so a provider\n // speakerKey like \"Pat (you)\" can never leak the marker into a non-self\n // rendered label — the fusion reconstructor reads the suffix as\n // authoritative self metadata and would misattribute the speaker as the\n // wearer (issue #1849).\n const key = stripSelfMarker(segment.speakerKey.trim());\n // Bare diarization indexes read better with a prefix.\n if (/^\\d+$/.test(key)) {\n return { label: `Speaker ${key}`, isSelf: false };\n }\n return { label: key.length > 0 ? key : \"Unknown speaker\", isSelf: false };\n}\n\n/**\n * Distinct speaker labels for a set of segments, in first-appearance\n * order — used for day-transcript frontmatter.\n */\nexport function distinctSpeakerLabels(\n sourceId: string,\n segments: Array<{ speakerKey: string; speakerName?: string; isWearer?: boolean }>,\n registry: SpeakerRegistry,\n): string[] {\n const labels: string[] = [];\n const seen = new Set<string>();\n for (const segment of segments) {\n const { label } = resolveSpeaker(sourceId, segment, registry);\n if (!seen.has(label)) {\n seen.add(label);\n labels.push(label);\n }\n }\n return labels;\n}\n","/**\n * Wearable day-transcript composition and parsing.\n *\n * One markdown file per source per day, stored under\n * `<memoryDir>/wearables/<source>/<YYYY-MM-DD>.md` with YAML\n * frontmatter. The location is deliberate:\n *\n * - it is OUTSIDE the memory scan roots (facts/, procedures/,\n * reasoning-traces/, corrections/), so transcripts never appear as\n * memories in recall or governance passes;\n * - it is INSIDE the QMD collection root (the memory dir), so day\n * transcripts are full-text searchable after the next index update.\n *\n * Files are rebuilt idempotently from provider data on every sync; the\n * body hash in frontmatter lets the pipeline skip rewriting (and\n * re-extracting) unchanged days.\n *\n * This module is pure composition/parsing — file IO lives in\n * `StorageManager` so encrypted-at-rest deployments and atomic write\n * semantics are inherited from the same code paths memories use.\n */\n\nimport { createHash } from \"node:crypto\";\n\nimport type { SpeakerRegistry } from \"./speakers.js\";\nimport { distinctSpeakerLabels, resolveSpeaker } from \"./speakers.js\";\nimport type {\n WearableConversation,\n WearableDayTranscript,\n WearableDayTranscriptMeta,\n} from \"./types.js\";\n\nexport const WEARABLES_DIR_NAME = \"wearables\";\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/**\n * Day-transcript body serialization format version. Folded into\n * `hashTranscriptBody` so a version bump invalidates files written by an\n * older serializer and forces an idempotent rewrite. Decoders gate on this:\n * only bodies whose parsed meta carries >= this version have escape\n * sequences decoded; legacy bodies (absent / older) are left byte-for-byte\n * unchanged so a literal two-character `\\n`/`\\r` in a pre-escaper\n * transcript is never altered (issue #1849).\n */\nexport const TRANSCRIPT_FORMAT_VERSION = 2;\n\n/**\n * Result of parsing a rendered transcript segment line.\n */\nexport interface TranscriptSegmentMatch {\n /** Speaker label text between the `**` delimiters. */\n label: string;\n /** Clock text between the `[` `]` delimiters. */\n clock: string;\n /** Segment text after the colon. */\n text: string;\n}\n\n/**\n * Test a single character code point against the JavaScript regex `\\s`\n * whitespace class without invoking a regex. Used by the linear segment\n * line parser so no quantified pattern is ever evaluated on untrusted\n * transcript content (CodeQL polynomial-redos, issue #1849).\n */\nfunction isRegexWhitespace(code: number): boolean {\n return (\n (code >= 0x09 && code <= 0x0d) ||\n code === 0x20 ||\n code === 0xa0 ||\n code === 0x1680 ||\n (code >= 0x2000 && code <= 0x200a) ||\n code === 0x2028 ||\n code === 0x2029 ||\n code === 0x202f ||\n code === 0x205f ||\n code === 0x3000 ||\n code === 0xfeff\n );\n}\n\n/**\n * Parse a rendered transcript segment line (`**label** [clock]: text`)\n * into its three components using a linear, bounded scan — a CodeQL-safe\n * replacement for the polynomial-risk regex that previously matched this\n * format. Returns null when the line is not a segment line.\n *\n * The scan finds the FIRST occurrence of each structural delimiter\n * (`**` after the label, `]` after the clock) exactly as the original\n * non-greedy regex did, and verifies the fixed separator characters\n * (`\\s`, `[`, `:`, `\\s`) by single-character code-point comparison.\n * No quantified regex is evaluated on the line (#1849).\n *\n * Escaped labels (formatVersion >= 2) never contain an unescaped `**`,\n * `[`, or `]`, and rendered clocks are always `HH:MM` / `--:--`, so the\n * first delimiter is always the correct one for every well-formed\n * transcript. Legacy unescaped labels are used verbatim by the caller\n * and parsed identically here.\n */\nexport function parseTranscriptSegmentLine(\n line: string,\n): TranscriptSegmentMatch | null {\n // Must start with '**'.\n if (!line.startsWith(\"**\")) return null;\n\n // Find the closing '**' of the label: the first '**' at index >= 3\n // (label is at least 1 char) that is immediately followed by \\s and\n // then '['. This mirrors the non-greedy regex: the engine tries the\n // shortest label first and extends only when the subsequent fixed\n // delimiters do not line up. An escaped label ending with '\\*' is\n // correctly handled because the spurious '**' (escape-star + close-\n // star) is not followed by \\s and the scan continues.\n let labelEnd = -1;\n for (let i = 3; i < line.length; i++) {\n if (\n line.charCodeAt(i) !== 0x2a /* '*' */ ||\n i + 3 >= line.length ||\n line.charCodeAt(i + 1) !== 0x2a /* '*' */ ||\n !isRegexWhitespace(line.charCodeAt(i + 2)) ||\n line.charCodeAt(i + 3) !== 0x5b /* '[' */\n ) {\n continue;\n }\n labelEnd = i;\n break;\n }\n if (labelEnd === -1) return null;\n\n const label = line.slice(2, labelEnd);\n // Clock content starts after the closing '**', the \\s, and the '['.\n const clockStart = labelEnd + 4;\n\n // Find the closing ']' of the clock: the first ']' at index >=\n // clockStart + 1 (clock is at least 1 char) that is immediately\n // followed by ':' and \\s — matching the non-greedy regex.\n let clockEnd = -1;\n for (let i = clockStart + 1; i < line.length; i++) {\n if (\n line.charCodeAt(i) !== 0x5d /* ']' */ ||\n i + 2 >= line.length ||\n line.charCodeAt(i + 1) !== 0x3a /* ':' */ ||\n !isRegexWhitespace(line.charCodeAt(i + 2))\n ) {\n continue;\n }\n clockEnd = i;\n break;\n }\n if (clockEnd === -1) return null;\n\n return {\n label,\n clock: line.slice(clockStart, clockEnd),\n text: line.slice(clockEnd + 3),\n };\n}\n\nexport function isValidTranscriptDate(date: string): boolean {\n if (!DATE_PATTERN.test(date)) return false;\n const parsed = new Date(`${date}T00:00:00Z`);\n return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === date;\n}\n\nexport function hashTranscriptBody(body: string): string {\n // The version prefix makes the hash an idempotency key for the body AS\n // SERIALIZED under the current format: when the escape encoding changes\n // (version bump) every existing file's stored hash no longer matches and\n // the pipeline rewrites it with the new marker — no separate migration\n // pass needed (issue #1849).\n return createHash(\"sha256\")\n .update(`v${TRANSCRIPT_FORMAT_VERSION}\\n${body}`, \"utf-8\")\n .digest(\"hex\");\n}\n\nfunction formatClockTime(iso: string | undefined, timezone: string): string {\n if (!iso) return \"--:--\";\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) return \"--:--\";\n try {\n return new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n hour12: false,\n hour: \"2-digit\",\n minute: \"2-digit\",\n }).format(new Date(ms));\n } catch {\n // Unknown timezone identifiers fall back to UTC rather than\n // crashing a sync that already fetched data.\n return new Date(ms).toISOString().slice(11, 16);\n }\n}\n\nfunction conversationDurationMinutes(conversation: WearableConversation): number {\n const start = Date.parse(conversation.startIso);\n const end = conversation.endIso ? Date.parse(conversation.endIso) : NaN;\n if (Number.isNaN(start) || Number.isNaN(end) || end <= start) return 0;\n return (end - start) / 60_000;\n}\n\n/**\n * Escape segment text for the line-based markdown format so that embedded\n * newlines, carriage returns, and backslashes survive the serialize →\n * reconstruct round-trip losslessly. Reversed by `unescapeSegmentText`\n * (this module — the single decode primitive shared with the fusion\n * reconstruct path and every view/search/index surface).\n *\n * Without this, a segment whose text contains a newline is split across\n * multiple physical lines; the reconstruct path treats each line\n * independently and the continuation is silently dropped (or worse, a\n * continuation line that looks like a heading/clock is mis-parsed).\n */\nexport function escapeSegmentText(text: string): string {\n return text\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\r/g, \"\\\\r\");\n}\n\n/**\n * Reverse `escapeSegmentText`. Unknown escape sequences (a lone\n * backslash followed by a character the escaper never emits) are passed\n * through literally so legacy transcripts that never went through the\n * escaper still round-trip their original text. This is the SINGLE\n * decode primitive: the fusion reconstruct path and every user-facing\n * view/search/index surface call it so escaped storage never leaks to\n * display (#1849).\n */\nexport function unescapeSegmentText(text: string): string {\n return text.replace(/\\\\(.)/g, (_match, ch: string) => {\n if (ch === \"n\") return \"\\n\";\n if (ch === \"r\") return \"\\r\";\n if (ch === \"\\\\\") return \"\\\\\";\n return \"\\\\\" + ch;\n });\n}\n\n/**\n * Escape a speaker label for the line-based markdown format so that\n * markdown-delimiter characters in an arbitrary user/provider label\n * (`**`, `[`, `]`), the escape character (`\\`), and embedded newlines/\n * carriage returns cannot break `parseTranscriptSegmentLine` parsing.\n * Without this, a label containing `**` (or `** [clock]:`) can make the\n * non-greedy delimiter match land on the wrong `**` and mis-parse the\n * clock/text — or fail to parse the line at all. Reversed by\n * `unescapeSpeakerLabel` (this module). Legacy transcripts whose labels\n * were never escaped still parse: `unescapeSpeakerLabel` is a no-op on\n * any label without a backslash escape sequence (#1849).\n */\nexport function escapeSpeakerLabel(label: string): string {\n return label\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\*/g, \"\\\\*\")\n .replace(/\\[/g, \"\\\\[\")\n .replace(/\\]/g, \"\\\\]\")\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\r/g, \"\\\\r\");\n}\n\n/**\n * Reverse `escapeSpeakerLabel`. Unknown escape sequences (a backslash\n * followed by a character the escaper never emits) are passed through\n * literally so legacy transcripts with raw labels still round-trip\n * their original text, mirroring `unescapeSegmentText` for segment text.\n */\nexport function unescapeSpeakerLabel(label: string): string {\n return label.replace(/\\\\(.)/g, (_match, ch: string) => {\n if (ch === \"\\\\\") return \"\\\\\";\n if (ch === \"*\") return \"*\";\n if (ch === \"[\") return \"[\";\n if (ch === \"]\") return \"]\";\n if (ch === \"n\") return \"\\n\";\n if (ch === \"r\") return \"\\r\";\n return \"\\\\\" + ch;\n });\n}\n\n/**\n * Whether a parsed day-transcript's body was written by the escape-aware\n * serializer (meta carries `formatVersion` >= `TRANSCRIPT_FORMAT_VERSION`).\n * Legacy bodies (no marker, or an older version) must NOT be decoded: their\n * literal two-character `\\n`, `\\r`, and lone backslashes are original\n * content, not escape sequences (issue #1849).\n */\nexport function bodyIsEscaped(\n meta: { formatVersion?: number } | null | undefined,\n): boolean {\n return (\n meta !== null &&\n meta !== undefined &&\n typeof meta.formatVersion === \"number\" &&\n meta.formatVersion >= TRANSCRIPT_FORMAT_VERSION\n );\n}\n\n/**\n * Decode the escaped segment text AND speaker label of a stored\n * transcript body into the ORIGINAL forms for user-facing\n * view/search/index surfaces. Only segment lines are touched: the text\n * is decoded via `unescapeSegmentText` and the label via\n * `unescapeSpeakerLabel`; headings, locations, and the H1 header are\n * NOT escaped on write and pass through unchanged, so a title or\n * location containing a literal backslash is never altered. The fusion\n * reconstruct path does NOT use this — it decodes each segment once\n * during parse — so callers feeding bodies back into reconstruct must\n * pass the RAW stored body, not a decoded one, to avoid double-decoding.\n *\n * FORMAT-AWARE (issue #1849): when `escaped` is falsy (the default) the\n * body is returned byte-for-byte unchanged. Only bodies written by the\n * escape-aware serializer (`escaped = true`, derived from\n * `bodyIsEscaped(meta)`) are decoded, so a legacy transcript's literal\n * two-character `\\n`/`\\r` or lone backslash is never altered. Callers\n * that have the parsed meta should pass `bodyIsEscaped(meta)`.\n */\nexport function decodeTranscriptBody(body: string, escaped = false): string {\n if (!escaped) return body;\n const lines = body.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const match = parseTranscriptSegmentLine(lines[i]);\n if (match === null) continue;\n const { label, clock, text: rawText } = match;\n const decodedText = unescapeSegmentText(rawText);\n const decodedLabel = unescapeSpeakerLabel(label);\n if (decodedText === rawText && decodedLabel === label) continue;\n lines[i] = `**${decodedLabel}** [${clock}]: ${decodedText}`;\n }\n return lines.join(\"\\n\");\n}\n\n/** Compose the markdown body (no frontmatter) for one source/day. */\nexport function composeDayTranscriptBody(\n sourceId: string,\n date: string,\n timezone: string,\n conversations: WearableConversation[],\n registry: SpeakerRegistry,\n): string {\n const lines: string[] = [];\n lines.push(`# ${sourceId} transcript — ${date}`);\n lines.push(\"\");\n const ordered = [...conversations].sort((a, b) => {\n const aMs = Date.parse(a.startIso);\n const bMs = Date.parse(b.startIso);\n if (aMs < bMs) return -1;\n if (aMs > bMs) return 1;\n // Stable secondary key so equal start times order deterministically.\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n });\n for (const conversation of ordered) {\n const start = formatClockTime(conversation.startIso, timezone);\n const end = formatClockTime(conversation.endIso, timezone);\n const title = conversation.title?.trim();\n const heading = title && title.length > 0 ? ` · ${title}` : \"\";\n lines.push(`## ${start}–${end}${heading} (conversation ${conversation.id})`);\n if (conversation.location) {\n lines.push(`*Location: ${conversation.location}*`);\n }\n lines.push(\"\");\n for (const segment of conversation.segments) {\n const { label } = resolveSpeaker(sourceId, segment, registry);\n const at = formatClockTime(segment.startIso, timezone);\n lines.push(\n `**${escapeSpeakerLabel(label)}** [${at}]: ${escapeSegmentText(segment.text)}`,\n );\n }\n lines.push(\"\");\n }\n return `${lines.join(\"\\n\").trimEnd()}\\n`;\n}\n\nexport function composeDayTranscriptMeta(\n sourceId: string,\n date: string,\n timezone: string,\n conversations: WearableConversation[],\n registry: SpeakerRegistry,\n body: string,\n syncedAt: string,\n): WearableDayTranscriptMeta {\n const allSegments = conversations.flatMap((c) => c.segments);\n const durationMinutes = Math.round(\n conversations.reduce((sum, c) => sum + conversationDurationMinutes(c), 0),\n );\n return {\n kind: \"wearable-transcript\",\n source: sourceId,\n date,\n timezone,\n conversationCount: conversations.length,\n segmentCount: allSegments.length,\n speakers: distinctSpeakerLabels(sourceId, allSegments, registry),\n durationMinutes,\n contentHash: hashTranscriptBody(body),\n syncedAt,\n formatVersion: TRANSCRIPT_FORMAT_VERSION,\n };\n}\n\n/** Serialize meta + body into the persisted file format. */\nexport function serializeDayTranscript(\n meta: WearableDayTranscriptMeta,\n body: string,\n): string {\n const lines: string[] = [\"---\"];\n lines.push(`kind: ${meta.kind}`);\n if (typeof meta.formatVersion === \"number\") {\n lines.push(`formatVersion: ${meta.formatVersion}`);\n }\n lines.push(`source: ${JSON.stringify(meta.source)}`);\n lines.push(`date: ${JSON.stringify(meta.date)}`);\n lines.push(`timezone: ${JSON.stringify(meta.timezone)}`);\n lines.push(`conversationCount: ${meta.conversationCount}`);\n lines.push(`segmentCount: ${meta.segmentCount}`);\n if (meta.speakers.length === 0) {\n lines.push(\"speakers: []\");\n } else {\n lines.push(\"speakers:\");\n for (const speaker of meta.speakers) {\n lines.push(` - ${JSON.stringify(speaker)}`);\n }\n }\n lines.push(`durationMinutes: ${meta.durationMinutes}`);\n lines.push(`contentHash: ${JSON.stringify(meta.contentHash)}`);\n lines.push(`syncedAt: ${JSON.stringify(meta.syncedAt)}`);\n lines.push(\"---\");\n lines.push(\"\");\n return `${lines.join(\"\\n\")}${body}`;\n}\n\n/**\n * Parse a persisted day-transcript file. Returns null when the content\n * does not look like a wearable transcript (wrong kind, missing\n * frontmatter) so callers can distinguish \"not a transcript\" from a\n * read error.\n */\nexport function parseDayTranscript(raw: string): WearableDayTranscript | null {\n if (!raw.startsWith(\"---\\n\")) return null;\n const closeIndex = raw.indexOf(\"\\n---\\n\", 4);\n if (closeIndex === -1) return null;\n const header = raw.slice(4, closeIndex);\n const body = raw.slice(closeIndex + 5).replace(/^\\n/, \"\");\n\n const scalars = new Map<string, string>();\n const speakers: string[] = [];\n let inSpeakers = false;\n for (const line of header.split(\"\\n\")) {\n if (inSpeakers) {\n const item = line.match(/^ {2}- (.*)$/);\n if (item) {\n speakers.push(parseYamlScalar(item[1]));\n continue;\n }\n inSpeakers = false;\n }\n if (line === \"speakers:\") {\n inSpeakers = true;\n continue;\n }\n if (line === \"speakers: []\") continue;\n const match = line.match(/^([A-Za-z][A-Za-z0-9]*): (.*)$/);\n if (match) scalars.set(match[1], parseYamlScalar(match[2]));\n }\n\n if (scalars.get(\"kind\") !== \"wearable-transcript\") return null;\n const source = scalars.get(\"source\");\n const date = scalars.get(\"date\");\n if (!source || !date) return null;\n\n const meta: WearableDayTranscriptMeta = {\n kind: \"wearable-transcript\",\n ...(scalars.has(\"formatVersion\")\n ? { formatVersion: parseNonNegativeInt(scalars.get(\"formatVersion\")) }\n : {}),\n source,\n date,\n // Preserve a missing timezone field as \"\" rather than coercing to a\n // default: the fusion identity guard must see \"no resolvable tz id\"\n // and skip, never silently match a known zone.\n timezone: scalars.get(\"timezone\") ?? \"\",\n conversationCount: parseNonNegativeInt(scalars.get(\"conversationCount\")),\n segmentCount: parseNonNegativeInt(scalars.get(\"segmentCount\")),\n speakers,\n durationMinutes: parseNonNegativeInt(scalars.get(\"durationMinutes\")),\n contentHash: scalars.get(\"contentHash\") ?? \"\",\n syncedAt: scalars.get(\"syncedAt\") ?? \"\",\n };\n return { meta, body };\n}\n\nfunction parseYamlScalar(value: string): string {\n const trimmed = value.trim();\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"') && trimmed.length >= 2) {\n try {\n const parsed = JSON.parse(trimmed);\n if (typeof parsed === \"string\") return parsed;\n } catch {\n // Fall through to the raw value below.\n }\n }\n return trimmed;\n}\n\nfunction parseNonNegativeInt(value: string | undefined): number {\n if (value === undefined) return 0;\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < 0) return 0;\n return Math.floor(parsed);\n}\n"],"mappings":"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","names":[]}
@@ -10,21 +10,10 @@ import {
10
10
  import {
11
11
  listMemoryGovernanceRuns,
12
12
  readMemoryGovernanceRunArtifact
13
- } from "./chunk-IC6QX5TC.js";
13
+ } from "./chunk-3XIY7MDQ.js";
14
14
  import {
15
15
  StorageManager
16
- } from "./chunk-M7GGMGMZ.js";
17
- import {
18
- MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
19
- buildLifecycleEventsForMemory,
20
- inferMemoryStatus,
21
- sortMemoryLifecycleEvents,
22
- toMemoryPathRel
23
- } from "./chunk-RULE4VG5.js";
24
- import {
25
- normalizeProjectionPreview,
26
- normalizeProjectionTags
27
- } from "./chunk-SCU65EZI.js";
16
+ } from "./chunk-SVOHGUMI.js";
28
17
  import {
29
18
  MEMORY_PROJECTION_SCHEMA_VERSION,
30
19
  getMemoryProjectionPath,
@@ -40,6 +29,17 @@ import {
40
29
  import {
41
30
  openBetterSqlite3
42
31
  } from "./chunk-RL6NEVXT.js";
32
+ import {
33
+ MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
34
+ buildLifecycleEventsForMemory,
35
+ inferMemoryStatus,
36
+ sortMemoryLifecycleEvents,
37
+ toMemoryPathRel
38
+ } from "./chunk-RULE4VG5.js";
39
+ import {
40
+ normalizeProjectionPreview,
41
+ normalizeProjectionTags
42
+ } from "./chunk-SCU65EZI.js";
43
43
  import {
44
44
  log
45
45
  } from "./chunk-37CTGERM.js";
@@ -1023,4 +1023,4 @@ export {
1023
1023
  verifyMemoryProjection,
1024
1024
  repairMemoryProjection
1025
1025
  };
1026
- //# sourceMappingURL=chunk-6BISDEN6.js.map
1026
+ //# sourceMappingURL=chunk-BEM4D2QG.js.map
@@ -4,17 +4,17 @@ import {
4
4
  import {
5
5
  compareEntityTimestamps,
6
6
  normalizeEntityName
7
- } from "./chunk-M7GGMGMZ.js";
7
+ } from "./chunk-SVOHGUMI.js";
8
8
  import {
9
9
  resolveNamespaceCapabilities
10
10
  } from "./chunk-VSF7AAGE.js";
11
- import {
12
- sanitizeMemoryContent
13
- } from "./chunk-5GKPFL3S.js";
14
11
  import {
15
12
  normalizeEntityText,
16
13
  resolveRequestedEntitySectionKeys
17
14
  } from "./chunk-4DJQYKMN.js";
15
+ import {
16
+ sanitizeMemoryContent
17
+ } from "./chunk-5GKPFL3S.js";
18
18
 
19
19
  // src/entity-retrieval.ts
20
20
  import { createHash } from "crypto";
@@ -682,4 +682,4 @@ export {
682
682
  entityIndexVersion,
683
683
  entityRecentTranscriptLookbackHours
684
684
  };
685
- //# sourceMappingURL=chunk-ZPOROUTX.js.map
685
+ //# sourceMappingURL=chunk-CYWA2CXR.js.map