@remnic/core 9.47.1 → 9.48.0
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 +6 -5
- package/dist/access-admin-ops-surface.js +27 -25
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +6 -5
- package/dist/access-authorization-probe.js +28 -26
- package/dist/access-boundary.d.ts +6 -5
- package/dist/access-boundary.js +27 -25
- package/dist/access-cli.js +87 -85
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.d.ts +6 -5
- package/dist/access-extraction-force-flush.js +27 -25
- package/dist/access-http-lcm-compaction.d.ts +6 -5
- package/dist/access-http-lifecycle-flush.d.ts +6 -5
- package/dist/access-http-offline-stream.d.ts +6 -5
- package/dist/access-http-query.js +27 -25
- package/dist/access-http.d.ts +6 -5
- package/dist/access-http.js +36 -34
- package/dist/access-identity-continuity-surface.d.ts +5 -4
- package/dist/access-identity-continuity-surface.js +27 -25
- package/dist/access-lcm-surface.d.ts +6 -5
- package/dist/access-lcm-surface.js +27 -25
- package/dist/access-mcp.d.ts +6 -5
- package/dist/access-mcp.js +32 -30
- package/dist/access-namespace-preflight.js +27 -25
- package/dist/access-observe-write-surface.d.ts +6 -5
- package/dist/access-observe-write-surface.js +27 -25
- package/dist/access-offline-manifest.d.ts +5 -4
- package/dist/access-offline-manifest.js +4 -4
- package/dist/access-operations-batch.js +29 -27
- package/dist/access-operations.d.ts +6 -5
- package/dist/access-operations.js +31 -29
- package/dist/access-recall-concurrency.d.ts +6 -5
- package/dist/access-recall-concurrency.js +27 -25
- package/dist/access-recall-response.d.ts +6 -5
- package/dist/access-recall-response.js +27 -25
- package/dist/access-recall-surface.d.ts +11 -10
- package/dist/access-recall-surface.js +27 -25
- package/dist/access-schema.d.ts +2 -2
- package/dist/{access-service-DNBYHK33.d.ts → access-service-andNjwfD.d.ts} +3 -3
- package/dist/access-service-helpers.d.ts +6 -5
- package/dist/access-service.d.ts +10 -9
- package/dist/access-service.js +27 -25
- package/dist/access-surface-catalog.d.ts +6 -5
- package/dist/access-wearables-meetings-surface.d.ts +3 -2
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/artifact-search.d.ts +37 -0
- package/dist/artifact-search.js +13 -0
- package/dist/artifact-search.js.map +1 -0
- package/dist/bootstrap.d.ts +5 -4
- package/dist/briefing.d.ts +2 -1
- package/dist/briefing.js +10 -7
- package/dist/buffer.d.ts +2 -1
- package/dist/buffer.js +4 -4
- package/dist/causal-consolidation.js +11 -8
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/{chunk-T6JRMVOV.js → chunk-2EJNXE53.js} +2 -2
- package/dist/{chunk-SIP5DBDN.js → chunk-2P3GQVM2.js} +152 -130
- package/dist/chunk-2P3GQVM2.js.map +1 -0
- package/dist/{chunk-64BHFFKZ.js → chunk-2RPJ4NEE.js} +2 -2
- package/dist/{chunk-JFO5QZ7N.js → chunk-3JUYWPFJ.js} +2 -2
- package/dist/{chunk-L7G72WY4.js → chunk-3NDN5P55.js} +2 -2
- package/dist/{chunk-IXH2U2QT.js → chunk-45OTV7FZ.js} +19 -19
- package/dist/chunk-4O4TKIXQ.js +171 -0
- package/dist/chunk-4O4TKIXQ.js.map +1 -0
- package/dist/{chunk-BDNL34CT.js → chunk-4WBGZYZZ.js} +33 -8
- package/dist/chunk-4WBGZYZZ.js.map +1 -0
- package/dist/{chunk-U6JGQ37L.js → chunk-52I3YZZ6.js} +3 -3
- package/dist/{chunk-IZQ5TDFT.js → chunk-53AJILXF.js} +3 -3
- package/dist/chunk-5AUY5Z5E.js +34 -0
- package/dist/chunk-5AUY5Z5E.js.map +1 -0
- package/dist/{chunk-YWCOJOUI.js → chunk-5L7PTX2Q.js} +2 -2
- package/dist/{chunk-ELIL2A37.js → chunk-7FGL6CGA.js} +5 -5
- package/dist/{chunk-TVVEYCNW.js → chunk-7K5Q6COX.js} +4 -4
- package/dist/{chunk-HXXTXLKA.js → chunk-A2TFK5PG.js} +2 -2
- package/dist/{chunk-LI7OE7G6.js → chunk-ANCC3JJH.js} +2 -2
- package/dist/{chunk-ILBLZ5Y5.js → chunk-D5NGH2EW.js} +2 -2
- package/dist/{chunk-ST5IEK3H.js → chunk-DUEEQCTT.js} +2 -2
- package/dist/{chunk-QISYTKG4.js → chunk-EZNXW27Q.js} +6 -6
- package/dist/{chunk-47J4EDGR.js → chunk-FJ2ND6CZ.js} +2 -2
- package/dist/{chunk-FQU3HTQM.js → chunk-FMCHFSZF.js} +6 -6
- package/dist/{chunk-FULKGK7P.js → chunk-G2RBWVFQ.js} +2 -2
- package/dist/{chunk-W2ZSNIBP.js → chunk-GD7UPIBG.js} +2 -2
- package/dist/{chunk-Z54EM5YZ.js → chunk-GF22HWS2.js} +4 -4
- package/dist/{chunk-ZDAYWJV6.js → chunk-GVX3NAUU.js} +2 -2
- package/dist/{chunk-M2INOSTA.js → chunk-GWHPQOUC.js} +2 -2
- package/dist/{chunk-N5XULCU3.js → chunk-HIDEBEAN.js} +9 -9
- package/dist/{chunk-74HN5QUH.js → chunk-HRMEYQRO.js} +2 -2
- package/dist/{chunk-VFZS2HAA.js → chunk-IP3DHDU2.js} +2 -2
- package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
- package/dist/{chunk-PFJXPOD4.js → chunk-JYPO7L3G.js} +2 -2
- package/dist/{chunk-G2L5IZV4.js → chunk-KSGEGHYA.js} +44 -44
- package/dist/{chunk-PKCLQ7KM.js → chunk-LZGCJSQR.js} +5 -5
- package/dist/{chunk-WOC6DXP5.js → chunk-M4DMO67I.js} +4 -4
- package/dist/{chunk-JCNJIXZN.js → chunk-NXUJRXSF.js} +2 -2
- package/dist/{chunk-5VQPZ4GV.js → chunk-PELT6ADC.js} +3 -3
- package/dist/{chunk-IDE5AR5W.js → chunk-PLCZD3LG.js} +28 -66
- package/dist/chunk-PLCZD3LG.js.map +1 -0
- package/dist/{chunk-KMWYZTIE.js → chunk-PRVJH7CY.js} +2 -2
- package/dist/{chunk-RVO6TCTU.js → chunk-QCECHYZL.js} +6 -6
- package/dist/{chunk-KA47C7BF.js → chunk-RSPSHRZA.js} +2 -2
- package/dist/{chunk-E4FOAENZ.js → chunk-S55OAS5P.js} +5 -5
- package/dist/{chunk-BIPBQQFI.js → chunk-UMOO7IY7.js} +2 -2
- package/dist/{chunk-5U7ID3EZ.js → chunk-UTVZKHJK.js} +2 -2
- package/dist/{chunk-V5FE3UAI.js → chunk-WOEVJKKQ.js} +2 -2
- package/dist/{chunk-N3LL5NEU.js → chunk-WQIHRA5F.js} +2 -2
- package/dist/chunk-Z7L72ZHL.js +75 -0
- package/dist/chunk-Z7L72ZHL.js.map +1 -0
- package/dist/{cli-BWXSPxB9.d.ts → cli-p_WmVo2h.d.ts} +3 -3
- package/dist/cli.d.ts +7 -6
- package/dist/cli.js +55 -53
- package/dist/compounding/engine.d.ts +2 -1
- package/dist/compounding/engine.js +10 -7
- package/dist/connectors/codex-materialize-runner.js +10 -7
- package/dist/connectors/index.js +10 -7
- package/dist/consolidation-provenance-check.d.ts +2 -1
- package/dist/consolidation-undo.d.ts +2 -1
- package/dist/contradiction/index.d.ts +7 -6
- package/dist/convergence-refresh.d.ts +2 -1
- package/dist/convergence-refresh.js +12 -9
- package/dist/corpus-watermark.js +15 -13
- package/dist/entity-recall-cancellation.d.ts +36 -0
- package/dist/entity-recall-cancellation.js +19 -0
- package/dist/entity-recall-cancellation.js.map +1 -0
- package/dist/entity-retrieval.d.ts +14 -6
- package/dist/entity-retrieval.js +11 -7
- package/dist/explicit-capture.d.ts +5 -4
- package/dist/external-wiki-access.d.ts +6 -5
- package/dist/external-wiki-access.js +28 -26
- package/dist/external-wiki-mcp-tools.d.ts +6 -5
- package/dist/extraction.js +15 -13
- package/dist/index.d.ts +11 -10
- package/dist/index.js +114 -112
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +7 -7
- package/dist/maintenance/memory-governance.js +10 -7
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +7 -6
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -7
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -1
- package/dist/maintenance/rebuild-memory-projection.js +11 -8
- package/dist/{maintenance-DQrL93Uu.d.ts → maintenance-BfcuwfJS.d.ts} +1 -1
- package/dist/mcp-memory-inspector-app.d.ts +6 -5
- package/dist/memory-worth-outcomes.d.ts +2 -1
- package/dist/namespaces/migrate.d.ts +2 -1
- package/dist/namespaces/migrate.js +14 -12
- package/dist/namespaces/search.js +4 -4
- package/dist/namespaces/storage.d.ts +7 -6
- package/dist/namespaces/storage.js +10 -7
- package/dist/offline-sync-impression-drain.js +3 -3
- package/dist/operator-doctor-corpus.js +16 -14
- package/dist/operator-toolkit.d.ts +2 -1
- package/dist/operator-toolkit.js +20 -18
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -1
- package/dist/orchestration/maintenance.d.ts +3 -2
- package/dist/orchestration/maintenance.js +14 -12
- package/dist/{orchestrator-CRR-6sVB.d.ts → orchestrator-B7nLo4z1.d.ts} +3 -3
- package/dist/orchestrator.d.ts +5 -4
- package/dist/orchestrator.js +89 -87
- package/dist/qmd.js +4 -4
- package/dist/recall-qos.d.ts +109 -2
- package/dist/recall-qos.js +12 -3
- package/dist/search/factory.js +3 -3
- package/dist/search/index.js +3 -3
- package/dist/semantic-consolidation.js +11 -8
- package/dist/semantic-rule-promotion.js +10 -7
- package/dist/semantic-rule-verifier.js +10 -7
- package/dist/{service-DJ0LDyuN.d.ts → service-B5W5IYCx.d.ts} +1 -1
- package/dist/source-agent-qualifier.js +13 -11
- package/dist/{storage-y340tYKb.d.ts → storage-COL907QF.d.ts} +2 -1
- package/dist/storage.d.ts +2 -1
- package/dist/storage.js +9 -6
- package/dist/temporal-supersession.d.ts +2 -1
- package/dist/tier-migration.d.ts +2 -1
- package/dist/verified-recall.js +10 -7
- package/package.json +2 -2
- package/src/artifact-search.test.ts +110 -0
- package/src/artifact-search.ts +115 -0
- package/src/entity-recall-cancellation.ts +58 -0
- package/src/entity-retrieval.ts +58 -4
- package/src/orchestration/namespace-read-fanout.ts +4 -1
- package/src/orchestration/recall-internal-deps.ts +2 -0
- package/src/orchestration/recall-internal.ts +76 -55
- package/src/orchestration/recall-search-pipeline.ts +9 -1
- package/src/orchestration/recall-search-prefilter.ts +18 -1
- package/src/orchestrator.ts +5 -6
- package/src/recall-qos.test.ts +289 -0
- package/src/recall-qos.ts +238 -1
- package/src/storage.ts +12 -56
- package/dist/chunk-BDNL34CT.js.map +0 -1
- package/dist/chunk-C3AKM55W.js +0 -59
- package/dist/chunk-C3AKM55W.js.map +0 -1
- package/dist/chunk-IDE5AR5W.js.map +0 -1
- package/dist/chunk-SIP5DBDN.js.map +0 -1
- /package/dist/{chunk-T6JRMVOV.js.map → chunk-2EJNXE53.js.map} +0 -0
- /package/dist/{chunk-64BHFFKZ.js.map → chunk-2RPJ4NEE.js.map} +0 -0
- /package/dist/{chunk-JFO5QZ7N.js.map → chunk-3JUYWPFJ.js.map} +0 -0
- /package/dist/{chunk-L7G72WY4.js.map → chunk-3NDN5P55.js.map} +0 -0
- /package/dist/{chunk-IXH2U2QT.js.map → chunk-45OTV7FZ.js.map} +0 -0
- /package/dist/{chunk-U6JGQ37L.js.map → chunk-52I3YZZ6.js.map} +0 -0
- /package/dist/{chunk-IZQ5TDFT.js.map → chunk-53AJILXF.js.map} +0 -0
- /package/dist/{chunk-YWCOJOUI.js.map → chunk-5L7PTX2Q.js.map} +0 -0
- /package/dist/{chunk-ELIL2A37.js.map → chunk-7FGL6CGA.js.map} +0 -0
- /package/dist/{chunk-TVVEYCNW.js.map → chunk-7K5Q6COX.js.map} +0 -0
- /package/dist/{chunk-HXXTXLKA.js.map → chunk-A2TFK5PG.js.map} +0 -0
- /package/dist/{chunk-LI7OE7G6.js.map → chunk-ANCC3JJH.js.map} +0 -0
- /package/dist/{chunk-ILBLZ5Y5.js.map → chunk-D5NGH2EW.js.map} +0 -0
- /package/dist/{chunk-ST5IEK3H.js.map → chunk-DUEEQCTT.js.map} +0 -0
- /package/dist/{chunk-QISYTKG4.js.map → chunk-EZNXW27Q.js.map} +0 -0
- /package/dist/{chunk-47J4EDGR.js.map → chunk-FJ2ND6CZ.js.map} +0 -0
- /package/dist/{chunk-FQU3HTQM.js.map → chunk-FMCHFSZF.js.map} +0 -0
- /package/dist/{chunk-FULKGK7P.js.map → chunk-G2RBWVFQ.js.map} +0 -0
- /package/dist/{chunk-W2ZSNIBP.js.map → chunk-GD7UPIBG.js.map} +0 -0
- /package/dist/{chunk-Z54EM5YZ.js.map → chunk-GF22HWS2.js.map} +0 -0
- /package/dist/{chunk-ZDAYWJV6.js.map → chunk-GVX3NAUU.js.map} +0 -0
- /package/dist/{chunk-M2INOSTA.js.map → chunk-GWHPQOUC.js.map} +0 -0
- /package/dist/{chunk-N5XULCU3.js.map → chunk-HIDEBEAN.js.map} +0 -0
- /package/dist/{chunk-74HN5QUH.js.map → chunk-HRMEYQRO.js.map} +0 -0
- /package/dist/{chunk-VFZS2HAA.js.map → chunk-IP3DHDU2.js.map} +0 -0
- /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-PFJXPOD4.js.map → chunk-JYPO7L3G.js.map} +0 -0
- /package/dist/{chunk-G2L5IZV4.js.map → chunk-KSGEGHYA.js.map} +0 -0
- /package/dist/{chunk-PKCLQ7KM.js.map → chunk-LZGCJSQR.js.map} +0 -0
- /package/dist/{chunk-WOC6DXP5.js.map → chunk-M4DMO67I.js.map} +0 -0
- /package/dist/{chunk-JCNJIXZN.js.map → chunk-NXUJRXSF.js.map} +0 -0
- /package/dist/{chunk-5VQPZ4GV.js.map → chunk-PELT6ADC.js.map} +0 -0
- /package/dist/{chunk-KMWYZTIE.js.map → chunk-PRVJH7CY.js.map} +0 -0
- /package/dist/{chunk-RVO6TCTU.js.map → chunk-QCECHYZL.js.map} +0 -0
- /package/dist/{chunk-KA47C7BF.js.map → chunk-RSPSHRZA.js.map} +0 -0
- /package/dist/{chunk-E4FOAENZ.js.map → chunk-S55OAS5P.js.map} +0 -0
- /package/dist/{chunk-BIPBQQFI.js.map → chunk-UMOO7IY7.js.map} +0 -0
- /package/dist/{chunk-5U7ID3EZ.js.map → chunk-UTVZKHJK.js.map} +0 -0
- /package/dist/{chunk-V5FE3UAI.js.map → chunk-WOEVJKKQ.js.map} +0 -0
- /package/dist/{chunk-N3LL5NEU.js.map → chunk-WQIHRA5F.js.map} +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { selectArtifactMatches, tokenizeArtifactSearchText } from "./artifact-search.js";
|
|
4
|
+
import type { MemoryFile } from "./types.js";
|
|
5
|
+
|
|
6
|
+
function artifact(id: string, content: string, tags: string[] = []): MemoryFile {
|
|
7
|
+
return {
|
|
8
|
+
path: `artifacts/${id}.md`,
|
|
9
|
+
content,
|
|
10
|
+
frontmatter: {
|
|
11
|
+
id,
|
|
12
|
+
category: "artifact",
|
|
13
|
+
created: "2026-08-01T00:00:00.000Z",
|
|
14
|
+
updated: "2026-08-01T00:00:00.000Z",
|
|
15
|
+
...(tags.length > 0 ? { tags } : {}),
|
|
16
|
+
},
|
|
17
|
+
} as unknown as MemoryFile;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test("artifact scan ranks by query token overlap and applies the result cap", async () => {
|
|
21
|
+
const matches = await selectArtifactMatches(
|
|
22
|
+
async () => [
|
|
23
|
+
artifact("a", "the deploy runbook covers rollback"),
|
|
24
|
+
artifact("b", "unrelated grocery list"),
|
|
25
|
+
artifact("c", "runbook", ["deploy", "rollback"]),
|
|
26
|
+
],
|
|
27
|
+
"deploy rollback runbook",
|
|
28
|
+
2,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
assert.deepEqual(
|
|
32
|
+
matches.map((m) => m.frontmatter.id),
|
|
33
|
+
["a", "c"],
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("a stopword-only query never reads the artifact tier", async () => {
|
|
38
|
+
// Reading the tier is a full recursive filesystem scan on a cold cache, so a
|
|
39
|
+
// query that cannot match anything must not pay for it (issue #2291).
|
|
40
|
+
let loaded = 0;
|
|
41
|
+
const matches = await selectArtifactMatches(
|
|
42
|
+
async () => {
|
|
43
|
+
loaded += 1;
|
|
44
|
+
return [artifact("a", "the and of")];
|
|
45
|
+
},
|
|
46
|
+
"the and of",
|
|
47
|
+
5,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
assert.deepEqual(matches, []);
|
|
51
|
+
assert.equal(loaded, 0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("an already-aborted caller never reads the artifact tier", async () => {
|
|
55
|
+
const aborted = new AbortController();
|
|
56
|
+
aborted.abort();
|
|
57
|
+
let loaded = 0;
|
|
58
|
+
|
|
59
|
+
await assert.rejects(
|
|
60
|
+
selectArtifactMatches(
|
|
61
|
+
async () => {
|
|
62
|
+
loaded += 1;
|
|
63
|
+
return [artifact("a", "deploy runbook")];
|
|
64
|
+
},
|
|
65
|
+
"deploy runbook",
|
|
66
|
+
5,
|
|
67
|
+
{ abortSignal: aborted.signal },
|
|
68
|
+
),
|
|
69
|
+
(err: unknown) => err instanceof Error && err.name === "AbortError",
|
|
70
|
+
);
|
|
71
|
+
assert.equal(loaded, 0);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("artifact scan stops at the caller's signal instead of scanning the tier", async () => {
|
|
75
|
+
// Yield/abort checkpoints land every 256 documents, so the corpus must cross
|
|
76
|
+
// that boundary for cancellation to be observable mid-scan (issue #2291).
|
|
77
|
+
const artifacts = Array.from({ length: 600 }, (_unused, index) =>
|
|
78
|
+
artifact(`doc-${index}`, "deploy runbook rollback"),
|
|
79
|
+
);
|
|
80
|
+
const aborted = new AbortController();
|
|
81
|
+
let scannedBeforeAbort = 0;
|
|
82
|
+
const artifactsWithProbe = artifacts.map((memory) => ({
|
|
83
|
+
get content() {
|
|
84
|
+
scannedBeforeAbort += 1;
|
|
85
|
+
if (scannedBeforeAbort === 300) aborted.abort();
|
|
86
|
+
return memory.content;
|
|
87
|
+
},
|
|
88
|
+
path: memory.path,
|
|
89
|
+
frontmatter: memory.frontmatter,
|
|
90
|
+
})) as unknown as MemoryFile[];
|
|
91
|
+
|
|
92
|
+
await assert.rejects(
|
|
93
|
+
selectArtifactMatches(async () => artifactsWithProbe, "deploy runbook", 5, {
|
|
94
|
+
abortSignal: aborted.signal,
|
|
95
|
+
}),
|
|
96
|
+
(err: unknown) => err instanceof Error && err.name === "AbortError",
|
|
97
|
+
);
|
|
98
|
+
assert.ok(
|
|
99
|
+
scannedBeforeAbort < artifacts.length,
|
|
100
|
+
`scan stopped early (read ${scannedBeforeAbort} of ${artifacts.length})`,
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("artifact tokenizer drops stopwords and single characters", () => {
|
|
105
|
+
assert.deepEqual(tokenizeArtifactSearchText("The a deploy-runbook v2"), [
|
|
106
|
+
"deploy",
|
|
107
|
+
"runbook",
|
|
108
|
+
"v2",
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verbatim-artifact keyword scan — extracted from `storage.ts` so the scan can
|
|
3
|
+
* be made interruptible without growing that file (issue #2291).
|
|
4
|
+
*
|
|
5
|
+
* The scan re-tokenizes every stored artifact, and `fetchActiveArtifactsForNamespace`
|
|
6
|
+
* repeats it with a growing fetch limit. On a large artifact tier that is long
|
|
7
|
+
* enough to starve the event loop, which would stop a recall section deadline
|
|
8
|
+
* from ever firing — so the loop yields periodically and observes cancellation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { throwIfAborted } from "./abort-error.js";
|
|
12
|
+
import { yieldToEventLoop } from "./recall-qos.js";
|
|
13
|
+
import type { MemoryFile } from "./types.js";
|
|
14
|
+
|
|
15
|
+
const ARTIFACT_SEARCH_STOPWORDS = new Set([
|
|
16
|
+
"a",
|
|
17
|
+
"an",
|
|
18
|
+
"and",
|
|
19
|
+
"are",
|
|
20
|
+
"as",
|
|
21
|
+
"at",
|
|
22
|
+
"be",
|
|
23
|
+
"but",
|
|
24
|
+
"by",
|
|
25
|
+
"for",
|
|
26
|
+
"from",
|
|
27
|
+
"has",
|
|
28
|
+
"have",
|
|
29
|
+
"i",
|
|
30
|
+
"in",
|
|
31
|
+
"is",
|
|
32
|
+
"it",
|
|
33
|
+
"of",
|
|
34
|
+
"on",
|
|
35
|
+
"or",
|
|
36
|
+
"that",
|
|
37
|
+
"the",
|
|
38
|
+
"this",
|
|
39
|
+
"to",
|
|
40
|
+
"was",
|
|
41
|
+
"were",
|
|
42
|
+
"with",
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Artifacts scored between two yields. Small enough that a deadline observes a
|
|
47
|
+
* sub-millisecond delay on a normal tier, large enough that the yields cost
|
|
48
|
+
* nothing measurable relative to tokenizing that many documents.
|
|
49
|
+
*/
|
|
50
|
+
const ARTIFACT_SCAN_YIELD_INTERVAL = 256;
|
|
51
|
+
|
|
52
|
+
export function tokenizeArtifactSearchText(input: string): string[] {
|
|
53
|
+
return input
|
|
54
|
+
.toLowerCase()
|
|
55
|
+
.split(/[^a-z0-9]+/i)
|
|
56
|
+
.map((t) => t.trim())
|
|
57
|
+
.filter((t) => t.length >= 2)
|
|
58
|
+
.filter((t) => !ARTIFACT_SEARCH_STOPWORDS.has(t));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ArtifactSearchOptions {
|
|
62
|
+
abortSignal?: AbortSignal;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Rank the artifact tier by how many query tokens each document contains, best
|
|
67
|
+
* first.
|
|
68
|
+
*
|
|
69
|
+
* `loadArtifacts` is a thunk rather than a value so the two cheap early exits —
|
|
70
|
+
* an already-cancelled caller, and a query with no searchable tokens — happen
|
|
71
|
+
* BEFORE the tier is read. Reading it can mean a full recursive filesystem scan
|
|
72
|
+
* on a cold cache, which a stopword-only query must never pay for.
|
|
73
|
+
*
|
|
74
|
+
* Yields to the event loop every `ARTIFACT_SCAN_YIELD_INTERVAL` documents and
|
|
75
|
+
* checks the caller's signal there, so a pending recall section deadline can fire
|
|
76
|
+
* mid-scan and an abandoned recall stops scanning instead of running to the end.
|
|
77
|
+
*/
|
|
78
|
+
export async function selectArtifactMatches(
|
|
79
|
+
loadArtifacts: () => Promise<MemoryFile[]>,
|
|
80
|
+
query: string,
|
|
81
|
+
maxResults: number,
|
|
82
|
+
options: ArtifactSearchOptions = {},
|
|
83
|
+
): Promise<MemoryFile[]> {
|
|
84
|
+
throwIfAborted(options.abortSignal, "artifact search aborted");
|
|
85
|
+
const tokens = tokenizeArtifactSearchText(query);
|
|
86
|
+
if (tokens.length === 0) return [];
|
|
87
|
+
|
|
88
|
+
const artifacts = await loadArtifacts();
|
|
89
|
+
// The scan below never awaits real I/O, so this is the last chance to observe a
|
|
90
|
+
// signal that fired while the tier was being read.
|
|
91
|
+
throwIfAborted(options.abortSignal, "artifact search aborted");
|
|
92
|
+
const hits: Array<{ score: number; memory: MemoryFile }> = [];
|
|
93
|
+
let scanned = 0;
|
|
94
|
+
for (const memory of artifacts) {
|
|
95
|
+
scanned += 1;
|
|
96
|
+
if (scanned % ARTIFACT_SCAN_YIELD_INTERVAL === 0) {
|
|
97
|
+
await yieldToEventLoop();
|
|
98
|
+
throwIfAborted(options.abortSignal, "artifact search aborted");
|
|
99
|
+
}
|
|
100
|
+
const indexedTokens = new Set(
|
|
101
|
+
tokenizeArtifactSearchText(`${memory.content} ${(memory.frontmatter.tags ?? []).join(" ")}`),
|
|
102
|
+
);
|
|
103
|
+
let score = 0;
|
|
104
|
+
for (const token of tokens) {
|
|
105
|
+
if (indexedTokens.has(token)) score += 1;
|
|
106
|
+
}
|
|
107
|
+
if (score > 0) {
|
|
108
|
+
hits.push({ score, memory });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Ties keep corpus order: the comparator returns 0 for equal scores rather
|
|
112
|
+
// than an arbitrary sign, so repeated scans produce the same top-N.
|
|
113
|
+
hits.sort((a, b) => b.score - a.score);
|
|
114
|
+
return hits.slice(0, maxResults).map((h) => h.memory);
|
|
115
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cancellation checkpoints for entity recall (issue #2291).
|
|
3
|
+
*
|
|
4
|
+
* Entity recall reads every entity and memory file for the recalled namespaces and
|
|
5
|
+
* then makes several synchronous passes over the result. Both halves need the same
|
|
6
|
+
* two things at every boundary, in the same shape, with the same abort message —
|
|
7
|
+
* so they live here rather than being restated at each of a dozen call sites.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { throwIfAborted } from "./abort-error.js";
|
|
11
|
+
import { yieldToEventLoop } from "./recall-qos.js";
|
|
12
|
+
|
|
13
|
+
const ABORT_MESSAGE = "entity recall aborted";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Entities/memories processed between two yields during an index build. Matches
|
|
17
|
+
* the artifact scan's interval: large enough that the yields are free relative to
|
|
18
|
+
* the work, small enough that a section deadline is observed promptly.
|
|
19
|
+
*/
|
|
20
|
+
export const ENTITY_SCAN_YIELD_INTERVAL = 256;
|
|
21
|
+
|
|
22
|
+
/** Stop at the caller's signal without yielding. */
|
|
23
|
+
export function checkEntityRecallAbort(abortSignal?: AbortSignal): void {
|
|
24
|
+
throwIfAborted(abortSignal, ABORT_MESSAGE);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Hand the loop a macrotask, then stop if the caller gave up while it ran.
|
|
29
|
+
*
|
|
30
|
+
* A section deadline is a timer, so a synchronous pass over the corpus must be
|
|
31
|
+
* preceded by one of these or the deadline cannot fire during it.
|
|
32
|
+
*/
|
|
33
|
+
export async function yieldEntityRecallScan(abortSignal?: AbortSignal): Promise<void> {
|
|
34
|
+
await yieldToEventLoop();
|
|
35
|
+
throwIfAborted(abortSignal, ABORT_MESSAGE);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The same yield/abort pair, but only every `ENTITY_SCAN_YIELD_INTERVAL` items —
|
|
40
|
+
* for loops where yielding per item would cost more than the work itself.
|
|
41
|
+
*/
|
|
42
|
+
export async function yieldEntityRecallScanEvery(
|
|
43
|
+
processed: number,
|
|
44
|
+
abortSignal?: AbortSignal,
|
|
45
|
+
): Promise<void> {
|
|
46
|
+
if (processed % ENTITY_SCAN_YIELD_INTERVAL !== 0) return;
|
|
47
|
+
await yieldEntityRecallScan(abortSignal);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Exit an entity-recall path with "no section", unless the caller has actually
|
|
52
|
+
* cancelled — in which case throw, so the bounded runner records a breach rather
|
|
53
|
+
* than a successful empty section.
|
|
54
|
+
*/
|
|
55
|
+
export function entityRecallSectionAbsent(abortSignal?: AbortSignal): null {
|
|
56
|
+
throwIfAborted(abortSignal, ABORT_MESSAGE);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
package/src/entity-retrieval.ts
CHANGED
|
@@ -8,6 +8,12 @@ import { compareEntityTimestamps, normalizeEntityName, type StorageManager } fro
|
|
|
8
8
|
import { normalizeEntityText, resolveRequestedEntitySectionKeys } from "./entity-schema.js";
|
|
9
9
|
import type { EntityStructuredSection, MemoryFile, PluginConfig, TranscriptEntry } from "./types.js";
|
|
10
10
|
import { containsPhrase } from "./entity-retrieval-boundaries.js";
|
|
11
|
+
import {
|
|
12
|
+
checkEntityRecallAbort,
|
|
13
|
+
entityRecallSectionAbsent,
|
|
14
|
+
yieldEntityRecallScan,
|
|
15
|
+
yieldEntityRecallScanEvery,
|
|
16
|
+
} from "./entity-recall-cancellation.js";
|
|
11
17
|
|
|
12
18
|
const ENTITY_INDEX_VERSION = 3;
|
|
13
19
|
const RECENT_TRANSCRIPT_LOOKBACK_HOURS = 24;
|
|
@@ -81,6 +87,13 @@ export interface BuildEntityRecallSectionOptions {
|
|
|
81
87
|
maxRelatedEntities: number;
|
|
82
88
|
maxChars: number;
|
|
83
89
|
transcriptEntries: TranscriptEntry[];
|
|
90
|
+
/**
|
|
91
|
+
* Cancels the build at its next checkpoint (issue #2291). Entity recall reads
|
|
92
|
+
* every entity and memory file across the recalled namespaces, which is
|
|
93
|
+
* minutes of work on a large or slow store; without this the scan kept running
|
|
94
|
+
* after the recall that asked for it had been abandoned.
|
|
95
|
+
*/
|
|
96
|
+
abortSignal?: AbortSignal;
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
function tokenize(value: string): string[] {
|
|
@@ -523,7 +536,9 @@ async function buildEntityMentionIndex(
|
|
|
523
536
|
namespaceStorage?: (namespace: string) => Promise<StorageManager>,
|
|
524
537
|
nativeChunksOverride?: NativeKnowledgeChunk[],
|
|
525
538
|
resolvedStorages?: StorageManager[],
|
|
539
|
+
abortSignal?: AbortSignal,
|
|
526
540
|
): Promise<EntityMentionIndex> {
|
|
541
|
+
checkEntityRecallAbort(abortSignal);
|
|
527
542
|
const storages = resolvedStorages ?? await resolveEntityIndexStorages(
|
|
528
543
|
storage,
|
|
529
544
|
config,
|
|
@@ -539,6 +554,9 @@ async function buildEntityMentionIndex(
|
|
|
539
554
|
Promise.all(storages.map((scopedStorage) => scopedStorage.readAllMemories())),
|
|
540
555
|
nativeChunksOverride ? Promise.resolve(nativeChunksOverride) : readNativeChunks(config, recallNamespaces),
|
|
541
556
|
]);
|
|
557
|
+
// The bulk reads above cannot be interrupted mid-flight; stop before spending
|
|
558
|
+
// the (larger) indexing pass over their results.
|
|
559
|
+
checkEntityRecallAbort(abortSignal);
|
|
542
560
|
// Pair each entity with the storage that owns it (#1534): canonical ids must
|
|
543
561
|
// reflect that store's aliases and historical migration mappings, never another
|
|
544
562
|
// namespace's.
|
|
@@ -547,8 +565,14 @@ async function buildEntityMentionIndex(
|
|
|
547
565
|
);
|
|
548
566
|
const memories = memorySets.flat();
|
|
549
567
|
|
|
568
|
+
// The two passes over the corpus below never await real I/O, so without an
|
|
569
|
+
// explicit yield they hold the event loop and no recall section deadline can
|
|
570
|
+
// fire during them (issue #2291).
|
|
571
|
+
let scanned = 0;
|
|
550
572
|
const entities = new Map<string, EntityMentionIndexEntry>();
|
|
551
573
|
for (const { entity, storage: entityStorage } of entityRecords) {
|
|
574
|
+
scanned += 1;
|
|
575
|
+
await yieldEntityRecallScanEvery(scanned, abortSignal);
|
|
552
576
|
const canonicalId =
|
|
553
577
|
typeof entityStorage.normalizeEntityName === "function"
|
|
554
578
|
? entityStorage.normalizeEntityName(entity.name, entity.type)
|
|
@@ -589,7 +613,10 @@ async function buildEntityMentionIndex(
|
|
|
589
613
|
});
|
|
590
614
|
}
|
|
591
615
|
|
|
616
|
+
scanned = 0;
|
|
592
617
|
for (const memory of memories) {
|
|
618
|
+
scanned += 1;
|
|
619
|
+
await yieldEntityRecallScanEvery(scanned, abortSignal);
|
|
593
620
|
const entityRef = typeof memory.frontmatter.entityRef === "string" ? memory.frontmatter.entityRef : "";
|
|
594
621
|
if (!entityRef) continue;
|
|
595
622
|
const entry = entities.get(entityRef);
|
|
@@ -600,6 +627,11 @@ async function buildEntityMentionIndex(
|
|
|
600
627
|
}
|
|
601
628
|
}
|
|
602
629
|
|
|
630
|
+
// Each remaining phase is another synchronous pass over the whole index
|
|
631
|
+
// (alias map, native-chunk merge, sort, two full serializations). Yield between
|
|
632
|
+
// them so a section deadline can fire between passes instead of only after all
|
|
633
|
+
// of them (issue #2291).
|
|
634
|
+
await yieldEntityRecallScan(abortSignal);
|
|
603
635
|
const aliasIndex = buildAliasIndex([...entities.values()]);
|
|
604
636
|
for (const chunk of nativeChunks) {
|
|
605
637
|
const existingPseudo = entities.get(nativePseudoCanonicalId(chunk));
|
|
@@ -620,8 +652,11 @@ async function buildEntityMentionIndex(
|
|
|
620
652
|
entities.set(pseudoEntry.canonicalId, pseudoEntry);
|
|
621
653
|
}
|
|
622
654
|
|
|
655
|
+
await yieldEntityRecallScan(abortSignal);
|
|
623
656
|
const sortedEntities = [...entities.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
657
|
+
await yieldEntityRecallScan(abortSignal);
|
|
624
658
|
const previousEntities = previousIndex ? JSON.stringify(previousIndex.entities) : "";
|
|
659
|
+
await yieldEntityRecallScan(abortSignal);
|
|
625
660
|
const nextEntities = JSON.stringify(sortedEntities);
|
|
626
661
|
const entityStatusVersionAfter = shouldPersistIndex ? storage.getMemoryStatusVersion() : undefined;
|
|
627
662
|
const canPersistIndex = shouldPersistIndex && entityStatusVersionBefore === entityStatusVersionAfter;
|
|
@@ -635,6 +670,10 @@ async function buildEntityMentionIndex(
|
|
|
635
670
|
entities: sortedEntities,
|
|
636
671
|
};
|
|
637
672
|
if (canPersistIndex) {
|
|
673
|
+
// A build the caller has abandoned must not leave a persisted index behind:
|
|
674
|
+
// the write is I/O the timed-out recall no longer needs, and the next recall
|
|
675
|
+
// is already competing for the same disk.
|
|
676
|
+
checkEntityRecallAbort(abortSignal);
|
|
638
677
|
await writeEntityIndexState(storage, index);
|
|
639
678
|
}
|
|
640
679
|
return index;
|
|
@@ -1020,6 +1059,11 @@ function formatEntityHintSection(
|
|
|
1020
1059
|
}
|
|
1021
1060
|
|
|
1022
1061
|
export async function buildEntityRecallSection(options: BuildEntityRecallSectionOptions): Promise<string | null> {
|
|
1062
|
+
// Checkpoints sit at the real I/O boundaries below (persisted-index read,
|
|
1063
|
+
// native-chunk read, storage resolution, index build). The per-candidate
|
|
1064
|
+
// formatting that follows never awaits real I/O, so a deadline that fires
|
|
1065
|
+
// during a scan is observed here (issue #2291).
|
|
1066
|
+
checkEntityRecallAbort(options.abortSignal);
|
|
1023
1067
|
const prefixedMode = detectEntityQueryMode(options.query);
|
|
1024
1068
|
const persistedIndex = prefixedMode
|
|
1025
1069
|
? null
|
|
@@ -1029,6 +1073,7 @@ export async function buildEntityRecallSection(options: BuildEntityRecallSection
|
|
|
1029
1073
|
options.recallNamespaces,
|
|
1030
1074
|
options.namespaceStorage,
|
|
1031
1075
|
);
|
|
1076
|
+
checkEntityRecallAbort(options.abortSignal);
|
|
1032
1077
|
let nativeChunks: NativeKnowledgeChunk[] | undefined;
|
|
1033
1078
|
if (
|
|
1034
1079
|
!prefixedMode &&
|
|
@@ -1037,7 +1082,7 @@ export async function buildEntityRecallSection(options: BuildEntityRecallSection
|
|
|
1037
1082
|
) {
|
|
1038
1083
|
nativeChunks = await readNativeChunks(options.config, options.recallNamespaces);
|
|
1039
1084
|
if (!hasFreshNativeExplicitMention(persistedIndex, nativeChunks, options.query)) {
|
|
1040
|
-
return
|
|
1085
|
+
return entityRecallSectionAbsent(options.abortSignal);
|
|
1041
1086
|
}
|
|
1042
1087
|
}
|
|
1043
1088
|
const namespaceScoped =
|
|
@@ -1066,6 +1111,7 @@ export async function buildEntityRecallSection(options: BuildEntityRecallSection
|
|
|
1066
1111
|
let index: EntityMentionIndex | undefined = namespaceCacheKey
|
|
1067
1112
|
? namespaceEntityIndexCache.get(namespaceCacheKey)
|
|
1068
1113
|
: undefined;
|
|
1114
|
+
checkEntityRecallAbort(options.abortSignal);
|
|
1069
1115
|
if (!index) {
|
|
1070
1116
|
index = await buildEntityMentionIndex(
|
|
1071
1117
|
options.storage,
|
|
@@ -1074,21 +1120,27 @@ export async function buildEntityRecallSection(options: BuildEntityRecallSection
|
|
|
1074
1120
|
options.namespaceStorage,
|
|
1075
1121
|
nativeChunks,
|
|
1076
1122
|
resolvedStorages,
|
|
1123
|
+
options.abortSignal,
|
|
1077
1124
|
);
|
|
1078
1125
|
if (namespaceCacheKey) rememberNamespaceEntityIndex(namespaceCacheKey, index);
|
|
1079
1126
|
}
|
|
1127
|
+
// Candidate resolution is another synchronous pass over every entity and alias,
|
|
1128
|
+
// reached even on a cache hit that did no I/O at all — so yield here too, or a
|
|
1129
|
+
// cached corpus could still hold the loop past the section budget (issue #2291).
|
|
1130
|
+
await yieldEntityRecallScan(options.abortSignal);
|
|
1080
1131
|
const explicitCandidates = resolveExplicitCandidates(index, options.query);
|
|
1132
|
+
await yieldEntityRecallScan(options.abortSignal);
|
|
1081
1133
|
const queryCandidates = prefixedMode
|
|
1082
1134
|
? explicitCandidates
|
|
1083
1135
|
: resolveLanguageIndependentExplicitCandidates(index, options.query);
|
|
1084
1136
|
const mode = prefixedMode ?? (queryCandidates.length > 0 ? "direct" : null);
|
|
1085
|
-
if (!mode) return
|
|
1137
|
+
if (!mode) return entityRecallSectionAbsent(options.abortSignal);
|
|
1086
1138
|
|
|
1087
1139
|
const candidates = queryCandidates.length > 0
|
|
1088
1140
|
? queryCandidates
|
|
1089
1141
|
: resolveRecentTurnCandidates(index, options.transcriptEntries, options.recentTurns);
|
|
1090
1142
|
|
|
1091
|
-
if (candidates.length === 0) return
|
|
1143
|
+
if (candidates.length === 0) return entityRecallSectionAbsent(options.abortSignal);
|
|
1092
1144
|
|
|
1093
1145
|
const queryTokens = tokenize(options.query);
|
|
1094
1146
|
const candidateLimit = queryCandidates.length === 0 && mode === "follow_up"
|
|
@@ -1121,7 +1173,9 @@ export async function buildEntityRecallSection(options: BuildEntityRecallSection
|
|
|
1121
1173
|
);
|
|
1122
1174
|
|
|
1123
1175
|
const section = formatEntityHintSection(enriched, queryTokens, mode, options.maxRelatedEntities, options.maxChars);
|
|
1124
|
-
|
|
1176
|
+
// `entityRecallSectionAbsent` so a cancelled build is not recorded as a
|
|
1177
|
+
// successful section that simply found no entities.
|
|
1178
|
+
if (!section) return entityRecallSectionAbsent(options.abortSignal);
|
|
1125
1179
|
return section;
|
|
1126
1180
|
}
|
|
1127
1181
|
|
|
@@ -27,6 +27,7 @@ import { mergeArtifactRecallCandidates, tokenizeRecallQuery } from "./orchestrat
|
|
|
27
27
|
import { qmdCollectionPathParts } from "./qmd-result-resolver.js";
|
|
28
28
|
import { qmdCollectionNamespaceFromPrefix as computeQmdCollectionNamespaceFromPrefix } from "./orchestrator-namespace-scope.js";
|
|
29
29
|
import { resolveNamespaceFromStorageDir } from "../scopes/scope-plan.js";
|
|
30
|
+
import type { ArtifactRecallOptions } from "./recall-search-prefilter.js";
|
|
30
31
|
import type { SearchBackend, SearchExecutionOptions, SearchQueryOptions } from "../search/port.js";
|
|
31
32
|
import type { MemoryFile, PluginConfig, QmdSearchResult } from "../types.js";
|
|
32
33
|
import {
|
|
@@ -41,6 +42,7 @@ export interface NamespaceReadFanoutDeps {
|
|
|
41
42
|
namespace: string,
|
|
42
43
|
prompt: string,
|
|
43
44
|
targetCount: number,
|
|
45
|
+
options?: ArtifactRecallOptions,
|
|
44
46
|
): Promise<MemoryFile[]>;
|
|
45
47
|
loadNamespaceStorageDirHintsFromCatalog(): void;
|
|
46
48
|
readonly namespaceCatalog: NamespaceCatalog;
|
|
@@ -338,12 +340,13 @@ export class NamespaceReadFanoutCoordinator {
|
|
|
338
340
|
prompt: string,
|
|
339
341
|
recallNamespaces: string[],
|
|
340
342
|
targetCount: number,
|
|
343
|
+
options: ArtifactRecallOptions = {},
|
|
341
344
|
): Promise<MemoryFile[]> {
|
|
342
345
|
if (targetCount <= 0) return [];
|
|
343
346
|
const namespaces = Array.from(new Set(recallNamespaces));
|
|
344
347
|
const filteredByNamespace = await Promise.all(
|
|
345
348
|
namespaces.map((namespace) =>
|
|
346
|
-
this.deps.fetchActiveArtifactsForNamespace(namespace, prompt, targetCount),
|
|
349
|
+
this.deps.fetchActiveArtifactsForNamespace(namespace, prompt, targetCount, options),
|
|
347
350
|
),
|
|
348
351
|
);
|
|
349
352
|
|
|
@@ -35,6 +35,7 @@ import type { VerifiedEpisodeResult } from "../verified-recall.js";
|
|
|
35
35
|
import type { WorkProductLedgerSearchResult } from "../work-product-ledger.js";
|
|
36
36
|
import type { GraphRecallRankedResult, GraphRecallShadowComparison } from "./graph-recall-coordinator.js";
|
|
37
37
|
import type { RecallRerankCoordinator, RecallResultPartitionSink } from "./recall-rerank-coordinator.js";
|
|
38
|
+
import type { ArtifactRecallOptions } from "./recall-search-prefilter.js";
|
|
38
39
|
import type { RecallSectionAppendOptions, RecallSectionBuckets } from "./recall-section-coordinator.js";
|
|
39
40
|
|
|
40
41
|
export interface RecallInternalDeps {
|
|
@@ -339,6 +340,7 @@ export interface RecallInternalDeps {
|
|
|
339
340
|
prompt: string,
|
|
340
341
|
recallNamespaces: string[],
|
|
341
342
|
targetCount: number,
|
|
343
|
+
options?: ArtifactRecallOptions,
|
|
342
344
|
): Promise<MemoryFile[]>;
|
|
343
345
|
recallAssemblyClockMs(): number;
|
|
344
346
|
recordLastGraphRecallSnapshot(options: {
|