@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.
- package/dist/access-admin-ops-surface.d.ts +11 -11
- package/dist/access-admin-ops-surface.js +20 -18
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +21 -19
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +20 -18
- package/dist/access-cli.js +55 -53
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +1 -1
- package/dist/access-extraction-force-flush.d.ts +11 -11
- package/dist/access-extraction-force-flush.js +20 -18
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -11
- package/dist/access-http-lifecycle-flush.d.ts +11 -11
- package/dist/access-http-offline-stream.d.ts +11 -11
- package/dist/access-http-query.d.ts +2 -2
- package/dist/access-http-query.js +22 -20
- package/dist/access-http.d.ts +12 -12
- package/dist/access-http.js +31 -29
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +20 -18
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +20 -18
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +25 -23
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-B7Usyz-K.d.ts → access-namespace-preflight-D_6mo8ms.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +20 -18
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +20 -18
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-offline-manifest.js +3 -3
- package/dist/access-operations-batch.js +21 -19
- package/dist/access-operations.d.ts +16 -16
- package/dist/access-operations.js +24 -22
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +20 -18
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +20 -18
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +20 -18
- package/dist/access-schema.d.ts +83 -83
- package/dist/access-service-helpers.d.ts +11 -11
- package/dist/{access-service-DkaIG3n8.d.ts → access-service-sbiNS5UB.d.ts} +71 -71
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +20 -18
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-surface-catalog.js +22 -20
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +3 -3
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +5 -1
- package/dist/active-recall.js.map +1 -1
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-H64ZCNIR.js → auto-sync-XVMLL2RH.js} +5 -5
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +4 -4
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-CX2BZEJh.d.ts → catalog-DO_7zfkv.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +9 -6
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/chunk-236PXYG7.js +232 -0
- package/dist/chunk-236PXYG7.js.map +1 -0
- package/dist/{chunk-YF6UHWOV.js → chunk-2EPOGPKO.js} +3 -3
- package/dist/{chunk-IQSC3V2E.js → chunk-2IXQSGCU.js} +2 -2
- package/dist/{chunk-GUXS322H.js → chunk-2MVTAIL3.js} +2 -2
- package/dist/{chunk-TDKSCV62.js → chunk-3NH3VP3U.js} +2 -2
- package/dist/{chunk-6TAX66IA.js → chunk-4JSF7UUP.js} +2 -2
- package/dist/{chunk-URHCUXKI.js → chunk-AXGFOFNY.js} +2 -2
- package/dist/{chunk-BKYPJUQL.js → chunk-BA2GLSIP.js} +10 -10
- package/dist/{chunk-D27J626P.js → chunk-BK5G2MHO.js} +2 -2
- package/dist/{chunk-EV3YFLCB.js → chunk-BW4GAF57.js} +2 -2
- package/dist/{chunk-DHTQP334.js → chunk-CQBIN77D.js} +2 -2
- package/dist/{chunk-WN5FSGLE.js → chunk-CV3GNPOE.js} +45 -50
- package/dist/chunk-CV3GNPOE.js.map +1 -0
- package/dist/{chunk-6VALIZKG.js → chunk-DORFF5N5.js} +2 -2
- package/dist/{chunk-7QOO4LGC.js → chunk-DPGH6LHQ.js} +331 -73
- package/dist/chunk-DPGH6LHQ.js.map +1 -0
- package/dist/{chunk-76PL4XLG.js → chunk-DPNXADEI.js} +7 -2
- package/dist/chunk-DPNXADEI.js.map +1 -0
- package/dist/{chunk-MRTUN665.js → chunk-DUEDOYZD.js} +2 -2
- package/dist/{chunk-QFMO32TO.js → chunk-DUZARALI.js} +3 -3
- package/dist/{chunk-7KFIMGMP.js → chunk-ECLJ4LA2.js} +2 -2
- package/dist/{chunk-PDVQ5JU3.js → chunk-FBR7PIL4.js} +2 -2
- package/dist/{chunk-RK4WXUI2.js → chunk-HF5BUTI6.js} +2 -2
- package/dist/{chunk-3DVBKTSO.js → chunk-HSZMX3ZJ.js} +4 -4
- package/dist/{chunk-ODZG724P.js → chunk-I2HTKOOP.js} +2 -2
- package/dist/{chunk-GN55KU2P.js → chunk-IIRCHGWD.js} +2 -2
- package/dist/{chunk-AYYGRVRP.js → chunk-IURMJBGP.js} +2 -2
- package/dist/{chunk-4XDGYCZI.js → chunk-J3ATBB7M.js} +2 -2
- package/dist/{chunk-E4MVQQVX.js → chunk-JPAKBNTM.js} +2 -2
- package/dist/{chunk-NZWEEALH.js → chunk-KFIOPZ25.js} +2 -2
- package/dist/{chunk-EIVIB73I.js → chunk-KOO37E4Y.js} +2 -2
- package/dist/{chunk-VA5KMSAE.js → chunk-KTPXXVDQ.js} +3 -3
- package/dist/{chunk-NXKJHSDN.js → chunk-LBFFNDFM.js} +1 -1
- package/dist/chunk-LBFFNDFM.js.map +1 -0
- package/dist/{chunk-Z4RPXHJH.js → chunk-LYBIHM4N.js} +4 -4
- package/dist/{chunk-G57LQRP6.js → chunk-MFOSZUIT.js} +2 -2
- package/dist/{chunk-TYUS5TLG.js → chunk-N56GVOWR.js} +3 -3
- package/dist/{chunk-BQUEAVNW.js → chunk-NH6LC57D.js} +2 -2
- package/dist/{chunk-AUO2LJ3K.js → chunk-NLEH4RTB.js} +2 -2
- package/dist/{chunk-AYDDB3XI.js → chunk-OCCHUEZB.js} +2 -2
- package/dist/{chunk-57HVIG5U.js → chunk-OWQNJJY2.js} +2 -2
- package/dist/{chunk-OJD4WS2E.js → chunk-OZDFB22S.js} +2 -2
- package/dist/{chunk-2VXWFYJE.js → chunk-R2MZCYA6.js} +3 -3
- package/dist/{chunk-LBWQ65SX.js → chunk-T42L3S6A.js} +4 -4
- package/dist/{chunk-PAYGC3BD.js → chunk-THVI4Y7X.js} +7 -7
- package/dist/{chunk-S7Q5CM7E.js → chunk-TJFI3K2O.js} +15 -3
- package/dist/chunk-TJFI3K2O.js.map +1 -0
- package/dist/{chunk-R47WINCY.js → chunk-UCKG36HO.js} +2 -2
- package/dist/{chunk-QXIQPCCN.js → chunk-UFR6HX7A.js} +1 -1
- package/dist/{chunk-CDUUDBWJ.js → chunk-VFYRYFLL.js} +2 -2
- package/dist/{chunk-P4DHENRE.js → chunk-WDXD6ITO.js} +2 -2
- package/dist/{chunk-H5FW4S5Q.js → chunk-YB4GBWBU.js} +31 -31
- package/dist/{chunk-MBDJJTAT.js → chunk-YDKF4MSJ.js} +3 -3
- package/dist/{chunk-AZAA5DNP.js → chunk-YOXVZJFM.js} +2 -2
- package/dist/{chunk-OLWTCWQ2.js → chunk-YZJM6CLF.js} +8 -8
- package/dist/chunk-ZCUTSMR2.js +61 -0
- package/dist/chunk-ZCUTSMR2.js.map +1 -0
- package/dist/{chunk-LUNJ3WFO.js → chunk-ZK4BFABK.js} +4 -4
- package/dist/{chunk-RDFF2NAU.js → chunk-ZXBTTMGZ.js} +184 -88
- package/dist/chunk-ZXBTTMGZ.js.map +1 -0
- package/dist/{cli-ChKeYMZv.d.ts → cli-BqdqPdG_.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +44 -42
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +4 -4
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +5 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +8 -5
- package/dist/connectors/codex-materialize.d.ts +14 -1
- package/dist/connectors/codex-materialize.js +10 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +8 -5
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +3 -3
- package/dist/contradiction/index.js +1 -1
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +6 -6
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +6 -6
- package/dist/cross-lingual-recall.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-B8ZMBBCY.d.ts → dependency-propagation-delivery-D23_x-s5.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-origin-fields.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +4 -4
- package/dist/entity-schema.d.ts +1 -1
- package/dist/episodic-context.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/explicit-capture.js +1 -1
- package/dist/external-wiki-access.d.ts +11 -11
- package/dist/external-wiki-access.js +21 -19
- package/dist/external-wiki-collection-registration.d.ts +1 -1
- package/dist/external-wiki-collection.d.ts +1 -1
- package/dist/external-wiki-mcp-tools.d.ts +11 -11
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-judge.js +2 -2
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-normalization.d.ts +1 -1
- package/dist/extraction-prompt.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +9 -7
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/{index-CexDrJzC.d.ts → index--QhGIXF2.d.ts} +2 -2
- package/dist/index.d.ts +763 -716
- package/dist/index.js +79 -58
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +4 -4
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -4
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +5 -5
- package/dist/{maintenance-nkdtSrVr.d.ts → maintenance-PFvY1Pw_.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -11
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-subject-cli.d.ts +11 -11
- package/dist/memory-subject.d.ts +11 -11
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +5 -5
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +4 -4
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +7 -7
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +16 -13
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +10 -10
- package/dist/{orchestrator-Bvq1-RQs.d.ts → orchestrator-4wzJrPVm.d.ts} +8 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +55 -53
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-C8vfIiTX.d.ts → pipeline-CgXgdIrF.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{public-http-Cy0aYbys.d.ts → public-http-BP3VQDl5.d.ts} +1 -1
- package/dist/{qmd-B9p1SZTZ.d.ts → qmd-BppGGxsi.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/manifest.js +2 -2
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolution-4552BNRV.js +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +6 -2
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/salvage-envelope.js +1 -1
- package/dist/schemas.d.ts +136 -136
- package/dist/{scope-profiles-DLrRxFWR.d.ts → scope-profiles-ChZUtxDU.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-DlN6xe20.d.ts → semantic-consolidation-mSPwQSmF.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +9 -6
- package/dist/semantic-rule-promotion.js +4 -4
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +4 -4
- package/dist/{service-GkYyVfm9.d.ts → service-BICWLwSP.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +7 -7
- package/dist/signal.d.ts +1 -1
- package/dist/skills-registry.js +4 -56
- package/dist/skills-registry.js.map +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +8 -5
- package/dist/{storage-BIDzuKIn.d.ts → storage-B7urx1K3.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +6 -6
- package/dist/subject-config.d.ts +1 -1
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +14 -14
- package/dist/support-passport/index.js +23 -21
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/types.d.ts +78 -78
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-BxAcOl5Q.d.ts → types-ETU5N6nB.d.ts} +111 -4
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +4 -4
- package/dist/who-knows-cli.d.ts +11 -11
- package/dist/who-knows-cli.js +5 -5
- package/dist/who-knows.d.ts +11 -11
- package/dist/who-knows.js +4 -4
- package/dist/write-envelope.d.ts +1 -1
- package/dist/write-envelope.js +1 -1
- package/package.json +2 -2
- package/src/cli/skills-commands.ts +150 -0
- package/src/cli.ts +4 -2
- package/src/config.ts +4 -2
- package/src/connectors/codex-materialize-runner.ts +16 -0
- package/src/connectors/codex-materialize-skills.test.ts +141 -0
- package/src/connectors/codex-materialize.ts +58 -40
- package/src/connectors/materialize-paths.ts +50 -0
- package/src/connectors/materialize-skills.ts +127 -0
- package/src/index.ts +5 -10
- package/src/procedural/skill-io.test.ts +190 -0
- package/src/procedural/skill-io.ts +220 -0
- package/src/procedural/skill-projection.test.ts +214 -0
- package/src/procedural/skill-projection.ts +356 -0
- package/src/review/frontmatter.test.ts +15 -0
- package/src/review/frontmatter.ts +20 -0
- package/src/review/review-deck-snapshot.ts +4 -20
- package/src/review/review-deck.ts +1 -12
- package/src/types.ts +2 -3
- package/dist/chunk-76PL4XLG.js.map +0 -1
- package/dist/chunk-7QOO4LGC.js.map +0 -1
- package/dist/chunk-NXKJHSDN.js.map +0 -1
- package/dist/chunk-RDFF2NAU.js.map +0 -1
- package/dist/chunk-S7Q5CM7E.js.map +0 -1
- package/dist/chunk-WN5FSGLE.js.map +0 -1
- /package/dist/{auto-sync-H64ZCNIR.js.map → auto-sync-XVMLL2RH.js.map} +0 -0
- /package/dist/{chunk-YF6UHWOV.js.map → chunk-2EPOGPKO.js.map} +0 -0
- /package/dist/{chunk-IQSC3V2E.js.map → chunk-2IXQSGCU.js.map} +0 -0
- /package/dist/{chunk-GUXS322H.js.map → chunk-2MVTAIL3.js.map} +0 -0
- /package/dist/{chunk-TDKSCV62.js.map → chunk-3NH3VP3U.js.map} +0 -0
- /package/dist/{chunk-6TAX66IA.js.map → chunk-4JSF7UUP.js.map} +0 -0
- /package/dist/{chunk-URHCUXKI.js.map → chunk-AXGFOFNY.js.map} +0 -0
- /package/dist/{chunk-BKYPJUQL.js.map → chunk-BA2GLSIP.js.map} +0 -0
- /package/dist/{chunk-D27J626P.js.map → chunk-BK5G2MHO.js.map} +0 -0
- /package/dist/{chunk-EV3YFLCB.js.map → chunk-BW4GAF57.js.map} +0 -0
- /package/dist/{chunk-DHTQP334.js.map → chunk-CQBIN77D.js.map} +0 -0
- /package/dist/{chunk-6VALIZKG.js.map → chunk-DORFF5N5.js.map} +0 -0
- /package/dist/{chunk-MRTUN665.js.map → chunk-DUEDOYZD.js.map} +0 -0
- /package/dist/{chunk-QFMO32TO.js.map → chunk-DUZARALI.js.map} +0 -0
- /package/dist/{chunk-7KFIMGMP.js.map → chunk-ECLJ4LA2.js.map} +0 -0
- /package/dist/{chunk-PDVQ5JU3.js.map → chunk-FBR7PIL4.js.map} +0 -0
- /package/dist/{chunk-RK4WXUI2.js.map → chunk-HF5BUTI6.js.map} +0 -0
- /package/dist/{chunk-3DVBKTSO.js.map → chunk-HSZMX3ZJ.js.map} +0 -0
- /package/dist/{chunk-ODZG724P.js.map → chunk-I2HTKOOP.js.map} +0 -0
- /package/dist/{chunk-GN55KU2P.js.map → chunk-IIRCHGWD.js.map} +0 -0
- /package/dist/{chunk-AYYGRVRP.js.map → chunk-IURMJBGP.js.map} +0 -0
- /package/dist/{chunk-4XDGYCZI.js.map → chunk-J3ATBB7M.js.map} +0 -0
- /package/dist/{chunk-E4MVQQVX.js.map → chunk-JPAKBNTM.js.map} +0 -0
- /package/dist/{chunk-NZWEEALH.js.map → chunk-KFIOPZ25.js.map} +0 -0
- /package/dist/{chunk-EIVIB73I.js.map → chunk-KOO37E4Y.js.map} +0 -0
- /package/dist/{chunk-VA5KMSAE.js.map → chunk-KTPXXVDQ.js.map} +0 -0
- /package/dist/{chunk-Z4RPXHJH.js.map → chunk-LYBIHM4N.js.map} +0 -0
- /package/dist/{chunk-G57LQRP6.js.map → chunk-MFOSZUIT.js.map} +0 -0
- /package/dist/{chunk-TYUS5TLG.js.map → chunk-N56GVOWR.js.map} +0 -0
- /package/dist/{chunk-BQUEAVNW.js.map → chunk-NH6LC57D.js.map} +0 -0
- /package/dist/{chunk-AUO2LJ3K.js.map → chunk-NLEH4RTB.js.map} +0 -0
- /package/dist/{chunk-AYDDB3XI.js.map → chunk-OCCHUEZB.js.map} +0 -0
- /package/dist/{chunk-57HVIG5U.js.map → chunk-OWQNJJY2.js.map} +0 -0
- /package/dist/{chunk-OJD4WS2E.js.map → chunk-OZDFB22S.js.map} +0 -0
- /package/dist/{chunk-2VXWFYJE.js.map → chunk-R2MZCYA6.js.map} +0 -0
- /package/dist/{chunk-LBWQ65SX.js.map → chunk-T42L3S6A.js.map} +0 -0
- /package/dist/{chunk-PAYGC3BD.js.map → chunk-THVI4Y7X.js.map} +0 -0
- /package/dist/{chunk-R47WINCY.js.map → chunk-UCKG36HO.js.map} +0 -0
- /package/dist/{chunk-QXIQPCCN.js.map → chunk-UFR6HX7A.js.map} +0 -0
- /package/dist/{chunk-CDUUDBWJ.js.map → chunk-VFYRYFLL.js.map} +0 -0
- /package/dist/{chunk-P4DHENRE.js.map → chunk-WDXD6ITO.js.map} +0 -0
- /package/dist/{chunk-H5FW4S5Q.js.map → chunk-YB4GBWBU.js.map} +0 -0
- /package/dist/{chunk-MBDJJTAT.js.map → chunk-YDKF4MSJ.js.map} +0 -0
- /package/dist/{chunk-AZAA5DNP.js.map → chunk-YOXVZJFM.js.map} +0 -0
- /package/dist/{chunk-OLWTCWQ2.js.map → chunk-YZJM6CLF.js.map} +0 -0
- /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
|
-
|
|
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
|
-
|
|
356
|
+
ensureSafeManagedSubdir(memoriesDir, destRolloutsDir, ROLLOUT_SUBDIR),
|
|
327
357
|
retainedRolloutNames,
|
|
328
358
|
);
|
|
329
|
-
|
|
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 =
|
|
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
|
+
}
|