@remnic/core 10.0.90 → 10.0.92

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 (443) hide show
  1. package/dist/access-admin-ops-surface.js +92 -92
  2. package/dist/access-authorization-probe.js +93 -93
  3. package/dist/access-boundary.js +92 -92
  4. package/dist/access-cli.js +200 -199
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +92 -92
  7. package/dist/access-http-admission.d.ts +14 -0
  8. package/dist/access-http-admission.d.ts.map +1 -0
  9. package/dist/access-http-admission.js +9 -0
  10. package/dist/access-http-briefing.js +93 -93
  11. package/dist/access-http-query.js +94 -94
  12. package/dist/access-http-types.d.ts +145 -0
  13. package/dist/access-http-types.d.ts.map +1 -0
  14. package/dist/access-http-types.js +1 -0
  15. package/dist/access-http.d.ts +3 -142
  16. package/dist/access-http.d.ts.map +1 -1
  17. package/dist/access-http.js +121 -120
  18. package/dist/access-identity-continuity-surface.js +92 -92
  19. package/dist/access-lcm-surface.js +92 -92
  20. package/dist/access-mcp.d.ts.map +1 -1
  21. package/dist/access-mcp.js +108 -108
  22. package/dist/access-namespace-preflight.js +92 -92
  23. package/dist/access-observe-write-category.js +12 -12
  24. package/dist/access-observe-write-surface.js +92 -92
  25. package/dist/access-offline-manifest.js +17 -17
  26. package/dist/access-operations-batch.js +96 -96
  27. package/dist/access-operations.js +103 -103
  28. package/dist/access-recall-concurrency.js +92 -92
  29. package/dist/access-recall-diagnostics.js +7 -7
  30. package/dist/access-recall-response.js +92 -92
  31. package/dist/access-recall-surface.js +92 -92
  32. package/dist/access-schema.js +8 -8
  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 +92 -92
  36. package/dist/access-surface-catalog.js +94 -94
  37. package/dist/active-memory-bridge.js +6 -6
  38. package/dist/active-recall.js +43 -43
  39. package/dist/adapters/index.js +4 -4
  40. package/dist/adapters/registry.js +2 -2
  41. package/dist/auto-sync-3AZW7MXI.js +3 -3
  42. package/dist/briefing.js +31 -31
  43. package/dist/buffer-admission.d.ts +9 -0
  44. package/dist/buffer-admission.d.ts.map +1 -1
  45. package/dist/buffer-admission.js +3 -1
  46. package/dist/buffer.js +14 -14
  47. package/dist/calibration.js +5 -5
  48. package/dist/causal-behavior.js +2 -2
  49. package/dist/causal-chain.js +2 -2
  50. package/dist/causal-consolidation.js +36 -36
  51. package/dist/causal-retrieval.js +2 -2
  52. package/dist/causal-trajectory-graph.js +2 -2
  53. package/dist/causal-trajectory.js +1 -1
  54. package/dist/{chunk-4XYAWK5H.js → chunk-25BUV3TU.js} +2 -2
  55. package/dist/{chunk-QKES7GU6.js → chunk-2FGLEF7C.js} +2 -2
  56. package/dist/{chunk-3NF4QUNZ.js → chunk-2JSDPUKD.js} +5 -5
  57. package/dist/{chunk-AJHNXIQU.js → chunk-2LCRG2YV.js} +2 -2
  58. package/dist/{chunk-YRZPBN46.js → chunk-3RLK65ZK.js} +2 -2
  59. package/dist/{chunk-V4XE263I.js → chunk-43AA5FVD.js} +1 -1
  60. package/dist/chunk-43AA5FVD.js.map +1 -0
  61. package/dist/{chunk-GCBIXUPT.js → chunk-47F6WWLQ.js} +2 -2
  62. package/dist/{chunk-TSS3D7ZY.js → chunk-4CJTQHEG.js} +3 -3
  63. package/dist/{chunk-QD5R6U5J.js → chunk-4J363OAA.js} +2 -2
  64. package/dist/{chunk-C2HORX76.js → chunk-4WR2OD7K.js} +2 -2
  65. package/dist/{chunk-4X7HQKAG.js → chunk-4XINO77L.js} +2 -2
  66. package/dist/{chunk-OXQ7F45O.js → chunk-5HFXGXE5.js} +6 -6
  67. package/dist/{chunk-ELHJK7LM.js → chunk-5IYOKDM4.js} +12 -12
  68. package/dist/{chunk-LEEAO2TX.js → chunk-5M5M22GW.js} +3 -3
  69. package/dist/{chunk-52II7PB6.js → chunk-645WOU5I.js} +4 -4
  70. package/dist/{chunk-QEZ3DW5O.js → chunk-65AUYLU3.js} +5 -5
  71. package/dist/{chunk-B56LORLY.js → chunk-6F42QONS.js} +2 -2
  72. package/dist/{chunk-ADYP7HXE.js → chunk-6F5MJFXY.js} +3 -3
  73. package/dist/{chunk-GOZXQNIM.js → chunk-6P7U3HWG.js} +3 -3
  74. package/dist/{chunk-SBRUTD5O.js → chunk-6SRYCJ5I.js} +10 -10
  75. package/dist/{chunk-JM2YZKCQ.js → chunk-7GLJMDEQ.js} +6 -6
  76. package/dist/{chunk-BLSHFIR6.js → chunk-7HTUYFR3.js} +5 -5
  77. package/dist/{chunk-SYKJBS7S.js → chunk-7W3RXOIG.js} +24 -24
  78. package/dist/{chunk-J6GZ2254.js → chunk-AL3TCNSJ.js} +4 -4
  79. package/dist/{chunk-BIVKPQ6Q.js → chunk-AP6NCP3V.js} +8 -8
  80. package/dist/{chunk-32YX7EJ3.js → chunk-ATPWZ4F7.js} +13 -13
  81. package/dist/{chunk-4GMI5HFH.js → chunk-B4QYQFGY.js} +26 -23
  82. package/dist/chunk-B4QYQFGY.js.map +1 -0
  83. package/dist/{chunk-HXFIILGF.js → chunk-BFPF5N3H.js} +4 -4
  84. package/dist/{chunk-LHGPU6DQ.js → chunk-BJRTMNCB.js} +6 -6
  85. package/dist/{chunk-F74G4KIH.js → chunk-BLJT6JP2.js} +2 -2
  86. package/dist/{chunk-3TKX2RYK.js → chunk-BQZEGOZ4.js} +2 -2
  87. package/dist/{chunk-HLN2HOXX.js → chunk-BRLXOZLM.js} +6 -6
  88. package/dist/{chunk-5UBXIV3N.js → chunk-BSACVS6R.js} +14 -14
  89. package/dist/{chunk-4Q26RNSR.js → chunk-CD4BCCNP.js} +2 -2
  90. package/dist/{chunk-5SOSQTVE.js → chunk-CIEEYVUB.js} +2 -2
  91. package/dist/{chunk-SO4DZIVF.js → chunk-DKAA2IHQ.js} +38 -36
  92. package/dist/chunk-DKAA2IHQ.js.map +1 -0
  93. package/dist/{chunk-G54BOKZ6.js → chunk-E3A7ELSG.js} +9 -9
  94. package/dist/{chunk-OL553AWT.js → chunk-E4IRW2FG.js} +12 -12
  95. package/dist/{chunk-TFEHSPGQ.js → chunk-E5PPE4VA.js} +4 -4
  96. package/dist/{chunk-3UZ2VMKD.js → chunk-E6M225BJ.js} +6 -6
  97. package/dist/{chunk-TIGYOZBU.js → chunk-E6Z4FBX6.js} +4 -4
  98. package/dist/{chunk-2XOFVTTT.js → chunk-E7YZXWIS.js} +2 -2
  99. package/dist/{chunk-QKERFEJS.js → chunk-EFYN5I7Z.js} +2 -2
  100. package/dist/{chunk-M3BEJJNV.js → chunk-ENQCH3SW.js} +8 -1
  101. package/dist/chunk-ENQCH3SW.js.map +1 -0
  102. package/dist/{chunk-D5EV6Y2L.js → chunk-ETZLQOUR.js} +2 -2
  103. package/dist/{chunk-IBDWYRDR.js → chunk-EXHXJETE.js} +2 -2
  104. package/dist/{chunk-APYY5CX3.js → chunk-F55YQP7N.js} +5 -4
  105. package/dist/chunk-F55YQP7N.js.map +1 -0
  106. package/dist/{chunk-S45INSV3.js → chunk-FF6N7BXC.js} +4 -4
  107. package/dist/{chunk-N7EKND7S.js → chunk-FLRP63GU.js} +175 -173
  108. package/dist/chunk-FLRP63GU.js.map +1 -0
  109. package/dist/{chunk-77PDJ4OE.js → chunk-FNXQZGUS.js} +2 -2
  110. package/dist/{chunk-H3WAT6BR.js → chunk-FQKBJJYB.js} +2 -2
  111. package/dist/{chunk-CC4V7HNP.js → chunk-FSO3G4JJ.js} +53 -53
  112. package/dist/{chunk-ZOJUWVAU.js → chunk-GKL6QYZO.js} +1 -1
  113. package/dist/{chunk-2U4LMSEX.js → chunk-GVGAO3WV.js} +3 -3
  114. package/dist/{chunk-DT7SNJH6.js → chunk-HF3BKUVV.js} +4 -4
  115. package/dist/{chunk-TRMZHGIX.js → chunk-HH5LY6RR.js} +15 -15
  116. package/dist/{chunk-Q5XJYCFE.js → chunk-HVX4LPNY.js} +2 -2
  117. package/dist/chunk-I3V3JJS7.js +24 -0
  118. package/dist/chunk-I3V3JJS7.js.map +1 -0
  119. package/dist/{chunk-5MF6LMUY.js → chunk-ID6JGIMD.js} +2 -2
  120. package/dist/{chunk-YLTU2DC4.js → chunk-IHCXMYSE.js} +2 -2
  121. package/dist/{chunk-2G4TV2ML.js → chunk-IPT2OY4B.js} +4 -4
  122. package/dist/{chunk-ZANNIXXE.js → chunk-IQMDQJCD.js} +3 -3
  123. package/dist/{chunk-S7GJNA3K.js → chunk-IQWUACVG.js} +2 -2
  124. package/dist/{chunk-QNSZYTSG.js → chunk-ITHZFHST.js} +2 -2
  125. package/dist/{chunk-QUMKDRCF.js → chunk-JDY34KBL.js} +4 -4
  126. package/dist/{chunk-2N4OUSIA.js → chunk-JV4S6VDH.js} +2 -2
  127. package/dist/{chunk-7LCGFUMB.js → chunk-KQAFEZQX.js} +4 -4
  128. package/dist/{chunk-ARRTLPRC.js → chunk-LUJCQQ3L.js} +3 -3
  129. package/dist/{chunk-DC55RIEZ.js → chunk-M4IEZGCK.js} +4 -4
  130. package/dist/{chunk-TNW6BOMM.js → chunk-MB6ZJDN6.js} +2 -2
  131. package/dist/{chunk-GGRRF4PZ.js → chunk-MDPT6QFK.js} +1 -1
  132. package/dist/{chunk-GGRRF4PZ.js.map → chunk-MDPT6QFK.js.map} +1 -1
  133. package/dist/{chunk-UWRHHRDK.js → chunk-MKRYQROF.js} +5 -5
  134. package/dist/{chunk-P452SEKO.js → chunk-MXHMNKWG.js} +2 -2
  135. package/dist/{chunk-IPD5GC7C.js → chunk-N4KAE2NB.js} +2 -2
  136. package/dist/{chunk-BCCG7VWM.js → chunk-NBH6K6G5.js} +63 -9
  137. package/dist/chunk-NBH6K6G5.js.map +1 -0
  138. package/dist/{chunk-25Q52SOT.js → chunk-NTRUKAAE.js} +3 -3
  139. package/dist/{chunk-3DAWHP5K.js → chunk-NZSRRVJQ.js} +5 -5
  140. package/dist/{chunk-ROGO7FRA.js → chunk-O55EKIEC.js} +2 -2
  141. package/dist/{chunk-J5WI4QRP.js → chunk-OIGJZHDU.js} +4 -4
  142. package/dist/{chunk-K3FH3LAQ.js → chunk-OJXK5C6Z.js} +4 -4
  143. package/dist/{chunk-EA2LA5RI.js → chunk-P4WNBSTW.js} +2 -2
  144. package/dist/{chunk-EURPSF4Y.js → chunk-PH5ZQM35.js} +2 -2
  145. package/dist/{chunk-3ICAWZUU.js → chunk-PLCYKAWH.js} +4 -4
  146. package/dist/{chunk-3Y5X6YFH.js → chunk-PMJFRDJI.js} +2 -2
  147. package/dist/{chunk-PDOOUXCS.js → chunk-PPCLZ4JU.js} +2 -2
  148. package/dist/{chunk-VKILERSL.js → chunk-PWXPRVLK.js} +2 -2
  149. package/dist/{chunk-G6HWNPZA.js → chunk-QJPJXCS4.js} +2 -2
  150. package/dist/{chunk-YMJRLT7S.js → chunk-R2DOFBG4.js} +3 -3
  151. package/dist/{chunk-PKZ274PI.js → chunk-R2LBF4HH.js} +3 -3
  152. package/dist/{chunk-NQP4ZYGG.js → chunk-RGUUBAT5.js} +3 -3
  153. package/dist/{chunk-EWJZZDRG.js → chunk-RHQLIHMB.js} +2 -2
  154. package/dist/{chunk-CYCEROPR.js → chunk-RIMK327Y.js} +7 -7
  155. package/dist/{chunk-P4JED3ZL.js → chunk-RMXS4I66.js} +2 -2
  156. package/dist/{chunk-TX6EHB2Q.js → chunk-RRJBDXAQ.js} +2 -2
  157. package/dist/{chunk-MTTYIXSU.js → chunk-RRTVGD3J.js} +9 -9
  158. package/dist/{chunk-OZNBZJ3S.js → chunk-SLDRXK37.js} +8 -8
  159. package/dist/{chunk-NIVKG3DS.js → chunk-SUSW74WU.js} +6 -6
  160. package/dist/{chunk-G67EUQAB.js → chunk-TG3R2SID.js} +4 -4
  161. package/dist/{chunk-3PJEDHLD.js → chunk-U6UNF5ZS.js} +3 -3
  162. package/dist/{chunk-YAVIBVCU.js → chunk-UA2NDTIU.js} +4 -4
  163. package/dist/{chunk-KVSOEVAF.js → chunk-UE6RRXMF.js} +2 -2
  164. package/dist/{chunk-TAUHAP5J.js → chunk-UXPXOVJI.js} +9 -9
  165. package/dist/{chunk-KZSGBWOJ.js → chunk-V2KGDESV.js} +2 -2
  166. package/dist/{chunk-UH3FPX3L.js → chunk-W7K5BP3M.js} +2 -2
  167. package/dist/{chunk-TQ4SCPGQ.js → chunk-WRT7GJ2D.js} +3 -3
  168. package/dist/{chunk-IHJZSRDM.js → chunk-X562NQFA.js} +90 -89
  169. package/dist/chunk-X562NQFA.js.map +1 -0
  170. package/dist/{chunk-M5O2ANZD.js → chunk-XDSCJOH7.js} +24 -22
  171. package/dist/chunk-XDSCJOH7.js.map +1 -0
  172. package/dist/{chunk-CN7Q3T2J.js → chunk-XNI3URDR.js} +2 -2
  173. package/dist/{chunk-7O2MC42R.js → chunk-XNRLRNQF.js} +2 -2
  174. package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
  175. package/dist/{chunk-ZBFLV657.js → chunk-XYKBWTWI.js} +2 -2
  176. package/dist/{chunk-PWPIERBM.js → chunk-YCNIGRRF.js} +4 -4
  177. package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
  178. package/dist/{chunk-DZ7DPDVW.js → chunk-YODUDA6R.js} +15 -15
  179. package/dist/{chunk-2K3AUDH7.js → chunk-YYOMGOHB.js} +4 -4
  180. package/dist/{chunk-ROKDHO74.js → chunk-Z7JIZ42P.js} +4 -4
  181. package/dist/{chunk-ATWFYXEN.js → chunk-ZTTCDQ4I.js} +146 -143
  182. package/dist/chunk-ZTTCDQ4I.js.map +1 -0
  183. package/dist/cli.d.ts.map +1 -1
  184. package/dist/cli.js +154 -153
  185. package/dist/coding/export-okf-codegraph.js +30 -30
  186. package/dist/coding/pre-action-gate.js +2 -2
  187. package/dist/compounding/engine.js +32 -32
  188. package/dist/config.d.ts.map +1 -1
  189. package/dist/config.js +43 -43
  190. package/dist/connectors/codex-materialize-runner.js +33 -33
  191. package/dist/connectors/codex-materialize.js +2 -2
  192. package/dist/connectors/index.js +36 -36
  193. package/dist/connectors/live/framework.d.ts +7 -0
  194. package/dist/connectors/live/framework.d.ts.map +1 -1
  195. package/dist/connectors/live/google-drive.d.ts +15 -3
  196. package/dist/connectors/live/google-drive.d.ts.map +1 -1
  197. package/dist/consolidation-provenance-check.js +2 -2
  198. package/dist/contradiction/index.js +6 -6
  199. package/dist/{contradiction-scan-P7KXB5R4.js → contradiction-scan-P2MHYFVG.js} +5 -5
  200. package/dist/convergence-refresh.js +33 -33
  201. package/dist/corpus-watermark.js +33 -33
  202. package/dist/dashboard-runtime.js +2 -2
  203. package/dist/deep-recall-http-glue.js +9 -9
  204. package/dist/deep-recall.js +2 -2
  205. package/dist/embedding-fallback.js +3 -3
  206. package/dist/entity-retrieval.js +32 -32
  207. package/dist/event-order-recall.js +3 -3
  208. package/dist/explicit-capture.js +10 -10
  209. package/dist/external-wiki-access.js +93 -93
  210. package/dist/extraction-evidence-class.js +2 -2
  211. package/dist/extraction-normalization.js +1 -1
  212. package/dist/extraction-source-grounding.js +3 -3
  213. package/dist/extraction.js +47 -47
  214. package/dist/{first-start-migration-A6PHIBUQ.js → first-start-migration-QFI7B5SH.js} +7 -7
  215. package/dist/{graph-edge-decay-EKWXRBWF.js → graph-edge-decay-OEX5OGMI.js} +6 -6
  216. package/dist/graph-jsonl.js +3 -3
  217. package/dist/graph-snapshot.js +3 -3
  218. package/dist/graph.js +2 -2
  219. package/dist/harmonic-retrieval.js +30 -30
  220. package/dist/index.js +309 -308
  221. package/dist/lcm/engine.js +3 -3
  222. package/dist/lcm/index.js +8 -8
  223. package/dist/live-connectors-runner.d.ts +2 -0
  224. package/dist/live-connectors-runner.d.ts.map +1 -1
  225. package/dist/live-connectors-runner.js +3 -3
  226. package/dist/local-llm-config.js +1 -1
  227. package/dist/local-llm.js +2 -2
  228. package/dist/location/index.js +33 -33
  229. package/dist/maintenance/memory-governance.js +31 -31
  230. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +30 -30
  231. package/dist/maintenance/rebuild-memory-projection-runner.js +33 -33
  232. package/dist/maintenance/rebuild-memory-projection.js +32 -32
  233. package/dist/memory-browse-operations.js +93 -93
  234. package/dist/memory-browse.js +10 -10
  235. package/dist/memory-subject.js +1 -1
  236. package/dist/namespaces/migrate.js +32 -32
  237. package/dist/namespaces/search.js +7 -7
  238. package/dist/namespaces/storage.js +31 -31
  239. package/dist/offline-sync-changeset-generations.js +4 -4
  240. package/dist/offline-sync-embedding-generation.js +3 -3
  241. package/dist/offline-sync-impression-drain.js +3 -3
  242. package/dist/offline-sync-snapshot-stream.js +6 -6
  243. package/dist/offline-sync.js +7 -7
  244. package/dist/operator-doctor-corpus.js +34 -34
  245. package/dist/operator-toolkit.js +58 -58
  246. package/dist/orchestration/backlog-scheduler.js +201 -200
  247. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  248. package/dist/orchestration/buffer-drain-runner.js +201 -200
  249. package/dist/orchestration/maintenance.js +48 -48
  250. package/dist/orchestration/turn-ingestion.d.ts +2 -2
  251. package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
  252. package/dist/orchestrator.d.ts +2 -0
  253. package/dist/orchestrator.d.ts.map +1 -1
  254. package/dist/orchestrator.js +201 -200
  255. package/dist/provenance.js +2 -2
  256. package/dist/qmd.js +6 -6
  257. package/dist/recall-explain-renderer.js +3 -3
  258. package/dist/recall-navigate-access.js +2 -2
  259. package/dist/recall-navigate-disclosure.js +2 -2
  260. package/dist/recall-navigate.js +2 -2
  261. package/dist/recall-navigation-operations.js +93 -93
  262. package/dist/recall-navigation.js +92 -92
  263. package/dist/recall-state-view-admission.js +7 -7
  264. package/dist/recall-why-service.js +14 -14
  265. package/dist/recall-why.js +5 -5
  266. package/dist/recall-xray-cli.js +4 -4
  267. package/dist/recall-xray-renderer.js +3 -3
  268. package/dist/recall-xray.js +2 -2
  269. package/dist/reconcile/cursor.js +2 -2
  270. package/dist/reconcile/manifest.js +6 -6
  271. package/dist/reconcile/plan.js +2 -2
  272. package/dist/{resolution-23ZBDM6A.js → resolution-N7H3F2E4.js} +7 -7
  273. package/dist/resolution-N7H3F2E4.js.map +1 -0
  274. package/dist/resume-bundles.js +45 -45
  275. package/dist/salvage-envelope.js +1 -1
  276. package/dist/search/factory.js +6 -6
  277. package/dist/search/index.js +10 -10
  278. package/dist/semantic-consolidation.js +39 -39
  279. package/dist/semantic-rule-promotion.js +30 -30
  280. package/dist/semantic-rule-verifier.js +30 -30
  281. package/dist/source-agent-qualifier.js +43 -43
  282. package/dist/standup/build.js +30 -30
  283. package/dist/{state-store-I5HLZTZT.js → state-store-EEAX2MBS.js} +2 -2
  284. package/dist/state-store-EEAX2MBS.js.map +1 -0
  285. package/dist/storage.js +29 -29
  286. package/dist/subject-config.js +1 -1
  287. package/dist/summarizer.js +5 -5
  288. package/dist/support-passport/index.js +108 -108
  289. package/dist/tier-routing.js +3 -3
  290. package/dist/{tier-stats-C3IAMS4X.js → tier-stats-FQGQSFET.js} +7 -7
  291. package/dist/transfer/capsule-export.js +2 -2
  292. package/dist/transfer/capsule-import.js +2 -2
  293. package/dist/transfer/export-okf.js +30 -30
  294. package/dist/types.d.ts +10 -10
  295. package/dist/types.d.ts.map +1 -1
  296. package/dist/types.js +1 -1
  297. package/dist/verified-recall.js +30 -30
  298. package/dist/who-knows-cli.js +31 -31
  299. package/dist/who-knows.js +30 -30
  300. package/dist/write-envelope.js +1 -1
  301. package/package.json +2 -2
  302. package/src/access-http-admission.ts +33 -0
  303. package/src/access-http-types.ts +153 -0
  304. package/src/access-http.test.ts +63 -1
  305. package/src/access-http.ts +26 -148
  306. package/src/access-mcp.test.ts +24 -0
  307. package/src/access-mcp.ts +3 -0
  308. package/src/access-observe-replay-durability.test.ts +4 -1
  309. package/src/access-service-observe-idempotency.test.ts +44 -0
  310. package/src/access-service.ts +3 -0
  311. package/src/buffer-admission.ts +13 -0
  312. package/src/cli.ts +1 -2
  313. package/src/config.test.ts +17 -0
  314. package/src/config.ts +25 -7
  315. package/src/connectors/live/framework.ts +8 -0
  316. package/src/connectors/live/google-drive.test.ts +35 -0
  317. package/src/connectors/live/google-drive.ts +108 -10
  318. package/src/live-connectors-runner.ts +3 -0
  319. package/src/live-connectors-secretref.test.ts +126 -0
  320. package/src/orchestration/turn-ingestion.ts +3 -3
  321. package/src/orchestrator.ts +5 -5
  322. package/src/types.ts +10 -10
  323. package/dist/chunk-4GMI5HFH.js.map +0 -1
  324. package/dist/chunk-APYY5CX3.js.map +0 -1
  325. package/dist/chunk-ATWFYXEN.js.map +0 -1
  326. package/dist/chunk-BCCG7VWM.js.map +0 -1
  327. package/dist/chunk-IHJZSRDM.js.map +0 -1
  328. package/dist/chunk-M3BEJJNV.js.map +0 -1
  329. package/dist/chunk-M5O2ANZD.js.map +0 -1
  330. package/dist/chunk-N7EKND7S.js.map +0 -1
  331. package/dist/chunk-SO4DZIVF.js.map +0 -1
  332. package/dist/chunk-V4XE263I.js.map +0 -1
  333. /package/dist/{contradiction-scan-P7KXB5R4.js.map → access-http-admission.js.map} +0 -0
  334. /package/dist/{graph-edge-decay-EKWXRBWF.js.map → access-http-types.js.map} +0 -0
  335. /package/dist/{chunk-4XYAWK5H.js.map → chunk-25BUV3TU.js.map} +0 -0
  336. /package/dist/{chunk-QKES7GU6.js.map → chunk-2FGLEF7C.js.map} +0 -0
  337. /package/dist/{chunk-3NF4QUNZ.js.map → chunk-2JSDPUKD.js.map} +0 -0
  338. /package/dist/{chunk-AJHNXIQU.js.map → chunk-2LCRG2YV.js.map} +0 -0
  339. /package/dist/{chunk-YRZPBN46.js.map → chunk-3RLK65ZK.js.map} +0 -0
  340. /package/dist/{chunk-GCBIXUPT.js.map → chunk-47F6WWLQ.js.map} +0 -0
  341. /package/dist/{chunk-TSS3D7ZY.js.map → chunk-4CJTQHEG.js.map} +0 -0
  342. /package/dist/{chunk-QD5R6U5J.js.map → chunk-4J363OAA.js.map} +0 -0
  343. /package/dist/{chunk-C2HORX76.js.map → chunk-4WR2OD7K.js.map} +0 -0
  344. /package/dist/{chunk-4X7HQKAG.js.map → chunk-4XINO77L.js.map} +0 -0
  345. /package/dist/{chunk-OXQ7F45O.js.map → chunk-5HFXGXE5.js.map} +0 -0
  346. /package/dist/{chunk-ELHJK7LM.js.map → chunk-5IYOKDM4.js.map} +0 -0
  347. /package/dist/{chunk-LEEAO2TX.js.map → chunk-5M5M22GW.js.map} +0 -0
  348. /package/dist/{chunk-52II7PB6.js.map → chunk-645WOU5I.js.map} +0 -0
  349. /package/dist/{chunk-QEZ3DW5O.js.map → chunk-65AUYLU3.js.map} +0 -0
  350. /package/dist/{chunk-B56LORLY.js.map → chunk-6F42QONS.js.map} +0 -0
  351. /package/dist/{chunk-ADYP7HXE.js.map → chunk-6F5MJFXY.js.map} +0 -0
  352. /package/dist/{chunk-GOZXQNIM.js.map → chunk-6P7U3HWG.js.map} +0 -0
  353. /package/dist/{chunk-SBRUTD5O.js.map → chunk-6SRYCJ5I.js.map} +0 -0
  354. /package/dist/{chunk-JM2YZKCQ.js.map → chunk-7GLJMDEQ.js.map} +0 -0
  355. /package/dist/{chunk-BLSHFIR6.js.map → chunk-7HTUYFR3.js.map} +0 -0
  356. /package/dist/{chunk-SYKJBS7S.js.map → chunk-7W3RXOIG.js.map} +0 -0
  357. /package/dist/{chunk-J6GZ2254.js.map → chunk-AL3TCNSJ.js.map} +0 -0
  358. /package/dist/{chunk-BIVKPQ6Q.js.map → chunk-AP6NCP3V.js.map} +0 -0
  359. /package/dist/{chunk-32YX7EJ3.js.map → chunk-ATPWZ4F7.js.map} +0 -0
  360. /package/dist/{chunk-HXFIILGF.js.map → chunk-BFPF5N3H.js.map} +0 -0
  361. /package/dist/{chunk-LHGPU6DQ.js.map → chunk-BJRTMNCB.js.map} +0 -0
  362. /package/dist/{chunk-F74G4KIH.js.map → chunk-BLJT6JP2.js.map} +0 -0
  363. /package/dist/{chunk-3TKX2RYK.js.map → chunk-BQZEGOZ4.js.map} +0 -0
  364. /package/dist/{chunk-HLN2HOXX.js.map → chunk-BRLXOZLM.js.map} +0 -0
  365. /package/dist/{chunk-5UBXIV3N.js.map → chunk-BSACVS6R.js.map} +0 -0
  366. /package/dist/{chunk-4Q26RNSR.js.map → chunk-CD4BCCNP.js.map} +0 -0
  367. /package/dist/{chunk-5SOSQTVE.js.map → chunk-CIEEYVUB.js.map} +0 -0
  368. /package/dist/{chunk-G54BOKZ6.js.map → chunk-E3A7ELSG.js.map} +0 -0
  369. /package/dist/{chunk-OL553AWT.js.map → chunk-E4IRW2FG.js.map} +0 -0
  370. /package/dist/{chunk-TFEHSPGQ.js.map → chunk-E5PPE4VA.js.map} +0 -0
  371. /package/dist/{chunk-3UZ2VMKD.js.map → chunk-E6M225BJ.js.map} +0 -0
  372. /package/dist/{chunk-TIGYOZBU.js.map → chunk-E6Z4FBX6.js.map} +0 -0
  373. /package/dist/{chunk-2XOFVTTT.js.map → chunk-E7YZXWIS.js.map} +0 -0
  374. /package/dist/{chunk-QKERFEJS.js.map → chunk-EFYN5I7Z.js.map} +0 -0
  375. /package/dist/{chunk-D5EV6Y2L.js.map → chunk-ETZLQOUR.js.map} +0 -0
  376. /package/dist/{chunk-IBDWYRDR.js.map → chunk-EXHXJETE.js.map} +0 -0
  377. /package/dist/{chunk-S45INSV3.js.map → chunk-FF6N7BXC.js.map} +0 -0
  378. /package/dist/{chunk-77PDJ4OE.js.map → chunk-FNXQZGUS.js.map} +0 -0
  379. /package/dist/{chunk-H3WAT6BR.js.map → chunk-FQKBJJYB.js.map} +0 -0
  380. /package/dist/{chunk-CC4V7HNP.js.map → chunk-FSO3G4JJ.js.map} +0 -0
  381. /package/dist/{chunk-ZOJUWVAU.js.map → chunk-GKL6QYZO.js.map} +0 -0
  382. /package/dist/{chunk-2U4LMSEX.js.map → chunk-GVGAO3WV.js.map} +0 -0
  383. /package/dist/{chunk-DT7SNJH6.js.map → chunk-HF3BKUVV.js.map} +0 -0
  384. /package/dist/{chunk-TRMZHGIX.js.map → chunk-HH5LY6RR.js.map} +0 -0
  385. /package/dist/{chunk-Q5XJYCFE.js.map → chunk-HVX4LPNY.js.map} +0 -0
  386. /package/dist/{chunk-5MF6LMUY.js.map → chunk-ID6JGIMD.js.map} +0 -0
  387. /package/dist/{chunk-YLTU2DC4.js.map → chunk-IHCXMYSE.js.map} +0 -0
  388. /package/dist/{chunk-2G4TV2ML.js.map → chunk-IPT2OY4B.js.map} +0 -0
  389. /package/dist/{chunk-ZANNIXXE.js.map → chunk-IQMDQJCD.js.map} +0 -0
  390. /package/dist/{chunk-S7GJNA3K.js.map → chunk-IQWUACVG.js.map} +0 -0
  391. /package/dist/{chunk-QNSZYTSG.js.map → chunk-ITHZFHST.js.map} +0 -0
  392. /package/dist/{chunk-QUMKDRCF.js.map → chunk-JDY34KBL.js.map} +0 -0
  393. /package/dist/{chunk-2N4OUSIA.js.map → chunk-JV4S6VDH.js.map} +0 -0
  394. /package/dist/{chunk-7LCGFUMB.js.map → chunk-KQAFEZQX.js.map} +0 -0
  395. /package/dist/{chunk-ARRTLPRC.js.map → chunk-LUJCQQ3L.js.map} +0 -0
  396. /package/dist/{chunk-DC55RIEZ.js.map → chunk-M4IEZGCK.js.map} +0 -0
  397. /package/dist/{chunk-TNW6BOMM.js.map → chunk-MB6ZJDN6.js.map} +0 -0
  398. /package/dist/{chunk-UWRHHRDK.js.map → chunk-MKRYQROF.js.map} +0 -0
  399. /package/dist/{chunk-P452SEKO.js.map → chunk-MXHMNKWG.js.map} +0 -0
  400. /package/dist/{chunk-IPD5GC7C.js.map → chunk-N4KAE2NB.js.map} +0 -0
  401. /package/dist/{chunk-25Q52SOT.js.map → chunk-NTRUKAAE.js.map} +0 -0
  402. /package/dist/{chunk-3DAWHP5K.js.map → chunk-NZSRRVJQ.js.map} +0 -0
  403. /package/dist/{chunk-ROGO7FRA.js.map → chunk-O55EKIEC.js.map} +0 -0
  404. /package/dist/{chunk-J5WI4QRP.js.map → chunk-OIGJZHDU.js.map} +0 -0
  405. /package/dist/{chunk-K3FH3LAQ.js.map → chunk-OJXK5C6Z.js.map} +0 -0
  406. /package/dist/{chunk-EA2LA5RI.js.map → chunk-P4WNBSTW.js.map} +0 -0
  407. /package/dist/{chunk-EURPSF4Y.js.map → chunk-PH5ZQM35.js.map} +0 -0
  408. /package/dist/{chunk-3ICAWZUU.js.map → chunk-PLCYKAWH.js.map} +0 -0
  409. /package/dist/{chunk-3Y5X6YFH.js.map → chunk-PMJFRDJI.js.map} +0 -0
  410. /package/dist/{chunk-PDOOUXCS.js.map → chunk-PPCLZ4JU.js.map} +0 -0
  411. /package/dist/{chunk-VKILERSL.js.map → chunk-PWXPRVLK.js.map} +0 -0
  412. /package/dist/{chunk-G6HWNPZA.js.map → chunk-QJPJXCS4.js.map} +0 -0
  413. /package/dist/{chunk-YMJRLT7S.js.map → chunk-R2DOFBG4.js.map} +0 -0
  414. /package/dist/{chunk-PKZ274PI.js.map → chunk-R2LBF4HH.js.map} +0 -0
  415. /package/dist/{chunk-NQP4ZYGG.js.map → chunk-RGUUBAT5.js.map} +0 -0
  416. /package/dist/{chunk-EWJZZDRG.js.map → chunk-RHQLIHMB.js.map} +0 -0
  417. /package/dist/{chunk-CYCEROPR.js.map → chunk-RIMK327Y.js.map} +0 -0
  418. /package/dist/{chunk-P4JED3ZL.js.map → chunk-RMXS4I66.js.map} +0 -0
  419. /package/dist/{chunk-TX6EHB2Q.js.map → chunk-RRJBDXAQ.js.map} +0 -0
  420. /package/dist/{chunk-MTTYIXSU.js.map → chunk-RRTVGD3J.js.map} +0 -0
  421. /package/dist/{chunk-OZNBZJ3S.js.map → chunk-SLDRXK37.js.map} +0 -0
  422. /package/dist/{chunk-NIVKG3DS.js.map → chunk-SUSW74WU.js.map} +0 -0
  423. /package/dist/{chunk-G67EUQAB.js.map → chunk-TG3R2SID.js.map} +0 -0
  424. /package/dist/{chunk-3PJEDHLD.js.map → chunk-U6UNF5ZS.js.map} +0 -0
  425. /package/dist/{chunk-YAVIBVCU.js.map → chunk-UA2NDTIU.js.map} +0 -0
  426. /package/dist/{chunk-KVSOEVAF.js.map → chunk-UE6RRXMF.js.map} +0 -0
  427. /package/dist/{chunk-TAUHAP5J.js.map → chunk-UXPXOVJI.js.map} +0 -0
  428. /package/dist/{chunk-KZSGBWOJ.js.map → chunk-V2KGDESV.js.map} +0 -0
  429. /package/dist/{chunk-UH3FPX3L.js.map → chunk-W7K5BP3M.js.map} +0 -0
  430. /package/dist/{chunk-TQ4SCPGQ.js.map → chunk-WRT7GJ2D.js.map} +0 -0
  431. /package/dist/{chunk-CN7Q3T2J.js.map → chunk-XNI3URDR.js.map} +0 -0
  432. /package/dist/{chunk-7O2MC42R.js.map → chunk-XNRLRNQF.js.map} +0 -0
  433. /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
  434. /package/dist/{chunk-ZBFLV657.js.map → chunk-XYKBWTWI.js.map} +0 -0
  435. /package/dist/{chunk-PWPIERBM.js.map → chunk-YCNIGRRF.js.map} +0 -0
  436. /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
  437. /package/dist/{chunk-DZ7DPDVW.js.map → chunk-YODUDA6R.js.map} +0 -0
  438. /package/dist/{chunk-2K3AUDH7.js.map → chunk-YYOMGOHB.js.map} +0 -0
  439. /package/dist/{chunk-ROKDHO74.js.map → chunk-Z7JIZ42P.js.map} +0 -0
  440. /package/dist/{resolution-23ZBDM6A.js.map → contradiction-scan-P2MHYFVG.js.map} +0 -0
  441. /package/dist/{first-start-migration-A6PHIBUQ.js.map → first-start-migration-QFI7B5SH.js.map} +0 -0
  442. /package/dist/{state-store-I5HLZTZT.js.map → graph-edge-decay-OEX5OGMI.js.map} +0 -0
  443. /package/dist/{tier-stats-C3IAMS4X.js.map → tier-stats-FQGQSFET.js.map} +0 -0
