@remnic/core 9.65.2 → 9.65.4

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 (492) hide show
  1. package/dist/access-admin-ops-surface.d.ts +44 -44
  2. package/dist/access-admin-ops-surface.js +21 -19
  3. package/dist/access-authorization-probe.d.ts +38 -38
  4. package/dist/access-authorization-probe.js +22 -20
  5. package/dist/access-boundary.d.ts +40 -40
  6. package/dist/access-boundary.js +21 -19
  7. package/dist/access-cli.js +62 -60
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +3 -1
  10. package/dist/access-extraction-force-flush.d.ts +25 -25
  11. package/dist/access-extraction-force-flush.js +21 -19
  12. package/dist/access-health-types.d.ts +3 -1
  13. package/dist/access-http-lcm-compaction.d.ts +39 -39
  14. package/dist/access-http-lifecycle-flush.d.ts +39 -39
  15. package/dist/access-http-offline-stream.d.ts +25 -25
  16. package/dist/access-http-query.d.ts +4 -2
  17. package/dist/access-http-query.js +23 -21
  18. package/dist/access-http.d.ts +51 -44
  19. package/dist/access-http.js +37 -35
  20. package/dist/access-identity-continuity-surface.d.ts +12 -12
  21. package/dist/access-identity-continuity-surface.js +21 -19
  22. package/dist/access-lcm-surface.d.ts +27 -27
  23. package/dist/access-lcm-surface.js +21 -19
  24. package/dist/access-mcp-admin-tools.d.ts +9 -1
  25. package/dist/access-mcp-admin-tools.js +3 -1
  26. package/dist/access-mcp-output-schemas.js +1 -1
  27. package/dist/access-mcp.d.ts +39 -39
  28. package/dist/access-mcp.js +31 -29
  29. package/dist/access-memory-search-fanout.d.ts +4 -2
  30. package/dist/{access-namespace-preflight-Dhvcl-T0.d.ts → access-namespace-preflight-D_6mo8ms.d.ts} +1 -1
  31. package/dist/access-namespace-preflight.d.ts +5 -3
  32. package/dist/access-namespace-preflight.js +21 -19
  33. package/dist/access-observe-write-surface.d.ts +39 -39
  34. package/dist/access-observe-write-surface.js +21 -19
  35. package/dist/access-offline-manifest.d.ts +12 -12
  36. package/dist/access-offline-manifest.js +3 -3
  37. package/dist/access-operations-batch.js +25 -23
  38. package/dist/access-operations.d.ts +40 -40
  39. package/dist/access-operations.js +28 -26
  40. package/dist/access-recall-concurrency.d.ts +40 -40
  41. package/dist/access-recall-concurrency.js +21 -19
  42. package/dist/access-recall-response.d.ts +24 -24
  43. package/dist/access-recall-response.js +21 -19
  44. package/dist/access-recall-surface.d.ts +24 -24
  45. package/dist/access-recall-surface.js +21 -19
  46. package/dist/access-schema.d.ts +45 -45
  47. package/dist/{access-service-Bke1alZO.d.ts → access-service-DqPa3hy4.d.ts} +219 -75
  48. package/dist/access-service-helpers.d.ts +40 -40
  49. package/dist/access-service.d.ts +47 -47
  50. package/dist/access-service.js +21 -19
  51. package/dist/access-surface-catalog.d.ts +40 -40
  52. package/dist/access-surface-catalog.js +27 -21
  53. package/dist/access-surface-catalog.js.map +1 -1
  54. package/dist/access-wearables-meetings-surface.d.ts +5 -3
  55. package/dist/action-confidence.d.ts +3 -1
  56. package/dist/active-memory-bridge.d.ts +3 -1
  57. package/dist/active-recall.d.ts +3 -1
  58. package/dist/active-recall.js +10 -1
  59. package/dist/active-recall.js.map +1 -1
  60. package/dist/ambient-provenance.d.ts +3 -1
  61. package/dist/artifact-search.d.ts +3 -1
  62. package/dist/{auto-sync-H64ZCNIR.js → auto-sync-XVMLL2RH.js} +5 -5
  63. package/dist/behavior-learner.d.ts +3 -1
  64. package/dist/behavior-signals.d.ts +3 -1
  65. package/dist/bootstrap.d.ts +12 -12
  66. package/dist/briefing-window.d.ts +4 -2
  67. package/dist/briefing.d.ts +4 -2
  68. package/dist/briefing.js +4 -4
  69. package/dist/buffer-surprise-report.d.ts +3 -1
  70. package/dist/buffer-turn-helpers.d.ts +3 -1
  71. package/dist/buffer.d.ts +4 -2
  72. package/dist/bulk-import/index.d.ts +5 -3
  73. package/dist/calibration.d.ts +3 -1
  74. package/dist/capabilities.d.ts +3 -1
  75. package/dist/{catalog-DV6qnbr7.d.ts → catalog-DO_7zfkv.d.ts} +1 -1
  76. package/dist/causal-behavior.d.ts +1 -1
  77. package/dist/causal-consolidation.d.ts +3 -1
  78. package/dist/causal-consolidation.js +12 -9
  79. package/dist/causal-consolidation.js.map +1 -1
  80. package/dist/causal-trajectory-graph.d.ts +1 -1
  81. package/dist/chunk-236PXYG7.js +232 -0
  82. package/dist/chunk-236PXYG7.js.map +1 -0
  83. package/dist/{chunk-YB42WZCY.js → chunk-2IXQSGCU.js} +2 -2
  84. package/dist/{chunk-PE255F4T.js → chunk-2MVTAIL3.js} +2 -2
  85. package/dist/{chunk-LGO6A7GJ.js → chunk-3JZBPTLD.js} +338 -80
  86. package/dist/chunk-3JZBPTLD.js.map +1 -0
  87. package/dist/{chunk-GDFVPUJ5.js → chunk-3NH3VP3U.js} +2 -2
  88. package/dist/{chunk-TBOJ76ER.js → chunk-AB3A7EFH.js} +18 -2
  89. package/dist/{chunk-TBOJ76ER.js.map → chunk-AB3A7EFH.js.map} +1 -1
  90. package/dist/{chunk-GNCCUR6K.js → chunk-AXGFOFNY.js} +2 -2
  91. package/dist/{chunk-SB7D4BT7.js → chunk-AZLLH6YA.js} +1554 -114
  92. package/dist/chunk-AZLLH6YA.js.map +1 -0
  93. package/dist/{chunk-5YQ63T4B.js → chunk-BK5G2MHO.js} +2 -2
  94. package/dist/{chunk-LZH5FR2W.js → chunk-CQBIN77D.js} +2 -2
  95. package/dist/{chunk-35NN3ZFA.js → chunk-CTVWELNK.js} +21 -3
  96. package/dist/chunk-CTVWELNK.js.map +1 -0
  97. package/dist/{chunk-NKKD4ZAU.js → chunk-DORFF5N5.js} +2 -2
  98. package/dist/{chunk-BWLRUMOG.js → chunk-DPNXADEI.js} +441 -2
  99. package/dist/chunk-DPNXADEI.js.map +1 -0
  100. package/dist/{chunk-SQI57UCJ.js → chunk-DUEDOYZD.js} +2 -2
  101. package/dist/{chunk-5NM4OH4P.js → chunk-DUZARALI.js} +3 -3
  102. package/dist/{chunk-2E36UXVW.js → chunk-ECLJ4LA2.js} +2 -2
  103. package/dist/{chunk-TZNQUNKL.js → chunk-FC7TIRFS.js} +15 -1
  104. package/dist/chunk-FC7TIRFS.js.map +1 -0
  105. package/dist/{chunk-IID2CMUU.js → chunk-GHD2LLG4.js} +2 -2
  106. package/dist/{chunk-ZQHI2BZB.js → chunk-HF5BUTI6.js} +2 -2
  107. package/dist/{chunk-3DVBKTSO.js → chunk-HSZMX3ZJ.js} +4 -4
  108. package/dist/{chunk-S2DPMJXH.js → chunk-I23ZHYSL.js} +2 -2
  109. package/dist/{chunk-ODZG724P.js → chunk-I2HTKOOP.js} +2 -2
  110. package/dist/chunk-I32NKXE2.js +879 -0
  111. package/dist/chunk-I32NKXE2.js.map +1 -0
  112. package/dist/{chunk-NTWN6HB2.js → chunk-IURMJBGP.js} +2 -2
  113. package/dist/{chunk-OCZIYAE5.js → chunk-IWKGOCL6.js} +3 -3
  114. package/dist/{chunk-UXO5IMBK.js → chunk-J3ATBB7M.js} +2 -2
  115. package/dist/{chunk-Y7DK7HHJ.js → chunk-JPAKBNTM.js} +2 -2
  116. package/dist/{chunk-SPUJNJJC.js → chunk-KFIOPZ25.js} +2 -2
  117. package/dist/{chunk-JYYBA7TF.js → chunk-KOO37E4Y.js} +2 -2
  118. package/dist/{chunk-OTHZWLUD.js → chunk-KPB2QHJZ.js} +2 -2
  119. package/dist/{chunk-SBDCZ2AX.js → chunk-LBFFNDFM.js} +1 -1
  120. package/dist/chunk-LBFFNDFM.js.map +1 -0
  121. package/dist/{chunk-CT56C22C.js → chunk-LYBIHM4N.js} +4 -4
  122. package/dist/{chunk-3I7ASZN3.js → chunk-MFOSZUIT.js} +2 -2
  123. package/dist/{chunk-DZUJ46DO.js → chunk-NLEH4RTB.js} +2 -2
  124. package/dist/{chunk-ZO6AXLEH.js → chunk-OCCHUEZB.js} +2 -2
  125. package/dist/{chunk-EILZG5VC.js → chunk-OU34IQXV.js} +2 -2
  126. package/dist/{chunk-2XDWWPTN.js → chunk-OZDFB22S.js} +2 -2
  127. package/dist/{chunk-UIN2BWDD.js → chunk-PTZQYCJ4.js} +2 -2
  128. package/dist/{chunk-7BY7V4YX.js → chunk-Q4WONDMJ.js} +4 -3
  129. package/dist/{chunk-7BY7V4YX.js.map → chunk-Q4WONDMJ.js.map} +1 -1
  130. package/dist/{chunk-Z7PBQPJ4.js → chunk-QJW4MZSW.js} +2 -2
  131. package/dist/{chunk-R7JAM2XF.js → chunk-R2MZCYA6.js} +3 -3
  132. package/dist/{chunk-TF77C7SV.js → chunk-SR5LNI6Q.js} +7 -5
  133. package/dist/chunk-SR5LNI6Q.js.map +1 -0
  134. package/dist/{chunk-LBWQ65SX.js → chunk-T42L3S6A.js} +4 -4
  135. package/dist/{chunk-J275IYMG.js → chunk-THVI4Y7X.js} +34 -7
  136. package/dist/chunk-THVI4Y7X.js.map +1 -0
  137. package/dist/{chunk-QOS5LBZN.js → chunk-TJFI3K2O.js} +15 -3
  138. package/dist/chunk-TJFI3K2O.js.map +1 -0
  139. package/dist/{chunk-2YI72KM3.js → chunk-UCKG36HO.js} +2 -2
  140. package/dist/{chunk-SIHSCCUQ.js → chunk-UFR6HX7A.js} +1 -1
  141. package/dist/{chunk-CDUUDBWJ.js → chunk-VFYRYFLL.js} +2 -2
  142. package/dist/{chunk-XRYZRW54.js → chunk-WDXD6ITO.js} +2 -2
  143. package/dist/{chunk-4KYOLI6M.js → chunk-XGM3VQWG.js} +120 -14
  144. package/dist/chunk-XGM3VQWG.js.map +1 -0
  145. package/dist/{chunk-MN4KB2A4.js → chunk-YDKF4MSJ.js} +3 -3
  146. package/dist/{chunk-YLDXWCZP.js → chunk-YHGCT462.js} +14 -9
  147. package/dist/chunk-YHGCT462.js.map +1 -0
  148. package/dist/{chunk-7YQ4IYS2.js → chunk-YOXVZJFM.js} +2 -2
  149. package/dist/{chunk-ZWXJBFZU.js → chunk-YZJM6CLF.js} +8 -8
  150. package/dist/chunk-ZCUTSMR2.js +61 -0
  151. package/dist/chunk-ZCUTSMR2.js.map +1 -0
  152. package/dist/{chunk-LUNJ3WFO.js → chunk-ZK4BFABK.js} +4 -4
  153. package/dist/{chunk-V3MXUL3D.js → chunk-ZPOHEB5Z.js} +329 -653
  154. package/dist/chunk-ZPOHEB5Z.js.map +1 -0
  155. package/dist/{chunk-RDFF2NAU.js → chunk-ZXBTTMGZ.js} +184 -88
  156. package/dist/chunk-ZXBTTMGZ.js.map +1 -0
  157. package/dist/{cli-cShQ13XG.d.ts → cli-DO3i14pM.d.ts} +5 -5
  158. package/dist/cli.d.ts +25 -25
  159. package/dist/cli.js +51 -48
  160. package/dist/coding/pre-action-gate.d.ts +2 -60
  161. package/dist/compounding/engine.d.ts +4 -2
  162. package/dist/compounding/engine.js +4 -4
  163. package/dist/compounding/preference-consolidator.d.ts +3 -1
  164. package/dist/compression-optimizer.d.ts +3 -1
  165. package/dist/config.d.ts +3 -1
  166. package/dist/config.js +10 -1
  167. package/dist/connectors/codex-materialize-runner.d.ts +3 -1
  168. package/dist/connectors/codex-materialize-runner.js +8 -5
  169. package/dist/connectors/codex-materialize.d.ts +16 -1
  170. package/dist/connectors/codex-materialize.js +10 -1
  171. package/dist/connectors/index.d.ts +3 -1
  172. package/dist/connectors/index.js +8 -5
  173. package/dist/consolidation-provenance-check.d.ts +4 -2
  174. package/dist/consolidation-undo.d.ts +4 -2
  175. package/dist/contradiction/index.d.ts +10 -270
  176. package/dist/contradiction/index.js +12 -7
  177. package/dist/{contradiction-review-QKJH5DKD.js → contradiction-review-WRVLRWHS.js} +6 -2
  178. package/dist/{contradiction-scan-VOTQRRSX.js → contradiction-scan-AKUWPX5N.js} +5 -3
  179. package/dist/converge-config.d.ts +3 -1
  180. package/dist/convergence-refresh.d.ts +4 -2
  181. package/dist/convergence-refresh.js +6 -6
  182. package/dist/conversation-index/backend.d.ts +3 -1
  183. package/dist/conversation-index/chunker.d.ts +3 -1
  184. package/dist/conversation-index/faiss-adapter.d.ts +3 -1
  185. package/dist/conversation-index/indexer.d.ts +3 -1
  186. package/dist/conversation-index/search.d.ts +3 -1
  187. package/dist/corpus-watermark.d.ts +3 -1
  188. package/dist/corpus-watermark.js +6 -6
  189. package/dist/cross-lingual-recall.d.ts +3 -1
  190. package/dist/day-summary.d.ts +3 -1
  191. package/dist/delinearize.d.ts +3 -1
  192. package/dist/dependency-propagation-config.d.ts +3 -1
  193. package/dist/{dependency-propagation-delivery-CsGg-VwS.d.ts → dependency-propagation-delivery-D23_x-s5.d.ts} +1 -1
  194. package/dist/direct-answer-wiring.d.ts +3 -1
  195. package/dist/direct-answer.d.ts +3 -1
  196. package/dist/embedding-fallback.d.ts +3 -1
  197. package/dist/enrichment/index.d.ts +3 -1
  198. package/dist/entity-origin-fields.d.ts +3 -1
  199. package/dist/entity-retrieval.d.ts +4 -2
  200. package/dist/entity-retrieval.js +4 -4
  201. package/dist/entity-schema.d.ts +3 -1
  202. package/dist/episodic-context.d.ts +3 -1
  203. package/dist/explicit-capture.d.ts +12 -12
  204. package/dist/explicit-capture.js +1 -1
  205. package/dist/external-wiki-access.d.ts +40 -40
  206. package/dist/external-wiki-access.js +22 -20
  207. package/dist/external-wiki-collection-registration.d.ts +3 -1
  208. package/dist/external-wiki-collection.d.ts +3 -1
  209. package/dist/external-wiki-mcp-tools.d.ts +40 -40
  210. package/dist/extraction-error-classification.d.ts +3 -1
  211. package/dist/extraction-faithfulness.d.ts +3 -1
  212. package/dist/extraction-judge-telemetry.d.ts +3 -1
  213. package/dist/extraction-judge-training.d.ts +3 -1
  214. package/dist/extraction-judge.d.ts +3 -1
  215. package/dist/extraction-judge.js +2 -2
  216. package/dist/extraction-liveness.d.ts +3 -1
  217. package/dist/extraction-normalization.d.ts +3 -1
  218. package/dist/extraction-prompt.d.ts +3 -1
  219. package/dist/extraction-source-grounding-rules.d.ts +3 -1
  220. package/dist/extraction-source-grounding.d.ts +3 -1
  221. package/dist/extraction.d.ts +3 -1
  222. package/dist/extraction.js +9 -7
  223. package/dist/fallback-llm.d.ts +3 -1
  224. package/dist/graph-dashboard-diff.d.ts +3 -1
  225. package/dist/graph-dashboard-key.d.ts +3 -1
  226. package/dist/graph-dashboard-parser.d.ts +3 -1
  227. package/dist/graph-edge-reinforcement.d.ts +3 -1
  228. package/dist/graph-path-reconstruction.d.ts +3 -1
  229. package/dist/graph-path-scoring.d.ts +3 -1
  230. package/dist/graph-snapshot.d.ts +3 -1
  231. package/dist/graph.d.ts +3 -1
  232. package/dist/harmonic-retrieval.js +1 -1
  233. package/dist/importance.d.ts +3 -1
  234. package/dist/importers/index.d.ts +3 -1
  235. package/dist/in-flight-reads.d.ts +3 -1
  236. package/dist/index--QhGIXF2.d.ts +284 -0
  237. package/dist/index.d.ts +314 -45
  238. package/dist/index.js +163 -88
  239. package/dist/intent.d.ts +3 -1
  240. package/dist/lcm/engine.d.ts +3 -1
  241. package/dist/lcm/index.d.ts +3 -1
  242. package/dist/lcm/tools.d.ts +3 -1
  243. package/dist/lifecycle.d.ts +3 -1
  244. package/dist/live-connectors-runner.d.ts +3 -1
  245. package/dist/local-llm.d.ts +3 -1
  246. package/dist/local-model-endpoint.d.ts +3 -1
  247. package/dist/maintenance/memory-governance.d.ts +3 -1
  248. package/dist/maintenance/memory-governance.js +4 -4
  249. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +4 -2
  250. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -4
  251. package/dist/maintenance/rebuild-memory-projection.d.ts +4 -2
  252. package/dist/maintenance/rebuild-memory-projection.js +5 -5
  253. package/dist/{maintenance-jhMpeGne.d.ts → maintenance-PFvY1Pw_.d.ts} +3 -3
  254. package/dist/mcp-memory-inspector-app.d.ts +37 -37
  255. package/dist/memory-action-policy.d.ts +3 -1
  256. package/dist/memory-cache.d.ts +3 -1
  257. package/dist/memory-lifecycle-ledger-utils.d.ts +3 -1
  258. package/dist/memory-projection-store.d.ts +3 -1
  259. package/dist/memory-provenance.d.ts +3 -1
  260. package/dist/memory-snapshot.d.ts +3 -1
  261. package/dist/memory-subject-cli.d.ts +40 -40
  262. package/dist/memory-subject.d.ts +42 -42
  263. package/dist/memory-worth-outcomes.d.ts +4 -2
  264. package/dist/models-json.d.ts +3 -1
  265. package/dist/namespaces/migrate.d.ts +5 -3
  266. package/dist/namespaces/migrate.js +5 -5
  267. package/dist/namespaces/principal.d.ts +3 -1
  268. package/dist/namespaces/search.d.ts +3 -1
  269. package/dist/namespaces/storage.d.ts +5 -3
  270. package/dist/namespaces/storage.js +4 -4
  271. package/dist/native-knowledge.d.ts +3 -1
  272. package/dist/offline-sync-impression-drain.d.ts +3 -1
  273. package/dist/operator-doctor-corpus.d.ts +3 -1
  274. package/dist/operator-doctor-corpus.js +7 -7
  275. package/dist/operator-doctor-replica.d.ts +3 -1
  276. package/dist/operator-toolkit.d.ts +5 -3
  277. package/dist/operator-toolkit.js +19 -13
  278. package/dist/orchestration/compression-guideline-coordinator.d.ts +4 -2
  279. package/dist/orchestration/maintenance.d.ts +6 -4
  280. package/dist/orchestration/maintenance.js +10 -10
  281. package/dist/{orchestrator-DyGUC7re.d.ts → orchestrator-4wzJrPVm.d.ts} +23 -9
  282. package/dist/orchestrator.d.ts +12 -12
  283. package/dist/orchestrator.js +62 -60
  284. package/dist/patterns-cli.d.ts +3 -1
  285. package/dist/{pipeline-C25Oxtp1.d.ts → pipeline-CgXgdIrF.d.ts} +1 -1
  286. package/dist/policy-runtime.d.ts +3 -1
  287. package/dist/proactive-contention.d.ts +3 -1
  288. package/dist/provenance.d.ts +3 -1
  289. package/dist/{public-http-CMe7NQzj.d.ts → public-http-DhIXYBYI.d.ts} +1 -1
  290. package/dist/{qmd-DkDVCbgF.d.ts → qmd-BppGGxsi.d.ts} +1 -1
  291. package/dist/qmd-preflight.d.ts +4 -2
  292. package/dist/qmd-recall-cache.d.ts +3 -1
  293. package/dist/qmd.d.ts +4 -2
  294. package/dist/recall-concurrency-config.d.ts +3 -1
  295. package/dist/recall-disclosure-escalation.d.ts +3 -1
  296. package/dist/recall-explain-renderer.d.ts +3 -1
  297. package/dist/recall-explain-renderer.js +3 -3
  298. package/dist/recall-memory-map.d.ts +3 -1
  299. package/dist/recall-planner-llm.d.ts +3 -1
  300. package/dist/recall-state.d.ts +3 -1
  301. package/dist/recall-tag-filter.d.ts +3 -1
  302. package/dist/recall-timings.d.ts +3 -1
  303. package/dist/recall-xray-cli.d.ts +3 -1
  304. package/dist/recall-xray-cli.js +4 -4
  305. package/dist/recall-xray-renderer.d.ts +3 -1
  306. package/dist/recall-xray-renderer.js +3 -3
  307. package/dist/recall-xray.d.ts +3 -1
  308. package/dist/recall-xray.js +2 -2
  309. package/dist/reconcile/cursor.d.ts +3 -1
  310. package/dist/reconcile/manifest.d.ts +3 -1
  311. package/dist/reconcile/manifest.js +2 -2
  312. package/dist/reconcile/plan.d.ts +3 -1
  313. package/dist/replay/normalizers/chatgpt.d.ts +3 -1
  314. package/dist/replay/normalizers/claude.d.ts +3 -1
  315. package/dist/replay/normalizers/openclaw.d.ts +3 -1
  316. package/dist/replay/normalizers/shared.d.ts +3 -1
  317. package/dist/replay/runner.d.ts +3 -1
  318. package/dist/replay/types.d.ts +3 -1
  319. package/dist/replica-divergence.d.ts +3 -1
  320. package/dist/replica-peers-config.d.ts +3 -1
  321. package/dist/{resolution-MFY72STR.js → resolution-4552BNRV.js} +14 -4
  322. package/dist/resolve-auth-token.d.ts +3 -1
  323. package/dist/resume-bundles.js +8 -2
  324. package/dist/retrieval-agents.d.ts +4 -2
  325. package/dist/retrieval-tiers.d.ts +3 -1
  326. package/dist/routing/engine.d.ts +3 -1
  327. package/dist/routing/store.d.ts +3 -1
  328. package/dist/salvage-envelope.d.ts +3 -1
  329. package/dist/salvage-envelope.js +1 -1
  330. package/dist/schemas.d.ts +44 -44
  331. package/dist/{scope-profiles-B9xMoFO-.d.ts → scope-profiles-ChZUtxDU.d.ts} +1 -1
  332. package/dist/search/embed-helper.d.ts +3 -1
  333. package/dist/search/factory.d.ts +3 -1
  334. package/dist/search/index.d.ts +3 -1
  335. package/dist/search/lancedb-backend.d.ts +3 -1
  336. package/dist/search/meilisearch-backend.d.ts +3 -1
  337. package/dist/search/noop-backend.d.ts +3 -1
  338. package/dist/search/orama-backend.d.ts +3 -1
  339. package/dist/search/port.d.ts +3 -1
  340. package/dist/search/remote-backend.d.ts +3 -1
  341. package/dist/{semantic-TddhF0y7.d.ts → semantic-BalR6EXU.d.ts} +1 -1
  342. package/dist/{semantic-consolidation-C4uykW84.d.ts → semantic-consolidation-mSPwQSmF.d.ts} +1 -1
  343. package/dist/semantic-consolidation.d.ts +4 -2
  344. package/dist/semantic-consolidation.js +9 -6
  345. package/dist/semantic-rule-promotion.js +4 -4
  346. package/dist/semantic-rule-verifier.d.ts +3 -1
  347. package/dist/semantic-rule-verifier.js +4 -4
  348. package/dist/{service-DCZDZNcV.d.ts → service-BICWLwSP.d.ts} +2 -2
  349. package/dist/session-observer-bands.d.ts +3 -1
  350. package/dist/session-observer-state.d.ts +3 -1
  351. package/dist/shared-context/manager.d.ts +3 -1
  352. package/dist/signal.d.ts +3 -1
  353. package/dist/skills-registry.js +4 -56
  354. package/dist/skills-registry.js.map +1 -1
  355. package/dist/source-agent-qualifier.d.ts +3 -1
  356. package/dist/source-agent-qualifier.js +8 -5
  357. package/dist/{storage-CcKmEjL8.d.ts → storage-B7urx1K3.d.ts} +1 -1
  358. package/dist/storage.d.ts +4 -2
  359. package/dist/storage.js +6 -6
  360. package/dist/subject-config.d.ts +3 -1
  361. package/dist/summarizer.d.ts +3 -1
  362. package/dist/summary-snapshot.d.ts +3 -1
  363. package/dist/support-passport/index.d.ts +43 -43
  364. package/dist/support-passport/index.js +24 -22
  365. package/dist/temporal-supersession.d.ts +4 -2
  366. package/dist/temporal-timeline-recall.d.ts +3 -1
  367. package/dist/temporal-validity.d.ts +3 -1
  368. package/dist/threading.d.ts +3 -1
  369. package/dist/tier-migration.d.ts +4 -2
  370. package/dist/tier-routing.d.ts +3 -1
  371. package/dist/topics.d.ts +3 -1
  372. package/dist/transcript.d.ts +3 -1
  373. package/dist/transfer/types.d.ts +20 -20
  374. package/dist/trust-score-stage.d.ts +3 -1
  375. package/dist/trust-score.d.ts +3 -1
  376. package/dist/{types-Pi0lfaEt.d.ts → types-ETU5N6nB.d.ts} +433 -7
  377. package/dist/types.d.ts +3 -1
  378. package/dist/types.js +1 -1
  379. package/dist/utility-runtime.d.ts +3 -1
  380. package/dist/verified-recall.js +4 -4
  381. package/dist/who-knows-cli.d.ts +40 -40
  382. package/dist/who-knows-cli.js +5 -5
  383. package/dist/who-knows.d.ts +42 -42
  384. package/dist/who-knows.js +4 -4
  385. package/dist/write-envelope.d.ts +3 -1
  386. package/dist/write-envelope.js +1 -1
  387. package/package.json +2 -2
  388. package/src/access-boundary.ts +4 -0
  389. package/src/access-http.ts +6 -5
  390. package/src/access-mcp-admin-tools.ts +24 -0
  391. package/src/access-mcp-output-schemas.ts +14 -0
  392. package/src/access-mcp.ts +2 -2
  393. package/src/access-operations-batch.ts +5 -0
  394. package/src/access-service.ts +27 -0
  395. package/src/access-surface-catalog.test.ts +24 -0
  396. package/src/access-surface-catalog.ts +4 -0
  397. package/src/cli/skills-commands.ts +150 -0
  398. package/src/cli.ts +4 -2
  399. package/src/coding/action-gate.test.ts +341 -0
  400. package/src/coding/action-gate.ts +556 -0
  401. package/src/config.ts +9 -2
  402. package/src/connectors/codex-materialize-runner.ts +16 -0
  403. package/src/connectors/codex-materialize-skills.test.ts +141 -0
  404. package/src/connectors/codex-materialize.ts +58 -40
  405. package/src/connectors/materialize-paths.ts +50 -0
  406. package/src/connectors/materialize-skills.ts +127 -0
  407. package/src/contradiction/contradiction-review.ts +50 -4
  408. package/src/contradiction/contradiction-scan.ts +14 -3
  409. package/src/contradiction/resolution.ts +50 -2
  410. package/src/index.ts +9 -13
  411. package/src/orchestration/recall-rerank-coordinator.ts +123 -4
  412. package/src/orchestration/recall-result-formatter.ts +6 -2
  413. package/src/orchestration/recall-search-pipeline.ts +14 -1
  414. package/src/preferences/drift-config.ts +118 -0
  415. package/src/preferences/drift-recall.ts +133 -0
  416. package/src/preferences/drift-types.ts +87 -0
  417. package/src/preferences/index.ts +12 -0
  418. package/src/preferences/preference-drift.test.ts +690 -0
  419. package/src/preferences/preference-drift.ts +724 -0
  420. package/src/procedural/skill-io.test.ts +190 -0
  421. package/src/procedural/skill-io.ts +220 -0
  422. package/src/procedural/skill-projection.test.ts +214 -0
  423. package/src/procedural/skill-projection.ts +356 -0
  424. package/src/review/review-deck-access.test.ts +325 -0
  425. package/src/review/review-deck-http-base.ts +133 -0
  426. package/src/review/review-deck-mutation.test.ts +417 -0
  427. package/src/review/review-deck-mutation.ts +677 -0
  428. package/src/review/review-deck-snapshot.ts +196 -0
  429. package/src/review/review-deck-surface.ts +117 -0
  430. package/src/review/review-deck.test.ts +347 -0
  431. package/src/review/review-deck.ts +311 -0
  432. package/src/storage.ts +4 -6
  433. package/src/types.ts +9 -6
  434. package/dist/chunk-35NN3ZFA.js.map +0 -1
  435. package/dist/chunk-4KYOLI6M.js.map +0 -1
  436. package/dist/chunk-BWLRUMOG.js.map +0 -1
  437. package/dist/chunk-J275IYMG.js.map +0 -1
  438. package/dist/chunk-LGO6A7GJ.js.map +0 -1
  439. package/dist/chunk-QOS5LBZN.js.map +0 -1
  440. package/dist/chunk-RDFF2NAU.js.map +0 -1
  441. package/dist/chunk-SB7D4BT7.js.map +0 -1
  442. package/dist/chunk-SBDCZ2AX.js.map +0 -1
  443. package/dist/chunk-TF77C7SV.js.map +0 -1
  444. package/dist/chunk-TZNQUNKL.js.map +0 -1
  445. package/dist/chunk-V3MXUL3D.js.map +0 -1
  446. package/dist/chunk-XCTXS7VY.js +0 -402
  447. package/dist/chunk-XCTXS7VY.js.map +0 -1
  448. package/dist/chunk-YLDXWCZP.js.map +0 -1
  449. /package/dist/{auto-sync-H64ZCNIR.js.map → auto-sync-XVMLL2RH.js.map} +0 -0
  450. /package/dist/{chunk-YB42WZCY.js.map → chunk-2IXQSGCU.js.map} +0 -0
  451. /package/dist/{chunk-PE255F4T.js.map → chunk-2MVTAIL3.js.map} +0 -0
  452. /package/dist/{chunk-GDFVPUJ5.js.map → chunk-3NH3VP3U.js.map} +0 -0
  453. /package/dist/{chunk-GNCCUR6K.js.map → chunk-AXGFOFNY.js.map} +0 -0
  454. /package/dist/{chunk-5YQ63T4B.js.map → chunk-BK5G2MHO.js.map} +0 -0
  455. /package/dist/{chunk-LZH5FR2W.js.map → chunk-CQBIN77D.js.map} +0 -0
  456. /package/dist/{chunk-NKKD4ZAU.js.map → chunk-DORFF5N5.js.map} +0 -0
  457. /package/dist/{chunk-SQI57UCJ.js.map → chunk-DUEDOYZD.js.map} +0 -0
  458. /package/dist/{chunk-5NM4OH4P.js.map → chunk-DUZARALI.js.map} +0 -0
  459. /package/dist/{chunk-2E36UXVW.js.map → chunk-ECLJ4LA2.js.map} +0 -0
  460. /package/dist/{chunk-IID2CMUU.js.map → chunk-GHD2LLG4.js.map} +0 -0
  461. /package/dist/{chunk-ZQHI2BZB.js.map → chunk-HF5BUTI6.js.map} +0 -0
  462. /package/dist/{chunk-3DVBKTSO.js.map → chunk-HSZMX3ZJ.js.map} +0 -0
  463. /package/dist/{chunk-S2DPMJXH.js.map → chunk-I23ZHYSL.js.map} +0 -0
  464. /package/dist/{chunk-ODZG724P.js.map → chunk-I2HTKOOP.js.map} +0 -0
  465. /package/dist/{chunk-NTWN6HB2.js.map → chunk-IURMJBGP.js.map} +0 -0
  466. /package/dist/{chunk-OCZIYAE5.js.map → chunk-IWKGOCL6.js.map} +0 -0
  467. /package/dist/{chunk-UXO5IMBK.js.map → chunk-J3ATBB7M.js.map} +0 -0
  468. /package/dist/{chunk-Y7DK7HHJ.js.map → chunk-JPAKBNTM.js.map} +0 -0
  469. /package/dist/{chunk-SPUJNJJC.js.map → chunk-KFIOPZ25.js.map} +0 -0
  470. /package/dist/{chunk-JYYBA7TF.js.map → chunk-KOO37E4Y.js.map} +0 -0
  471. /package/dist/{chunk-OTHZWLUD.js.map → chunk-KPB2QHJZ.js.map} +0 -0
  472. /package/dist/{chunk-CT56C22C.js.map → chunk-LYBIHM4N.js.map} +0 -0
  473. /package/dist/{chunk-3I7ASZN3.js.map → chunk-MFOSZUIT.js.map} +0 -0
  474. /package/dist/{chunk-DZUJ46DO.js.map → chunk-NLEH4RTB.js.map} +0 -0
  475. /package/dist/{chunk-ZO6AXLEH.js.map → chunk-OCCHUEZB.js.map} +0 -0
  476. /package/dist/{chunk-EILZG5VC.js.map → chunk-OU34IQXV.js.map} +0 -0
  477. /package/dist/{chunk-2XDWWPTN.js.map → chunk-OZDFB22S.js.map} +0 -0
  478. /package/dist/{chunk-UIN2BWDD.js.map → chunk-PTZQYCJ4.js.map} +0 -0
  479. /package/dist/{chunk-Z7PBQPJ4.js.map → chunk-QJW4MZSW.js.map} +0 -0
  480. /package/dist/{chunk-R7JAM2XF.js.map → chunk-R2MZCYA6.js.map} +0 -0
  481. /package/dist/{chunk-LBWQ65SX.js.map → chunk-T42L3S6A.js.map} +0 -0
  482. /package/dist/{chunk-2YI72KM3.js.map → chunk-UCKG36HO.js.map} +0 -0
  483. /package/dist/{chunk-SIHSCCUQ.js.map → chunk-UFR6HX7A.js.map} +0 -0
  484. /package/dist/{chunk-CDUUDBWJ.js.map → chunk-VFYRYFLL.js.map} +0 -0
  485. /package/dist/{chunk-XRYZRW54.js.map → chunk-WDXD6ITO.js.map} +0 -0
  486. /package/dist/{chunk-MN4KB2A4.js.map → chunk-YDKF4MSJ.js.map} +0 -0
  487. /package/dist/{chunk-7YQ4IYS2.js.map → chunk-YOXVZJFM.js.map} +0 -0
  488. /package/dist/{chunk-ZWXJBFZU.js.map → chunk-YZJM6CLF.js.map} +0 -0
  489. /package/dist/{chunk-LUNJ3WFO.js.map → chunk-ZK4BFABK.js.map} +0 -0
  490. /package/dist/{contradiction-review-QKJH5DKD.js.map → contradiction-review-WRVLRWHS.js.map} +0 -0
  491. /package/dist/{contradiction-scan-VOTQRRSX.js.map → contradiction-scan-AKUWPX5N.js.map} +0 -0
  492. /package/dist/{resolution-MFY72STR.js.map → resolution-4552BNRV.js.map} +0 -0
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Skill-bundle section of the Codex materializer (issue #2369).
3
+ *
4
+ * Synthetic fixtures only — never real user data.
5
+ */
6
+
7
+ import test from "node:test";
8
+ import assert from "node:assert/strict";
9
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import os from "node:os";
11
+ import path from "node:path";
12
+
13
+ import {
14
+ ensureSentinel,
15
+ materializeForNamespace,
16
+ SENTINEL_FILE,
17
+ } from "./codex-materialize.js";
18
+ import { parseProcedureStepsFromBody } from "../procedural/procedure-types.js";
19
+ import { parseSkillBundle, projectProceduresToSkills } from "../procedural/skill-projection.js";
20
+ import type { MemoryFile } from "../types.js";
21
+
22
+ function makeProcedure(id: string, title: string, updated = "2026-08-18T00:00:00Z"): MemoryFile {
23
+ return {
24
+ path: `/tmp/remnic-test/procedures/${id}.md`,
25
+ frontmatter: {
26
+ id,
27
+ category: "procedure",
28
+ created: "2026-08-18T00:00:00Z",
29
+ updated,
30
+ source: "procedure-miner",
31
+ confidence: 0.8,
32
+ confidenceTier: "implied",
33
+ tags: [],
34
+ } as MemoryFile["frontmatter"],
35
+ content: [title, "", "## Step 1", "", "Read the plan.", "", "## Step 2", "", "Apply the change.", ""].join("\n"),
36
+ };
37
+ }
38
+
39
+ function makeTempCodexHome(): { root: string; memoriesDir: string } {
40
+ const root = mkdtempSync(path.join(os.tmpdir(), "codex-materialize-skills-"));
41
+ const memoriesDir = path.join(root, "memories");
42
+ mkdirSync(memoriesDir, { recursive: true });
43
+ return { root, memoriesDir };
44
+ }
45
+
46
+ test("writes skills/<slug>/SKILL.md whose body round-trips to the same steps", () => {
47
+ const { root, memoriesDir } = makeTempCodexHome();
48
+ try {
49
+ ensureSentinel(memoriesDir, "synthetic-ns", new Date("2026-08-18T00:00:00Z"));
50
+ const memories = [makeProcedure("procedure-1", "Ship a focused change")];
51
+ const skills = projectProceduresToSkills(memories);
52
+
53
+ const result = materializeForNamespace("synthetic-ns", { memories, codexHome: root, skills });
54
+ assert.equal(result.wrote, true);
55
+ assert.ok(result.filesWritten.includes(path.join("skills", "ship-a-focused-change", "SKILL.md")));
56
+
57
+ const skillPath = path.join(memoriesDir, "skills", "ship-a-focused-change", "SKILL.md");
58
+ const parsed = parseSkillBundle(readFileSync(skillPath, "utf-8"), "ship-a-focused-change");
59
+ assert.ok(parsed);
60
+ assert.deepEqual(parsed.steps, parseProcedureStepsFromBody(memories[0].content));
61
+ assert.equal(parsed.provenance.memoryId, "procedure-1");
62
+
63
+ const sentinel = JSON.parse(readFileSync(path.join(memoriesDir, SENTINEL_FILE), "utf-8")) as {
64
+ projected_skills?: string[];
65
+ };
66
+ assert.deepEqual(sentinel.projected_skills, ["ship-a-focused-change"]);
67
+ } finally {
68
+ rmSync(root, { recursive: true, force: true });
69
+ }
70
+ });
71
+
72
+ test("re-running with unchanged procedures is a hash no-op", () => {
73
+ const { root, memoriesDir } = makeTempCodexHome();
74
+ try {
75
+ ensureSentinel(memoriesDir, "synthetic-ns", new Date("2026-08-18T00:00:00Z"));
76
+ const memories = [makeProcedure("procedure-1", "Ship a focused change")];
77
+ const skills = projectProceduresToSkills(memories);
78
+
79
+ const first = materializeForNamespace("synthetic-ns", { memories, codexHome: root, skills });
80
+ assert.equal(first.wrote, true);
81
+ const second = materializeForNamespace("synthetic-ns", { memories, codexHome: root, skills });
82
+ assert.equal(second.wrote, false);
83
+ assert.equal(second.skippedIdempotent, true);
84
+ assert.deepEqual(second.filesWritten, []);
85
+ assert.ok(existsSync(path.join(memoriesDir, "skills", "ship-a-focused-change", "SKILL.md")));
86
+ } finally {
87
+ rmSync(root, { recursive: true, force: true });
88
+ }
89
+ });
90
+
91
+ test("retiring a procedure removes its projected folder and leaves hand-created folders alone", () => {
92
+ const { root, memoriesDir } = makeTempCodexHome();
93
+ try {
94
+ ensureSentinel(memoriesDir, "synthetic-ns", new Date("2026-08-18T00:00:00Z"));
95
+ const kept = makeProcedure("procedure-1", "Ship a focused change", "2026-08-18T02:00:00Z");
96
+ const retired = makeProcedure("procedure-2", "Rotate the token", "2026-08-18T01:00:00Z");
97
+
98
+ materializeForNamespace("synthetic-ns", {
99
+ memories: [kept, retired],
100
+ codexHome: root,
101
+ skills: projectProceduresToSkills([kept, retired]),
102
+ });
103
+ assert.ok(existsSync(path.join(memoriesDir, "skills", "rotate-the-token", "SKILL.md")));
104
+
105
+ // A skill folder the user curated by hand — never in projected_skills.
106
+ const handMade = path.join(memoriesDir, "skills", "hand-authored");
107
+ mkdirSync(handMade, { recursive: true });
108
+ writeFileSync(path.join(handMade, "SKILL.md"), "---\nname: hand-authored\n---\n\nMine.\n");
109
+
110
+ materializeForNamespace("synthetic-ns", {
111
+ memories: [kept],
112
+ codexHome: root,
113
+ skills: projectProceduresToSkills([kept]),
114
+ });
115
+ assert.equal(existsSync(path.join(memoriesDir, "skills", "rotate-the-token")), false);
116
+ assert.ok(existsSync(path.join(memoriesDir, "skills", "ship-a-focused-change", "SKILL.md")));
117
+ assert.ok(existsSync(path.join(handMade, "SKILL.md")));
118
+ } finally {
119
+ rmSync(root, { recursive: true, force: true });
120
+ }
121
+ });
122
+
123
+ test("omitting the skills option leaves an existing skills/ directory untouched", () => {
124
+ const { root, memoriesDir } = makeTempCodexHome();
125
+ try {
126
+ ensureSentinel(memoriesDir, "synthetic-ns", new Date("2026-08-18T00:00:00Z"));
127
+ const kept = makeProcedure("procedure-1", "Ship a focused change");
128
+ materializeForNamespace("synthetic-ns", {
129
+ memories: [kept],
130
+ codexHome: root,
131
+ skills: projectProceduresToSkills([kept]),
132
+ });
133
+
134
+ // Gate turned off on a later run: `skills` omitted entirely.
135
+ const later = makeProcedure("procedure-1", "Ship a focused change", "2026-08-19T00:00:00Z");
136
+ materializeForNamespace("synthetic-ns", { memories: [later], codexHome: root });
137
+ assert.ok(existsSync(path.join(memoriesDir, "skills", "ship-a-focused-change", "SKILL.md")));
138
+ } finally {
139
+ rmSync(root, { recursive: true, force: true });
140
+ }
141
+ });
@@ -48,7 +48,18 @@ import { log } from "../logger.js";
48
48
  import { readEnvVar, resolveHomeDir } from "../runtime/env.js";
49
49
  import type { MemoryFile } from "../types.js";
50
50
  import { estimateTokenCount } from "../token-estimate.js";
51
+ import type { SkillBundle } from "../procedural/skill-projection.js";
51
52
  import { expandTildePath } from "../utils/path.js";
53
+ import { ensureSafeManagedSubdir } from "./materialize-paths.js";
54
+ import {
55
+ commitSkillFiles,
56
+ planSkillFiles,
57
+ retiredSkillsAlreadyRemoved,
58
+ skillFilePath,
59
+ skillFileRelPath,
60
+ stageSkillFiles,
61
+ type SkillFile,
62
+ } from "./materialize-skills.js";
52
63
 
53
64
  // ─── Types ─────────────────────────────────────────────────────────────────
54
65
 
@@ -67,6 +78,13 @@ export interface MaterializeOptions {
67
78
  rolloutRetentionDays?: number;
68
79
  /** Per-session rollout summaries to render. */
69
80
  rolloutSummaries?: RolloutSummaryInput[];
81
+ /**
82
+ * Projected procedural skill bundles (issue #2369). `undefined` leaves
83
+ * `skills/` untouched; an array — even an empty one — is authoritative for
84
+ * the slugs Remnic projected, so retired procedures get their folder
85
+ * removed. Folders this materializer never projected are never touched.
86
+ */
87
+ skills?: SkillBundle[];
70
88
  /** Current time, injected for deterministic tests. */
71
89
  now?: Date;
72
90
  /** Optional logger override for tests. */
@@ -115,6 +133,12 @@ interface SentinelFile {
115
133
  namespace: string;
116
134
  updated_at: string;
117
135
  content_hash: string;
136
+ /**
137
+ * Slugs under `skills/` that THIS materializer projected (issue #2369).
138
+ * Absent on sentinels written before skill projection existed, which reads
139
+ * as "we have projected nothing", so hand-created folders stay untouched.
140
+ */
141
+ projected_skills?: string[];
118
142
  }
119
143
 
120
144
  // ─── Constants ─────────────────────────────────────────────────────────────
@@ -297,6 +321,10 @@ export function materializeForNamespace(
297
321
  const destRolloutsDir = path.join(memoriesDir, ROLLOUT_SUBDIR);
298
322
  const retainedRolloutNames = new Set(rolloutFiles.map((r) => r.name));
299
323
 
324
+ // Skill bundles (issue #2369) — planning, ownership, and disk layout live in
325
+ // materialize-skills.ts.
326
+ const skillPlan = planSkillFiles(options.skills, existingSentinel.projected_skills ?? []);
327
+
300
328
  // ── Idempotence check ──────────────────────────────────────────────────
301
329
  const hash = computeContentHash({
302
330
  namespace,
@@ -304,6 +332,7 @@ export function materializeForNamespace(
304
332
  memoryMd,
305
333
  rawMemories,
306
334
  rolloutFiles,
335
+ skillFiles: skillPlan.files,
307
336
  });
308
337
 
309
338
  if (existingSentinel.content_hash === hash) {
@@ -318,15 +347,17 @@ export function materializeForNamespace(
318
347
  path.join(memoriesDir, "MEMORY.md"),
319
348
  path.join(memoriesDir, "raw_memories.md"),
320
349
  ...rolloutFiles.map((r) => path.join(memoriesDir, ROLLOUT_SUBDIR, r.name)),
350
+ ...skillPlan.files.map((s) => skillFilePath(memoriesDir, s.slug)),
321
351
  ];
322
352
  const allPresent = requiredFiles.every((f) => fs.existsSync(f));
323
353
  const rolloutsClean =
324
354
  !rolloutsSupplied ||
325
355
  rolloutDirectoryMatchesRetainedSet(
326
- ensureSafeRolloutsDir(memoriesDir, destRolloutsDir),
356
+ ensureSafeManagedSubdir(memoriesDir, destRolloutsDir, ROLLOUT_SUBDIR),
327
357
  retainedRolloutNames,
328
358
  );
329
- if (allPresent && rolloutsClean) {
359
+ const skillsClean = retiredSkillsAlreadyRemoved(memoriesDir, skillPlan.retiredSlugs);
360
+ if (allPresent && rolloutsClean && skillsClean) {
330
361
  logger.debug?.(`no-op materialization for namespace=${namespace} (hash unchanged)`);
331
362
  return {
332
363
  namespace,
@@ -403,6 +434,11 @@ export function materializeForNamespace(
403
434
  filesWritten.push(path.join(ROLLOUT_SUBDIR, rollout.name));
404
435
  }
405
436
 
437
+ stageSkillFiles(tmpDir, skillPlan.files);
438
+ for (const skill of skillPlan.files) {
439
+ filesWritten.push(skillFileRelPath(skill.slug));
440
+ }
441
+
406
442
  // Rename into place. Atomic per-file is sufficient — Codex reads each file
407
443
  // independently and tolerates an inconsistent in-between snapshot across
408
444
  // files for the duration of the rename loop (milliseconds).
@@ -412,7 +448,7 @@ export function materializeForNamespace(
412
448
  fs.renameSync(src, dest);
413
449
  }
414
450
 
415
- const safeDestRolloutsDir = ensureSafeRolloutsDir(memoriesDir, destRolloutsDir);
451
+ const safeDestRolloutsDir = ensureSafeManagedSubdir(memoriesDir, destRolloutsDir, ROLLOUT_SUBDIR);
416
452
  // Only garbage-collect rollout files when the caller actually supplied a
417
453
  // `rolloutSummaries` array — otherwise we'd wipe legitimately
418
454
  // user/Codex-created recap files on every session-end run, since those
@@ -447,12 +483,17 @@ export function materializeForNamespace(
447
483
  fs.renameSync(src, dest);
448
484
  }
449
485
 
486
+ commitSkillFiles(memoriesDir, tmpDir, skillPlan);
487
+
450
488
  // Update sentinel last so a crash leaves hash mismatched → next run rewrites.
451
489
  const sentinel: SentinelFile = {
452
490
  version: MATERIALIZE_VERSION,
453
491
  namespace,
454
492
  updated_at: now.toISOString(),
455
493
  content_hash: hash,
494
+ projected_skills: skillPlan.supplied
495
+ ? skillPlan.files.map((s) => s.slug)
496
+ : (existingSentinel.projected_skills ?? []),
456
497
  };
457
498
  writeSentinelAtomically(sentinelPath, sentinel);
458
499
 
@@ -805,43 +846,6 @@ function rolloutDirectoryMatchesRetainedSet(
805
846
  return true;
806
847
  }
807
848
 
808
- function isPathInside(parent: string, child: string): boolean {
809
- const relative = path.relative(parent, child);
810
- return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
811
- }
812
-
813
- function ensureSafeRolloutsDir(memoriesDir: string, rolloutsDir: string): string {
814
- const memoriesReal = fs.realpathSync(memoriesDir);
815
-
816
- try {
817
- const stat = fs.lstatSync(rolloutsDir);
818
- if (stat.isSymbolicLink()) {
819
- throw new Error("is a symbolic link");
820
- }
821
- if (!stat.isDirectory()) {
822
- throw new Error("is not a directory");
823
- }
824
- } catch (err) {
825
- if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
826
- throw new Error(
827
- `codex-materialize: unsafe ${ROLLOUT_SUBDIR} directory at ${rolloutsDir}: ${
828
- err instanceof Error ? err.message : String(err)
829
- }`,
830
- );
831
- }
832
- fs.mkdirSync(rolloutsDir, { recursive: true });
833
- }
834
-
835
- const rolloutsReal = fs.realpathSync(rolloutsDir);
836
- if (!isPathInside(memoriesReal, rolloutsReal)) {
837
- throw new Error(
838
- `codex-materialize: unsafe ${ROLLOUT_SUBDIR} directory at ${rolloutsDir}: resolves outside ${memoriesDir}`,
839
- );
840
- }
841
-
842
- return rolloutsDir;
843
- }
844
-
845
849
  function readSentinel(sentinelPath: string): SentinelFile | null {
846
850
  if (!fs.existsSync(sentinelPath)) return null;
847
851
  try {
@@ -857,11 +861,18 @@ function readSentinel(sentinelPath: string): SentinelFile | null {
857
861
  ) {
858
862
  throw new Error("invalid sentinel schema");
859
863
  }
864
+ // A sentinel written before issue #2369 has no `projected_skills`, which
865
+ // reads as "nothing projected" — so an unrelated skills/ folder written by
866
+ // a user or another tool is never treated as ours.
867
+ const projected = Array.isArray(parsed.projected_skills)
868
+ ? parsed.projected_skills.filter((slug): slug is string => typeof slug === "string")
869
+ : [];
860
870
  return {
861
871
  version: parsed.version,
862
872
  namespace: parsed.namespace,
863
873
  updated_at: parsed.updated_at,
864
874
  content_hash: parsed.content_hash,
875
+ projected_skills: projected,
865
876
  };
866
877
  } catch (err) {
867
878
  throw new Error(
@@ -1040,6 +1051,7 @@ function computeContentHash(input: {
1040
1051
  memoryMd: string;
1041
1052
  rawMemories: string;
1042
1053
  rolloutFiles: Array<{ name: string; body: string }>;
1054
+ skillFiles: Array<{ slug: string; body: string }>;
1043
1055
  }): string {
1044
1056
  const hash = createHash("sha256");
1045
1057
  hash.update(`v${MATERIALIZE_VERSION}\n`);
@@ -1055,6 +1067,12 @@ function computeContentHash(input: {
1055
1067
  hash.update(`\n---rollout:${r.name}---\n`);
1056
1068
  hash.update(r.body);
1057
1069
  }
1070
+ // Sorted by slug so call order cannot flip the hash (§26).
1071
+ const sortedSkills = [...input.skillFiles].sort((a, b) => (a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0));
1072
+ for (const s of sortedSkills) {
1073
+ hash.update(`\n---skill:${s.slug}---\n`);
1074
+ hash.update(s.body);
1075
+ }
1058
1076
  return hash.digest("hex");
1059
1077
  }
1060
1078
 
@@ -0,0 +1,50 @@
1
+ /**
2
+ * materialize-paths.ts — containment guard for the materializer's managed
3
+ * sub-directories (`rollout_summaries/`, `skills/`).
4
+ *
5
+ * Extracted from codex-materialize.ts so the skills section (issue #2369) and
6
+ * the rollout section share ONE containment implementation instead of forking
7
+ * the check (rule 22).
8
+ */
9
+
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+
13
+ import { pathIsInside } from "../utils/path-containment.js";
14
+
15
+ /**
16
+ * Resolve a managed sub-directory of `memoriesDir`, creating it when absent,
17
+ * and refuse a symlinked or escaping target. Returns the (unchanged) path so
18
+ * callers can use it directly.
19
+ */
20
+ export function ensureSafeManagedSubdir(memoriesDir: string, subdir: string, label: string): string {
21
+ const memoriesReal = fs.realpathSync(memoriesDir);
22
+
23
+ try {
24
+ const stat = fs.lstatSync(subdir);
25
+ if (stat.isSymbolicLink()) {
26
+ throw new Error("is a symbolic link");
27
+ }
28
+ if (!stat.isDirectory()) {
29
+ throw new Error("is not a directory");
30
+ }
31
+ } catch (err) {
32
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
33
+ throw new Error(
34
+ `codex-materialize: unsafe ${label} directory at ${subdir}: ${
35
+ err instanceof Error ? err.message : String(err)
36
+ }`,
37
+ );
38
+ }
39
+ fs.mkdirSync(subdir, { recursive: true });
40
+ }
41
+
42
+ const subdirReal = fs.realpathSync(subdir);
43
+ if (!pathIsInside(memoriesReal, subdirReal)) {
44
+ throw new Error(
45
+ `codex-materialize: unsafe ${label} directory at ${subdir}: resolves outside ${memoriesDir}`,
46
+ );
47
+ }
48
+
49
+ return path.normalize(subdir);
50
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * materialize-skills.ts — the `skills/` section of the Codex materializer
3
+ * (issue #2369).
4
+ *
5
+ * Extracted from codex-materialize.ts (which sits at its structural LOC cap)
6
+ * so the skill staging / commit / prune rules live in one small module. Format
7
+ * and slug rules stay in the host-agnostic `procedural/skill-projection.ts`;
8
+ * this module only decides what lands on disk.
9
+ *
10
+ * Ownership rule: Remnic removes ONLY the slugs a previous run recorded in the
11
+ * sentinel's `projected_skills`. A skill folder a user (or another tool)
12
+ * created is never in that list, so it is never a removal candidate.
13
+ */
14
+
15
+ import fs from "node:fs";
16
+ import path from "node:path";
17
+
18
+ import { renderSkillBundle, SKILL_FILE_NAME, type SkillBundle } from "../procedural/skill-projection.js";
19
+ import { isValidSkillSlug } from "../skills-registry.js";
20
+ import { ensureSafeManagedSubdir } from "./materialize-paths.js";
21
+
22
+ /** Sub-directory for projected procedural skill bundles. */
23
+ export const SKILLS_SUBDIR = "skills";
24
+
25
+ export interface SkillFile {
26
+ slug: string;
27
+ body: string;
28
+ }
29
+
30
+ export interface SkillPlan {
31
+ /** False when the caller omitted `skills` — leave `skills/` untouched. */
32
+ supplied: boolean;
33
+ /** Bundles to write, deduplicated on slug. */
34
+ files: SkillFile[];
35
+ /** Previously projected slugs this run no longer projects. */
36
+ retiredSlugs: string[];
37
+ }
38
+
39
+ /**
40
+ * Decide what this run writes and removes. `undefined` bundles mean "not our
41
+ * business this run"; an empty array is authoritative and retires everything
42
+ * previously projected.
43
+ */
44
+ export function planSkillFiles(
45
+ bundles: SkillBundle[] | undefined,
46
+ previouslyProjectedSlugs: readonly string[],
47
+ ): SkillPlan {
48
+ const files: SkillFile[] = [];
49
+ const seen = new Set<string>();
50
+ for (const bundle of bundles ?? []) {
51
+ // Invalid or duplicate slugs are dropped so two bundles can never target
52
+ // one folder and leave the second rename with no source (§37).
53
+ if (!isValidSkillSlug(bundle.slug) || seen.has(bundle.slug)) continue;
54
+ seen.add(bundle.slug);
55
+ files.push({ slug: bundle.slug, body: renderSkillBundle(bundle) });
56
+ }
57
+ const previous = previouslyProjectedSlugs.filter((slug) => isValidSkillSlug(slug));
58
+ return {
59
+ supplied: bundles !== undefined,
60
+ files,
61
+ retiredSlugs: bundles === undefined ? [] : previous.filter((slug) => !seen.has(slug)),
62
+ };
63
+ }
64
+
65
+ /** Absolute path of a projected bundle's SKILL.md under `memoriesDir`. */
66
+ export function skillFilePath(memoriesDir: string, slug: string): string {
67
+ return path.join(memoriesDir, SKILLS_SUBDIR, slug, SKILL_FILE_NAME);
68
+ }
69
+
70
+ /** Relative path recorded in `MaterializeResult.filesWritten`. */
71
+ export function skillFileRelPath(slug: string): string {
72
+ return path.join(SKILLS_SUBDIR, slug, SKILL_FILE_NAME);
73
+ }
74
+
75
+ /**
76
+ * True when no retired folder is still on disk. Guards the idempotent
77
+ * early-return: a hash match is only a safe no-op if the removals also landed.
78
+ */
79
+ export function retiredSkillsAlreadyRemoved(memoriesDir: string, retiredSlugs: readonly string[]): boolean {
80
+ return retiredSlugs.every((slug) => !fs.existsSync(path.join(memoriesDir, SKILLS_SUBDIR, slug)));
81
+ }
82
+
83
+ /** Render bundles into the per-run staging directory. */
84
+ export function stageSkillFiles(tmpDir: string, files: readonly SkillFile[]): void {
85
+ for (const file of files) {
86
+ const stagedDir = path.join(tmpDir, SKILLS_SUBDIR, file.slug);
87
+ fs.mkdirSync(stagedDir, { recursive: true });
88
+ fs.writeFileSync(path.join(stagedDir, SKILL_FILE_NAME), file.body);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Remove retired folders, then rename each staged SKILL.md into place. Per-file
94
+ * renames (never directory renames) so an existing folder cannot cause
95
+ * ENOTEMPTY and so Codex never observes a half-written file.
96
+ */
97
+ export function commitSkillFiles(memoriesDir: string, tmpDir: string, plan: SkillPlan): void {
98
+ if (!plan.supplied) return;
99
+ if (plan.files.length === 0 && plan.retiredSlugs.length === 0) return;
100
+
101
+ const skillsDir = ensureSafeManagedSubdir(memoriesDir, path.join(memoriesDir, SKILLS_SUBDIR), SKILLS_SUBDIR);
102
+ for (const slug of plan.retiredSlugs) {
103
+ const retiredDir = path.join(skillsDir, slug);
104
+ try {
105
+ // Never follow a symlink out of the managed tree while pruning.
106
+ if (fs.lstatSync(retiredDir).isSymbolicLink()) continue;
107
+ } catch (err) {
108
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") continue;
109
+ throw err;
110
+ }
111
+ try {
112
+ fs.rmSync(retiredDir, { recursive: true, force: true });
113
+ } catch (err) {
114
+ throw new Error(
115
+ `codex-materialize: failed to remove retired skill ${slug}: ${err instanceof Error ? err.message : String(err)}`,
116
+ );
117
+ }
118
+ }
119
+ for (const file of plan.files) {
120
+ const destDir = path.join(skillsDir, file.slug);
121
+ fs.mkdirSync(destDir, { recursive: true });
122
+ fs.renameSync(
123
+ path.join(tmpDir, SKILLS_SUBDIR, file.slug, SKILL_FILE_NAME),
124
+ path.join(destDir, SKILL_FILE_NAME),
125
+ );
126
+ }
127
+ }
@@ -17,9 +17,31 @@ import type { ContradictionVerdict } from "./contradiction-judge.js";
17
17
 
18
18
  // ── Types ──────────────────────────────────────────────────────────────────────
19
19
 
20
- export type ResolutionVerb = "keep-a" | "keep-b" | "merge" | "both-valid" | "needs-more-context";
21
-
22
- const VALID_RESOLUTION_VERBS: readonly ResolutionVerb[] = [
20
+ /**
21
+ * Resolution verbs. The first five are contradiction-pair verbs; the last
22
+ * three resolve a `kind: "preference-drift"` item (issue #2371). Verb validity
23
+ * is per-kind — `executeResolution` rejects a cross-kind verb — but the stored
24
+ * union is shared so one review queue, one `review_resolve` surface, and one
25
+ * resolution record serve both item kinds.
26
+ */
27
+ export type ResolutionVerb =
28
+ | "keep-a"
29
+ | "keep-b"
30
+ | "merge"
31
+ | "both-valid"
32
+ | "needs-more-context"
33
+ | "keep"
34
+ | "supersede"
35
+ | "archive";
36
+
37
+ /** Review-item kind. Absent on every pair written before issue #2371. */
38
+ export type ReviewItemKind = "contradiction" | "preference-drift";
39
+
40
+ /** Verbs valid for a `kind: "preference-drift"` item. */
41
+ export const PREFERENCE_DRIFT_VERBS: readonly ResolutionVerb[] = ["keep", "supersede", "archive"];
42
+
43
+ /** Verbs valid for a contradiction pair. */
44
+ export const CONTRADICTION_VERBS: readonly ResolutionVerb[] = [
23
45
  "keep-a",
24
46
  "keep-b",
25
47
  "merge",
@@ -27,11 +49,23 @@ const VALID_RESOLUTION_VERBS: readonly ResolutionVerb[] = [
27
49
  "needs-more-context",
28
50
  ];
29
51
 
52
+ const VALID_RESOLUTION_VERBS: readonly ResolutionVerb[] = [
53
+ ...CONTRADICTION_VERBS,
54
+ ...PREFERENCE_DRIFT_VERBS,
55
+ ];
56
+
30
57
  export interface ContradictionPair {
31
58
  /** Deterministic pair ID: sha256(sorted(memoryIdA, memoryIdB) plus namespace when scoped). */
32
59
  pairId: string;
33
60
  /** Memory IDs (sorted). */
34
61
  memoryIds: [string, string];
62
+ /**
63
+ * Item kind (issue #2371). Absent on every pair written before drift
64
+ * detection existed, which reads as `"contradiction"`. A
65
+ * `"preference-drift"` item pairs the aging preference (first id) with the
66
+ * recent evidence that pointed away from it (second id).
67
+ */
68
+ kind?: ReviewItemKind;
35
69
  /** Judge verdict. */
36
70
  verdict: ContradictionVerdict;
37
71
  /** Judge rationale. */
@@ -85,8 +119,20 @@ export function isDefaultReviewNamespace(
85
119
  return !requested || requested === defaultNamespace || resolvedNamespace === defaultNamespace;
86
120
  }
87
121
 
122
+ const TERMINAL_RESOLUTIONS: Partial<Record<ResolutionVerb, true>> = {
123
+ "keep-a": true,
124
+ "keep-b": true,
125
+ merge: true,
126
+ // Every preference-drift verb is terminal: the user has answered the "is
127
+ // this preference still true?" question, so the item must leave the
128
+ // unresolved queue and never be re-proposed by a later scan (issue #2371).
129
+ keep: true,
130
+ supersede: true,
131
+ archive: true,
132
+ };
133
+
88
134
  function isTerminalResolution(resolution: ResolutionVerb | undefined): boolean {
89
- return resolution === "keep-a" || resolution === "keep-b" || resolution === "merge";
135
+ return resolution !== undefined && TERMINAL_RESOLUTIONS[resolution] === true;
90
136
  }
91
137
 
92
138
  function preservesDirectResolution(resolution: ResolutionVerb | undefined): boolean {
@@ -380,8 +380,16 @@ function normalizeScanNamespace(namespace: string | undefined): string | undefin
380
380
  return trimmed ? trimmed : undefined;
381
381
  }
382
382
 
383
- async function resolveScanStorage(
384
- deps: ScanDependencies,
383
+ /**
384
+ * Resolve the namespace-scoped storage a scan must read and write through.
385
+ *
386
+ * Exported and narrowed to the fields it actually reads so sibling scans
387
+ * (preference drift, issue #2371) share ONE namespace resolver rather than
388
+ * re-deriving the read/write namespace independently — the divergence the
389
+ * namespace/ACL scenario matrix in AGENTS.md exists to prevent.
390
+ */
391
+ export async function resolveScanStorage(
392
+ deps: Pick<ScanDependencies, "config" | "storage" | "storageForNamespace">,
385
393
  namespace: string | undefined,
386
394
  ): Promise<ScanStorageResolution> {
387
395
  if (!resolveNamespaceCapabilities(deps.config).namespaces) {
@@ -427,7 +435,10 @@ function isStorageManagerLike(value: unknown): value is StorageManager {
427
435
  return typeof candidate.readAllMemories === "function";
428
436
  }
429
437
 
430
- function fallbackResolvedNamespace(deps: ScanDependencies, namespace: string | undefined): string | undefined {
438
+ function fallbackResolvedNamespace(
439
+ deps: Pick<ScanDependencies, "config">,
440
+ namespace: string | undefined,
441
+ ): string | undefined {
431
442
  if (namespace) return namespace;
432
443
  return resolveNamespaceCapabilities(deps.config).namespaces ? deps.config.defaultNamespace : undefined;
433
444
  }