@remnic/core 9.3.623 → 9.3.625

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 (262) hide show
  1. package/dist/access-cli.js +18 -16
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.d.ts +12 -5
  4. package/dist/access-http.js +10 -9
  5. package/dist/access-mcp.d.ts +5 -5
  6. package/dist/access-mcp.js +8 -8
  7. package/dist/access-schema.d.ts +5 -5
  8. package/dist/{access-service-CBNEKjzN.d.ts → access-service-C_sfOHsX.d.ts} +26 -3
  9. package/dist/access-service.d.ts +5 -5
  10. package/dist/access-service.js +7 -7
  11. package/dist/action-confidence.d.ts +1 -1
  12. package/dist/active-memory-bridge.d.ts +1 -1
  13. package/dist/active-recall.d.ts +1 -1
  14. package/dist/active-recall.js +2 -1
  15. package/dist/active-recall.js.map +1 -1
  16. package/dist/behavior-learner.d.ts +1 -1
  17. package/dist/behavior-signals.d.ts +1 -1
  18. package/dist/bootstrap.d.ts +4 -4
  19. package/dist/briefing.d.ts +1 -1
  20. package/dist/briefing.js +3 -3
  21. package/dist/buffer-surprise-report.d.ts +1 -1
  22. package/dist/buffer.d.ts +1 -1
  23. package/dist/calibration.d.ts +1 -1
  24. package/dist/causal-behavior.d.ts +1 -1
  25. package/dist/causal-consolidation.d.ts +1 -1
  26. package/dist/causal-consolidation.js +4 -4
  27. package/dist/{chunk-C4PZTWTG.js → chunk-2RHI3FGV.js} +540 -17
  28. package/dist/chunk-2RHI3FGV.js.map +1 -0
  29. package/dist/{chunk-GYTVOLNX.js → chunk-3MNBW7R7.js} +2 -2
  30. package/dist/{chunk-QFQQFX2H.js → chunk-3R2UZV3U.js} +2 -2
  31. package/dist/{chunk-O4UNM6OR.js → chunk-532VCWYW.js} +2 -2
  32. package/dist/{chunk-2UFQYU5F.js → chunk-57QXN2CS.js} +2 -2
  33. package/dist/chunk-7WV3F5DQ.js +22 -0
  34. package/dist/chunk-7WV3F5DQ.js.map +1 -0
  35. package/dist/{chunk-RKW6QR7W.js → chunk-AZ4RI3QD.js} +1461 -78
  36. package/dist/chunk-AZ4RI3QD.js.map +1 -0
  37. package/dist/{chunk-KQFQ3IS5.js → chunk-F3FY3D3S.js} +43 -7
  38. package/dist/chunk-F3FY3D3S.js.map +1 -0
  39. package/dist/{chunk-4R4KTDIE.js → chunk-FPNQF475.js} +1 -1
  40. package/dist/chunk-FPNQF475.js.map +1 -0
  41. package/dist/{chunk-UGEBPVNI.js → chunk-GE7Q7KXP.js} +2 -2
  42. package/dist/{chunk-GLWW3EJQ.js → chunk-KB4MFBF5.js} +3 -3
  43. package/dist/{chunk-5GOMXHLC.js → chunk-KKTXCFD7.js} +255 -1
  44. package/dist/chunk-KKTXCFD7.js.map +1 -0
  45. package/dist/{chunk-FH3PPO42.js → chunk-KVFYTRMV.js} +2 -2
  46. package/dist/{chunk-BNW5NJJH.js → chunk-LQYTQCXM.js} +2 -2
  47. package/dist/{chunk-AYHXQR53.js → chunk-MVQN73GT.js} +2 -2
  48. package/dist/{chunk-ZZPIJPPD.js → chunk-N5RGXWLQ.js} +2 -2
  49. package/dist/chunk-NDAH7BJ5.js +213 -0
  50. package/dist/chunk-NDAH7BJ5.js.map +1 -0
  51. package/dist/{chunk-R3OQGYOU.js → chunk-P2D2MM47.js} +2 -2
  52. package/dist/{chunk-PSUB67YB.js → chunk-PW6GURU3.js} +2 -2
  53. package/dist/{chunk-W3BKVM64.js → chunk-QDV6VAD4.js} +2 -2
  54. package/dist/{chunk-3QSU4NFF.js → chunk-QHXW3LZV.js} +3 -3
  55. package/dist/{chunk-I6UCUHLK.js → chunk-SHV5Y2WU.js} +182 -3
  56. package/dist/chunk-SHV5Y2WU.js.map +1 -0
  57. package/dist/{chunk-OZXVGYGZ.js → chunk-STDAAGH7.js} +2 -2
  58. package/dist/{chunk-FMGWXIES.js → chunk-TZDSNIRO.js} +5 -5
  59. package/dist/{chunk-2L54V4ZO.js → chunk-UELS6WWF.js} +2 -2
  60. package/dist/{chunk-PJGB7XRR.js → chunk-UGHUNQ74.js} +502 -134
  61. package/dist/chunk-UGHUNQ74.js.map +1 -0
  62. package/dist/{chunk-ZJSZNTEI.js → chunk-Y3TMFC6I.js} +140 -10
  63. package/dist/chunk-Y3TMFC6I.js.map +1 -0
  64. package/dist/{chunk-BPSGLMQ4.js → chunk-YQNADJCT.js} +2 -2
  65. package/dist/{cli-Cw729yLf.d.ts → cli-EZv6YE6_.d.ts} +3 -3
  66. package/dist/cli.d.ts +6 -6
  67. package/dist/cli.js +23 -21
  68. package/dist/compounding/engine.d.ts +1 -1
  69. package/dist/compounding/engine.js +3 -3
  70. package/dist/compounding/preference-consolidator.d.ts +1 -1
  71. package/dist/compression-optimizer.d.ts +1 -1
  72. package/dist/config.d.ts +1 -1
  73. package/dist/config.js +2 -1
  74. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  75. package/dist/connectors/codex-materialize-runner.js +3 -3
  76. package/dist/connectors/codex-materialize.d.ts +1 -1
  77. package/dist/connectors/index.d.ts +1 -1
  78. package/dist/connectors/index.js +3 -3
  79. package/dist/consolidation-provenance-check.d.ts +1 -1
  80. package/dist/consolidation-undo.d.ts +1 -1
  81. package/dist/contradiction/index.d.ts +2 -2
  82. package/dist/conversation-index/backend.d.ts +1 -1
  83. package/dist/conversation-index/chunker.d.ts +1 -1
  84. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  85. package/dist/conversation-index/indexer.d.ts +1 -1
  86. package/dist/conversation-index/search.d.ts +1 -1
  87. package/dist/day-summary.d.ts +1 -1
  88. package/dist/delinearize.d.ts +1 -1
  89. package/dist/direct-answer-wiring.d.ts +1 -1
  90. package/dist/direct-answer.d.ts +1 -1
  91. package/dist/embedding-fallback.d.ts +1 -1
  92. package/dist/enrichment/index.d.ts +1 -1
  93. package/dist/entity-retrieval.d.ts +1 -1
  94. package/dist/entity-retrieval.js +3 -3
  95. package/dist/entity-schema.d.ts +1 -1
  96. package/dist/explicit-capture.d.ts +4 -4
  97. package/dist/extraction-judge-telemetry.d.ts +1 -1
  98. package/dist/extraction-judge-training.d.ts +1 -1
  99. package/dist/extraction-judge.d.ts +1 -1
  100. package/dist/extraction.d.ts +1 -1
  101. package/dist/fallback-llm.d.ts +1 -1
  102. package/dist/identity-continuity.d.ts +1 -1
  103. package/dist/importance.d.ts +1 -1
  104. package/dist/index.d.ts +307 -9
  105. package/dist/index.js +155 -29
  106. package/dist/index.js.map +1 -1
  107. package/dist/intent.d.ts +1 -1
  108. package/dist/lcm/engine.d.ts +1 -1
  109. package/dist/lcm/index.d.ts +1 -1
  110. package/dist/lcm/tools.d.ts +1 -1
  111. package/dist/lifecycle.d.ts +1 -1
  112. package/dist/live-connectors-runner.d.ts +1 -1
  113. package/dist/local-llm.d.ts +1 -1
  114. package/dist/maintenance/memory-governance.d.ts +1 -1
  115. package/dist/maintenance/memory-governance.js +3 -3
  116. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  117. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  118. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  119. package/dist/memory-action-policy.d.ts +1 -1
  120. package/dist/memory-cache.d.ts +1 -1
  121. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  122. package/dist/memory-projection-store.d.ts +1 -1
  123. package/dist/memory-provenance.d.ts +1 -1
  124. package/dist/memory-worth-outcomes.d.ts +1 -1
  125. package/dist/models-json.d.ts +1 -1
  126. package/dist/namespaces/migrate.d.ts +1 -1
  127. package/dist/namespaces/migrate.js +4 -4
  128. package/dist/namespaces/principal.d.ts +1 -1
  129. package/dist/namespaces/search.d.ts +1 -1
  130. package/dist/namespaces/storage.d.ts +1 -1
  131. package/dist/namespaces/storage.js +3 -3
  132. package/dist/native-knowledge.d.ts +1 -1
  133. package/dist/operator-toolkit.d.ts +1 -1
  134. package/dist/operator-toolkit.js +8 -7
  135. package/dist/{orchestrator-CqWOjfgl.d.ts → orchestrator-CEycaY3M.d.ts} +361 -4
  136. package/dist/orchestrator.d.ts +4 -4
  137. package/dist/orchestrator.js +13 -11
  138. package/dist/patterns-cli.d.ts +1 -1
  139. package/dist/policy-runtime.d.ts +1 -1
  140. package/dist/qmd-recall-cache.d.ts +1 -1
  141. package/dist/qmd.d.ts +1 -1
  142. package/dist/recall-disclosure-escalation.d.ts +1 -1
  143. package/dist/recall-explain-renderer.d.ts +1 -1
  144. package/dist/recall-explain-renderer.js +3 -3
  145. package/dist/recall-planner-llm.d.ts +1 -1
  146. package/dist/recall-state.d.ts +1 -1
  147. package/dist/recall-tag-filter.d.ts +1 -1
  148. package/dist/recall-xray-cli.d.ts +1 -1
  149. package/dist/recall-xray-cli.js +4 -4
  150. package/dist/recall-xray-renderer.d.ts +1 -1
  151. package/dist/recall-xray-renderer.js +3 -3
  152. package/dist/recall-xray.d.ts +1 -1
  153. package/dist/recall-xray.js +2 -2
  154. package/dist/resolve-auth-token.d.ts +1 -1
  155. package/dist/resume-bundles.js +3 -2
  156. package/dist/retrieval-agents.d.ts +1 -1
  157. package/dist/retrieval-tiers.d.ts +1 -1
  158. package/dist/routing/engine.d.ts +1 -1
  159. package/dist/routing/store.d.ts +1 -1
  160. package/dist/schemas.d.ts +32 -32
  161. package/dist/search/embed-helper.d.ts +1 -1
  162. package/dist/search/factory.d.ts +1 -1
  163. package/dist/search/index.d.ts +1 -1
  164. package/dist/search/lancedb-backend.d.ts +1 -1
  165. package/dist/search/meilisearch-backend.d.ts +1 -1
  166. package/dist/search/noop-backend.d.ts +1 -1
  167. package/dist/search/orama-backend.d.ts +1 -1
  168. package/dist/search/port.d.ts +1 -1
  169. package/dist/search/remote-backend.d.ts +1 -1
  170. package/dist/{semantic-SLAa_prH.d.ts → semantic-DJR8_DMQ.d.ts} +1 -1
  171. package/dist/{semantic-consolidation-4HkHWgeI.d.ts → semantic-consolidation-FbhPeJjB.d.ts} +1 -1
  172. package/dist/semantic-consolidation.d.ts +2 -2
  173. package/dist/semantic-consolidation.js +4 -4
  174. package/dist/semantic-rule-promotion.js +3 -3
  175. package/dist/semantic-rule-verifier.d.ts +1 -1
  176. package/dist/semantic-rule-verifier.js +3 -3
  177. package/dist/session-observer-bands.d.ts +1 -1
  178. package/dist/session-observer-state.d.ts +1 -1
  179. package/dist/shared-context/manager.d.ts +5 -5
  180. package/dist/signal.d.ts +1 -1
  181. package/dist/storage.d.ts +19 -1
  182. package/dist/storage.js +2 -2
  183. package/dist/summarizer.d.ts +1 -1
  184. package/dist/summary-snapshot.d.ts +1 -1
  185. package/dist/temporal-supersession.d.ts +1 -1
  186. package/dist/temporal-validity.d.ts +1 -1
  187. package/dist/threading.d.ts +1 -1
  188. package/dist/tier-migration.d.ts +1 -1
  189. package/dist/tier-routing.d.ts +1 -1
  190. package/dist/topics.d.ts +1 -1
  191. package/dist/transcript.d.ts +1 -1
  192. package/dist/transfer/types.d.ts +12 -12
  193. package/dist/types-D5VRAI04.d.ts +3134 -0
  194. package/dist/types.d.ts +3 -2862
  195. package/dist/types.js +1 -1
  196. package/dist/utility-runtime.d.ts +1 -1
  197. package/dist/verified-recall.js +3 -3
  198. package/package.json +1 -1
  199. package/src/access-http.ts +182 -8
  200. package/src/access-mcp.ts +198 -0
  201. package/src/access-service.ts +65 -0
  202. package/src/cli.ts +187 -0
  203. package/src/config.ts +7 -0
  204. package/src/index.ts +7 -0
  205. package/src/orchestrator.ts +42 -0
  206. package/src/storage.ts +106 -0
  207. package/src/types.ts +5 -0
  208. package/src/wearables/cleanup.test.ts +134 -0
  209. package/src/wearables/cleanup.ts +188 -0
  210. package/src/wearables/cli.test.ts +170 -0
  211. package/src/wearables/cli.ts +441 -0
  212. package/src/wearables/config.test.ts +143 -0
  213. package/src/wearables/config.ts +332 -0
  214. package/src/wearables/corrections.test.ts +118 -0
  215. package/src/wearables/corrections.ts +211 -0
  216. package/src/wearables/day-store.test.ts +143 -0
  217. package/src/wearables/day-store.ts +238 -0
  218. package/src/wearables/errors.test.ts +32 -0
  219. package/src/wearables/errors.ts +29 -0
  220. package/src/wearables/index.ts +114 -0
  221. package/src/wearables/memory-gen.test.ts +342 -0
  222. package/src/wearables/memory-gen.ts +413 -0
  223. package/src/wearables/pipeline.test.ts +608 -0
  224. package/src/wearables/pipeline.ts +519 -0
  225. package/src/wearables/redaction.test.ts +94 -0
  226. package/src/wearables/redaction.ts +156 -0
  227. package/src/wearables/registry.test.ts +62 -0
  228. package/src/wearables/registry.ts +133 -0
  229. package/src/wearables/service.test.ts +425 -0
  230. package/src/wearables/service.ts +691 -0
  231. package/src/wearables/speakers.test.ts +110 -0
  232. package/src/wearables/speakers.ts +174 -0
  233. package/src/wearables/storage-io.test.ts +105 -0
  234. package/src/wearables/sync-state.test.ts +134 -0
  235. package/src/wearables/sync-state.ts +186 -0
  236. package/src/wearables/types.ts +285 -0
  237. package/dist/chunk-4R4KTDIE.js.map +0 -1
  238. package/dist/chunk-5GOMXHLC.js.map +0 -1
  239. package/dist/chunk-C4PZTWTG.js.map +0 -1
  240. package/dist/chunk-I6UCUHLK.js.map +0 -1
  241. package/dist/chunk-KQFQ3IS5.js.map +0 -1
  242. package/dist/chunk-PJGB7XRR.js.map +0 -1
  243. package/dist/chunk-RKW6QR7W.js.map +0 -1
  244. package/dist/chunk-ZJSZNTEI.js.map +0 -1
  245. /package/dist/{chunk-GYTVOLNX.js.map → chunk-3MNBW7R7.js.map} +0 -0
  246. /package/dist/{chunk-QFQQFX2H.js.map → chunk-3R2UZV3U.js.map} +0 -0
  247. /package/dist/{chunk-O4UNM6OR.js.map → chunk-532VCWYW.js.map} +0 -0
  248. /package/dist/{chunk-2UFQYU5F.js.map → chunk-57QXN2CS.js.map} +0 -0
  249. /package/dist/{chunk-UGEBPVNI.js.map → chunk-GE7Q7KXP.js.map} +0 -0
  250. /package/dist/{chunk-GLWW3EJQ.js.map → chunk-KB4MFBF5.js.map} +0 -0
  251. /package/dist/{chunk-FH3PPO42.js.map → chunk-KVFYTRMV.js.map} +0 -0
  252. /package/dist/{chunk-BNW5NJJH.js.map → chunk-LQYTQCXM.js.map} +0 -0
  253. /package/dist/{chunk-AYHXQR53.js.map → chunk-MVQN73GT.js.map} +0 -0
  254. /package/dist/{chunk-ZZPIJPPD.js.map → chunk-N5RGXWLQ.js.map} +0 -0
  255. /package/dist/{chunk-R3OQGYOU.js.map → chunk-P2D2MM47.js.map} +0 -0
  256. /package/dist/{chunk-PSUB67YB.js.map → chunk-PW6GURU3.js.map} +0 -0
  257. /package/dist/{chunk-W3BKVM64.js.map → chunk-QDV6VAD4.js.map} +0 -0
  258. /package/dist/{chunk-3QSU4NFF.js.map → chunk-QHXW3LZV.js.map} +0 -0
  259. /package/dist/{chunk-OZXVGYGZ.js.map → chunk-STDAAGH7.js.map} +0 -0
  260. /package/dist/{chunk-FMGWXIES.js.map → chunk-TZDSNIRO.js.map} +0 -0
  261. /package/dist/{chunk-2L54V4ZO.js.map → chunk-UELS6WWF.js.map} +0 -0
  262. /package/dist/{chunk-BPSGLMQ4.js.map → chunk-YQNADJCT.js.map} +0 -0
