@remnic/core 10.0.44 → 10.0.46
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.js +24 -24
- package/dist/access-authorization-probe.js +25 -25
- package/dist/access-boundary.js +24 -24
- package/dist/access-cli.js +65 -65
- package/dist/access-extraction-force-flush.js +24 -24
- package/dist/access-http-briefing.js +25 -25
- package/dist/access-http-query.js +26 -26
- package/dist/access-http.js +41 -41
- package/dist/access-identity-continuity-surface.js +24 -24
- package/dist/access-lcm-surface.js +24 -24
- package/dist/access-mcp.js +33 -33
- package/dist/access-namespace-preflight.js +24 -24
- package/dist/access-observe-write-category.js +3 -3
- package/dist/access-observe-write-surface.js +24 -24
- package/dist/access-offline-manifest.js +2 -2
- package/dist/access-operations-batch.js +25 -25
- package/dist/access-operations.js +32 -32
- package/dist/access-recall-concurrency.js +24 -24
- package/dist/access-recall-response.js +24 -24
- package/dist/access-recall-surface.js +24 -24
- package/dist/access-schema.js +2 -2
- package/dist/access-service.js +24 -24
- package/dist/access-surface-catalog.js +26 -26
- package/dist/active-recall.js +5 -5
- package/dist/briefing.js +5 -5
- package/dist/buffer.js +4 -4
- package/dist/causal-consolidation.js +6 -6
- package/dist/{chunk-XXBCUIYQ.js → chunk-2DPJFJFH.js} +7 -7
- package/dist/{chunk-UIK3U36O.js → chunk-2NX7JTST.js} +2 -2
- package/dist/{chunk-M2YC4YFA.js → chunk-2RQR4TXV.js} +2 -2
- package/dist/{chunk-4QLKVZH6.js → chunk-2V664H6O.js} +2 -2
- package/dist/{chunk-HWEWQC6J.js → chunk-3DKKZVXS.js} +4 -4
- package/dist/{chunk-ICTJOOWZ.js → chunk-3EGL7XZO.js} +2 -2
- package/dist/{chunk-3RJDYZJE.js → chunk-4J7BNL7B.js} +2 -2
- package/dist/{chunk-AER3HD3B.js → chunk-5KGYR5VN.js} +2 -2
- package/dist/{chunk-SEVMJSQW.js → chunk-5R2IF2KI.js} +4 -4
- package/dist/{chunk-6X3J4NLZ.js → chunk-6CC2YCKG.js} +2 -2
- package/dist/{chunk-JJSMSS45.js → chunk-6CWAY4R7.js} +2 -2
- package/dist/{chunk-AY6LO7GD.js → chunk-6HVLHIAB.js} +2 -2
- package/dist/{chunk-MGYZENUY.js → chunk-6P6MZDBL.js} +2 -2
- package/dist/{chunk-6ITNEPJO.js → chunk-6Q56WDHG.js} +2 -2
- package/dist/{chunk-O67H7OXJ.js → chunk-7AIPWNM7.js} +2 -2
- package/dist/{chunk-UKIIAEJF.js → chunk-A45QXZMU.js} +2 -2
- package/dist/{chunk-XJ77IOAD.js → chunk-B44NFHWI.js} +2 -2
- package/dist/{chunk-N3PUGD7E.js → chunk-B4XSXFKI.js} +2 -2
- package/dist/{chunk-CMUNW67K.js → chunk-BCDECPVO.js} +2 -2
- package/dist/{chunk-CJKLRBLT.js → chunk-BGK3SFCC.js} +2 -2
- package/dist/{chunk-DB3RMRV7.js → chunk-BMOJFGVX.js} +2 -2
- package/dist/{chunk-FPHU6KVI.js → chunk-BWRX322G.js} +2 -2
- package/dist/{chunk-6FRFVIKC.js → chunk-CCTLRRV6.js} +41 -7
- package/dist/chunk-CCTLRRV6.js.map +1 -0
- package/dist/{chunk-ZN7VA5RL.js → chunk-CHSRA76M.js} +2 -2
- package/dist/{chunk-JGS55BAS.js → chunk-CMNL3QSL.js} +2 -2
- package/dist/{chunk-5TRU6QQC.js → chunk-CTHYR4QK.js} +2 -2
- package/dist/{chunk-ZI4LCS7K.js → chunk-DRXTMDAG.js} +2 -2
- package/dist/{chunk-E5IEI2MR.js → chunk-EX27VKU6.js} +2 -2
- package/dist/{chunk-F2RDRE7M.js → chunk-FPANDKMQ.js} +2 -2
- package/dist/{chunk-F22JNLX3.js → chunk-FWV2SXSL.js} +26 -26
- package/dist/{chunk-LWIU5BMA.js → chunk-GLZWOIU3.js} +3 -3
- package/dist/{chunk-RROFIAYI.js → chunk-I3IPXGFQ.js} +11 -9
- package/dist/{chunk-RROFIAYI.js.map → chunk-I3IPXGFQ.js.map} +1 -1
- package/dist/{chunk-TOCDY4BJ.js → chunk-J6B6FK2X.js} +17 -17
- package/dist/{chunk-FNEYSTB6.js → chunk-JCYEDMOY.js} +2 -2
- package/dist/{chunk-PJXSBXEC.js → chunk-JGMJZBFV.js} +2 -2
- package/dist/{chunk-ZPSNLMSX.js → chunk-KAUCVTAW.js} +2 -2
- package/dist/{chunk-3ALI4FY6.js → chunk-KRACAE73.js} +6 -6
- package/dist/{chunk-BIYVUPC6.js → chunk-L46MG2GE.js} +2 -2
- package/dist/{chunk-SLVR3U4E.js → chunk-LJCHDFLP.js} +3 -3
- package/dist/chunk-LJCHDFLP.js.map +1 -0
- package/dist/{chunk-Q77SLBWT.js → chunk-LU57EEJW.js} +3 -3
- package/dist/{chunk-R42VUN5V.js → chunk-M7L5A2AT.js} +3 -3
- package/dist/{chunk-GZMHBRIR.js → chunk-M7LA4QBY.js} +3 -3
- package/dist/{chunk-VTZSIG6O.js → chunk-ME5LVISI.js} +2 -2
- package/dist/{chunk-XBHUMPIQ.js → chunk-MXPVNDGO.js} +7 -7
- package/dist/{chunk-ONIVKGNC.js → chunk-MZCTS3ZG.js} +2 -2
- package/dist/{chunk-7BCYAJKP.js → chunk-NDMPRT3Z.js} +2 -2
- package/dist/{chunk-CHNZPOWX.js → chunk-PJBYVJKO.js} +8 -3
- package/dist/chunk-PJBYVJKO.js.map +1 -0
- package/dist/{chunk-UTA4O5MJ.js → chunk-PKPOO6X5.js} +13 -13
- package/dist/{chunk-QGNZ5QPV.js → chunk-PPDNG7WC.js} +2 -2
- package/dist/{chunk-CNAL35D5.js → chunk-QE2XQM3S.js} +2 -2
- package/dist/{chunk-F7MP76CB.js → chunk-QFQ4UG3I.js} +2 -2
- package/dist/{chunk-CRXGQRNA.js → chunk-QR6W3FSW.js} +2 -2
- package/dist/{chunk-IWDFHVPJ.js → chunk-RQLTCHUL.js} +2 -2
- package/dist/{chunk-JXQTECMU.js → chunk-RT2GN6KQ.js} +2 -2
- package/dist/{chunk-5SF7JOJB.js → chunk-TF5NZGPS.js} +2 -2
- package/dist/{chunk-UGV256H3.js → chunk-UXAF4RQM.js} +2 -2
- package/dist/{chunk-JRFB6PQY.js → chunk-VTCNVGM6.js} +2 -2
- package/dist/{chunk-XXW3P3RX.js → chunk-VWJ56T63.js} +2 -2
- package/dist/{chunk-OUYYEPXD.js → chunk-WOPZ2P5Z.js} +2 -2
- package/dist/{chunk-GDOI4U4D.js → chunk-XM747YJF.js} +2 -2
- package/dist/{chunk-GAUS4XGN.js → chunk-XTVH2NAW.js} +3 -3
- package/dist/{chunk-MGRXFPBA.js → chunk-YBKQAVPZ.js} +18 -18
- package/dist/{chunk-BEAW464C.js → chunk-YFJHFEET.js} +2 -2
- package/dist/{chunk-7XZ7TOLQ.js → chunk-YL564QS2.js} +2 -2
- package/dist/{chunk-UMSH4I3E.js → chunk-YUE7J2HZ.js} +2 -2
- package/dist/cli.js +51 -51
- package/dist/coding/export-okf-codegraph.js +4 -4
- package/dist/compounding/engine.js +6 -6
- package/dist/config.js +5 -5
- package/dist/connectors/codex-materialize-runner.js +5 -5
- package/dist/connectors/index.js +5 -5
- package/dist/convergence-refresh.d.ts.map +1 -1
- package/dist/convergence-refresh.js +7 -7
- package/dist/corpus-watermark.js +7 -7
- package/dist/deep-recall-http-glue.js +3 -3
- package/dist/entity-retrieval.js +5 -5
- package/dist/external-wiki-access.js +25 -25
- package/dist/extraction.js +9 -9
- package/dist/harmonic-retrieval.js +5 -5
- package/dist/index.js +65 -65
- package/dist/location/index.js +7 -7
- package/dist/maintenance/memory-governance.js +5 -5
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
- package/dist/maintenance/rebuild-memory-projection-runner.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.js +6 -6
- package/dist/memory-browse-operations.js +25 -25
- package/dist/memory-browse.js +4 -4
- package/dist/namespaces/migrate.js +6 -6
- package/dist/namespaces/search.d.ts +2 -0
- package/dist/namespaces/search.d.ts.map +1 -1
- package/dist/namespaces/search.js +3 -3
- package/dist/namespaces/storage.js +5 -5
- package/dist/offline-sync-snapshot-stream.js +2 -2
- package/dist/offline-sync.d.ts.map +1 -1
- package/dist/offline-sync.js +1 -1
- package/dist/operator-doctor-corpus.js +8 -8
- package/dist/operator-toolkit.js +14 -14
- package/dist/orchestration/maintenance.js +15 -15
- package/dist/orchestrator.js +65 -65
- package/dist/qmd.d.ts.map +1 -1
- package/dist/qmd.js +1 -1
- package/dist/recall-navigation-operations.js +25 -25
- package/dist/recall-navigation.js +24 -24
- package/dist/recall-state-view-admission.js +4 -4
- package/dist/recall-why-service.js +4 -4
- package/dist/resume-bundles.js +5 -5
- package/dist/search/factory.js +2 -2
- package/dist/search/index.js +2 -2
- package/dist/semantic-consolidation.js +6 -6
- package/dist/semantic-rule-promotion.js +5 -5
- package/dist/semantic-rule-verifier.js +5 -5
- package/dist/source-agent-qualifier.js +8 -8
- package/dist/standup/build.js +5 -5
- package/dist/storage.js +4 -4
- package/dist/support-passport/index.js +27 -27
- package/dist/transfer/export-okf.js +5 -5
- package/dist/verified-recall.js +5 -5
- package/dist/who-knows-cli.js +6 -6
- package/dist/who-knows.js +5 -5
- package/package.json +2 -2
- package/src/convergence-refresh.test.ts +70 -0
- package/src/convergence-refresh.ts +3 -1
- package/src/namespaces/search.ts +7 -3
- package/src/offline-sync.test.ts +109 -0
- package/src/offline-sync.ts +50 -6
- package/src/orchestrator-convergence-refresh.test.ts +21 -16
- package/src/qmd-client.test.ts +23 -0
- package/src/qmd.ts +13 -8
- package/dist/chunk-6FRFVIKC.js.map +0 -1
- package/dist/chunk-CHNZPOWX.js.map +0 -1
- package/dist/chunk-SLVR3U4E.js.map +0 -1
- /package/dist/{chunk-XXBCUIYQ.js.map → chunk-2DPJFJFH.js.map} +0 -0
- /package/dist/{chunk-UIK3U36O.js.map → chunk-2NX7JTST.js.map} +0 -0
- /package/dist/{chunk-M2YC4YFA.js.map → chunk-2RQR4TXV.js.map} +0 -0
- /package/dist/{chunk-4QLKVZH6.js.map → chunk-2V664H6O.js.map} +0 -0
- /package/dist/{chunk-HWEWQC6J.js.map → chunk-3DKKZVXS.js.map} +0 -0
- /package/dist/{chunk-ICTJOOWZ.js.map → chunk-3EGL7XZO.js.map} +0 -0
- /package/dist/{chunk-3RJDYZJE.js.map → chunk-4J7BNL7B.js.map} +0 -0
- /package/dist/{chunk-AER3HD3B.js.map → chunk-5KGYR5VN.js.map} +0 -0
- /package/dist/{chunk-SEVMJSQW.js.map → chunk-5R2IF2KI.js.map} +0 -0
- /package/dist/{chunk-6X3J4NLZ.js.map → chunk-6CC2YCKG.js.map} +0 -0
- /package/dist/{chunk-JJSMSS45.js.map → chunk-6CWAY4R7.js.map} +0 -0
- /package/dist/{chunk-AY6LO7GD.js.map → chunk-6HVLHIAB.js.map} +0 -0
- /package/dist/{chunk-MGYZENUY.js.map → chunk-6P6MZDBL.js.map} +0 -0
- /package/dist/{chunk-6ITNEPJO.js.map → chunk-6Q56WDHG.js.map} +0 -0
- /package/dist/{chunk-O67H7OXJ.js.map → chunk-7AIPWNM7.js.map} +0 -0
- /package/dist/{chunk-UKIIAEJF.js.map → chunk-A45QXZMU.js.map} +0 -0
- /package/dist/{chunk-XJ77IOAD.js.map → chunk-B44NFHWI.js.map} +0 -0
- /package/dist/{chunk-N3PUGD7E.js.map → chunk-B4XSXFKI.js.map} +0 -0
- /package/dist/{chunk-CMUNW67K.js.map → chunk-BCDECPVO.js.map} +0 -0
- /package/dist/{chunk-CJKLRBLT.js.map → chunk-BGK3SFCC.js.map} +0 -0
- /package/dist/{chunk-DB3RMRV7.js.map → chunk-BMOJFGVX.js.map} +0 -0
- /package/dist/{chunk-FPHU6KVI.js.map → chunk-BWRX322G.js.map} +0 -0
- /package/dist/{chunk-ZN7VA5RL.js.map → chunk-CHSRA76M.js.map} +0 -0
- /package/dist/{chunk-JGS55BAS.js.map → chunk-CMNL3QSL.js.map} +0 -0
- /package/dist/{chunk-5TRU6QQC.js.map → chunk-CTHYR4QK.js.map} +0 -0
- /package/dist/{chunk-ZI4LCS7K.js.map → chunk-DRXTMDAG.js.map} +0 -0
- /package/dist/{chunk-E5IEI2MR.js.map → chunk-EX27VKU6.js.map} +0 -0
- /package/dist/{chunk-F2RDRE7M.js.map → chunk-FPANDKMQ.js.map} +0 -0
- /package/dist/{chunk-F22JNLX3.js.map → chunk-FWV2SXSL.js.map} +0 -0
- /package/dist/{chunk-LWIU5BMA.js.map → chunk-GLZWOIU3.js.map} +0 -0
- /package/dist/{chunk-TOCDY4BJ.js.map → chunk-J6B6FK2X.js.map} +0 -0
- /package/dist/{chunk-FNEYSTB6.js.map → chunk-JCYEDMOY.js.map} +0 -0
- /package/dist/{chunk-PJXSBXEC.js.map → chunk-JGMJZBFV.js.map} +0 -0
- /package/dist/{chunk-ZPSNLMSX.js.map → chunk-KAUCVTAW.js.map} +0 -0
- /package/dist/{chunk-3ALI4FY6.js.map → chunk-KRACAE73.js.map} +0 -0
- /package/dist/{chunk-BIYVUPC6.js.map → chunk-L46MG2GE.js.map} +0 -0
- /package/dist/{chunk-Q77SLBWT.js.map → chunk-LU57EEJW.js.map} +0 -0
- /package/dist/{chunk-R42VUN5V.js.map → chunk-M7L5A2AT.js.map} +0 -0
- /package/dist/{chunk-GZMHBRIR.js.map → chunk-M7LA4QBY.js.map} +0 -0
- /package/dist/{chunk-VTZSIG6O.js.map → chunk-ME5LVISI.js.map} +0 -0
- /package/dist/{chunk-XBHUMPIQ.js.map → chunk-MXPVNDGO.js.map} +0 -0
- /package/dist/{chunk-ONIVKGNC.js.map → chunk-MZCTS3ZG.js.map} +0 -0
- /package/dist/{chunk-7BCYAJKP.js.map → chunk-NDMPRT3Z.js.map} +0 -0
- /package/dist/{chunk-UTA4O5MJ.js.map → chunk-PKPOO6X5.js.map} +0 -0
- /package/dist/{chunk-QGNZ5QPV.js.map → chunk-PPDNG7WC.js.map} +0 -0
- /package/dist/{chunk-CNAL35D5.js.map → chunk-QE2XQM3S.js.map} +0 -0
- /package/dist/{chunk-F7MP76CB.js.map → chunk-QFQ4UG3I.js.map} +0 -0
- /package/dist/{chunk-CRXGQRNA.js.map → chunk-QR6W3FSW.js.map} +0 -0
- /package/dist/{chunk-IWDFHVPJ.js.map → chunk-RQLTCHUL.js.map} +0 -0
- /package/dist/{chunk-JXQTECMU.js.map → chunk-RT2GN6KQ.js.map} +0 -0
- /package/dist/{chunk-5SF7JOJB.js.map → chunk-TF5NZGPS.js.map} +0 -0
- /package/dist/{chunk-UGV256H3.js.map → chunk-UXAF4RQM.js.map} +0 -0
- /package/dist/{chunk-JRFB6PQY.js.map → chunk-VTCNVGM6.js.map} +0 -0
- /package/dist/{chunk-XXW3P3RX.js.map → chunk-VWJ56T63.js.map} +0 -0
- /package/dist/{chunk-OUYYEPXD.js.map → chunk-WOPZ2P5Z.js.map} +0 -0
- /package/dist/{chunk-GDOI4U4D.js.map → chunk-XM747YJF.js.map} +0 -0
- /package/dist/{chunk-GAUS4XGN.js.map → chunk-XTVH2NAW.js.map} +0 -0
- /package/dist/{chunk-MGRXFPBA.js.map → chunk-YBKQAVPZ.js.map} +0 -0
- /package/dist/{chunk-BEAW464C.js.map → chunk-YFJHFEET.js.map} +0 -0
- /package/dist/{chunk-7XZ7TOLQ.js.map → chunk-YL564QS2.js.map} +0 -0
- /package/dist/{chunk-UMSH4I3E.js.map → chunk-YUE7J2HZ.js.map} +0 -0
package/dist/verified-recall.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compareVerifiedEpisodeResults,
|
|
3
3
|
searchVerifiedEpisodes
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VWJ56T63.js";
|
|
5
5
|
import "./chunk-B7RWBZXW.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-L46MG2GE.js";
|
|
7
7
|
import "./chunk-UHVXE24Y.js";
|
|
8
8
|
import "./chunk-DM5QZ3YU.js";
|
|
9
9
|
import "./chunk-VY6BD5KB.js";
|
|
10
10
|
import "./chunk-G6OFEGUR.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-PJBYVJKO.js";
|
|
12
|
+
import "./chunk-2V664H6O.js";
|
|
13
13
|
import "./chunk-XKDQTDXN.js";
|
|
14
14
|
import "./chunk-CYEPCZN5.js";
|
|
15
15
|
import "./chunk-XRFLR3XF.js";
|
|
@@ -48,7 +48,7 @@ import "./chunk-LWRIGKFM.js";
|
|
|
48
48
|
import "./chunk-5WITETWU.js";
|
|
49
49
|
import "./chunk-6CIAGZ6X.js";
|
|
50
50
|
import "./chunk-7CT3AI52.js";
|
|
51
|
-
import "./chunk-
|
|
51
|
+
import "./chunk-I3IPXGFQ.js";
|
|
52
52
|
import "./chunk-G26YNEOC.js";
|
|
53
53
|
import "./chunk-AF2XMJKD.js";
|
|
54
54
|
import "./chunk-OR42ZI54.js";
|
package/dist/who-knows-cli.js
CHANGED
|
@@ -4,15 +4,15 @@ import {
|
|
|
4
4
|
renderWhoKnows,
|
|
5
5
|
renderWhoKnowsJson,
|
|
6
6
|
renderWhoKnowsText
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-BWRX322G.js";
|
|
8
|
+
import "./chunk-JGMJZBFV.js";
|
|
9
|
+
import "./chunk-L46MG2GE.js";
|
|
10
10
|
import "./chunk-UHVXE24Y.js";
|
|
11
11
|
import "./chunk-DM5QZ3YU.js";
|
|
12
12
|
import "./chunk-VY6BD5KB.js";
|
|
13
13
|
import "./chunk-G6OFEGUR.js";
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-PJBYVJKO.js";
|
|
15
|
+
import "./chunk-2V664H6O.js";
|
|
16
16
|
import "./chunk-XKDQTDXN.js";
|
|
17
17
|
import "./chunk-CYEPCZN5.js";
|
|
18
18
|
import "./chunk-XRFLR3XF.js";
|
|
@@ -51,7 +51,7 @@ import "./chunk-LWRIGKFM.js";
|
|
|
51
51
|
import "./chunk-5WITETWU.js";
|
|
52
52
|
import "./chunk-6CIAGZ6X.js";
|
|
53
53
|
import "./chunk-7CT3AI52.js";
|
|
54
|
-
import "./chunk-
|
|
54
|
+
import "./chunk-I3IPXGFQ.js";
|
|
55
55
|
import "./chunk-G26YNEOC.js";
|
|
56
56
|
import "./chunk-AF2XMJKD.js";
|
|
57
57
|
import "./chunk-OR42ZI54.js";
|
package/dist/who-knows.js
CHANGED
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
loadWhoKnowsEntities,
|
|
10
10
|
tokenizeTopic,
|
|
11
11
|
validateWhoKnowsInput
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-JGMJZBFV.js";
|
|
13
|
+
import "./chunk-L46MG2GE.js";
|
|
14
14
|
import "./chunk-UHVXE24Y.js";
|
|
15
15
|
import "./chunk-DM5QZ3YU.js";
|
|
16
16
|
import "./chunk-VY6BD5KB.js";
|
|
17
17
|
import "./chunk-G6OFEGUR.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-PJBYVJKO.js";
|
|
19
|
+
import "./chunk-2V664H6O.js";
|
|
20
20
|
import "./chunk-XKDQTDXN.js";
|
|
21
21
|
import "./chunk-CYEPCZN5.js";
|
|
22
22
|
import "./chunk-XRFLR3XF.js";
|
|
@@ -55,7 +55,7 @@ import "./chunk-LWRIGKFM.js";
|
|
|
55
55
|
import "./chunk-5WITETWU.js";
|
|
56
56
|
import "./chunk-6CIAGZ6X.js";
|
|
57
57
|
import "./chunk-7CT3AI52.js";
|
|
58
|
-
import "./chunk-
|
|
58
|
+
import "./chunk-I3IPXGFQ.js";
|
|
59
59
|
import "./chunk-G26YNEOC.js";
|
|
60
60
|
import "./chunk-AF2XMJKD.js";
|
|
61
61
|
import "./chunk-OR42ZI54.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.46",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -3347,7 +3347,7 @@
|
|
|
3347
3347
|
"core"
|
|
3348
3348
|
],
|
|
3349
3349
|
"peerDependencies": {
|
|
3350
|
-
"@remnic/coding-graph": "^10.0.
|
|
3350
|
+
"@remnic/coding-graph": "^10.0.46"
|
|
3351
3351
|
},
|
|
3352
3352
|
"peerDependenciesMeta": {
|
|
3353
3353
|
"@remnic/coding-graph": {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { refreshConvergedNamespaces } from "./convergence-refresh.js";
|
|
5
|
+
|
|
6
|
+
test("convergence refresh requests a forced, strict index update", async () => {
|
|
7
|
+
let observedOptions: unknown;
|
|
8
|
+
let continued = false;
|
|
9
|
+
await assert.rejects(
|
|
10
|
+
() => refreshConvergedNamespaces(
|
|
11
|
+
["generalist"],
|
|
12
|
+
{
|
|
13
|
+
updateNamespacesDetailed: async (_namespaces, _execution, options) => {
|
|
14
|
+
observedOptions = options;
|
|
15
|
+
return { backendCount: 1, eligibleNamespaces: ["generalist"] };
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
async () => {
|
|
19
|
+
continued = true;
|
|
20
|
+
throw new Error("storage-reached");
|
|
21
|
+
},
|
|
22
|
+
false,
|
|
23
|
+
),
|
|
24
|
+
/storage-reached/,
|
|
25
|
+
);
|
|
26
|
+
assert.equal(continued, true);
|
|
27
|
+
assert.deepEqual(observedOptions, { strict: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("an index-update throttle skip fails convergence refresh", async () => {
|
|
31
|
+
let continued = false;
|
|
32
|
+
await assert.rejects(
|
|
33
|
+
() => refreshConvergedNamespaces(
|
|
34
|
+
["generalist"],
|
|
35
|
+
{
|
|
36
|
+
updateNamespacesDetailed: async () => {
|
|
37
|
+
throw new Error("QMD update skipped by min-interval gate");
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
async () => {
|
|
41
|
+
continued = true;
|
|
42
|
+
throw new Error("storage-reached");
|
|
43
|
+
},
|
|
44
|
+
false,
|
|
45
|
+
),
|
|
46
|
+
/min-interval/,
|
|
47
|
+
);
|
|
48
|
+
assert.equal(continued, false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("a real QMD refresh failure still fails convergence refresh", async () => {
|
|
52
|
+
let continued = false;
|
|
53
|
+
await assert.rejects(
|
|
54
|
+
() => refreshConvergedNamespaces(
|
|
55
|
+
["generalist"],
|
|
56
|
+
{
|
|
57
|
+
updateNamespacesDetailed: async () => {
|
|
58
|
+
throw new Error("qmd exploded");
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
async () => {
|
|
62
|
+
continued = true;
|
|
63
|
+
throw new Error("storage-reached");
|
|
64
|
+
},
|
|
65
|
+
false,
|
|
66
|
+
),
|
|
67
|
+
/qmd exploded/,
|
|
68
|
+
);
|
|
69
|
+
assert.equal(continued, false);
|
|
70
|
+
});
|
|
@@ -15,10 +15,12 @@ export async function refreshConvergedNamespaces(
|
|
|
15
15
|
const targetNamespaces = Array.from(new Set(namespaces.map((namespace) => namespace.trim()).filter(Boolean)));
|
|
16
16
|
if (targetNamespaces.length === 0) return;
|
|
17
17
|
|
|
18
|
+
// Forced so a min-interval skip cannot masquerade as proof that the
|
|
19
|
+
// converged writes are indexed; a real failure still fails the refresh.
|
|
18
20
|
const searchResult = await namespaceSearchRouter.updateNamespacesDetailed(
|
|
19
21
|
targetNamespaces,
|
|
20
22
|
undefined,
|
|
21
|
-
{ strict: true },
|
|
23
|
+
{ strict: true, force: true },
|
|
22
24
|
);
|
|
23
25
|
if (searchEnabled) {
|
|
24
26
|
const eligible = new Set(searchResult.eligibleNamespaces);
|
package/src/namespaces/search.ts
CHANGED
|
@@ -217,7 +217,7 @@ export class NamespaceSearchRouter {
|
|
|
217
217
|
async updateNamespaces(
|
|
218
218
|
namespaces: string[],
|
|
219
219
|
execution?: SearchExecutionOptions,
|
|
220
|
-
options?: { strict?: boolean },
|
|
220
|
+
options?: { strict?: boolean; force?: boolean },
|
|
221
221
|
): Promise<number> {
|
|
222
222
|
return (await this.updateNamespacesDetailed(namespaces, execution, options)).backendCount;
|
|
223
223
|
}
|
|
@@ -225,7 +225,7 @@ export class NamespaceSearchRouter {
|
|
|
225
225
|
async updateNamespacesDetailed(
|
|
226
226
|
namespaces: string[],
|
|
227
227
|
execution?: SearchExecutionOptions,
|
|
228
|
-
options?: { strict?: boolean },
|
|
228
|
+
options?: { strict?: boolean; force?: boolean },
|
|
229
229
|
): Promise<NamespaceUpdateResult> {
|
|
230
230
|
const unique = Array.from(new Set(namespaces.map((value) => value.trim()).filter(Boolean)));
|
|
231
231
|
const eligible = (await Promise.all(
|
|
@@ -578,10 +578,14 @@ function backendSearchLimit(
|
|
|
578
578
|
async function updateBackendRecord(
|
|
579
579
|
record: NamespaceBackendRecord,
|
|
580
580
|
execution?: SearchExecutionOptions,
|
|
581
|
-
options?: { strict?: boolean },
|
|
581
|
+
options?: { strict?: boolean; force?: boolean },
|
|
582
582
|
): Promise<void> {
|
|
583
583
|
if (options?.strict === true) {
|
|
584
584
|
if (
|
|
585
|
+
// Forced refreshes must skip the throttled all-collections update: its
|
|
586
|
+
// success has to prove the current writes are indexed, which a throttle
|
|
587
|
+
// skip cannot.
|
|
588
|
+
options.force !== true &&
|
|
585
589
|
record.backend.updatesAllCollections?.() === true &&
|
|
586
590
|
typeof record.backend.updateStrict === "function"
|
|
587
591
|
) {
|
package/src/offline-sync.test.ts
CHANGED
|
@@ -3713,3 +3713,112 @@ test("applyOfflineSyncSnapshot consumes readIncomingFile for records without inl
|
|
|
3713
3713
|
await rm(root, { recursive: true, force: true });
|
|
3714
3714
|
}
|
|
3715
3715
|
});
|
|
3716
|
+
|
|
3717
|
+
test("buildOfflineSyncSnapshot skips a file that vanishes during the walk", async () => {
|
|
3718
|
+
const root = await tempDir("remnic-offline-vanish");
|
|
3719
|
+
try {
|
|
3720
|
+
await write(root, "facts/keep.md", "keep");
|
|
3721
|
+
await write(root, "facts/gone.md", "gone");
|
|
3722
|
+
const snapshot = await buildOfflineSyncSnapshot({
|
|
3723
|
+
root,
|
|
3724
|
+
sourceId: "local",
|
|
3725
|
+
includeContent: false,
|
|
3726
|
+
readFileDigest: async ({ path: relPath, filePath }) => {
|
|
3727
|
+
if (relPath.endsWith("gone.md")) {
|
|
3728
|
+
await rm(filePath);
|
|
3729
|
+
throw Object.assign(new Error("ENOENT: vanished"), { code: "ENOENT" });
|
|
3730
|
+
}
|
|
3731
|
+
const content = await readFile(filePath);
|
|
3732
|
+
return {
|
|
3733
|
+
sha256: createHash("sha256").update(content).digest("hex"),
|
|
3734
|
+
bytes: content.length,
|
|
3735
|
+
};
|
|
3736
|
+
},
|
|
3737
|
+
});
|
|
3738
|
+
assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/keep.md"]);
|
|
3739
|
+
} finally {
|
|
3740
|
+
await rm(root, { recursive: true, force: true });
|
|
3741
|
+
}
|
|
3742
|
+
});
|
|
3743
|
+
|
|
3744
|
+
test("base snapshot fails when a base-known file vanishes before the fast-base stat", async () => {
|
|
3745
|
+
const root = await tempDir("remnic-offline-base-vanish");
|
|
3746
|
+
try {
|
|
3747
|
+
await write(root, "facts/keep.md", "keep");
|
|
3748
|
+
await write(root, "facts/gone.md", "gone");
|
|
3749
|
+
const baseSnapshot = await buildOfflineSyncSnapshot({
|
|
3750
|
+
root,
|
|
3751
|
+
sourceId: "local",
|
|
3752
|
+
includeContent: false,
|
|
3753
|
+
});
|
|
3754
|
+
await assert.rejects(
|
|
3755
|
+
() => buildOfflineSyncSnapshotFromBase({
|
|
3756
|
+
root,
|
|
3757
|
+
sourceId: "local",
|
|
3758
|
+
baseFiles: baseSnapshot.files,
|
|
3759
|
+
baseCapturedAt: new Date(),
|
|
3760
|
+
includeContent: false,
|
|
3761
|
+
excludeFile: async ({ path: relPath, filePath }) => {
|
|
3762
|
+
if (relPath === "facts/gone.md") await rm(filePath, { force: true });
|
|
3763
|
+
return false;
|
|
3764
|
+
},
|
|
3765
|
+
}),
|
|
3766
|
+
/base file vanished during walk/,
|
|
3767
|
+
);
|
|
3768
|
+
} finally {
|
|
3769
|
+
await rm(root, { recursive: true, force: true });
|
|
3770
|
+
}
|
|
3771
|
+
});
|
|
3772
|
+
|
|
3773
|
+
test("base snapshot omits a brand-new file that vanishes during the walk", async () => {
|
|
3774
|
+
const root = await tempDir("remnic-offline-new-vanish");
|
|
3775
|
+
try {
|
|
3776
|
+
await write(root, "facts/keep.md", "keep");
|
|
3777
|
+
await write(root, "facts/gone.md", "gone");
|
|
3778
|
+
const baseSnapshot = await buildOfflineSyncSnapshot({
|
|
3779
|
+
root,
|
|
3780
|
+
sourceId: "local",
|
|
3781
|
+
includeContent: false,
|
|
3782
|
+
});
|
|
3783
|
+
const baseFiles = baseSnapshot.files.filter((file) => file.path !== "facts/gone.md");
|
|
3784
|
+
const snapshot = await buildOfflineSyncSnapshotFromBase({
|
|
3785
|
+
root,
|
|
3786
|
+
sourceId: "local",
|
|
3787
|
+
baseFiles,
|
|
3788
|
+
baseCapturedAt: new Date(),
|
|
3789
|
+
includeContent: false,
|
|
3790
|
+
excludeFile: async ({ path: relPath, filePath }) => {
|
|
3791
|
+
if (relPath === "facts/gone.md") await rm(filePath, { force: true });
|
|
3792
|
+
return false;
|
|
3793
|
+
},
|
|
3794
|
+
});
|
|
3795
|
+
assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/keep.md"]);
|
|
3796
|
+
} finally {
|
|
3797
|
+
await rm(root, { recursive: true, force: true });
|
|
3798
|
+
}
|
|
3799
|
+
});
|
|
3800
|
+
|
|
3801
|
+
test("snapshot fails when an embedding generation member vanishes during the walk", async () => {
|
|
3802
|
+
const root = await tempDir("remnic-offline-shard-vanish");
|
|
3803
|
+
try {
|
|
3804
|
+
await write(root, "state/embeddings/shard-0000.json", "a");
|
|
3805
|
+
await write(root, "state/embeddings/shard-0001.json", "b");
|
|
3806
|
+
await assert.rejects(
|
|
3807
|
+
() => buildOfflineSyncSnapshot({
|
|
3808
|
+
root,
|
|
3809
|
+
sourceId: "local",
|
|
3810
|
+
includeContent: false,
|
|
3811
|
+
readFileDigest: async ({ path: relPath, filePath }) => {
|
|
3812
|
+
if (relPath === "state/embeddings/shard-0000.json") {
|
|
3813
|
+
await rm(path.join(root, "state/embeddings/shard-0001.json"), { force: true });
|
|
3814
|
+
}
|
|
3815
|
+
const content = await readFile(filePath);
|
|
3816
|
+
return { sha256: createHash("sha256").update(content).digest("hex"), bytes: content.length };
|
|
3817
|
+
},
|
|
3818
|
+
}),
|
|
3819
|
+
/embedding generation member vanished/,
|
|
3820
|
+
);
|
|
3821
|
+
} finally {
|
|
3822
|
+
await rm(root, { recursive: true, force: true });
|
|
3823
|
+
}
|
|
3824
|
+
});
|
package/src/offline-sync.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
utimes,
|
|
13
13
|
writeFile,
|
|
14
14
|
} from "node:fs/promises";
|
|
15
|
+
import type { Stats } from "node:fs";
|
|
15
16
|
import path from "node:path";
|
|
16
17
|
import {
|
|
17
18
|
DEFAULT_TRANSFER_EXCLUDE_DIRS,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
|
|
27
28
|
import { EmbeddingIndexStorageError } from "./embedding-index-storage.js";
|
|
28
29
|
import { isEmbeddingGenerationMarkerPath, matchesOfflineSyncDefaultExclude } from "./offline-sync-exclude-globs.js";
|
|
30
|
+
import { isNotFoundError } from "./utils/errno.js";
|
|
29
31
|
import { isMacOsReplicationJunkBasename } from "./utils/macos-replication-junk.js";
|
|
30
32
|
import {
|
|
31
33
|
applyIncomingEmbeddingGenerations,
|
|
@@ -790,6 +792,31 @@ async function canReuseFastBaseFileStateFromDisk(
|
|
|
790
792
|
|
|
791
793
|
async function readOfflineSyncFileRecord(
|
|
792
794
|
options: OfflineSyncFileRecordOptions,
|
|
795
|
+
): Promise<OfflineSyncFileRecord | null> {
|
|
796
|
+
try {
|
|
797
|
+
return await readPresentOfflineSyncFileRecord(options);
|
|
798
|
+
} catch (error) {
|
|
799
|
+
// A file removed between readdir and stat/hash is a race, not a corrupt corpus.
|
|
800
|
+
if (isNotFoundError(error)) return null;
|
|
801
|
+
throw error;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// A vanished path may only be omitted when nothing can misread the gap:
|
|
806
|
+
// embedding generations travel and replace as one unit (issue #3148), and a
|
|
807
|
+
// base-known file omitted here would fabricate a delete in
|
|
808
|
+
// buildOfflineSyncChangesetFromSnapshot.
|
|
809
|
+
function assertSnapshotVanishIsSafe(relPath: string, base: ReadonlyMap<string, unknown> | undefined): void {
|
|
810
|
+
if (embeddingGenerationMembership(relPath) !== null) {
|
|
811
|
+
throw new Error(`offline sync embedding generation member vanished during snapshot walk: ${relPath}`);
|
|
812
|
+
}
|
|
813
|
+
if (base?.has(relPath)) {
|
|
814
|
+
throw new Error(`offline sync snapshot base file vanished during walk: ${relPath}`);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
async function readPresentOfflineSyncFileRecord(
|
|
819
|
+
options: OfflineSyncFileRecordOptions,
|
|
793
820
|
): Promise<OfflineSyncFileRecord> {
|
|
794
821
|
throwIfOfflineSyncAborted(options.signal);
|
|
795
822
|
const relPath = validateArchiveRelativePath(options.relPath, "offlineSyncFile.path");
|
|
@@ -895,7 +922,7 @@ export async function* iterateOfflineSyncSnapshotFileRecords(options: {
|
|
|
895
922
|
path: relPosix,
|
|
896
923
|
filePath: abs,
|
|
897
924
|
})) continue;
|
|
898
|
-
|
|
925
|
+
const record = await readOfflineSyncFileRecord({
|
|
899
926
|
root,
|
|
900
927
|
relPath: relPosix,
|
|
901
928
|
filePath: abs,
|
|
@@ -904,6 +931,8 @@ export async function* iterateOfflineSyncSnapshotFileRecords(options: {
|
|
|
904
931
|
readFileDigest: options.readFileDigest,
|
|
905
932
|
signal: options.signal,
|
|
906
933
|
});
|
|
934
|
+
if (record) yield record;
|
|
935
|
+
else assertSnapshotVanishIsSafe(relPosix, undefined);
|
|
907
936
|
}
|
|
908
937
|
}
|
|
909
938
|
|
|
@@ -1060,7 +1089,18 @@ export async function buildOfflineSyncSnapshotFromBase(options: {
|
|
|
1060
1089
|
path: relPosix,
|
|
1061
1090
|
filePath: abs,
|
|
1062
1091
|
})) continue;
|
|
1063
|
-
|
|
1092
|
+
let st: Stats;
|
|
1093
|
+
try {
|
|
1094
|
+
st = await stat(abs);
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
// Same race readOfflineSyncFileRecord tolerates, unless omitting the
|
|
1097
|
+
// path could fabricate a delete or downgrade a generation.
|
|
1098
|
+
if (isNotFoundError(error)) {
|
|
1099
|
+
assertSnapshotVanishIsSafe(relPosix, base);
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
throw error;
|
|
1103
|
+
}
|
|
1064
1104
|
const baseEntry = base.get(relPosix);
|
|
1065
1105
|
if (
|
|
1066
1106
|
options.includeContent !== true &&
|
|
@@ -1071,7 +1111,7 @@ export async function buildOfflineSyncSnapshotFromBase(options: {
|
|
|
1071
1111
|
files.push(baseEntry);
|
|
1072
1112
|
continue;
|
|
1073
1113
|
}
|
|
1074
|
-
|
|
1114
|
+
const record = await readOfflineSyncFileRecord({
|
|
1075
1115
|
root,
|
|
1076
1116
|
relPath: relPosix,
|
|
1077
1117
|
filePath: abs,
|
|
@@ -1079,7 +1119,9 @@ export async function buildOfflineSyncSnapshotFromBase(options: {
|
|
|
1079
1119
|
readFile: options.readFile,
|
|
1080
1120
|
readFileDigest: options.readFileDigest,
|
|
1081
1121
|
signal: options.signal,
|
|
1082
|
-
})
|
|
1122
|
+
});
|
|
1123
|
+
if (record) files.push(record);
|
|
1124
|
+
else assertSnapshotVanishIsSafe(relPosix, base);
|
|
1083
1125
|
}
|
|
1084
1126
|
}
|
|
1085
1127
|
|
|
@@ -1165,7 +1207,7 @@ export async function buildOfflineSyncSnapshotForPaths(options: {
|
|
|
1165
1207
|
})) {
|
|
1166
1208
|
throw new Error(`offline sync snapshot path is excluded: ${relPath}`);
|
|
1167
1209
|
}
|
|
1168
|
-
|
|
1210
|
+
const record = await readOfflineSyncFileRecord({
|
|
1169
1211
|
root,
|
|
1170
1212
|
relPath,
|
|
1171
1213
|
filePath,
|
|
@@ -1173,7 +1215,9 @@ export async function buildOfflineSyncSnapshotForPaths(options: {
|
|
|
1173
1215
|
readFile: options.readFile,
|
|
1174
1216
|
readFileDigest: options.readFileDigest,
|
|
1175
1217
|
signal: options.signal,
|
|
1176
|
-
})
|
|
1218
|
+
});
|
|
1219
|
+
if (record) files.push(record);
|
|
1220
|
+
else assertSnapshotVanishIsSafe(relPath, undefined);
|
|
1177
1221
|
}
|
|
1178
1222
|
throwIfOfflineSyncAborted(options.signal);
|
|
1179
1223
|
|
|
@@ -22,38 +22,43 @@ test("refreshNamespacesAfterConvergence runs one strict QMD batch and rebuilds e
|
|
|
22
22
|
});
|
|
23
23
|
const qmdCalls: Array<{
|
|
24
24
|
namespaces: string[];
|
|
25
|
-
options: { strict?: boolean } | undefined;
|
|
25
|
+
options: { strict?: boolean; force?: boolean } | undefined;
|
|
26
26
|
}> = [];
|
|
27
27
|
const storageCalls: string[] = [];
|
|
28
28
|
const orchestrator = Object.create(Orchestrator.prototype) as Orchestrator;
|
|
29
|
-
|
|
29
|
+
type ConvergenceRefreshInternals = {
|
|
30
30
|
config: { defaultNamespace: string; qmdEnabled: boolean };
|
|
31
31
|
namespaceSearchRouter: {
|
|
32
32
|
updateNamespacesDetailed(
|
|
33
33
|
namespaces: string[],
|
|
34
34
|
execution?: unknown,
|
|
35
|
-
options?: { strict?: boolean }
|
|
35
|
+
options?: { strict?: boolean; force?: boolean },
|
|
36
36
|
): Promise<{ backendCount: number; eligibleNamespaces: string[] }>;
|
|
37
37
|
};
|
|
38
38
|
getStorage(namespace: string): Promise<StorageManager>;
|
|
39
39
|
};
|
|
40
|
-
internals.config = { defaultNamespace: "default", qmdEnabled: true };
|
|
41
|
-
internals.namespaceSearchRouter = {
|
|
42
|
-
async updateNamespacesDetailed(namespaces, _execution, options) {
|
|
43
|
-
qmdCalls.push({ namespaces, options });
|
|
44
|
-
return { backendCount: 1, eligibleNamespaces: namespaces };
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
40
|
const storageByNamespace = new Map([
|
|
48
41
|
["team", teamStorage],
|
|
49
42
|
["shared", sharedStorage],
|
|
50
43
|
]);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
// SAFETY: fresh prototype instance under test; every internals field is
|
|
45
|
+
// provided here before refreshNamespacesAfterConvergence runs.
|
|
46
|
+
const internals: ConvergenceRefreshInternals = {
|
|
47
|
+
config: { defaultNamespace: "default", qmdEnabled: true },
|
|
48
|
+
namespaceSearchRouter: {
|
|
49
|
+
async updateNamespacesDetailed(namespaces, _execution, options) {
|
|
50
|
+
qmdCalls.push({ namespaces, options });
|
|
51
|
+
return { backendCount: 1, eligibleNamespaces: namespaces };
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
getStorage: async (namespace) => {
|
|
55
|
+
storageCalls.push(namespace);
|
|
56
|
+
const storage = storageByNamespace.get(namespace);
|
|
57
|
+
assert.ok(storage);
|
|
58
|
+
return storage;
|
|
59
|
+
},
|
|
56
60
|
};
|
|
61
|
+
Object.assign(orchestrator, internals);
|
|
57
62
|
const refreshSurface = orchestrator as unknown as {
|
|
58
63
|
refreshNamespacesAfterConvergence(namespaces: readonly string[]): Promise<void>;
|
|
59
64
|
};
|
|
@@ -63,7 +68,7 @@ test("refreshNamespacesAfterConvergence runs one strict QMD batch and rebuilds e
|
|
|
63
68
|
assert.deepEqual(qmdCalls, [
|
|
64
69
|
{
|
|
65
70
|
namespaces: ["team", "shared"],
|
|
66
|
-
options: { strict: true },
|
|
71
|
+
options: { strict: true, force: true },
|
|
67
72
|
},
|
|
68
73
|
]);
|
|
69
74
|
assert.deepEqual(storageCalls, ["team", "shared"]);
|
package/src/qmd-client.test.ts
CHANGED
|
@@ -80,6 +80,29 @@ test("updateStrict respects QMD update min-interval throttles", async () => {
|
|
|
80
80
|
assert.equal(calls.length, 1);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
+
test("a locked qmd update does not suppress the next strict update", async () => {
|
|
84
|
+
const client = new QmdClient("memories", 3, { updateMinIntervalMs: 0 });
|
|
85
|
+
client.resetUpdateThrottles();
|
|
86
|
+
let calls = 0;
|
|
87
|
+
Object.assign(client, {
|
|
88
|
+
available: true,
|
|
89
|
+
runQmdCommand: async () => {
|
|
90
|
+
calls += 1;
|
|
91
|
+
if (calls === 1) throw new Error("qmd update failed (code 1): SqliteError: database is locked");
|
|
92
|
+
return { stdout: "", stderr: "" };
|
|
93
|
+
},
|
|
94
|
+
} satisfies Partial<SubprocessInternals>);
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await assert.rejects(() => client.updateStrict(), /database is locked/);
|
|
98
|
+
await client.updateStrict();
|
|
99
|
+
} finally {
|
|
100
|
+
client.resetUpdateThrottles();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
assert.equal(calls, 2);
|
|
104
|
+
});
|
|
105
|
+
|
|
83
106
|
test("embedCollectionStrict rejects QMD embed subprocess failures", async () => {
|
|
84
107
|
const client = new QmdClient("memories", 3, { updateMinIntervalMs: 60_000 });
|
|
85
108
|
client.resetUpdateThrottles();
|
package/src/qmd.ts
CHANGED
|
@@ -2406,15 +2406,20 @@ export class QmdClient implements SearchBackend {
|
|
|
2406
2406
|
clearQmdResultCaches();
|
|
2407
2407
|
log.debug(`QMD update completed for collection=${name}`);
|
|
2408
2408
|
} catch (err) {
|
|
2409
|
-
const at = Date.now();
|
|
2410
|
-
if (options.perCollectionThrottle) {
|
|
2411
|
-
globalState.lastUpdateFailByCollectionMs[name] = at;
|
|
2412
|
-
globalState.lastGlobalUpdateFailAtMs = at;
|
|
2413
|
-
} else {
|
|
2414
|
-
this._lastUpdateFailAtMs = at;
|
|
2415
|
-
globalState.lastGlobalUpdateFailAtMs = at;
|
|
2416
|
-
}
|
|
2417
2409
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2410
|
+
// Another qmd process holds the index. Arming the 15-minute failure
|
|
2411
|
+
// backoff would make the next converge attempt fail even after the lock
|
|
2412
|
+
// is gone. A real index failure still backs off.
|
|
2413
|
+
if (!/database is locked|SQLITE_BUSY/i.test(msg)) {
|
|
2414
|
+
const at = Date.now();
|
|
2415
|
+
if (options.perCollectionThrottle) {
|
|
2416
|
+
globalState.lastUpdateFailByCollectionMs[name] = at;
|
|
2417
|
+
globalState.lastGlobalUpdateFailAtMs = at;
|
|
2418
|
+
} else {
|
|
2419
|
+
this._lastUpdateFailAtMs = at;
|
|
2420
|
+
globalState.lastGlobalUpdateFailAtMs = at;
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2418
2423
|
log.warn(`QMD update failed for collection ${name}: ${msg}`);
|
|
2419
2424
|
if (options.strict) {
|
|
2420
2425
|
throw err;
|