@remnic/core 9.63.1 → 9.63.3
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 +1 -1
- package/dist/access-admin-ops-surface.js +33 -32
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +1 -1
- package/dist/access-authorization-probe.js +34 -33
- package/dist/access-boundary.d.ts +1 -1
- package/dist/access-boundary.js +33 -32
- package/dist/access-cli.js +97 -95
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.d.ts +1 -1
- package/dist/access-extraction-force-flush.js +33 -32
- package/dist/access-http-lcm-compaction.d.ts +1 -1
- package/dist/access-http-lifecycle-flush.d.ts +1 -1
- package/dist/access-http-offline-stream.d.ts +1 -1
- package/dist/access-http-query.js +33 -32
- package/dist/access-http.d.ts +2 -2
- package/dist/access-http.js +46 -45
- package/dist/access-identity-continuity-surface.js +33 -32
- package/dist/access-lcm-surface.d.ts +1 -1
- package/dist/access-lcm-surface.js +33 -32
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.js +39 -38
- package/dist/access-namespace-preflight.js +33 -32
- package/dist/access-observe-write-surface.d.ts +1 -1
- package/dist/access-observe-write-surface.js +33 -32
- package/dist/access-operations-batch.js +34 -33
- package/dist/access-operations.d.ts +4 -4
- package/dist/access-operations.js +38 -37
- package/dist/access-recall-concurrency.d.ts +1 -1
- package/dist/access-recall-concurrency.js +33 -32
- package/dist/access-recall-response.d.ts +1 -1
- package/dist/access-recall-response.js +33 -32
- package/dist/access-recall-surface.d.ts +1 -1
- package/dist/access-recall-surface.js +33 -32
- package/dist/access-schema.d.ts +88 -88
- package/dist/access-schema.js +3 -3
- package/dist/access-service-helpers.d.ts +1 -1
- package/dist/access-service.d.ts +1 -1
- package/dist/access-service.js +33 -32
- package/dist/access-surface-catalog.d.ts +1 -1
- package/dist/access-surface-catalog.js +35 -34
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/{auto-sync-Z52VWMNC.js → auto-sync-BKRJDMKX.js} +3 -3
- package/dist/behavior-learner.js +2 -2
- package/dist/briefing.js +14 -14
- package/dist/buffer.js +4 -3
- package/dist/causal-behavior.js +3 -3
- package/dist/causal-chain.js +3 -3
- package/dist/causal-consolidation.js +17 -17
- package/dist/causal-retrieval.js +3 -3
- package/dist/causal-trajectory.js +2 -2
- package/dist/{chunk-5F6HMNMZ.js → chunk-2LA3H54D.js} +2 -2
- package/dist/{chunk-IRLH4NM2.js → chunk-2PSX5BJU.js} +3 -3
- package/dist/{chunk-JBPKEARU.js → chunk-2QSZNTDO.js} +7 -7
- package/dist/{chunk-F2FZEVDE.js → chunk-3LJ2L3T3.js} +2 -2
- package/dist/{chunk-RBBYKXXK.js → chunk-44KHFMCM.js} +64 -62
- package/dist/{chunk-RBBYKXXK.js.map → chunk-44KHFMCM.js.map} +1 -1
- package/dist/{chunk-AUIAHNO7.js → chunk-4NMVX5U7.js} +2 -2
- package/dist/{chunk-ANMIOQPU.js → chunk-4XN35YQD.js} +478 -164
- package/dist/chunk-4XN35YQD.js.map +1 -0
- package/dist/{chunk-LJOIZPJH.js → chunk-5QZR2RA3.js} +2 -2
- package/dist/{chunk-RBJKE5LV.js → chunk-5UFQCU6M.js} +32 -32
- package/dist/{chunk-S52C2DEM.js → chunk-5W5N7XJZ.js} +2 -2
- package/dist/{chunk-GL4RHVHX.js → chunk-5W62VEFH.js} +4 -4
- package/dist/{chunk-DEOYTFA6.js → chunk-645WOU5I.js} +4 -52
- package/dist/chunk-645WOU5I.js.map +1 -0
- package/dist/{chunk-4WW5FMUD.js → chunk-6D7PZKCJ.js} +9 -9
- package/dist/{chunk-NVSTZ2PN.js → chunk-6F2UMP4H.js} +7 -7
- package/dist/{chunk-IIPJKCZB.js → chunk-6NAPOLRO.js} +3 -3
- package/dist/{chunk-U4CCRHN6.js → chunk-6UZR7KEK.js} +2 -2
- package/dist/{chunk-2TYIAKU5.js → chunk-6YMEU6OQ.js} +2 -2
- package/dist/{chunk-LAX6S5WS.js → chunk-A73QHOZP.js} +2 -2
- package/dist/{chunk-ZSH6GJ2V.js → chunk-AJSZ2EK6.js} +3 -3
- package/dist/{chunk-GUBT77YC.js → chunk-B2OOJCUX.js} +2 -2
- package/dist/chunk-BUYVPUEM.js +66 -0
- package/dist/chunk-BUYVPUEM.js.map +1 -0
- package/dist/{chunk-YTHQHH5U.js → chunk-C6WMSXCJ.js} +2 -2
- package/dist/{chunk-JKW663ZN.js → chunk-CIAI24TQ.js} +2 -2
- package/dist/{chunk-PD6O7AXF.js → chunk-CSAKJVXW.js} +14 -17
- package/dist/chunk-CSAKJVXW.js.map +1 -0
- package/dist/{chunk-ALKRPLWB.js → chunk-DNRTKO2U.js} +2 -2
- package/dist/{chunk-J5AFG5JN.js → chunk-EJLTNPRY.js} +3 -3
- package/dist/{chunk-ORB56L3Y.js → chunk-EJQXDKYK.js} +4 -4
- package/dist/{chunk-UM7A4CPT.js → chunk-FKJVEW6O.js} +2 -2
- package/dist/{chunk-ZXMNT75M.js → chunk-GFNS3BHV.js} +3 -3
- package/dist/{chunk-ALNZEWGN.js → chunk-GPFZFCRN.js} +2 -2
- package/dist/{chunk-MQFFR6QB.js → chunk-GTRGH4ZT.js} +2 -2
- package/dist/{chunk-USKANB65.js → chunk-I6OQCT3Y.js} +2 -2
- package/dist/{chunk-2AZBPQLK.js → chunk-IKXUG6KC.js} +7 -7
- package/dist/{chunk-5LVSFKQB.js → chunk-IM56VPKO.js} +3 -3
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-R7R4FSV2.js → chunk-ITYAXU4S.js} +3 -3
- package/dist/{chunk-7CJO52ET.js → chunk-JJ7JW62R.js} +23 -23
- package/dist/{chunk-5S7Y7AC6.js → chunk-JJX5XEIG.js} +2 -2
- package/dist/{chunk-OF6KQJ35.js → chunk-JWPG65LH.js} +14 -14
- package/dist/chunk-K2XUWZBM.js +417 -0
- package/dist/chunk-K2XUWZBM.js.map +1 -0
- package/dist/{chunk-G2FCSILA.js → chunk-KWIGDKM2.js} +2 -2
- package/dist/{chunk-UKBIB3FP.js → chunk-KWN6NOHQ.js} +2 -2
- package/dist/{chunk-227UFSEQ.js → chunk-LEPOP46W.js} +4 -4
- package/dist/{chunk-SHSJP4D6.js → chunk-LZCSOUJ7.js} +2 -2
- package/dist/{chunk-256FBCVX.js → chunk-M4AHBM72.js} +7 -7
- package/dist/{chunk-5PFGMO33.js → chunk-MEW53VQJ.js} +2 -2
- package/dist/{chunk-DCUMFQYM.js → chunk-ML3U4KPR.js} +2 -2
- package/dist/{chunk-UDL4DNBM.js → chunk-MR5HWQHS.js} +2 -2
- package/dist/{chunk-H7PRHUBD.js → chunk-NRLELHAY.js} +2 -2
- package/dist/{chunk-ZS4JADYY.js → chunk-OW7NVMGQ.js} +2 -2
- package/dist/{chunk-U5LYM2NS.js → chunk-PADG72LJ.js} +3 -3
- package/dist/{chunk-6GGTATZF.js → chunk-PYIR4SSB.js} +4 -4
- package/dist/chunk-PYIR4SSB.js.map +1 -0
- package/dist/{chunk-UW74LP76.js → chunk-Q35XGIEM.js} +2 -2
- package/dist/{chunk-XPRJSHNI.js → chunk-QJ622LTI.js} +6 -3
- package/dist/chunk-QJ622LTI.js.map +1 -0
- package/dist/{chunk-FNYIMSKI.js → chunk-QSIQVC32.js} +2 -2
- package/dist/{chunk-EJYAE2U7.js → chunk-R74NCKEY.js} +4 -4
- package/dist/{chunk-KP36WQBJ.js → chunk-RYGJGXL6.js} +2 -2
- package/dist/{chunk-4OW2T32G.js → chunk-SQ3EGGII.js} +2 -2
- package/dist/{chunk-FU7OICXE.js → chunk-TRPX6F2G.js} +10 -10
- package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
- package/dist/{chunk-6VX2ZZH2.js → chunk-VAMQU4W7.js} +2 -2
- package/dist/{chunk-WLITCQAC.js → chunk-VN7IIQGS.js} +2 -2
- package/dist/{chunk-MMM7SDOJ.js → chunk-WDUG2CH3.js} +2 -2
- package/dist/{chunk-P2BT5ALE.js → chunk-X4MGNVOO.js} +2 -2
- package/dist/{chunk-BE5SVRFC.js → chunk-XFCQIQQU.js} +3 -3
- package/dist/{chunk-S4MF77NV.js → chunk-XZPSBGHA.js} +2 -2
- package/dist/{chunk-PMRUMGK6.js → chunk-Y7COCXY2.js} +2 -2
- package/dist/{chunk-DJ7HMNSL.js → chunk-YCVB7X56.js} +4 -2
- package/dist/chunk-YCVB7X56.js.map +1 -0
- package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
- package/dist/{chunk-A5BYPCDI.js → chunk-Z334TJRB.js} +2 -2
- package/dist/{cli-DJwNNDL1.d.ts → cli-Bw9z-KqX.d.ts} +1 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +67 -66
- package/dist/coding/pre-action-gate.js +3 -3
- package/dist/compounding/engine.js +14 -14
- package/dist/connectors/codex-materialize-runner.js +14 -14
- package/dist/connectors/index.d.ts +45 -1
- package/dist/connectors/index.js +23 -15
- package/dist/convergence-refresh.js +16 -16
- package/dist/conversation-index/backend.js +2 -2
- package/dist/corpus-watermark.js +16 -16
- package/dist/dashboard-runtime.js +2 -2
- package/dist/direct-answer-wiring.js +3 -3
- package/dist/direct-answer.js +2 -2
- package/dist/entity-retrieval.js +14 -14
- package/dist/event-order-query.d.ts +3 -0
- package/dist/event-order-query.js +9 -0
- package/dist/event-order-query.js.map +1 -0
- package/dist/event-order-recall.d.ts +2 -2
- package/dist/event-order-recall.js +5 -2
- package/dist/external-wiki-access.d.ts +1 -1
- package/dist/external-wiki-access.js +34 -33
- package/dist/external-wiki-mcp-tools.d.ts +1 -1
- package/dist/extraction-normalization.js +2 -2
- package/dist/extraction.js +23 -22
- package/dist/harmonic-construction.js +2 -2
- package/dist/harmonic-retrieval.js +2 -2
- package/dist/index.d.ts +682 -681
- package/dist/index.js +106 -103
- package/dist/intent.js +2 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.js +6 -6
- package/dist/maintenance/memory-governance.js +14 -14
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -14
- package/dist/maintenance/rebuild-memory-projection.js +15 -15
- package/dist/mcp-memory-inspector-app.d.ts +1 -1
- package/dist/namespaces/migrate.js +15 -15
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.js +14 -14
- package/dist/operator-doctor-corpus.js +17 -17
- package/dist/operator-toolkit.js +21 -21
- package/dist/orchestration/maintenance.js +19 -19
- package/dist/orchestrator.js +99 -97
- package/dist/policy-runtime.js +3 -3
- package/dist/{public-http-43z1YT-k.d.ts → public-http-DK-QwaBu.d.ts} +1 -1
- package/dist/recall-planner-i18n.d.ts +31 -0
- package/dist/recall-planner-i18n.js +30 -0
- package/dist/recall-planner-i18n.js.map +1 -0
- package/dist/recall-planner-llm.js +2 -1
- package/dist/recall-planner-llm.js.map +1 -1
- package/dist/recall-query-policy.js +2 -2
- package/dist/recall-tokenization.js +1 -1
- package/dist/resume-bundles.js +3 -3
- package/dist/retrieval-agents.js +2 -1
- package/dist/schemas.d.ts +108 -108
- package/dist/search/factory.js +8 -8
- package/dist/search/index.js +14 -14
- package/dist/search/orama-backend.js +2 -2
- package/dist/semantic-consolidation.js +15 -15
- package/dist/semantic-rule-promotion.js +14 -14
- package/dist/semantic-rule-verifier.js +14 -14
- package/dist/shared-context/manager.d.ts +8 -8
- package/dist/source-agent-qualifier.js +20 -19
- package/dist/storage.js +13 -13
- package/dist/support-passport/index.d.ts +4 -4
- package/dist/support-passport/index.js +36 -35
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-zones.js +2 -2
- package/dist/verified-recall.js +14 -14
- package/dist/work-product-ledger.js +2 -2
- package/package.json +2 -2
- package/src/connectors/droid-mcp.ts +434 -0
- package/src/connectors/index.ts +70 -0
- package/src/direct-answer.ts +6 -3
- package/src/event-order-query.ts +78 -0
- package/src/event-order-recall.ts +4 -90
- package/src/intent.ts +19 -19
- package/src/recall-planner-i18n.ts +193 -0
- package/src/recall-tokenization.ts +3 -1
- package/src/retrieval-agents.ts +6 -10
- package/dist/chunk-6GGTATZF.js.map +0 -1
- package/dist/chunk-ANMIOQPU.js.map +0 -1
- package/dist/chunk-DEOYTFA6.js.map +0 -1
- package/dist/chunk-DJ7HMNSL.js.map +0 -1
- package/dist/chunk-PD6O7AXF.js.map +0 -1
- package/dist/chunk-XPRJSHNI.js.map +0 -1
- package/dist/{access-service-DBpEnlzA.d.ts → access-service-M4LgO141.d.ts} +64 -64
- /package/dist/{auto-sync-Z52VWMNC.js.map → auto-sync-BKRJDMKX.js.map} +0 -0
- /package/dist/{chunk-5F6HMNMZ.js.map → chunk-2LA3H54D.js.map} +0 -0
- /package/dist/{chunk-IRLH4NM2.js.map → chunk-2PSX5BJU.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-F2FZEVDE.js.map → chunk-3LJ2L3T3.js.map} +0 -0
- /package/dist/{chunk-AUIAHNO7.js.map → chunk-4NMVX5U7.js.map} +0 -0
- /package/dist/{chunk-LJOIZPJH.js.map → chunk-5QZR2RA3.js.map} +0 -0
- /package/dist/{chunk-RBJKE5LV.js.map → chunk-5UFQCU6M.js.map} +0 -0
- /package/dist/{chunk-S52C2DEM.js.map → chunk-5W5N7XJZ.js.map} +0 -0
- /package/dist/{chunk-GL4RHVHX.js.map → chunk-5W62VEFH.js.map} +0 -0
- /package/dist/{chunk-4WW5FMUD.js.map → chunk-6D7PZKCJ.js.map} +0 -0
- /package/dist/{chunk-NVSTZ2PN.js.map → chunk-6F2UMP4H.js.map} +0 -0
- /package/dist/{chunk-IIPJKCZB.js.map → chunk-6NAPOLRO.js.map} +0 -0
- /package/dist/{chunk-U4CCRHN6.js.map → chunk-6UZR7KEK.js.map} +0 -0
- /package/dist/{chunk-2TYIAKU5.js.map → chunk-6YMEU6OQ.js.map} +0 -0
- /package/dist/{chunk-LAX6S5WS.js.map → chunk-A73QHOZP.js.map} +0 -0
- /package/dist/{chunk-ZSH6GJ2V.js.map → chunk-AJSZ2EK6.js.map} +0 -0
- /package/dist/{chunk-GUBT77YC.js.map → chunk-B2OOJCUX.js.map} +0 -0
- /package/dist/{chunk-YTHQHH5U.js.map → chunk-C6WMSXCJ.js.map} +0 -0
- /package/dist/{chunk-JKW663ZN.js.map → chunk-CIAI24TQ.js.map} +0 -0
- /package/dist/{chunk-ALKRPLWB.js.map → chunk-DNRTKO2U.js.map} +0 -0
- /package/dist/{chunk-J5AFG5JN.js.map → chunk-EJLTNPRY.js.map} +0 -0
- /package/dist/{chunk-ORB56L3Y.js.map → chunk-EJQXDKYK.js.map} +0 -0
- /package/dist/{chunk-UM7A4CPT.js.map → chunk-FKJVEW6O.js.map} +0 -0
- /package/dist/{chunk-ZXMNT75M.js.map → chunk-GFNS3BHV.js.map} +0 -0
- /package/dist/{chunk-ALNZEWGN.js.map → chunk-GPFZFCRN.js.map} +0 -0
- /package/dist/{chunk-MQFFR6QB.js.map → chunk-GTRGH4ZT.js.map} +0 -0
- /package/dist/{chunk-USKANB65.js.map → chunk-I6OQCT3Y.js.map} +0 -0
- /package/dist/{chunk-2AZBPQLK.js.map → chunk-IKXUG6KC.js.map} +0 -0
- /package/dist/{chunk-5LVSFKQB.js.map → chunk-IM56VPKO.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-R7R4FSV2.js.map → chunk-ITYAXU4S.js.map} +0 -0
- /package/dist/{chunk-7CJO52ET.js.map → chunk-JJ7JW62R.js.map} +0 -0
- /package/dist/{chunk-5S7Y7AC6.js.map → chunk-JJX5XEIG.js.map} +0 -0
- /package/dist/{chunk-OF6KQJ35.js.map → chunk-JWPG65LH.js.map} +0 -0
- /package/dist/{chunk-G2FCSILA.js.map → chunk-KWIGDKM2.js.map} +0 -0
- /package/dist/{chunk-UKBIB3FP.js.map → chunk-KWN6NOHQ.js.map} +0 -0
- /package/dist/{chunk-227UFSEQ.js.map → chunk-LEPOP46W.js.map} +0 -0
- /package/dist/{chunk-SHSJP4D6.js.map → chunk-LZCSOUJ7.js.map} +0 -0
- /package/dist/{chunk-256FBCVX.js.map → chunk-M4AHBM72.js.map} +0 -0
- /package/dist/{chunk-5PFGMO33.js.map → chunk-MEW53VQJ.js.map} +0 -0
- /package/dist/{chunk-DCUMFQYM.js.map → chunk-ML3U4KPR.js.map} +0 -0
- /package/dist/{chunk-UDL4DNBM.js.map → chunk-MR5HWQHS.js.map} +0 -0
- /package/dist/{chunk-H7PRHUBD.js.map → chunk-NRLELHAY.js.map} +0 -0
- /package/dist/{chunk-ZS4JADYY.js.map → chunk-OW7NVMGQ.js.map} +0 -0
- /package/dist/{chunk-U5LYM2NS.js.map → chunk-PADG72LJ.js.map} +0 -0
- /package/dist/{chunk-UW74LP76.js.map → chunk-Q35XGIEM.js.map} +0 -0
- /package/dist/{chunk-FNYIMSKI.js.map → chunk-QSIQVC32.js.map} +0 -0
- /package/dist/{chunk-EJYAE2U7.js.map → chunk-R74NCKEY.js.map} +0 -0
- /package/dist/{chunk-KP36WQBJ.js.map → chunk-RYGJGXL6.js.map} +0 -0
- /package/dist/{chunk-4OW2T32G.js.map → chunk-SQ3EGGII.js.map} +0 -0
- /package/dist/{chunk-FU7OICXE.js.map → chunk-TRPX6F2G.js.map} +0 -0
- /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
- /package/dist/{chunk-6VX2ZZH2.js.map → chunk-VAMQU4W7.js.map} +0 -0
- /package/dist/{chunk-WLITCQAC.js.map → chunk-VN7IIQGS.js.map} +0 -0
- /package/dist/{chunk-MMM7SDOJ.js.map → chunk-WDUG2CH3.js.map} +0 -0
- /package/dist/{chunk-P2BT5ALE.js.map → chunk-X4MGNVOO.js.map} +0 -0
- /package/dist/{chunk-BE5SVRFC.js.map → chunk-XFCQIQQU.js.map} +0 -0
- /package/dist/{chunk-S4MF77NV.js.map → chunk-XZPSBGHA.js.map} +0 -0
- /package/dist/{chunk-PMRUMGK6.js.map → chunk-Y7COCXY2.js.map} +0 -0
- /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
- /package/dist/{chunk-A5BYPCDI.js.map → chunk-Z334TJRB.js.map} +0 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @remnic/core — Droid connector helpers
|
|
3
|
+
*
|
|
4
|
+
* Factory Droid reads MCP server config from the user-level ~/.factory/mcp.json.
|
|
5
|
+
* `remnic connectors install droid` writes a "remnic" entry under mcpServers
|
|
6
|
+
* with HTTP transport and a bearer token so Droid can authenticate to the
|
|
7
|
+
* Remnic daemon. The token is NEVER written into the project-level
|
|
8
|
+
* .factory/mcp.json — only the user-level file at ~/.factory/mcp.json.
|
|
9
|
+
*
|
|
10
|
+
* Extracted from connectors/index.ts to keep the main file under its
|
|
11
|
+
* ratchet ceiling (issue #1995).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import os from "node:os";
|
|
17
|
+
|
|
18
|
+
import { readEnvVar } from "../runtime/env.js";
|
|
19
|
+
import type { ConnectorManifest } from "./index.js";
|
|
20
|
+
|
|
21
|
+
// ── Manifest ────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The droid connector manifest entry. Imported into BUILTIN_CONNECTORS
|
|
25
|
+
* in connectors/index.ts.
|
|
26
|
+
*/
|
|
27
|
+
export const DROID_CONNECTOR_MANIFEST: ConnectorManifest = {
|
|
28
|
+
id: "droid",
|
|
29
|
+
name: "Factory Droid",
|
|
30
|
+
version: "1.0.0",
|
|
31
|
+
description:
|
|
32
|
+
"Factory Droid — AI software engineering agent; memory via HTTP MCP with bearer auth",
|
|
33
|
+
capabilities: {
|
|
34
|
+
observe: true,
|
|
35
|
+
recall: true,
|
|
36
|
+
store: true,
|
|
37
|
+
search: true,
|
|
38
|
+
entities: true,
|
|
39
|
+
realtimeSync: true,
|
|
40
|
+
batch: true,
|
|
41
|
+
maxBudgetChars: 32000,
|
|
42
|
+
connectionType: "mcp",
|
|
43
|
+
},
|
|
44
|
+
configSchema: {
|
|
45
|
+
mcpServerUrl: "URL of the MCP Remnic: server (default: http://127.0.0.1:4318/mcp)",
|
|
46
|
+
namespace: "Optional namespace",
|
|
47
|
+
},
|
|
48
|
+
homepage: "https://factory.ai",
|
|
49
|
+
author: "Remnic",
|
|
50
|
+
tags: ["official", "ai", "droid", "factory"],
|
|
51
|
+
requiresToken: true,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ── Provenance reading for removeConnector ─────────────────────────────────
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Result of reading the droid MCP provenance from the saved connector JSON.
|
|
58
|
+
*/
|
|
59
|
+
export interface DroidMcpProvenance {
|
|
60
|
+
/** Resolved MCP config path, or null if none found. */
|
|
61
|
+
mcpConfigPath: string | null;
|
|
62
|
+
/** True if the connector JSON was malformed (abort removal). */
|
|
63
|
+
registryParseFailed: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── Install step (called from installConnector) ────────────────────────────
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Result of the droid install step — either success with rollback/mcpPath,
|
|
70
|
+
* or an error result that installConnector should return directly.
|
|
71
|
+
*/
|
|
72
|
+
export type DroidInstallStepResult =
|
|
73
|
+
| { ok: true; rollback: (() => void) | null; mcpPath: string }
|
|
74
|
+
| { ok: false; errorMessage: string };
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Write the remnic MCP entry to ~/.factory/mcp.json, with token rollback on failure.
|
|
78
|
+
*
|
|
79
|
+
* `saveTokenStore` is passed as a callback to avoid a circular dependency
|
|
80
|
+
* back into the main connectors module.
|
|
81
|
+
*/
|
|
82
|
+
export function droidInstallStep(
|
|
83
|
+
authToken: string | undefined,
|
|
84
|
+
resolvedConfig: Record<string, unknown>,
|
|
85
|
+
requiresToken: boolean,
|
|
86
|
+
tokenEntry: { token: string } | null,
|
|
87
|
+
priorTokenStore: unknown,
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
|
+
saveTokenStoreFn: (store: any) => void,
|
|
90
|
+
): DroidInstallStepResult {
|
|
91
|
+
try {
|
|
92
|
+
const result = writeDroidMcpEntry(authToken, resolvedConfig);
|
|
93
|
+
return { ok: true, rollback: result.rollback, mcpPath: result.mcpPath };
|
|
94
|
+
} catch (droidErr) {
|
|
95
|
+
const errMsg = droidErr instanceof Error ? droidErr.message : String(droidErr);
|
|
96
|
+
let tokenSuffix = "";
|
|
97
|
+
if (requiresToken && tokenEntry !== null && priorTokenStore !== null) {
|
|
98
|
+
try {
|
|
99
|
+
saveTokenStoreFn(priorTokenStore);
|
|
100
|
+
tokenSuffix = " Token has been rolled back.";
|
|
101
|
+
} catch (e) {
|
|
102
|
+
tokenSuffix = ` Token rollback FAILED (${e instanceof Error ? e.message : String(e)}) — manually inspect ~/.remnic/tokens.json and reinstall.`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
errorMessage: `Droid install aborted: ~/.factory/mcp.json write failed — ${errMsg}.${tokenSuffix} Resolve the write permission issue on ~/.factory/, then reinstall.`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Called from `removeConnector` when `connectorId === "droid"`. Falls back to
|
|
114
|
+
* env resolution if the path is missing (legacy install pre-dating
|
|
115
|
+
* factoryMcpPath provenance).
|
|
116
|
+
*/
|
|
117
|
+
export function readDroidMcpProvenance(configPath: string): DroidMcpProvenance {
|
|
118
|
+
let mcpConfigPath: string | null = null;
|
|
119
|
+
let registryParseFailed = false;
|
|
120
|
+
if (!fs.existsSync(configPath)) {
|
|
121
|
+
return { mcpConfigPath: null, registryParseFailed: false };
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const stored = JSON.parse(fs.readFileSync(configPath, "utf8")) as Record<string, unknown>;
|
|
125
|
+
if (typeof stored.factoryMcpPath === "string" && stored.factoryMcpPath.length > 0) {
|
|
126
|
+
mcpConfigPath = stored.factoryMcpPath;
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
registryParseFailed = true;
|
|
130
|
+
}
|
|
131
|
+
if (mcpConfigPath === null && !registryParseFailed) {
|
|
132
|
+
try {
|
|
133
|
+
mcpConfigPath = resolveFactoryMcpPath();
|
|
134
|
+
} catch {
|
|
135
|
+
// Resolution failed — leave null; cleanup block skips.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { mcpConfigPath, registryParseFailed };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Atomic write with owner-only (0o600) permissions. Mirrors the
|
|
143
|
+
* `writeSecretFileSync` in connectors/index.ts — kept local so this module
|
|
144
|
+
* has no dependency back into the main file.
|
|
145
|
+
*/
|
|
146
|
+
function writeSecretFileSync(filePath: string, data: string): void {
|
|
147
|
+
const dir = path.dirname(filePath);
|
|
148
|
+
const base = path.basename(filePath);
|
|
149
|
+
const tmpPath = path.join(
|
|
150
|
+
dir,
|
|
151
|
+
`.${base}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`,
|
|
152
|
+
);
|
|
153
|
+
let wroteTemp = false;
|
|
154
|
+
try {
|
|
155
|
+
fs.writeFileSync(tmpPath, data, { mode: 0o600, flag: "wx" });
|
|
156
|
+
wroteTemp = true;
|
|
157
|
+
try { fs.chmodSync(tmpPath, 0o600); } catch { /* best-effort */ }
|
|
158
|
+
fs.renameSync(tmpPath, filePath);
|
|
159
|
+
try { fs.chmodSync(filePath, 0o600); } catch { /* best-effort */ }
|
|
160
|
+
} catch (err) {
|
|
161
|
+
if (wroteTemp) { try { fs.unlinkSync(tmpPath); } catch { /* best-effort */ } }
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── Install ─────────────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Result of the droid MCP install step.
|
|
170
|
+
*/
|
|
171
|
+
export interface DroidMcpInstallResult {
|
|
172
|
+
/** Absolute path to ~/.factory/mcp.json that was written. */
|
|
173
|
+
mcpPath: string;
|
|
174
|
+
/** Rollback closure: restores prior content (or deletes if newly created). */
|
|
175
|
+
rollback: () => void;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Write the remnic MCP server entry to ~/.factory/mcp.json.
|
|
180
|
+
*
|
|
181
|
+
* Called from `installConnector` when `connectorId === "droid"`. Upserts only
|
|
182
|
+
* the `remnic` key under `mcpServers`, preserving all other entries. Returns
|
|
183
|
+
* the resolved path and a rollback closure. Throws on write failure (after
|
|
184
|
+
* attempting rollback).
|
|
185
|
+
*/
|
|
186
|
+
export function writeDroidMcpEntry(
|
|
187
|
+
authToken: string | undefined,
|
|
188
|
+
resolvedConfig: Record<string, unknown>,
|
|
189
|
+
): DroidMcpInstallResult {
|
|
190
|
+
const mcpPath = resolveFactoryMcpPath();
|
|
191
|
+
const priorContent = readFactoryMcpIfExists(mcpPath);
|
|
192
|
+
const mcpConfig = upsertFactoryMcpRemnicEntry(priorContent, authToken, resolvedConfig);
|
|
193
|
+
fs.mkdirSync(path.dirname(mcpPath), { recursive: true });
|
|
194
|
+
const rollback = (): void => {
|
|
195
|
+
try {
|
|
196
|
+
if (priorContent === null) {
|
|
197
|
+
if (fs.existsSync(mcpPath)) fs.unlinkSync(mcpPath);
|
|
198
|
+
} else {
|
|
199
|
+
writeSecretFileSync(mcpPath, priorContent);
|
|
200
|
+
}
|
|
201
|
+
} catch { /* best-effort */ }
|
|
202
|
+
};
|
|
203
|
+
try {
|
|
204
|
+
writeSecretFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2));
|
|
205
|
+
} catch (writeErr) {
|
|
206
|
+
try { rollback(); } catch { /* best-effort */ }
|
|
207
|
+
throw writeErr;
|
|
208
|
+
}
|
|
209
|
+
return { mcpPath, rollback };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Remove ──────────────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Remove the remnic entry from ~/.factory/mcp.json at the given path.
|
|
216
|
+
*
|
|
217
|
+
* Validates the path is absolute and ends with `.factory/mcp.json` before
|
|
218
|
+
* modifying. Returns a note string on success, or null if no modification
|
|
219
|
+
* was needed. Throws on write failure or if the path fails safety validation.
|
|
220
|
+
*/
|
|
221
|
+
export function removeDroidMcpEntry(mcpPath: string): string | null {
|
|
222
|
+
const expectedSuffix = path.join(".factory", "mcp.json");
|
|
223
|
+
if (!path.isAbsolute(mcpPath) || !mcpPath.endsWith(expectedSuffix)) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`MCP config path ${JSON.stringify(mcpPath)} failed safety validation ` +
|
|
226
|
+
`(must be absolute and end with "${expectedSuffix}"). ` +
|
|
227
|
+
`Refusing to modify — remove the "remnic" entry manually if it exists.`,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const priorContent = readFactoryMcpIfExists(mcpPath);
|
|
231
|
+
if (priorContent === null) return null;
|
|
232
|
+
const updated = removeFactoryMcpRemnicEntry(priorContent);
|
|
233
|
+
if (updated === null) return null;
|
|
234
|
+
writeSecretFileSync(mcpPath, JSON.stringify(updated, null, 2));
|
|
235
|
+
return `Removed remnic entry from Droid MCP config: ${mcpPath}`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Doctor ──────────────────────────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
export interface DroidDoctorCheck {
|
|
241
|
+
name: string;
|
|
242
|
+
ok: boolean;
|
|
243
|
+
detail: string;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Check that ~/.factory/mcp.json contains a remnic entry.
|
|
248
|
+
*
|
|
249
|
+
* Called from `doctorConnector` when `connectorId === "droid"`.
|
|
250
|
+
*/
|
|
251
|
+
export function droidMcpDoctorCheck(savedPath: string | undefined): DroidDoctorCheck {
|
|
252
|
+
const mcpPath = savedPath ?? resolveFactoryMcpPath();
|
|
253
|
+
// Safety gate: validate persisted path before reading (mirrors removeConnector).
|
|
254
|
+
if (savedPath !== undefined) {
|
|
255
|
+
const expectedSuffix = path.join(".factory", "mcp.json");
|
|
256
|
+
if (!path.isAbsolute(savedPath) || !savedPath.endsWith(expectedSuffix)) {
|
|
257
|
+
return { name: "Droid MCP config", ok: false, detail: `Unsafe path in droid.json: ${savedPath}` };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
const raw = fs.readFileSync(mcpPath, "utf8");
|
|
262
|
+
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
|
263
|
+
const servers = parsed.mcpServers;
|
|
264
|
+
if (typeof servers !== "object" || servers === null || !("remnic" in (servers as Record<string, unknown>))) {
|
|
265
|
+
return { name: "Droid MCP config", ok: false, detail: `remnic entry missing in ${mcpPath} — run \`remnic connectors install droid\`` };
|
|
266
|
+
}
|
|
267
|
+
const remnic = (servers as Record<string, Record<string, unknown>>).remnic;
|
|
268
|
+
// Validate the entry has the expected shape (type + url + Authorization).
|
|
269
|
+
if (remnic.type !== "http" || typeof remnic.url !== "string") {
|
|
270
|
+
return { name: "Droid MCP config", ok: false, detail: `remnic entry malformed in ${mcpPath}` };
|
|
271
|
+
}
|
|
272
|
+
const headers = remnic.headers as Record<string, unknown> | undefined;
|
|
273
|
+
const hasAuth = headers && typeof headers["Authorization"] === "string" && (headers["Authorization"] as string).startsWith("Bearer ");
|
|
274
|
+
return {
|
|
275
|
+
name: "Droid MCP config",
|
|
276
|
+
ok: !!hasAuth,
|
|
277
|
+
detail: hasAuth ? `remnic entry present in ${mcpPath}` : `remnic entry missing Authorization header in ${mcpPath} — run \`remnic connectors install droid\``,
|
|
278
|
+
};
|
|
279
|
+
} catch {
|
|
280
|
+
return { name: "Droid MCP config", ok: false, detail: `Cannot read ${mcpPath} — run \`remnic connectors install droid\`` };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ── Path resolution and low-level helpers ───────────────────────────────────
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Resolve the path to ~/.factory/mcp.json. Honours the HOME env override
|
|
288
|
+
* so tests can point the install at a temp dir without leaking into the
|
|
289
|
+
* real home directory. Falls back to os.homedir() when HOME is unset.
|
|
290
|
+
*/
|
|
291
|
+
export function resolveFactoryMcpPath(): string {
|
|
292
|
+
const envHome = readEnvVar("HOME");
|
|
293
|
+
const home = envHome && envHome.length > 0 ? envHome : os.homedir();
|
|
294
|
+
return path.resolve(home, ".factory", "mcp.json");
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Read the existing ~/.factory/mcp.json, if any. Returns raw string contents
|
|
299
|
+
* so the caller can parse, modify, and restore on rollback.
|
|
300
|
+
*/
|
|
301
|
+
export function readFactoryMcpIfExists(mcpPath: string): string | null {
|
|
302
|
+
try {
|
|
303
|
+
if (!fs.existsSync(mcpPath)) return null;
|
|
304
|
+
return fs.readFileSync(mcpPath, "utf8");
|
|
305
|
+
} catch {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Default MCP server URL for the Remnic daemon.
|
|
312
|
+
*/
|
|
313
|
+
const REMNIC_DEFAULT_MCP_URL = "http://127.0.0.1:4318/mcp";
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Upsert the "remnic" entry in a ~/.factory/mcp.json config object.
|
|
317
|
+
*
|
|
318
|
+
* Parses the prior content (if any), preserves all existing mcpServers entries,
|
|
319
|
+
* and sets the "remnic" entry with HTTP transport, the bearer token, and an
|
|
320
|
+
* optional namespace header. Returns the full config object ready to serialize.
|
|
321
|
+
*
|
|
322
|
+
* If no token is available, the entry is still written but without the
|
|
323
|
+
* Authorization header — the caller will see an auth error from the daemon,
|
|
324
|
+
* which is more informative than a missing entry.
|
|
325
|
+
*/
|
|
326
|
+
export function upsertFactoryMcpRemnicEntry(
|
|
327
|
+
priorContent: string | null,
|
|
328
|
+
authToken: string | undefined,
|
|
329
|
+
resolvedConfig: Record<string, unknown>,
|
|
330
|
+
): Record<string, unknown> {
|
|
331
|
+
let config: Record<string, unknown>;
|
|
332
|
+
if (priorContent !== null) {
|
|
333
|
+
try {
|
|
334
|
+
const parsed = JSON.parse(priorContent);
|
|
335
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
336
|
+
config = parsed as Record<string, unknown>;
|
|
337
|
+
} else {
|
|
338
|
+
// Non-object JSON — start fresh but warn in console.
|
|
339
|
+
console.warn("[remnic/connectors] ~/.factory/mcp.json contained non-object JSON; starting fresh.");
|
|
340
|
+
config = {};
|
|
341
|
+
}
|
|
342
|
+
} catch {
|
|
343
|
+
// Malformed JSON — do NOT silently discard other MCP server entries.
|
|
344
|
+
// Throw so the caller can surface the error instead of overwriting the file.
|
|
345
|
+
throw new Error(
|
|
346
|
+
"~/.factory/mcp.json contains malformed JSON and cannot be parsed. " +
|
|
347
|
+
"Fix the file manually before reinstalling, or use --force to overwrite (this will lose existing MCP server entries).",
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
config = {};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (typeof config.mcpServers !== "object" || config.mcpServers === null || Array.isArray(config.mcpServers)) {
|
|
355
|
+
config.mcpServers = {};
|
|
356
|
+
}
|
|
357
|
+
const servers = config.mcpServers as Record<string, unknown>;
|
|
358
|
+
|
|
359
|
+
const mcpUrl =
|
|
360
|
+
typeof resolvedConfig.mcpServerUrl === "string" && resolvedConfig.mcpServerUrl.length > 0
|
|
361
|
+
? resolvedConfig.mcpServerUrl
|
|
362
|
+
: REMNIC_DEFAULT_MCP_URL;
|
|
363
|
+
|
|
364
|
+
const remnicEntry: Record<string, unknown> = { type: "http", url: mcpUrl };
|
|
365
|
+
|
|
366
|
+
// Preserve prior headers EXCEPT Remnic-managed ones (Authorization,
|
|
367
|
+
// X-Engram-Namespace). These are always replaced from the current install
|
|
368
|
+
// config so a reinstall that clears namespace does not leave a stale header.
|
|
369
|
+
const REMNIC_MANAGED_HEADERS = new Set(["Authorization", "X-Engram-Namespace"]);
|
|
370
|
+
const headers: Record<string, string> = {};
|
|
371
|
+
if (
|
|
372
|
+
typeof servers.remnic === "object" && servers.remnic !== null &&
|
|
373
|
+
typeof (servers.remnic as Record<string, unknown>).headers === "object" &&
|
|
374
|
+
(servers.remnic as Record<string, unknown>).headers !== null
|
|
375
|
+
) {
|
|
376
|
+
const priorHeaders = (servers.remnic as Record<string, Record<string, unknown>>).headers as Record<string, unknown>;
|
|
377
|
+
for (const [key, value] of Object.entries(priorHeaders)) {
|
|
378
|
+
if (!REMNIC_MANAGED_HEADERS.has(key) && typeof value === "string") {
|
|
379
|
+
headers[key] = value;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (authToken && authToken.length > 0) {
|
|
385
|
+
headers["Authorization"] = `Bearer ${authToken}`;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const namespace =
|
|
389
|
+
typeof resolvedConfig.namespace === "string" && resolvedConfig.namespace.length > 0
|
|
390
|
+
? resolvedConfig.namespace
|
|
391
|
+
: undefined;
|
|
392
|
+
if (namespace) {
|
|
393
|
+
headers["X-Engram-Namespace"] = namespace;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (Object.keys(headers).length > 0) {
|
|
397
|
+
remnicEntry.headers = headers;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
servers.remnic = remnicEntry;
|
|
401
|
+
return config;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Remove the "remnic" entry from a ~/.factory/mcp.json config object.
|
|
406
|
+
*
|
|
407
|
+
* Returns the updated config object (with remnic removed) or null if the
|
|
408
|
+
* entry was not present (no modification needed).
|
|
409
|
+
*/
|
|
410
|
+
export function removeFactoryMcpRemnicEntry(
|
|
411
|
+
priorContent: string,
|
|
412
|
+
): Record<string, unknown> | null {
|
|
413
|
+
let config: Record<string, unknown>;
|
|
414
|
+
try {
|
|
415
|
+
const parsed = JSON.parse(priorContent);
|
|
416
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
config = parsed as Record<string, unknown>;
|
|
420
|
+
} catch {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (typeof config.mcpServers !== "object" || config.mcpServers === null) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
const servers = config.mcpServers as Record<string, unknown>;
|
|
428
|
+
if (!("remnic" in servers)) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
delete servers.remnic;
|
|
433
|
+
return config;
|
|
434
|
+
}
|
package/src/connectors/index.ts
CHANGED
|
@@ -29,6 +29,15 @@ import {
|
|
|
29
29
|
} from "./hermes-shim.js";
|
|
30
30
|
import { getConnectorsDir, getRegistryPath } from "./paths.js";
|
|
31
31
|
export { getConnectorsDir, getRegistryPath } from "./paths.js";
|
|
32
|
+
export { resolveFactoryMcpPath, upsertFactoryMcpRemnicEntry, removeFactoryMcpRemnicEntry } from "./droid-mcp.js";
|
|
33
|
+
export { DROID_CONNECTOR_MANIFEST } from "./droid-mcp.js";
|
|
34
|
+
import {
|
|
35
|
+
DROID_CONNECTOR_MANIFEST,
|
|
36
|
+
droidInstallStep,
|
|
37
|
+
removeDroidMcpEntry as removeDroidMcpEntryImpl,
|
|
38
|
+
droidMcpDoctorCheck as droidMcpDoctorCheckImpl,
|
|
39
|
+
readDroidMcpProvenance,
|
|
40
|
+
} from "./droid-mcp.js";
|
|
32
41
|
|
|
33
42
|
// Native memory artifact materialization for Codex CLI (#378). Surfaced here
|
|
34
43
|
// so downstream callers can `import { materializeForNamespace } from "@remnic/core/connectors"`.
|
|
@@ -565,6 +574,7 @@ export const BUILTIN_CONNECTORS: ConnectorManifest[] = [
|
|
|
565
574
|
tags: ["official", "python", "hermes"],
|
|
566
575
|
requiresToken: true,
|
|
567
576
|
},
|
|
577
|
+
DROID_CONNECTOR_MANIFEST,
|
|
568
578
|
];
|
|
569
579
|
|
|
570
580
|
// ── Registry management ───────────────────────────────────────────────────
|
|
@@ -2028,6 +2038,24 @@ export function installConnector(options: InstallOptions): InstallResult {
|
|
|
2028
2038
|
}
|
|
2029
2039
|
}
|
|
2030
2040
|
|
|
2041
|
+
// ── Droid: write remnic MCP server entry to ~/.factory/mcp.json ──────────
|
|
2042
|
+
let droidMcpRollback: (() => void) | null = null;
|
|
2043
|
+
if (options.connectorId === "droid") {
|
|
2044
|
+
const stepResult = droidInstallStep(
|
|
2045
|
+
tokenEntry?.token,
|
|
2046
|
+
resolvedConfig,
|
|
2047
|
+
manifest.requiresToken ?? false,
|
|
2048
|
+
tokenEntry,
|
|
2049
|
+
nonHermesPriorTokenStore,
|
|
2050
|
+
saveTokenStore,
|
|
2051
|
+
);
|
|
2052
|
+
if (!stepResult.ok) {
|
|
2053
|
+
return { connectorId: options.connectorId, status: "error", message: stepResult.errorMessage };
|
|
2054
|
+
}
|
|
2055
|
+
droidMcpRollback = stepResult.rollback;
|
|
2056
|
+
resolvedConfig.factoryMcpPath = stepResult.mcpPath;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2031
2059
|
// Finding 5: strip internal/test-only keys that must never be persisted to
|
|
2032
2060
|
// the config file. These keys are used at install time only (e.g. to inject
|
|
2033
2061
|
// a synthetic extension source dir in tests) and have no meaning on disk.
|
|
@@ -2095,6 +2123,14 @@ export function installConnector(options: InstallOptions): InstallResult {
|
|
|
2095
2123
|
// Best-effort rollback.
|
|
2096
2124
|
}
|
|
2097
2125
|
}
|
|
2126
|
+
// Roll back the Droid ~/.factory/mcp.json if it was written.
|
|
2127
|
+
if (droidMcpRollback !== null) {
|
|
2128
|
+
try {
|
|
2129
|
+
droidMcpRollback();
|
|
2130
|
+
} catch {
|
|
2131
|
+
// Best-effort rollback.
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2098
2134
|
// Only include a token-rollback suffix for connectors that actually had a
|
|
2099
2135
|
// token to roll back. Non-token connectors (requiresToken !== true) never
|
|
2100
2136
|
// generated a token entry. For requiresToken connectors where generateToken
|
|
@@ -2316,6 +2352,20 @@ export function removeConnector(connectorId: string): RemoveResult {
|
|
|
2316
2352
|
}
|
|
2317
2353
|
}
|
|
2318
2354
|
}
|
|
2355
|
+
|
|
2356
|
+
// For droid, read the persisted factoryMcpPath before deleting the config.
|
|
2357
|
+
let droidMcpConfigPath: string | null = null;
|
|
2358
|
+
let droidRegistryParseFailed = false;
|
|
2359
|
+
if (connectorId === "droid") {
|
|
2360
|
+
const prov = readDroidMcpProvenance(configPath);
|
|
2361
|
+
droidMcpConfigPath = prov.mcpConfigPath;
|
|
2362
|
+
droidRegistryParseFailed = prov.registryParseFailed;
|
|
2363
|
+
}
|
|
2364
|
+
if (connectorId === "droid" && droidRegistryParseFailed) {
|
|
2365
|
+
console.warn(`[remnic/connectors] removeConnector: droid.json malformed — aborting. Fix or delete ${configPath} manually and retry.`);
|
|
2366
|
+
return { connectorId, configPath, message: "Removal aborted: droid.json is malformed. Registry config left in place.", status: "skipped", reason: "config-parse-failed" };
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2319
2369
|
if (connectorId === "weclone" && weCloneRegistryParseFailed) {
|
|
2320
2370
|
console.warn(
|
|
2321
2371
|
"[remnic/connectors] removeConnector: weclone.json is malformed — " +
|
|
@@ -2564,6 +2614,21 @@ export function removeConnector(connectorId: string): RemoveResult {
|
|
|
2564
2614
|
};
|
|
2565
2615
|
}
|
|
2566
2616
|
|
|
2617
|
+
// Droid-specific: remove the "remnic" entry from ~/.factory/mcp.json.
|
|
2618
|
+
let droidMcpDeleteFailed: string | null = null;
|
|
2619
|
+
if (connectorId === "droid") {
|
|
2620
|
+
if (droidMcpConfigPath === null) {
|
|
2621
|
+
notes.push("Droid MCP config cleanup skipped: no persisted path found (legacy install).");
|
|
2622
|
+
} else {
|
|
2623
|
+
try { const note = removeDroidMcpEntryImpl(droidMcpConfigPath); if (note) notes.push(note); }
|
|
2624
|
+
catch (err) { droidMcpDeleteFailed = err instanceof Error ? err.message : String(err); }
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
if (droidMcpDeleteFailed !== null && droidMcpConfigPath !== null) {
|
|
2628
|
+
const ts = tokenRevoked ? "token was revoked" : "TOKEN REVOCATION ALSO FAILED — inspect ~/.remnic/tokens.json and revoke manually";
|
|
2629
|
+
return { connectorId, configPath, status: "error", message: `Droid remove partially succeeded: registry config deleted, ${ts}, but the MCP config at ${droidMcpConfigPath} could not be updated (${droidMcpDeleteFailed}). Manually remove the "remnic" entry.` };
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2567
2632
|
// Hermes-specific: strip the remnic: block from config.yaml (the writability
|
|
2568
2633
|
// preflight above already verified every remnic:-bearing target, so a
|
|
2569
2634
|
// partial failure here is a rare TOCTOU race — the error branch below stays
|
|
@@ -3301,6 +3366,11 @@ export async function doctorConnector(connectorId: string): Promise<DoctorResult
|
|
|
3301
3366
|
}
|
|
3302
3367
|
}
|
|
3303
3368
|
|
|
3369
|
+
// Droid-specific: check ~/.factory/mcp.json has the remnic entry
|
|
3370
|
+
if (connectorId === "droid") {
|
|
3371
|
+
checks.push(droidMcpDoctorCheckImpl(instance.config.factoryMcpPath as string | undefined));
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3304
3374
|
const healthy = checks.every((c) => c.ok);
|
|
3305
3375
|
return { connectorId, checks, healthy };
|
|
3306
3376
|
}
|
package/src/direct-answer.ts
CHANGED
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
* Not wired into retrieval yet — see slice 3.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
isUnsegmentableRecallChar as isUnsegmentableRecallCodepoint,
|
|
23
|
+
normalizeRecallTokenSet,
|
|
24
|
+
normalizeRecallTokens,
|
|
25
|
+
} from "./recall-tokenization.js";
|
|
22
26
|
import type { TrustZoneName } from "./trust-zones.js";
|
|
23
27
|
import type { MemoryFile, MemoryStatus } from "./types.js";
|
|
24
28
|
|
|
@@ -154,8 +158,7 @@ interface ScoredCandidate {
|
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
function hasUnsegmentableRecallChar(token: string): boolean {
|
|
157
|
-
|
|
158
|
-
return /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u.test(token);
|
|
161
|
+
return [...token].some((ch) => isUnsegmentableRecallCodepoint(ch));
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
function requiredCjkPhraseTokens(query: string): string[] {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { EVENT_ORDER_CUES, containsRecallCue } from "./recall-planner-i18n.js";
|
|
2
|
+
|
|
3
|
+
function isTimelineSummaryQuery(normalized: string): boolean {
|
|
4
|
+
return (
|
|
5
|
+
/\b(?:summarize|summary|major progress|what happened|develop(?:ed|ment)?|approached)\b/.test(
|
|
6
|
+
normalized,
|
|
7
|
+
) &&
|
|
8
|
+
(/\b(?:over time|throughout|between|project|progress|timeline|journey|sessions?|along the way)\b/.test(
|
|
9
|
+
normalized,
|
|
10
|
+
) ||
|
|
11
|
+
/\bfrom\b.+\bthrough\b/.test(normalized))
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function shouldRecallEventOrderEvidence(query: string): boolean {
|
|
16
|
+
const normalized = query.toLowerCase();
|
|
17
|
+
|
|
18
|
+
if (containsRecallCue(normalized, EVENT_ORDER_CUES)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (
|
|
23
|
+
/\border in which\b/.test(normalized) ||
|
|
24
|
+
/\bsequence in which\b/.test(normalized) ||
|
|
25
|
+
/\breconstruct\b.*\btimeline\b/.test(normalized) ||
|
|
26
|
+
/\btimeline\b.*\bin order\b/.test(normalized) ||
|
|
27
|
+
/\bsequence\b.*\bin order\b/.test(normalized) ||
|
|
28
|
+
/\bintroduced\b.*\bin order\b/.test(normalized) ||
|
|
29
|
+
(/\bwalk me through\b/.test(normalized) && /\bin order\b/.test(normalized)) ||
|
|
30
|
+
(/\bin order\b/.test(normalized) &&
|
|
31
|
+
/\b(?:develop(?:ed|ment)?|evolv(?:e|ed|ing)?|progress|throughout|conversations?|sessions?)\b/.test(
|
|
32
|
+
normalized,
|
|
33
|
+
)) ||
|
|
34
|
+
/\bprogress\b.*\bin order\b/.test(normalized) ||
|
|
35
|
+
/\bchronological(?:ly| order)?\b/.test(normalized) ||
|
|
36
|
+
isTimelineSummaryQuery(normalized)
|
|
37
|
+
) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (/\bwhich\b.*\bfirst\b/.test(normalized) || /\bwhat was the first\b/.test(normalized)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (/\bhappened first\b/.test(normalized) || /\bwhich\b.*\bhappened\b/.test(normalized)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (/\bhow many days\b/.test(normalized)) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (/\bwhen did\b/.test(normalized)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (/\bwhich\b.*\blast\b/.test(normalized) || /\bthe last\b.*\bi\b/.test(normalized)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (/\bwho\b.*\bfirst\b/.test(normalized)) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (/\bmost recent(?:ly)?\b/.test(normalized)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (/\border of\b/.test(normalized) || /\bfrom earliest to latest\b/.test(normalized)) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (/\bhow long\b/.test(normalized)) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
if (/\bhow many (months|weeks|years)\b/.test(normalized)) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (/\bhow many\b.*\bbefore\b/.test(normalized)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (/\bhow old\b.*\bwhen\b/.test(normalized)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|