package/dist/types.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  SPECULATIVE_TTL_DAYS,
5
5
  confidenceTier,
6
6
  isRecallDisclosure
7
- } from "./chunk-4R4KTDIE.js";
7
+ } from "./chunk-FPNQF475.js";
8
8
  import "./chunk-PZ5AY32C.js";
9
9
  export {
10
10
  DEFAULT_RECALL_DISCLOSURE,
@@ -1,5 +1,5 @@
1
1
  import { TierRoutingPolicy } from './tier-routing.js';
2
- import './types.js';
2
+ import './types-D5VRAI04.js';
3
3
  import './types-BliCnURB.js';
4
4
  import './index-DJ9QWMw-.js';
5
5
  import './lifecycle.js';
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  searchVerifiedEpisodes
3
- } from "./chunk-R3OQGYOU.js";
3
+ } from "./chunk-P2D2MM47.js";
4
4
  import "./chunk-HQ6NIBL6.js";
5
- import "./chunk-PJGB7XRR.js";
5
+ import "./chunk-UGHUNQ74.js";
6
6
  import "./chunk-5UZXUTVO.js";
7
7
  import "./chunk-J6A3CX5N.js";
8
- import "./chunk-4R4KTDIE.js";
8
+ import "./chunk-FPNQF475.js";
9
9
  import "./chunk-RULE4VG5.js";
