@remnic/core 9.3.665 → 9.3.666
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-audit.js +2 -2
- package/dist/access-cli.js +41 -40
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +3 -2
- package/dist/access-http.js +25 -25
- package/dist/access-mcp.d.ts +3 -2
- package/dist/access-mcp.js +22 -22
- package/dist/access-schema.d.ts +36 -36
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-D0SLB4MH.d.ts → access-service-DsS-TatL.d.ts} +1 -1
- package/dist/access-service.d.ts +3 -2
- package/dist/access-service.js +21 -21
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/bootstrap.d.ts +2 -1
- package/dist/briefing.js +4 -3
- package/dist/capabilities.d.ts +73 -0
- package/dist/capabilities.js +8 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +7 -6
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-ROHLEUTH.js → chunk-23EBQ27U.js} +5 -5
- package/dist/{chunk-YW52BQSU.js → chunk-2TCHDANJ.js} +2 -2
- package/dist/{chunk-IROWLAWG.js → chunk-46WUVFOD.js} +4 -4
- package/dist/{chunk-7C4MPEPE.js → chunk-4T7P2HLJ.js} +3 -3
- package/dist/{chunk-7XH7VJN4.js → chunk-6T4LTI2F.js} +4 -4
- package/dist/{chunk-TVVEYCNW.js → chunk-7K5Q6COX.js} +4 -4
- package/dist/{chunk-BZG2CWOQ.js → chunk-A5TEHAR4.js} +3 -3
- package/dist/{chunk-C7AF236A.js → chunk-AARDBQTA.js} +2 -2
- package/dist/{chunk-IHG6CC7T.js → chunk-BQJUPECT.js} +2 -2
- package/dist/{chunk-7OGJQP7T.js → chunk-CRO4LCQ6.js} +5 -5
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-WH4SKYPX.js → chunk-GZ6QAYSH.js} +94 -74
- package/dist/chunk-GZ6QAYSH.js.map +1 -0
- package/dist/{chunk-UXA5L2DZ.js → chunk-HQCGRSRU.js} +2 -2
- package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
- package/dist/{chunk-XW3W4PV4.js → chunk-JTPXSXHC.js} +2 -2
- package/dist/{chunk-OHJFJ4HI.js → chunk-KOXGLQS7.js} +2 -2
- package/dist/{chunk-2OPARZ4B.js → chunk-MPXYHC35.js} +26 -26
- package/dist/{chunk-6JBKHTQD.js → chunk-MR4PJ277.js} +2 -2
- package/dist/{chunk-EXXBA5OM.js → chunk-OI4BXFSB.js} +4 -4
- package/dist/{chunk-SQZ42MKH.js → chunk-OQH5XUH3.js} +6 -3
- package/dist/chunk-OQH5XUH3.js.map +1 -0
- package/dist/{chunk-2HEZXPYU.js → chunk-Q2LQZYQ7.js} +3 -3
- package/dist/{chunk-XRSIGVTS.js → chunk-QHWJG5C5.js} +8 -8
- package/dist/{chunk-T2AN3BSP.js → chunk-QZ7ODIVL.js} +2 -2
- package/dist/chunk-RI5XBIZ6.js +23 -0
- package/dist/chunk-RI5XBIZ6.js.map +1 -0
- package/dist/{chunk-D7IXTY5E.js → chunk-TJ7HH5LB.js} +2 -2
- package/dist/{chunk-V25ZAOSB.js → chunk-UOBLE67F.js} +4 -4
- package/dist/{chunk-JIX3ZL2J.js → chunk-UVUTV7CM.js} +15 -15
- package/dist/{chunk-VH6EIKVS.js → chunk-WKMCC4NQ.js} +35 -16
- package/dist/chunk-WKMCC4NQ.js.map +1 -0
- package/dist/{chunk-SSOMTUCA.js → chunk-WXGTC424.js} +1 -1
- package/dist/{chunk-KHGE6PMF.js → chunk-WXXLSZHA.js} +2 -2
- package/dist/{chunk-DSLUOQDY.js → chunk-XMWF6AU3.js} +2 -2
- package/dist/{chunk-DQY7NJ5L.js → chunk-XS2CWEHZ.js} +2 -2
- package/dist/{cli-BQRqR9N-.d.ts → cli-BypxcNqq.d.ts} +2 -2
- package/dist/cli.d.ts +4 -3
- package/dist/cli.js +42 -42
- package/dist/compounding/engine.js +4 -3
- package/dist/connectors/codex-materialize-runner.js +4 -3
- package/dist/connectors/index.js +4 -3
- package/dist/consolidation-provenance-check.js +2 -2
- package/dist/conversation-index/backend.js +2 -2
- package/dist/dashboard-runtime.js +2 -2
- package/dist/direct-answer-wiring.d.ts +13 -3
- package/dist/direct-answer-wiring.js +1 -1
- package/dist/entity-retrieval.js +4 -3
- package/dist/explicit-capture.d.ts +2 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +66 -65
- package/dist/index.js.map +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +4 -4
- package/dist/maintenance/memory-governance.js +4 -4
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -3
- package/dist/maintenance/rebuild-memory-projection.js +5 -5
- package/dist/mcp-memory-inspector-app.d.ts +3 -2
- package/dist/namespaces/migrate.js +11 -11
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.js +4 -3
- package/dist/operator-toolkit.js +15 -15
- package/dist/{orchestrator-Cg1UkvmO.d.ts → orchestrator-DZqPVoMI.d.ts} +8 -0
- package/dist/orchestrator.d.ts +2 -1
- package/dist/orchestrator.js +32 -31
- package/dist/recall-planner-llm.d.ts +2 -1
- package/dist/recall-planner-llm.js +3 -2
- package/dist/recall-planner-llm.js.map +1 -1
- package/dist/schemas.d.ts +42 -42
- package/dist/search/factory.js +6 -6
- package/dist/search/index.js +10 -10
- package/dist/search/lancedb-backend.js +1 -1
- package/dist/search/meilisearch-backend.js +1 -1
- package/dist/search/orama-backend.js +1 -1
- package/dist/semantic-consolidation.js +5 -4
- package/dist/semantic-rule-promotion.js +4 -3
- package/dist/semantic-rule-verifier.js +4 -3
- package/dist/shared-context/manager.d.ts +2 -2
- package/dist/storage.js +3 -2
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +1 -1
- package/dist/verified-recall.js +4 -3
- package/package.json +1 -1
- package/src/capabilities.test.ts +97 -0
- package/src/capabilities.ts +86 -0
- package/src/direct-answer-wiring.test.ts +53 -2
- package/src/direct-answer-wiring.ts +18 -5
- package/src/orchestrator.ts +69 -19
- package/src/recall-planner-llm.test.ts +12 -11
- package/src/recall-planner-llm.ts +7 -1
- package/src/storage-fallback-category-dirs.test.ts +150 -1
- package/src/storage.ts +51 -14
- package/dist/chunk-SQZ42MKH.js.map +0 -1
- package/dist/chunk-VH6EIKVS.js.map +0 -1
- package/dist/chunk-WH4SKYPX.js.map +0 -1
- /package/dist/{chunk-ROHLEUTH.js.map → chunk-23EBQ27U.js.map} +0 -0
- /package/dist/{chunk-YW52BQSU.js.map → chunk-2TCHDANJ.js.map} +0 -0
- /package/dist/{chunk-IROWLAWG.js.map → chunk-46WUVFOD.js.map} +0 -0
- /package/dist/{chunk-7C4MPEPE.js.map → chunk-4T7P2HLJ.js.map} +0 -0
- /package/dist/{chunk-7XH7VJN4.js.map → chunk-6T4LTI2F.js.map} +0 -0
- /package/dist/{chunk-TVVEYCNW.js.map → chunk-7K5Q6COX.js.map} +0 -0
- /package/dist/{chunk-BZG2CWOQ.js.map → chunk-A5TEHAR4.js.map} +0 -0
- /package/dist/{chunk-C7AF236A.js.map → chunk-AARDBQTA.js.map} +0 -0
- /package/dist/{chunk-IHG6CC7T.js.map → chunk-BQJUPECT.js.map} +0 -0
- /package/dist/{chunk-7OGJQP7T.js.map → chunk-CRO4LCQ6.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-UXA5L2DZ.js.map → chunk-HQCGRSRU.js.map} +0 -0
- /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-XW3W4PV4.js.map → chunk-JTPXSXHC.js.map} +0 -0
- /package/dist/{chunk-OHJFJ4HI.js.map → chunk-KOXGLQS7.js.map} +0 -0
- /package/dist/{chunk-2OPARZ4B.js.map → chunk-MPXYHC35.js.map} +0 -0
- /package/dist/{chunk-6JBKHTQD.js.map → chunk-MR4PJ277.js.map} +0 -0
- /package/dist/{chunk-EXXBA5OM.js.map → chunk-OI4BXFSB.js.map} +0 -0
- /package/dist/{chunk-2HEZXPYU.js.map → chunk-Q2LQZYQ7.js.map} +0 -0
- /package/dist/{chunk-XRSIGVTS.js.map → chunk-QHWJG5C5.js.map} +0 -0
- /package/dist/{chunk-T2AN3BSP.js.map → chunk-QZ7ODIVL.js.map} +0 -0
- /package/dist/{chunk-D7IXTY5E.js.map → chunk-TJ7HH5LB.js.map} +0 -0
- /package/dist/{chunk-V25ZAOSB.js.map → chunk-UOBLE67F.js.map} +0 -0
- /package/dist/{chunk-JIX3ZL2J.js.map → chunk-UVUTV7CM.js.map} +0 -0
- /package/dist/{chunk-SSOMTUCA.js.map → chunk-WXGTC424.js.map} +0 -0
- /package/dist/{chunk-KHGE6PMF.js.map → chunk-WXXLSZHA.js.map} +0 -0
- /package/dist/{chunk-DSLUOQDY.js.map → chunk-XMWF6AU3.js.map} +0 -0
- /package/dist/{chunk-DQY7NJ5L.js.map → chunk-XS2CWEHZ.js.map} +0 -0
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
import "../chunk-WEHSQBFR.js";
|
|
6
6
|
import "../chunk-X7Y7WX73.js";
|
|
7
7
|
import "../chunk-J4EB7DNW.js";
|
|
8
|
+
import "../chunk-UI3NYK34.js";
|
|
9
|
+
import "../chunk-GCGJW34D.js";
|
|
8
10
|
import "../chunk-BJMBJZ2Y.js";
|
|
9
11
|
import "../chunk-UKJAGEXH.js";
|
|
10
12
|
import "../chunk-FP2373TW.js";
|
|
11
|
-
import "../chunk-UI3NYK34.js";
|
|
12
|
-
import "../chunk-GCGJW34D.js";
|
|
13
13
|
import "../chunk-A6XUJE5D.js";
|
|
14
14
|
import "../chunk-PZ5AY32C.js";
|
|
15
15
|
export {
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
import "../chunk-VF4XKTX3.js";
|
|
5
5
|
import "../chunk-WEHSQBFR.js";
|
|
6
6
|
import "../chunk-X7Y7WX73.js";
|
|
7
|
+
import "../chunk-GCGJW34D.js";
|
|
7
8
|
import "../chunk-BJMBJZ2Y.js";
|
|
8
9
|
import "../chunk-UKJAGEXH.js";
|
|
9
10
|
import "../chunk-FP2373TW.js";
|
|
10
|
-
import "../chunk-GCGJW34D.js";
|
|
11
11
|
import "../chunk-A6XUJE5D.js";
|
|
12
12
|
import "../chunk-VS2IYZRU.js";
|
|
13
13
|
import "../chunk-PZ5AY32C.js";
|
package/dist/verified-recall.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compareVerifiedEpisodeResults,
|
|
3
3
|
searchVerifiedEpisodes
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2TCHDANJ.js";
|
|
5
5
|
import "./chunk-HQ6NIBL6.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-WKMCC4NQ.js";
|
|
7
7
|
import "./chunk-M7XQSUBB.js";
|
|
8
8
|
import "./chunk-5UZXUTVO.js";
|
|
9
|
+
import "./chunk-5GPPACXK.js";
|
|
9
10
|
import "./chunk-J6A3CX5N.js";
|
|
10
11
|
import "./chunk-AZBV4RRY.js";
|
|
11
12
|
import "./chunk-AWJ2FHCF.js";
|
|
@@ -17,8 +18,8 @@ import "./chunk-6KYMPV2O.js";
|
|
|
17
18
|
import "./chunk-DM2T26WE.js";
|
|
18
19
|
import "./chunk-LDXUBPMO.js";
|
|
19
20
|
import "./chunk-FVQJYWH7.js";
|
|
20
|
-
import "./chunk-G7D6GZ5J.js";
|
|
21
21
|
import "./chunk-VF4XKTX3.js";
|
|
22
|
+
import "./chunk-G7D6GZ5J.js";
|
|
22
23
|
import "./chunk-4DJQYKMN.js";
|
|
23
24
|
import "./chunk-ZBJMUXZH.js";
|
|
24
25
|
import "./chunk-2ODBA7MQ.js";
|
package/package.json
CHANGED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { parseConfig } from "./config.js";
|
|
5
|
+
import { resolveCapabilities, type CapabilitySet } from "./capabilities.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Characterization tests for the recall-operation CapabilitySet (issue #1523).
|
|
9
|
+
*
|
|
10
|
+
* These guard against composition drift: every capability field must project
|
|
11
|
+
* from its `<field>Enabled` config flag, so a future edit to
|
|
12
|
+
* `resolveCapabilities` that accidentally maps a field to the wrong flag (the
|
|
13
|
+
* rule-39 gate-divergence class, moved up one layer) fails loudly here.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Map of CapabilitySet field → the PluginConfig flag it projects from.
|
|
18
|
+
* Kept explicit (rather than derived by string concat) so the two graph flags
|
|
19
|
+
* with non-`<field>Enabled` names are covered too.
|
|
20
|
+
*/
|
|
21
|
+
const FIELD_TO_FLAG: Record<keyof CapabilitySet, string> = {
|
|
22
|
+
rerankCache: "rerankCacheEnabled",
|
|
23
|
+
recallDirectAnswer: "recallDirectAnswerEnabled",
|
|
24
|
+
recallMemoryWorthFilter: "recallMemoryWorthFilterEnabled",
|
|
25
|
+
recallMmr: "recallMmrEnabled",
|
|
26
|
+
recallReasoningTraceBoost: "recallReasoningTraceBoostEnabled",
|
|
27
|
+
recallPlannerLlm: "recallPlannerLlmEnabled",
|
|
28
|
+
recallPlanner: "recallPlannerEnabled",
|
|
29
|
+
recallConfidenceGate: "recallConfidenceGateEnabled",
|
|
30
|
+
graphRecall: "graphRecallEnabled",
|
|
31
|
+
graphAssistInFullMode: "graphAssistInFullModeEnabled",
|
|
32
|
+
graphExpandedIntent: "graphExpandedIntentEnabled",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const FIELDS = Object.keys(FIELD_TO_FLAG) as Array<keyof CapabilitySet>;
|
|
36
|
+
|
|
37
|
+
test("resolveCapabilities projects every field from its <field>Enabled flag (true variant)", () => {
|
|
38
|
+
// Build a config where every migrated flag is explicitly true.
|
|
39
|
+
const overrides: Record<string, boolean> = {};
|
|
40
|
+
for (const flag of Object.values(FIELD_TO_FLAG)) overrides[flag] = true;
|
|
41
|
+
const config = parseConfig(overrides);
|
|
42
|
+
const caps = resolveCapabilities(config);
|
|
43
|
+
|
|
44
|
+
for (const field of FIELDS) {
|
|
45
|
+
const flag = FIELD_TO_FLAG[field];
|
|
46
|
+
assert.equal(
|
|
47
|
+
caps[field],
|
|
48
|
+
(config as unknown as Record<string, boolean>)[flag],
|
|
49
|
+
`caps.${field} must equal config.${flag} (true variant)`,
|
|
50
|
+
);
|
|
51
|
+
assert.equal(caps[field], true, `caps.${field} should be true here`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("resolveCapabilities projects every field from its <field>Enabled flag (false variant)", () => {
|
|
56
|
+
const overrides: Record<string, boolean> = {};
|
|
57
|
+
for (const flag of Object.values(FIELD_TO_FLAG)) overrides[flag] = false;
|
|
58
|
+
const config = parseConfig(overrides);
|
|
59
|
+
const caps = resolveCapabilities(config);
|
|
60
|
+
|
|
61
|
+
for (const field of FIELDS) {
|
|
62
|
+
const flag = FIELD_TO_FLAG[field];
|
|
63
|
+
// The two optional graph flags carry default-when-undefined semantics, but
|
|
64
|
+
// when explicitly set to a concrete boolean the projection must match it.
|
|
65
|
+
assert.equal(
|
|
66
|
+
caps[field],
|
|
67
|
+
(config as unknown as Record<string, boolean>)[flag],
|
|
68
|
+
`caps.${field} must equal config.${flag} (false variant)`,
|
|
69
|
+
);
|
|
70
|
+
assert.equal(caps[field], false, `caps.${field} should be false here`);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("resolveCapabilities preserves optional-flag defaults when the flag is undefined", () => {
|
|
75
|
+
// parseConfig with no overrides exercises the documented defaults. The two
|
|
76
|
+
// optional graph flags encode asymmetric defaults on purpose:
|
|
77
|
+
// graphAssistInFullModeEnabled → default-ON (`!== false`)
|
|
78
|
+
// graphExpandedIntentEnabled → default-OFF (`=== true`)
|
|
79
|
+
const config = parseConfig({});
|
|
80
|
+
const caps = resolveCapabilities(config);
|
|
81
|
+
|
|
82
|
+
assert.equal(
|
|
83
|
+
caps.graphAssistInFullMode,
|
|
84
|
+
config.graphAssistInFullModeEnabled !== false,
|
|
85
|
+
"graphAssistInFullMode must be default-on unless explicitly false",
|
|
86
|
+
);
|
|
87
|
+
assert.equal(
|
|
88
|
+
caps.graphExpandedIntent,
|
|
89
|
+
config.graphExpandedIntentEnabled === true,
|
|
90
|
+
"graphExpandedIntent must be default-off unless explicitly true",
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("resolveCapabilities returns a frozen object", () => {
|
|
95
|
+
const caps = resolveCapabilities(parseConfig({}));
|
|
96
|
+
assert.equal(Object.isFrozen(caps), true, "CapabilitySet must be frozen");
|
|
97
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapabilitySet — recall-operation feature gates resolved once, then threaded.
|
|
3
|
+
*
|
|
4
|
+
* Issue #1523 (Phase 1 of epic #1520). Root cause this addresses: 161+
|
|
5
|
+
* scattered `config.<flag>Enabled` reads mean each gate is re-derived at every
|
|
6
|
+
* call site, and reviews keep finding parallel code paths where one branch
|
|
7
|
+
* checks a gate the other forgot (CLAUDE.md rule 39 — the "gate divergence"
|
|
8
|
+
* defect class). The fix is to resolve a frozen capability projection ONCE at
|
|
9
|
+
* the top of the recall operation and pass it down explicitly.
|
|
10
|
+
*
|
|
11
|
+
* Scope of THIS module (first migration PR): only the recall-operation-scoped
|
|
12
|
+
* flags below. Flags that are also read in graph construction, writes, CLI, or
|
|
13
|
+
* the summarizer are deliberately deferred to a follow-up so we never leave a
|
|
14
|
+
* single flag half-migrated (some sites on `caps.`, some on `config.`).
|
|
15
|
+
*
|
|
16
|
+
* Field naming: each field is the config flag name with the trailing `Enabled`
|
|
17
|
+
* removed (`recallMmrEnabled` → `recallMmr`).
|
|
18
|
+
*
|
|
19
|
+
* This is plumbing, not a feature — there is deliberately NO `enabled` gate for
|
|
20
|
+
* the CapabilitySet itself (rule 30 governs behavior changes; resolving and
|
|
21
|
+
* threading a capability projection must stay behavior-preserving).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { PluginConfig } from "./types.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Frozen projection of recall-operation feature gates.
|
|
28
|
+
*
|
|
29
|
+
* Every field is `readonly boolean`. The composition that maps a config flag to
|
|
30
|
+
* a capability (including default-when-undefined semantics for optional flags)
|
|
31
|
+
* lives ONLY in {@link resolveCapabilities} — call sites must read the
|
|
32
|
+
* capability, never re-derive it from raw config.
|
|
33
|
+
*/
|
|
34
|
+
export interface CapabilitySet {
|
|
35
|
+
/** `rerankCacheEnabled` — cache reranker scores across recall passes. */
|
|
36
|
+
readonly rerankCache: boolean;
|
|
37
|
+
/** `recallDirectAnswerEnabled` — observation-mode direct-answer tier. */
|
|
38
|
+
readonly recallDirectAnswer: boolean;
|
|
39
|
+
/** `recallMemoryWorthFilterEnabled` — Memory-Worth score reweighting. */
|
|
40
|
+
readonly recallMemoryWorthFilter: boolean;
|
|
41
|
+
/** `recallMmrEnabled` — maximal-marginal-relevance diversification. */
|
|
42
|
+
readonly recallMmr: boolean;
|
|
43
|
+
/** `recallReasoningTraceBoostEnabled` — boost reasoning-trace memories. */
|
|
44
|
+
readonly recallReasoningTraceBoost: boolean;
|
|
45
|
+
/** `recallPlannerLlmEnabled` — LLM-backed recall-mode planner. */
|
|
46
|
+
readonly recallPlannerLlm: boolean;
|
|
47
|
+
/** `recallPlannerEnabled` — recall-mode planner (heuristic + optional LLM). */
|
|
48
|
+
readonly recallPlanner: boolean;
|
|
49
|
+
/** `recallConfidenceGateEnabled` — Synapse-style confidence gate. */
|
|
50
|
+
readonly recallConfidenceGate: boolean;
|
|
51
|
+
/** `graphRecallEnabled` — graph-mode recall tier (gates planner graph mode). */
|
|
52
|
+
readonly graphRecall: boolean;
|
|
53
|
+
/** `graphAssistInFullModeEnabled` — graph-assist overlay in full mode. */
|
|
54
|
+
readonly graphAssistInFullMode: boolean;
|
|
55
|
+
/** `graphExpandedIntentEnabled` — promote broad-intent asks to graph mode. */
|
|
56
|
+
readonly graphExpandedIntent: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the recall-operation {@link CapabilitySet} from parsed config.
|
|
61
|
+
*
|
|
62
|
+
* Call this ONCE per recall operation (at the `recall()` / `recallInternal`
|
|
63
|
+
* entry) and thread the result down. Composition lives here and only here.
|
|
64
|
+
*
|
|
65
|
+
* Session toggles are intentionally not a parameter yet: `session-toggles.ts`
|
|
66
|
+
* is agent-scoped (per session/agent enable-disable of the whole plugin), not
|
|
67
|
+
* flag-scoped — none of the flags projected here have a per-session override,
|
|
68
|
+
* so there is nothing for a toggle argument to compose at this layer.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveCapabilities(config: PluginConfig): CapabilitySet {
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
rerankCache: config.rerankCacheEnabled,
|
|
73
|
+
recallDirectAnswer: config.recallDirectAnswerEnabled,
|
|
74
|
+
recallMemoryWorthFilter: config.recallMemoryWorthFilterEnabled,
|
|
75
|
+
recallMmr: config.recallMmrEnabled,
|
|
76
|
+
recallReasoningTraceBoost: config.recallReasoningTraceBoostEnabled,
|
|
77
|
+
recallPlannerLlm: config.recallPlannerLlmEnabled,
|
|
78
|
+
recallPlanner: config.recallPlannerEnabled,
|
|
79
|
+
recallConfidenceGate: config.recallConfidenceGateEnabled,
|
|
80
|
+
graphRecall: config.graphRecallEnabled,
|
|
81
|
+
// Optional flags: preserve the exact default-when-undefined semantics the
|
|
82
|
+
// migrated call sites used (`!== false` = default-on, `=== true` = default-off).
|
|
83
|
+
graphAssistInFullMode: config.graphAssistInFullModeEnabled !== false,
|
|
84
|
+
graphExpandedIntent: config.graphExpandedIntentEnabled === true,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type DirectAnswerWiringInput,
|
|
8
8
|
} from "./direct-answer-wiring.js";
|
|
9
9
|
import { DEFAULT_TAXONOMY } from "./taxonomy/default-taxonomy.js";
|
|
10
|
-
import type { MemoryFile
|
|
10
|
+
import type { MemoryFile } from "./types.js";
|
|
11
11
|
import type { TrustZoneName } from "./trust-zones.js";
|
|
12
12
|
|
|
13
13
|
type WiringConfig = DirectAnswerWiringInput["config"];
|
|
@@ -94,7 +94,8 @@ test("tryDirectAnswer disabled-path does not call any source accessor", async ()
|
|
|
94
94
|
const result = await tryDirectAnswer({
|
|
95
95
|
query: "does not matter",
|
|
96
96
|
namespace: "default",
|
|
97
|
-
config:
|
|
97
|
+
config: BASE_CONFIG,
|
|
98
|
+
enabled: false,
|
|
98
99
|
sources,
|
|
99
100
|
});
|
|
100
101
|
assert.equal(result.eligible, false);
|
|
@@ -104,6 +105,42 @@ test("tryDirectAnswer disabled-path does not call any source accessor", async ()
|
|
|
104
105
|
assert.deepEqual(sources.calls.importance, []);
|
|
105
106
|
});
|
|
106
107
|
|
|
108
|
+
// ── Backward-compat (#1523): omit top-level `enabled`, fall back to config ──
|
|
109
|
+
|
|
110
|
+
test("tryDirectAnswer falls back to config.recallDirectAnswerEnabled when `enabled` is omitted (disabled)", async () => {
|
|
111
|
+
// Old input shape: config carries recallDirectAnswerEnabled: false and no
|
|
112
|
+
// top-level `enabled`. Must short-circuit as "disabled" (identical to the
|
|
113
|
+
// pre-#1523 behavior) rather than treating undefined as enabled.
|
|
114
|
+
const sources = makeMockSources({ memories: [makeMemory()] });
|
|
115
|
+
const result = await tryDirectAnswer({
|
|
116
|
+
query: "does not matter",
|
|
117
|
+
namespace: "default",
|
|
118
|
+
config: { ...BASE_CONFIG, recallDirectAnswerEnabled: false },
|
|
119
|
+
sources,
|
|
120
|
+
});
|
|
121
|
+
assert.equal(result.eligible, false);
|
|
122
|
+
assert.equal(result.reason, "disabled");
|
|
123
|
+
assert.equal(sources.calls.listCandidates, 0);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("tryDirectAnswer falls back to config.recallDirectAnswerEnabled when `enabled` is omitted (enabled)", async () => {
|
|
127
|
+
// Old input shape with recallDirectAnswerEnabled: true and no `enabled` must
|
|
128
|
+
// NOT short-circuit — it proceeds to materialize candidates.
|
|
129
|
+
const sources = makeMockSources({
|
|
130
|
+
memories: [makeMemory({ tags: ["pnpm"], content: "remnic uses pnpm" })],
|
|
131
|
+
trustZones: { m1: "trusted" },
|
|
132
|
+
importance: { m1: 0.9 },
|
|
133
|
+
});
|
|
134
|
+
const result = await tryDirectAnswer({
|
|
135
|
+
query: "package manager remnic",
|
|
136
|
+
namespace: "default",
|
|
137
|
+
config: BASE_CONFIG, // recallDirectAnswerEnabled: true
|
|
138
|
+
sources,
|
|
139
|
+
});
|
|
140
|
+
assert.notEqual(result.reason, "disabled");
|
|
141
|
+
assert.equal(sources.calls.listCandidates, 1);
|
|
142
|
+
});
|
|
143
|
+
|
|
107
144
|
// ── Empty-query short-circuit: no I/O ───────────────────────────────────────
|
|
108
145
|
|
|
109
146
|
test("tryDirectAnswer skips all I/O when query normalizes to zero searchable tokens", async () => {
|
|
@@ -119,6 +156,7 @@ test("tryDirectAnswer skips all I/O when query normalizes to zero searchable tok
|
|
|
119
156
|
query: "? !!! ",
|
|
120
157
|
namespace: "default",
|
|
121
158
|
config: BASE_CONFIG,
|
|
159
|
+
enabled: true,
|
|
122
160
|
sources,
|
|
123
161
|
});
|
|
124
162
|
assert.equal(result.reason, "empty-query");
|
|
@@ -135,6 +173,7 @@ test("tryDirectAnswer with empty memory list returns no-candidates", async () =>
|
|
|
135
173
|
query: "package manager remnic",
|
|
136
174
|
namespace: "default",
|
|
137
175
|
config: BASE_CONFIG,
|
|
176
|
+
enabled: true,
|
|
138
177
|
sources,
|
|
139
178
|
});
|
|
140
179
|
assert.equal(result.reason, "no-candidates");
|
|
@@ -158,6 +197,7 @@ test("tryDirectAnswer skips importance resolution for non-trusted memories", asy
|
|
|
158
197
|
query: "package manager remnic",
|
|
159
198
|
namespace: "default",
|
|
160
199
|
config: BASE_CONFIG,
|
|
200
|
+
enabled: true,
|
|
161
201
|
sources,
|
|
162
202
|
});
|
|
163
203
|
assert.equal(result.eligible, false);
|
|
@@ -182,6 +222,7 @@ test("tryDirectAnswer skips importance for quarantine-zone memories", async () =
|
|
|
182
222
|
query: "package manager remnic",
|
|
183
223
|
namespace: "default",
|
|
184
224
|
config: BASE_CONFIG,
|
|
225
|
+
enabled: true,
|
|
185
226
|
sources,
|
|
186
227
|
});
|
|
187
228
|
assert.equal(result.eligible, false);
|
|
@@ -203,6 +244,7 @@ test("tryDirectAnswer skips importance when trust zone is missing (null)", async
|
|
|
203
244
|
query: "package manager remnic",
|
|
204
245
|
namespace: "default",
|
|
205
246
|
config: BASE_CONFIG,
|
|
247
|
+
enabled: true,
|
|
206
248
|
sources,
|
|
207
249
|
});
|
|
208
250
|
assert.equal(result.eligible, false);
|
|
@@ -229,6 +271,7 @@ test("tryDirectAnswer skips importance when taxonomy bucket is not eligible", as
|
|
|
229
271
|
query: "package manager remnic",
|
|
230
272
|
namespace: "default",
|
|
231
273
|
config: BASE_CONFIG,
|
|
274
|
+
enabled: true,
|
|
232
275
|
sources,
|
|
233
276
|
});
|
|
234
277
|
assert.equal(result.eligible, false);
|
|
@@ -254,6 +297,7 @@ test("tryDirectAnswer returns eligible for a single trusted user-confirmed decis
|
|
|
254
297
|
query: "package manager remnic",
|
|
255
298
|
namespace: "default",
|
|
256
299
|
config: BASE_CONFIG,
|
|
300
|
+
enabled: true,
|
|
257
301
|
sources,
|
|
258
302
|
});
|
|
259
303
|
assert.equal(result.eligible, true);
|
|
@@ -284,6 +328,7 @@ test("tryDirectAnswer defers to hybrid when two trusted candidates are within am
|
|
|
284
328
|
query: "package manager remnic",
|
|
285
329
|
namespace: "default",
|
|
286
330
|
config: BASE_CONFIG,
|
|
331
|
+
enabled: true,
|
|
287
332
|
sources,
|
|
288
333
|
});
|
|
289
334
|
assert.equal(result.eligible, false);
|
|
@@ -327,6 +372,7 @@ test("tryDirectAnswer throws AbortError when signal aborts mid-loop", async () =
|
|
|
327
372
|
query: "package manager remnic",
|
|
328
373
|
namespace: "default",
|
|
329
374
|
config: BASE_CONFIG,
|
|
375
|
+
enabled: true,
|
|
330
376
|
sources,
|
|
331
377
|
abortSignal: controller.signal,
|
|
332
378
|
}),
|
|
@@ -363,6 +409,7 @@ test("tryDirectAnswer throws when abort lands during trustZoneFor on the only me
|
|
|
363
409
|
query: "package manager remnic",
|
|
364
410
|
namespace: "default",
|
|
365
411
|
config: BASE_CONFIG,
|
|
412
|
+
enabled: true,
|
|
366
413
|
sources,
|
|
367
414
|
abortSignal: controller.signal,
|
|
368
415
|
}),
|
|
@@ -391,6 +438,7 @@ test("tryDirectAnswer throws when abort lands during trustZoneFor on the last of
|
|
|
391
438
|
query: "package manager remnic",
|
|
392
439
|
namespace: "default",
|
|
393
440
|
config: BASE_CONFIG,
|
|
441
|
+
enabled: true,
|
|
394
442
|
sources,
|
|
395
443
|
abortSignal: controller.signal,
|
|
396
444
|
}),
|
|
@@ -408,6 +456,7 @@ test("tryDirectAnswer throws when signal is already aborted before I/O", async (
|
|
|
408
456
|
query: "anything",
|
|
409
457
|
namespace: "default",
|
|
410
458
|
config: BASE_CONFIG,
|
|
459
|
+
enabled: true,
|
|
411
460
|
sources,
|
|
412
461
|
abortSignal: controller.signal,
|
|
413
462
|
}),
|
|
@@ -434,6 +483,7 @@ test("tryDirectAnswer passes the requested namespace to listCandidateMemories",
|
|
|
434
483
|
query: "anything",
|
|
435
484
|
namespace: "project-x",
|
|
436
485
|
config: BASE_CONFIG,
|
|
486
|
+
enabled: true,
|
|
437
487
|
sources,
|
|
438
488
|
});
|
|
439
489
|
assert.equal(observedNamespace, "project-x");
|
|
@@ -465,6 +515,7 @@ test("tryDirectAnswer forwards queryEntityRefs to the eligibility gate", async (
|
|
|
465
515
|
query: "package manager remnic",
|
|
466
516
|
namespace: "default",
|
|
467
517
|
config: BASE_CONFIG,
|
|
518
|
+
enabled: true,
|
|
468
519
|
sources,
|
|
469
520
|
queryEntityRefs: ["remnic"],
|
|
470
521
|
});
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Short-circuit contract:
|
|
16
16
|
*
|
|
17
|
-
* - When `
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* - When the resolved gate (`input.enabled` if supplied, else
|
|
18
|
+
* `config.recallDirectAnswerEnabled`) is `false`, the function returns the
|
|
19
|
+
* eligibility verdict with reason `"disabled"` without touching any source
|
|
20
|
+
* accessor. This is the documented default.
|
|
20
21
|
* - When enabled, the wiring cheaply drops non-trusted-zone memories
|
|
21
22
|
* and ineligible taxonomy buckets before computing importance, so
|
|
22
23
|
* the eligibility module sees a pre-filtered candidate set. The
|
|
@@ -79,6 +80,15 @@ export interface DirectAnswerWiringInput {
|
|
|
79
80
|
| "recallDirectAnswerAmbiguityMargin"
|
|
80
81
|
| "recallDirectAnswerEligibleTaxonomyBuckets"
|
|
81
82
|
>;
|
|
83
|
+
/**
|
|
84
|
+
* Direct-answer capability gate, resolved once at the recall-operation entry
|
|
85
|
+
* (issue #1523: `caps.recallDirectAnswer`). OPTIONAL and additive: when the
|
|
86
|
+
* caller supplies it, this module and the orchestrator agree on a single
|
|
87
|
+
* resolved gate value for the whole operation. When omitted, we fall back to
|
|
88
|
+
* `config.recallDirectAnswerEnabled` so existing callers on the old input
|
|
89
|
+
* shape keep identical behavior.
|
|
90
|
+
*/
|
|
91
|
+
enabled?: boolean;
|
|
82
92
|
sources: DirectAnswerSources;
|
|
83
93
|
queryEntityRefs?: string[];
|
|
84
94
|
abortSignal?: AbortSignal;
|
|
@@ -92,10 +102,13 @@ export interface DirectAnswerWiringInput {
|
|
|
92
102
|
export async function tryDirectAnswer(
|
|
93
103
|
input: DirectAnswerWiringInput,
|
|
94
104
|
): Promise<DirectAnswerResult> {
|
|
95
|
-
const { query, namespace, config, sources, queryEntityRefs, abortSignal } = input;
|
|
105
|
+
const { query, namespace, config, enabled, sources, queryEntityRefs, abortSignal } = input;
|
|
96
106
|
|
|
97
107
|
const eligibilityConfig: DirectAnswerConfig = {
|
|
98
|
-
|
|
108
|
+
// Prefer the resolved capability when supplied; fall back to the config
|
|
109
|
+
// flag so callers on the old input shape (config-only, no `enabled`) get
|
|
110
|
+
// identical gating (issue #1523 backward-compat).
|
|
111
|
+
enabled: enabled ?? config.recallDirectAnswerEnabled,
|
|
99
112
|
tokenOverlapFloor: config.recallDirectAnswerTokenOverlapFloor,
|
|
100
113
|
importanceFloor: config.recallDirectAnswerImportanceFloor,
|
|
101
114
|
ambiguityMargin: config.recallDirectAnswerAmbiguityMargin,
|