@remnic/core 10.0.91 → 10.0.93

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 (522) hide show
  1. package/dist/access-admin-ops-surface.js +334 -61
  2. package/dist/access-authorization-probe.js +2 -283
  3. package/dist/access-boundary.d.ts +1 -1
  4. package/dist/access-boundary.d.ts.map +1 -1
  5. package/dist/access-boundary.js +1 -282
  6. package/dist/access-cli.js +288 -304
  7. package/dist/access-cli.js.map +1 -1
  8. package/dist/access-coding-context-resolution.js +3 -3
  9. package/dist/access-extraction-force-flush.js +334 -61
  10. package/dist/access-http-briefing.js +334 -62
  11. package/dist/access-http-query.js +334 -63
  12. package/dist/access-http.js +332 -114
  13. package/dist/access-identity-continuity-surface.js +334 -61
  14. package/dist/access-lcm-surface.js +334 -61
  15. package/dist/access-mcp-frames.d.ts +20 -6
  16. package/dist/access-mcp-frames.d.ts.map +1 -1
  17. package/dist/access-mcp-frames.js +1 -1
  18. package/dist/access-mcp.d.ts +5 -0
  19. package/dist/access-mcp.d.ts.map +1 -1
  20. package/dist/access-mcp.js +332 -89
  21. package/dist/access-namespace-preflight.js +2 -282
  22. package/dist/access-observe-transcript.js +3 -3
  23. package/dist/access-observe-write-category.js +6 -6
  24. package/dist/access-observe-write-surface.js +334 -61
  25. package/dist/access-offline-manifest.js +12 -11
  26. package/dist/access-operations-batch.js +332 -66
  27. package/dist/access-operations.js +332 -75
  28. package/dist/access-recall-concurrency.js +334 -61
  29. package/dist/access-recall-diagnostics.js +6 -5
  30. package/dist/access-recall-response.js +334 -61
  31. package/dist/access-recall-surface.js +334 -61
  32. package/dist/access-schema.js +2 -2
  33. package/dist/access-service.d.ts +1 -0
  34. package/dist/access-service.d.ts.map +1 -1
  35. package/dist/access-service.js +337 -64
  36. package/dist/access-surface-catalog.js +6 -283
  37. package/dist/access-surface-catalog.js.map +1 -1
  38. package/dist/active-context-transform.js +1 -1
  39. package/dist/active-memory-bridge.js +3 -2
  40. package/dist/active-recall.js +36 -37
  41. package/dist/active-recall.js.map +1 -1
  42. package/dist/activity/journal-extract.d.ts +13 -1
  43. package/dist/activity/journal-extract.d.ts.map +1 -1
  44. package/dist/{auto-sync-3AZW7MXI.js → auto-sync-BO5YZKM6.js} +13 -15
  45. package/dist/{auto-sync-3AZW7MXI.js.map → auto-sync-BO5YZKM6.js.map} +1 -1
  46. package/dist/briefing.js +20 -20
  47. package/dist/buffer.js +16 -16
  48. package/dist/calibration.js +10 -10
  49. package/dist/{capsule-merge-2IIXFXN5.js → capsule-merge-6IDAYTKG.js} +6 -6
  50. package/dist/causal-consolidation.js +23 -23
  51. package/dist/{chunk-QL7RHYK6.js → chunk-2LMA22UU.js} +5 -5
  52. package/dist/{chunk-RGUUBAT5.js → chunk-2WCLTWV2.js} +3 -3
  53. package/dist/{chunk-RSK2WIEM.js → chunk-2YODOG2O.js} +2 -2
  54. package/dist/{chunk-WYG5UNDY.js → chunk-3AUDELZL.js} +2 -2
  55. package/dist/{chunk-OFVCFKDH.js → chunk-3CYOCE6U.js} +3 -3
  56. package/dist/{chunk-6HM6HARE.js → chunk-3FBAEPCU.js} +2 -2
  57. package/dist/{chunk-PIITB2YZ.js → chunk-3ZS3NUEI.js} +4 -4
  58. package/dist/{chunk-V4XE263I.js → chunk-43AA5FVD.js} +1 -1
  59. package/dist/chunk-43AA5FVD.js.map +1 -0
  60. package/dist/{chunk-ANZZLRUX.js → chunk-4IVRQMZW.js} +2 -2
  61. package/dist/{chunk-63E37JCN.js → chunk-4RAJT5L2.js} +4 -4
  62. package/dist/{chunk-45VUHDV2.js → chunk-5CO5G4MN.js} +2 -2
  63. package/dist/{chunk-PUEO37QG.js → chunk-5YT2IIFX.js} +729 -17
  64. package/dist/chunk-5YT2IIFX.js.map +1 -0
  65. package/dist/{chunk-P5VZSSYL.js → chunk-622V2AZA.js} +233 -40
  66. package/dist/chunk-622V2AZA.js.map +1 -0
  67. package/dist/{chunk-3UMDRGFS.js → chunk-65HWAQ4F.js} +3 -3
  68. package/dist/{chunk-T67LUYCR.js → chunk-77NTFICT.js} +5 -5
  69. package/dist/chunk-A2WFUMUN.js +86 -0
  70. package/dist/chunk-A2WFUMUN.js.map +1 -0
  71. package/dist/{chunk-UE6RRXMF.js → chunk-A4F7OIC5.js} +2 -2
  72. package/dist/chunk-AHA5VQGP.js +117 -0
  73. package/dist/chunk-AHA5VQGP.js.map +1 -0
  74. package/dist/{chunk-AOFEMUHG.js → chunk-ATG2PQ4V.js} +56 -5
  75. package/dist/chunk-ATG2PQ4V.js.map +1 -0
  76. package/dist/{chunk-63O7LVMN.js → chunk-B33YWUJV.js} +3 -3
  77. package/dist/{chunk-QVMOCJRV.js → chunk-B377OKUP.js} +2 -2
  78. package/dist/{chunk-IUKYFTGW.js → chunk-B3DR2JBZ.js} +140 -125
  79. package/dist/chunk-B3DR2JBZ.js.map +1 -0
  80. package/dist/{chunk-ZT6WPSII.js → chunk-BGHCSLE5.js} +2 -2
  81. package/dist/{chunk-YYOMGOHB.js → chunk-BOK6BN5M.js} +2 -2
  82. package/dist/{chunk-OH7QLI6W.js → chunk-BPPUH5JM.js} +5 -5
  83. package/dist/{chunk-EV76IZ37.js → chunk-BTI4C4MA.js} +2 -2
  84. package/dist/{chunk-O6ZSWHVR.js → chunk-C2KE5DMD.js} +4 -4
  85. package/dist/{chunk-47F6WWLQ.js → chunk-C73N66PR.js} +2 -2
  86. package/dist/{chunk-3XIWW4TB.js → chunk-CGJYL633.js} +2 -2
  87. package/dist/{chunk-WRT7GJ2D.js → chunk-CIWMTNHW.js} +4 -4
  88. package/dist/{chunk-O55EKIEC.js → chunk-D3ZKHLN2.js} +2 -2
  89. package/dist/{chunk-CD4BCCNP.js → chunk-DJ7WVEWX.js} +2 -2
  90. package/dist/{chunk-RO7RUBLD.js → chunk-EFL24KOI.js} +2 -2
  91. package/dist/{chunk-4CJTQHEG.js → chunk-ESLOLCD4.js} +3 -3
  92. package/dist/{chunk-Z4Q6USKJ.js → chunk-ETBVCRVL.js} +4 -4
  93. package/dist/{chunk-VILPVT4S.js → chunk-EZ47HOJ6.js} +4 -4
  94. package/dist/{chunk-EFYN5I7Z.js → chunk-F4QFU5IZ.js} +2 -2
  95. package/dist/{chunk-T6OZW2WR.js → chunk-FA7SLQ5X.js} +7 -1
  96. package/dist/chunk-FA7SLQ5X.js.map +1 -0
  97. package/dist/{chunk-SHUBTAY2.js → chunk-FMKILENU.js} +14 -14
  98. package/dist/{chunk-YLXETSAH.js → chunk-GCZJ7XRI.js} +2 -2
  99. package/dist/{chunk-J4MMPQ7Q.js → chunk-GQXOBKPL.js} +7 -9
  100. package/dist/{chunk-J4MMPQ7Q.js.map → chunk-GQXOBKPL.js.map} +1 -1
  101. package/dist/{chunk-UDKWFDV2.js → chunk-HLUHEMN4.js} +27 -3
  102. package/dist/chunk-HLUHEMN4.js.map +1 -0
  103. package/dist/{chunk-QFEDITXM.js → chunk-JNDMIAJ4.js} +2 -2
  104. package/dist/chunk-JTSSSR4L.js +359 -0
  105. package/dist/chunk-JTSSSR4L.js.map +1 -0
  106. package/dist/{chunk-5KBIOI4G.js → chunk-JVBAPAWZ.js} +217 -13
  107. package/dist/chunk-JVBAPAWZ.js.map +1 -0
  108. package/dist/{chunk-E5PPE4VA.js → chunk-K2VRKCPA.js} +2 -2
  109. package/dist/{chunk-MKRYQROF.js → chunk-LGEHAQHK.js} +4 -4
  110. package/dist/{chunk-7HTUYFR3.js → chunk-LLPMMMWG.js} +27 -10
  111. package/dist/chunk-LLPMMMWG.js.map +1 -0
  112. package/dist/{chunk-XYTQJD7F.js → chunk-LMJVP5CM.js} +2 -2
  113. package/dist/{chunk-JC36FPQ5.js → chunk-LPV72KRQ.js} +2 -2
  114. package/dist/{chunk-2LCRG2YV.js → chunk-M2XR6B6V.js} +2 -2
  115. package/dist/{chunk-GGRRF4PZ.js → chunk-MDPT6QFK.js} +1 -1
  116. package/dist/{chunk-GGRRF4PZ.js.map → chunk-MDPT6QFK.js.map} +1 -1
  117. package/dist/{chunk-VPQFK63H.js → chunk-MH632KPO.js} +2 -2
  118. package/dist/{chunk-EQ4U73UH.js → chunk-MLAMXQAW.js} +14 -2
  119. package/dist/chunk-MLAMXQAW.js.map +1 -0
  120. package/dist/{chunk-ZU5JDLOH.js → chunk-MMRTPH3J.js} +2 -2
  121. package/dist/{chunk-ZZ67UTQX.js → chunk-MUX7IL77.js} +2 -2
  122. package/dist/{chunk-RYWROSL5.js → chunk-NAQ4A5IX.js} +7 -7
  123. package/dist/{chunk-TVIVY7KU.js → chunk-NED7IR7B.js} +4 -4
  124. package/dist/{chunk-ADYP7HXE.js → chunk-NHVAX7MH.js} +3 -3
  125. package/dist/{chunk-TG3R2SID.js → chunk-NMGHGTVR.js} +5 -5
  126. package/dist/{chunk-YCNIGRRF.js → chunk-NS7U4ZYU.js} +13 -117
  127. package/dist/chunk-NS7U4ZYU.js.map +1 -0
  128. package/dist/{chunk-NGHDW6UU.js → chunk-NUQEREIP.js} +2 -2
  129. package/dist/{chunk-5IIQU4JZ.js → chunk-OB72KNMF.js} +4 -4
  130. package/dist/{chunk-7L3TODI7.js → chunk-OJXJWNFY.js} +2 -2
  131. package/dist/{chunk-66L5ZRJC.js → chunk-P3WRNWWM.js} +157 -4
  132. package/dist/chunk-P3WRNWWM.js.map +1 -0
  133. package/dist/{chunk-E6M225BJ.js → chunk-PC5DRBGD.js} +18 -8
  134. package/dist/chunk-PC5DRBGD.js.map +1 -0
  135. package/dist/{chunk-EURPSF4Y.js → chunk-PH5ZQM35.js} +2 -2
  136. package/dist/{chunk-BMWMTAQF.js → chunk-PRAUN6PN.js} +3 -3
  137. package/dist/{chunk-RWGRSKN5.js → chunk-PT4DZDJA.js} +2 -2
  138. package/dist/{chunk-7EPY3IQL.js → chunk-PZJQ46DH.js} +2 -2
  139. package/dist/{chunk-AH637HZ5.js → chunk-QDESY76F.js} +3 -3
  140. package/dist/{chunk-I6QGI65T.js → chunk-QRMG4HK4.js} +3 -3
  141. package/dist/{chunk-ZU3NJNO7.js → chunk-QSF5SJAM.js} +4 -4
  142. package/dist/{chunk-5IYOKDM4.js → chunk-R23JWJD3.js} +5 -5
  143. package/dist/{chunk-EWJZZDRG.js → chunk-R4BBGPI2.js} +219 -219
  144. package/dist/chunk-R4BBGPI2.js.map +1 -0
  145. package/dist/{chunk-LJQFJF4J.js → chunk-RM2YUFH6.js} +6 -6
  146. package/dist/{chunk-IPT2OY4B.js → chunk-RQFXZYTF.js} +35 -33
  147. package/dist/chunk-RQFXZYTF.js.map +1 -0
  148. package/dist/{chunk-AP6NCP3V.js → chunk-S3S72HRM.js} +8 -6
  149. package/dist/chunk-S3S72HRM.js.map +1 -0
  150. package/dist/{chunk-6QMWECO5.js → chunk-SGQPR5SX.js} +7 -7
  151. package/dist/{chunk-ZGFLIK5H.js → chunk-SPAKBUJY.js} +2 -2
  152. package/dist/{chunk-ER7XANP3.js → chunk-SQ3GEIEL.js} +4 -4
  153. package/dist/{chunk-5UCMYPVF.js → chunk-SVT5IA2H.js} +2 -2
  154. package/dist/{chunk-HVKUCT3W.js → chunk-T4KY6UYF.js} +5 -3
  155. package/dist/{chunk-HVKUCT3W.js.map → chunk-T4KY6UYF.js.map} +1 -1
  156. package/dist/{chunk-FRSHF6FU.js → chunk-TJ4F6H4H.js} +3 -3
  157. package/dist/{chunk-R6HMAIKM.js → chunk-TOSPNM4T.js} +2 -2
  158. package/dist/{chunk-NJJMVS5Y.js → chunk-TRQE3RYR.js} +5 -5
  159. package/dist/{chunk-RMXS4I66.js → chunk-TVDJEUV5.js} +2 -2
  160. package/dist/{chunk-PLCYKAWH.js → chunk-TYRWSE4B.js} +2 -2
  161. package/dist/{chunk-YS2GHSWN.js → chunk-UBEKET2V.js} +389 -39
  162. package/dist/chunk-UBEKET2V.js.map +1 -0
  163. package/dist/{chunk-IQWUACVG.js → chunk-UHMHJX5G.js} +3 -3
  164. package/dist/{chunk-YODUDA6R.js → chunk-UN4RVUG2.js} +22 -22
  165. package/dist/{chunk-KZSGBWOJ.js → chunk-V2KGDESV.js} +2 -2
  166. package/dist/{chunk-E6Z4FBX6.js → chunk-V2QSNFCT.js} +2 -2
  167. package/dist/{chunk-BCCG7VWM.js → chunk-V65H7MI6.js} +396 -13
  168. package/dist/chunk-V65H7MI6.js.map +1 -0
  169. package/dist/chunk-W54Z2MOP.js +4500 -0
  170. package/dist/chunk-W54Z2MOP.js.map +1 -0
  171. package/dist/{chunk-N74SGESS.js → chunk-WKSQTMU7.js} +2 -2
  172. package/dist/{chunk-RIZ5NUNN.js → chunk-WQ4XKIOX.js} +4 -4
  173. package/dist/{chunk-WWK6J6SE.js → chunk-WQURALGV.js} +4996 -2748
  174. package/dist/chunk-WQURALGV.js.map +1 -0
  175. package/dist/{chunk-QEYTRFR3.js → chunk-X26YOFGS.js} +2 -2
  176. package/dist/{chunk-ITHZFHST.js → chunk-X2BVB74H.js} +2 -2
  177. package/dist/{chunk-GNXGJBCA.js → chunk-XIYLWEXC.js} +2 -2
  178. package/dist/{chunk-IBJ6U753.js → chunk-XKZBZOKV.js} +4 -4
  179. package/dist/{chunk-7O2MC42R.js → chunk-XNRLRNQF.js} +2 -2
  180. package/dist/{chunk-65AUYLU3.js → chunk-XPXHQI6N.js} +2 -2
  181. package/dist/{chunk-JBCCWVU2.js → chunk-XRDYRWZI.js} +2 -2
  182. package/dist/chunk-XRWWCW4H.js +102 -0
  183. package/dist/chunk-XRWWCW4H.js.map +1 -0
  184. package/dist/chunk-YKJBSMMP.js +67137 -0
  185. package/dist/chunk-YKJBSMMP.js.map +1 -0
  186. package/dist/{chunk-2JSDPUKD.js → chunk-YMFPFXEX.js} +2 -2
  187. package/dist/{chunk-645WOU5I.js → chunk-ZDZJNXL6.js} +4 -4
  188. package/dist/{chunk-6TCXIRP5.js → chunk-ZKE4CO6B.js} +4 -4
  189. package/dist/{chunk-KEPXC4KN.js → chunk-ZSE774RV.js} +2 -2
  190. package/dist/cli.d.ts.map +1 -1
  191. package/dist/cli.js +305 -187
  192. package/dist/coding/export-okf-codegraph.js +29 -29
  193. package/dist/compounding/engine.js +23 -24
  194. package/dist/config.d.ts.map +1 -1
  195. package/dist/config.js +36 -37
  196. package/dist/connectors/codex-materialize-runner.js +20 -20
  197. package/dist/connectors/index.js +21 -21
  198. package/dist/connectors/live/framework.d.ts +7 -0
  199. package/dist/connectors/live/framework.d.ts.map +1 -1
  200. package/dist/connectors/live/google-drive.d.ts +17 -3
  201. package/dist/connectors/live/google-drive.d.ts.map +1 -1
  202. package/dist/consolidation-provenance-check.js +4 -4
  203. package/dist/consolidation-undo.js +5 -5
  204. package/dist/contradiction/index.js +7 -6
  205. package/dist/{contradiction-scan-P2MHYFVG.js → contradiction-scan-IBPYGQZF.js} +4 -3
  206. package/dist/convergence-refresh.js +22 -22
  207. package/dist/corpus-watermark.js +23 -23
  208. package/dist/deep-recall-http-glue.js +2 -2
  209. package/dist/deep-recall-policy-llm.js +4 -4
  210. package/dist/direct-answer-wiring.js +2 -2
  211. package/dist/embedding-fallback.js +1 -1
  212. package/dist/entity-retrieval.js +21 -21
  213. package/dist/eval-trajectory-store.js +2 -2
  214. package/dist/eval-trajectory.js +2 -2
  215. package/dist/event-order-recall.js +2 -2
  216. package/dist/explicit-capture.d.ts +3 -2
  217. package/dist/explicit-capture.d.ts.map +1 -1
  218. package/dist/explicit-capture.js +530 -6
  219. package/dist/explicit-cue-recall.js +2 -2
  220. package/dist/external-wiki-access.js +2 -283
  221. package/dist/extraction-faithfulness.js +4 -4
  222. package/dist/extraction-judge.js +5 -5
  223. package/dist/extraction-normalization.js +1 -1
  224. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  225. package/dist/extraction-quote-grounding.js +1 -1
  226. package/dist/extraction-source-grounding-rules.js +2 -2
  227. package/dist/extraction-source-grounding.js +3 -3
  228. package/dist/extraction.js +43 -44
  229. package/dist/fallback-llm-codex-subscription.js +2 -2
  230. package/dist/fallback-llm.js +3 -3
  231. package/dist/{first-start-migration-QFI7B5SH.js → first-start-migration-FILJH3RO.js} +6 -5
  232. package/dist/{first-start-migration-QFI7B5SH.js.map → first-start-migration-FILJH3RO.js.map} +1 -1
  233. package/dist/focused-list-recall.js +2 -2
  234. package/dist/harmonic-retrieval.js +20 -20
  235. package/dist/index.js +776 -797
  236. package/dist/lcm/index.js +2 -2
  237. package/dist/live-connectors-runner.d.ts +2 -0
  238. package/dist/live-connectors-runner.d.ts.map +1 -1
  239. package/dist/live-connectors-runner.js +3 -4
  240. package/dist/local-llm-config.js +2 -2
  241. package/dist/local-llm-helpers.d.ts +4 -0
  242. package/dist/local-llm-helpers.d.ts.map +1 -1
  243. package/dist/local-llm-helpers.js +3 -1
  244. package/dist/local-llm.d.ts +4 -1
  245. package/dist/local-llm.d.ts.map +1 -1
  246. package/dist/local-llm.js +4 -4
  247. package/dist/location/index.js +27 -29
  248. package/dist/maintenance/memory-governance.js +20 -20
  249. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +20 -20
  250. package/dist/maintenance/rebuild-memory-projection-runner.js +22 -22
  251. package/dist/maintenance/rebuild-memory-projection.js +21 -21
  252. package/dist/memory-browse-operations.js +2 -283
  253. package/dist/memory-browse.js +8 -7
  254. package/dist/memory-controller.js +2 -2
  255. package/dist/memory-subject.js +2 -2
  256. package/dist/namespaces/migrate.js +21 -21
  257. package/dist/namespaces/search.js +5 -5
  258. package/dist/namespaces/storage.js +20 -20
  259. package/dist/offline-sync-changeset-generations.js +1 -1
  260. package/dist/offline-sync-embedding-generation.js +1 -1
  261. package/dist/offline-sync-impression-drain.js +6 -6
  262. package/dist/offline-sync-snapshot-stream.js +1 -1
  263. package/dist/offline-sync.js +1 -1
  264. package/dist/operator-doctor-corpus.js +24 -24
  265. package/dist/operator-toolkit.js +58 -61
  266. package/dist/orchestration/backlog-scheduler.js +279 -295
  267. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  268. package/dist/orchestration/buffer-drain-runner.js +279 -295
  269. package/dist/orchestration/extraction-run.d.ts +4 -0
  270. package/dist/orchestration/extraction-run.d.ts.map +1 -1
  271. package/dist/orchestration/fact-dedup-window.d.ts +1 -1
  272. package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
  273. package/dist/orchestration/maintenance.js +39 -42
  274. package/dist/orchestrator.d.ts +11 -11
  275. package/dist/orchestrator.d.ts.map +1 -1
  276. package/dist/orchestrator.js +291 -308
  277. package/dist/page-versioning.js +3 -3
  278. package/dist/qmd-daemon-session.js +2 -2
  279. package/dist/qmd.js +3 -3
  280. package/dist/recall-explain-renderer.js +3 -3
  281. package/dist/recall-navigate-access.js +2 -2
  282. package/dist/recall-navigate-disclosure.js +2 -2
  283. package/dist/recall-navigate.js +2 -2
  284. package/dist/recall-navigation-operations.js +2 -283
  285. package/dist/recall-navigation.js +334 -61
  286. package/dist/recall-planner-llm.js +3 -3
  287. package/dist/recall-state-view-admission.js +6 -6
  288. package/dist/recall-why-service.js +10 -9
  289. package/dist/recall-why.js +4 -3
  290. package/dist/recall-xray-cli.js +4 -4
  291. package/dist/recall-xray-renderer.js +3 -3
  292. package/dist/recall-xray.js +2 -2
  293. package/dist/reconcile/manifest.js +3 -3
  294. package/dist/{resolution-N7H3F2E4.js → resolution-OVVIF7ZY.js} +8 -7
  295. package/dist/response-guidance-recall.js +2 -2
  296. package/dist/resume-bundles.js +40 -41
  297. package/dist/salvage-envelope.js +4 -4
  298. package/dist/search/factory.js +4 -4
  299. package/dist/search/index.js +4 -4
  300. package/dist/semantic-consolidation.js +23 -23
  301. package/dist/semantic-rule-promotion.js +20 -20
  302. package/dist/semantic-rule-verifier.js +20 -20
  303. package/dist/shared-context/manager.js +2 -3
  304. package/dist/source-agent-qualifier.js +32 -33
  305. package/dist/standup/build.js +20 -20
  306. package/dist/{state-store-I5HLZTZT.js → state-store-EEAX2MBS.js} +2 -2
  307. package/dist/storage/content-hash-index.d.ts +10 -0
  308. package/dist/storage/content-hash-index.d.ts.map +1 -1
  309. package/dist/storage.js +19 -19
  310. package/dist/subject-config.js +3 -3
  311. package/dist/summarizer.d.ts +4 -1
  312. package/dist/summarizer.d.ts.map +1 -1
  313. package/dist/summarizer.js +6 -6
  314. package/dist/support-passport/index.js +53 -297
  315. package/dist/targeted-fact-recall.js +2 -2
  316. package/dist/tier-routing.js +3 -2
  317. package/dist/{tier-stats-FQGQSFET.js → tier-stats-FXEUJN73.js} +6 -5
  318. package/dist/{tier-stats-FQGQSFET.js.map → tier-stats-FXEUJN73.js.map} +1 -1
  319. package/dist/transcript.js +2 -2
  320. package/dist/transfer/capsule-import.js +4 -4
  321. package/dist/transfer/export-okf.js +20 -20
  322. package/dist/types.d.ts +10 -10
  323. package/dist/types.d.ts.map +1 -1
  324. package/dist/types.js +1 -1
  325. package/dist/verified-recall.js +20 -20
  326. package/dist/wearables/memory-gen.d.ts +5 -1
  327. package/dist/wearables/memory-gen.d.ts.map +1 -1
  328. package/dist/wearables/service.d.ts.map +1 -1
  329. package/dist/who-knows-cli.js +21 -21
  330. package/dist/who-knows.js +20 -20
  331. package/dist/write-envelope.js +3 -3
  332. package/package.json +2 -2
  333. package/src/access-boundary.ts +5 -1
  334. package/src/access-category-alias.test.ts +5 -0
  335. package/src/access-mcp-frames.test.ts +60 -5
  336. package/src/access-mcp-frames.ts +72 -18
  337. package/src/access-mcp.test.ts +66 -2
  338. package/src/access-mcp.ts +19 -13
  339. package/src/access-service-coding-write.test.ts +7 -0
  340. package/src/access-service.ts +3 -0
  341. package/src/activity/journal-extract-storage.test.ts +31 -0
  342. package/src/activity/journal-extract.test.ts +1 -0
  343. package/src/activity/journal-extract.ts +24 -3
  344. package/src/cli.ts +1 -2
  345. package/src/config.test.ts +17 -0
  346. package/src/config.ts +25 -7
  347. package/src/connectors/live/framework.ts +8 -0
  348. package/src/connectors/live/google-drive.test.ts +74 -0
  349. package/src/connectors/live/google-drive.ts +138 -11
  350. package/src/explicit-capture.test.ts +109 -17
  351. package/src/explicit-capture.ts +90 -37
  352. package/src/extraction-quote-grounding.test.ts +20 -0
  353. package/src/extraction-quote-grounding.ts +21 -11
  354. package/src/fact-dedup-window.test.ts +37 -0
  355. package/src/live-connectors-runner.ts +3 -0
  356. package/src/live-connectors-secretref.test.ts +126 -0
  357. package/src/local-llm-helpers.ts +12 -0
  358. package/src/local-llm-shutdown.test.ts +194 -0
  359. package/src/local-llm.ts +32 -30
  360. package/src/orchestration/extraction-run.ts +12 -3
  361. package/src/orchestration/fact-dedup-window.ts +5 -1
  362. package/src/orchestrator-connector-dedup.test.ts +9 -5
  363. package/src/orchestrator.ts +52 -55
  364. package/src/storage/content-hash-index.ts +27 -2
  365. package/src/storage-fact-hash-publish-failure.test.ts +87 -0
  366. package/src/summarizer-shutdown.test.ts +195 -0
  367. package/src/summarizer.ts +15 -2
  368. package/src/types.ts +10 -10
  369. package/src/wearables/memory-gen.test.ts +20 -0
  370. package/src/wearables/memory-gen.ts +14 -7
  371. package/src/wearables/service.ts +14 -1
  372. package/src/wearables/storage-io.test.ts +25 -0
  373. package/dist/chunk-27XYV2XL.js +0 -43
  374. package/dist/chunk-27XYV2XL.js.map +0 -1
  375. package/dist/chunk-5KBIOI4G.js.map +0 -1
  376. package/dist/chunk-66L5ZRJC.js.map +0 -1
  377. package/dist/chunk-7HTUYFR3.js.map +0 -1
  378. package/dist/chunk-AOFEMUHG.js.map +0 -1
  379. package/dist/chunk-AP6NCP3V.js.map +0 -1
  380. package/dist/chunk-APYY5CX3.js +0 -335
  381. package/dist/chunk-APYY5CX3.js.map +0 -1
  382. package/dist/chunk-ATPWMT2E.js +0 -223
  383. package/dist/chunk-ATPWMT2E.js.map +0 -1
  384. package/dist/chunk-BCCG7VWM.js.map +0 -1
  385. package/dist/chunk-E6M225BJ.js.map +0 -1
  386. package/dist/chunk-EQ4U73UH.js.map +0 -1
  387. package/dist/chunk-ETSJD5IV.js +0 -361
  388. package/dist/chunk-ETSJD5IV.js.map +0 -1
  389. package/dist/chunk-EWJZZDRG.js.map +0 -1
  390. package/dist/chunk-FHGWKODN.js +0 -2300
  391. package/dist/chunk-FHGWKODN.js.map +0 -1
  392. package/dist/chunk-GRTKLWYX.js +0 -621
  393. package/dist/chunk-GRTKLWYX.js.map +0 -1
  394. package/dist/chunk-IGLJNHOX.js +0 -62
  395. package/dist/chunk-IGLJNHOX.js.map +0 -1
  396. package/dist/chunk-IPT2OY4B.js.map +0 -1
  397. package/dist/chunk-IUKYFTGW.js.map +0 -1
  398. package/dist/chunk-KUIRDD74.js +0 -3991
  399. package/dist/chunk-KUIRDD74.js.map +0 -1
  400. package/dist/chunk-MVMRUGBH.js +0 -1919
  401. package/dist/chunk-MVMRUGBH.js.map +0 -1
  402. package/dist/chunk-ODL4ZTNI.js +0 -3644
  403. package/dist/chunk-ODL4ZTNI.js.map +0 -1
  404. package/dist/chunk-OGXXGHW6.js +0 -223
  405. package/dist/chunk-OGXXGHW6.js.map +0 -1
  406. package/dist/chunk-P5VZSSYL.js.map +0 -1
  407. package/dist/chunk-PPCLZ4JU.js +0 -28
  408. package/dist/chunk-PPCLZ4JU.js.map +0 -1
  409. package/dist/chunk-PUEO37QG.js.map +0 -1
  410. package/dist/chunk-RRTVGD3J.js +0 -792
  411. package/dist/chunk-RRTVGD3J.js.map +0 -1
  412. package/dist/chunk-STSLB4HT.js +0 -18793
  413. package/dist/chunk-STSLB4HT.js.map +0 -1
  414. package/dist/chunk-T6OZW2WR.js.map +0 -1
  415. package/dist/chunk-TRBGGJKZ.js +0 -32218
  416. package/dist/chunk-TRBGGJKZ.js.map +0 -1
  417. package/dist/chunk-UDKWFDV2.js.map +0 -1
  418. package/dist/chunk-V4XE263I.js.map +0 -1
  419. package/dist/chunk-V7IZKJI3.js +0 -22
  420. package/dist/chunk-V7IZKJI3.js.map +0 -1
  421. package/dist/chunk-VMVVSZTD.js +0 -61
  422. package/dist/chunk-VMVVSZTD.js.map +0 -1
  423. package/dist/chunk-VNUOAXWP.js +0 -203
  424. package/dist/chunk-VNUOAXWP.js.map +0 -1
  425. package/dist/chunk-WLBEZZK5.js +0 -729
  426. package/dist/chunk-WLBEZZK5.js.map +0 -1
  427. package/dist/chunk-WUUES6CU.js +0 -25
  428. package/dist/chunk-WUUES6CU.js.map +0 -1
  429. package/dist/chunk-WWK6J6SE.js.map +0 -1
  430. package/dist/chunk-XICOH5HT.js +0 -346
  431. package/dist/chunk-XICOH5HT.js.map +0 -1
  432. package/dist/chunk-YCNIGRRF.js.map +0 -1
  433. package/dist/chunk-YS2GHSWN.js.map +0 -1
  434. package/dist/chunk-ZCLEYJPB.js +0 -10143
  435. package/dist/chunk-ZCLEYJPB.js.map +0 -1
  436. /package/dist/{capsule-merge-2IIXFXN5.js.map → capsule-merge-6IDAYTKG.js.map} +0 -0
  437. /package/dist/{chunk-QL7RHYK6.js.map → chunk-2LMA22UU.js.map} +0 -0
  438. /package/dist/{chunk-RGUUBAT5.js.map → chunk-2WCLTWV2.js.map} +0 -0
  439. /package/dist/{chunk-RSK2WIEM.js.map → chunk-2YODOG2O.js.map} +0 -0
  440. /package/dist/{chunk-WYG5UNDY.js.map → chunk-3AUDELZL.js.map} +0 -0
  441. /package/dist/{chunk-OFVCFKDH.js.map → chunk-3CYOCE6U.js.map} +0 -0
  442. /package/dist/{chunk-6HM6HARE.js.map → chunk-3FBAEPCU.js.map} +0 -0
  443. /package/dist/{chunk-PIITB2YZ.js.map → chunk-3ZS3NUEI.js.map} +0 -0
  444. /package/dist/{chunk-ANZZLRUX.js.map → chunk-4IVRQMZW.js.map} +0 -0
  445. /package/dist/{chunk-63E37JCN.js.map → chunk-4RAJT5L2.js.map} +0 -0
  446. /package/dist/{chunk-45VUHDV2.js.map → chunk-5CO5G4MN.js.map} +0 -0
  447. /package/dist/{chunk-3UMDRGFS.js.map → chunk-65HWAQ4F.js.map} +0 -0
  448. /package/dist/{chunk-T67LUYCR.js.map → chunk-77NTFICT.js.map} +0 -0
  449. /package/dist/{chunk-UE6RRXMF.js.map → chunk-A4F7OIC5.js.map} +0 -0
  450. /package/dist/{chunk-63O7LVMN.js.map → chunk-B33YWUJV.js.map} +0 -0
  451. /package/dist/{chunk-QVMOCJRV.js.map → chunk-B377OKUP.js.map} +0 -0
  452. /package/dist/{chunk-ZT6WPSII.js.map → chunk-BGHCSLE5.js.map} +0 -0
  453. /package/dist/{chunk-YYOMGOHB.js.map → chunk-BOK6BN5M.js.map} +0 -0
  454. /package/dist/{chunk-OH7QLI6W.js.map → chunk-BPPUH5JM.js.map} +0 -0
  455. /package/dist/{chunk-EV76IZ37.js.map → chunk-BTI4C4MA.js.map} +0 -0
  456. /package/dist/{chunk-O6ZSWHVR.js.map → chunk-C2KE5DMD.js.map} +0 -0
  457. /package/dist/{chunk-47F6WWLQ.js.map → chunk-C73N66PR.js.map} +0 -0
  458. /package/dist/{chunk-3XIWW4TB.js.map → chunk-CGJYL633.js.map} +0 -0
  459. /package/dist/{chunk-WRT7GJ2D.js.map → chunk-CIWMTNHW.js.map} +0 -0
  460. /package/dist/{chunk-O55EKIEC.js.map → chunk-D3ZKHLN2.js.map} +0 -0
  461. /package/dist/{chunk-CD4BCCNP.js.map → chunk-DJ7WVEWX.js.map} +0 -0
  462. /package/dist/{chunk-RO7RUBLD.js.map → chunk-EFL24KOI.js.map} +0 -0
  463. /package/dist/{chunk-4CJTQHEG.js.map → chunk-ESLOLCD4.js.map} +0 -0
  464. /package/dist/{chunk-Z4Q6USKJ.js.map → chunk-ETBVCRVL.js.map} +0 -0
  465. /package/dist/{chunk-VILPVT4S.js.map → chunk-EZ47HOJ6.js.map} +0 -0
  466. /package/dist/{chunk-EFYN5I7Z.js.map → chunk-F4QFU5IZ.js.map} +0 -0
  467. /package/dist/{chunk-SHUBTAY2.js.map → chunk-FMKILENU.js.map} +0 -0
  468. /package/dist/{chunk-YLXETSAH.js.map → chunk-GCZJ7XRI.js.map} +0 -0
  469. /package/dist/{chunk-QFEDITXM.js.map → chunk-JNDMIAJ4.js.map} +0 -0
  470. /package/dist/{chunk-E5PPE4VA.js.map → chunk-K2VRKCPA.js.map} +0 -0
  471. /package/dist/{chunk-MKRYQROF.js.map → chunk-LGEHAQHK.js.map} +0 -0
  472. /package/dist/{chunk-XYTQJD7F.js.map → chunk-LMJVP5CM.js.map} +0 -0
  473. /package/dist/{chunk-JC36FPQ5.js.map → chunk-LPV72KRQ.js.map} +0 -0
  474. /package/dist/{chunk-2LCRG2YV.js.map → chunk-M2XR6B6V.js.map} +0 -0
  475. /package/dist/{chunk-VPQFK63H.js.map → chunk-MH632KPO.js.map} +0 -0
  476. /package/dist/{chunk-ZU5JDLOH.js.map → chunk-MMRTPH3J.js.map} +0 -0
  477. /package/dist/{chunk-ZZ67UTQX.js.map → chunk-MUX7IL77.js.map} +0 -0
  478. /package/dist/{chunk-RYWROSL5.js.map → chunk-NAQ4A5IX.js.map} +0 -0
  479. /package/dist/{chunk-TVIVY7KU.js.map → chunk-NED7IR7B.js.map} +0 -0
  480. /package/dist/{chunk-ADYP7HXE.js.map → chunk-NHVAX7MH.js.map} +0 -0
  481. /package/dist/{chunk-TG3R2SID.js.map → chunk-NMGHGTVR.js.map} +0 -0
  482. /package/dist/{chunk-NGHDW6UU.js.map → chunk-NUQEREIP.js.map} +0 -0
  483. /package/dist/{chunk-5IIQU4JZ.js.map → chunk-OB72KNMF.js.map} +0 -0
  484. /package/dist/{chunk-7L3TODI7.js.map → chunk-OJXJWNFY.js.map} +0 -0
  485. /package/dist/{chunk-EURPSF4Y.js.map → chunk-PH5ZQM35.js.map} +0 -0
  486. /package/dist/{chunk-BMWMTAQF.js.map → chunk-PRAUN6PN.js.map} +0 -0
  487. /package/dist/{chunk-RWGRSKN5.js.map → chunk-PT4DZDJA.js.map} +0 -0
  488. /package/dist/{chunk-7EPY3IQL.js.map → chunk-PZJQ46DH.js.map} +0 -0
  489. /package/dist/{chunk-AH637HZ5.js.map → chunk-QDESY76F.js.map} +0 -0
  490. /package/dist/{chunk-I6QGI65T.js.map → chunk-QRMG4HK4.js.map} +0 -0
  491. /package/dist/{chunk-ZU3NJNO7.js.map → chunk-QSF5SJAM.js.map} +0 -0
  492. /package/dist/{chunk-5IYOKDM4.js.map → chunk-R23JWJD3.js.map} +0 -0
  493. /package/dist/{chunk-LJQFJF4J.js.map → chunk-RM2YUFH6.js.map} +0 -0
  494. /package/dist/{chunk-6QMWECO5.js.map → chunk-SGQPR5SX.js.map} +0 -0
  495. /package/dist/{chunk-ZGFLIK5H.js.map → chunk-SPAKBUJY.js.map} +0 -0
  496. /package/dist/{chunk-ER7XANP3.js.map → chunk-SQ3GEIEL.js.map} +0 -0
  497. /package/dist/{chunk-5UCMYPVF.js.map → chunk-SVT5IA2H.js.map} +0 -0
  498. /package/dist/{chunk-FRSHF6FU.js.map → chunk-TJ4F6H4H.js.map} +0 -0
  499. /package/dist/{chunk-R6HMAIKM.js.map → chunk-TOSPNM4T.js.map} +0 -0
  500. /package/dist/{chunk-NJJMVS5Y.js.map → chunk-TRQE3RYR.js.map} +0 -0
  501. /package/dist/{chunk-RMXS4I66.js.map → chunk-TVDJEUV5.js.map} +0 -0
  502. /package/dist/{chunk-PLCYKAWH.js.map → chunk-TYRWSE4B.js.map} +0 -0
  503. /package/dist/{chunk-IQWUACVG.js.map → chunk-UHMHJX5G.js.map} +0 -0
  504. /package/dist/{chunk-YODUDA6R.js.map → chunk-UN4RVUG2.js.map} +0 -0
  505. /package/dist/{chunk-KZSGBWOJ.js.map → chunk-V2KGDESV.js.map} +0 -0
  506. /package/dist/{chunk-E6Z4FBX6.js.map → chunk-V2QSNFCT.js.map} +0 -0
  507. /package/dist/{chunk-N74SGESS.js.map → chunk-WKSQTMU7.js.map} +0 -0
  508. /package/dist/{chunk-RIZ5NUNN.js.map → chunk-WQ4XKIOX.js.map} +0 -0
  509. /package/dist/{chunk-QEYTRFR3.js.map → chunk-X26YOFGS.js.map} +0 -0
  510. /package/dist/{chunk-ITHZFHST.js.map → chunk-X2BVB74H.js.map} +0 -0
  511. /package/dist/{chunk-GNXGJBCA.js.map → chunk-XIYLWEXC.js.map} +0 -0
  512. /package/dist/{chunk-IBJ6U753.js.map → chunk-XKZBZOKV.js.map} +0 -0
  513. /package/dist/{chunk-7O2MC42R.js.map → chunk-XNRLRNQF.js.map} +0 -0
  514. /package/dist/{chunk-65AUYLU3.js.map → chunk-XPXHQI6N.js.map} +0 -0
  515. /package/dist/{chunk-JBCCWVU2.js.map → chunk-XRDYRWZI.js.map} +0 -0
  516. /package/dist/{chunk-2JSDPUKD.js.map → chunk-YMFPFXEX.js.map} +0 -0
  517. /package/dist/{chunk-645WOU5I.js.map → chunk-ZDZJNXL6.js.map} +0 -0
  518. /package/dist/{chunk-6TCXIRP5.js.map → chunk-ZKE4CO6B.js.map} +0 -0
  519. /package/dist/{chunk-KEPXC4KN.js.map → chunk-ZSE774RV.js.map} +0 -0
  520. /package/dist/{contradiction-scan-P2MHYFVG.js.map → contradiction-scan-IBPYGQZF.js.map} +0 -0
  521. /package/dist/{resolution-N7H3F2E4.js.map → resolution-OVVIF7ZY.js.map} +0 -0
  522. /package/dist/{state-store-I5HLZTZT.js.map → state-store-EEAX2MBS.js.map} +0 -0