@@ -1600,7 +1600,10 @@ test("#3251 namespaced buffered admission without writeNamespaceOverride or plan
1600
1600
  [makeReplayTurn("no-target-sess", "capture without target")],
1601
1601
  bufferedObserveOptions({ sessionOwnerPrincipal: "alice" }),
1602
1602
  ),
1603
- /requires an explicit storage target/,
1603
+ (err: Error) =>
1604
+ err.name === "StorageTargetRequiredError" &&
1605
+ /requires an explicit storage target/.test(err.message),
1606
+ "the missing-target refusal must be its own non-retryable class, never ReplayAdmissionRefusedError",
1604
1607
  );
1605
1608
  assert.equal(lcmCalls.length, 0, "zero LCM archival on rejected admission");
1606
1609
  assert.equal(queued.length, 0, "zero extraction queued on rejected admission");
@@ -22,6 +22,7 @@ import { EngramAccessService } from "./access-service.js";
22
22
  import { EngramAccessInputError } from "./access-service.js";
23
23
  import { initLogger } from "./logger.js";
24
24
  import { Orchestrator } from "./orchestrator.js";
25
+ import { ReplayAdmissionRefusedError } from "./orchestration/turn-ingestion.js";
25
26
  import type { EngramAccessObserveRequest } from "./access-service.js";
