@remnic/core 9.65.3 → 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 (429) 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 +11 -11
  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-service-DkaIG3n8.d.ts → access-service-DqPa3hy4.d.ts} +7 -7
  44. package/dist/access-service-helpers.d.ts +11 -11
  45. package/dist/access-service.d.ts +11 -11
  46. package/dist/access-service.js +20 -18
  47. package/dist/access-surface-catalog.d.ts +11 -11
  48. package/dist/access-surface-catalog.js +22 -20
  49. package/dist/access-surface-catalog.js.map +1 -1
  50. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  51. package/dist/action-confidence.d.ts +1 -1
  52. package/dist/active-memory-bridge.d.ts +1 -1
  53. package/dist/active-recall.d.ts +1 -1
  54. package/dist/active-recall.js +5 -1
  55. package/dist/active-recall.js.map +1 -1
  56. package/dist/ambient-provenance.d.ts +1 -1
  57. package/dist/artifact-search.d.ts +1 -1
  58. package/dist/{auto-sync-H64ZCNIR.js → auto-sync-XVMLL2RH.js} +5 -5
  59. package/dist/behavior-learner.d.ts +1 -1
  60. package/dist/behavior-signals.d.ts +1 -1
  61. package/dist/bootstrap.d.ts +9 -9
  62. package/dist/briefing-window.d.ts +2 -2
  63. package/dist/briefing.d.ts +2 -2
  64. package/dist/briefing.js +4 -4
  65. package/dist/buffer-surprise-report.d.ts +1 -1
  66. package/dist/buffer-turn-helpers.d.ts +1 -1
  67. package/dist/buffer.d.ts +2 -2
  68. package/dist/bulk-import/index.d.ts +3 -3
  69. package/dist/calibration.d.ts +1 -1
  70. package/dist/capabilities.d.ts +1 -1
  71. package/dist/{catalog-CX2BZEJh.d.ts → catalog-DO_7zfkv.d.ts} +1 -1
  72. package/dist/causal-behavior.d.ts +1 -1
  73. package/dist/causal-consolidation.d.ts +1 -1
  74. package/dist/causal-consolidation.js +9 -6
  75. package/dist/causal-consolidation.js.map +1 -1
  76. package/dist/causal-trajectory-graph.d.ts +1 -1
  77. package/dist/chunk-236PXYG7.js +232 -0
  78. package/dist/chunk-236PXYG7.js.map +1 -0
  79. package/dist/{chunk-IQSC3V2E.js → chunk-2IXQSGCU.js} +2 -2
  80. package/dist/{chunk-GUXS322H.js → chunk-2MVTAIL3.js} +2 -2
  81. package/dist/{chunk-7QOO4LGC.js → chunk-3JZBPTLD.js} +331 -73
  82. package/dist/chunk-3JZBPTLD.js.map +1 -0
  83. package/dist/{chunk-TDKSCV62.js → chunk-3NH3VP3U.js} +2 -2
  84. package/dist/{chunk-URHCUXKI.js → chunk-AXGFOFNY.js} +2 -2
  85. package/dist/{chunk-WN5FSGLE.js → chunk-AZLLH6YA.js} +19 -19
  86. package/dist/{chunk-D27J626P.js → chunk-BK5G2MHO.js} +2 -2
  87. package/dist/{chunk-DHTQP334.js → chunk-CQBIN77D.js} +2 -2
  88. package/dist/{chunk-6VALIZKG.js → chunk-DORFF5N5.js} +2 -2
  89. package/dist/{chunk-76PL4XLG.js → chunk-DPNXADEI.js} +7 -2
  90. package/dist/chunk-DPNXADEI.js.map +1 -0
  91. package/dist/{chunk-MRTUN665.js → chunk-DUEDOYZD.js} +2 -2
  92. package/dist/{chunk-QFMO32TO.js → chunk-DUZARALI.js} +3 -3
  93. package/dist/{chunk-7KFIMGMP.js → chunk-ECLJ4LA2.js} +2 -2
  94. package/dist/{chunk-PDVQ5JU3.js → chunk-GHD2LLG4.js} +2 -2
  95. package/dist/{chunk-RK4WXUI2.js → chunk-HF5BUTI6.js} +2 -2
  96. package/dist/{chunk-3DVBKTSO.js → chunk-HSZMX3ZJ.js} +4 -4
  97. package/dist/{chunk-EV3YFLCB.js → chunk-I23ZHYSL.js} +2 -2
  98. package/dist/{chunk-ODZG724P.js → chunk-I2HTKOOP.js} +2 -2
  99. package/dist/{chunk-AYYGRVRP.js → chunk-IURMJBGP.js} +2 -2
  100. package/dist/{chunk-TYUS5TLG.js → chunk-IWKGOCL6.js} +3 -3
  101. package/dist/{chunk-4XDGYCZI.js → chunk-J3ATBB7M.js} +2 -2
  102. package/dist/{chunk-E4MVQQVX.js → chunk-JPAKBNTM.js} +2 -2
  103. package/dist/{chunk-NZWEEALH.js → chunk-KFIOPZ25.js} +2 -2
  104. package/dist/{chunk-EIVIB73I.js → chunk-KOO37E4Y.js} +2 -2
  105. package/dist/{chunk-57HVIG5U.js → chunk-KPB2QHJZ.js} +2 -2
  106. package/dist/{chunk-NXKJHSDN.js → chunk-LBFFNDFM.js} +1 -1
  107. package/dist/chunk-LBFFNDFM.js.map +1 -0
  108. package/dist/{chunk-Z4RPXHJH.js → chunk-LYBIHM4N.js} +4 -4
  109. package/dist/{chunk-G57LQRP6.js → chunk-MFOSZUIT.js} +2 -2
  110. package/dist/{chunk-AUO2LJ3K.js → chunk-NLEH4RTB.js} +2 -2
  111. package/dist/{chunk-AYDDB3XI.js → chunk-OCCHUEZB.js} +2 -2
  112. package/dist/{chunk-6TAX66IA.js → chunk-OU34IQXV.js} +2 -2
  113. package/dist/{chunk-OJD4WS2E.js → chunk-OZDFB22S.js} +2 -2
  114. package/dist/{chunk-GN55KU2P.js → chunk-PTZQYCJ4.js} +2 -2
  115. package/dist/{chunk-BQUEAVNW.js → chunk-QJW4MZSW.js} +2 -2
  116. package/dist/{chunk-2VXWFYJE.js → chunk-R2MZCYA6.js} +3 -3
  117. package/dist/{chunk-VA5KMSAE.js → chunk-SR5LNI6Q.js} +3 -3
  118. package/dist/{chunk-LBWQ65SX.js → chunk-T42L3S6A.js} +4 -4
  119. package/dist/{chunk-PAYGC3BD.js → chunk-THVI4Y7X.js} +7 -7
  120. package/dist/{chunk-S7Q5CM7E.js → chunk-TJFI3K2O.js} +15 -3
  121. package/dist/chunk-TJFI3K2O.js.map +1 -0
  122. package/dist/{chunk-R47WINCY.js → chunk-UCKG36HO.js} +2 -2
  123. package/dist/{chunk-QXIQPCCN.js → chunk-UFR6HX7A.js} +1 -1
  124. package/dist/{chunk-CDUUDBWJ.js → chunk-VFYRYFLL.js} +2 -2
  125. package/dist/{chunk-P4DHENRE.js → chunk-WDXD6ITO.js} +2 -2
  126. package/dist/{chunk-BKYPJUQL.js → chunk-XGM3VQWG.js} +10 -10
  127. package/dist/{chunk-MBDJJTAT.js → chunk-YDKF4MSJ.js} +3 -3
  128. package/dist/{chunk-YF6UHWOV.js → chunk-YHGCT462.js} +3 -3
  129. package/dist/{chunk-AZAA5DNP.js → chunk-YOXVZJFM.js} +2 -2
  130. package/dist/{chunk-OLWTCWQ2.js → chunk-YZJM6CLF.js} +8 -8
  131. package/dist/chunk-ZCUTSMR2.js +61 -0
  132. package/dist/chunk-ZCUTSMR2.js.map +1 -0
  133. package/dist/{chunk-LUNJ3WFO.js → chunk-ZK4BFABK.js} +4 -4
  134. package/dist/{chunk-H5FW4S5Q.js → chunk-ZPOHEB5Z.js} +31 -31
  135. package/dist/{chunk-RDFF2NAU.js → chunk-ZXBTTMGZ.js} +184 -88
  136. package/dist/chunk-ZXBTTMGZ.js.map +1 -0
  137. package/dist/{cli-ChKeYMZv.d.ts → cli-DO3i14pM.d.ts} +5 -5
  138. package/dist/cli.d.ts +13 -13
  139. package/dist/cli.js +44 -42
  140. package/dist/coding/pre-action-gate.d.ts +1 -1
  141. package/dist/compounding/engine.d.ts +2 -2
  142. package/dist/compounding/engine.js +4 -4
  143. package/dist/compounding/preference-consolidator.d.ts +1 -1
  144. package/dist/compression-optimizer.d.ts +1 -1
  145. package/dist/config.d.ts +1 -1
  146. package/dist/config.js +5 -1
  147. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  148. package/dist/connectors/codex-materialize-runner.js +8 -5
  149. package/dist/connectors/codex-materialize.d.ts +14 -1
  150. package/dist/connectors/codex-materialize.js +10 -1
  151. package/dist/connectors/index.d.ts +1 -1
  152. package/dist/connectors/index.js +8 -5
  153. package/dist/consolidation-provenance-check.d.ts +2 -2
  154. package/dist/consolidation-undo.d.ts +2 -2
  155. package/dist/contradiction/index.d.ts +3 -3
  156. package/dist/contradiction/index.js +1 -1
  157. package/dist/converge-config.d.ts +1 -1
  158. package/dist/convergence-refresh.d.ts +2 -2
  159. package/dist/convergence-refresh.js +6 -6
  160. package/dist/conversation-index/backend.d.ts +1 -1
  161. package/dist/conversation-index/chunker.d.ts +1 -1
  162. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  163. package/dist/conversation-index/indexer.d.ts +1 -1
  164. package/dist/conversation-index/search.d.ts +1 -1
  165. package/dist/corpus-watermark.d.ts +1 -1
  166. package/dist/corpus-watermark.js +6 -6
  167. package/dist/cross-lingual-recall.d.ts +1 -1
  168. package/dist/day-summary.d.ts +1 -1
  169. package/dist/delinearize.d.ts +1 -1
  170. package/dist/dependency-propagation-config.d.ts +1 -1
  171. package/dist/{dependency-propagation-delivery-B8ZMBBCY.d.ts → dependency-propagation-delivery-D23_x-s5.d.ts} +1 -1
  172. package/dist/direct-answer-wiring.d.ts +1 -1
  173. package/dist/direct-answer.d.ts +1 -1
  174. package/dist/embedding-fallback.d.ts +1 -1
  175. package/dist/enrichment/index.d.ts +1 -1
  176. package/dist/entity-origin-fields.d.ts +1 -1
  177. package/dist/entity-retrieval.d.ts +2 -2
  178. package/dist/entity-retrieval.js +4 -4
  179. package/dist/entity-schema.d.ts +1 -1
  180. package/dist/episodic-context.d.ts +1 -1
  181. package/dist/explicit-capture.d.ts +9 -9
  182. package/dist/explicit-capture.js +1 -1
  183. package/dist/external-wiki-access.d.ts +11 -11
  184. package/dist/external-wiki-access.js +21 -19
  185. package/dist/external-wiki-collection-registration.d.ts +1 -1
  186. package/dist/external-wiki-collection.d.ts +1 -1
  187. package/dist/external-wiki-mcp-tools.d.ts +11 -11
  188. package/dist/extraction-error-classification.d.ts +1 -1
  189. package/dist/extraction-faithfulness.d.ts +1 -1
  190. package/dist/extraction-judge-telemetry.d.ts +1 -1
  191. package/dist/extraction-judge-training.d.ts +1 -1
  192. package/dist/extraction-judge.d.ts +1 -1
  193. package/dist/extraction-judge.js +2 -2
  194. package/dist/extraction-liveness.d.ts +1 -1
  195. package/dist/extraction-normalization.d.ts +1 -1
  196. package/dist/extraction-prompt.d.ts +1 -1
  197. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  198. package/dist/extraction-source-grounding.d.ts +1 -1
  199. package/dist/extraction.d.ts +1 -1
  200. package/dist/extraction.js +9 -7
  201. package/dist/fallback-llm.d.ts +1 -1
  202. package/dist/graph-dashboard-diff.d.ts +1 -1
  203. package/dist/graph-dashboard-key.d.ts +1 -1
  204. package/dist/graph-dashboard-parser.d.ts +1 -1
  205. package/dist/graph-edge-reinforcement.d.ts +1 -1
  206. package/dist/graph-path-reconstruction.d.ts +1 -1
  207. package/dist/graph-path-scoring.d.ts +1 -1
  208. package/dist/graph-snapshot.d.ts +1 -1
  209. package/dist/graph.d.ts +1 -1
  210. package/dist/harmonic-retrieval.js +1 -1
  211. package/dist/importance.d.ts +1 -1
  212. package/dist/importers/index.d.ts +1 -1
  213. package/dist/in-flight-reads.d.ts +1 -1
  214. package/dist/{index-CexDrJzC.d.ts → index--QhGIXF2.d.ts} +2 -2
  215. package/dist/index.d.ts +87 -40
  216. package/dist/index.js +79 -58
  217. package/dist/intent.d.ts +1 -1
  218. package/dist/lcm/engine.d.ts +1 -1
  219. package/dist/lcm/index.d.ts +1 -1
  220. package/dist/lcm/tools.d.ts +1 -1
  221. package/dist/lifecycle.d.ts +1 -1
  222. package/dist/live-connectors-runner.d.ts +1 -1
  223. package/dist/local-llm.d.ts +1 -1
  224. package/dist/local-model-endpoint.d.ts +1 -1
  225. package/dist/maintenance/memory-governance.d.ts +1 -1
  226. package/dist/maintenance/memory-governance.js +4 -4
  227. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  228. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -4
  229. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  230. package/dist/maintenance/rebuild-memory-projection.js +5 -5
  231. package/dist/{maintenance-nkdtSrVr.d.ts → maintenance-PFvY1Pw_.d.ts} +3 -3
  232. package/dist/mcp-memory-inspector-app.d.ts +11 -11
  233. package/dist/memory-action-policy.d.ts +1 -1
  234. package/dist/memory-cache.d.ts +1 -1
  235. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  236. package/dist/memory-projection-store.d.ts +1 -1
  237. package/dist/memory-provenance.d.ts +1 -1
  238. package/dist/memory-snapshot.d.ts +1 -1
  239. package/dist/memory-subject-cli.d.ts +11 -11
  240. package/dist/memory-subject.d.ts +11 -11
  241. package/dist/memory-worth-outcomes.d.ts +2 -2
  242. package/dist/models-json.d.ts +1 -1
  243. package/dist/namespaces/migrate.d.ts +3 -3
  244. package/dist/namespaces/migrate.js +5 -5
  245. package/dist/namespaces/principal.d.ts +1 -1
  246. package/dist/namespaces/search.d.ts +1 -1
  247. package/dist/namespaces/storage.d.ts +3 -3
  248. package/dist/namespaces/storage.js +4 -4
  249. package/dist/native-knowledge.d.ts +1 -1
  250. package/dist/offline-sync-impression-drain.d.ts +1 -1
  251. package/dist/operator-doctor-corpus.d.ts +1 -1
  252. package/dist/operator-doctor-corpus.js +7 -7
  253. package/dist/operator-doctor-replica.d.ts +1 -1
  254. package/dist/operator-toolkit.d.ts +3 -3
  255. package/dist/operator-toolkit.js +16 -13
  256. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  257. package/dist/orchestration/maintenance.d.ts +4 -4
  258. package/dist/orchestration/maintenance.js +10 -10
  259. package/dist/{orchestrator-Bvq1-RQs.d.ts → orchestrator-4wzJrPVm.d.ts} +8 -8
  260. package/dist/orchestrator.d.ts +9 -9
  261. package/dist/orchestrator.js +55 -53
  262. package/dist/patterns-cli.d.ts +1 -1
  263. package/dist/{pipeline-C8vfIiTX.d.ts → pipeline-CgXgdIrF.d.ts} +1 -1
  264. package/dist/policy-runtime.d.ts +1 -1
  265. package/dist/proactive-contention.d.ts +1 -1
  266. package/dist/provenance.d.ts +1 -1
  267. package/dist/{public-http-Cy0aYbys.d.ts → public-http-DhIXYBYI.d.ts} +1 -1
  268. package/dist/{qmd-B9p1SZTZ.d.ts → qmd-BppGGxsi.d.ts} +1 -1
  269. package/dist/qmd-preflight.d.ts +2 -2
  270. package/dist/qmd-recall-cache.d.ts +1 -1
  271. package/dist/qmd.d.ts +2 -2
  272. package/dist/recall-concurrency-config.d.ts +1 -1
  273. package/dist/recall-disclosure-escalation.d.ts +1 -1
  274. package/dist/recall-explain-renderer.d.ts +1 -1
  275. package/dist/recall-explain-renderer.js +3 -3
  276. package/dist/recall-memory-map.d.ts +1 -1
  277. package/dist/recall-planner-llm.d.ts +1 -1
  278. package/dist/recall-state.d.ts +1 -1
  279. package/dist/recall-tag-filter.d.ts +1 -1
  280. package/dist/recall-timings.d.ts +1 -1
  281. package/dist/recall-xray-cli.d.ts +1 -1
  282. package/dist/recall-xray-cli.js +4 -4
  283. package/dist/recall-xray-renderer.d.ts +1 -1
  284. package/dist/recall-xray-renderer.js +3 -3
  285. package/dist/recall-xray.d.ts +1 -1
  286. package/dist/recall-xray.js +2 -2
  287. package/dist/reconcile/cursor.d.ts +1 -1
  288. package/dist/reconcile/manifest.d.ts +1 -1
  289. package/dist/reconcile/manifest.js +2 -2
  290. package/dist/reconcile/plan.d.ts +1 -1
  291. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  292. package/dist/replay/normalizers/claude.d.ts +1 -1
  293. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  294. package/dist/replay/normalizers/shared.d.ts +1 -1
  295. package/dist/replay/runner.d.ts +1 -1
  296. package/dist/replay/types.d.ts +1 -1
  297. package/dist/replica-divergence.d.ts +1 -1
  298. package/dist/replica-peers-config.d.ts +1 -1
  299. package/dist/resolution-4552BNRV.js +1 -1
  300. package/dist/resolve-auth-token.d.ts +1 -1
  301. package/dist/resume-bundles.js +6 -2
  302. package/dist/retrieval-agents.d.ts +2 -2
  303. package/dist/retrieval-tiers.d.ts +1 -1
  304. package/dist/routing/engine.d.ts +1 -1
  305. package/dist/routing/store.d.ts +1 -1
  306. package/dist/salvage-envelope.d.ts +1 -1
  307. package/dist/salvage-envelope.js +1 -1
  308. package/dist/schemas.d.ts +12 -12
  309. package/dist/{scope-profiles-DLrRxFWR.d.ts → scope-profiles-ChZUtxDU.d.ts} +1 -1
  310. package/dist/search/embed-helper.d.ts +1 -1
  311. package/dist/search/factory.d.ts +1 -1
  312. package/dist/search/index.d.ts +1 -1
  313. package/dist/search/lancedb-backend.d.ts +1 -1
  314. package/dist/search/meilisearch-backend.d.ts +1 -1
  315. package/dist/search/noop-backend.d.ts +1 -1
  316. package/dist/search/orama-backend.d.ts +1 -1
  317. package/dist/search/port.d.ts +1 -1
  318. package/dist/search/remote-backend.d.ts +1 -1
  319. package/dist/{semantic-consolidation-DlN6xe20.d.ts → semantic-consolidation-mSPwQSmF.d.ts} +1 -1
  320. package/dist/semantic-consolidation.d.ts +2 -2
  321. package/dist/semantic-consolidation.js +9 -6
  322. package/dist/semantic-rule-promotion.js +4 -4
  323. package/dist/semantic-rule-verifier.d.ts +1 -1
  324. package/dist/semantic-rule-verifier.js +4 -4
  325. package/dist/{service-GkYyVfm9.d.ts → service-BICWLwSP.d.ts} +2 -2
  326. package/dist/session-observer-bands.d.ts +1 -1
  327. package/dist/session-observer-state.d.ts +1 -1
  328. package/dist/shared-context/manager.d.ts +1 -1
  329. package/dist/signal.d.ts +1 -1
  330. package/dist/skills-registry.js +4 -56
  331. package/dist/skills-registry.js.map +1 -1
  332. package/dist/source-agent-qualifier.d.ts +1 -1
  333. package/dist/source-agent-qualifier.js +8 -5
  334. package/dist/{storage-BIDzuKIn.d.ts → storage-B7urx1K3.d.ts} +1 -1
  335. package/dist/storage.d.ts +2 -2
  336. package/dist/storage.js +6 -6
  337. package/dist/subject-config.d.ts +1 -1
  338. package/dist/summarizer.d.ts +1 -1
  339. package/dist/summary-snapshot.d.ts +1 -1
  340. package/dist/support-passport/index.d.ts +14 -14
  341. package/dist/support-passport/index.js +23 -21
  342. package/dist/temporal-supersession.d.ts +2 -2
  343. package/dist/temporal-timeline-recall.d.ts +1 -1
  344. package/dist/temporal-validity.d.ts +1 -1
  345. package/dist/threading.d.ts +1 -1
  346. package/dist/tier-migration.d.ts +2 -2
  347. package/dist/tier-routing.d.ts +1 -1
  348. package/dist/topics.d.ts +1 -1
  349. package/dist/transcript.d.ts +1 -1
  350. package/dist/trust-score-stage.d.ts +1 -1
  351. package/dist/trust-score.d.ts +1 -1
  352. package/dist/{types-BxAcOl5Q.d.ts → types-ETU5N6nB.d.ts} +111 -4
  353. package/dist/types.d.ts +1 -1
  354. package/dist/types.js +1 -1
  355. package/dist/utility-runtime.d.ts +1 -1
  356. package/dist/verified-recall.js +4 -4
  357. package/dist/who-knows-cli.d.ts +11 -11
  358. package/dist/who-knows-cli.js +5 -5
  359. package/dist/who-knows.d.ts +11 -11
  360. package/dist/who-knows.js +4 -4
  361. package/dist/write-envelope.d.ts +1 -1
  362. package/dist/write-envelope.js +1 -1
  363. package/package.json +2 -2
  364. package/src/cli/skills-commands.ts +150 -0
  365. package/src/cli.ts +4 -2
  366. package/src/config.ts +4 -2
  367. package/src/connectors/codex-materialize-runner.ts +16 -0
  368. package/src/connectors/codex-materialize-skills.test.ts +141 -0
  369. package/src/connectors/codex-materialize.ts +58 -40
  370. package/src/connectors/materialize-paths.ts +50 -0
  371. package/src/connectors/materialize-skills.ts +127 -0
  372. package/src/index.ts +5 -10
  373. package/src/procedural/skill-io.test.ts +190 -0
  374. package/src/procedural/skill-io.ts +220 -0
  375. package/src/procedural/skill-projection.test.ts +214 -0
  376. package/src/procedural/skill-projection.ts +356 -0
  377. package/src/types.ts +2 -3
  378. package/dist/chunk-76PL4XLG.js.map +0 -1
  379. package/dist/chunk-7QOO4LGC.js.map +0 -1
  380. package/dist/chunk-NXKJHSDN.js.map +0 -1
  381. package/dist/chunk-RDFF2NAU.js.map +0 -1
  382. package/dist/chunk-S7Q5CM7E.js.map +0 -1
  383. /package/dist/{auto-sync-H64ZCNIR.js.map → auto-sync-XVMLL2RH.js.map} +0 -0
  384. /package/dist/{chunk-IQSC3V2E.js.map → chunk-2IXQSGCU.js.map} +0 -0
  385. /package/dist/{chunk-GUXS322H.js.map → chunk-2MVTAIL3.js.map} +0 -0
  386. /package/dist/{chunk-TDKSCV62.js.map → chunk-3NH3VP3U.js.map} +0 -0
  387. /package/dist/{chunk-URHCUXKI.js.map → chunk-AXGFOFNY.js.map} +0 -0
  388. /package/dist/{chunk-WN5FSGLE.js.map → chunk-AZLLH6YA.js.map} +0 -0
  389. /package/dist/{chunk-D27J626P.js.map → chunk-BK5G2MHO.js.map} +0 -0
  390. /package/dist/{chunk-DHTQP334.js.map → chunk-CQBIN77D.js.map} +0 -0
  391. /package/dist/{chunk-6VALIZKG.js.map → chunk-DORFF5N5.js.map} +0 -0
  392. /package/dist/{chunk-MRTUN665.js.map → chunk-DUEDOYZD.js.map} +0 -0
  393. /package/dist/{chunk-QFMO32TO.js.map → chunk-DUZARALI.js.map} +0 -0
  394. /package/dist/{chunk-7KFIMGMP.js.map → chunk-ECLJ4LA2.js.map} +0 -0
  395. /package/dist/{chunk-PDVQ5JU3.js.map → chunk-GHD2LLG4.js.map} +0 -0
  396. /package/dist/{chunk-RK4WXUI2.js.map → chunk-HF5BUTI6.js.map} +0 -0
  397. /package/dist/{chunk-3DVBKTSO.js.map → chunk-HSZMX3ZJ.js.map} +0 -0
  398. /package/dist/{chunk-EV3YFLCB.js.map → chunk-I23ZHYSL.js.map} +0 -0
  399. /package/dist/{chunk-ODZG724P.js.map → chunk-I2HTKOOP.js.map} +0 -0
  400. /package/dist/{chunk-AYYGRVRP.js.map → chunk-IURMJBGP.js.map} +0 -0
  401. /package/dist/{chunk-TYUS5TLG.js.map → chunk-IWKGOCL6.js.map} +0 -0
  402. /package/dist/{chunk-4XDGYCZI.js.map → chunk-J3ATBB7M.js.map} +0 -0
  403. /package/dist/{chunk-E4MVQQVX.js.map → chunk-JPAKBNTM.js.map} +0 -0
  404. /package/dist/{chunk-NZWEEALH.js.map → chunk-KFIOPZ25.js.map} +0 -0
  405. /package/dist/{chunk-EIVIB73I.js.map → chunk-KOO37E4Y.js.map} +0 -0
  406. /package/dist/{chunk-57HVIG5U.js.map → chunk-KPB2QHJZ.js.map} +0 -0
  407. /package/dist/{chunk-Z4RPXHJH.js.map → chunk-LYBIHM4N.js.map} +0 -0
  408. /package/dist/{chunk-G57LQRP6.js.map → chunk-MFOSZUIT.js.map} +0 -0
  409. /package/dist/{chunk-AUO2LJ3K.js.map → chunk-NLEH4RTB.js.map} +0 -0
  410. /package/dist/{chunk-AYDDB3XI.js.map → chunk-OCCHUEZB.js.map} +0 -0
  411. /package/dist/{chunk-6TAX66IA.js.map → chunk-OU34IQXV.js.map} +0 -0
  412. /package/dist/{chunk-OJD4WS2E.js.map → chunk-OZDFB22S.js.map} +0 -0
  413. /package/dist/{chunk-GN55KU2P.js.map → chunk-PTZQYCJ4.js.map} +0 -0
  414. /package/dist/{chunk-BQUEAVNW.js.map → chunk-QJW4MZSW.js.map} +0 -0
  415. /package/dist/{chunk-2VXWFYJE.js.map → chunk-R2MZCYA6.js.map} +0 -0
  416. /package/dist/{chunk-VA5KMSAE.js.map → chunk-SR5LNI6Q.js.map} +0 -0
  417. /package/dist/{chunk-LBWQ65SX.js.map → chunk-T42L3S6A.js.map} +0 -0
  418. /package/dist/{chunk-PAYGC3BD.js.map → chunk-THVI4Y7X.js.map} +0 -0
  419. /package/dist/{chunk-R47WINCY.js.map → chunk-UCKG36HO.js.map} +0 -0
  420. /package/dist/{chunk-QXIQPCCN.js.map → chunk-UFR6HX7A.js.map} +0 -0
  421. /package/dist/{chunk-CDUUDBWJ.js.map → chunk-VFYRYFLL.js.map} +0 -0
  422. /package/dist/{chunk-P4DHENRE.js.map → chunk-WDXD6ITO.js.map} +0 -0
  423. /package/dist/{chunk-BKYPJUQL.js.map → chunk-XGM3VQWG.js.map} +0 -0
  424. /package/dist/{chunk-MBDJJTAT.js.map → chunk-YDKF4MSJ.js.map} +0 -0
  425. /package/dist/{chunk-YF6UHWOV.js.map → chunk-YHGCT462.js.map} +0 -0
  426. /package/dist/{chunk-AZAA5DNP.js.map → chunk-YOXVZJFM.js.map} +0 -0
  427. /package/dist/{chunk-OLWTCWQ2.js.map → chunk-YZJM6CLF.js.map} +0 -0
  428. /package/dist/{chunk-LUNJ3WFO.js.map → chunk-ZK4BFABK.js.map} +0 -0
  429. /package/dist/{chunk-H5FW4S5Q.js.map → chunk-ZPOHEB5Z.js.map} +0 -0
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Regression tests for the skill-bundle projection contract (issue #2369).
3
+ */
4
+
5
+ import test from "node:test";
6
+ import assert from "node:assert/strict";
7
+
8
+ import type { MemoryFile, MemoryStatus } from "../types.js";
9
+ import { buildProcedurePersistBody, parseProcedureStepsFromBody } from "./procedure-types.js";
10
+ import {
11
+ parseSkillBundle,
12
+ parseSkillProjectionConfig,
13
+ projectProceduresToSkills,
14
+ renderSkillBundle,
15
+ sanitizeSkillSlug,
16
+ } from "./skill-projection.js";
17
+
18
+ const STEPS = [
19
+ { order: 1, intent: "Read the failing test output." },
20
+ { order: 2, intent: "Fix the smallest cause.", expectedOutcome: "The suite passes." },
21
+ ];
22
+
23
+ function makeProcedure(options: {
24
+ id: string;
25
+ title: string;
26
+ status?: MemoryStatus;
27
+ updated?: string;
28
+ category?: string;
29
+ }): MemoryFile {
30
+ return {
31
+ path: `procedures/2026-08-18/${options.id}.md`,
32
+ content: buildProcedurePersistBody(options.title, STEPS),
33
+ frontmatter: {
34
+ id: options.id,
35
+ category: (options.category ?? "procedure") as MemoryFile["frontmatter"]["category"],
36
+ created: "2026-08-18T00:00:00.000Z",
37
+ updated: options.updated ?? "2026-08-18T00:00:00.000Z",
38
+ source: "procedure-miner",
39
+ confidence: 0.8,
40
+ confidenceTier: "explicit",
41
+ tags: [],
42
+ ...(options.status ? { status: options.status } : {}),
43
+ } as MemoryFile["frontmatter"],
44
+ };
45
+ }
46
+
47
+ test("the stored machine attributes footer never reaches the skill body", () => {
48
+ const memory = makeProcedure({ id: "procedure-1", title: "Fix a failing test" });
49
+ memory.content = `${memory.content}\n[Attributes: procedure_cluster: abc; trajectory_count: 4]`;
50
+ const bundles = projectProceduresToSkills([memory]);
51
+ assert.equal(bundles.length, 1);
52
+ assert.ok(!bundles[0].body.includes("[Attributes:"));
53
+ assert.equal(bundles[0].body.trimEnd().endsWith("The suite passes."), true);
54
+ assert.equal(parseProcedureStepsFromBody(bundles[0].body)?.length, 2);
55
+ assert.equal(parseProcedureStepsFromBody(bundles[0].body)?.[1].intent, "Fix the smallest cause.");
56
+ });
57
+
58
+ test("projects active procedures and round-trips the steps through parseProcedureStepsFromBody", () => {
59
+ const bundles = projectProceduresToSkills([
60
+ makeProcedure({ id: "procedure-1", title: "Fix a failing test" }),
61
+ ]);
62
+ assert.equal(bundles.length, 1);
63
+ assert.equal(bundles[0].slug, "fix-a-failing-test");
64
+ assert.equal(bundles[0].frontmatter.name, "fix-a-failing-test");
65
+ assert.equal(bundles[0].frontmatter.description, "Fix a failing test");
66
+ assert.equal(bundles[0].provenance.memoryId, "procedure-1");
67
+
68
+ const rendered = renderSkillBundle(bundles[0]);
69
+ const parsed = parseSkillBundle(rendered, bundles[0].slug);
70
+ assert.ok(parsed);
71
+ assert.deepEqual(
72
+ parsed.steps?.map((s) => ({ order: s.order, intent: s.intent })),
73
+ parseProcedureStepsFromBody(bundles[0].body)?.map((s) => ({ order: s.order, intent: s.intent })),
74
+ );
75
+ assert.equal(parsed.steps?.length, 2);
76
+ assert.equal(parsed.steps?.[1].expectedOutcome, "The suite passes.");
77
+ assert.equal(parsed.provenance.memoryId, "procedure-1");
78
+ });
79
+
80
+ test("never projects a non-active status (every status enumerated)", () => {
81
+ const statuses: MemoryStatus[] = [
82
+ "pending_review",
83
+ "rejected",
84
+ "quarantined",
85
+ "superseded",
86
+ "archived",
87
+ "forgotten",
88
+ ];
89
+ for (const status of statuses) {
90
+ const bundles = projectProceduresToSkills([
91
+ makeProcedure({ id: `procedure-${status}`, title: `Do ${status} work`, status }),
92
+ ]);
93
+ assert.deepEqual(bundles, [], `${status} must not project`);
94
+ }
95
+ // Sanity: the same memory projects once its status is active.
96
+ assert.equal(
97
+ projectProceduresToSkills([makeProcedure({ id: "procedure-ok", title: "Do active work", status: "active" })])
98
+ .length,
99
+ 1,
100
+ );
101
+ });
102
+
103
+ test("non-procedure categories are excluded", () => {
104
+ const bundles = projectProceduresToSkills([
105
+ makeProcedure({ id: "fact-1", title: "Not a procedure", category: "fact" }),
106
+ ]);
107
+ assert.deepEqual(bundles, []);
108
+ });
109
+
110
+ test("duplicate titles yield distinct stable slugs", () => {
111
+ const memories = [
112
+ makeProcedure({ id: "procedure-a", title: "Deploy the service", updated: "2026-08-18T03:00:00.000Z" }),
113
+ makeProcedure({ id: "procedure-b", title: "Deploy the service!", updated: "2026-08-18T02:00:00.000Z" }),
114
+ makeProcedure({ id: "procedure-c", title: "Deploy the service?", updated: "2026-08-18T01:00:00.000Z" }),
115
+ ];
116
+ const first = projectProceduresToSkills(memories).map((b) => b.slug);
117
+ assert.deepEqual(first, ["deploy-the-service", "deploy-the-service-2", "deploy-the-service-3"]);
118
+ // Stable across runs, and stable when the input order changes (sort is total).
119
+ assert.deepEqual(projectProceduresToSkills([...memories].reverse()).map((b) => b.slug), first);
120
+ });
121
+
122
+ test("ties on updated fall back to id order, so ordering is deterministic", () => {
123
+ const memories = [
124
+ makeProcedure({ id: "procedure-z", title: "Zed task", updated: "2026-08-18T00:00:00.000Z" }),
125
+ makeProcedure({ id: "procedure-a", title: "Alpha task", updated: "2026-08-18T00:00:00.000Z" }),
126
+ ];
127
+ assert.deepEqual(projectProceduresToSkills(memories).map((b) => b.slug), ["alpha-task", "zed-task"]);
128
+ assert.deepEqual(projectProceduresToSkills([...memories].reverse()).map((b) => b.slug), [
129
+ "alpha-task",
130
+ "zed-task",
131
+ ]);
132
+ });
133
+
134
+ test("reserved slugs are prefixed rather than shadowed", () => {
135
+ const bundles = projectProceduresToSkills(
136
+ [makeProcedure({ id: "procedure-1", title: "remnic memory workflow" })],
137
+ { reservedSlugs: ["remnic-memory-workflow"] },
138
+ );
139
+ assert.equal(bundles[0].slug, "user-remnic-memory-workflow");
140
+ });
141
+
142
+ test("maxSkills caps the projection and 0 disables it", () => {
143
+ const memories = [
144
+ makeProcedure({ id: "procedure-a", title: "Task A", updated: "2026-08-18T03:00:00.000Z" }),
145
+ makeProcedure({ id: "procedure-b", title: "Task B", updated: "2026-08-18T02:00:00.000Z" }),
146
+ ];
147
+ assert.equal(projectProceduresToSkills(memories, { maxSkills: 1 }).length, 1);
148
+ // Newest first.
149
+ assert.equal(projectProceduresToSkills(memories, { maxSkills: 1 })[0].slug, "task-a");
150
+ assert.deepEqual(projectProceduresToSkills(memories, { maxSkills: 0 }), []);
151
+ });
152
+
153
+ test("rendered frontmatter survives titles containing YAML metacharacters", () => {
154
+ const bundles = projectProceduresToSkills([
155
+ makeProcedure({ id: "procedure-1", title: "When you work on goals like: ship #2369" }),
156
+ ]);
157
+ const parsed = parseSkillBundle(renderSkillBundle(bundles[0]), bundles[0].slug);
158
+ assert.equal(parsed?.description, "When you work on goals like: ship #2369");
159
+ assert.equal(bundles[0].slug, "when-you-work-on-goals-like-ship-2369");
160
+ });
161
+
162
+ test("parseSkillBundle tolerates a step-less body and a missing frontmatter block", () => {
163
+ const parsed = parseSkillBundle("Just prose, no steps.\n", "prose");
164
+ assert.equal(parsed?.steps, null);
165
+ assert.equal(parsed?.body, "Just prose, no steps.");
166
+ assert.equal(parsed?.provenance.memoryId, undefined);
167
+ assert.equal(parseSkillBundle("---\nname: empty\n---\n\n", "empty"), null);
168
+ });
169
+
170
+ test("parseSkillBundle ignores nested frontmatter sequences", () => {
171
+ const text = [
172
+ "---",
173
+ "name: deploy",
174
+ "description: Deploy the thing",
175
+ "allowed-tools:",
176
+ " - remnic_recall",
177
+ "---",
178
+ "",
179
+ "Deploy the thing",
180
+ "",
181
+ "## Step 1",
182
+ "",
183
+ "Push the button.",
184
+ "",
185
+ ].join("\n");
186
+ const parsed = parseSkillBundle(text, "deploy");
187
+ assert.equal(parsed?.description, "Deploy the thing");
188
+ assert.equal(parsed?.steps?.length, 1);
189
+ assert.equal(parsed?.steps?.[0].intent, "Push the button.");
190
+ });
191
+
192
+ test("sanitizeSkillSlug produces valid slugs for hostile titles", () => {
193
+ assert.equal(sanitizeSkillSlug(" /// "), "procedure");
194
+ assert.equal(sanitizeSkillSlug("../../etc/passwd"), "etc-passwd");
195
+ assert.equal(sanitizeSkillSlug("Ünïcodé Title"), "n-cod-title");
196
+ assert.equal(sanitizeSkillSlug("A".repeat(120)).length, 64);
197
+ });
198
+
199
+ test("parseSkillProjectionConfig defaults off, coerces strings, and rejects garbage", () => {
200
+ assert.deepEqual(parseSkillProjectionConfig(undefined), { enabled: false, maxSkills: 20 });
201
+ assert.deepEqual(parseSkillProjectionConfig({ enabled: "true", maxSkills: "5" }), {
202
+ enabled: true,
203
+ maxSkills: 5,
204
+ });
205
+ // String "false"/"0" must read as falsy / as the documented disable value.
206
+ assert.deepEqual(parseSkillProjectionConfig({ enabled: "false", maxSkills: "0" }), {
207
+ enabled: false,
208
+ maxSkills: 0,
209
+ });
210
+ assert.throws(() => parseSkillProjectionConfig({ enabled: "maybe" }), /skillProjection\.enabled/);
211
+ assert.throws(() => parseSkillProjectionConfig({ maxSkills: 2.5 }), /integer/);
212
+ assert.throws(() => parseSkillProjectionConfig({ maxSkills: -1 }), /between 0/);
213
+ assert.throws(() => parseSkillProjectionConfig([]), /must be an object/);
214
+ });
@@ -0,0 +1,356 @@
1
+ /**
2
+ * skill-projection.ts — project active `procedure` memories into portable
3
+ * `skills/<slug>/SKILL.md` bundles, and parse hand-authored bundles back into
4
+ * procedure candidates (issue #2369).
5
+ *
6
+ * Pure module: no filesystem, no host coupling. The Codex materializer, the
7
+ * `remnic export skills` CLI, and `remnic import skills` all consume these
8
+ * functions so one slug + format contract serves every surface.
9
+ *
10
+ * Import is INERT by construction: this module only ever returns text and
11
+ * parsed steps. It never resolves, loads, or executes a bundle resource.
12
+ */
13
+
14
+ import { coerceBool, coerceNumber } from "../connectors/coerce.js";
15
+ import { isValidSkillSlug } from "../skills-registry.js";
16
+ import { stripAttributesSuffix } from "../structured-attributes.js";
17
+ import type { MemoryFile, MemoryStatus } from "../types.js";
18
+ import { parseProcedureStepsFromBody, type ProcedureStep } from "./procedure-types.js";
19
+
20
+ /** Provenance frontmatter keys. Namespaced so hosts treat them as opaque. */
21
+ export const SKILL_MEMORY_ID_KEY = "x-remnic-memory-id";
22
+ export const SKILL_UPDATED_KEY = "x-remnic-updated";
23
+ export const SKILL_SOURCE_KEY = "x-remnic-source";
24
+
25
+ /** File name every bundle directory must contain. */
26
+ export const SKILL_FILE_NAME = "SKILL.md";
27
+
28
+ /** Source tag written on imported procedure memories. */
29
+ export const SKILL_IMPORT_SOURCE = "skill-import";
30
+
31
+ /**
32
+ * The ONLY statuses that may be projected (Review Prevention Checklist §41 —
33
+ * define the active set, never an exclusion list). `pending_review`,
34
+ * `rejected`, `quarantined`, `superseded`, `archived`, and `forgotten` are
35
+ * withheld because a projected skill is loaded by a host with no further gate.
36
+ */
37
+ const PROJECTABLE_STATUSES: Partial<Record<MemoryStatus, true>> = { active: true };
38
+
39
+ /** Default projection cap. `0` disables projection entirely. */
40
+ export const DEFAULT_SKILL_PROJECTION_MAX_SKILLS = 20;
41
+
42
+ /** Hard ceiling so a misconfigured cap cannot flood a host skills directory. */
43
+ const MAX_SKILL_PROJECTION_MAX_SKILLS = 500;
44
+
45
+ const MAX_SLUG_LENGTH = 64;
46
+ const MAX_DESCRIPTION_LENGTH = 300;
47
+
48
+ export interface SkillBundleProvenance {
49
+ /** Remnic memory id this bundle was projected from. */
50
+ memoryId: string;
51
+ /** `frontmatter.updated` of the source memory. */
52
+ updated: string;
53
+ /** `frontmatter.source` of the source memory, when present. */
54
+ source?: string;
55
+ }
56
+
57
+ export interface SkillBundle {
58
+ slug: string;
59
+ frontmatter: { name: string; description: string };
60
+ /** Markdown body: the stored procedure body (title line + `## Step N`). */
61
+ body: string;
62
+ provenance: SkillBundleProvenance;
63
+ }
64
+
65
+ export interface ProjectProceduresOptions {
66
+ /** Cap on projected bundles. `0` projects nothing. Default 20. */
67
+ maxSkills?: number;
68
+ /** Slugs a projected bundle must not take (e.g. `BUILTIN_SKILLS` slugs). */
69
+ reservedSlugs?: readonly string[];
70
+ }
71
+
72
+ export interface SkillProjectionConfig {
73
+ enabled: boolean;
74
+ maxSkills: number;
75
+ }
76
+
77
+ /**
78
+ * Parse the `procedural.skillProjection` config block. Mirrors
79
+ * `parseProceduralMaintenanceConfig`: string forms (`"false"`, `"0"`) coerce,
80
+ * unrecognized values throw rather than silently defaulting.
81
+ */
82
+ export function parseSkillProjectionConfig(raw: unknown): SkillProjectionConfig {
83
+ if (raw !== undefined && (raw === null || typeof raw !== "object" || Array.isArray(raw))) {
84
+ throw new Error(
85
+ `procedural.skillProjection must be an object (got ${JSON.stringify(raw)}). Omit the key to keep skill projection disabled (issue #2369).`,
86
+ );
87
+ }
88
+ const block = raw && typeof raw === "object" ? (raw as Record<string, unknown>) : {};
89
+
90
+ let enabled = false;
91
+ if (block.enabled !== undefined) {
92
+ const coerced = coerceBool(block.enabled);
93
+ if (coerced === undefined) {
94
+ throw new Error(
95
+ `procedural.skillProjection.enabled must be a boolean or one of "true"/"false"/"1"/"0"/"yes"/"no"/"on"/"off" (got ${JSON.stringify(block.enabled)}).`,
96
+ );
97
+ }
98
+ enabled = coerced;
99
+ }
100
+
101
+ let maxSkills = DEFAULT_SKILL_PROJECTION_MAX_SKILLS;
102
+ if (block.maxSkills !== undefined) {
103
+ const coerced = coerceNumber(block.maxSkills);
104
+ if (coerced === undefined || !Number.isFinite(coerced)) {
105
+ throw new Error(
106
+ `procedural.skillProjection.maxSkills must be a finite number (got ${JSON.stringify(block.maxSkills)}).`,
107
+ );
108
+ }
109
+ if (!Number.isInteger(coerced)) {
110
+ throw new Error(
111
+ `procedural.skillProjection.maxSkills must be an integer (got ${JSON.stringify(block.maxSkills)}).`,
112
+ );
113
+ }
114
+ if (coerced < 0 || coerced > MAX_SKILL_PROJECTION_MAX_SKILLS) {
115
+ throw new Error(
116
+ `procedural.skillProjection.maxSkills must be between 0 and ${MAX_SKILL_PROJECTION_MAX_SKILLS} (got ${JSON.stringify(block.maxSkills)}). 0 disables projection.`,
117
+ );
118
+ }
119
+ // `0` is a documented disable value (§33) — never clamp it up.
120
+ maxSkills = coerced;
121
+ }
122
+
123
+ return { enabled, maxSkills };
124
+ }
125
+
126
+ /**
127
+ * Sanitize arbitrary text into a slug `isValidSkillSlug` accepts.
128
+ * Deterministic: the same title always yields the same slug.
129
+ */
130
+ export function sanitizeSkillSlug(input: string): string {
131
+ const base = input
132
+ .toLowerCase()
133
+ .replace(/[^a-z0-9]+/g, "-")
134
+ .slice(0, MAX_SLUG_LENGTH);
135
+ const trimmed = trimDashes(base);
136
+ return trimmed.length > 0 && isValidSkillSlug(trimmed) ? trimmed : "procedure";
137
+ }
138
+
139
+ function trimDashes(value: string): string {
140
+ let start = 0;
141
+ let end = value.length;
142
+ while (start < end && value[start] === "-") start += 1;
143
+ while (end > start && value[end - 1] === "-") end -= 1;
144
+ return value.slice(start, end);
145
+ }
146
+
147
+ /** Append a suffix while keeping the slug valid and within the length cap. */
148
+ function withSuffix(base: string, suffix: string): string {
149
+ const room = MAX_SLUG_LENGTH - suffix.length;
150
+ const head = trimDashes(base.slice(0, Math.max(1, room)));
151
+ const candidate = `${head.length > 0 ? head : "procedure"}${suffix}`;
152
+ return isValidSkillSlug(candidate) ? candidate : `procedure${suffix}`;
153
+ }
154
+
155
+ /**
156
+ * Reserve a slug: prefix reserved (built-in) collisions with `user-`, then
157
+ * disambiguate duplicates with a stable `-2`, `-3`, … tiebreak so a batch with
158
+ * repeated titles never writes two bundles to one directory (§37).
159
+ */
160
+ function allocateSlug(base: string, used: Set<string>, reserved: ReadonlySet<string>): string {
161
+ let candidate = reserved.has(base) ? withSuffix(`user-${base}`, "") : base;
162
+ if (!isValidSkillSlug(candidate)) candidate = "procedure";
163
+ if (!used.has(candidate) && !reserved.has(candidate)) {
164
+ used.add(candidate);
165
+ return candidate;
166
+ }
167
+ for (let n = 2; n < 10_000; n++) {
168
+ const next = withSuffix(candidate, `-${n}`);
169
+ if (!used.has(next) && !reserved.has(next)) {
170
+ used.add(next);
171
+ return next;
172
+ }
173
+ }
174
+ // Unreachable in practice (10k distinct suffixes per base slug).
175
+ throw new Error(`skill-projection: could not allocate a unique slug for ${base}`);
176
+ }
177
+
178
+ /** First non-empty line of the body, with a leading markdown heading stripped. */
179
+ export function procedureTitleFromBody(body: string): string {
180
+ for (const line of body.replace(/\r\n/g, "\n").split("\n")) {
181
+ const trimmed = line.trim();
182
+ if (trimmed.length === 0) continue;
183
+ if (/^##\s+Step\s+\d+/i.test(trimmed)) break;
184
+ return trimmed.replace(/^#{1,6}\s*/, "").trim();
185
+ }
186
+ return "";
187
+ }
188
+
189
+ /** Total comparator: newest first, `id` as the stable tiebreak (§12). */
190
+ function compareProcedures(a: MemoryFile, b: MemoryFile): number {
191
+ const aTs = Date.parse(a.frontmatter.updated ?? "");
192
+ const bTs = Date.parse(b.frontmatter.updated ?? "");
193
+ const aVal = Number.isFinite(aTs) ? aTs : 0;
194
+ const bVal = Number.isFinite(bTs) ? bTs : 0;
195
+ if (aVal !== bVal) return bVal - aVal;
196
+ const aId = a.frontmatter.id ?? "";
197
+ const bId = b.frontmatter.id ?? "";
198
+ if (aId < bId) return -1;
199
+ if (aId > bId) return 1;
200
+ return 0;
201
+ }
202
+
203
+ /**
204
+ * Project procedure memories into skill bundles. Filters to `active`
205
+ * procedures, orders deterministically, caps at `maxSkills`, and assigns
206
+ * collision-free slugs.
207
+ */
208
+ export function projectProceduresToSkills(
209
+ memories: readonly MemoryFile[],
210
+ options: ProjectProceduresOptions = {},
211
+ ): SkillBundle[] {
212
+ const maxSkills =
213
+ typeof options.maxSkills === "number" && Number.isInteger(options.maxSkills) && options.maxSkills >= 0
214
+ ? Math.min(MAX_SKILL_PROJECTION_MAX_SKILLS, options.maxSkills)
215
+ : DEFAULT_SKILL_PROJECTION_MAX_SKILLS;
216
+ if (maxSkills === 0) return [];
217
+
218
+ const reserved = new Set((options.reservedSlugs ?? []).map((s) => s.trim().toLowerCase()));
219
+ const candidates = memories
220
+ .filter(
221
+ (m) =>
222
+ m.frontmatter.category === "procedure" &&
223
+ PROJECTABLE_STATUSES[(m.frontmatter.status ?? "active") as MemoryStatus] === true &&
224
+ typeof m.content === "string" &&
225
+ m.content.trim().length > 0,
226
+ )
227
+ .sort(compareProcedures)
228
+ .slice(0, maxSkills);
229
+
230
+ const used = new Set<string>();
231
+ const bundles: SkillBundle[] = [];
232
+ for (const memory of candidates) {
233
+ // Drop the machine "[Attributes: ...]" footer writeMemory appends: a human
234
+ // skill body must not carry it, and leaving it in would let the footer be
235
+ // absorbed into the final step's intent on re-parse.
236
+ const body = stripAttributesSuffix(memory.content.replace(/\r\n/g, "\n")).trim();
237
+ const title = procedureTitleFromBody(body) || memory.frontmatter.id;
238
+ const slug = allocateSlug(sanitizeSkillSlug(title), used, reserved);
239
+ bundles.push({
240
+ slug,
241
+ frontmatter: {
242
+ name: slug,
243
+ description: title.slice(0, MAX_DESCRIPTION_LENGTH),
244
+ },
245
+ body: `${body}\n`,
246
+ provenance: {
247
+ memoryId: memory.frontmatter.id,
248
+ updated: memory.frontmatter.updated,
249
+ ...(memory.frontmatter.source ? { source: memory.frontmatter.source } : {}),
250
+ },
251
+ });
252
+ }
253
+ return bundles;
254
+ }
255
+
256
+ /**
257
+ * Render one bundle as SKILL.md text. Scalars are emitted as YAML
258
+ * double-quoted strings so a title containing `:` or `#` cannot corrupt the
259
+ * frontmatter block.
260
+ */
261
+ export function renderSkillBundle(bundle: SkillBundle): string {
262
+ const lines = [
263
+ "---",
264
+ `name: ${yamlScalar(bundle.frontmatter.name)}`,
265
+ `description: ${yamlScalar(bundle.frontmatter.description)}`,
266
+ `${SKILL_MEMORY_ID_KEY}: ${yamlScalar(bundle.provenance.memoryId)}`,
267
+ `${SKILL_UPDATED_KEY}: ${yamlScalar(bundle.provenance.updated)}`,
268
+ ];
269
+ if (bundle.provenance.source) {
270
+ lines.push(`${SKILL_SOURCE_KEY}: ${yamlScalar(bundle.provenance.source)}`);
271
+ }
272
+ lines.push("---", "");
273
+ return `${lines.join("\n")}\n${bundle.body.trimEnd()}\n`;
274
+ }
275
+
276
+ function yamlScalar(value: string): string {
277
+ return JSON.stringify(value.replace(/\r?\n/g, " ").trim());
278
+ }
279
+
280
+ export interface ParsedSkillBundle {
281
+ /** Directory name the bundle was read from (already sanitized by the caller). */
282
+ slug: string;
283
+ name?: string;
284
+ description?: string;
285
+ /** Markdown body with the frontmatter block removed. */
286
+ body: string;
287
+ /** Parsed `## Step N` sections, or `null` for a step-less body. */
288
+ steps: ProcedureStep[] | null;
289
+ provenance: { memoryId?: string; updated?: string; source?: string };
290
+ }
291
+
292
+ /**
293
+ * Parse SKILL.md text. Frontmatter is optional; unknown keys are ignored.
294
+ * Returns `null` when there is no body to import.
295
+ */
296
+ export function parseSkillBundle(text: string, slug: string): ParsedSkillBundle | null {
297
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
298
+ // Line scan rather than a lazy `[\s\S]*?` fence regex (issue #2439).
299
+ let frontmatter: Record<string, string> = {};
300
+ let bodyLines = lines;
301
+ if (lines[0]?.trim() === "---") {
302
+ const close = lines.findIndex((line, idx) => idx > 0 && line.trim() === "---");
303
+ if (close !== -1) {
304
+ frontmatter = parseFlatFrontmatter(lines.slice(1, close).join("\n"));
305
+ bodyLines = lines.slice(close + 1);
306
+ }
307
+ }
308
+ const body = bodyLines.join("\n").trim();
309
+ if (body.length === 0) return null;
310
+ return {
311
+ slug,
312
+ name: frontmatter.name,
313
+ description: frontmatter.description,
314
+ body,
315
+ steps: parseProcedureStepsFromBody(body),
316
+ provenance: {
317
+ memoryId: frontmatter[SKILL_MEMORY_ID_KEY],
318
+ updated: frontmatter[SKILL_UPDATED_KEY],
319
+ source: frontmatter[SKILL_SOURCE_KEY],
320
+ },
321
+ };
322
+ }
323
+
324
+ /**
325
+ * Minimal flat `key: value` frontmatter reader. Nested blocks and sequences
326
+ * (e.g. `allowed-tools:`) are skipped — the import path only consumes scalars.
327
+ */
328
+ function parseFlatFrontmatter(block: string): Record<string, string> {
329
+ const out: Record<string, string> = {};
330
+ for (const line of block.split("\n")) {
331
+ if (/^\s/.test(line) || line.trim().startsWith("#")) continue;
332
+ const colon = line.indexOf(":");
333
+ if (colon <= 0) continue;
334
+ const key = line.slice(0, colon).trim();
335
+ const raw = line.slice(colon + 1).trim();
336
+ if (key.length === 0 || raw.length === 0) continue;
337
+ out[key] = unquoteScalar(raw);
338
+ }
339
+ return out;
340
+ }
341
+
342
+ function unquoteScalar(raw: string): string {
343
+ if (raw.startsWith('"') && raw.endsWith('"') && raw.length >= 2) {
344
+ try {
345
+ const parsed = JSON.parse(raw) as unknown;
346
+ if (typeof parsed === "string") return parsed;
347
+ } catch {
348
+ // fall through to the literal slice below
349
+ }
350
+ return raw.slice(1, -1);
351
+ }
352
+ if (raw.startsWith("'") && raw.endsWith("'") && raw.length >= 2) {
353
+ return raw.slice(1, -1);
354
+ }
355
+ return raw;
356
+ }
package/src/types.ts CHANGED
@@ -13,6 +13,7 @@ export type { ContradictionLocalizationConfig, ContradictionScanConfig } from ".
13
13
  export type { GraphPathScoringConfig } from "./graph-path-scoring-config.js";
14
14
  import type { DriftDetectionSettings, MemoryDriftProvenance, RecallDriftAnnotation } from "./preferences/drift-types.js";
15
15
  import type { ProceduralMaintenanceConfig } from "./procedural/maintenance-config.js";
16
+ import type { SkillProjectionConfig } from "./procedural/skill-projection.js";
16
17
  import type { ActionGateConfig } from "./coding/action-gate.js";
17
18
 
18
19
  import type { AmbientCaptureProvenance, BufferTurnOwner, SecurityConfig, OriginMetadata } from "./security/types.js";
@@ -390,11 +391,8 @@ export interface DreamsPhasesConfig {
390
391
 
391
392
  export interface ProceduralConfig {
392
393
  enabled: boolean;
393
- /** Minimum cluster size before emitting a candidate; `0` disables mining (`minOccurrences_zero`). */
394
394
  minOccurrences: number;
395
- /** Minimum success rate from trajectory outcomes in [0, 1]. */
396
395
  successFloor: number;
397
- /** When auto-promotion is enabled, promote pending_review → active after this many occurrences. */
398
396
  autoPromoteOccurrences: number;
399
397
  autoPromoteEnabled: boolean;
400
398
  lookbackDays: number;
@@ -402,6 +400,7 @@ export interface ProceduralConfig {
402
400
  proceduralMiningCronAutoRegister: boolean;
403
401
  recallMaxProcedures: number;
404
402
  maintenance: ProceduralMaintenanceConfig;
403
+ skillProjection: SkillProjectionConfig;
405
404
  }
406
405
 
407
406
  /**