10
10
  import "./chunk-SCU65EZI.js";
11
11
  import "./chunk-MB5RSUW6.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.3.623",
3
+ "version": "9.3.625",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,6 +8,7 @@ import { fileURLToPath, URL } from "node:url";
8
8
  import { gunzipSync } from "node:zlib";
9
9
  import { log } from "./logger.js";
10
10
  import { EngramAccessInputError, type EngramAccessService } from "./access-service.js";
11
+ import { WearablesInputError } from "./wearables/errors.js";
11
12
  import { EngramMcpServer } from "./access-mcp.js";
12
13
  import { validateRequest, type SchemaName, type SchemaTypeFor } from "./access-schema.js";
13
14
  import {
@@ -984,23 +985,149 @@ export class EngramAccessHttpServer {
984
985
  ...(disclosure !== undefined ? { disclosure } : {}),
985
986
  });
986
987
  } catch (err) {
987
- const message = err instanceof Error ? err.message : String(err);
988
- if (message.startsWith("recallXray:")) {
988
+ // Only surface the message for the deliberately-prefixed recallXray
989
+ // input-validation errors, and only when it is a real Error.message
990
+ // never String(err) of an arbitrary throw, which CodeQL flags as
991
+ // stack-trace exposure (js/stack-trace-exposure). Validation errors are
992
+ // always thrown as Error instances (see access-service.ts), so this is
993
+ // behavior-preserving; anything else is a server-side fault and is
994
+ // rethrown so the outer `handle()` catch returns 500 + logs it.
995
+ if (err instanceof Error && err.message.startsWith("recallXray:")) {
989
996
  this.respondJson(res, 400, {
990
997
  error: "invalid_request",
991
998
  code: "invalid_request",
992
- message,
999
+ message: err.message,
993
1000
  });
994
1001
  return;
995
1002
  }
996
- // Anything else is a server-side fault; rethrow so the
997
- // outer `handle()` catch returns 500 + logs the error.
998
1003
  throw err;
999
1004
  }
1000
1005
  this.respondJson(res, 200, payload);
1001
1006
  return;
1002
1007
  }
