@remnic/core 9.69.70 → 9.69.72
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 +2 -2
- package/dist/access-authorization-probe.js +3 -3
- package/dist/access-boundary.js +2 -2
- package/dist/access-cli.js +17 -17
- package/dist/access-extraction-force-flush.js +2 -2
- package/dist/access-http-briefing.js +3 -3
- package/dist/access-http-query.js +2 -2
- package/dist/access-http.js +14 -14
- package/dist/access-identity-continuity-surface.js +2 -2
- package/dist/access-lcm-surface.js +2 -2
- package/dist/access-mcp.js +9 -9
- package/dist/access-namespace-preflight.js +2 -2
- package/dist/access-observe-write-surface.js +2 -2
- package/dist/access-operations-batch.js +3 -3
- package/dist/access-operations.js +8 -8
- package/dist/access-recall-concurrency.js +2 -2
- package/dist/access-recall-response.js +2 -2
- package/dist/access-recall-surface.js +2 -2
- package/dist/access-service.js +2 -2
- package/dist/access-surface-catalog.js +4 -4
- package/dist/{chunk-WIQDQQPW.js → chunk-2XGPN5TL.js} +2 -2
- package/dist/{chunk-GXHUFKAG.js → chunk-2ZU6CYTJ.js} +2 -2
- package/dist/{chunk-AJBWVDRV.js → chunk-3X5OPDPF.js} +3 -3
- package/dist/chunk-3X5OPDPF.js.map +1 -0
- package/dist/{chunk-YLZPCYCB.js → chunk-5I63ZRIH.js} +2 -2
- package/dist/{chunk-PT7D26KT.js → chunk-7FL25VH3.js} +3 -3
- package/dist/{chunk-6LQ7GDTI.js → chunk-ADWCQWK3.js} +2 -2
- package/dist/{chunk-2R72MVH7.js → chunk-BIKX37D2.js} +15 -9
- package/dist/{chunk-2R72MVH7.js.map → chunk-BIKX37D2.js.map} +1 -1
- package/dist/{chunk-T5TAT2XL.js → chunk-HW4DKAAA.js} +2 -2
- package/dist/{chunk-66NK4G3A.js → chunk-ICMXLHSC.js} +2 -2
- package/dist/{chunk-KTYBS2UG.js → chunk-KNPWG3VI.js} +4 -4
- package/dist/{chunk-GULV5LFB.js → chunk-ONEUTVGJ.js} +2 -2
- package/dist/{chunk-IKDP2PN5.js → chunk-QVWNNN64.js} +4 -4
- package/dist/{chunk-EGXTKSSA.js → chunk-RFVF5PLN.js} +2 -2
- package/dist/{chunk-4FLIMX4B.js → chunk-VDKZTNG5.js} +2 -2
- package/dist/{chunk-JHAFEGED.js → chunk-W7PY7IIB.js} +8 -8
- package/dist/{chunk-NGN2VAKB.js → chunk-YDMN7TUM.js} +2 -2
- package/dist/{chunk-SQIEPKIU.js → chunk-YEHKKZ5T.js} +2 -2
- package/dist/cli.js +15 -15
- package/dist/deep-recall-seeds.d.ts +3 -1
- package/dist/deep-recall-seeds.d.ts.map +1 -1
- package/dist/deep-recall-seeds.js +1 -1
- package/dist/external-wiki-access.js +3 -3
- package/dist/index.js +17 -17
- package/dist/memory-browse-operations.js +3 -3
- package/dist/orchestrator.js +17 -17
- package/dist/recall-navigation-operations.js +3 -3
- package/dist/recall-navigation.js +2 -2
- package/dist/support-passport/index.js +5 -5
- package/dist/support-passport/private-file.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/deep-recall-seeds.test.ts +35 -0
- package/src/deep-recall-seeds.ts +5 -4
- package/src/deep-recall-surface.test.ts +5 -8
- package/src/support-passport/grant-service.test.ts +45 -20
- package/src/support-passport/private-file.test.ts +10 -5
- package/src/support-passport/private-file.ts +14 -7
- package/dist/chunk-AJBWVDRV.js.map +0 -1
- /package/dist/{chunk-WIQDQQPW.js.map → chunk-2XGPN5TL.js.map} +0 -0
- /package/dist/{chunk-GXHUFKAG.js.map → chunk-2ZU6CYTJ.js.map} +0 -0
- /package/dist/{chunk-YLZPCYCB.js.map → chunk-5I63ZRIH.js.map} +0 -0
- /package/dist/{chunk-PT7D26KT.js.map → chunk-7FL25VH3.js.map} +0 -0
- /package/dist/{chunk-6LQ7GDTI.js.map → chunk-ADWCQWK3.js.map} +0 -0
- /package/dist/{chunk-T5TAT2XL.js.map → chunk-HW4DKAAA.js.map} +0 -0
- /package/dist/{chunk-66NK4G3A.js.map → chunk-ICMXLHSC.js.map} +0 -0
- /package/dist/{chunk-KTYBS2UG.js.map → chunk-KNPWG3VI.js.map} +0 -0
- /package/dist/{chunk-GULV5LFB.js.map → chunk-ONEUTVGJ.js.map} +0 -0
- /package/dist/{chunk-IKDP2PN5.js.map → chunk-QVWNNN64.js.map} +0 -0
- /package/dist/{chunk-EGXTKSSA.js.map → chunk-RFVF5PLN.js.map} +0 -0
- /package/dist/{chunk-4FLIMX4B.js.map → chunk-VDKZTNG5.js.map} +0 -0
- /package/dist/{chunk-JHAFEGED.js.map → chunk-W7PY7IIB.js.map} +0 -0
- /package/dist/{chunk-NGN2VAKB.js.map → chunk-YDMN7TUM.js.map} +0 -0
- /package/dist/{chunk-SQIEPKIU.js.map → chunk-YEHKKZ5T.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "9.69.
|
|
3
|
+
"version": "9.69.72",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -3317,7 +3317,7 @@
|
|
|
3317
3317
|
"core"
|
|
3318
3318
|
],
|
|
3319
3319
|
"peerDependencies": {
|
|
3320
|
-
"@remnic/coding-graph": "^9.69.
|
|
3320
|
+
"@remnic/coding-graph": "^9.69.72"
|
|
3321
3321
|
},
|
|
3322
3322
|
"peerDependenciesMeta": {
|
|
3323
3323
|
"@remnic/coding-graph": {
|
|
@@ -51,3 +51,38 @@ test("seed search forwards the cancellation signal and the capped limit to the r
|
|
|
51
51
|
assert.equal(captured?.execution?.signal, controller.signal, "the transport signal reaches the search router");
|
|
52
52
|
assert.equal(resolverCalls, 2, "each returned hit is resolved per hit");
|
|
53
53
|
});
|
|
54
|
+
|
|
55
|
+
test("resolve-miss drops the hit instead of admitting the bare docid (issue #3087)", async () => {
|
|
56
|
+
const resolvedPaths: string[] = [];
|
|
57
|
+
const router: DeepRecallSeedRouter = {
|
|
58
|
+
async searchAcrossNamespaces() {
|
|
59
|
+
return [
|
|
60
|
+
{ path: "entities/company-x.md", docid: "foreign-doc-42", score: 0.7 },
|
|
61
|
+
{ path: "facts/2026-09-12/fact-good.md", docid: "doc-good", score: 0.6 },
|
|
62
|
+
{ path: "", docid: "doc-nopath", score: 0.5 },
|
|
63
|
+
];
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const searchSeed = createDeepRecallSeedSearch({
|
|
67
|
+
namespace: "ns_3087",
|
|
68
|
+
storage: { dir: "/synthetic/ns_3087", readMemoryByPath: async () => null },
|
|
69
|
+
router,
|
|
70
|
+
resolver: {
|
|
71
|
+
async readQmdResultMemory(resultPath) {
|
|
72
|
+
resolvedPaths.push(resultPath);
|
|
73
|
+
return resultPath === "facts/2026-09-12/fact-good.md"
|
|
74
|
+
? { frontmatter: { id: "fact-good-123" } }
|
|
75
|
+
: null;
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const seeds = await searchSeed("company x", 3);
|
|
81
|
+
|
|
82
|
+
assert.deepEqual(
|
|
83
|
+
seeds,
|
|
84
|
+
[{ memoryId: "fact-good-123", score: 0.6 }],
|
|
85
|
+
"a hit the resolver cannot resolve is dropped, never admitted by its bare docid",
|
|
86
|
+
);
|
|
87
|
+
assert.ok(!resolvedPaths.includes(""), "an empty path is skipped before the resolver");
|
|
88
|
+
});
|
package/src/deep-recall-seeds.ts
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
* and the two forms it cannot decode (`qmd:///`, a leading `/`) are stripped
|
|
16
16
|
* for one retry. A hit with no path is dropped: its namespace membership
|
|
17
17
|
* cannot be verified, and a bare docid from a foreign collection must never
|
|
18
|
-
* enter the working set as a resolved memory.
|
|
18
|
+
* enter the working set as a resolved memory. A hit with a path that still
|
|
19
|
+
* fails to resolve is dropped for the same reason: the resolver miss leaves
|
|
20
|
+
* the docid as the only identity, and a docid is not a memory id.
|
|
19
21
|
*
|
|
20
22
|
* An unavailable namespace backend (or a missing collection) contributes an
|
|
21
23
|
* empty result set and reports itself only through `execution.onDegradation`,
|
|
@@ -127,9 +129,8 @@ export function createDeepRecallSeedSearch(deps: {
|
|
|
127
129
|
const seeds: DeepRecallSeedHit[] = [];
|
|
128
130
|
for (const hit of hits) {
|
|
129
131
|
if (typeof hit.path !== "string" || hit.path.length === 0) continue;
|
|
130
|
-
const memoryId =
|
|
131
|
-
|
|
132
|
-
if (typeof memoryId !== "string" || memoryId.length === 0) continue;
|
|
132
|
+
const memoryId = await resolveSeedMemoryId(deps.resolver, deps.storage, deps.namespace, hit.path);
|
|
133
|
+
if (memoryId === null) continue;
|
|
133
134
|
seeds.push({
|
|
134
135
|
memoryId,
|
|
135
136
|
score: typeof hit.score === "number" && Number.isFinite(hit.score) ? hit.score : 0,
|
|
@@ -870,8 +870,8 @@ test("deep recall seeds resolve collection-qualified paths when namespaces are d
|
|
|
870
870
|
path: rawPath,
|
|
871
871
|
score: 0.5 + index / 10,
|
|
872
872
|
})),
|
|
873
|
-
// A foreign-collection hit
|
|
874
|
-
//
|
|
873
|
+
// A foreign-collection hit is unresolvable: skip it. Admitting the
|
|
874
|
+
// bare docid would violate the foreign-collection guard (#3087).
|
|
875
875
|
{ docid: "hash-doc-foreign", path: "foreign-coll/facts/2026-08-01/fact-1.md", score: 0.9 },
|
|
876
876
|
],
|
|
877
877
|
};
|
|
@@ -885,15 +885,12 @@ test("deep recall seeds resolve collection-qualified paths when namespaces are d
|
|
|
885
885
|
|
|
886
886
|
assert.deepEqual(
|
|
887
887
|
seeds.map((seed) => seed.memoryId),
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
"hash-doc-foreign",
|
|
891
|
-
],
|
|
892
|
-
"every collection-qualified raw path form must resolve to the frontmatter id; only a foreign collection keeps its docid",
|
|
888
|
+
rawPaths.map(() => "mem-seed-1"),
|
|
889
|
+
"every collection-qualified raw path form must resolve to the frontmatter id; a foreign collection is dropped",
|
|
893
890
|
);
|
|
894
891
|
assert.deepEqual(
|
|
895
892
|
seeds.map((seed) => seed.score),
|
|
896
|
-
[0.5, 0.6, 0.7, 0.8
|
|
893
|
+
[0.5, 0.6, 0.7, 0.8],
|
|
897
894
|
"resolution must not disturb the hit scores",
|
|
898
895
|
);
|
|
899
896
|
} finally {
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
readPrivateFileNoFollow,
|
|
30
30
|
removePrivateFilesNoFollow,
|
|
31
31
|
requirePrivateFileDescriptorRoot,
|
|
32
|
+
withPrivateDirectoryNoFollow,
|
|
32
33
|
writePrivateFileAtomicallyNoFollow,
|
|
33
34
|
} from "./private-file.js";
|
|
34
35
|
|
|
@@ -2865,15 +2866,7 @@ test("the grant store rejects a symlinked grant directory", async () => {
|
|
|
2865
2866
|
}
|
|
2866
2867
|
});
|
|
2867
2868
|
|
|
2868
|
-
test("private file operations select supported directory access strategies", () => {
|
|
2869
|
-
assert.throws(
|
|
2870
|
-
() =>
|
|
2871
|
-
requirePrivateFileDescriptorRoot(
|
|
2872
|
-
"win32",
|
|
2873
|
-
"private file directory cannot be pinned",
|
|
2874
|
-
),
|
|
2875
|
-
/private file directory cannot be pinned/,
|
|
2876
|
-
);
|
|
2869
|
+
test("private file operations select supported directory access strategies", async () => {
|
|
2877
2870
|
assert.equal(
|
|
2878
2871
|
requirePrivateFileDescriptorRoot("linux", "unreachable"),
|
|
2879
2872
|
"/proc/self/fd",
|
|
@@ -2882,6 +2875,28 @@ test("private file operations select supported directory access strategies", ()
|
|
|
2882
2875
|
requirePrivateFileDescriptorRoot("darwin", "unreachable"),
|
|
2883
2876
|
"/dev/fd",
|
|
2884
2877
|
);
|
|
2878
|
+
assert.throws(
|
|
2879
|
+
() => requirePrivateFileDescriptorRoot("sunos", "unreachable"),
|
|
2880
|
+
/unreachable/,
|
|
2881
|
+
);
|
|
2882
|
+
|
|
2883
|
+
// win32 has no POSIX directory-fd root; pinning degrades to the resolved
|
|
2884
|
+
// stable path while the no-follow ancestor walk and containment asserts stay.
|
|
2885
|
+
const root = await mkdtemp(path.join(tmpdir(), "remnic-support-private-win32-"));
|
|
2886
|
+
try {
|
|
2887
|
+
const target = path.join(root, "state", "memory");
|
|
2888
|
+
await mkdir(target, { recursive: true });
|
|
2889
|
+
const pinned = await withPrivateDirectoryNoFollow(
|
|
2890
|
+
root,
|
|
2891
|
+
target,
|
|
2892
|
+
"private file directory cannot be pinned",
|
|
2893
|
+
async (pinnedDirectory) => pinnedDirectory,
|
|
2894
|
+
"win32",
|
|
2895
|
+
);
|
|
2896
|
+
assert.equal(pinned, path.resolve(target));
|
|
2897
|
+
} finally {
|
|
2898
|
+
await rm(root, { recursive: true, force: true });
|
|
2899
|
+
}
|
|
2885
2900
|
});
|
|
2886
2901
|
|
|
2887
2902
|
test("private directory creation syncs every verified parent entry", async () => {
|
|
@@ -3105,25 +3120,35 @@ test("the grant store rejects a symlink alias in the configured memory root", as
|
|
|
3105
3120
|
}
|
|
3106
3121
|
});
|
|
3107
3122
|
|
|
3108
|
-
test("Windows private-file operations
|
|
3123
|
+
test("Windows private-file operations pin without a POSIX descriptor root", async () => {
|
|
3109
3124
|
const root = await mkdtemp(path.join(tmpdir(), "remnic-support-private-win32-"));
|
|
3110
3125
|
try {
|
|
3111
3126
|
const directory = path.join(root, "state", "support-passport", "grants");
|
|
3112
|
-
const filePath = path.join(directory, "grant.json");
|
|
3113
3127
|
const errorMessage = "private Windows file operation failed";
|
|
3128
|
+
await ensurePrivateDirectoryNoFollow(root, directory, errorMessage, undefined, true, "win32");
|
|
3129
|
+
assert.equal((await lstat(directory)).isDirectory(), true);
|
|
3130
|
+
|
|
3131
|
+
const liveParent = path.join(root, "live");
|
|
3132
|
+
const parkedParent = path.join(root, "parked");
|
|
3133
|
+
const outside = path.join(root, "outside");
|
|
3134
|
+
await mkdir(liveParent);
|
|
3135
|
+
await mkdir(path.join(outside, "memory"), { recursive: true });
|
|
3136
|
+
await renameSync(liveParent, parkedParent);
|
|
3137
|
+
await symlink(outside, liveParent, "dir");
|
|
3138
|
+
|
|
3114
3139
|
await assert.rejects(
|
|
3115
|
-
ensurePrivateDirectoryNoFollow(
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3140
|
+
ensurePrivateDirectoryNoFollow(
|
|
3141
|
+
root,
|
|
3142
|
+
path.join(liveParent, "memory"),
|
|
3143
|
+
errorMessage,
|
|
3144
|
+
undefined,
|
|
3145
|
+
true,
|
|
3146
|
+
"win32",
|
|
3122
3147
|
),
|
|
3123
|
-
|
|
3148
|
+
new RegExp(errorMessage),
|
|
3124
3149
|
);
|
|
3125
3150
|
assert.equal(
|
|
3126
|
-
await lstat(
|
|
3151
|
+
await lstat(path.join(outside, "memory", "state")).then(
|
|
3127
3152
|
() => true,
|
|
3128
3153
|
() => false
|
|
3129
3154
|
),
|
|
@@ -30,16 +30,21 @@ test("private append cannot escape its pinned directory", async (context) => {
|
|
|
30
30
|
await assert.rejects(readFile(outsidePath, "utf8"), { code: "ENOENT" });
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
test("private append
|
|
33
|
+
test("private append writes through a path-pinned directory on win32", async (context) => {
|
|
34
34
|
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-append-win32-"));
|
|
35
35
|
context.after(async () => await rm(root, { recursive: true, force: true }));
|
|
36
36
|
const privateDirectory = path.join(root, "private");
|
|
37
|
+
await mkdir(privateDirectory);
|
|
37
38
|
const auditPath = path.join(privateDirectory, "audit.jsonl");
|
|
38
|
-
await
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
await appendPrivateFileNoFollow(
|
|
40
|
+
privateDirectory,
|
|
41
|
+
auditPath,
|
|
42
|
+
"private\n",
|
|
43
|
+
"private path escaped",
|
|
44
|
+
root,
|
|
45
|
+
"win32",
|
|
41
46
|
);
|
|
42
|
-
|
|
47
|
+
assert.equal(await readFile(auditPath, "utf8"), "private\n");
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
test("private append rejects a renamed target after opening it", async (context) => {
|
|
@@ -104,6 +104,7 @@ export async function resolvePrivateDirectoryPath(
|
|
|
104
104
|
errorMessage: string,
|
|
105
105
|
platform: NodeJS.Platform = process.platform
|
|
106
106
|
): Promise<string> {
|
|
107
|
+
if (pinsDirectoriesByPath(platform)) return path.resolve(directory);
|
|
107
108
|
const descriptorRoot = requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
108
109
|
const pinnedPath = path.join(descriptorRoot, String(handle.fd));
|
|
109
110
|
const metadata = await stat(pinnedPath);
|
|
@@ -125,6 +126,10 @@ export function requirePrivateFileDescriptorRoot(platform: NodeJS.Platform, erro
|
|
|
125
126
|
throw new Error(errorMessage);
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
function pinsDirectoriesByPath(platform: NodeJS.Platform): boolean {
|
|
130
|
+
return platform === "win32";
|
|
131
|
+
}
|
|
132
|
+
|
|
128
133
|
async function openStableDirectoryFromRoot(
|
|
129
134
|
trustedRoot: string,
|
|
130
135
|
directory: string,
|
|
@@ -140,7 +145,7 @@ async function openStableDirectoryFromRoot(
|
|
|
140
145
|
const root = path.resolve(trustedRoot);
|
|
141
146
|
const target = path.resolve(directory);
|
|
142
147
|
assertContainedPath(root, target, errorMessage);
|
|
143
|
-
requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
148
|
+
if (!pinsDirectoriesByPath(platform)) requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
144
149
|
const relative = path.relative(root, target);
|
|
145
150
|
const components = relative === "" ? [] : relative.split(path.sep);
|
|
146
151
|
const handles: FileHandle[] = [];
|
|
@@ -149,7 +154,7 @@ async function openStableDirectoryFromRoot(
|
|
|
149
154
|
let current = await openDirectoryNoFollow(root, errorMessage);
|
|
150
155
|
handles.push(current.handle);
|
|
151
156
|
for (const component of components) {
|
|
152
|
-
const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage);
|
|
157
|
+
const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage, platform);
|
|
153
158
|
const child = await openDirectoryNoFollow(path.join(pinnedParent, component), errorMessage);
|
|
154
159
|
handles.push(child.handle);
|
|
155
160
|
assertStableDirectory(current.before, current.opened, await lstat(currentPath), errorMessage);
|
|
@@ -186,7 +191,7 @@ export async function ensurePrivateDirectoryNoFollow(
|
|
|
186
191
|
const target = path.resolve(directory);
|
|
187
192
|
assertContainedPath(root, target, errorMessage);
|
|
188
193
|
const relative = path.relative(root, target);
|
|
189
|
-
requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
194
|
+
if (!pinsDirectoriesByPath(platform)) requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
190
195
|
const components = relative === "" ? [] : relative.split(path.sep);
|
|
191
196
|
const handles: FileHandle[] = [];
|
|
192
197
|
let currentPath = root;
|
|
@@ -194,7 +199,7 @@ export async function ensurePrivateDirectoryNoFollow(
|
|
|
194
199
|
let current = await openDirectoryNoFollow(root, errorMessage);
|
|
195
200
|
handles.push(current.handle);
|
|
196
201
|
for (const component of components) {
|
|
197
|
-
const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage);
|
|
202
|
+
const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage, platform);
|
|
198
203
|
const childPath = path.join(pinnedParent, component);
|
|
199
204
|
let created = false;
|
|
200
205
|
try {
|
|
@@ -393,10 +398,12 @@ export async function appendPrivateFileInPinnedDirectory(
|
|
|
393
398
|
errorMessage: string,
|
|
394
399
|
platform: NodeJS.Platform = process.platform
|
|
395
400
|
): Promise<void> {
|
|
396
|
-
const descriptorRoot = requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
397
401
|
const resolvedDirectory = path.resolve(pinnedDirectory);
|
|
398
|
-
|
|
399
|
-
|
|
402
|
+
if (!pinsDirectoriesByPath(platform)) {
|
|
403
|
+
const descriptorRoot = requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
404
|
+
const descriptor = path.relative(descriptorRoot, resolvedDirectory);
|
|
405
|
+
if (!/^\d+$/.test(descriptor)) throw new Error(errorMessage);
|
|
406
|
+
}
|
|
400
407
|
const before = await stat(resolvedDirectory);
|
|
401
408
|
if (!before.isDirectory()) throw new Error(errorMessage);
|
|
402
409
|
let directoryHandle: FileHandle | undefined;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/deep-recall-seeds.ts"],"sourcesContent":["/**\n * Deep-recall seed retrieval (issue #2332 review).\n *\n * Seeds route through the SAME namespace search router the rest of the read\n * path uses, so a non-default caller searches its own suffixed collection\n * instead of the base (default-namespace) collection — read and write resolve\n * through one resolver, never two (§30).\n *\n * Each hit is resolved to a real memory id through the shared QMD result\n * resolver (`QmdResultResolver.readQmdResultMemory`), per hit and bounded by\n * the requested limit, instead of pre-scanning the whole namespace corpus to\n * build a path index. With namespaces disabled the fanout queries the QMD\n * backend directly, so hits keep the indexer's raw path forms; the resolver\n * decodes `qmd://<collection>/` URIs and `<collection>/`-prefixed relatives,\n * and the two forms it cannot decode (`qmd:///`, a leading `/`) are stripped\n * for one retry. A hit with no path is dropped: its namespace membership\n * cannot be verified, and a bare docid from a foreign collection must never\n * enter the working set as a resolved memory.\n *\n * An unavailable namespace backend (or a missing collection) contributes an\n * empty result set and reports itself only through `execution.onDegradation`,\n * so the observer is REQUIRED here: without it a dead index is indistinguishable\n * from a healthy empty one and deep recall would answer `ok: true` with zero\n * entries instead of the advertised `backend_unavailable` failure.\n */\n\nimport type { DeepRecallSeedHit } from \"./deep-recall.js\";\nimport type { SearchDegradation } from \"./search/port.js\";\n\n/** Namespace-scoped search entrypoint (satisfied by `Orchestrator.searchAcrossNamespaces`). */\nexport interface DeepRecallSeedRouter {\n searchAcrossNamespaces(options: {\n query: string;\n namespaces: string[];\n maxResults?: number;\n execution?: {\n signal?: AbortSignal;\n onDegradation?: (degradation: SearchDegradation) => void;\n };\n }): Promise<ReadonlyArray<{ path?: string; docid?: string; score?: number }>>;\n}\n\n/** Namespace-scoped storage read (satisfied by `StorageManager`). */\nexport interface DeepRecallSeedStorage {\n readonly dir: string;\n readMemoryByPath(filePath: string): Promise<{ frontmatter: { id?: string } } | null>;\n}\n\n/** Full QMD hit -> memory resolver (satisfied by `QmdResultResolver#readQmdResultMemory`). */\nexport interface DeepRecallSeedResultResolver {\n readQmdResultMemory(\n resultPath: string,\n fallbackStorage: DeepRecallSeedStorage,\n recallNamespaces?: readonly string[],\n preferredNamespace?: string,\n ): Promise<{ frontmatter: { id?: string } } | null>;\n}\n\n/**\n * Strip the collection-qualified forms the indexer can emit but\n * `readQmdResultMemory` cannot decode: the empty-hostname `qmd:///` URI and\n * the root-relative `/` path both reach its fallback probe still prefixed and\n * miss. Returns `null` when the path carries no such prefix — including a\n * decodable `qmd://<collection>/` or `<collection>/` form, which the resolver\n * owns, and a foreign collection prefix, which must stay a miss.\n */\nfunction collectionPrefixStrippedPath(resultPath: string): string | null {\n const trimmed = resultPath.trim();\n if (trimmed.startsWith(\"qmd:///\")) {\n const relative = trimmed.slice(\"qmd:///\".length).replace(/^\\/+/, \"\");\n return relative.length > 0 ? relative : null;\n }\n if (trimmed.startsWith(\"/\")) {\n const relative = trimmed.replace(/^\\/+/, \"\");\n return relative.length > 0 ? relative : null;\n }\n return null;\n}\n\nasync function resolveSeedMemoryId(\n resolver: DeepRecallSeedResultResolver,\n storage: DeepRecallSeedStorage,\n namespace: string,\n resultPath: string | undefined,\n): Promise<string | null> {\n if (typeof resultPath !== \"string\" || resultPath.length === 0) return null;\n const attempts = [resultPath];\n const stripped = collectionPrefixStrippedPath(resultPath);\n if (stripped !== null) attempts.push(stripped);\n for (const attempt of attempts) {\n const memory = await resolver.readQmdResultMemory(attempt, storage, [namespace]);\n const id = memory?.frontmatter?.id;\n if (typeof id === \"string\" && id.length > 0) return id;\n }\n return null;\n}\n\nexport function createDeepRecallSeedSearch(deps: {\n namespace: string;\n storage: DeepRecallSeedStorage;\n router: DeepRecallSeedRouter;\n resolver: DeepRecallSeedResultResolver;\n /** Transport cancellation (issue #2915); forwarded to the search router. */\n signal?: AbortSignal;\n}): (query: string, limit: number) => Promise<DeepRecallSeedHit[]> {\n return async (query, limit) => {\n if (limit <= 0) return [];\n const unavailable: SearchDegradation[] = [];\n const hits = await deps.router.searchAcrossNamespaces({\n query,\n namespaces: [deps.namespace],\n maxResults: limit,\n execution: {\n ...(deps.signal ? { signal: deps.signal } : {}),\n onDegradation: (degradation) => {\n if (degradation.code === \"backend_unavailable\") unavailable.push(degradation);\n },\n },\n });\n // The requested namespace has no usable index: a seed-search failure, which\n // is the one condition deep recall reports as `backend_unavailable`.\n if (unavailable.length > 0) {\n throw new Error(\n `deep recall seed search unavailable: ${unavailable.map((d) => d.detail ?? d.code).join(\"; \")}`,\n );\n }\n const seeds: DeepRecallSeedHit[] = [];\n for (const hit of hits) {\n if (typeof hit.path !== \"string\" || hit.path.length === 0) continue;\n const memoryId =\n (await resolveSeedMemoryId(deps.resolver, deps.storage, deps.namespace, hit.path)) ?? hit.docid;\n if (typeof memoryId !== \"string\" || memoryId.length === 0) continue;\n seeds.push({\n memoryId,\n score: typeof hit.score === \"number\" && Number.isFinite(hit.score) ? hit.score : 0,\n });\n }\n return seeds;\n };\n}\n"],"mappings":";AAkEA,SAAS,6BAA6B,YAAmC;AACvE,QAAM,UAAU,WAAW,KAAK;AAChC,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,UAAM,WAAW,QAAQ,MAAM,UAAU,MAAM,EAAE,QAAQ,QAAQ,EAAE;AACnE,WAAO,SAAS,SAAS,IAAI,WAAW;AAAA,EAC1C;AACA,MAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,UAAM,WAAW,QAAQ,QAAQ,QAAQ,EAAE;AAC3C,WAAO,SAAS,SAAS,IAAI,WAAW;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,eAAe,oBACb,UACA,SACA,WACA,YACwB;AACxB,MAAI,OAAO,eAAe,YAAY,WAAW,WAAW,EAAG,QAAO;AACtE,QAAM,WAAW,CAAC,UAAU;AAC5B,QAAM,WAAW,6BAA6B,UAAU;AACxD,MAAI,aAAa,KAAM,UAAS,KAAK,QAAQ;AAC7C,aAAW,WAAW,UAAU;AAC9B,UAAM,SAAS,MAAM,SAAS,oBAAoB,SAAS,SAAS,CAAC,SAAS,CAAC;AAC/E,UAAM,KAAK,QAAQ,aAAa;AAChC,QAAI,OAAO,OAAO,YAAY,GAAG,SAAS,EAAG,QAAO;AAAA,EACtD;AACA,SAAO;AACT;AAEO,SAAS,2BAA2B,MAOwB;AACjE,SAAO,OAAO,OAAO,UAAU;AAC7B,QAAI,SAAS,EAAG,QAAO,CAAC;AACxB,UAAM,cAAmC,CAAC;AAC1C,UAAM,OAAO,MAAM,KAAK,OAAO,uBAAuB;AAAA,MACpD;AAAA,MACA,YAAY,CAAC,KAAK,SAAS;AAAA,MAC3B,YAAY;AAAA,MACZ,WAAW;AAAA,QACT,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,QAC7C,eAAe,CAAC,gBAAgB;AAC9B,cAAI,YAAY,SAAS,sBAAuB,aAAY,KAAK,WAAW;AAAA,QAC9E;AAAA,MACF;AAAA,IACF,CAAC;AAGD,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,IAAI;AAAA,QACR,wCAAwC,YAAY,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,MAC/F;AAAA,IACF;AACA,UAAM,QAA6B,CAAC;AACpC,eAAW,OAAO,MAAM;AACtB,UAAI,OAAO,IAAI,SAAS,YAAY,IAAI,KAAK,WAAW,EAAG;AAC3D,YAAM,WACH,MAAM,oBAAoB,KAAK,UAAU,KAAK,SAAS,KAAK,WAAW,IAAI,IAAI,KAAM,IAAI;AAC5F,UAAI,OAAO,aAAa,YAAY,SAAS,WAAW,EAAG;AAC3D,YAAM,KAAK;AAAA,QACT;AAAA,QACA,OAAO,OAAO,IAAI,UAAU,YAAY,OAAO,SAAS,IAAI,KAAK,IAAI,IAAI,QAAQ;AAAA,MACnF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|