@remnic/core 9.65.3 → 9.65.5

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 (436) hide show
  1. package/dist/access-admin-ops-surface.d.ts +11 -11
  2. package/dist/access-admin-ops-surface.js +20 -18
  3. package/dist/access-authorization-probe.d.ts +11 -11
  4. package/dist/access-authorization-probe.js +21 -19
  5. package/dist/access-boundary.d.ts +11 -11
  6. package/dist/access-boundary.js +20 -18
  7. package/dist/access-cli.js +55 -53
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +1 -1
  10. package/dist/access-extraction-force-flush.d.ts +11 -11
  11. package/dist/access-extraction-force-flush.js +20 -18
  12. package/dist/access-health-types.d.ts +1 -1
  13. package/dist/access-http-lcm-compaction.d.ts +11 -11
  14. package/dist/access-http-lifecycle-flush.d.ts +11 -11
  15. package/dist/access-http-offline-stream.d.ts +11 -11
  16. package/dist/access-http-query.d.ts +2 -2
  17. package/dist/access-http-query.js +22 -20
  18. package/dist/access-http.d.ts +12 -12
  19. package/dist/access-http.js +31 -29
  20. package/dist/access-identity-continuity-surface.d.ts +9 -9
  21. package/dist/access-identity-continuity-surface.js +20 -18
  22. package/dist/access-lcm-surface.d.ts +11 -11
  23. package/dist/access-lcm-surface.js +20 -18
  24. package/dist/access-mcp.d.ts +11 -11
  25. package/dist/access-mcp.js +25 -23
  26. package/dist/access-memory-search-fanout.d.ts +2 -2
  27. package/dist/{access-namespace-preflight-B7Usyz-K.d.ts → access-namespace-preflight-D_6mo8ms.d.ts} +1 -1
  28. package/dist/access-namespace-preflight.d.ts +3 -3
  29. package/dist/access-namespace-preflight.js +20 -18
  30. package/dist/access-observe-write-surface.d.ts +11 -11
  31. package/dist/access-observe-write-surface.js +20 -18
  32. package/dist/access-offline-manifest.d.ts +9 -9
  33. package/dist/access-offline-manifest.js +3 -3
  34. package/dist/access-operations-batch.js +21 -19
  35. package/dist/access-operations.d.ts +16 -16
  36. package/dist/access-operations.js +24 -22
  37. package/dist/access-recall-concurrency.d.ts +11 -11
  38. package/dist/access-recall-concurrency.js +20 -18
  39. package/dist/access-recall-response.d.ts +11 -11
  40. package/dist/access-recall-response.js +20 -18
  41. package/dist/access-recall-surface.d.ts +11 -11
  42. package/dist/access-recall-surface.js +20 -18
  43. package/dist/access-schema.d.ts +83 -83
  44. package/dist/access-service-helpers.d.ts +11 -11
  45. package/dist/{access-service-DkaIG3n8.d.ts → access-service-sbiNS5UB.d.ts} +71 -71
  46. package/dist/access-service.d.ts +11 -11
  47. package/dist/access-service.js +20 -18
  48. package/dist/access-surface-catalog.d.ts +11 -11
  49. package/dist/access-surface-catalog.js +22 -20
  50. package/dist/access-surface-catalog.js.map +1 -1
  51. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  52. package/dist/action-confidence.d.ts +1 -1
  53. package/dist/active-memory-bridge.d.ts +1 -1
  54. package/dist/active-recall.d.ts +1 -1
  55. package/dist/active-recall.js +5 -1
  56. package/dist/active-recall.js.map +1 -1
  57. package/dist/ambient-provenance.d.ts +1 -1
  58. package/dist/artifact-search.d.ts +1 -1
  59. package/dist/{auto-sync-H64ZCNIR.js → auto-sync-XVMLL2RH.js} +5 -5
  60. package/dist/behavior-learner.d.ts +1 -1
  61. package/dist/behavior-signals.d.ts +1 -1
  62. package/dist/bootstrap.d.ts +9 -9
  63. package/dist/briefing-window.d.ts +2 -2
  64. package/dist/briefing.d.ts +2 -2
  65. package/dist/briefing.js +4 -4
  66. package/dist/buffer-surprise-report.d.ts +1 -1
  67. package/dist/buffer-turn-helpers.d.ts +1 -1
  68. package/dist/buffer.d.ts +2 -2
  69. package/dist/bulk-import/index.d.ts +3 -3
  70. package/dist/calibration.d.ts +1 -1
  71. package/dist/capabilities.d.ts +1 -1
  72. package/dist/{catalog-CX2BZEJh.d.ts → catalog-DO_7zfkv.d.ts} +1 -1
  73. package/dist/causal-behavior.d.ts +1 -1
  74. package/dist/causal-consolidation.d.ts +1 -1
  75. package/dist/causal-consolidation.js +9 -6
  76. package/dist/causal-consolidation.js.map +1 -1
  77. package/dist/causal-trajectory-graph.d.ts +1 -1
  78. package/dist/chunk-236PXYG7.js +232 -0
  79. package/dist/chunk-236PXYG7.js.map +1 -0
  80. package/dist/{chunk-YF6UHWOV.js → chunk-2EPOGPKO.js} +3 -3
  81. package/dist/{chunk-IQSC3V2E.js → chunk-2IXQSGCU.js} +2 -2
  82. package/dist/{chunk-GUXS322H.js → chunk-2MVTAIL3.js} +2 -2
  83. package/dist/{chunk-TDKSCV62.js → chunk-3NH3VP3U.js} +2 -2
  84. package/dist/{chunk-6TAX66IA.js → chunk-4JSF7UUP.js} +2 -2
  85. package/dist/{chunk-URHCUXKI.js → chunk-AXGFOFNY.js} +2 -2
  86. package/dist/{chunk-BKYPJUQL.js → chunk-BA2GLSIP.js} +10 -10
  87. package/dist/{chunk-D27J626P.js → chunk-BK5G2MHO.js} +2 -2
  88. package/dist/{chunk-EV3YFLCB.js → chunk-BW4GAF57.js} +2 -2
  89. package/dist/{chunk-DHTQP334.js → chunk-CQBIN77D.js} +2 -2
  90. package/dist/{chunk-WN5FSGLE.js → chunk-CV3GNPOE.js} +45 -50
  91. package/dist/chunk-CV3GNPOE.js.map +1 -0
  92. package/dist/{chunk-6VALIZKG.js → chunk-DORFF5N5.js} +2 -2
  93. package/dist/{chunk-7QOO4LGC.js → chunk-DPGH6LHQ.js} +331 -73
  94. package/dist/chunk-DPGH6LHQ.js.map +1 -0
  95. package/dist/{chunk-76PL4XLG.js → chunk-DPNXADEI.js} +7 -2
  96. package/dist/chunk-DPNXADEI.js.map +1 -0
  97. package/dist/{chunk-MRTUN665.js → chunk-DUEDOYZD.js} +2 -2
  98. package/dist/{chunk-QFMO32TO.js → chunk-DUZARALI.js} +3 -3
  99. package/dist/{chunk-7KFIMGMP.js → chunk-ECLJ4LA2.js} +2 -2
  100. package/dist/{chunk-PDVQ5JU3.js → chunk-FBR7PIL4.js} +2 -2
  101. package/dist/{chunk-RK4WXUI2.js → chunk-HF5BUTI6.js} +2 -2
  102. package/dist/{chunk-3DVBKTSO.js → chunk-HSZMX3ZJ.js} +4 -4
  103. package/dist/{chunk-ODZG724P.js → chunk-I2HTKOOP.js} +2 -2
  104. package/dist/{chunk-GN55KU2P.js → chunk-IIRCHGWD.js} +2 -2
  105. package/dist/{chunk-AYYGRVRP.js → chunk-IURMJBGP.js} +2 -2
  106. package/dist/{chunk-4XDGYCZI.js → chunk-J3ATBB7M.js} +2 -2
  107. package/dist/{chunk-E4MVQQVX.js → chunk-JPAKBNTM.js} +2 -2
  108. package/dist/{chunk-NZWEEALH.js → chunk-KFIOPZ25.js} +2 -2
  109. package/dist/{chunk-EIVIB73I.js → chunk-KOO37E4Y.js} +2 -2
  110. package/dist/{chunk-VA5KMSAE.js → chunk-KTPXXVDQ.js} +3 -3
  111. package/dist/{chunk-NXKJHSDN.js → chunk-LBFFNDFM.js} +1 -1
  112. package/dist/chunk-LBFFNDFM.js.map +1 -0
  113. package/dist/{chunk-Z4RPXHJH.js → chunk-LYBIHM4N.js} +4 -4
  114. package/dist/{chunk-G57LQRP6.js → chunk-MFOSZUIT.js} +2 -2
  115. package/dist/{chunk-TYUS5TLG.js → chunk-N56GVOWR.js} +3 -3
  116. package/dist/{chunk-BQUEAVNW.js → chunk-NH6LC57D.js} +2 -2
  117. package/dist/{chunk-AUO2LJ3K.js → chunk-NLEH4RTB.js} +2 -2
  118. package/dist/{chunk-AYDDB3XI.js → chunk-OCCHUEZB.js} +2 -2
  119. package/dist/{chunk-57HVIG5U.js → chunk-OWQNJJY2.js} +2 -2
  120. package/dist/{chunk-OJD4WS2E.js → chunk-OZDFB22S.js} +2 -2
  121. package/dist/{chunk-2VXWFYJE.js → chunk-R2MZCYA6.js} +3 -3
  122. package/dist/{chunk-LBWQ65SX.js → chunk-T42L3S6A.js} +4 -4
  123. package/dist/{chunk-PAYGC3BD.js → chunk-THVI4Y7X.js} +7 -7
  124. package/dist/{chunk-S7Q5CM7E.js → chunk-TJFI3K2O.js} +15 -3
  125. package/dist/chunk-TJFI3K2O.js.map +1 -0
  126. package/dist/{chunk-R47WINCY.js → chunk-UCKG36HO.js} +2 -2
  127. package/dist/{chunk-QXIQPCCN.js → chunk-UFR6HX7A.js} +1 -1
  128. package/dist/{chunk-CDUUDBWJ.js → chunk-VFYRYFLL.js} +2 -2
  129. package/dist/{chunk-P4DHENRE.js → chunk-WDXD6ITO.js} +2 -2
  130. package/dist/{chunk-H5FW4S5Q.js → chunk-YB4GBWBU.js} +31 -31
  131. package/dist/{chunk-MBDJJTAT.js → chunk-YDKF4MSJ.js} +3 -3
  132. package/dist/{chunk-AZAA5DNP.js → chunk-YOXVZJFM.js} +2 -2
  133. package/dist/{chunk-OLWTCWQ2.js → chunk-YZJM6CLF.js} +8 -8
  134. package/dist/chunk-ZCUTSMR2.js +61 -0
  135. package/dist/chunk-ZCUTSMR2.js.map +1 -0
  136. package/dist/{chunk-LUNJ3WFO.js → chunk-ZK4BFABK.js} +4 -4
  137. package/dist/{chunk-RDFF2NAU.js → chunk-ZXBTTMGZ.js} +184 -88
  138. package/dist/chunk-ZXBTTMGZ.js.map +1 -0
  139. package/dist/{cli-ChKeYMZv.d.ts → cli-BqdqPdG_.d.ts} +5 -5
  140. package/dist/cli.d.ts +13 -13
  141. package/dist/cli.js +44 -42
  142. package/dist/coding/pre-action-gate.d.ts +1 -1
  143. package/dist/compounding/engine.d.ts +2 -2
  144. package/dist/compounding/engine.js +4 -4
  145. package/dist/compounding/preference-consolidator.d.ts +1 -1
  146. package/dist/compression-optimizer.d.ts +1 -1
  147. package/dist/config.d.ts +1 -1
  148. package/dist/config.js +5 -1
  149. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  150. package/dist/connectors/codex-materialize-runner.js +8 -5
  151. package/dist/connectors/codex-materialize.d.ts +14 -1
  152. package/dist/connectors/codex-materialize.js +10 -1
  153. package/dist/connectors/index.d.ts +1 -1
  154. package/dist/connectors/index.js +8 -5
  155. package/dist/consolidation-provenance-check.d.ts +2 -2
  156. package/dist/consolidation-undo.d.ts +2 -2
  157. package/dist/contradiction/index.d.ts +3 -3
  158. package/dist/contradiction/index.js +1 -1
  159. package/dist/converge-config.d.ts +1 -1
  160. package/dist/convergence-refresh.d.ts +2 -2
  161. package/dist/convergence-refresh.js +6 -6
  162. package/dist/conversation-index/backend.d.ts +1 -1
  163. package/dist/conversation-index/chunker.d.ts +1 -1
  164. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  165. package/dist/conversation-index/indexer.d.ts +1 -1
  166. package/dist/conversation-index/search.d.ts +1 -1
  167. package/dist/corpus-watermark.d.ts +1 -1
  168. package/dist/corpus-watermark.js +6 -6
  169. package/dist/cross-lingual-recall.d.ts +1 -1
  170. package/dist/day-summary.d.ts +1 -1
  171. package/dist/delinearize.d.ts +1 -1
  172. package/dist/dependency-propagation-config.d.ts +1 -1
  173. package/dist/{dependency-propagation-delivery-B8ZMBBCY.d.ts → dependency-propagation-delivery-D23_x-s5.d.ts} +1 -1
  174. package/dist/direct-answer-wiring.d.ts +1 -1
  175. package/dist/direct-answer.d.ts +1 -1
  176. package/dist/embedding-fallback.d.ts +1 -1
  177. package/dist/enrichment/index.d.ts +1 -1
  178. package/dist/entity-origin-fields.d.ts +1 -1
  179. package/dist/entity-retrieval.d.ts +2 -2
  180. package/dist/entity-retrieval.js +4 -4
  181. package/dist/entity-schema.d.ts +1 -1
  182. package/dist/episodic-context.d.ts +1 -1
  183. package/dist/explicit-capture.d.ts +9 -9
  184. package/dist/explicit-capture.js +1 -1
  185. package/dist/external-wiki-access.d.ts +11 -11
  186. package/dist/external-wiki-access.js +21 -19
  187. package/dist/external-wiki-collection-registration.d.ts +1 -1
  188. package/dist/external-wiki-collection.d.ts +1 -1
  189. package/dist/external-wiki-mcp-tools.d.ts +11 -11
  190. package/dist/extraction-error-classification.d.ts +1 -1
  191. package/dist/extraction-faithfulness.d.ts +1 -1
  192. package/dist/extraction-judge-telemetry.d.ts +1 -1
  193. package/dist/extraction-judge-training.d.ts +1 -1
  194. package/dist/extraction-judge.d.ts +1 -1
  195. package/dist/extraction-judge.js +2 -2
  196. package/dist/extraction-liveness.d.ts +1 -1
  197. package/dist/extraction-normalization.d.ts +1 -1
  198. package/dist/extraction-prompt.d.ts +1 -1
  199. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  200. package/dist/extraction-source-grounding.d.ts +1 -1
  201. package/dist/extraction.d.ts +1 -1
  202. package/dist/extraction.js +9 -7
  203. package/dist/fallback-llm.d.ts +1 -1
  204. package/dist/graph-dashboard-diff.d.ts +1 -1
  205. package/dist/graph-dashboard-key.d.ts +1 -1
  206. package/dist/graph-dashboard-parser.d.ts +1 -1
  207. package/dist/graph-edge-reinforcement.d.ts +1 -1
  208. package/dist/graph-path-reconstruction.d.ts +1 -1
  209. package/dist/graph-path-scoring.d.ts +1 -1
  210. package/dist/graph-snapshot.d.ts +1 -1
  211. package/dist/graph.d.ts +1 -1
  212. package/dist/harmonic-retrieval.js +1 -1
  213. package/dist/importance.d.ts +1 -1
  214. package/dist/importers/index.d.ts +1 -1
  215. package/dist/in-flight-reads.d.ts +1 -1
  216. package/dist/{index-CexDrJzC.d.ts → index--QhGIXF2.d.ts} +2 -2
  217. package/dist/index.d.ts +763 -716
  218. package/dist/index.js +79 -58
  219. package/dist/intent.d.ts +1 -1
  220. package/dist/lcm/engine.d.ts +1 -1
  221. package/dist/lcm/index.d.ts +1 -1
  222. package/dist/lcm/tools.d.ts +1 -1
  223. package/dist/lifecycle.d.ts +1 -1
  224. package/dist/live-connectors-runner.d.ts +1 -1
  225. package/dist/local-llm.d.ts +1 -1
  226. package/dist/local-model-endpoint.d.ts +1 -1
  227. package/dist/maintenance/memory-governance.d.ts +1 -1
  228. package/dist/maintenance/memory-governance.js +4 -4
  229. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  230. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -4
  231. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  232. package/dist/maintenance/rebuild-memory-projection.js +5 -5
  233. package/dist/{maintenance-nkdtSrVr.d.ts → maintenance-PFvY1Pw_.d.ts} +3 -3
  234. package/dist/mcp-memory-inspector-app.d.ts +11 -11
  235. package/dist/memory-action-policy.d.ts +1 -1
  236. package/dist/memory-cache.d.ts +1 -1
  237. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  238. package/dist/memory-projection-store.d.ts +1 -1
  239. package/dist/memory-provenance.d.ts +1 -1
  240. package/dist/memory-snapshot.d.ts +1 -1
  241. package/dist/memory-subject-cli.d.ts +11 -11
  242. package/dist/memory-subject.d.ts +11 -11
  243. package/dist/memory-worth-outcomes.d.ts +2 -2
  244. package/dist/models-json.d.ts +1 -1
  245. package/dist/namespaces/migrate.d.ts +3 -3
  246. package/dist/namespaces/migrate.js +5 -5
  247. package/dist/namespaces/principal.d.ts +1 -1
  248. package/dist/namespaces/search.d.ts +1 -1
  249. package/dist/namespaces/storage.d.ts +3 -3
  250. package/dist/namespaces/storage.js +4 -4
  251. package/dist/native-knowledge.d.ts +1 -1
  252. package/dist/offline-sync-impression-drain.d.ts +1 -1
  253. package/dist/operator-doctor-corpus.d.ts +1 -1
  254. package/dist/operator-doctor-corpus.js +7 -7
  255. package/dist/operator-doctor-replica.d.ts +1 -1
  256. package/dist/operator-toolkit.d.ts +3 -3
  257. package/dist/operator-toolkit.js +16 -13
  258. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  259. package/dist/orchestration/maintenance.d.ts +4 -4
  260. package/dist/orchestration/maintenance.js +10 -10
  261. package/dist/{orchestrator-Bvq1-RQs.d.ts → orchestrator-4wzJrPVm.d.ts} +8 -8
  262. package/dist/orchestrator.d.ts +9 -9
  263. package/dist/orchestrator.js +55 -53
  264. package/dist/patterns-cli.d.ts +1 -1
  265. package/dist/{pipeline-C8vfIiTX.d.ts → pipeline-CgXgdIrF.d.ts} +1 -1
  266. package/dist/policy-runtime.d.ts +1 -1
  267. package/dist/proactive-contention.d.ts +1 -1
  268. package/dist/provenance.d.ts +1 -1
  269. package/dist/{public-http-Cy0aYbys.d.ts → public-http-BP3VQDl5.d.ts} +1 -1
  270. package/dist/{qmd-B9p1SZTZ.d.ts → qmd-BppGGxsi.d.ts} +1 -1
  271. package/dist/qmd-preflight.d.ts +2 -2
  272. package/dist/qmd-recall-cache.d.ts +1 -1
  273. package/dist/qmd.d.ts +2 -2
  274. package/dist/recall-concurrency-config.d.ts +1 -1
  275. package/dist/recall-disclosure-escalation.d.ts +1 -1
  276. package/dist/recall-explain-renderer.d.ts +1 -1
  277. package/dist/recall-explain-renderer.js +3 -3
  278. package/dist/recall-memory-map.d.ts +1 -1
  279. package/dist/recall-planner-llm.d.ts +1 -1
  280. package/dist/recall-state.d.ts +1 -1
  281. package/dist/recall-tag-filter.d.ts +1 -1
  282. package/dist/recall-timings.d.ts +1 -1
  283. package/dist/recall-xray-cli.d.ts +1 -1
  284. package/dist/recall-xray-cli.js +4 -4
  285. package/dist/recall-xray-renderer.d.ts +1 -1
  286. package/dist/recall-xray-renderer.js +3 -3
  287. package/dist/recall-xray.d.ts +1 -1
  288. package/dist/recall-xray.js +2 -2
  289. package/dist/reconcile/cursor.d.ts +1 -1
  290. package/dist/reconcile/manifest.d.ts +1 -1
  291. package/dist/reconcile/manifest.js +2 -2
  292. package/dist/reconcile/plan.d.ts +1 -1
  293. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  294. package/dist/replay/normalizers/claude.d.ts +1 -1
  295. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  296. package/dist/replay/normalizers/shared.d.ts +1 -1
  297. package/dist/replay/runner.d.ts +1 -1
  298. package/dist/replay/types.d.ts +1 -1
  299. package/dist/replica-divergence.d.ts +1 -1
  300. package/dist/replica-peers-config.d.ts +1 -1
  301. package/dist/resolution-4552BNRV.js +1 -1
  302. package/dist/resolve-auth-token.d.ts +1 -1
  303. package/dist/resume-bundles.js +6 -2
  304. package/dist/retrieval-agents.d.ts +2 -2
  305. package/dist/retrieval-tiers.d.ts +1 -1
  306. package/dist/routing/engine.d.ts +1 -1
  307. package/dist/routing/store.d.ts +1 -1
  308. package/dist/salvage-envelope.d.ts +1 -1
  309. package/dist/salvage-envelope.js +1 -1
  310. package/dist/schemas.d.ts +136 -136
  311. package/dist/{scope-profiles-DLrRxFWR.d.ts → scope-profiles-ChZUtxDU.d.ts} +1 -1
  312. package/dist/search/embed-helper.d.ts +1 -1
  313. package/dist/search/factory.d.ts +1 -1
  314. package/dist/search/index.d.ts +1 -1
  315. package/dist/search/lancedb-backend.d.ts +1 -1
  316. package/dist/search/meilisearch-backend.d.ts +1 -1
  317. package/dist/search/noop-backend.d.ts +1 -1
  318. package/dist/search/orama-backend.d.ts +1 -1
  319. package/dist/search/port.d.ts +1 -1
  320. package/dist/search/remote-backend.d.ts +1 -1
  321. package/dist/{semantic-consolidation-DlN6xe20.d.ts → semantic-consolidation-mSPwQSmF.d.ts} +1 -1
  322. package/dist/semantic-consolidation.d.ts +2 -2
  323. package/dist/semantic-consolidation.js +9 -6
  324. package/dist/semantic-rule-promotion.js +4 -4
  325. package/dist/semantic-rule-verifier.d.ts +1 -1
  326. package/dist/semantic-rule-verifier.js +4 -4
  327. package/dist/{service-GkYyVfm9.d.ts → service-BICWLwSP.d.ts} +2 -2
  328. package/dist/session-observer-bands.d.ts +1 -1
  329. package/dist/session-observer-state.d.ts +1 -1
  330. package/dist/shared-context/manager.d.ts +7 -7
  331. package/dist/signal.d.ts +1 -1
  332. package/dist/skills-registry.js +4 -56
  333. package/dist/skills-registry.js.map +1 -1
  334. package/dist/source-agent-qualifier.d.ts +1 -1
  335. package/dist/source-agent-qualifier.js +8 -5
  336. package/dist/{storage-BIDzuKIn.d.ts → storage-B7urx1K3.d.ts} +1 -1
  337. package/dist/storage.d.ts +2 -2
  338. package/dist/storage.js +6 -6
  339. package/dist/subject-config.d.ts +1 -1
  340. package/dist/summarizer.d.ts +1 -1
  341. package/dist/summary-snapshot.d.ts +1 -1
  342. package/dist/support-passport/index.d.ts +14 -14
  343. package/dist/support-passport/index.js +23 -21
  344. package/dist/temporal-supersession.d.ts +2 -2
  345. package/dist/temporal-timeline-recall.d.ts +1 -1
  346. package/dist/temporal-validity.d.ts +1 -1
  347. package/dist/threading.d.ts +1 -1
  348. package/dist/tier-migration.d.ts +2 -2
  349. package/dist/tier-routing.d.ts +1 -1
  350. package/dist/topics.d.ts +1 -1
  351. package/dist/transcript.d.ts +1 -1
  352. package/dist/transfer/types.d.ts +78 -78
  353. package/dist/trust-score-stage.d.ts +1 -1
  354. package/dist/trust-score.d.ts +1 -1
  355. package/dist/{types-BxAcOl5Q.d.ts → types-ETU5N6nB.d.ts} +111 -4
  356. package/dist/types.d.ts +1 -1
  357. package/dist/types.js +1 -1
  358. package/dist/utility-runtime.d.ts +1 -1
  359. package/dist/verified-recall.js +4 -4
  360. package/dist/who-knows-cli.d.ts +11 -11
  361. package/dist/who-knows-cli.js +5 -5
  362. package/dist/who-knows.d.ts +11 -11
  363. package/dist/who-knows.js +4 -4
  364. package/dist/write-envelope.d.ts +1 -1
  365. package/dist/write-envelope.js +1 -1
  366. package/package.json +2 -2
  367. package/src/cli/skills-commands.ts +150 -0
  368. package/src/cli.ts +4 -2
  369. package/src/config.ts +4 -2
  370. package/src/connectors/codex-materialize-runner.ts +16 -0
  371. package/src/connectors/codex-materialize-skills.test.ts +141 -0
  372. package/src/connectors/codex-materialize.ts +58 -40
  373. package/src/connectors/materialize-paths.ts +50 -0
  374. package/src/connectors/materialize-skills.ts +127 -0
  375. package/src/index.ts +5 -10
  376. package/src/procedural/skill-io.test.ts +190 -0
  377. package/src/procedural/skill-io.ts +220 -0
  378. package/src/procedural/skill-projection.test.ts +214 -0
  379. package/src/procedural/skill-projection.ts +356 -0
  380. package/src/review/frontmatter.test.ts +15 -0
  381. package/src/review/frontmatter.ts +20 -0
  382. package/src/review/review-deck-snapshot.ts +4 -20
  383. package/src/review/review-deck.ts +1 -12
  384. package/src/types.ts +2 -3
  385. package/dist/chunk-76PL4XLG.js.map +0 -1
  386. package/dist/chunk-7QOO4LGC.js.map +0 -1
  387. package/dist/chunk-NXKJHSDN.js.map +0 -1
  388. package/dist/chunk-RDFF2NAU.js.map +0 -1
  389. package/dist/chunk-S7Q5CM7E.js.map +0 -1
  390. package/dist/chunk-WN5FSGLE.js.map +0 -1
  391. /package/dist/{auto-sync-H64ZCNIR.js.map → auto-sync-XVMLL2RH.js.map} +0 -0
  392. /package/dist/{chunk-YF6UHWOV.js.map → chunk-2EPOGPKO.js.map} +0 -0
  393. /package/dist/{chunk-IQSC3V2E.js.map → chunk-2IXQSGCU.js.map} +0 -0
  394. /package/dist/{chunk-GUXS322H.js.map → chunk-2MVTAIL3.js.map} +0 -0
  395. /package/dist/{chunk-TDKSCV62.js.map → chunk-3NH3VP3U.js.map} +0 -0
  396. /package/dist/{chunk-6TAX66IA.js.map → chunk-4JSF7UUP.js.map} +0 -0
  397. /package/dist/{chunk-URHCUXKI.js.map → chunk-AXGFOFNY.js.map} +0 -0
  398. /package/dist/{chunk-BKYPJUQL.js.map → chunk-BA2GLSIP.js.map} +0 -0
  399. /package/dist/{chunk-D27J626P.js.map → chunk-BK5G2MHO.js.map} +0 -0
  400. /package/dist/{chunk-EV3YFLCB.js.map → chunk-BW4GAF57.js.map} +0 -0
  401. /package/dist/{chunk-DHTQP334.js.map → chunk-CQBIN77D.js.map} +0 -0
  402. /package/dist/{chunk-6VALIZKG.js.map → chunk-DORFF5N5.js.map} +0 -0
  403. /package/dist/{chunk-MRTUN665.js.map → chunk-DUEDOYZD.js.map} +0 -0
  404. /package/dist/{chunk-QFMO32TO.js.map → chunk-DUZARALI.js.map} +0 -0
  405. /package/dist/{chunk-7KFIMGMP.js.map → chunk-ECLJ4LA2.js.map} +0 -0
  406. /package/dist/{chunk-PDVQ5JU3.js.map → chunk-FBR7PIL4.js.map} +0 -0
  407. /package/dist/{chunk-RK4WXUI2.js.map → chunk-HF5BUTI6.js.map} +0 -0
  408. /package/dist/{chunk-3DVBKTSO.js.map → chunk-HSZMX3ZJ.js.map} +0 -0
  409. /package/dist/{chunk-ODZG724P.js.map → chunk-I2HTKOOP.js.map} +0 -0
  410. /package/dist/{chunk-GN55KU2P.js.map → chunk-IIRCHGWD.js.map} +0 -0
  411. /package/dist/{chunk-AYYGRVRP.js.map → chunk-IURMJBGP.js.map} +0 -0
  412. /package/dist/{chunk-4XDGYCZI.js.map → chunk-J3ATBB7M.js.map} +0 -0
  413. /package/dist/{chunk-E4MVQQVX.js.map → chunk-JPAKBNTM.js.map} +0 -0
  414. /package/dist/{chunk-NZWEEALH.js.map → chunk-KFIOPZ25.js.map} +0 -0
  415. /package/dist/{chunk-EIVIB73I.js.map → chunk-KOO37E4Y.js.map} +0 -0
  416. /package/dist/{chunk-VA5KMSAE.js.map → chunk-KTPXXVDQ.js.map} +0 -0
  417. /package/dist/{chunk-Z4RPXHJH.js.map → chunk-LYBIHM4N.js.map} +0 -0
  418. /package/dist/{chunk-G57LQRP6.js.map → chunk-MFOSZUIT.js.map} +0 -0
  419. /package/dist/{chunk-TYUS5TLG.js.map → chunk-N56GVOWR.js.map} +0 -0
  420. /package/dist/{chunk-BQUEAVNW.js.map → chunk-NH6LC57D.js.map} +0 -0
  421. /package/dist/{chunk-AUO2LJ3K.js.map → chunk-NLEH4RTB.js.map} +0 -0
  422. /package/dist/{chunk-AYDDB3XI.js.map → chunk-OCCHUEZB.js.map} +0 -0
  423. /package/dist/{chunk-57HVIG5U.js.map → chunk-OWQNJJY2.js.map} +0 -0
  424. /package/dist/{chunk-OJD4WS2E.js.map → chunk-OZDFB22S.js.map} +0 -0
  425. /package/dist/{chunk-2VXWFYJE.js.map → chunk-R2MZCYA6.js.map} +0 -0
  426. /package/dist/{chunk-LBWQ65SX.js.map → chunk-T42L3S6A.js.map} +0 -0
  427. /package/dist/{chunk-PAYGC3BD.js.map → chunk-THVI4Y7X.js.map} +0 -0
  428. /package/dist/{chunk-R47WINCY.js.map → chunk-UCKG36HO.js.map} +0 -0
  429. /package/dist/{chunk-QXIQPCCN.js.map → chunk-UFR6HX7A.js.map} +0 -0
  430. /package/dist/{chunk-CDUUDBWJ.js.map → chunk-VFYRYFLL.js.map} +0 -0
  431. /package/dist/{chunk-P4DHENRE.js.map → chunk-WDXD6ITO.js.map} +0 -0
  432. /package/dist/{chunk-H5FW4S5Q.js.map → chunk-YB4GBWBU.js.map} +0 -0
  433. /package/dist/{chunk-MBDJJTAT.js.map → chunk-YDKF4MSJ.js.map} +0 -0
  434. /package/dist/{chunk-AZAA5DNP.js.map → chunk-YOXVZJFM.js.map} +0 -0
  435. /package/dist/{chunk-OLWTCWQ2.js.map → chunk-YZJM6CLF.js.map} +0 -0
  436. /package/dist/{chunk-LUNJ3WFO.js.map → chunk-ZK4BFABK.js.map} +0 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Skill bundle export / import commands (issue #2369) — registered as
3
+ * `remnic export skills` and `remnic import skills <dir>`.
4
+ *
5
+ * Lives outside cli.ts (which is at its structural ceiling) and mirrors the
6
+ * registerMeetingsCommands seam: one registrar, no behavior of its own beyond
7
+ * option parsing and rendering. All projection, path safety, and persistence
8
+ * live in `procedural/skill-projection.ts` + `procedural/skill-io.ts`, so the
9
+ * CLI and the Codex materializer share one contract.
10
+ */
11
+
12
+ import type { CliCommand } from "../cli.js";
13
+ import type { Orchestrator } from "../orchestrator.js";
14
+ import {
15
+ exportSkillBundles,
16
+ persistImportedSkills,
17
+ readSkillBundlesFromDir,
18
+ } from "../procedural/skill-io.js";
19
+ import { projectProceduresToSkills } from "../procedural/skill-projection.js";
20
+ import { BUILTIN_SKILLS } from "../skills-registry.js";
21
+ import { expandTildePath } from "../utils/path.js";
22
+
23
+ /**
24
+ * Parse `--max-skills`. Rejects non-integers and negatives rather than
25
+ * silently defaulting (Review Prevention Checklist §1, §39); `0` is honored as
26
+ * the documented disable value (§33).
27
+ */
28
+ function parseMaxSkills(raw: unknown, fallback: number): number {
29
+ if (raw === undefined || raw === null || raw === "") return fallback;
30
+ const value = Number(raw);
31
+ if (!Number.isInteger(value) || value < 0) {
32
+ throw new Error(`Invalid --max-skills '${String(raw)}'. Expected a non-negative integer (0 exports nothing).`);
33
+ }
34
+ return value;
35
+ }
36
+
37
+ export function registerSkillsCommands(
38
+ exportCmd: CliCommand,
39
+ importCmd: CliCommand,
40
+ orchestrator: Orchestrator,
41
+ ): void {
42
+ exportCmd
43
+ .command("skills")
44
+ .description(
45
+ "Export active procedure memories as portable skills/<slug>/SKILL.md bundles (issue #2369). Read-only with respect to memory.",
46
+ )
47
+ .option("--out <dir>", "Output directory (e.g. a host skills dir or a git repo)")
48
+ .option("--max-skills <n>", "Cap on exported bundles (0 exports none; default from config)")
49
+ .option("--namespace <ns>", "Namespace to export (default: config defaultNamespace)", "")
50
+ .action(async (...args: unknown[]) => {
51
+ const options = (args[0] ?? {}) as Record<string, unknown>;
52
+ const out = options.out ? String(options.out) : "";
53
+ if (!out) {
54
+ console.error("Missing --out. Example: remnic export skills --out ./exported-skills");
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ if (orchestrator.config.procedural.enabled !== true) {
59
+ console.error(
60
+ "procedural memory is disabled (procedural.enabled=false); there are no procedure memories to export.",
61
+ );
62
+ process.exitCode = 1;
63
+ return;
64
+ }
65
+ let maxSkills: number;
66
+ try {
67
+ maxSkills = parseMaxSkills(options.maxSkills, orchestrator.config.procedural.skillProjection.maxSkills);
68
+ } catch (err) {
69
+ console.error(err instanceof Error ? err.message : String(err));
70
+ process.exitCode = 1;
71
+ return;
72
+ }
73
+
74
+ const namespace = options.namespace ? String(options.namespace) : "";
75
+ const storage = await orchestrator.getStorageForNamespace(namespace || undefined);
76
+ const bundles = projectProceduresToSkills(await storage.readAllMemories(), {
77
+ maxSkills,
78
+ reservedSlugs: BUILTIN_SKILLS.map((skill) => skill.slug),
79
+ });
80
+ const result = await exportSkillBundles({ bundles, outDir: expandTildePath(out) });
81
+ console.log(`Exported ${result.slugs.length} skill bundle(s) to ${result.outDir}`);
82
+ for (const slug of result.slugs) {
83
+ console.log(` ${slug}/SKILL.md`);
84
+ }
85
+ console.log("OK");
86
+ });
87
+
88
+ importCmd
89
+ .command("skills <dir>")
90
+ .description(
91
+ "Import skills/<slug>/SKILL.md bundles as pending_review procedure memories (issue #2369). Never executes bundle scripts.",
92
+ )
93
+ .option("--namespace <ns>", "Namespace to import into (default: config defaultNamespace)", "")
94
+ .action(async (...args: unknown[]) => {
95
+ const dir = args[0] ? String(args[0]) : "";
96
+ const options = (args[1] ?? {}) as Record<string, unknown>;
97
+ if (!dir) {
98
+ console.error("Usage: remnic import skills <dir>");
99
+ process.exitCode = 1;
100
+ return;
101
+ }
102
+ if (orchestrator.config.procedural.enabled !== true) {
103
+ console.error(
104
+ "procedural memory is disabled (procedural.enabled=false); enable it before importing procedures.",
105
+ );
106
+ process.exitCode = 1;
107
+ return;
108
+ }
109
+
110
+ let read;
111
+ try {
112
+ read = await readSkillBundlesFromDir(expandTildePath(dir));
113
+ } catch (err) {
114
+ console.error(err instanceof Error ? err.message : String(err));
115
+ process.exitCode = 1;
116
+ return;
117
+ }
118
+ for (const skip of read.skipped) {
119
+ console.log(`Skipped ${skip.entry}: ${skip.reason}`);
120
+ }
121
+ if (read.bundles.length === 0) {
122
+ console.log("No SKILL.md bundles found.");
123
+ console.log("OK");
124
+ return;
125
+ }
126
+
127
+ const namespace = options.namespace ? String(options.namespace) : "";
128
+ const storage = await orchestrator.getStorageForNamespace(namespace || undefined);
129
+ const { imported, rejected } = await persistImportedSkills({ storage, bundles: read.bundles });
130
+
131
+ // Direct writes bypass extraction, so the search index needs an explicit
132
+ // refresh or imported procedures stay undiscoverable (§31).
133
+ await orchestrator.qmd.probe();
134
+ if (orchestrator.qmd.isAvailable()) {
135
+ await orchestrator.qmd.update();
136
+ } else {
137
+ console.log(`QMD unavailable in this process; skipped reindex. Status: ${orchestrator.qmd.debugStatus()}`);
138
+ }
139
+
140
+ console.log(`Imported ${imported.length} procedure(s) as pending_review (promote after review).`);
141
+ for (const record of imported) {
142
+ const resources = record.hasUnimportedResources ? " [has unimported resources]" : "";
143
+ console.log(` ${record.slug}: ${record.steps} step(s)${resources}`);
144
+ }
145
+ for (const reject of rejected) {
146
+ console.log(` ${reject.slug}: ${reject.reason}`);
147
+ }
148
+ console.log("OK");
149
+ });
150
+ }
package/src/cli.ts CHANGED
@@ -16,6 +16,7 @@ import { padEndDisplay, truncateGraphemeSafe } from "./whitespace.js";
16
16
  import { runWearablesCliCommand } from "./wearables/cli.js";
17
17
  import { registerMeetingsCommands } from "./cli/meetings-commands.js";
18
18
  import { registerOkfCommands } from "./cli/okf-commands.js";
19
+ import { registerSkillsCommands } from "./cli/skills-commands.js";
19
20
  import { registerResearchStatusCommands } from "./cli/research-status-commands.js";
20
21
  import { registerCreationLedgerCommands } from "./cli/creation-ledger-commands.js";
21
22
  import type {
@@ -4316,7 +4317,7 @@ export function registerCli(
4316
4317
  }
4317
4318
  });
4318
4319
 
4319
- cmd
4320
+ const exportCmd = cmd
4320
4321
  .command("export")
4321
4322
  .description("Export Remnic memory to JSON, Markdown bundle, or SQLite")
4322
4323
  .option("--format <format>", "Export format: json|md|sqlite", "json")
@@ -4368,7 +4369,7 @@ export function registerCli(
4368
4369
  console.log("OK");
4369
4370
  });