1003
1008
 
1009
+ // -- Wearables (Limitless / Bee / Omi transcript ingestion). All
1010
+ // behavior + validation lives in WearablesService; these routes
1011
+ // translate transport shape only. Service validation errors map
1012
+ // to 400 via respondWearablesError; backend faults bubble to the
1013
+ // global 500 handler.
1014
+ if (
1015
+ req.method === "GET" &&
1016
+ (pathname === "/engram/v1/wearables/status" || pathname === "/remnic/v1/wearables/status")
1017
+ ) {
1018
+ this.respondJson(res, 200, await this.service.wearablesStatus());
1019
+ return;
1020
+ }
1021
+
1022
+ if (
1023
+ req.method === "POST" &&
1024
+ (pathname === "/engram/v1/wearables/sync" || pathname === "/remnic/v1/wearables/sync")
1025
+ ) {
1026
+ const body = (await this.readJsonBody(req)) as Record<string, unknown>;
1027
+ const source = optionalQueryString(body.source, "source");
1028
+ const date = optionalQueryString(body.date, "date");
1029
+ let days: number | undefined;
1030
+ if (body.days !== undefined && body.days !== null) {
1031
+ if (
1032
+ typeof body.days !== "number" ||
1033
+ !Number.isInteger(body.days) ||
1034
+ body.days < 1
1035
+ ) {
1036
+ throw new EngramAccessInputError(
1037
+ `days must be a positive integer (got ${JSON.stringify(body.days)})`,
1038
+ );
1039
+ }
1040
+ days = body.days;
1041
+ }
1042
+ if (body.forceMemories !== undefined && typeof body.forceMemories !== "boolean") {
1043
+ throw new EngramAccessInputError(
1044
+ `forceMemories must be a boolean (got ${JSON.stringify(body.forceMemories)})`,
1045
+ );
1046
+ }
1047
+ try {
1048
+ const summaries = await this.service.wearablesSync({
1049
+ source,
1050
+ date,
1051
+ days,
1052
+ forceMemories: body.forceMemories === true,
1053
+ });
1054
+ this.respondJson(res, 200, { summaries });
1055
+ } catch (err) {
1056
+ if (this.respondWearablesError(res, err)) return;
1057
+ throw err;
1058
+ }
1059
+ return;
1060
+ }
1061
+
1062
+ if (
1063
+ req.method === "GET" &&
1064
+ (pathname === "/engram/v1/wearables/transcript" || pathname === "/remnic/v1/wearables/transcript")
1065
+ ) {
1066
+ const date = parsed.searchParams.get("date");
1067
+ if (!date || date.trim().length === 0) {
1068
+ throw new EngramAccessInputError(
1069
+ "date query parameter is required (YYYY-MM-DD)",
1070
+ );
1071
+ }
1072
+ const sourceParam = parsed.searchParams.get("source");
1073
+ try {
1074
+ const transcripts = await this.service.wearablesTranscriptDay({
1075
+ date,
1076
+ source: sourceParam && sourceParam.length > 0 ? sourceParam : undefined,
1077
+ });
1078
+ this.respondJson(res, 200, { transcripts });
1079
+ } catch (err) {
1080
+ if (this.respondWearablesError(res, err)) return;
1081
+ throw err;
1082
+ }
1083
+ return;
1084
+ }
1085
+
1086
+ if (
1087
+ req.method === "GET" &&
1088
+ (pathname === "/engram/v1/wearables/transcripts/search" ||
1089
+ pathname === "/remnic/v1/wearables/transcripts/search")
1090
+ ) {
1091
+ const queryParam = parsed.searchParams.get("q");
1092
+ if (!queryParam || queryParam.trim().length === 0) {
1093
+ throw new EngramAccessInputError(
1094
+ "q query parameter is required and must be non-empty",
1095
+ );
1096
+ }
1097
+ try {
1098
+ const results = await this.service.wearablesTranscriptSearch({
1099
+ query: queryParam,
1100
+ source: nonEmptyQueryParam(parsed.searchParams.get("source")),
1101
+ from: nonEmptyQueryParam(parsed.searchParams.get("from")),
1102
+ to: nonEmptyQueryParam(parsed.searchParams.get("to")),
1103
+ limit: positiveIntQueryParam(parsed.searchParams.get("limit"), "limit"),
1104
+ });
1105
+ this.respondJson(res, 200, { results });
1106
+ } catch (err) {
1107
+ if (this.respondWearablesError(res, err)) return;
1108
+ throw err;
1109
+ }
1110
+ return;
1111
+ }
1112
+
1113
+ if (
1114
+ req.method === "GET" &&
1115
+ (pathname === "/engram/v1/wearables/memories" || pathname === "/remnic/v1/wearables/memories")
1116
+ ) {
1117
+ try {
1118
+ const memories = await this.service.wearablesTranscriptMemories({
1119
+ source: nonEmptyQueryParam(parsed.searchParams.get("source")),
1120
+ date: nonEmptyQueryParam(parsed.searchParams.get("date")),
1121
+ limit: positiveIntQueryParam(parsed.searchParams.get("limit"), "limit"),
1122
+ });
1123
+ this.respondJson(res, 200, { memories });
1124
+ } catch (err) {
1125
+ if (this.respondWearablesError(res, err)) return;
1126
+ throw err;
1127
+ }
1128
+ return;
1129
+ }
1130
+
1004
1131
  if (req.method === "POST" && pathname === "/engram/v1/observe") {
1005
1132
  const body = await this.readValidatedBody(req, "observe");
1006
1133
  this.ensureWriteRateLimitAvailable();
@@ -1558,12 +1685,15 @@ export class EngramAccessHttpServer {
1558
1685
  );
1559
1686
  this.respondJson(res, 200, snapshot);
1560
1687
  } catch (err) {
1561
- const message = err instanceof Error ? err.message : String(err);
1562
- if (message.startsWith("graphSnapshot:")) {
1688
+ // As with recallXray above: surface only the deliberately-prefixed
1689
+ // graphSnapshot validation Error.message, never String(err) of an
1690
+ // arbitrary throw (CodeQL js/stack-trace-exposure). Validation errors are
1691
+ // always Error instances; anything else is rethrown as a 500.
1692
+ if (err instanceof Error && err.message.startsWith("graphSnapshot:")) {
1563
1693
  this.respondJson(res, 400, {
1564
1694
  error: "invalid_request",
1565
1695
  code: "invalid_request",
1566
- message,
1696
+ message: err.message,
1567
1697
  });
1568
1698
  return;
1569
1699
  }
@@ -2365,4 +2495,48 @@ export class EngramAccessHttpServer {
2365
2495
  private shouldCountWriteRateLimit(response: { dryRun?: boolean; idempotencyReplay?: boolean }): boolean {
2366
2496
  return response.dryRun !== true && response.idempotencyReplay !== true;
2367
2497
  }
2498
+
2499
+ /**
2500
+ * Map wearables validation errors (WearablesInputError — invalid
2501
+ * params, unknown/disabled sources, missing connector packages) to a
2502
+ * 400 response. Returns false for everything else so backend faults
2503
+ * keep flowing to the global 500 handler.
2504
+ */
2505
+ private respondWearablesError(res: ServerResponse, err: unknown): boolean {
2506
+ if (err instanceof WearablesInputError) {
2507
+ this.respondJson(res, 400, {
2508
+ error: "invalid_request",
2509
+ code: "invalid_request",
2510
+ message: err.message,
2511
+ });
2512
+ return true;
2513
+ }
2514
+ return false;
2515
+ }
2516
+ }
2517
+
2518
+ /** Optional string field from a JSON body: absent/null/"" → undefined. */
2519
+ function optionalQueryString(value: unknown, label: string): string | undefined {
2520
+ if (value === undefined || value === null || value === "") return undefined;
2521
+ if (typeof value !== "string") {
2522
+ throw new EngramAccessInputError(
2523
+ `${label} must be a string (got ${JSON.stringify(value)})`,
2524
+ );
2525
+ }
2526
+ return value;
2527
+ }
2528
+
2529
+ /** Optional non-empty query param: null/"" → undefined. */
2530
+ function nonEmptyQueryParam(value: string | null): string | undefined {
2531
+ return value !== null && value.length > 0 ? value : undefined;
2532
+ }
2533
+
2534
+ /** Optional positive-integer query param; rejects invalid values. */
2535
+ function positiveIntQueryParam(value: string | null, label: string): number | undefined {
2536
+ if (value === null || value.length === 0) return undefined;
2537
+ const parsed = Number(value);
2538
+ if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
2539
+ throw new EngramAccessInputError(`${label} expects a positive integer`);
2540
+ }
2541
+ return parsed;
2368
2542
  }
package/src/access-mcp.ts CHANGED
@@ -204,6 +204,36 @@ function parseMcpRequest<N extends SchemaName>(
204
204
  );
205
205
  }
