@sema-agent/core 7.24.4 → 7.25.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/CHANGELOG.md +78 -0
- package/dist/agents/peer-session-drain.d.ts +16 -1
- package/dist/agents/peer-session-drain.js +16 -1
- package/dist/agents/send-message-tool.d.ts +10 -2
- package/dist/agents/send-message-tool.js +18 -7
- package/dist/agents/subagent.js +13 -10
- package/dist/core/background-agent-store.d.ts +3 -3
- package/dist/core/checkpoint-store.d.ts +1 -0
- package/dist/core/delegation-frames.d.ts +5 -0
- package/dist/core/gate-fold.js +85 -0
- package/dist/core/gate-lanes.js +23 -1
- package/dist/core/gate-outcome.d.ts +8 -2
- package/dist/core/gate-outcome.js +3 -1
- package/dist/core/hooks.d.ts +29 -0
- package/dist/core/hooks.js +2 -0
- package/dist/core/mailbox-store.d.ts +5 -0
- package/dist/core/mailbox-store.js +1 -1
- package/dist/core/parent-spec-seat.d.ts +7 -1
- package/dist/core/parent-spec-seat.js +2 -0
- package/dist/core/permission-rule-consent.js +29 -47
- package/dist/core/permission-rule-provider.js +4 -3
- package/dist/core/permission-rule-store.d.ts +143 -7
- package/dist/core/permission-rule-store.js +165 -15
- package/dist/core/runner/contracts.d.ts +36 -2
- package/dist/core/runner/gate-exit.d.ts +14 -0
- package/dist/core/runner/gate-exit.js +4 -0
- package/dist/core/runner/prepare-delegation-surface.d.ts +4 -4
- package/dist/core/runner/prepare-delegation-surface.js +2 -1
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -0
- package/dist/core/runner/prepare-gate-stations.js +7 -4
- package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
- package/dist/core/runner/prepare-hands-readface.js +25 -10
- package/dist/core/runner/prepare-inherited-gate.d.ts +23 -0
- package/dist/core/runner/prepare-inherited-gate.js +11 -6
- package/dist/core/runner/prepare-policy-chain.d.ts +15 -1
- package/dist/core/runner/prepare-policy-chain.js +14 -4
- package/dist/core/runner/prepare-suspend-saga.d.ts +5 -2
- package/dist/core/runner/prepare-suspend-saga.js +4 -3
- package/dist/core/runner/prepare-task.js +10 -9
- package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
- package/dist/core/runner/prepare-wiring-manifest.js +8 -4
- package/dist/core/runner/resume-apply.js +2 -0
- package/dist/core/runner/run-harness-handlers.js +1 -0
- package/dist/core/runner/run-identity-wiring.d.ts +2 -2
- package/dist/core/runner/run-leg.js +26 -0
- package/dist/core/runner/run-notification-lane.d.ts +2 -2
- package/dist/core/runner/run-notification-lane.js +2 -1
- package/dist/core/runner/run-settle-and-teardown.js +1 -0
- package/dist/core/runner/session-rule-policy.d.ts +3 -4
- package/dist/core/runner/session-rule-policy.js +11 -3
- package/dist/core/session-policy-store.d.ts +27 -3
- package/dist/core/session-policy-store.js +12 -4
- package/dist/core/store-contracts/background-agent-store-contract.js +10 -0
- package/dist/core/store-contracts/peer-directory-contract.d.ts +23 -0
- package/dist/core/store-contracts/peer-directory-contract.js +82 -0
- package/dist/core/task-event.d.ts +6 -0
- package/dist/core/task-notification.d.ts +79 -4
- package/dist/core/task-notification.js +90 -1
- package/dist/core/task-registry-agent.js +1 -2
- package/dist/core/task-registry-monitor.js +2 -1
- package/dist/core/task-registry-shared.d.ts +53 -5
- package/dist/core/task-registry-shared.js +13 -2
- package/dist/core/task-registry.js +8 -5
- package/dist/core/task-spec.d.ts +12 -5
- package/dist/core/tool-catalog-entries.js +1 -0
- package/dist/core/tool-spec.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +2 -2
- package/dist/engine/loop/agent-loop.js +1 -1
- package/dist/engine/loop/types.d.ts +4 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -3
- package/dist/prompts/coordinator.d.ts +2 -1
- package/dist/stores/file/adoption/adopt.js +23 -1
- package/dist/stores/file/permission-rule-store.js +3 -1
- package/dist/stores/file/session-policy-store.d.ts +31 -0
- package/dist/stores/file/session-policy-store.js +87 -7
- package/dist/tools/fs/bash-program-position.d.ts +4 -26
- package/dist/tools/fs/bash-program-position.js +0 -11
- package/dist/tools/fs/bash-readonly-classifier.d.ts +20 -309
- package/dist/tools/fs/bash-readonly-classifier.js +28 -766
- package/dist/tools/fs/fs-bash.d.ts +43 -207
- package/dist/tools/fs/fs-bash.js +20 -341
- package/dist/tools/fs/index.d.ts +12 -13
- package/dist/tools/fs/index.js +3 -4
- package/dist/tools/fs/read-deny.d.ts +25 -0
- package/dist/tools/fs/read-deny.js +19 -0
- package/dist/tools/fs/safety.js +2 -6
- package/dist/tools/fs/shell-read-check.d.ts +99 -0
- package/dist/tools/fs/shell-read-check.js +229 -0
- package/dist/tools/fs/shell-read-establish.d.ts +70 -115
- package/dist/tools/fs/shell-read-establish.js +216 -102
- package/dist/tools/fs/shell-read-judge.d.ts +231 -0
- package/dist/tools/fs/shell-read-judge.js +170 -0
- package/dist/tools/fs/shell-read-plan.d.ts +3 -0
- package/dist/tools/fs/shell-read-plan.js +82 -19
- package/dist/tools/monitor.d.ts +5 -5
- package/dist/tools/monitor.js +3 -5
- package/dist/tools/task-list.d.ts +4 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +19 -3
- package/wire-changes/core@7.25.0.json +671 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { normalizeAbsPathLexically,
|
|
1
|
+
import { normalizeAbsPathLexically, pathFamilyOf } from "./safety.js";
|
|
2
2
|
import { boundaryIsTheSandbox, canonicalPathIsAuthoritative } from "../../core/remote-env.js";
|
|
3
|
+
import { resolveOperandSpellings } from "./bash-readonly-classifier.js";
|
|
3
4
|
const GLOB_METACHAR_RE = /[*?[]/;
|
|
4
5
|
export const MAX_GLOB_DIRS = 256;
|
|
5
6
|
export const MAX_GLOB_MATCHES = 8192;
|
|
@@ -69,26 +70,75 @@ function globSegmentMatches(re, pattern, name) {
|
|
|
69
70
|
return false;
|
|
70
71
|
return re.test(name);
|
|
71
72
|
}
|
|
72
|
-
function
|
|
73
|
-
return `${dir.replace(/[\\/]+$/, "")}/${name}`;
|
|
74
|
-
}
|
|
75
|
-
function pathSegmentsFrom(p, from) {
|
|
73
|
+
function pathSegmentsFrom(p, from, posix = false) {
|
|
76
74
|
const segs = [];
|
|
75
|
+
const isSep = (c) => c === "/" || (!posix && c === "\\");
|
|
77
76
|
let scan = from;
|
|
78
77
|
while (scan < p.length) {
|
|
79
|
-
while (scan < p.length && (p[scan]
|
|
78
|
+
while (scan < p.length && isSep(p[scan]))
|
|
80
79
|
scan++;
|
|
81
80
|
const s = scan;
|
|
82
|
-
while (scan < p.length && p[scan]
|
|
81
|
+
while (scan < p.length && !isSep(p[scan]))
|
|
83
82
|
scan++;
|
|
84
83
|
if (scan > s)
|
|
85
84
|
segs.push({ text: p.slice(s, scan), start: s });
|
|
86
85
|
}
|
|
87
86
|
return segs;
|
|
88
87
|
}
|
|
89
|
-
|
|
88
|
+
function basesFor(candidate, boundary) {
|
|
89
|
+
if (candidate.bases === "unmodelled")
|
|
90
|
+
return "unmodelled";
|
|
91
|
+
if (candidate.bases.length > 0)
|
|
92
|
+
return candidate.bases;
|
|
93
|
+
return [boundary.cwd ?? boundary.roots[0] ?? ""];
|
|
94
|
+
}
|
|
95
|
+
function lexicalReadsOf(candidate, boundary) {
|
|
96
|
+
if (candidate.expands === "unresolvable") {
|
|
97
|
+
return [{ candidate, established: "unestablishable", spellings: [candidate.spelled], cause: `"${candidate.raw}" is rewritten by the shell before the program sees it` }];
|
|
98
|
+
}
|
|
99
|
+
const bases = basesFor(candidate, boundary);
|
|
100
|
+
if (bases === "unmodelled") {
|
|
101
|
+
return [{ candidate, established: "unestablishable", spellings: [candidate.spelled], cause: `"${candidate.raw}" is relative to a working directory this command's text does not settle` }];
|
|
102
|
+
}
|
|
103
|
+
const out = [];
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
for (const base of bases) {
|
|
106
|
+
const sp = resolveOperandSpellings(base === "" ? undefined : base, candidate.spelled, boundary.homeDir);
|
|
107
|
+
if (sp === undefined) {
|
|
108
|
+
const key = `?${candidate.spelled}`;
|
|
109
|
+
if (!seen.has(key)) {
|
|
110
|
+
seen.add(key);
|
|
111
|
+
out.push({ candidate, established: "unestablishable", spellings: [candidate.spelled], cause: `"${candidate.raw}" cannot be placed against ${base === "" ? "no working directory" : `"${base}"`}` });
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (seen.has(sp.folded))
|
|
116
|
+
continue;
|
|
117
|
+
seen.add(sp.folded);
|
|
118
|
+
out.push({ candidate, established: "lexical", target: sp.folded, spellings: sp.spelled !== undefined ? [sp.folded, sp.spelled] : [sp.folded] });
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
function judgedCandidatesOf(segment) {
|
|
123
|
+
return segment.reads.filter((c) => c.provenance !== "write_redirect_target");
|
|
124
|
+
}
|
|
125
|
+
export function establishShellReadsLexically(plan, boundary) {
|
|
126
|
+
const reads = new Map();
|
|
127
|
+
const nested = new Map();
|
|
128
|
+
for (const segment of plan.segments) {
|
|
129
|
+
reads.set(segment, judgedCandidatesOf(segment).flatMap((c) => lexicalReadsOf(c, boundary)));
|
|
130
|
+
for (const n of segment.nested)
|
|
131
|
+
nested.set(n, establishShellReadsLexically(n, boundary));
|
|
132
|
+
}
|
|
133
|
+
return { plan, reads, nested, roots: boundary.roots };
|
|
134
|
+
}
|
|
135
|
+
export function createShellReadEstablisher(env, readRoots) {
|
|
90
136
|
const lexicalOnly = boundaryIsTheSandbox(env);
|
|
91
137
|
const resolverIsAuthoritative = canonicalPathIsAuthoritative(env);
|
|
138
|
+
const family = pathFamilyOf({ root: readRoots[0] ?? "/" });
|
|
139
|
+
const SEP = family === "posix" ? /\// : /[\\/]/;
|
|
140
|
+
const TRAILING_SEP = family === "posix" ? /\/+$/ : /[\\/]+$/;
|
|
141
|
+
const joinUnder = (dir, name) => `${dir.replace(TRAILING_SEP, "")}/${name}`;
|
|
92
142
|
let rootsOnce;
|
|
93
143
|
const canonicalRoots = async (signal) => {
|
|
94
144
|
rootsOnce ??= (async () => {
|
|
@@ -102,7 +152,7 @@ export function createCanonicalReadBoundary(env, readRoots) {
|
|
|
102
152
|
return await rootsOnce;
|
|
103
153
|
};
|
|
104
154
|
const resolveWithoutParentSteps = async (p, signal) => {
|
|
105
|
-
const parts = p.split(
|
|
155
|
+
const parts = p.split(SEP);
|
|
106
156
|
const tail = [];
|
|
107
157
|
let allAbsent = true;
|
|
108
158
|
while (parts.length > 0) {
|
|
@@ -110,7 +160,7 @@ export function createCanonicalReadBoundary(env, readRoots) {
|
|
|
110
160
|
if (canon.ok) {
|
|
111
161
|
if (tail.length === 0)
|
|
112
162
|
return { value: canon.value, exact: true };
|
|
113
|
-
return { value: [canon.value.replace(
|
|
163
|
+
return { value: [canon.value.replace(TRAILING_SEP, ""), ...[...tail].reverse()].join("/"), exact: false, missing: allAbsent };
|
|
114
164
|
}
|
|
115
165
|
if (canon.error.code !== "not_found" && canon.error.code !== "not_directory")
|
|
116
166
|
allAbsent = false;
|
|
@@ -120,7 +170,7 @@ export function createCanonicalReadBoundary(env, readRoots) {
|
|
|
120
170
|
};
|
|
121
171
|
const parentOf = (canonical) => normalizeAbsPathLexically(`${canonical}/..`);
|
|
122
172
|
const resolve = async (p, signal) => {
|
|
123
|
-
const parts = p.split(
|
|
173
|
+
const parts = p.split(SEP);
|
|
124
174
|
const first = parts.findIndex((seg) => seg === "..");
|
|
125
175
|
if (first < 0)
|
|
126
176
|
return await resolveWithoutParentSteps(p, signal);
|
|
@@ -132,7 +182,7 @@ export function createCanonicalReadBoundary(env, readRoots) {
|
|
|
132
182
|
const flush = async () => {
|
|
133
183
|
if (pending.length === 0)
|
|
134
184
|
return true;
|
|
135
|
-
const next = await resolveWithoutParentSteps([head.value.replace(
|
|
185
|
+
const next = await resolveWithoutParentSteps([head.value.replace(TRAILING_SEP, ""), ...pending].join("/"), signal);
|
|
136
186
|
pending = [];
|
|
137
187
|
if (next === undefined)
|
|
138
188
|
return false;
|
|
@@ -170,40 +220,37 @@ export function createCanonicalReadBoundary(env, readRoots) {
|
|
|
170
220
|
};
|
|
171
221
|
const expandGlobCandidate = async (p, roots, signal) => {
|
|
172
222
|
const head = literalRootHead(p, roots);
|
|
173
|
-
let segs = pathSegmentsFrom(p, head?.length ?? 0);
|
|
223
|
+
let segs = pathSegmentsFrom(p, head?.length ?? 0, family === "posix");
|
|
174
224
|
let first = segs.findIndex((s) => GLOB_METACHAR_RE.test(s.text));
|
|
175
225
|
if (first < 0 && head !== undefined && GLOB_METACHAR_RE.test(head)) {
|
|
176
|
-
segs = pathSegmentsFrom(p, 0);
|
|
226
|
+
segs = pathSegmentsFrom(p, 0, family === "posix");
|
|
177
227
|
first = segs.findIndex((s) => GLOB_METACHAR_RE.test(s.text));
|
|
178
228
|
}
|
|
179
229
|
if (first < 0)
|
|
180
230
|
return undefined;
|
|
181
|
-
let frontier = [p.slice(0, segs[first].start).replace(
|
|
231
|
+
let frontier = [p.slice(0, segs[first].start).replace(TRAILING_SEP, "") || "/"];
|
|
182
232
|
let dirsUsed = 0;
|
|
183
233
|
let matches = 0;
|
|
184
234
|
const canonicalOfMatch = new Map();
|
|
185
235
|
for (let j = first; j < segs.length; j++) {
|
|
186
236
|
const seg = segs[j].text;
|
|
187
237
|
if (!GLOB_METACHAR_RE.test(seg)) {
|
|
188
|
-
frontier = frontier.map((d) =>
|
|
238
|
+
frontier = frontier.map((d) => joinUnder(d, seg));
|
|
189
239
|
continue;
|
|
190
240
|
}
|
|
191
241
|
const re = compileGlobSegment(seg);
|
|
192
|
-
if (re === undefined)
|
|
242
|
+
if (re === undefined)
|
|
193
243
|
return { unverifiable: `the pattern "${seg}" cannot be expanded to verify what it would read` };
|
|
194
|
-
}
|
|
195
244
|
const isLast = j === segs.length - 1;
|
|
196
245
|
const next = [];
|
|
197
246
|
for (const dir of frontier) {
|
|
198
|
-
if (++dirsUsed > MAX_GLOB_DIRS)
|
|
247
|
+
if (++dirsUsed > MAX_GLOB_DIRS)
|
|
199
248
|
return { unverifiable: `"${p}" expands over more than ${MAX_GLOB_DIRS} directories, too many to enumerate to verify` };
|
|
200
|
-
}
|
|
201
249
|
const canonDir = await resolve(dir, signal);
|
|
202
250
|
if (canonDir !== undefined && canonDir.missing === true)
|
|
203
251
|
continue;
|
|
204
|
-
if (canonDir !== undefined && !canonDir.exact)
|
|
252
|
+
if (canonDir !== undefined && !canonDir.exact)
|
|
205
253
|
return { unverifiable: `"${dir}" cannot be resolved to verify what "${seg}" would expand to` };
|
|
206
|
-
}
|
|
207
254
|
const listed = await env.listDir(canonDir?.value ?? dir, signal);
|
|
208
255
|
if (!listed.ok) {
|
|
209
256
|
if (listed.error.code === "not_found" || listed.error.code === "not_directory")
|
|
@@ -213,18 +260,17 @@ export function createCanonicalReadBoundary(env, readRoots) {
|
|
|
213
260
|
if (seg.startsWith(".")) {
|
|
214
261
|
for (const dot of [".", ".."])
|
|
215
262
|
if (re.test(dot))
|
|
216
|
-
next.push(
|
|
263
|
+
next.push(joinUnder(dir, dot));
|
|
217
264
|
}
|
|
218
265
|
const canonDirHere = isLast ? canonDir?.value : undefined;
|
|
219
266
|
for (const entry of listed.value) {
|
|
220
267
|
if (!globSegmentMatches(re, seg, entry.name))
|
|
221
268
|
continue;
|
|
222
|
-
if (++matches > MAX_GLOB_MATCHES)
|
|
269
|
+
if (++matches > MAX_GLOB_MATCHES)
|
|
223
270
|
return { unverifiable: `"${p}" expands to more than ${MAX_GLOB_MATCHES} names, too many to enumerate to verify` };
|
|
224
|
-
|
|
225
|
-
const candidate = joinCandidate(dir, entry.name);
|
|
271
|
+
const candidate = joinUnder(dir, entry.name);
|
|
226
272
|
if (canonDirHere !== undefined && entry.kind !== "symlink")
|
|
227
|
-
canonicalOfMatch.set(candidate,
|
|
273
|
+
canonicalOfMatch.set(candidate, joinUnder(canonDirHere, entry.name));
|
|
228
274
|
next.push(candidate);
|
|
229
275
|
}
|
|
230
276
|
}
|
|
@@ -234,93 +280,161 @@ export function createCanonicalReadBoundary(env, readRoots) {
|
|
|
234
280
|
}
|
|
235
281
|
return { paths: frontier, canonical: canonicalOfMatch };
|
|
236
282
|
};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (
|
|
254
|
-
return
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return;
|
|
283
|
+
const enumerateTraversal = async (rootCanonical, signal) => {
|
|
284
|
+
const members = [];
|
|
285
|
+
const stack = [{ dir: rootCanonical, spell: rootCanonical }];
|
|
286
|
+
const visited = new Set([rootCanonical]);
|
|
287
|
+
let dirsUsed = 0;
|
|
288
|
+
while (stack.length > 0) {
|
|
289
|
+
const { dir, spell } = stack.pop();
|
|
290
|
+
if (++dirsUsed > MAX_GLOB_DIRS)
|
|
291
|
+
return { unverifiable: `"${rootCanonical}" holds more than ${MAX_GLOB_DIRS} directories, too many to enumerate to verify what a recursive read would open` };
|
|
292
|
+
const listed = await env.listDir(dir, signal);
|
|
293
|
+
if (!listed.ok) {
|
|
294
|
+
if (listed.error.code === "not_directory" || listed.error.code === "not_found")
|
|
295
|
+
continue;
|
|
296
|
+
return { unverifiable: `"${dir}" cannot be enumerated to verify what a recursive read would open (${listed.error.code})` };
|
|
297
|
+
}
|
|
298
|
+
for (const entry of listed.value) {
|
|
299
|
+
if (members.length >= MAX_GLOB_MATCHES)
|
|
300
|
+
return { unverifiable: `"${rootCanonical}" holds more than ${MAX_GLOB_MATCHES} entries, too many to enumerate to verify what a recursive read would open` };
|
|
301
|
+
const path = joinUnder(spell, entry.name);
|
|
302
|
+
const canonicalPath = joinUnder(dir, entry.name);
|
|
303
|
+
if (entry.kind === "symlink") {
|
|
304
|
+
const canon = await env.canonicalPath(canonicalPath, signal);
|
|
305
|
+
members.push({ path, ...(canon.ok ? { canonical: canon.value } : {}), symlink: true });
|
|
306
|
+
if (canon.ok && !visited.has(canon.value)) {
|
|
307
|
+
visited.add(canon.value);
|
|
308
|
+
stack.push({ dir: canon.value, spell: path });
|
|
309
|
+
}
|
|
310
|
+
continue;
|
|
266
311
|
}
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
312
|
+
if (entry.kind === "directory") {
|
|
313
|
+
if (!visited.has(canonicalPath)) {
|
|
314
|
+
visited.add(canonicalPath);
|
|
315
|
+
stack.push({ dir: canonicalPath, spell: path });
|
|
316
|
+
}
|
|
317
|
+
continue;
|
|
270
318
|
}
|
|
271
|
-
|
|
319
|
+
members.push({ path, canonical: canonicalPath, symlink: false });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return { members };
|
|
323
|
+
};
|
|
324
|
+
const canonicalReadOf = async (candidate, spelling, spellings, established, origin, canonicalKnown, signal) => {
|
|
325
|
+
const real = canonicalKnown !== undefined ? { value: canonicalKnown, exact: true } : await resolve(spelling, signal);
|
|
326
|
+
if (real === undefined) {
|
|
327
|
+
if (resolverIsAuthoritative)
|
|
328
|
+
return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads` };
|
|
329
|
+
return { candidate, established: "lexical", target: normalizeAbsPathLexically(spelling), spellings, ...(origin !== undefined ? { origin } : {}) };
|
|
330
|
+
}
|
|
331
|
+
if (!real.exact && real.missing !== true) {
|
|
332
|
+
return { candidate, established: "unestablishable", spellings, ...(origin !== undefined ? { origin } : {}), cause: `"${spelling}" cannot be resolved to verify where it reads` };
|
|
333
|
+
}
|
|
334
|
+
return { candidate, established, target: real.value, spellings, ...(origin !== undefined ? { origin } : {}) };
|
|
335
|
+
};
|
|
336
|
+
const establishSegment = async (segment, boundary, roots, signal) => {
|
|
337
|
+
const out = [];
|
|
338
|
+
for (const candidate of judgedCandidatesOf(segment)) {
|
|
339
|
+
const traversal = candidate.provenance === "traversal_root";
|
|
340
|
+
const land = async (read, spellings, folded) => {
|
|
341
|
+
out.push(read);
|
|
342
|
+
if (!traversal || (read.established !== "canonical" && read.established !== "expansion") || read.target === undefined)
|
|
272
343
|
return;
|
|
273
|
-
|
|
344
|
+
const walkedTree = await enumerateTraversal(read.target, signal);
|
|
345
|
+
if ("unverifiable" in walkedTree) {
|
|
346
|
+
out.push({ candidate, established: "unestablishable", spellings, origin: folded, cause: walkedTree.unverifiable });
|
|
274
347
|
return;
|
|
275
|
-
|
|
276
|
-
|
|
348
|
+
}
|
|
349
|
+
for (const m of walkedTree.members)
|
|
350
|
+
out.push(await canonicalReadOf(candidate, m.path, [m.path], "expansion", folded, m.canonical, signal));
|
|
277
351
|
};
|
|
278
|
-
for (const
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
await judge(p);
|
|
282
|
-
}
|
|
283
|
-
for (const p of paths.patterns) {
|
|
284
|
-
if (denied !== undefined)
|
|
285
|
-
break;
|
|
286
|
-
const expanded = await expandGlobCandidate(p, roots, signal);
|
|
287
|
-
if (expanded !== undefined && "unverifiable" in expanded) {
|
|
288
|
-
unresolvable ??= expanded.unverifiable;
|
|
352
|
+
for (const lexical of lexicalReadsOf(candidate, boundary)) {
|
|
353
|
+
if (lexical.established !== "lexical") {
|
|
354
|
+
out.push(lexical);
|
|
289
355
|
continue;
|
|
290
356
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
357
|
+
const folded = lexical.target;
|
|
358
|
+
const spellings = lexical.spellings;
|
|
359
|
+
const walked = spellings.find((s) => s !== folded) ?? folded;
|
|
360
|
+
if (candidate.expands === "glob") {
|
|
361
|
+
let matched = false;
|
|
362
|
+
for (const p of spellings) {
|
|
363
|
+
const expanded = await expandGlobCandidate(p, roots, signal);
|
|
364
|
+
if (expanded !== undefined && "unverifiable" in expanded) {
|
|
365
|
+
out.push({ candidate, established: "unestablishable", spellings, origin: folded, cause: expanded.unverifiable });
|
|
366
|
+
matched = true;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (expanded !== undefined && expanded.paths.length > 0) {
|
|
370
|
+
matched = true;
|
|
371
|
+
for (const t of expanded.paths)
|
|
372
|
+
await land(await canonicalReadOf(candidate, t, [t, folded], "expansion", folded, expanded.canonical.get(t), signal), spellings, folded);
|
|
373
|
+
}
|
|
296
374
|
}
|
|
375
|
+
if (!matched)
|
|
376
|
+
await land(await canonicalReadOf(candidate, walked, spellings, "canonical", undefined, undefined, signal), spellings, folded);
|
|
377
|
+
continue;
|
|
297
378
|
}
|
|
298
|
-
|
|
299
|
-
await judge(p);
|
|
379
|
+
await land(await canonicalReadOf(candidate, walked, spellings, "canonical", undefined, undefined, signal), spellings, folded);
|
|
300
380
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
381
|
+
}
|
|
382
|
+
return out;
|
|
383
|
+
};
|
|
384
|
+
const establishPlan = async (plan, boundary, roots, signal) => {
|
|
385
|
+
const reads = new Map();
|
|
386
|
+
const nested = new Map();
|
|
387
|
+
for (const segment of plan.segments) {
|
|
388
|
+
reads.set(segment, await establishSegment(segment, boundary, roots, signal));
|
|
389
|
+
for (const n of segment.nested)
|
|
390
|
+
nested.set(n, await establishPlan(n, boundary, roots, signal));
|
|
391
|
+
}
|
|
392
|
+
return { plan, reads, nested, roots };
|
|
393
|
+
};
|
|
394
|
+
return {
|
|
395
|
+
lexicalOnly,
|
|
396
|
+
establish: async (plan, boundary, signal) => {
|
|
306
397
|
if (lexicalOnly)
|
|
307
|
-
return
|
|
308
|
-
const real = await resolve(path, signal);
|
|
309
|
-
return real === undefined || (!real.exact && real.missing !== true) ? undefined : real.value;
|
|
310
|
-
},
|
|
311
|
-
allResolveInside: async (paths, signal) => {
|
|
312
|
-
if (lexicalOnly || paths.length === 0)
|
|
313
|
-
return false;
|
|
398
|
+
return establishShellReadsLexically(plan, boundary);
|
|
314
399
|
const roots = await canonicalRoots(signal);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
400
|
+
return await establishPlan(plan, { ...boundary, roots }, roots, signal);
|
|
401
|
+
},
|
|
402
|
+
reestablish: async (previous, boundary, signal) => {
|
|
403
|
+
if (lexicalOnly)
|
|
404
|
+
return { changed: [] };
|
|
405
|
+
const again = await establishPlan(previous.plan, { ...boundary, roots: previous.roots }, previous.roots, signal);
|
|
406
|
+
const settled = (e) => {
|
|
407
|
+
const out = [];
|
|
408
|
+
const walk = (p) => {
|
|
409
|
+
for (const reads of p.reads.values())
|
|
410
|
+
for (const r of reads)
|
|
411
|
+
if ((r.established === "canonical" || r.established === "expansion") && r.target !== undefined)
|
|
412
|
+
out.push(r.target);
|
|
413
|
+
for (const n of p.nested.values())
|
|
414
|
+
walk(n);
|
|
415
|
+
};
|
|
416
|
+
walk(e);
|
|
417
|
+
return out.sort();
|
|
418
|
+
};
|
|
419
|
+
const before = settled(previous);
|
|
420
|
+
const after = new Set(settled(again));
|
|
421
|
+
const beforeSet = new Set(before);
|
|
422
|
+
const moved = before.filter((t) => !after.has(t));
|
|
423
|
+
const appeared = [...after].filter((t) => !beforeSet.has(t));
|
|
424
|
+
if (moved.length === 0 && appeared.length === 0)
|
|
425
|
+
return { changed: [] };
|
|
426
|
+
const changed = [];
|
|
427
|
+
const collect = (p) => {
|
|
428
|
+
for (const reads of p.reads.values())
|
|
429
|
+
for (const r of reads)
|
|
430
|
+
if (r.target !== undefined && (moved.includes(r.target) || appeared.includes(r.target)))
|
|
431
|
+
changed.push(r);
|
|
432
|
+
for (const n of p.nested.values())
|
|
433
|
+
collect(n);
|
|
434
|
+
};
|
|
435
|
+
collect(previous);
|
|
436
|
+
collect(again);
|
|
437
|
+
return { changed };
|
|
323
438
|
},
|
|
324
439
|
};
|
|
325
440
|
}
|
|
326
|
-
export const NOTHING_ANSWERED = new Set();
|