4370
4371
 
4371
- cmd
4372
+ const importCmd = cmd
4372
4373
  .command("import")
4373
4374
  .description("Import Remnic memory from JSON bundle, Markdown bundle, or SQLite")
4374
4375
  .option("--from <path>", "Import source path (dir or file)")
@@ -4424,6 +4425,7 @@ export function registerCli(
4424
4425
  }
4425
4426
  console.log("OK");
4426
4427
  });
4428
+ registerSkillsCommands(exportCmd, importCmd, orchestrator);
4427
4429
 
4428
4430
  cmd
4429
4431
  .command("backup")
package/src/config.ts CHANGED
@@ -43,6 +43,7 @@ import { parseExtractionLivenessConfig } from "./extraction-liveness.js";
43
43
  import { parseReplicaPeersConfig } from "./replica-peers-config.js";
44
44
  import { parseDependencyPropagationConfig } from "./dependency-propagation-config.js";
45
45
  import { parseProceduralMaintenanceConfig } from "./procedural/maintenance-config.js";
46
+ import { parseSkillProjectionConfig } from "./procedural/skill-projection.js";
46
47
  import { parseDriftDetectionConfig } from "./preferences/drift-config.js";
47
48
  import {
48
49
  parseContradictionLocalizationConfig,
@@ -1128,8 +1129,8 @@ export function parseConfig(
1128
1129
  ? Math.min(10, Math.max(1, Math.floor(recallMaxCoerced)))
1129
1130
  : 2;
1130
1131
  const maintenance = parseProceduralMaintenanceConfig(rawProcedural.maintenance);
1131
- // Default-on procedural memory (issue #567 PR 4/5). Explicit false-ish
1132
- // values stay off. Unknown values throw.
1132
+ const skillProjection = parseSkillProjectionConfig(rawProcedural.skillProjection);
1133
+ // Default-on procedural memory (issue #567 PR 4/5). Explicit false-ish values stay off. Unknown values throw.
1133
1134
  const rawEnabledValue = rawProcedural.enabled;
1134
1135
  let proceduralEnabled: boolean;
1135
1136
  if (rawEnabledValue === undefined) {
@@ -1154,6 +1155,7 @@ export function parseConfig(
1154
1155
  lookbackDays,
1155
1156
  proceduralMiningCronAutoRegister: coerceBool(rawProcedural.proceduralMiningCronAutoRegister) === true,
1156
1157
  recallMaxProcedures,
1158
+ skillProjection,
1157
1159
  };
1158
1160
  const wearables = parseWearablesConfig(cfg.wearables);
1159
1161
  const { activity, meetings, provenance } = parseCaptureCompanionConfigs(cfg);
@@ -14,6 +14,8 @@ import { existsSync } from "node:fs";
14
14
  import { log } from "../logger.js";
15
15
  import { resolveNamespaceChildRoot } from "../namespaces/path.js";
16
16
  import { isSafeRouteNamespace } from "../routing/engine.js";
17
+ import { projectProceduresToSkills } from "../procedural/skill-projection.js";
18
+ import { BUILTIN_SKILLS } from "../skills-registry.js";
17
19
  import { StorageManager } from "../storage.js";
18
20
  import { excludeSupportPassportPrivateMemories } from "../support-passport/card-projection.js";
19
21
  import type { PluginConfig, MemoryFile } from "../types.js";
@@ -109,6 +111,19 @@ export async function runCodexMaterialize(
109
111
  }
110
112
  memories = excludeSupportPassportPrivateMemories(memories);
111
113
 
114
+ // Skill projection (issue #2369). Gated on `procedural.enabled` AND
115
+ // `procedural.skillProjection.enabled`; `maxSkills: 0` disables it too.
116
+ // Passing `undefined` leaves `skills/` untouched, so a disabled gate never
117
+ // deletes a folder the user curated.
118
+ const skillProjection = cfg.procedural?.skillProjection;
119
+ const skills =
120
+ cfg.procedural?.enabled === true && skillProjection?.enabled === true
121
+ ? projectProceduresToSkills(memories, {
122
+ maxSkills: skillProjection.maxSkills,
123
+ reservedSlugs: BUILTIN_SKILLS.map((skill) => skill.slug),
124
+ })
125
+ : undefined;
126
+
112
127
  // Intentionally NOT catching here: per the JSDoc contract above,
113
128
  // schema-validation and I/O errors from `materializeForNamespace` must
114
129
  // surface to callers so they can exit non-zero (CLI) or log + recover
@@ -122,6 +137,7 @@ export async function runCodexMaterialize(
122
137
  maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,
123
138
  rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,
124
139
  rolloutSummaries: options.rolloutSummaries,
140
+ skills,
125
141
  now: options.now,
126
142
  });
127
143
  if (options.reason) {
@@ -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
+ }