206
206
 
207
+ /**
208
+ * Strict optional-string MCP argument: absent/null/"" → undefined,
209
+ * non-string → loud error (CLAUDE.md rule 51 — no silent coercion).
210
+ */
211
+ function optionalNonEmptyString(value: unknown, label: string): string | undefined {
212
+ if (value === undefined || value === null || value === "") return undefined;
213
+ if (typeof value !== "string") {
214
+ throw new Error(`${label} expects a string; got ${JSON.stringify(value)}`);
215
+ }
216
+ return value;
217
+ }
218
+
219
+ /**
220
+ * Strict optional positive-integer MCP argument. Accepts JSON numbers
221
+ * and numeric strings (loosely-typed MCP clients send both); rejects
222
+ * everything else including booleans, which `Number()` would silently
223
+ * coerce.
224
+ */
225
+ function optionalPositiveInteger(value: unknown, label: string): number | undefined {
226
+ if (value === undefined || value === null || value === "") return undefined;
227
+ if (typeof value !== "number" && typeof value !== "string") {
228
+ throw new Error(`${label} expects a positive integer; got ${JSON.stringify(value)}`);
229
+ }
230
+ const parsed = typeof value === "number" ? value : Number(value);
231
+ if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
232
+ throw new Error(`${label} expects a positive integer; got ${JSON.stringify(value)}`);
233
+ }
234
+ return parsed;
235
+ }
236
+
207
237
  function getObjectProperties(value: unknown): Record<string, unknown> | undefined {
208
238
  return value && typeof value === "object" && !Array.isArray(value)
209
239
  ? (value as Record<string, unknown>)
@@ -452,6 +482,124 @@ export class EngramMcpServer {
452
482
  additionalProperties: false,
453
483
  },
454
484
  },