26
27
  import type { CodingContext, PluginConfig } from "./types.js";
27
28
 
@@ -328,6 +329,49 @@ test("#2206 project-scope-off ignores ambient coding context in observe idempote
328
329
  }
329
330
  });
330
331
 
332
+ test("#1649 a capacity-refused observe does not consume its idempotencyKey", async () => {
333
+ const memoryDir = mkdtempSync(join(tmpdir(), "remnic-observe-cap-idem-"));
334
+ try {
335
+ const probe = makeObserveProbe(memoryDir);
336
+ const service = new EngramAccessService(probe.orch);
337
+ const orch: Orchestrator = probe.orch;
338
+ const realIngest = orch.ingestReplayBatch.bind(orch);
339
+ type IngestFn = Orchestrator["ingestReplayBatch"];
340
+ let armed = true;
341
+ orch.ingestReplayBatch = async (...args: Parameters<IngestFn>): Promise<void> => {
342
+ if (armed) {
343
+ armed = false;
344
+ throw new ReplayAdmissionRefusedError(
345
+ "durable observe capture refused: extraction buffer at entry capacity for batch containing pi-geek:abc123",
346
+ );
347
+ }
348
+ return realIngest(...args);
349
+ };
350
+ const key = "observe-capacity-pi-geek-abc123-#1";
351
+
352
+ // First attempt refuses at entry capacity — the key must stay unused.
353
+ await assert.rejects(
354
+ service.observe(observeRequest({ idempotencyKey: key })),
355
+ (err: Error) => err instanceof ReplayAdmissionRefusedError,
356
+ "the seeded first attempt must refuse with the capacity backpressure error",
357
+ );
358
+ assert.equal(probe.extractionCalls.length, 0, "refused attempt must not ingest");
359
+
360
+ // Identical retry, same key: a REAL miss once capacity exists, not a replay
361
+ // of a response that never happened.
362
+ const retry = await service.observe(observeRequest({ idempotencyKey: key }));
363
+ assert.equal(retry.idempotencyReplay, undefined, "refusal must not have cached a response");
364
+ assert.equal(probe.extractionCalls.length, 1, "retry is admitted exactly once");
365
+
366
+ // Third identical request replays — the success bound the key, not the refusal.
367
+ const replay = await service.observe(observeRequest({ idempotencyKey: key }));
368
+ assert.equal(replay.idempotencyReplay, true, "post-success identical request replays");
369
+ assert.equal(probe.extractionCalls.length, 1, "replay must not ingest again");
370
+ } finally {
371
+ rmSync(memoryDir, { recursive: true, force: true });
372
+ }
373
+ });
374
+
331
375
  test("#1649 review fix: projectTag observe replays correctly despite scope resolution side effects", async () => {
332
376
  // The fingerprint folds in the RESOLVED writeNamespace (not the raw ambient
333
377
  // context). resolveMemoryScopePlan seeds the session context from projectTag
@@ -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
 
@@ -26,6 +26,19 @@ export class ReplayAdmissionRefusedError extends Error {
26
26
  }
27
27
  }
28
28
 
29
+ /**
30
+ * Durable observe capture rejected because the request resolves to no storage
31
+ * target (namespaces ON with neither writeNamespaceOverride nor a plan's
32
+ * writeNamespace). A property of the request — retrying cannot fix it — so it
33
+ * must never be classified as retryable backpressure.
34
+ */
35
+ export class StorageTargetRequiredError extends Error {
36
+ constructor(message: string) {
37
+ super(message);
38
+ this.name = "StorageTargetRequiredError";
39
+ }
40
+ }
41
+
29
42
  /**
30
43
  * Live-admission bound (#1908 policy, backlog-safe): a NEW session enters
31
44
  * only while under the cap or when an EMPTY entry can be reclaimed. Buffered
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
  /**
@@ -88,6 +88,41 @@ test("validateGoogleDriveConfig accepts a minimal valid config", () => {
88
88
  assert.deepEqual([...cfg.folderIds], []);
89
89
  });
90
90
 
91
+ test("a SecretRef credential resolves at poll time and never reaches the persisted config", async () => {
92
+ const client = makeMockClient({});
93
+ let seenSecret = "";
94
+ const factory: GoogleDriveClientFactory = async (config) => {
95
+ seenSecret = config.clientSecret;
96
+ return client;
97
+ };
98
+ const connector = createGoogleDriveConnector({ clientFactory: factory });
99
+ const ref = { source: "keychain", id: "projects/drive-secret" };
100
+ const config: ConnectorConfig = { ...SYNTHETIC_CREDS, clientSecret: ref };
101
+ await connector.syncIncremental({
102
+ cursor: null,
103
+ config,
104
+ resolveSecretRef: async () => "resolved-drive-secret",
105
+ });
106
+ assert.equal(seenSecret, "resolved-drive-secret");
107
+ const persisted = JSON.stringify(connector.persistConfig?.(config) ?? {});
108
+ assert.equal(persisted.includes("resolved-drive-secret"), false);
109
+ assert.equal(persisted.includes("projects/drive-secret"), false);
110
+ });
111
+
112
+ test("a SecretRef with no resolver fails the connector and hides the reference", async () => {
113
+ const connector = createGoogleDriveConnector({ clientFactory: makeFactory(makeMockClient({})) });
114
+ const config: ConnectorConfig = { ...SYNTHETIC_CREDS, clientSecret: { source: "keychain", id: "projects/drive-secret" } };
115
+ let message = "";
116
+ try {
117
+ await connector.syncIncremental({ cursor: null, config });
118
+ } catch (err) {
119
+ message = err instanceof Error ? err.message : "";
120
+ }
121
+ assert.match(message, /clientSecret is a secret reference but no resolver is configured/);
122
+ assert.equal(message.includes("projects/drive-secret"), false);
123
+ assert.equal(message.includes("keychain"), false);
124
+ });
125
+
91
126
  test("validateGoogleDriveConfig rejects non-object input", () => {
92
127
  assert.throws(() => validateGoogleDriveConfig(null), /must be an object/);
93
128
  assert.throws(() => validateGoogleDriveConfig([]), /must be an object/);
@@ -65,6 +65,8 @@ import type {
65
65
  } from "./framework.js";
66
66
  import { isTransientHttpError } from "./transient-errors.js";
67
67
  import { 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,51 @@ function requireNonEmptyString(value: unknown, key: string): string {
321
369
  return trimmed;
322
370
  }
323
371
 
372
+
373
+ async function resolveCredential(
374
+ value: DriveCredential,
375
+ key: string,
376
+ resolver: ResolveSecretRefFn | null | undefined,
377
+ ): Promise<string> {
378
+ if (Object.prototype.toString.call(value) !== "[object Object]") {
379
+ // SAFETY: a DriveCredential that is not an object is the plain string form.
380
+ return value as string;
381
+ }
382
+ // SAFETY: the toString check proves value is a non-null object, so it is a SecretRef.
383
+ const ref = value as SecretRef;
384
+ if (resolver == null) {
385
+ throw new Error(`googleDrive: ${key} is a secret reference but no resolver is configured`);
386
+ }
387
+ let resolved: unknown;
388
+ try {
389
+ resolved = await resolver(ref);
390
+ } catch {
391
+ throw new Error(`googleDrive: ${key} secret reference could not be resolved`);
392
+ }
393
+ if (Object.prototype.toString.call(resolved) !== "[object String]") {
394
+ throw new Error(`googleDrive: ${key} secret reference resolved to a non-string value`);
395
+ }
396
+ // SAFETY: the toString check above proves `resolved` is a string.
397
+ const text = (resolved as string).trim();
398
+ if (text.length === 0) {
399
+ throw new Error(`googleDrive: ${key} secret reference resolved to an empty value`);
400
+ }
401
+ return text;
402
+ }
403
+
404
+ async function resolveGoogleDriveConfig(
405
+ stored: GoogleDriveConnectorConfig,
406
+ resolver: ResolveSecretRefFn | null | undefined,
407
+ ): Promise<GoogleDriveResolvedConfig> {
408
+ return {
409
+ clientId: await resolveCredential(stored.clientId, "clientId", resolver),
410
+ [CLIENT_SECRET_FIELD]: await resolveCredential(stored[CLIENT_SECRET_FIELD], CLIENT_SECRET_FIELD, resolver),
411
+ [REFRESH_TOKEN_FIELD]: await resolveCredential(stored[REFRESH_TOKEN_FIELD], REFRESH_TOKEN_FIELD, resolver),
412
+ pollIntervalMs: stored.pollIntervalMs,
413
+ folderIds: stored.folderIds,
414
+ };
415
+ }
416
+
324
417
  /**
325
418
  * Decide whether a Drive `change` corresponds to an importable text file
326
419
  * scoped to (optionally) the configured folder set.
@@ -405,16 +498,21 @@ export function createGoogleDriveConnector(
405
498
 
406
499
  persistConfig(validated: ConnectorConfig): ConnectorConfig {
407
500
  const config = validateGoogleDriveConfig(validated);
408
- return Object.freeze({
409
- clientId: config.clientId,
501
+ const persisted: PersistedGoogleDriveConfig = {
410
502
  pollIntervalMs: config.pollIntervalMs,
411
503
  folderIds: config.folderIds,
412
- });
504
+ };
505
+ if (Object.prototype.toString.call(config.clientId) === "[object String]") {
506
+ // SAFETY: the toString check proves clientId is a string, not a SecretRef.
507
+ persisted.clientId = config.clientId as string;
508
+ }
509
+ return Object.freeze(persisted);
510
+
413
511
  },
414
512
 
415
513
  async syncIncremental(args: SyncIncrementalArgs): Promise<SyncIncrementalResult> {
416
514
  // Re-validate on every pass: a JS caller could mutate it between passes.
417
- const config = validateGoogleDriveConfig(args.config);
515
+ const config = await resolveGoogleDriveConfig(validateGoogleDriveConfig(args.config), args.resolveSecretRef);
418
516
  throwIfAborted(args.abortSignal, "googleDrive: sync aborted");
419
517
 
420
518
  const client = await clientFactory(config);
@@ -31,6 +31,7 @@ import {
31
31
  } from "./connectors/live/index.js";
32
32
  import { abortError } from "./abort-error.js";
33
33
  import type { LiveConnectorsConfig } from "./types.js";
34
+ import type { ResolveSecretRefFn } from "./resolve-auth-token.js";
34
35
 
35
36
  export type LiveConnectorSkipReason =
36
37
  | "disabled"
@@ -138,6 +139,7 @@ export async function runLiveConnectorsOnce(options: {
138
139
  now?: LiveConnectorsNow;
139
140
  abortSignal?: AbortSignal;
140
141
  definitions?: LiveConnectorDefinition[];
142
+ resolveSecretRef?: ResolveSecretRefFn | null;
141
143
  }): Promise<LiveConnectorsRunSummary> {
142
144
  const ranAt = resolveNow(options.now);
143
145
  const force = options.force === true;
@@ -228,6 +230,7 @@ export async function runLiveConnectorsOnce(options: {
228
230
  cursor,
229
231
  config: validatedConfig,
230
232
  abortSignal: pollAbortSignal,
233
+ resolveSecretRef: options.resolveSecretRef,
231
234
  }),
232
235
  ingestFn: async (docs) => {
233
236
  throwIfAborted(pollAbortSignal);
@@ -0,0 +1,126 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { mkdtemp, readFile, rm } from "node:fs/promises";
6
+
7
+ import { parseConfig } from "./config.js";
8
+ import {
9
+ builtInLiveConnectorDefinitions,
10
+ runLiveConnectorsOnce,
11
+ } from "./live-connectors-runner.js";
12
+ import {
13
+ GOOGLE_DRIVE_CONNECTOR_ID,
14
+ NOTION_CONNECTOR_ID,
15
+ createGoogleDriveConnector,
16
+ type GoogleDriveClient,
17
+ } from "./connectors/live/index.js";
18
+ import type { LiveConnector } from "./connectors/live/framework.js";
19
+
20
+ const RESOLVED_SECRET = "resolved-drive-secret";
21
+ const SECRET_REF = { source: "keychain", id: "projects/drive-secret" };
22
+
23
+ function driveConfig() {
24
+ return {
25
+ openaiApiKey: "sk-test",
26
+ connectors: {
27
+ googleDrive: {
28
+ enabled: true,
29
+ clientId: "synthetic-client",
30
+ clientSecret: SECRET_REF,
31
+ refreshToken: "synthetic-token",
32
+ },
33
+ notion: { enabled: true, token: "secret_synthetic_notion_token" },
34
+ },
35
+ };
36
+ }
37
+
38
+ function notionStub(): LiveConnector {
39
+ return {
40
+ id: NOTION_CONNECTOR_ID,
41
+ displayName: "Notion",
42
+ validateConfig: () => ({}),
43
+ async syncIncremental() {
44
+ return { newDocs: [], nextCursor: { kind: "notion", value: "cursor-1", updatedAt: "2026-11-11T00:00:00.000Z" } };
45
+ },
46
+ };
47
+ }
48
+
49
+ test("a Drive SecretRef resolves through parseConfig and the runner, and stays out of the state file", async () => {
50
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-drive-secretref-"));
51
+ try {
52
+ const parsed = parseConfig(driveConfig());
53
+ const definitions = builtInLiveConnectorDefinitions(parsed.connectors);
54
+ let seenSecret = "";
55
+ const client: GoogleDriveClient = {
56
+ async getStartPageToken() {
57
+ return { startPageToken: "seed-token" };
58
+ },
59
+ async listChanges() {
60
+ return { changes: [], newStartPageToken: "next-token" };
61
+ },
62
+ async exportFile() {
63
+ return "";
64
+ },
65
+ async getFileMedia() {
66
+ return "";
67
+ },
68
+ };
69
+ for (const definition of definitions) {
70
+ if (definition.id === GOOGLE_DRIVE_CONNECTOR_ID) {
71
+ definition.createConnector = () =>
72
+ createGoogleDriveConnector({
73
+ clientFactory: async (config) => {
74
+ seenSecret = config.clientSecret;
75
+ return client;
76
+ },
77
+ });
78
+ }
79
+ if (definition.id === NOTION_CONNECTOR_ID) {
80
+ definition.createConnector = notionStub;
81
+ }
82
+ }
83
+ const summary = await runLiveConnectorsOnce({
84
+ memoryDir,
85
+ connectors: parsed.connectors,
86
+ definitions,
87
+ force: true,
88
+ resolveSecretRef: async () => RESOLVED_SECRET,
89
+ ingestDocuments: async () => {},
90
+ });
91
+ assert.equal(seenSecret, RESOLVED_SECRET);
92
+ const drive = summary.results.find((item) => item.id === GOOGLE_DRIVE_CONNECTOR_ID);
93
+ assert.equal(drive?.ran, true);
94
+ const statePath = path.join(memoryDir, "state", "connectors", `${GOOGLE_DRIVE_CONNECTOR_ID}.json`);
95
+ const stateText = await readFile(statePath, "utf-8");
96
+ assert.equal(stateText.includes(RESOLVED_SECRET), false);
97
+ assert.equal(stateText.includes(SECRET_REF.id), false);
98
+ } finally {
99
+ await rm(memoryDir, { recursive: true, force: true });
100
+ }
101
+ });
102
+
103
+ test("a Drive SecretRef with no resolver fails that connector and the others still run", async () => {
104
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-drive-secretref-"));
105
+ try {
106
+ const parsed = parseConfig(driveConfig());
107
+ const definitions = builtInLiveConnectorDefinitions(parsed.connectors);
108
+ for (const definition of definitions) {
109
+ if (definition.id === NOTION_CONNECTOR_ID) definition.createConnector = notionStub;
110
+ }
111
+ const summary = await runLiveConnectorsOnce({
112
+ memoryDir,
113
+ connectors: parsed.connectors,
114
+ definitions,
115
+ force: true,
116
+ ingestDocuments: async () => {},
117
+ });
118
+ const drive = summary.results.find((item) => item.id === GOOGLE_DRIVE_CONNECTOR_ID);
119
+ const notion = summary.results.find((item) => item.id === NOTION_CONNECTOR_ID);
120
+ assert.match(drive?.error ?? "", /clientSecret is a secret reference but no resolver is configured/);
121
+ assert.equal((drive?.error ?? "").includes(SECRET_REF.id), false);
122
+ assert.equal(notion?.ran, true);
123
+ } finally {
124
+ await rm(memoryDir, { recursive: true, force: true });
125
+ }
126
+ });
@@ -59,8 +59,8 @@ import {
59
59
 
60
60
  // The class lives in ../buffer-admission.js next to BufferAdmissionRefusedError;
61
61
  // re-exported here so existing importers keep their import path.
62
- import { ReplayAdmissionRefusedError } from "../buffer-admission.js";
63
- export { ReplayAdmissionRefusedError };
62
+ import { ReplayAdmissionRefusedError, StorageTargetRequiredError } from "../buffer-admission.js";
63
+ export { ReplayAdmissionRefusedError, StorageTargetRequiredError };
64
64
 
65
65
  export interface TurnIngestionDeps {
66
66
  readonly buffer: SmartBuffer;
@@ -428,7 +428,7 @@ export class TurnIngestionCoordinator {
428
428
  ? options.scopeProfileWritePlan?.writeNamespace
429
429
  : this.deps.config.defaultNamespace);
430
430
  if (!resolvedWriteTarget) {
431
- throw new ReplayAdmissionRefusedError(
431
+ throw new StorageTargetRequiredError(
432
432
  "accepted capture requires an explicit storage target (writeNamespaceOverride or scopeProfileWritePlan.writeNamespace)",
433
433
  );
434
434
  }
@@ -236,6 +236,7 @@ import {
236
236
  runLiveConnectorsOnce,
237
237
  type LiveConnectorsRunSummary,
238
238
  } from "./live-connectors-runner.js";
239
+ import type { ResolveSecretRefFn } from "./resolve-auth-token.js";
239
240
  import {
240
241
  runPatternReinforcement,
241
242
  type PatternReinforcementResult,
@@ -2116,20 +2117,19 @@ export class Orchestrator {
2116
2117
  async runLiveConnectors(options: {
2117
2118
  force?: boolean;
2118
2119
  abortSignal?: AbortSignal;
2120
+ resolveSecretRef?: ResolveSecretRefFn | null;
2119
2121
  } = {}): Promise<LiveConnectorsRunSummary> {
2120
2122
  return runLiveConnectorsOnce({
2121
2123
  memoryDir: this.config.memoryDir,
2122
2124
  connectors: this.config.connectors,
2123
2125
  force: options.force === true,
2124
2126
  abortSignal: options.abortSignal,
2127
+ resolveSecretRef: options.resolveSecretRef,
2125
2128
  ingestDocuments: async (docs) => {
2126
2129
  const fetchedAt = new Date().toISOString();
2127
2130
  const turns = docs.map((doc) => ({
2128
- role: "assistant" as const,
2129
- content: doc.title
2130
- ? `# ${doc.title}\n\n${doc.content}`
2131
- : doc.content,
2132
- timestamp: fetchedAt,
2131
+ role: "assistant" as const, timestamp: fetchedAt,
2132
+ content: doc.title ? `# ${doc.title}\n\n${doc.content}` : doc.content,
2133
2133
  }));
2134
2134
  await this.ingestBulkImportBatch(turns);
2135
2135
  },