package/src/access-mcp.ts CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  import { EngramAccessInputError, type EngramAccessRecallResponse, type EngramAccessService } from "./access-service.js";
33
33
  import { EngramAccessForbiddenError, unknownToolError } from "./access-errors.js";
34
34
  import { log } from "./logger.js";
35
- import { McpFrameReader } from "./access-mcp-frames.js";
35
+ import { McpFrameReader, type McpFrameForm } from "./access-mcp-frames.js";
36
36
  import "./access-operations.js";
37
37
  import { validateBriefingFormat } from "./briefing.js";
38
38
  import { processChatMessage } from "./chat/chat-factory.js";
@@ -470,6 +470,8 @@ async function getMcpServerVersion(): Promise<string> {
470
470
 
471
471
  export class EngramMcpServer {
472
472
  private readonly frames = new McpFrameReader();
473
+ /** The form of the first frame on this connection. Replies use the same form. */
474
+ private replyForm: McpFrameForm | null = null;
473
475
  private flushTask: Promise<void> | null = null;
474
476
  private readonly tools: McpTool[];
475
477
  private readonly resources: McpResource[];
@@ -2493,7 +2495,7 @@ export class EngramMcpServer {
2493
2495
  private async flushBuffer(output: Writable): Promise<void> {
2494
2496
  for (let frame = this.frames.next(); frame; frame = this.frames.next()) {
2495
2497
  if (frame.kind === "too-large") {
2496
- this.writeMessage(output, { jsonrpc: "2.0", id: null, error: { code: -32600, message: "frame too large" } });
2498
+ this.writeRefusal(output, frame.form, -32600, "frame too large");
2497
2499
  continue;
2498
2500
  }
2499
2501
  let parsed: JsonRpcRequest;
@@ -2501,16 +2503,10 @@ export class EngramMcpServer {
2501
2503
  // SAFETY: handleRequest defaults missing fields; a non-object body throws and scheduleFlush answers -32700.
2502
2504
  parsed = JSON.parse(frame.text) as JsonRpcRequest;
2503
2505
  } catch {
2504
- this.writeMessage(output, {
2505
- jsonrpc: "2.0",
2506
- id: null,
2507
- error: {
2508
- code: -32700,
2509
- message: "Parse error",
2510
- },
2511
- });
2506
+ this.writeRefusal(output, frame.form, -32700, "Parse error");
2512
2507
  continue;
2513
2508
  }
2509
+ this.replyForm ??= frame.form;
2514
2510
  const response = await this.handleRequest(parsed);
2515
2511
  if (response) {
2516
2512
  this.writeMessage(output, response);
@@ -2519,9 +2515,19 @@ export class EngramMcpServer {
2519
2515
  }
2520
2516
 
2521
2517
  private writeMessage(output: Writable, payload: Record<string, unknown>): void {
2522
- const body = JSON.stringify(payload);
2523
- const message = `Content-Length: ${Buffer.byteLength(body, "utf-8")}\r\n\r\n${body}`;
2524
- output.write(message);
2518
+ output.write(this.frameBody(JSON.stringify(payload), this.replyForm));
2519
+ }
2520
+
2521
+ /** Answers a frame that held no valid JSON in its own form, without fixing the connection's reply form. */
2522
+ private writeRefusal(output: Writable, frameForm: McpFrameForm, code: number, message: string): void {
2523
+ const body = JSON.stringify({ jsonrpc: "2.0", id: null, error: { code, message } });
2524
+ output.write(this.frameBody(body, this.replyForm ?? frameForm));
2525
+ }
2526
+
2527
+ private frameBody(body: string, form: McpFrameForm | null): string {
2528
+ return form === "line"
2529
+ ? `${body}\n`
2530
+ : `Content-Length: ${Buffer.byteLength(body, "utf-8")}\r\n\r\n${body}`;
2525
2531
  }
2526
2532
 
2527
2533
  private toolAcceptsArgument(name: string, key: string): boolean {
@@ -19,6 +19,8 @@
19
19
  * - Precedence: explicit `namespace` wins; namespaces-disabled is a no-op.
20
20
  */
21
21
  import assert from "node:assert/strict";
22
+ import os from "node:os";
23
+ import path from "node:path";
22
24
  import test from "node:test";
23
25
 
24
26
  import { EngramAccessService } from "./access-service.js";
@@ -39,9 +41,14 @@ import { sealedWriteToLegacyArgs, type SealedMemoryEnvelope } from "./write-enve
39
41
  // the PRODUCTION mapping (`sealedWriteToLegacyArgs`), so mock behavior
40
42
  // cannot drift from the real envelope→options translation.
41
43
  function withSealedWrite<T extends { writeMemory: (...args: never[]) => unknown }>(stub: T): T {
44
+ // SAFETY: the decorated stub only gains writeSealedMemory and dir, both assigned below before any caller reads them.
42
45
  const decorated = stub as T & {
43
46
  writeSealedMemory?: (envelope: SealedMemoryEnvelope, extras?: Record<string, unknown>) => unknown;
47
+ dir?: string;
44
48
  };
49
+ // #3354: the fact-dedup window keys its lock on storage.dir and takes a
50
+ // real file lock, so the fake needs a writable per-process dir.
51
+ decorated.dir = path.join(os.tmpdir(), `remnic-coding-write-fake-${process.pid}`);
45
52
  decorated.writeSealedMemory = (envelope, extras = {}) => {
46
53
  const { category, content, options } = sealedWriteToLegacyArgs(envelope, extras);
47
54
  return (stub.writeMemory as (c: unknown, b: unknown, o: unknown) => unknown)(category, content, options);
@@ -1365,6 +1365,7 @@ export class EngramAccessService extends SupportPassportAccessServiceBase {
1365
1365
  }
1366
1366
  private readonly extractionLivenessWarn = new ExtractionLivenessWarnThrottle();
1367
1367
 
1368
+ private readonly secretRefResolver: ResolveSecretRefFn | null;
1368
1369
  constructor(
1369
1370
  private readonly orchestrator: Orchestrator,
1370
1371
  options: {
@@ -1375,6 +1376,7 @@ export class EngramAccessService extends SupportPassportAccessServiceBase {
1375
1376
  ) {
1376
1377
  super();
1377
1378
  this.idempotency = new AccessIdempotencyStore(orchestrator.config.memoryDir);
1379
+ this.secretRefResolver = options.resolveSecretRef ?? null;
1378
1380
  // Peer SecretRef tokens resolve at poll time through the host resolver, the
1379
1381
  // same indirection as agentAccessHttp.authToken (review round 1). Absent a
1380
1382
  // resolver, string/${ENV} tokens still work and a SecretRef degrades to a
@@ -3721,6 +3723,7 @@ export class EngramAccessService extends SupportPassportAccessServiceBase {
3721
3723
  this.writableNamespaceFor(undefined, undefined, request.authenticatedPrincipal ?? principal);
3722
3724
  return this.orchestrator.runLiveConnectors({
3723
3725
  force: request.force === true,
3726
+ resolveSecretRef: this.secretRefResolver,
3724
3727
  });
3725
3728
  }
3726
3729
 
@@ -6,6 +6,7 @@ import test from "node:test";
6
6
  import { parseConfig } from "../config.js";
7
7
  import { NamespaceStorageRouter } from "../namespaces/storage.js";
8
8
  import { StorageManager } from "../storage.js";
9
+ import { persistUnderFactDedupWindow } from "../orchestration/fact-dedup-window.js";
9
10
  import { withTempDir } from "../testing/tmp-dir.js";
10
11
  import {
11
12
  createJournalMemoryWriter,
@@ -127,3 +128,33 @@ test("tombstoned journal facts are skipped and not written as pending_review", a
127
128
  assert.equal(pending.length, 0, "tombstoned content must not land as a new pending_review file");
128
129
  });
129
130
  });
131
+
132
+ test("a fact an extraction run commits after the journal probe is not written again", async () => {
133
+ await withTempDir(async (dir) => {
134
+ const config = parseConfig({ memoryDir: dir });
135
+ const storage = new StorageManager(config.memoryDir);
136
+ await storage.ensureDirectories();
137
+ const real = createJournalMemoryWriter(storage);
138
+ const deps = extractDeps(storage, "fact");
139
+ deps.writer = {
140
+ writeSealedMemory: real.writeSealedMemory,
141
+ hasJournalMemoryContent: async (content) => {
142
+ const known = await real.hasJournalMemoryContent(content);
143
+ assert.equal(known, false, "the probe runs before the other writer commits");
144
+ await persistUnderFactDedupWindow(storage.dir, async () => false, () => storage.writeMemory("fact", FACT));
145
+ return known;
146
+ },
147
+ };
148
+ const result = await runJournalReviewExtraction({
149
+ date: "2026-08-20",
150
+ journalText: FACT,
151
+ source: "memoryDir",
152
+ journalConfig: { extractionMode: "review" },
153
+ deps,
154
+ });
155
+ assert.equal(result.pendingReview, 0);
156
+ assert.equal(result.skipped, 1);
157
+ const copies = (await storage.readAllMemories()).filter((memory) => memory.content.includes(FACT));
158
+ assert.equal(copies.length, 1, "the corpus holds one copy of the fact");
159
+ });
160
+ });
@@ -298,6 +298,7 @@ test("memoryDir source provenance differs only in the attribute value", async ()
298
298
  test("createJournalMemoryWriter dedups journal-tagged memories beyond the fact hash", async () => {
299
299
  const calls: string[] = [];
300
300
  const storage = {
301
+ dir: "/tmp/journal-writer-test-unused",
301
302
  writeSealedMemory: async () => ({}),
302
303
  hasFactContentHash: async (content: string) => {
303
304
  calls.push(`hash:${content}`);
@@ -11,6 +11,7 @@
11
11
  * Per-day change detection lives in journal-state.ts (timeline.json);
12
12
  * this module is the pure pass over one day's POST-STRIP text.
13
13
  */
14
+ import { persistUnderFactDedupWindow } from "../orchestration/fact-dedup-window.js";
14
15
  import { scoreImportance } from "../importance.js";
15
16
  import { getVerdictKind, type JudgeCandidate, type JudgeVerdict } from "../extraction-judge.js";
16
17
  import { log } from "../logger.js";
@@ -29,6 +30,7 @@ import type { ActivityTimelineJournalConfig } from "./types.js";
29
30
 
30
31
  /** The subset of StorageManager the journal pass writes through. */
31
32
  export interface JournalStorageIo {
33
+ readonly dir: string;
32
34
  writeSealedMemory(
33
35
  envelope: SealedMemoryEnvelope,
34
36
  extras: { status: MemoryStatus; contentHashSource: string; importance?: ImportanceScore },
@@ -38,7 +40,14 @@ export interface JournalStorageIo {
38
40
  }
39
41
 
40
42
  export interface JournalMemoryWriter {
41
- writeSealedMemory: JournalStorageIo["writeSealedMemory"];
43
+ /**
44
+ * `duplicate` is true when a concurrent writer committed the same fact
45
+ * first and nothing was written.
46
+ */
47
+ writeSealedMemory(
48
+ envelope: SealedMemoryEnvelope,
49
+ extras: { status: MemoryStatus; contentHashSource: string; importance?: ImportanceScore },
50
+ ): Promise<{ tombstoneBlocked?: boolean; duplicate?: boolean }>;
42
51
  /** Dedup beyond the fact-only hash index: scans journal-tagged memories. */
43
52
  hasJournalMemoryContent(content: string): Promise<boolean>;
44
53
  }
@@ -51,7 +60,19 @@ export interface JournalMemoryWriter {
51
60
  */
52
61
  export function createJournalMemoryWriter(storage: JournalStorageIo): JournalMemoryWriter {
53
62
  return {
54
- writeSealedMemory: storage.writeSealedMemory.bind(storage),
63
+ writeSealedMemory: async (envelope, extras) => {
64
+ // Facts register in the fact hash index, so extraction and this pass can
65
+ // commit the same new fact. Commit it under the same window extraction
66
+ // uses and recheck the index there. The scan in hasJournalMemoryContent
67
+ // stays outside: it is corpus-sized and the window is bounded.
68
+ if (envelope.category !== "fact") return storage.writeSealedMemory(envelope, extras);
69
+ const outcome = await persistUnderFactDedupWindow(
70
+ storage.dir,
71
+ () => storage.hasFactContentHash(extras.contentHashSource),
72
+ () => storage.writeSealedMemory(envelope, extras),
73
+ );
74
+ return outcome.suppressed ? { duplicate: true } : outcome.result;
75
+ },
55
76
  hasJournalMemoryContent: async (content: string) => {
56
77
  if (await storage.hasFactContentHash(content)) return true;
57
78
  const needle = stripAttributesSuffix(content);
@@ -210,7 +231,7 @@ export async function runJournalReviewExtraction(input: {
210
231
  contentHashSource: normalizedKey,
211
232
  importance: scoreImportance(fact.content, fact.category, fact.tags),
212
233
  });
213
- if (write.tombstoneBlocked) {
234
+ if (write.tombstoneBlocked || write.duplicate) {
214
235
  result.skipped += 1;
215
236
  continue;
216
237
  }
package/src/cli.ts CHANGED
@@ -6107,8 +6107,7 @@ export function registerCli(
6107
6107
  priorState: state,
6108
6108
  syncFn: (cursor) =>
6109
6109
  connector.syncIncremental({
6110
- cursor,
6111
- config: validatedCfg,
6110
+ cursor, config: validatedCfg, resolveSecretRef: hostSecretRefResolver(registerOptions),
6112
6111
  }),
6113
6112
  ingestFn: sharedIngestFn,
6114
6113
  writeCursorFn: makeWriteCursorFn(name),
@@ -1891,6 +1891,23 @@ test("parseConfig connectors.googleDrive accepts valid overrides", () => {
1891
1891
  ]);
1892
1892
  });
1893
1893
 
1894
+ test("parseConfig connectors.googleDrive keeps a SecretRef credential verbatim", () => {
1895
+ const secretRef = { source: "keychain", id: "projects/drive-secret" };
1896
+ const result = parseConfig({
1897
+ openaiApiKey: "sk-test",
1898
+ connectors: {
1899
+ googleDrive: {
1900
+ enabled: true,
1901
+ clientId: "synthetic-client",
1902
+ clientSecret: secretRef,
1903
+ refreshToken: "synthetic-token",
1904
+ },
1905
+ },
1906
+ });
1907
+ assert.deepEqual(result.connectors.googleDrive.clientSecret, secretRef);
1908
+ assert.equal(result.connectors.googleDrive.clientId, "synthetic-client");
1909
+ });
1910
+
1894
1911
  test("parseConfig rejects malformed connectors top-level", () => {
1895
1912
  assert.throws(
1896
1913
  () => parseConfig({ openaiApiKey: "sk-test", connectors: "nope" }),
package/src/config.ts CHANGED
@@ -19,6 +19,7 @@ import type {
19
19
  RecallSectionConfig,
20
20
  ReasoningEffort,
21
21
  SemanticChunkingConfigShape,
22
+ SecretRef,
22
23
  SessionObserverBandConfig,
23
24
  SlotBehaviorConfig,
24
25
  SlotMismatchMode,
@@ -3719,16 +3720,33 @@ export function parseConfig(
3719
3720
  ? (rawConnectors.googleDrive as Record<string, unknown>)
3720
3721
  : {};
3721
3722
  const driveEnabled = coerceBool(rawDrive.enabled) === true;
3723
+ const driveClientIdRaw = rawDrive.clientId;
3724
+ // SAFETY: a string value is a string; an object with a string source is a SecretRef, kept verbatim.
3722
3725
  const driveClientId =
3723
- typeof rawDrive.clientId === "string" ? rawDrive.clientId : "";
3726
+ Object.prototype.toString.call(driveClientIdRaw) === "[object String]"
3727
+ ? (driveClientIdRaw as string)
3728
+ : Object.prototype.toString.call(driveClientIdRaw) === "[object Object]" &&
3729
+ Object.prototype.toString.call((driveClientIdRaw as { source?: object }).source) === "[object String]"
3730
+ ? (driveClientIdRaw as SecretRef)
3731
+ : "";
3732
+ const driveClientSecretRaw = rawDrive[CLIENT_SECRET_FIELD];
3733
+ // SAFETY: a string value is a string; an object with a string source is a SecretRef, kept verbatim.
3724
3734
  const driveClientSecret =
3725
- typeof rawDrive[CLIENT_SECRET_FIELD] === "string"
3726
- ? rawDrive[CLIENT_SECRET_FIELD]
3727
- : "";
3735
+ Object.prototype.toString.call(driveClientSecretRaw) === "[object String]"
3736
+ ? (driveClientSecretRaw as string)
3737
+ : Object.prototype.toString.call(driveClientSecretRaw) === "[object Object]" &&
3738
+ Object.prototype.toString.call((driveClientSecretRaw as { source?: object }).source) === "[object String]"
3739
+ ? (driveClientSecretRaw as SecretRef)
3740
+ : "";
3741
+ const driveRefreshTokenRaw = rawDrive[REFRESH_TOKEN_FIELD];
3742
+ // SAFETY: a string value is a string; an object with a string source is a SecretRef, kept verbatim.
3728
3743
  const driveRefreshToken =
3729
- typeof rawDrive[REFRESH_TOKEN_FIELD] === "string"
3730
- ? rawDrive[REFRESH_TOKEN_FIELD]
3731
- : "";
3744
+ Object.prototype.toString.call(driveRefreshTokenRaw) === "[object String]"
3745
+ ? (driveRefreshTokenRaw as string)
3746
+ : Object.prototype.toString.call(driveRefreshTokenRaw) === "[object Object]" &&
3747
+ Object.prototype.toString.call((driveRefreshTokenRaw as { source?: object }).source) === "[object String]"
3748
+ ? (driveRefreshTokenRaw as SecretRef)
3749
+ : "";
3732
3750
  const drivePollCoerced = coerceNumber(rawDrive.pollIntervalMs);
3733
3751
  let drivePollIntervalMs = 300_000;
3734
3752
  if (drivePollCoerced !== undefined) {
@@ -1,3 +1,5 @@
1
+ import type { ResolveSecretRefFn } from "../../resolve-auth-token.js";
2
+
1
3
  /**
2
4
  * @remnic/core — Live Connectors Framework (issue #683 PR 1/N)
3
5
  *
@@ -139,6 +141,12 @@ export interface SyncIncrementalArgs {
139
141
  readonly config: ConnectorConfig;
140
142
  /** Optional abort signal. Connectors SHOULD honor it for cooperative cancellation. */
141
143
  readonly abortSignal?: AbortSignal;
144
+ /**
145
+ * Host resolver for credential fields that hold a SecretRef. A connector
146
+ * resolves those at poll time. Absent a resolver, a plain string still works
147
+ * and a SecretRef fails that connector only.
148
+ */
149
+ readonly resolveSecretRef?: ResolveSecretRefFn | null;
142
150
  }
143
151
 
144
152
  /**
@@ -14,6 +14,7 @@ import {
14
14
  type GoogleDriveClientFactory,
15
15
  type GoogleDriveSyncResult,
16
16
  } from "./index.js";
17
+ import { SECRET_RESOLVE_DEADLINE_MS } from "./google-drive.js";
17
18
  import type { ConnectorConfig, ConnectorCursor } from "./framework.js";
18
19
 
19
20
  /**
@@ -88,6 +89,79 @@ test("validateGoogleDriveConfig accepts a minimal valid config", () => {
88
89
  assert.deepEqual([...cfg.folderIds], []);
89
90
  });
90
91
 
92
+ test("a SecretRef credential resolves at poll time and never reaches the persisted config", async () => {
93
+ const client = makeMockClient({});
94
+ let seenSecret = "";
95
+ const factory: GoogleDriveClientFactory = async (config) => {
96
+ seenSecret = config.clientSecret;
97
+ return client;
98
+ };
99
+ const connector = createGoogleDriveConnector({ clientFactory: factory });
100
+ const ref = { source: "keychain", id: "projects/drive-secret" };
101
+ const config: ConnectorConfig = { ...SYNTHETIC_CREDS, clientSecret: ref };
102
+ await connector.syncIncremental({
103
+ cursor: null,
104
+ config,
105
+ resolveSecretRef: async () => "resolved-drive-secret",
106
+ });
107
+ assert.equal(seenSecret, "resolved-drive-secret");
108
+ const persisted = JSON.stringify(connector.persistConfig?.(config) ?? {});
109
+ assert.equal(persisted.includes("resolved-drive-secret"), false);
110
+ assert.equal(persisted.includes("projects/drive-secret"), false);
111
+ });
112
+
113
+ test("a SecretRef with no resolver fails the connector and hides the reference", async () => {
114
+ const connector = createGoogleDriveConnector({ clientFactory: makeFactory(makeMockClient({})) });
115
+ const config: ConnectorConfig = { ...SYNTHETIC_CREDS, clientSecret: { source: "keychain", id: "projects/drive-secret" } };
116
+ let message = "";
117
+ try {
118
+ await connector.syncIncremental({ cursor: null, config });
119
+ } catch (err) {
120
+ message = err instanceof Error ? err.message : "";
121
+ }
122
+ assert.match(message, /clientSecret is a secret reference but no resolver is configured/);
123
+ assert.equal(message.includes("projects/drive-secret"), false);
124
+ assert.equal(message.includes("keychain"), false);
125
+ });
126
+
127
+ const DRIVE_REF_CONFIG: ConnectorConfig = {
128
+ ...SYNTHETIC_CREDS,
129
+ clientSecret: { source: "keychain", id: "projects/drive-secret" },
130
+ };
131
+
132
+ async function driveSyncError(args: { resolveSecretRef: () => Promise<string | undefined>; abortSignal?: AbortSignal }): Promise<Error> {
133
+ const connector = createGoogleDriveConnector({ clientFactory: makeFactory(makeMockClient({})) });
134
+ try {
135
+ await connector.syncIncremental({ cursor: null, config: DRIVE_REF_CONFIG, ...args });
136
+ } catch (err) {
137
+ return err instanceof Error ? err : new Error("non-error thrown");
138
+ }
139
+ return new Error("sync did not fail");
140
+ }
141
+
142
+ test("a SecretRef resolver that never settles is cut off by the abort signal", async () => {
143
+ const controller = new AbortController();
144
+ const failure = driveSyncError({ resolveSecretRef: () => new Promise(() => undefined), abortSignal: controller.signal });
145
+ await new Promise((resolve) => setImmediate(resolve));
146
+ controller.abort();
147
+ assert.equal((await failure).name, "AbortError");
148
+ });
149
+
150
+ test("a SecretRef resolver that never settles fails at the deadline", async (t) => {
151
+ t.mock.timers.enable({ apis: ["setTimeout"] });
152
+ const failure = driveSyncError({ resolveSecretRef: () => new Promise(() => undefined) });
153
+ await new Promise((resolve) => setImmediate(resolve));
154
+ t.mock.timers.tick(SECRET_RESOLVE_DEADLINE_MS);
155
+ const err = await failure;
156
+ assert.match(err.message, /clientSecret secret reference was not resolved within 30000 ms/);
157
+ assert.equal(err.message.includes("projects/drive-secret"), false);
158
+ });
159
+
160
+ test("a SecretRef that resolves to no value says the resolver may be unavailable", async () => {
161
+ const err = await driveSyncError({ resolveSecretRef: async () => undefined });
162
+ assert.match(err.message, /resolved to no value \(the host secret resolver may be unavailable\)/);
163
+ });
164
+
91
165
  test("validateGoogleDriveConfig rejects non-object input", () => {
92
166
  assert.throws(() => validateGoogleDriveConfig(null), /must be an object/);
93
167
  assert.throws(() => validateGoogleDriveConfig([]), /must be an object/);
@@ -64,7 +64,9 @@ import type {
64
64
  SyncIncrementalResult,
65
65
  } from "./framework.js";
66
66
  import { isTransientHttpError } from "./transient-errors.js";
67
- import { throwIfAborted } from "../../abort-error.js";
67
+ import { raceAbort, throwIfAborted } from "../../abort-error.js";
68
+ import type { ResolveSecretRefFn } from "../../resolve-auth-token.js";
69
+ import type { SecretRef } from "../../types.js";
68
70
 
69
71
  /**
70
72
  * Stable connector id. Lives in the registry under this exact string.
@@ -122,15 +124,40 @@ const FOLDER_ID_PATTERN = /^[A-Za-z0-9_-]{8,256}$/;
122
124
  * Validated, frozen view of `connectors.googleDrive.*`.
123
125
  */
124
126
  export interface GoogleDriveConnectorConfig {
127
+ readonly clientId: DriveCredential;
128
+ readonly clientSecret: DriveCredential;
129
+ readonly refreshToken: DriveCredential;
130
+ /** Poll interval surfaced to the scheduler. */
131
+ readonly pollIntervalMs: number;
132
+ /** Folder ids to scope import to. Empty = "all accessible". */
133
+ readonly folderIds: readonly string[];
134
+ }
135
+
136
+ /** A credential field as configured: a plain string, or a SecretRef resolved at poll time. */
137
+ type DriveCredential = string | SecretRef;
138
+
139
+ interface DriveCredentialFields {
140
+ clientId: DriveCredential;
141
+ clientSecret: DriveCredential;
142
+ refreshToken: DriveCredential;
143
+ }
144
+
145
+ /** Resolved credentials passed to the Drive client. Always plain strings. */
146
+ interface GoogleDriveResolvedConfig {
125
147
  readonly clientId: string;
126
148
  readonly clientSecret: string;
127
149
  readonly refreshToken: string;
128
- /** Poll interval surfaced to the scheduler. */
129
150
  readonly pollIntervalMs: number;
130
- /** Folder ids to scope import to. Empty = "all accessible". */
131
151
  readonly folderIds: readonly string[];
132
152
  }
133
153
 
154
+ /** Disk form of a Drive config. A SecretRef clientId is omitted, never written. */
155
+ interface PersistedGoogleDriveConfig {
156
+ pollIntervalMs: number;
157
+ folderIds: readonly string[];
158
+ clientId?: string;
159
+ }
160
+
134
161
  /**
135
162
  * Optional injection point for tests. The real connector dynamically imports
136
163
  * `googleapis`; tests pass a stub here to avoid the optional-peer-dep
@@ -139,7 +166,7 @@ export interface GoogleDriveConnectorConfig {
139
166
  * The shape only covers the tiny slice of the SDK we actually use.
140
167
  */
141
168
  export interface GoogleDriveClientFactory {
142
- (config: GoogleDriveConnectorConfig): Promise<GoogleDriveClient>;
169
+ (config: GoogleDriveResolvedConfig): Promise<GoogleDriveClient>;
143
170
  }
144
171
 
145
172
  /**
@@ -243,9 +270,30 @@ export function validateGoogleDriveConfig(raw: unknown): GoogleDriveConnectorCon
243
270
  );
244
271
  }
245
272
  const r = raw as Record<string, unknown>;
246
- const clientId = requireNonEmptyString(r.clientId, "clientId");
247
- const clientSecret = requireNonEmptyString(r[CLIENT_SECRET_FIELD], CLIENT_SECRET_FIELD);
248
- const refreshToken = requireNonEmptyString(r[REFRESH_TOKEN_FIELD], REFRESH_TOKEN_FIELD);
273
+ const credentialKeys = ["clientId", CLIENT_SECRET_FIELD, REFRESH_TOKEN_FIELD] as const;
274
+ const credentials: DriveCredentialFields = {
275
+ clientId: "",
276
+ clientSecret: "",
277
+ refreshToken: "",
278
+ };
279
+ for (const key of credentialKeys) {
280
+ const value = r[key];
281
+ if (Object.prototype.toString.call(value) === "[object Object]") {
282
+ // SAFETY: the toString check proves value is a non-null object.
283
+ const source = (value as { source?: object }).source;
284
+ if (Object.prototype.toString.call(source) === "[object String]") {
285
+ // SAFETY: a string `source` on this object is the SecretRef discriminator.
286
+ credentials[key] = value as SecretRef;
287
+ } else {
288
+ credentials[key] = requireNonEmptyString(value, key);
289
+ }
290
+ } else {
291
+ credentials[key] = requireNonEmptyString(value, key);
292
+ }
293
+ }
294
+ const clientId = credentials.clientId;
295
+ const clientSecret = credentials.clientSecret;
296
+ const refreshToken = credentials.refreshToken;
249
297
 
250
298
  let pollIntervalMs: number;
251
299
  if (r.pollIntervalMs === undefined) {
@@ -321,6 +369,76 @@ function requireNonEmptyString(value: unknown, key: string): string {
321
369
  return trimmed;
322
370
  }
323
371
 
372
+
373
+ /** Longest wait for the host secret resolver, so a stalled secret store cannot hold the sync loop. */
374
+ export const SECRET_RESOLVE_DEADLINE_MS = 30_000;
375
+
376
+ async function resolveWithinDeadline(
377
+ ref: SecretRef,
378
+ key: string,
379
+ resolver: ResolveSecretRefFn,
380
+ abortSignal: AbortSignal | undefined,
381
+ ): Promise<string | undefined> {
382
+ throwIfAborted(abortSignal, "googleDrive: sync aborted");
383
+ const deadline = new AbortController();
384
+ const timer = setTimeout(() => deadline.abort(), SECRET_RESOLVE_DEADLINE_MS);
385
+ const signal = abortSignal ? AbortSignal.any([abortSignal, deadline.signal]) : deadline.signal;
386
+ try {
387
+ return await raceAbort(Promise.resolve(resolver(ref)), signal, "googleDrive: secret resolve aborted");
388
+ } catch {
389
+ throwIfAborted(abortSignal, "googleDrive: sync aborted");
390
+ if (deadline.signal.aborted) {
391
+ throw new Error(`googleDrive: ${key} secret reference was not resolved within ${SECRET_RESOLVE_DEADLINE_MS} ms`);
392
+ }
393
+ throw new Error(`googleDrive: ${key} secret reference could not be resolved`);
394
+ } finally {
395
+ clearTimeout(timer);
396
+ }
397
+ }
398
+
399
+ async function resolveCredential(
400
+ value: DriveCredential,
401
+ key: string,
402
+ resolver: ResolveSecretRefFn | null | undefined,
403
+ abortSignal: AbortSignal | undefined,
404
+ ): Promise<string> {
405
+ if (Object.prototype.toString.call(value) !== "[object Object]") {
406
+ // SAFETY: a DriveCredential that is not an object is the plain string form.
407
+ return value as string;
408
+ }
409
+ // SAFETY: the toString check proves value is a non-null object, so it is a SecretRef.
410
+ const ref = value as SecretRef;
411
+ if (resolver == null) {
412
+ throw new Error(`googleDrive: ${key} is a secret reference but no resolver is configured`);
413
+ }
414
+ const resolved = await resolveWithinDeadline(ref, key, resolver, abortSignal);
415
+ if (resolved === undefined) {
416
+ throw new Error(`googleDrive: ${key} secret reference resolved to no value (the host secret resolver may be unavailable)`);
417
+ }
418
+ if (Object.prototype.toString.call(resolved) !== "[object String]") {
419
+ throw new Error(`googleDrive: ${key} secret reference resolved to a non-string value`);
420
+ }
421
+ const text = resolved.trim();
422
+ if (text.length === 0) {
423
+ throw new Error(`googleDrive: ${key} secret reference resolved to an empty value`);
424
+ }
425
+ return text;
426
+ }
427
+
428
+ async function resolveGoogleDriveConfig(
429
+ stored: GoogleDriveConnectorConfig,
430
+ resolver: ResolveSecretRefFn | null | undefined,
431
+ abortSignal: AbortSignal | undefined,
432
+ ): Promise<GoogleDriveResolvedConfig> {
433
+ return {
434
+ clientId: await resolveCredential(stored.clientId, "clientId", resolver, abortSignal),
435
+ [CLIENT_SECRET_FIELD]: await resolveCredential(stored[CLIENT_SECRET_FIELD], CLIENT_SECRET_FIELD, resolver, abortSignal),
436
+ [REFRESH_TOKEN_FIELD]: await resolveCredential(stored[REFRESH_TOKEN_FIELD], REFRESH_TOKEN_FIELD, resolver, abortSignal),
437
+ pollIntervalMs: stored.pollIntervalMs,
438
+ folderIds: stored.folderIds,
439
+ };
440
+ }
441
+
324
442
  /**
325
443
  * Decide whether a Drive `change` corresponds to an importable text file
326
444
  * scoped to (optionally) the configured folder set.
@@ -405,16 +523,25 @@ export function createGoogleDriveConnector(
405
523
 
406
524
  persistConfig(validated: ConnectorConfig): ConnectorConfig {
407
525
  const config = validateGoogleDriveConfig(validated);
408
- return Object.freeze({
409
- clientId: config.clientId,
526
+ const persisted: PersistedGoogleDriveConfig = {
410
527
  pollIntervalMs: config.pollIntervalMs,
411
528
  folderIds: config.folderIds,
412
- });
529
+ };
530
+ if (Object.prototype.toString.call(config.clientId) === "[object String]") {
531
+ // SAFETY: the toString check proves clientId is a string, not a SecretRef.
532
+ persisted.clientId = config.clientId as string;
533
+ }
534
+ return Object.freeze(persisted);
535
+
413
536
  },
414
537
 
415
538
  async syncIncremental(args: SyncIncrementalArgs): Promise<SyncIncrementalResult> {
416
539
  // Re-validate on every pass: a JS caller could mutate it between passes.
417
- const config = validateGoogleDriveConfig(args.config);
540
+ const config = await resolveGoogleDriveConfig(
541
+ validateGoogleDriveConfig(args.config),
542
+ args.resolveSecretRef,
543
+ args.abortSignal,
544
+ );
418
545
  throwIfAborted(args.abortSignal, "googleDrive: sync aborted");
419
546
 
420
547
  const client = await clientFactory(config);