485
+ {
486
+ name: "engram.wearables_status",
487
+ description:
488
+ "Status of wearable transcript sources (Limitless / Bee / Omi): configured sources, connector availability, last sync, stored transcript days.",
489
+ inputSchema: {
490
+ type: "object",
491
+ properties: {},
492
+ additionalProperties: false,
493
+ },
494
+ },
495
+ {
496
+ name: "engram.wearables_sync",
497
+ description:
498
+ "Pull, clean, and store wearable transcripts for one source or all enabled sources; optionally creates trust-gated memories per the source's memoryMode.",
499
+ inputSchema: {
500
+ type: "object",
501
+ properties: {
502
+ source: {
503
+ type: "string",
504
+ description: "Source id (e.g. limitless, bee, omi). Omit to sync every enabled source.",
505
+ },
506
+ date: {
507
+ type: "string",
508
+ description: "Sync exactly this day (YYYY-MM-DD). Overrides days.",
509
+ },
510
+ days: {
511
+ type: "integer",
512
+ minimum: 1,
513
+ maximum: 90,
514
+ description: "Lookback window in days ending today (default 2).",
515
+ },
516
+ forceMemories: {
517
+ type: "boolean",
518
+ description: "Re-run memory extraction even for unchanged days.",
519
+ },
520
+ },
521
+ additionalProperties: false,
522
+ },
523
+ },
524
+ {
525
+ name: "engram.transcript_day",
526
+ description:
527
+ "Return the full stored wearable transcript(s) for a day, across all sources or one source, with cross-source overlap hints.",
528
+ inputSchema: {
529
+ type: "object",
530
+ properties: {
531
+ date: {
532
+ type: "string",
533
+ description: "Day to read (YYYY-MM-DD). Required.",
534
+ },
535
+ source: {
536
+ type: "string",
537
+ description: "Optional source id to scope to (e.g. limitless).",
538
+ },
539
+ },
540
+ required: ["date"],
541
+ additionalProperties: false,
542
+ },
543
+ },
544
+ {
545
+ name: "engram.transcript_search",
546
+ description:
547
+ "Search stored wearable transcripts. Results carry source + date so callers can pull the full day via engram.transcript_day.",
548
+ inputSchema: {
549
+ type: "object",
550
+ properties: {
551
+ query: {
552
+ type: "string",
553
+ description: "Search query. Required; non-empty.",
554
+ },
555
+ source: {
556
+ type: "string",
557
+ description: "Optional source id filter.",
558
+ },
559
+ from: {
560
+ type: "string",
561
+ description: "Optional inclusive start date (YYYY-MM-DD).",
562
+ },
563
+ to: {
564
+ type: "string",
565
+ description: "Optional inclusive end date (YYYY-MM-DD).",
566
+ },
567
+ limit: {
568
+ type: "integer",
569
+ minimum: 1,
570
+ maximum: 50,
571
+ description: "Maximum results (default 10).",
572
+ },
573
+ },
574
+ required: ["query"],
575
+ additionalProperties: false,
576
+ },
577
+ },
578
+ {
579
+ name: "engram.transcript_memories",
580
+ description:
581
+ "List memories created from wearable transcripts, filterable by source and/or day. Includes pending_review candidates awaiting approval.",
582
+ inputSchema: {
583
+ type: "object",
584
+ properties: {
585
+ source: {
586
+ type: "string",
587
+ description: "Optional source id filter (e.g. limitless).",
588
+ },
589
+ date: {
590
+ type: "string",
591
+ description: "Optional transcript day filter (YYYY-MM-DD).",
592
+ },
593
+ limit: {
594
+ type: "integer",
595
+ minimum: 1,
596
+ maximum: 200,
597
+ description: "Maximum results (default 50).",
598
+ },
599
+ },
600
+ additionalProperties: false,
601
+ },
602
+ },
455
603
  {
456
604
  name: "engram.action_confidence",
457
605
  description:
@@ -2348,6 +2496,56 @@ export class EngramMcpServer {
2348
2496
  : {}),
2349
2497
  });
