@remnic/core 9.18.0 → 9.20.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 (309) hide show
  1. package/dist/access-admin-ops-surface.d.ts +8 -8
  2. package/dist/access-admin-ops-surface.js +8 -8
  3. package/dist/access-boundary.d.ts +8 -8
  4. package/dist/access-boundary.js +8 -8
  5. package/dist/access-cli.js +32 -32
  6. package/dist/access-http.d.ts +8 -8
  7. package/dist/access-http.js +12 -12
  8. package/dist/access-identity-continuity-surface.d.ts +6 -6
  9. package/dist/access-identity-continuity-surface.js +8 -8
  10. package/dist/access-lcm-surface.d.ts +8 -8
  11. package/dist/access-lcm-surface.js +8 -8
  12. package/dist/access-mcp.d.ts +8 -8
  13. package/dist/access-mcp.js +11 -11
  14. package/dist/access-memory-search-fanout.d.ts +2 -2
  15. package/dist/{access-namespace-preflight-IbQ0O8Kf.d.ts → access-namespace-preflight-tmwQgCT6.d.ts} +1 -1
  16. package/dist/access-namespace-preflight.d.ts +3 -3
  17. package/dist/access-namespace-preflight.js +8 -8
  18. package/dist/access-observe-write-surface.d.ts +8 -8
  19. package/dist/access-observe-write-surface.js +8 -8
  20. package/dist/access-operations-batch.js +9 -9
  21. package/dist/access-operations.d.ts +8 -8
  22. package/dist/access-operations.js +10 -10
  23. package/dist/access-recall-concurrency.d.ts +8 -8
  24. package/dist/access-recall-concurrency.js +8 -8
  25. package/dist/access-recall-response.d.ts +8 -8
  26. package/dist/access-recall-response.js +8 -8
  27. package/dist/access-recall-surface.d.ts +8 -8
  28. package/dist/access-recall-surface.js +8 -8
  29. package/dist/{access-service-BqjsJRn_.d.ts → access-service-BqUii7LY.d.ts} +6 -6
  30. package/dist/access-service.d.ts +8 -8
  31. package/dist/access-service.js +8 -8
  32. package/dist/access-surface-catalog.d.ts +8 -8
  33. package/dist/action-confidence.d.ts +1 -1
  34. package/dist/active-memory-bridge.d.ts +1 -1
  35. package/dist/active-recall.d.ts +1 -1
  36. package/dist/active-recall.js +1 -1
  37. package/dist/{auto-sync-IH3G25MT.js → auto-sync-UPTUVE4X.js} +3 -3
  38. package/dist/behavior-learner.d.ts +1 -1
  39. package/dist/behavior-signals.d.ts +1 -1
  40. package/dist/bootstrap.d.ts +6 -6
  41. package/dist/briefing.d.ts +2 -2
  42. package/dist/briefing.js +4 -4
  43. package/dist/buffer-surprise-report.d.ts +1 -1
  44. package/dist/buffer.d.ts +2 -2
  45. package/dist/bulk-import/index.d.ts +3 -3
  46. package/dist/calibration.d.ts +1 -1
  47. package/dist/capabilities.d.ts +1 -1
  48. package/dist/{catalog-D_ToaAZM.d.ts → catalog-iWvWz61I.d.ts} +1 -1
  49. package/dist/causal-behavior.d.ts +1 -1
  50. package/dist/causal-consolidation.d.ts +1 -1
  51. package/dist/causal-consolidation.js +5 -5
  52. package/dist/causal-trajectory-graph.d.ts +1 -1
  53. package/dist/{chunk-52H7OB7Y.js → chunk-275ENJDK.js} +2 -2
  54. package/dist/{chunk-OR3OQHHS.js → chunk-3ZUY2VQF.js} +2 -2
  55. package/dist/{chunk-NEFMSUC2.js → chunk-4F6AANKS.js} +3 -3
  56. package/dist/{chunk-NGO4QKMY.js → chunk-4PYLBXX6.js} +2 -2
  57. package/dist/{chunk-UQXIW7OZ.js → chunk-53WDAG7X.js} +2 -2
  58. package/dist/{chunk-CNNQVI2A.js → chunk-5IXPWEQB.js} +5 -5
  59. package/dist/{chunk-XGYCI23S.js → chunk-5JQLAQUZ.js} +2 -2
  60. package/dist/{chunk-ALPBE5EC.js → chunk-62QM5MKY.js} +7 -7
  61. package/dist/{chunk-KPEIWAZY.js → chunk-6NZ7U4R4.js} +1 -1
  62. package/dist/chunk-6NZ7U4R4.js.map +1 -0
  63. package/dist/{chunk-ZHWEUG7Y.js → chunk-7M6APKJL.js} +333 -8
  64. package/dist/chunk-7M6APKJL.js.map +1 -0
  65. package/dist/{chunk-AKWX5E6R.js → chunk-AT3TDN6O.js} +1017 -296
  66. package/dist/chunk-AT3TDN6O.js.map +1 -0
  67. package/dist/{chunk-LU22QKFA.js → chunk-B7C3TF5H.js} +2 -2
  68. package/dist/{chunk-JHIEQCMG.js → chunk-EILX7SQR.js} +3 -3
  69. package/dist/{chunk-WIP3MQZY.js → chunk-EXU5IQ3S.js} +2 -2
  70. package/dist/{chunk-UL6WTQN4.js → chunk-EZKKHVFY.js} +2 -2
  71. package/dist/{chunk-LTGSOACX.js → chunk-GEUDRENF.js} +2 -2
  72. package/dist/{chunk-B3R5CC35.js → chunk-HJEEAO4O.js} +2 -2
  73. package/dist/{chunk-7NHIHUBW.js → chunk-HPOY2PHQ.js} +3 -3
  74. package/dist/{chunk-VMALXCR5.js → chunk-JFDEYXF7.js} +2 -2
  75. package/dist/{chunk-WMMT6W7C.js → chunk-JN3IUA2Q.js} +2 -2
  76. package/dist/{chunk-A2KGXUOU.js → chunk-JWMYVQAM.js} +2 -2
  77. package/dist/{chunk-6P6ZR6JR.js → chunk-KHRKEELC.js} +17 -17
  78. package/dist/{chunk-UVCY7VDX.js → chunk-LI4ZOFHW.js} +2 -2
  79. package/dist/{chunk-6YJLLDWO.js → chunk-LMHDFOVV.js} +3 -3
  80. package/dist/{chunk-UHOUNNY3.js → chunk-N7EVMKP6.js} +6 -6
  81. package/dist/{chunk-7JXXI3GW.js → chunk-Q3FWRDCP.js} +2 -2
  82. package/dist/{chunk-JEMLK7ED.js → chunk-QO2KODU6.js} +2 -2
  83. package/dist/{chunk-7KXE7KN5.js → chunk-TCZWBJBO.js} +2 -2
  84. package/dist/{chunk-B3ABVQ36.js → chunk-UHVXE24Y.js} +2 -1
  85. package/dist/{chunk-UX3ZXN34.js → chunk-UV72DEZD.js} +2 -2
  86. package/dist/{chunk-FO3WM76R.js → chunk-V5TSUGRB.js} +5 -5
  87. package/dist/{chunk-SGCWBLHN.js → chunk-X5QS5C2O.js} +2 -2
  88. package/dist/{cli-D2g5mRgf.d.ts → cli-DPEhq0Cp.d.ts} +5 -5
  89. package/dist/cli.d.ts +10 -10
  90. package/dist/cli.js +25 -25
  91. package/dist/compounding/engine.d.ts +2 -2
  92. package/dist/compounding/engine.js +4 -4
  93. package/dist/compounding/preference-consolidator.d.ts +1 -1
  94. package/dist/compression-optimizer.d.ts +1 -1
  95. package/dist/config.d.ts +1 -1
  96. package/dist/config.js +1 -1
  97. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  98. package/dist/connectors/codex-materialize-runner.js +4 -4
  99. package/dist/connectors/codex-materialize.d.ts +1 -1
  100. package/dist/connectors/index.d.ts +1 -1
  101. package/dist/connectors/index.js +4 -4
  102. package/dist/consolidation-provenance-check.d.ts +2 -2
  103. package/dist/consolidation-undo.d.ts +2 -2
  104. package/dist/contradiction/index.d.ts +2 -2
  105. package/dist/conversation-index/backend.d.ts +1 -1
  106. package/dist/conversation-index/chunker.d.ts +1 -1
  107. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  108. package/dist/conversation-index/indexer.d.ts +1 -1
  109. package/dist/conversation-index/search.d.ts +1 -1
  110. package/dist/day-summary.d.ts +1 -1
  111. package/dist/delinearize.d.ts +1 -1
  112. package/dist/direct-answer-wiring.d.ts +1 -1
  113. package/dist/direct-answer.d.ts +1 -1
  114. package/dist/embedding-fallback.d.ts +1 -1
  115. package/dist/enrichment/index.d.ts +1 -1
  116. package/dist/entity-retrieval.d.ts +2 -2
  117. package/dist/entity-retrieval.js +4 -4
  118. package/dist/entity-schema.d.ts +1 -1
  119. package/dist/explicit-capture.d.ts +6 -6
  120. package/dist/extraction-faithfulness.d.ts +1 -1
  121. package/dist/extraction-judge-telemetry.d.ts +1 -1
  122. package/dist/extraction-judge-training.d.ts +1 -1
  123. package/dist/extraction-judge.d.ts +1 -1
  124. package/dist/extraction.d.ts +1 -1
  125. package/dist/fallback-llm.d.ts +1 -1
  126. package/dist/graph-dashboard-diff.d.ts +1 -1
  127. package/dist/graph-dashboard-key.d.ts +1 -1
  128. package/dist/graph-dashboard-parser.d.ts +1 -1
  129. package/dist/graph-edge-reinforcement.d.ts +1 -1
  130. package/dist/graph-snapshot.d.ts +1 -1
  131. package/dist/graph.d.ts +1 -1
  132. package/dist/importance.d.ts +1 -1
  133. package/dist/importers/index.d.ts +1 -1
  134. package/dist/in-flight-reads.d.ts +1 -1
  135. package/dist/index.d.ts +470 -90
  136. package/dist/index.js +66 -36
  137. package/dist/intent.d.ts +1 -1
  138. package/dist/lcm/engine.d.ts +1 -1
  139. package/dist/lcm/index.d.ts +1 -1
  140. package/dist/lcm/tools.d.ts +1 -1
  141. package/dist/lifecycle.d.ts +1 -1
  142. package/dist/live-connectors-runner.d.ts +1 -1
  143. package/dist/local-llm.d.ts +1 -1
  144. package/dist/local-model-endpoint.d.ts +1 -1
  145. package/dist/maintenance/memory-governance.d.ts +1 -1
  146. package/dist/maintenance/memory-governance.js +4 -4
  147. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  148. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -4
  149. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  150. package/dist/maintenance/rebuild-memory-projection.js +5 -5
  151. package/dist/mcp-memory-inspector-app.d.ts +8 -8
  152. package/dist/memory-action-policy.d.ts +1 -1
  153. package/dist/memory-cache.d.ts +1 -1
  154. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  155. package/dist/memory-projection-store.d.ts +1 -1
  156. package/dist/memory-provenance.d.ts +1 -1
  157. package/dist/memory-worth-outcomes.d.ts +2 -2
  158. package/dist/models-json.d.ts +1 -1
  159. package/dist/namespaces/migrate.d.ts +3 -3
  160. package/dist/namespaces/migrate.js +5 -5
  161. package/dist/namespaces/principal.d.ts +1 -1
  162. package/dist/namespaces/search.d.ts +1 -1
  163. package/dist/namespaces/storage.d.ts +3 -3
  164. package/dist/namespaces/storage.js +4 -4
  165. package/dist/native-knowledge.d.ts +1 -1
  166. package/dist/offline-sync-impression-drain.d.ts +1 -1
  167. package/dist/operator-toolkit.d.ts +2 -2
  168. package/dist/operator-toolkit.js +9 -9
  169. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  170. package/dist/orchestration/maintenance.d.ts +3 -3
  171. package/dist/orchestration/maintenance.js +9 -9
  172. package/dist/{orchestrator-DrILRi-n.d.ts → orchestrator-DB93S48I.d.ts} +5 -5
  173. package/dist/orchestrator.d.ts +6 -6
  174. package/dist/orchestrator.js +32 -32
  175. package/dist/patterns-cli.d.ts +1 -1
  176. package/dist/{pipeline-BYo4b_y9.d.ts → pipeline-DmGc-H66.d.ts} +1 -1
  177. package/dist/policy-runtime.d.ts +1 -1
  178. package/dist/proactive-contention.d.ts +1 -1
  179. package/dist/provenance.d.ts +1 -1
  180. package/dist/{qmd-BIrL8e69.d.ts → qmd-cJT0crkF.d.ts} +1 -1
  181. package/dist/qmd-preflight.d.ts +2 -2
  182. package/dist/qmd-recall-cache.d.ts +1 -1
  183. package/dist/qmd.d.ts +2 -2
  184. package/dist/recall-concurrency-config.d.ts +1 -1
  185. package/dist/recall-disclosure-escalation.d.ts +1 -1
  186. package/dist/recall-explain-renderer.d.ts +1 -1
  187. package/dist/recall-explain-renderer.js +3 -3
  188. package/dist/recall-memory-map.d.ts +1 -1
  189. package/dist/recall-planner-llm.d.ts +1 -1
  190. package/dist/recall-state.d.ts +1 -1
  191. package/dist/recall-tag-filter.d.ts +1 -1
  192. package/dist/recall-timings.d.ts +1 -1
  193. package/dist/recall-xray-cli.d.ts +1 -1
  194. package/dist/recall-xray-cli.js +4 -4
  195. package/dist/recall-xray-renderer.d.ts +1 -1
  196. package/dist/recall-xray-renderer.js +3 -3
  197. package/dist/recall-xray.d.ts +1 -1
  198. package/dist/recall-xray.js +2 -2
  199. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  200. package/dist/replay/normalizers/claude.d.ts +1 -1
  201. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  202. package/dist/replay/normalizers/shared.d.ts +1 -1
  203. package/dist/replay/runner.d.ts +1 -1
  204. package/dist/replay/types.d.ts +1 -1
  205. package/dist/resolve-auth-token.d.ts +1 -1
  206. package/dist/resume-bundles.js +2 -2
  207. package/dist/retrieval-agents.d.ts +2 -2
  208. package/dist/retrieval-tiers.d.ts +1 -1
  209. package/dist/routing/engine.d.ts +1 -1
  210. package/dist/routing/store.d.ts +1 -1
  211. package/dist/salvage-envelope.d.ts +1 -1
  212. package/dist/schemas.d.ts +22 -22
  213. package/dist/{scope-profiles-BiQ9yXP4.d.ts → scope-profiles-KG2nODX7.d.ts} +1 -1
  214. package/dist/search/embed-helper.d.ts +1 -1
  215. package/dist/search/factory.d.ts +1 -1
  216. package/dist/search/index.d.ts +1 -1
  217. package/dist/search/lancedb-backend.d.ts +1 -1
  218. package/dist/search/meilisearch-backend.d.ts +1 -1
  219. package/dist/search/noop-backend.d.ts +1 -1
  220. package/dist/search/orama-backend.d.ts +1 -1
  221. package/dist/search/port.d.ts +1 -1
  222. package/dist/search/remote-backend.d.ts +1 -1
  223. package/dist/{semantic-consolidation-C-cSATcR.d.ts → semantic-consolidation-BaZlg0D8.d.ts} +1 -1
  224. package/dist/semantic-consolidation.d.ts +2 -2
  225. package/dist/semantic-consolidation.js +5 -5
  226. package/dist/semantic-rule-promotion.js +4 -4
  227. package/dist/semantic-rule-verifier.d.ts +1 -1
  228. package/dist/semantic-rule-verifier.js +4 -4
  229. package/dist/session-observer-bands.d.ts +1 -1
  230. package/dist/session-observer-state.d.ts +1 -1
  231. package/dist/shared-context/manager.d.ts +1 -1
  232. package/dist/signal.d.ts +1 -1
  233. package/dist/{storage-ByTv05qM.d.ts → storage-BKhHHnRo.d.ts} +2 -221
  234. package/dist/storage.d.ts +2 -2
  235. package/dist/storage.js +3 -3
  236. package/dist/summarizer.d.ts +1 -1
  237. package/dist/summary-snapshot.d.ts +1 -1
  238. package/dist/temporal-supersession.d.ts +2 -2
  239. package/dist/temporal-timeline-recall.d.ts +1 -1
  240. package/dist/temporal-validity.d.ts +1 -1
  241. package/dist/threading.d.ts +1 -1
  242. package/dist/tier-migration.d.ts +2 -2
  243. package/dist/tier-routing.d.ts +1 -1
  244. package/dist/topics.d.ts +1 -1
  245. package/dist/transcript.d.ts +1 -1
  246. package/dist/transfer/types.d.ts +12 -12
  247. package/dist/trust-score-stage.d.ts +1 -1
  248. package/dist/trust-score.d.ts +1 -1
  249. package/dist/{types-e0_MI3Iq.d.ts → types-GBQZuT7l.d.ts} +397 -1
  250. package/dist/types.d.ts +1 -1
  251. package/dist/types.js +1 -1
  252. package/dist/utility-runtime.d.ts +1 -1
  253. package/dist/verified-recall.js +4 -4
  254. package/dist/write-envelope.d.ts +1 -1
  255. package/package.json +2 -2
  256. package/src/config.test.ts +11 -0
  257. package/src/config.ts +5 -6
  258. package/src/meetings/build.test.ts +646 -0
  259. package/src/meetings/build.ts +365 -0
  260. package/src/meetings/cli.test.ts +276 -0
  261. package/src/meetings/cli.ts +242 -0
  262. package/src/meetings/config.test.ts +72 -0
  263. package/src/meetings/config.ts +183 -0
  264. package/src/meetings/errors.ts +13 -0
  265. package/src/meetings/fuse.test.ts +208 -0
  266. package/src/meetings/fuse.ts +297 -0
  267. package/src/meetings/index.ts +11 -1
  268. package/src/meetings/memory-generator.ts +116 -0
  269. package/src/meetings/recall-exclusion.test.ts +88 -0
  270. package/src/meetings/store.test.ts +232 -0
  271. package/src/meetings/store.ts +465 -0
  272. package/src/meetings/types.ts +111 -0
  273. package/src/orchestration/orchestrator-helpers.ts +23 -3
  274. package/src/orchestration/recall-search-pipeline.test.ts +182 -0
  275. package/src/orchestration/recall-search-pipeline.ts +36 -15
  276. package/src/types.ts +8 -0
  277. package/dist/chunk-AKWX5E6R.js.map +0 -1
  278. package/dist/chunk-KPEIWAZY.js.map +0 -1
  279. package/dist/chunk-ZHWEUG7Y.js.map +0 -1
  280. /package/dist/{auto-sync-IH3G25MT.js.map → auto-sync-UPTUVE4X.js.map} +0 -0
  281. /package/dist/{chunk-52H7OB7Y.js.map → chunk-275ENJDK.js.map} +0 -0
  282. /package/dist/{chunk-OR3OQHHS.js.map → chunk-3ZUY2VQF.js.map} +0 -0
  283. /package/dist/{chunk-NEFMSUC2.js.map → chunk-4F6AANKS.js.map} +0 -0
  284. /package/dist/{chunk-NGO4QKMY.js.map → chunk-4PYLBXX6.js.map} +0 -0
  285. /package/dist/{chunk-UQXIW7OZ.js.map → chunk-53WDAG7X.js.map} +0 -0
  286. /package/dist/{chunk-CNNQVI2A.js.map → chunk-5IXPWEQB.js.map} +0 -0
  287. /package/dist/{chunk-XGYCI23S.js.map → chunk-5JQLAQUZ.js.map} +0 -0
  288. /package/dist/{chunk-ALPBE5EC.js.map → chunk-62QM5MKY.js.map} +0 -0
  289. /package/dist/{chunk-LU22QKFA.js.map → chunk-B7C3TF5H.js.map} +0 -0
  290. /package/dist/{chunk-JHIEQCMG.js.map → chunk-EILX7SQR.js.map} +0 -0
  291. /package/dist/{chunk-WIP3MQZY.js.map → chunk-EXU5IQ3S.js.map} +0 -0
  292. /package/dist/{chunk-UL6WTQN4.js.map → chunk-EZKKHVFY.js.map} +0 -0
  293. /package/dist/{chunk-LTGSOACX.js.map → chunk-GEUDRENF.js.map} +0 -0
  294. /package/dist/{chunk-B3R5CC35.js.map → chunk-HJEEAO4O.js.map} +0 -0
  295. /package/dist/{chunk-7NHIHUBW.js.map → chunk-HPOY2PHQ.js.map} +0 -0
  296. /package/dist/{chunk-VMALXCR5.js.map → chunk-JFDEYXF7.js.map} +0 -0
  297. /package/dist/{chunk-WMMT6W7C.js.map → chunk-JN3IUA2Q.js.map} +0 -0
  298. /package/dist/{chunk-A2KGXUOU.js.map → chunk-JWMYVQAM.js.map} +0 -0
  299. /package/dist/{chunk-6P6ZR6JR.js.map → chunk-KHRKEELC.js.map} +0 -0
  300. /package/dist/{chunk-UVCY7VDX.js.map → chunk-LI4ZOFHW.js.map} +0 -0
  301. /package/dist/{chunk-6YJLLDWO.js.map → chunk-LMHDFOVV.js.map} +0 -0
  302. /package/dist/{chunk-UHOUNNY3.js.map → chunk-N7EVMKP6.js.map} +0 -0
  303. /package/dist/{chunk-7JXXI3GW.js.map → chunk-Q3FWRDCP.js.map} +0 -0
  304. /package/dist/{chunk-JEMLK7ED.js.map → chunk-QO2KODU6.js.map} +0 -0
  305. /package/dist/{chunk-7KXE7KN5.js.map → chunk-TCZWBJBO.js.map} +0 -0
  306. /package/dist/{chunk-B3ABVQ36.js.map → chunk-UHVXE24Y.js.map} +0 -0
  307. /package/dist/{chunk-UX3ZXN34.js.map → chunk-UV72DEZD.js.map} +0 -0
  308. /package/dist/{chunk-FO3WM76R.js.map → chunk-V5TSUGRB.js.map} +0 -0
  309. /package/dist/{chunk-SGCWBLHN.js.map → chunk-X5QS5C2O.js.map} +0 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Meetings CLI runner (issue #1900) — one implementation shared by every CLI
3
+ * host (`remnic meetings ...` and `openclaw engram meetings ...`) so the
4
+ * surfaces never fork (same rule as the wearables/recall-explain runners).
5
+ *
6
+ * Flag validation is strict: value-taking flags require a value, unknown flags
7
+ * error with the valid list, malformed dates/ids reject loudly, and a missing
8
+ * record is a caller error — all `MeetingsInputError` → exit code 1 (CLAUDE.md
9
+ * rules 14 + 51). Backend faults bubble to the host's 500 handler.
10
+ */
11
+
12
+ import { isValidTranscriptDate } from "../wearables/day-store.js";
13
+ import type { MeetingsBuilder, MeetingsDayBuildSummary } from "./build.js";
14
+ import { MeetingsInputError } from "./errors.js";
15
+ import { meetingIdDate, type MeetingRecordStore, type MeetingRecordSummary } from "./store.js";
16
+ import type { MeetingsConfig } from "./types.js";
17
+
18
+ export interface MeetingsCliIo {
19
+ stdout: { write(chunk: string): unknown };
20
+ stderr: { write(chunk: string): unknown };
21
+ }
22
+
23
+ /** Everything the runner drives. Injected so tests wire a store + builder over
24
+ * synthetic fixtures with no real filesystem. */
25
+ export interface MeetingsCliDeps {
26
+ store: MeetingRecordStore;
27
+ builder: MeetingsBuilder;
28
+ /**
29
+ * Meetings config, so list/show honor `meetings.enabled`. The runner reads the
30
+ * store directly (not through an enabled-aware service), so without this gate a
31
+ * disabled subsystem would still expose stored records via list/show even
32
+ * though `build` refuses. Disabled => no exposure (issue #1900).
33
+ */
34
+ config: MeetingsConfig;
35
+ }
36
+
37
+ const USAGE = `Usage: meetings <command> [options]
38
+
39
+ Commands:
40
+ list [--date <YYYY-MM-DD>] List meeting records (all days, or one day)
41
+ show <meeting-id> Print a stored meeting record
42
+ build --date <YYYY-MM-DD> Detect + fuse + store the day's meetings
43
+
44
+ Add --json to list/build for machine-readable output.
45
+ `;
46
+
47
+ const VALUE_FLAGS: Record<string, true> = { "--date": true };
48
+ const BOOLEAN_FLAGS: Record<string, true> = { "--json": true };
49
+
50
+ interface ParsedFlags {
51
+ flags: Map<string, string | true>;
52
+ positional: string[];
53
+ }
54
+
55
+ function parseFlags(args: string[]): ParsedFlags {
56
+ const flags = new Map<string, string | true>();
57
+ const positional: string[] = [];
58
+ for (let i = 0; i < args.length; i++) {
59
+ const arg = args[i]!;
60
+ if (!arg.startsWith("--")) {
61
+ positional.push(arg);
62
+ continue;
63
+ }
64
+ if (VALUE_FLAGS[arg] === true) {
65
+ const value = args[i + 1];
66
+ if (value === undefined || value.startsWith("--")) {
67
+ throw new MeetingsInputError(`flag ${arg} requires a value`);
68
+ }
69
+ flags.set(arg, value);
70
+ i++;
71
+ continue;
72
+ }
73
+ if (BOOLEAN_FLAGS[arg] === true) {
74
+ flags.set(arg, true);
75
+ continue;
76
+ }
77
+ const known = [...Object.keys(VALUE_FLAGS), ...Object.keys(BOOLEAN_FLAGS)].sort().join(", ");
78
+ throw new MeetingsInputError(`unknown flag ${arg} (valid: ${known})`);
79
+ }
80
+ return { flags, positional };
81
+ }
82
+
83
+ function requireDate(parsed: ParsedFlags): string {
84
+ const value = parsed.flags.get("--date");
85
+ if (typeof value !== "string") {
86
+ throw new MeetingsInputError("--date <YYYY-MM-DD> is required");
87
+ }
88
+ if (!isValidTranscriptDate(value)) {
89
+ throw new MeetingsInputError(`--date must be a real YYYY-MM-DD; got '${value}'`);
90
+ }
91
+ return value;
92
+ }
93
+
94
+ function renderSummaryLine(summary: MeetingRecordSummary): string {
95
+ const attendees = summary.attendees.length > 0 ? summary.attendees.join(", ") : "(none)";
96
+ const sources = summary.sources.length > 0 ? summary.sources.join("+") : "(none)";
97
+ const corroborated =
98
+ summary.corroboratedBy.length > 0 ? ` corroborated=${summary.corroboratedBy.join("+")}` : "";
99
+ return (
100
+ ` ${summary.id} ${summary.startUtc}–${summary.endUtc} [${summary.detectionSource}]` +
101
+ ` sources=${sources}${corroborated} attendees=${attendees} snapshots=${summary.snapshotCount}`
102
+ );
103
+ }
104
+
105
+ function renderBuildSummary(summary: MeetingsDayBuildSummary): string {
106
+ if (!summary.enabled) {
107
+ return `meetings disabled — set meetings.enabled=true to build (${summary.date})`;
108
+ }
109
+ const lines = [
110
+ `${summary.date}: ${summary.meetings.length} meeting${summary.meetings.length === 1 ? "" : "s"} ` +
111
+ `(${summary.built} written, ${summary.skipped} unchanged, ${summary.removed.length} removed)`,
112
+ ];
113
+ for (const meeting of summary.meetings) {
114
+ const sources = meeting.sources.length > 0 ? meeting.sources.join("+") : "(none)";
115
+ lines.push(
116
+ ` ${meeting.id} ${meeting.startUtc}–${meeting.endUtc} [${meeting.detectionSource}]` +
117
+ ` sources=${sources} snapshots=${meeting.snapshotCount} ${meeting.written ? "written" : "unchanged"}`,
118
+ );
119
+ }
120
+ if (summary.reindexWarning !== undefined) {
121
+ lines.push(` warning: ${summary.reindexWarning}`);
122
+ }
123
+ if (summary.memoryWarning !== undefined) {
124
+ lines.push(` warning: ${summary.memoryWarning}`);
125
+ }
126
+ return lines.join("\n");
127
+ }
128
+
129
+ /** Run a meetings CLI command. Returns a process exit code; all output via `io`. */
130
+ export async function runMeetingsCliCommand(
131
+ deps: MeetingsCliDeps,
132
+ args: string[],
133
+ io: MeetingsCliIo,
134
+ ): Promise<number> {
135
+ const [command, ...rest] = args;
136
+ try {
137
+ switch (command) {
138
+ case undefined:
139
+ case "help":
140
+ case "--help": {
141
+ io.stdout.write(USAGE);
142
+ return command === undefined ? 1 : 0;
143
+ }
144
+ case "list": {
145
+ const parsed = parseFlags(rest);
146
+ const json = parsed.flags.has("--json");
147
+ const dateFlag = parsed.flags.get("--date");
148
+ // Validate --date regardless of enabled state: a malformed --date is a
149
+ // caller error on every path, so it must reject before the disabled
150
+ // list short-circuit returns an empty result.
151
+ if (typeof dateFlag === "string" && !isValidTranscriptDate(dateFlag)) {
152
+ throw new MeetingsInputError(`--date must be a real YYYY-MM-DD; got '${dateFlag}'`);
153
+ }
154
+ if (!deps.config.enabled) {
155
+ if (json) {
156
+ io.stdout.write(typeof dateFlag === "string" ? "[]\n" : "{}\n");
157
+ } else {
158
+ io.stdout.write("meetings disabled — set meetings.enabled=true to build\n");
159
+ }
160
+ return 0;
161
+ }
162
+ if (typeof dateFlag === "string") {
163
+ const summaries = await deps.store.listMeetingSummaries(dateFlag);
164
+ if (json) {
165
+ io.stdout.write(`${JSON.stringify(summaries, null, 2)}\n`);
166
+ return 0;
167
+ }
168
+ if (summaries.length === 0) {
169
+ io.stdout.write(`No meetings for ${dateFlag}.\n`);
170
+ return 0;
171
+ }
172
+ io.stdout.write(`${dateFlag}:\n`);
173
+ for (const summary of summaries) io.stdout.write(`${renderSummaryLine(summary)}\n`);
174
+ return 0;
175
+ }
176
+ const dates = await deps.store.listMeetingDates();
177
+ if (json) {
178
+ const out: Record<string, MeetingRecordSummary[]> = {};
179
+ for (const date of dates) out[date] = await deps.store.listMeetingSummaries(date);
180
+ io.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
181
+ return 0;
182
+ }
183
+ if (dates.length === 0) {
184
+ io.stdout.write("No meetings recorded.\n");
185
+ return 0;
186
+ }
187
+ for (const date of dates) {
188
+ const summaries = await deps.store.listMeetingSummaries(date);
189
+ io.stdout.write(`${date}:\n`);
190
+ for (const summary of summaries) io.stdout.write(`${renderSummaryLine(summary)}\n`);
191
+ }
192
+ return 0;
193
+ }
194
+ case "show": {
195
+ const parsed = parseFlags(rest);
196
+ if (!deps.config.enabled) {
197
+ io.stdout.write("meetings disabled — set meetings.enabled=true to build\n");
198
+ return 0;
199
+ }
200
+ const id = parsed.positional[0];
201
+ if (id === undefined) {
202
+ throw new MeetingsInputError("show requires a meeting id");
203
+ }
204
+ const date = meetingIdDate(id);
205
+ if (date === null) {
206
+ throw new MeetingsInputError(`invalid meeting id '${id}' — expected mtg-YYYY-MM-DD-<hash>`);
207
+ }
208
+ // meetingIdDate matches the id's SHAPE; a syntactically valid but
209
+ // impossible calendar date (e.g. mtg-2026-13-40-...) must surface as a
210
+ // clean input error, not a 500 from the store's path validator.
211
+ if (!isValidTranscriptDate(date)) {
212
+ throw new MeetingsInputError(`invalid meeting id '${id}' — '${date}' is not a real calendar date`);
213
+ }
214
+ const raw = await deps.store.readMeetingRecord(date, id);
215
+ if (raw === null) {
216
+ throw new MeetingsInputError(`meeting '${id}' not found`);
217
+ }
218
+ io.stdout.write(raw.endsWith("\n") ? raw : `${raw}\n`);
219
+ return 0;
220
+ }
221
+ case "build": {
222
+ const parsed = parseFlags(rest);
223
+ const date = requireDate(parsed);
224
+ const summary = await deps.builder.buildDay(date);
225
+ if (parsed.flags.has("--json")) {
226
+ io.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);
227
+ return 0;
228
+ }
229
+ io.stdout.write(`${renderBuildSummary(summary)}\n`);
230
+ return 0;
231
+ }
232
+ default:
233
+ throw new MeetingsInputError(`unknown meetings command '${command}'\n\n${USAGE}`);
234
+ }
235
+ } catch (err) {
236
+ if (err instanceof MeetingsInputError) {
237
+ io.stderr.write(`meetings: ${err.message}\n`);
238
+ return 1;
239
+ }
240
+ throw err;
241
+ }
242
+ }
@@ -0,0 +1,72 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+
4
+ import { DEFAULT_MEETING_APP_PATTERNS } from "./detect.js";
5
+ import { DEFAULT_MEETINGS_CONFIG, parseMeetingsConfig } from "./config.js";
6
+
7
+ test("absent config is the disabled default with shipped app patterns", () => {
8
+ const cfg = parseMeetingsConfig(undefined);
9
+ assert.equal(cfg.enabled, false);
10
+ assert.deepEqual(cfg.appPatterns, [...DEFAULT_MEETING_APP_PATTERNS]);
11
+ assert.equal(cfg.minOverlapMinutes, DEFAULT_MEETINGS_CONFIG.minOverlapMinutes);
12
+ assert.equal(cfg.maxContextChars, 4000);
13
+ });
14
+
15
+ test("appPatterns are additive over the shipped defaults and de-duplicated", () => {
16
+ const cfg = parseMeetingsConfig({ appPatterns: ["Around", "Zoom"] });
17
+ for (const shipped of DEFAULT_MEETING_APP_PATTERNS) assert.ok(cfg.appPatterns.includes(shipped));
18
+ assert.ok(cfg.appPatterns.includes("Around"));
19
+ // "Zoom" is already shipped — no duplicate.
20
+ assert.equal(cfg.appPatterns.filter((p) => p === "Zoom").length, 1);
21
+ });
22
+
23
+ test("boolean-like strings coerce for enabled", () => {
24
+ assert.equal(parseMeetingsConfig({ enabled: "true" }).enabled, true);
25
+ assert.equal(parseMeetingsConfig({ enabled: "0" }).enabled, false);
26
+ });
27
+
28
+ test("invalid values reject loudly", () => {
29
+ assert.throws(() => parseMeetingsConfig({ enabled: "maybe" }), /boolean-like/);
30
+ assert.throws(() => parseMeetingsConfig({ minOverlapMinutes: -1 }), /between 0 and 1440/);
31
+ assert.throws(() => parseMeetingsConfig({ audioOnlyMinMinutes: 0 }), /between 1 and 1440/);
32
+ assert.throws(() => parseMeetingsConfig({ contextDwellSeconds: 1.5 }), /between 0 and 86400/);
33
+ assert.throws(() => parseMeetingsConfig({ appPatterns: [""] }), /non-empty strings/);
34
+ assert.throws(() => parseMeetingsConfig({ appPatterns: "Zoom" }), /must be an array/);
35
+ assert.throws(() => parseMeetingsConfig([]), /must be an object/);
36
+ });
37
+
38
+ test("numeric knobs coerce CLI string values", () => {
39
+ const cfg = parseMeetingsConfig({ minOverlapMinutes: "5", contextDwellSeconds: "45" });
40
+ assert.equal(cfg.minOverlapMinutes, 5);
41
+ assert.equal(cfg.contextDwellSeconds, 45);
42
+ });
43
+
44
+ test("summary mode defaults to smart and rejects unknown values", () => {
45
+ assert.equal(parseMeetingsConfig(undefined).summaryMode, "smart");
46
+ assert.equal(parseMeetingsConfig({ summaryMode: "off" }).summaryMode, "off");
47
+ assert.equal(parseMeetingsConfig({ summaryMode: "review" }).summaryMode, "review");
48
+ assert.throws(() => parseMeetingsConfig({ summaryMode: "loud" }), /off, review, smart/);
49
+ });
50
+
51
+ test("trust knobs default and reject out-of-range values", () => {
52
+ const cfg = parseMeetingsConfig(undefined);
53
+ assert.equal(cfg.sourceTrust, 0.85);
54
+ assert.equal(cfg.autoApproveTrust, 0.7);
55
+ assert.equal(cfg.reviewTrust, 0.45);
56
+ assert.equal(parseMeetingsConfig({ sourceTrust: 0.5 }).sourceTrust, 0.5);
57
+ assert.throws(() => parseMeetingsConfig({ sourceTrust: 1.5 }), /in \[0, 1\]/);
58
+ assert.throws(() => parseMeetingsConfig({ reviewTrust: -0.1 }), /in \[0, 1\]/);
59
+ assert.equal(parseMeetingsConfig({ autoApproveTrust: "0.9" }).autoApproveTrust, 0.9);
60
+ });
61
+
62
+ test("finding 5 — inverted trust thresholds (autoApprove < review) are rejected", () => {
63
+ assert.throws(
64
+ () => parseMeetingsConfig({ autoApproveTrust: 0.4, reviewTrust: 0.6 }),
65
+ /autoApproveTrust .* must be >= meetings\.reviewTrust/,
66
+ );
67
+ // Equal thresholds are allowed (auto-approve band coincides with review bar).
68
+ assert.equal(parseMeetingsConfig({ autoApproveTrust: 0.5, reviewTrust: 0.5 }).autoApproveTrust, 0.5);
69
+ // A default reviewTrust (0.45) with a lowered autoApproveTrust below it is
70
+ // still caught (the override interacts with the default).
71
+ assert.throws(() => parseMeetingsConfig({ autoApproveTrust: 0.3 }), /must be >= meetings\.reviewTrust/);
72
+ });
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Meetings config parsing — strict, loud, and default-safe (issue #1900).
3
+ *
4
+ * Mirrors the wearables/procedural blocks in config.ts: shape violations and
5
+ * unparseable values throw with actionable messages (CLAUDE.md rule 51);
6
+ * boolean-ish strings coerce via the shared helper (rule 36); every numeric
7
+ * knob is bounds-checked; the master gate defaults OFF (rule 48) so base
8
+ * installs see zero behavior change.
9
+ *
10
+ * This slice wires only the keys detection + fusion + the record store need.
11
+ * The memory-generation knobs (summaryMode, sourceTrust, autoApproveTrust,
12
+ * reviewTrust) land with the trust-pipeline slice.
13
+ */
14
+
15
+ import { coerceBool, coerceNumber } from "../connectors/coerce.js";
16
+ import { DEFAULT_MEETING_APP_PATTERNS } from "./detect.js";
17
+ import type { MeetingsConfig, MeetingSummaryMode } from "./types.js";
18
+
19
+ const DEFAULT_MIN_OVERLAP_MINUTES = 2;
20
+ const DEFAULT_AUDIO_ONLY_MIN_MINUTES = 15;
21
+ const DEFAULT_MERGE_GAP_MINUTES = 2;
22
+ const DEFAULT_CONTEXT_DWELL_SECONDS = 20;
23
+ const DEFAULT_MAX_CONTEXT_CHARS = 4000;
24
+ const DEFAULT_SUMMARY_MODE: MeetingSummaryMode = "smart";
25
+ const DEFAULT_SOURCE_TRUST = 0.85;
26
+ const DEFAULT_AUTO_APPROVE_TRUST = 0.7;
27
+ const DEFAULT_REVIEW_TRUST = 0.45;
28
+ const SUMMARY_MODES: readonly MeetingSummaryMode[] = ["off", "review", "smart"];
29
+
30
+ export const DEFAULT_MEETINGS_CONFIG: MeetingsConfig = {
31
+ enabled: false,
32
+ appPatterns: [...DEFAULT_MEETING_APP_PATTERNS],
33
+ minOverlapMinutes: DEFAULT_MIN_OVERLAP_MINUTES,
34
+ audioOnlyMinMinutes: DEFAULT_AUDIO_ONLY_MIN_MINUTES,
35
+ mergeGapMinutes: DEFAULT_MERGE_GAP_MINUTES,
36
+ contextDwellSeconds: DEFAULT_CONTEXT_DWELL_SECONDS,
37
+ maxContextChars: DEFAULT_MAX_CONTEXT_CHARS,
38
+ summaryMode: DEFAULT_SUMMARY_MODE,
39
+ sourceTrust: DEFAULT_SOURCE_TRUST,
40
+ autoApproveTrust: DEFAULT_AUTO_APPROVE_TRUST,
41
+ reviewTrust: DEFAULT_REVIEW_TRUST,
42
+ };
43
+
44
+ function parseBool(value: unknown, name: string, fallback: boolean): boolean {
45
+ if (value === undefined || value === null) return fallback;
46
+ const coerced = coerceBool(value);
47
+ if (coerced === undefined) {
48
+ throw new Error(
49
+ `${name} must be a boolean-like value (true/false/1/0/yes/no/on/off); got ${JSON.stringify(value)}`,
50
+ );
51
+ }
52
+ return coerced;
53
+ }
54
+
55
+ function parseBoundedInt(
56
+ value: unknown,
57
+ name: string,
58
+ fallback: number,
59
+ min: number,
60
+ max: number,
61
+ ): number {
62
+ if (value === undefined || value === null) return fallback;
63
+ const coerced = coerceNumber(value);
64
+ if (
65
+ coerced === undefined ||
66
+ !Number.isFinite(coerced) ||
67
+ !Number.isInteger(coerced) ||
68
+ coerced < min ||
69
+ coerced > max
70
+ ) {
71
+ throw new Error(
72
+ `${name} must be an integer between ${min} and ${max}; got ${JSON.stringify(value)}`,
73
+ );
74
+ }
75
+ return coerced;
76
+ }
77
+
78
+ function parseAppPatterns(value: unknown): string[] {
79
+ if (value === undefined || value === null) {
80
+ return [...DEFAULT_MEETING_APP_PATTERNS];
81
+ }
82
+ if (!Array.isArray(value)) {
83
+ throw new Error(
84
+ `meetings.appPatterns must be an array of non-empty strings; got ${JSON.stringify(value)}`,
85
+ );
86
+ }
87
+ const patterns: string[] = [];
88
+ for (const entry of value) {
89
+ if (typeof entry !== "string" || entry.trim().length === 0) {
90
+ throw new Error(
91
+ `meetings.appPatterns entries must be non-empty strings; got ${JSON.stringify(entry)}`,
92
+ );
93
+ }
94
+ patterns.push(entry);
95
+ }
96
+ // Additive over the shipped defaults so a user extension never silently
97
+ // drops a built-in meeting app (issue #1900 — appPatterns is additive).
98
+ const merged = [...DEFAULT_MEETING_APP_PATTERNS, ...patterns];
99
+ return [...new Set(merged)];
100
+ }
101
+
102
+ function parseSummaryMode(value: unknown): MeetingSummaryMode {
103
+ if (value === undefined || value === null) return DEFAULT_SUMMARY_MODE;
104
+ if (typeof value === "string" && (SUMMARY_MODES as readonly string[]).includes(value)) {
105
+ return value as MeetingSummaryMode;
106
+ }
107
+ throw new Error(
108
+ `meetings.summaryMode must be one of ${SUMMARY_MODES.join(", ")}; got ${JSON.stringify(value)}`,
109
+ );
110
+ }
111
+
112
+ function parseTrust01(value: unknown, name: string, fallback: number): number {
113
+ if (value === undefined || value === null) return fallback;
114
+ const coerced = coerceNumber(value);
115
+ if (coerced === undefined || !Number.isFinite(coerced) || coerced < 0 || coerced > 1) {
116
+ throw new Error(`${name} must be a number in [0, 1]; got ${JSON.stringify(value)}`);
117
+ }
118
+ return coerced;
119
+ }
120
+
121
+ /** Parse the `meetings` config block. Absent/`{}` → defaults (subsystem off). */
122
+ export function parseMeetingsConfig(value: unknown): MeetingsConfig {
123
+ if (value === undefined || value === null) {
124
+ return { ...DEFAULT_MEETINGS_CONFIG, appPatterns: [...DEFAULT_MEETING_APP_PATTERNS] };
125
+ }
126
+ if (typeof value !== "object" || Array.isArray(value)) {
127
+ throw new Error(`meetings must be an object; got ${JSON.stringify(value)}`);
128
+ }
129
+ const raw = value as Record<string, unknown>;
130
+ const config: MeetingsConfig = {
131
+ enabled: parseBool(raw.enabled, "meetings.enabled", DEFAULT_MEETINGS_CONFIG.enabled),
132
+ appPatterns: parseAppPatterns(raw.appPatterns),
133
+ minOverlapMinutes: parseBoundedInt(
134
+ raw.minOverlapMinutes,
135
+ "meetings.minOverlapMinutes",
136
+ DEFAULT_MIN_OVERLAP_MINUTES,
137
+ 0,
138
+ 1440,
139
+ ),
140
+ audioOnlyMinMinutes: parseBoundedInt(
141
+ raw.audioOnlyMinMinutes,
142
+ "meetings.audioOnlyMinMinutes",
143
+ DEFAULT_AUDIO_ONLY_MIN_MINUTES,
144
+ 1,
145
+ 1440,
146
+ ),
147
+ mergeGapMinutes: parseBoundedInt(
148
+ raw.mergeGapMinutes,
149
+ "meetings.mergeGapMinutes",
150
+ DEFAULT_MERGE_GAP_MINUTES,
151
+ 0,
152
+ 1440,
153
+ ),
154
+ contextDwellSeconds: parseBoundedInt(
155
+ raw.contextDwellSeconds,
156
+ "meetings.contextDwellSeconds",
157
+ DEFAULT_CONTEXT_DWELL_SECONDS,
158
+ 0,
159
+ 86_400,
160
+ ),
161
+ maxContextChars: parseBoundedInt(
162
+ raw.maxContextChars,
163
+ "meetings.maxContextChars",
164
+ DEFAULT_MAX_CONTEXT_CHARS,
165
+ 0,
166
+ 1_000_000,
167
+ ),
168
+ summaryMode: parseSummaryMode(raw.summaryMode),
169
+ sourceTrust: parseTrust01(raw.sourceTrust, "meetings.sourceTrust", DEFAULT_SOURCE_TRUST),
170
+ autoApproveTrust: parseTrust01(raw.autoApproveTrust, "meetings.autoApproveTrust", DEFAULT_AUTO_APPROVE_TRUST),
171
+ reviewTrust: parseTrust01(raw.reviewTrust, "meetings.reviewTrust", DEFAULT_REVIEW_TRUST),
172
+ };
173
+ // decideSmart checks the auto-approve band FIRST, so autoApproveTrust below
174
+ // reviewTrust fails open (a claim under the review bar would auto-activate).
175
+ // Reject the inversion loudly rather than silently mis-gate meeting facts.
176
+ if (config.autoApproveTrust < config.reviewTrust) {
177
+ throw new Error(
178
+ `meetings.autoApproveTrust (${config.autoApproveTrust}) must be >= meetings.reviewTrust ` +
179
+ `(${config.reviewTrust}); an inverted gate would auto-approve below the review bar`,
180
+ );
181
+ }
182
+ return config;
183
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Error thrown by meeting surfaces for caller-correctable problems: invalid
3
+ * dates/ids, unknown commands/flags, or a missing record. Transport layers map
4
+ * this to 400-class responses; anything else is a backend fault (500). Mirrors
5
+ * `WearablesInputError` so the meetings surfaces follow the same house style.
6
+ */
7
+
8
+ export class MeetingsInputError extends Error {
9
+ constructor(message: string) {
10
+ super(message);
11
+ this.name = "MeetingsInputError";
12
+ }
13
+ }