2350
2498
  }
2499
+ case "engram.wearables_status":
2500
+ return this.service.wearablesStatus();
2501
+ case "engram.wearables_sync": {
2502
+ const source = optionalNonEmptyString(args.source, "engram.wearables_sync: source");
2503
+ const date = optionalNonEmptyString(args.date, "engram.wearables_sync: date");
2504
+ const days = optionalPositiveInteger(args.days, "engram.wearables_sync: days");
2505
+ let forceMemories: boolean | undefined;
2506
+ if (args.forceMemories !== undefined && args.forceMemories !== null) {
2507
+ if (typeof args.forceMemories !== "boolean") {
2508
+ throw new Error(
2509
+ `engram.wearables_sync: forceMemories expects a boolean; got ${JSON.stringify(args.forceMemories)}`,
2510
+ );
2511
+ }
2512
+ forceMemories = args.forceMemories;
2513
+ }
2514
+ // Date/days/source validation beyond shape lives in the shared
2515
+ // WearablesService so CLI/HTTP/MCP reject identically.
2516
+ return this.service.wearablesSync({ source, date, days, forceMemories });
2517
+ }
2518
+ case "engram.transcript_day": {
2519
+ const date = typeof args.date === "string" ? args.date : "";
2520
+ if (date.trim().length === 0) {
2521
+ throw new Error(
2522
+ "engram.transcript_day: date is required and must be YYYY-MM-DD",
2523
+ );
2524
+ }
2525
+ const source = optionalNonEmptyString(args.source, "engram.transcript_day: source");
2526
+ return this.service.wearablesTranscriptDay({ date, source });
2527
+ }
2528
+ case "engram.transcript_search": {
2529
+ const query = typeof args.query === "string" ? args.query : "";
2530
+ if (query.trim().length === 0) {
2531
+ throw new Error(
2532
+ "engram.transcript_search: query is required and must be non-empty",
2533
+ );
2534
+ }
2535
+ return this.service.wearablesTranscriptSearch({
2536
+ query,
2537
+ source: optionalNonEmptyString(args.source, "engram.transcript_search: source"),
2538
+ from: optionalNonEmptyString(args.from, "engram.transcript_search: from"),
2539
+ to: optionalNonEmptyString(args.to, "engram.transcript_search: to"),
2540
+ limit: optionalPositiveInteger(args.limit, "engram.transcript_search: limit"),
2541
+ });
2542
+ }
2543
+ case "engram.transcript_memories":
2544
+ return this.service.wearablesTranscriptMemories({
2545
+ source: optionalNonEmptyString(args.source, "engram.transcript_memories: source"),
2546
+ date: optionalNonEmptyString(args.date, "engram.transcript_memories: date"),
2547
+ limit: optionalPositiveInteger(args.limit, "engram.transcript_memories: limit"),
2548
+ });
2351
2549
  case "engram.action_confidence": {
2352
2550
  const body: ActionConfidenceRequest = parseMcpRequest("actionConfidence", args);
2353
2551
  return this.service.actionConfidence(body);
@@ -39,6 +39,7 @@ import {
39
39
  import { runProcedureMining } from "./procedural/procedure-miner.js";
40
40
  import type { PatternReinforcementResult } from "./maintenance/pattern-reinforcement.js";
41
41
  import type { LiveConnectorsRunSummary } from "./live-connectors-runner.js";
42
+ import type { WearablesService } from "./wearables/service.js";
42
43
  import {
43
44
  computeProcedureStats,
44
45
  type ProcedureStatsReport,
@@ -6154,4 +6155,68 @@ export class EngramAccessService {
6154
6155
  notes: result.notes,
6155
6156
  };
6156
6157
  }
6158
+
6159
+ // ---------------------------------------------------------------------------
6160
+ // Wearables (Limitless / Bee / Omi transcript ingestion)
6161
+ //
6162
+ // Thin delegations to the orchestrator-owned WearablesService — the
6163
+ // same instance behind the CLI, so HTTP/MCP callers observe identical
6164
+ // behavior and validation (renderer-sharing rule).
6165
+ // ---------------------------------------------------------------------------
6166
+
6167
+ async wearablesStatus(): Promise<
6168
+ Awaited<ReturnType<WearablesService["status"]>>
6169
+ > {
6170
+ return this.orchestrator.getWearablesService().status();
6171
+ }
6172
+
6173
+ async wearablesSync(request: {
6174
+ source?: string;
6175
+ date?: string;
6176
+ days?: number;
6177
+ forceMemories?: boolean;
6178
+ }): Promise<Awaited<ReturnType<WearablesService["sync"]>>> {
6179
+ return this.orchestrator.getWearablesService().sync({
6180
+ source: request.source,
6181
+ date: request.date,
6182
+ days: request.days,
6183
+ forceMemories: request.forceMemories,
6184
+ });
6185
+ }
6186
+
6187
+ async wearablesTranscriptDay(request: {
6188
+ date: string;
6189
+ source?: string;
6190
+ }): Promise<Awaited<ReturnType<WearablesService["dayTranscript"]>>> {
6191
+ return this.orchestrator
6192
+ .getWearablesService()
6193
+ .dayTranscript(request.date, request.source);
6194
+ }
6195
+
6196
+ async wearablesTranscriptSearch(request: {
6197
+ query: string;
6198
+ source?: string;
6199
+ from?: string;
6200
+ to?: string;
6201
+ limit?: number;
6202
+ }): Promise<Awaited<ReturnType<WearablesService["searchTranscripts"]>>> {
6203
+ return this.orchestrator.getWearablesService().searchTranscripts(request.query, {
6204
+ source: request.source,
6205
+ from: request.from,
6206
+ to: request.to,
6207
+ limit: request.limit,
6208
+ });
6209
+ }
6210
+
6211
+ async wearablesTranscriptMemories(request: {
6212
+ source?: string;
6213
+ date?: string;
6214
+ limit?: number;
6215
+ }): Promise<Awaited<ReturnType<WearablesService["transcriptMemories"]>>> {
6216
+ return this.orchestrator.getWearablesService().transcriptMemories({
6217
+ source: request.source,
6218
+ date: request.date,
6219
+ limit: request.limit,
6220
+ });
6221
+ }
6157
6222
  }