@dsh-electron/dsh-plugin-multi-root-workspace 0.1.14 → 0.1.16
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.en.md +32 -1
- package/CHANGELOG.md +32 -1
- package/README.en.md +25 -8
- package/README.md +25 -8
- package/cordis.patch.yml +5 -1
- package/lib/{llm-message-fYORacvh.js → agent-instructions-BXuKhOzr.js} +1 -84
- package/lib/auto-review.js +2 -2
- package/lib/client.js +969 -100
- package/lib/client.js.map +1 -1
- package/lib/{command-Dcz6nULo.js → command-DaVJ-mK3.js} +76 -11
- package/lib/command.js +1 -1
- package/lib/compat-CTJbpCxu.js +267 -0
- package/lib/compat.js +1 -1
- package/lib/{dialects-Dr4rDR-H.js → dialects-CdlG3RF9.js} +1 -1
- package/lib/{compat-veDqCOoa.js → dsh-version-DCwF7REk.js} +2 -264
- package/lib/index.js +10 -8
- package/lib/{instructions-KsUwUFE0.js → instructions-Ca80SkZq.js} +696 -27
- package/lib/instructions.js +1 -1
- package/lib/llm-message-CepyzEBQ.js +84 -0
- package/lib/lsp.js +4 -4
- package/lib/{metadata-DgqSdVX_.js → metadata-99jgViHQ.js} +1 -1
- package/lib/{method-wrapper-CFY3p3j2.js → method-wrapper-D4oUCsEF.js} +22 -2
- package/lib/{native-win32-CRNXr11U.js → native-win32-C5oMtt7f.js} +1 -1
- package/lib/ops-compatibility.js +58 -0
- package/lib/ops-settings-UN2oOKEp.js +40 -0
- package/lib/ops-tools-k4JYaOrM.js +152 -0
- package/lib/process-fence.js +193 -34
- package/lib/{protocol-CnR1KTXy.js → protocol-CwDnFEyK.js} +8 -4
- package/lib/{registry-DR_sMuvS.js → registry-BibiukWk.js} +236 -80
- package/lib/{registry-lease-win32-DDO-c7ig.js → registry-lease-win32-r4puac9m.js} +1 -1
- package/lib/{registry-operation-C2ZV3Jhw.js → registry-operation-CIlmt6_c.js} +5 -1
- package/lib/registry.js +2 -2
- package/lib/{review-context-NDpKeMCc.js → review-context-OxO-fMWR.js} +24 -7
- package/lib/review-context.js +1 -1
- package/lib/{roots-Ca_W1S9n.js → roots-CmaWxRVy.js} +112 -22
- package/lib/{sandbox-C-vCJCp3.js → sandbox-aobdyQGi.js} +7 -4
- package/lib/sandbox.js +1 -1
- package/lib/scope.js +162 -12
- package/lib/skills-utyuvmwr.js +104 -0
- package/lib/skills.js +5 -106
- package/lib/{tool-context-CpgIFcCB.js → tool-context-CCeY3mrK.js} +1 -1
- package/lib/types/authority/protocol.d.ts +5 -1
- package/lib/types/client/files-tree/FilesTree.d.ts +49 -6
- package/lib/types/client/files-tree/adapter.d.ts +69 -16
- package/lib/types/client/files-tree/face.d.ts +31 -2
- package/lib/types/client/files-tree/locate.d.ts +74 -0
- package/lib/types/client/files-tree/navigator.d.ts +98 -0
- package/lib/types/client/index.d.ts +7 -0
- package/lib/types/client/locales.d.ts +30 -0
- package/lib/types/client/ops-settings.d.ts +6 -0
- package/lib/types/client/skills-settings.d.ts +3 -2
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/command.d.ts +3 -3
- package/lib/types/compat/files-tree.d.ts +8 -1
- package/lib/types/compat/forge-entrypoints.d.ts +44 -0
- package/lib/types/compat/instruction-activity.d.ts +32 -1
- package/lib/types/compat/ops-confirmation.d.ts +17 -0
- package/lib/types/compat/ops-settings.d.ts +11 -0
- package/lib/types/compat/ops-tools.d.ts +25 -0
- package/lib/types/compat/session-existence.d.ts +41 -0
- package/lib/types/compat/skills-client.d.ts +1 -0
- package/lib/types/compat/tool-context.d.ts +4 -0
- package/lib/types/compat.d.ts +2 -2
- package/lib/types/instructions.d.ts +6 -1
- package/lib/types/ops-compatibility.d.ts +34 -0
- package/lib/types/registry-operation.d.ts +7 -0
- package/lib/types/registry.d.ts +67 -8
- package/lib/types/review-context.d.ts +2 -0
- package/lib/types/roots.d.ts +116 -20
- package/lib/types/scope.d.ts +95 -3
- package/lib/workspace-files.js +4 -4
- package/lib/{workspace-routing-YPxaQ4V3.js → workspace-routing-D_qwMUh-.js} +4 -4
- package/package.json +5 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { t as effectiveDirectory } from "./session-directory-BLOstut1.js";
|
|
2
|
-
import { c as
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
5
|
-
import {
|
|
2
|
+
import { c as withRegistryAdmission, l as withRegistryRead } from "./registry-operation-CIlmt6_c.js";
|
|
3
|
+
import { t as instructionsApi } from "./agent-instructions-BXuKhOzr.js";
|
|
4
|
+
import { t as createInstructionMessage } from "./llm-message-CepyzEBQ.js";
|
|
5
|
+
import { t as aroundTool } from "./tool-context-CCeY3mrK.js";
|
|
6
|
+
import { r as opsCompatibilityToken } from "./ops-settings-UN2oOKEp.js";
|
|
7
|
+
import { l as isCanonicallyUnder } from "./roots-CmaWxRVy.js";
|
|
6
8
|
import { createHash } from "node:crypto";
|
|
7
9
|
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
8
10
|
import "@deepseek-ai/cordis";
|
|
@@ -45,11 +47,96 @@ function ptcFileActivity(event) {
|
|
|
45
47
|
failed: data.isError !== false || data.error !== void 0
|
|
46
48
|
};
|
|
47
49
|
}
|
|
50
|
+
const OPS_STABLE_MAX_BYTES = 2097152;
|
|
51
|
+
function stableOpsJson(value) {
|
|
52
|
+
const seen = /* @__PURE__ */ new Set();
|
|
53
|
+
let nodes = 0, bytes = 0;
|
|
54
|
+
const over = () => {
|
|
55
|
+
throw new Error("multi-root workspace: OPS fingerprint exceeded its budget");
|
|
56
|
+
};
|
|
57
|
+
const walk = (entry, depth) => {
|
|
58
|
+
if (depth > 24) over();
|
|
59
|
+
nodes += 1;
|
|
60
|
+
if (nodes > 65536) over();
|
|
61
|
+
if (entry === void 0) over();
|
|
62
|
+
if (entry === null || typeof entry === "string" || typeof entry === "boolean") {
|
|
63
|
+
if (typeof entry === "string") {
|
|
64
|
+
bytes += entry.length;
|
|
65
|
+
if (bytes > OPS_STABLE_MAX_BYTES) over();
|
|
66
|
+
}
|
|
67
|
+
return entry;
|
|
68
|
+
}
|
|
69
|
+
if (typeof entry === "number") {
|
|
70
|
+
if (!Number.isFinite(entry)) over();
|
|
71
|
+
return entry;
|
|
72
|
+
}
|
|
73
|
+
if (typeof entry !== "object" || entry === null) over();
|
|
74
|
+
const obj = entry;
|
|
75
|
+
if (seen.has(obj)) over();
|
|
76
|
+
seen.add(obj);
|
|
77
|
+
if (Array.isArray(obj)) {
|
|
78
|
+
const out = obj.map((item) => walk(item, depth + 1));
|
|
79
|
+
seen.delete(obj);
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
const keys = Object.keys(obj);
|
|
83
|
+
if (keys.length > 4096) over();
|
|
84
|
+
for (const key of keys) {
|
|
85
|
+
bytes += key.length;
|
|
86
|
+
if (bytes > OPS_STABLE_MAX_BYTES) over();
|
|
87
|
+
}
|
|
88
|
+
const out = Object.fromEntries(keys.sort().map((key) => [key, walk(obj[key], depth + 1)]));
|
|
89
|
+
seen.delete(obj);
|
|
90
|
+
return out;
|
|
91
|
+
};
|
|
92
|
+
const json = JSON.stringify(walk(value, 0));
|
|
93
|
+
if (json === void 0 || Buffer.byteLength(json, "utf8") > OPS_STABLE_MAX_BYTES) over();
|
|
94
|
+
return json;
|
|
95
|
+
}
|
|
96
|
+
const OPS_OPSID = (kind, id, name, args) => JSON.stringify([
|
|
97
|
+
kind,
|
|
98
|
+
id,
|
|
99
|
+
name,
|
|
100
|
+
createHash("sha256").update(stableOpsJson(args)).digest("hex")
|
|
101
|
+
]);
|
|
102
|
+
const opsActivityEvidenceKey = OPS_OPSID;
|
|
103
|
+
const OPS_PUBLISH = {
|
|
104
|
+
ops_discover_tools: true,
|
|
105
|
+
ops_load_sdk: true
|
|
106
|
+
};
|
|
48
107
|
/** Rebuild read-only discovery candidates from successful durable activity after resume. */
|
|
49
|
-
function replayedInstructionTargets(events, origin) {
|
|
108
|
+
function replayedInstructionTargets(events, origin, liveOpsEvidence, pendingExec, options) {
|
|
109
|
+
const opsOn = options?.opsCompatibility === true;
|
|
50
110
|
let cwd = origin;
|
|
51
111
|
const pending = /* @__PURE__ */ new Map();
|
|
112
|
+
const eventsPrior = /* @__PURE__ */ new Map();
|
|
113
|
+
const ended = /* @__PURE__ */ new Set();
|
|
52
114
|
const paths = /* @__PURE__ */ new Set();
|
|
115
|
+
const isCurrentExec = (call) => {
|
|
116
|
+
if (pendingExec === void 0 || call.name !== pendingExec.name) return false;
|
|
117
|
+
const kind = pendingExec.parent === void 0 ? "native" : "ptc";
|
|
118
|
+
if (call.kind !== kind || call.id !== pendingExec.callId) return false;
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(call.arguments);
|
|
122
|
+
return stableOpsJson(parsed) === stableOpsJson(pendingExec.arguments);
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const opsEntry = (call) => {
|
|
128
|
+
if (liveOpsEvidence === void 0) throw new Error("review-context: OPS file activity lacks live evidence");
|
|
129
|
+
let parsed;
|
|
130
|
+
try {
|
|
131
|
+
parsed = JSON.parse(call.arguments);
|
|
132
|
+
} catch {
|
|
133
|
+
throw new Error("review-context: incomplete durable OPS arguments");
|
|
134
|
+
}
|
|
135
|
+
const entry = liveOpsEvidence.get(OPS_OPSID(call.kind, call.id, call.name, parsed));
|
|
136
|
+
if (entry === void 0) throw new Error("review-context: OPS file activity lacks live evidence");
|
|
137
|
+
if (entry.complete !== true) throw new Error("review-context: incomplete OPS file activity evidence");
|
|
138
|
+
return entry;
|
|
139
|
+
};
|
|
53
140
|
for (const raw of events) {
|
|
54
141
|
const event = raw;
|
|
55
142
|
if (event.type === "working-directory/change" && typeof event.data.cwd === "string") {
|
|
@@ -65,7 +152,13 @@ function replayedInstructionTargets(events, origin) {
|
|
|
65
152
|
arguments: ptc.arguments
|
|
66
153
|
} : event.data;
|
|
67
154
|
if (typeof call.callId !== "string" || typeof call.name !== "string" || typeof call.arguments !== "string") throw new Error("review-context: incomplete durable file activity");
|
|
68
|
-
|
|
155
|
+
const kind = ptc?.kind === "start" ? "ptc" : "native";
|
|
156
|
+
const id = ptc?.kind === "start" ? ptc.id.slice(4) : call.callId;
|
|
157
|
+
const key = JSON.stringify([kind, id]);
|
|
158
|
+
if (opsOn && call.name.startsWith("ops_") && (pending.has(key) || ended.has(key))) throw new Error("review-context: duplicate durable OPS call");
|
|
159
|
+
pending.set(key, {
|
|
160
|
+
kind,
|
|
161
|
+
id,
|
|
69
162
|
name: call.name,
|
|
70
163
|
arguments: call.arguments,
|
|
71
164
|
directories: new Set(cwd === void 0 ? [] : [cwd])
|
|
@@ -75,12 +168,32 @@ function replayedInstructionTargets(events, origin) {
|
|
|
75
168
|
}
|
|
76
169
|
if (event.type !== "tool/result" && ptc?.kind !== "end") continue;
|
|
77
170
|
const message = event.data.message;
|
|
78
|
-
const
|
|
79
|
-
if (typeof
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
171
|
+
const rawId = ptc?.kind === "end" ? ptc.id : message?.source?.callId;
|
|
172
|
+
if (typeof rawId !== "string") throw new Error("review-context: incomplete durable result");
|
|
173
|
+
const kind = ptc?.kind === "end" ? "ptc" : "native";
|
|
174
|
+
const id = ptc?.kind === "end" ? ptc.id.slice(4) : rawId;
|
|
175
|
+
const key = JSON.stringify([kind, id]);
|
|
176
|
+
const call = pending.get(key);
|
|
177
|
+
if (call === void 0) {
|
|
178
|
+
const prior = eventsPrior.get(key);
|
|
179
|
+
if (opsOn && prior !== void 0 && prior.name.startsWith("ops_") && OPS_PUBLISH[prior.name] !== true) throw new Error("review-context: duplicate durable OPS result");
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
pending.delete(key);
|
|
183
|
+
if (opsOn && call.name.startsWith("ops_") && OPS_PUBLISH[call.name] !== true) {
|
|
184
|
+
eventsPrior.set(key, call);
|
|
185
|
+
ended.add(key);
|
|
186
|
+
if (ended.size > 512) throw new Error("review-context: durable OPS result index exceeded");
|
|
187
|
+
}
|
|
188
|
+
const failed = ptc?.kind === "end" ? ptc.failed : event.data.error !== void 0 || toolResultFailed(message ?? {});
|
|
189
|
+
if (opsOn && call.name.startsWith("ops_") && OPS_PUBLISH[call.name] !== true) {
|
|
190
|
+
const entry = opsEntry(call);
|
|
191
|
+
if (failed && entry.targets.length > 0) throw new Error("review-context: OPS activity contradicts its durable failure");
|
|
192
|
+
for (const target of entry.targets) paths.add(target.path);
|
|
193
|
+
if (paths.size > 512) throw new Error("review-context: durable work index exceeded");
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (failed) continue;
|
|
84
197
|
const path = touchedPathOfToolCall(call.name, call.arguments);
|
|
85
198
|
if (path === void 0) continue;
|
|
86
199
|
if (isAbsolute(path)) paths.add(path);
|
|
@@ -90,6 +203,12 @@ function replayedInstructionTargets(events, origin) {
|
|
|
90
203
|
}
|
|
91
204
|
if (paths.size > 512) throw new Error("review-context: durable work index exceeded");
|
|
92
205
|
}
|
|
206
|
+
for (const call of pending.values()) {
|
|
207
|
+
if (!call.name.startsWith("ops_") || OPS_PUBLISH[call.name] === true) continue;
|
|
208
|
+
if (!opsOn) continue;
|
|
209
|
+
if (isCurrentExec(call)) continue;
|
|
210
|
+
throw new Error("review-context: OPS file activity has no final result");
|
|
211
|
+
}
|
|
93
212
|
return [...paths];
|
|
94
213
|
}
|
|
95
214
|
/** fs tools whose success touches the file at their `file_path` argument. */
|
|
@@ -110,6 +229,344 @@ function touchedPathOfToolCall(name, argumentsJson) {
|
|
|
110
229
|
if (typeof parsed !== "object" || parsed === null || !("file_path" in parsed) || typeof parsed.file_path !== "string") return void 0;
|
|
111
230
|
return parsed.file_path.trim() || void 0;
|
|
112
231
|
}
|
|
232
|
+
const OPS_READ_TARGETS = {
|
|
233
|
+
inspect_local_file: true,
|
|
234
|
+
stat: true,
|
|
235
|
+
read_files: true
|
|
236
|
+
};
|
|
237
|
+
const OPS_WRITE_TARGETS = {
|
|
238
|
+
write_file: true,
|
|
239
|
+
edit_file: true,
|
|
240
|
+
replace: true,
|
|
241
|
+
copy: true
|
|
242
|
+
};
|
|
243
|
+
const OPS_TARGETS = {
|
|
244
|
+
...OPS_READ_TARGETS,
|
|
245
|
+
...OPS_WRITE_TARGETS,
|
|
246
|
+
move: true,
|
|
247
|
+
remove: true
|
|
248
|
+
};
|
|
249
|
+
function record(value) {
|
|
250
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
251
|
+
}
|
|
252
|
+
function canonicalize(path) {
|
|
253
|
+
if (!isAbsolute(path)) return void 0;
|
|
254
|
+
try {
|
|
255
|
+
return canonicalPath(path);
|
|
256
|
+
} catch {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function itemPath(item) {
|
|
261
|
+
if (!record(item) || typeof item.path !== "string" || item.path === "") return void 0;
|
|
262
|
+
return item.path;
|
|
263
|
+
}
|
|
264
|
+
function canonicalizeArg(path, cwd) {
|
|
265
|
+
if (typeof path !== "string" || path.trim() === "") return void 0;
|
|
266
|
+
const absolute = isAbsolute(path) ? path : cwd === void 0 ? void 0 : resolve(cwd, path);
|
|
267
|
+
return absolute === void 0 ? void 0 : canonicalize(absolute);
|
|
268
|
+
}
|
|
269
|
+
function requestCanonicals(name, args, cwd) {
|
|
270
|
+
if (!record(args)) return void 0;
|
|
271
|
+
if (name === "ops_read_files") {
|
|
272
|
+
if (!Array.isArray(args.files) || args.files.length === 0 || args.files.length > 32) return void 0;
|
|
273
|
+
const paths = args.files.map((file) => canonicalizeArg(record(file) ? file.path : void 0, cwd));
|
|
274
|
+
return paths.every((path) => path !== void 0) ? paths : void 0;
|
|
275
|
+
}
|
|
276
|
+
const path = canonicalizeArg(args.path, cwd);
|
|
277
|
+
if (path === void 0) return void 0;
|
|
278
|
+
const to = name === "ops_copy" || name === "ops_move" ? canonicalizeArg(args.to, cwd) : void 0;
|
|
279
|
+
if ((name === "ops_copy" || name === "ops_move") && to === void 0) return void 0;
|
|
280
|
+
return to === void 0 ? [path] : [path, to];
|
|
281
|
+
}
|
|
282
|
+
function observed(observedPaths, path) {
|
|
283
|
+
const canonical = canonicalize(path);
|
|
284
|
+
return canonical !== void 0 && observedPaths.some((candidate) => canonicalize(candidate) === canonical);
|
|
285
|
+
}
|
|
286
|
+
const REVISION = /^[0-9a-f]{64}$/;
|
|
287
|
+
const safeBytes = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
288
|
+
function dedupe(targets) {
|
|
289
|
+
const seen = /* @__PURE__ */ new Set();
|
|
290
|
+
return targets.filter((target) => {
|
|
291
|
+
const key = `${target.kind}:${canonicalize(target.path) ?? target.path}`;
|
|
292
|
+
if (seen.has(key)) return false;
|
|
293
|
+
seen.add(key);
|
|
294
|
+
return true;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const inside = (directory, path) => {
|
|
298
|
+
const rel = relative(directory, path);
|
|
299
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
300
|
+
};
|
|
301
|
+
function normalizeOpsFileActivity(input) {
|
|
302
|
+
const bare = input.name.startsWith("ops_") ? input.name.slice(4) : void 0;
|
|
303
|
+
if (bare === void 0 || OPS_TARGETS[bare] !== true) return {
|
|
304
|
+
targets: [],
|
|
305
|
+
complete: false
|
|
306
|
+
};
|
|
307
|
+
const value = input.finalValue;
|
|
308
|
+
if (input.isError === true) return {
|
|
309
|
+
targets: [],
|
|
310
|
+
complete: record(value) && record(value.error) && value.error.effectState === "none"
|
|
311
|
+
};
|
|
312
|
+
if (!record(value) || typeof value.status !== "string") return {
|
|
313
|
+
targets: [],
|
|
314
|
+
complete: false
|
|
315
|
+
};
|
|
316
|
+
if (value.status === "error") return {
|
|
317
|
+
targets: [],
|
|
318
|
+
complete: (record(value.error) ? value.error.effectState : void 0) === "none"
|
|
319
|
+
};
|
|
320
|
+
if (value.status !== "completed" && value.status !== "partial") return {
|
|
321
|
+
targets: [],
|
|
322
|
+
complete: false
|
|
323
|
+
};
|
|
324
|
+
const receipt = "data" in value ? record(value.data) ? value.data : void 0 : value;
|
|
325
|
+
if (!record(receipt)) return {
|
|
326
|
+
targets: [],
|
|
327
|
+
complete: false
|
|
328
|
+
};
|
|
329
|
+
const outerPartial = value.status === "partial";
|
|
330
|
+
if (record(value.error) || record(receipt.error) || typeof receipt.status === "string" && receipt.status !== "completed" && receipt.status !== "partial") return {
|
|
331
|
+
targets: [],
|
|
332
|
+
complete: false
|
|
333
|
+
};
|
|
334
|
+
const outerCwd = typeof receipt.resolvedFrom === "string" ? receipt.resolvedFrom : void 0;
|
|
335
|
+
if (outerCwd !== void 0 && input.cwd !== void 0 && canonicalize(outerCwd) !== canonicalize(input.cwd)) return {
|
|
336
|
+
targets: [],
|
|
337
|
+
complete: false
|
|
338
|
+
};
|
|
339
|
+
const proven = (path) => {
|
|
340
|
+
if (path === void 0) return void 0;
|
|
341
|
+
const canonical = canonicalize(path);
|
|
342
|
+
return canonical !== void 0 && observed(input.observedPaths, canonical) ? canonical : void 0;
|
|
343
|
+
};
|
|
344
|
+
if (OPS_WRITE_TARGETS[bare] === true || bare === "move" || bare === "remove") {
|
|
345
|
+
const args = record(input.args) ? input.args : {};
|
|
346
|
+
if (receipt.status !== "completed") return {
|
|
347
|
+
targets: [],
|
|
348
|
+
complete: false
|
|
349
|
+
};
|
|
350
|
+
if (record(receipt.error)) return {
|
|
351
|
+
targets: [],
|
|
352
|
+
complete: false
|
|
353
|
+
};
|
|
354
|
+
if (args.dryRun === true || receipt.dryRun === true || receipt.planned === true) return {
|
|
355
|
+
targets: [],
|
|
356
|
+
complete: true
|
|
357
|
+
};
|
|
358
|
+
if (receipt.phase !== "verify") return {
|
|
359
|
+
targets: [],
|
|
360
|
+
complete: false
|
|
361
|
+
};
|
|
362
|
+
if (receipt.changed === 0) return {
|
|
363
|
+
targets: [],
|
|
364
|
+
complete: true
|
|
365
|
+
};
|
|
366
|
+
if (typeof receipt.changed !== "number" || !Number.isSafeInteger(receipt.changed) || receipt.changed <= 0) return {
|
|
367
|
+
targets: [],
|
|
368
|
+
complete: false
|
|
369
|
+
};
|
|
370
|
+
const items = Array.isArray(receipt.items) ? receipt.items : void 0;
|
|
371
|
+
if (items === void 0 || items.length === 0) return {
|
|
372
|
+
targets: [],
|
|
373
|
+
complete: false
|
|
374
|
+
};
|
|
375
|
+
const request = requestCanonicals(input.name, input.args, input.cwd);
|
|
376
|
+
const targets = [];
|
|
377
|
+
let unproven = false;
|
|
378
|
+
let destProven = false;
|
|
379
|
+
let sourceProven = false;
|
|
380
|
+
for (const item of items) {
|
|
381
|
+
const path = itemPath(item);
|
|
382
|
+
const canonical = proven(path);
|
|
383
|
+
if (path === void 0 || canonical === void 0) {
|
|
384
|
+
unproven = true;
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
if (record(item) && record(item.error)) {
|
|
388
|
+
unproven = true;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (record(item) && item.removed === true) {
|
|
392
|
+
if ((bare === "move" || bare === "remove") && request !== void 0 && request[0] === canonical) {
|
|
393
|
+
targets.push({
|
|
394
|
+
path: canonical,
|
|
395
|
+
kind: "remove"
|
|
396
|
+
});
|
|
397
|
+
sourceProven = true;
|
|
398
|
+
} else unproven = true;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (bare === "remove") {
|
|
402
|
+
unproven = true;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
const bytesAfter = record(item) && safeBytes(item.bytesAfter);
|
|
406
|
+
const revision = record(item) && (typeof item.revision === "string" && REVISION.test(item.revision) || typeof item.revisionAfter === "string" && REVISION.test(item.revisionAfter));
|
|
407
|
+
if (!bytesAfter || !revision) {
|
|
408
|
+
unproven = true;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (!(bare === "replace" ? request !== void 0 && (request[0] === canonical || inside(request[0], canonical)) : request !== void 0 && request[bare === "copy" || bare === "move" ? 1 : 0] === canonical)) {
|
|
412
|
+
unproven = true;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
targets.push({
|
|
416
|
+
path: canonical,
|
|
417
|
+
kind: "write"
|
|
418
|
+
});
|
|
419
|
+
destProven = true;
|
|
420
|
+
}
|
|
421
|
+
if (bare === "move" && (!destProven || !sourceProven)) unproven = true;
|
|
422
|
+
const complete = !unproven && !outerPartial && receipt.complete === true && (receipt.failed === void 0 || receipt.failed === 0) && (receipt.pending === void 0 || receipt.pending === 0);
|
|
423
|
+
return {
|
|
424
|
+
targets: dedupe(targets),
|
|
425
|
+
complete
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
if (bare === "stat") {
|
|
429
|
+
const canonical = proven(itemPath(receipt));
|
|
430
|
+
const request = canonicalizeArg(record(input.args) ? input.args.path : void 0, input.cwd);
|
|
431
|
+
const provenStat = canonical !== void 0 && receipt.exists === true && typeof receipt.revision === "string" && REVISION.test(receipt.revision) && safeBytes(receipt.bytes) && request === canonical;
|
|
432
|
+
return {
|
|
433
|
+
targets: provenStat ? [{
|
|
434
|
+
path: canonical,
|
|
435
|
+
kind: "read"
|
|
436
|
+
}] : [],
|
|
437
|
+
complete: provenStat === true && receipt.complete === true
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
const topItems = Array.isArray(receipt.items) ? receipt.items : void 0;
|
|
441
|
+
if (topItems === void 0 && bare === "inspect_local_file") return {
|
|
442
|
+
targets: [],
|
|
443
|
+
complete: false
|
|
444
|
+
};
|
|
445
|
+
const requests = requestCanonicals(input.name, input.args, input.cwd);
|
|
446
|
+
const targets = [];
|
|
447
|
+
let unproven = false;
|
|
448
|
+
if (bare === "read_files") {
|
|
449
|
+
if (topItems === void 0 || requests === void 0) return {
|
|
450
|
+
targets: [],
|
|
451
|
+
complete: false
|
|
452
|
+
};
|
|
453
|
+
const provenIndexes = /* @__PURE__ */ new Set();
|
|
454
|
+
for (const [index, item] of topItems.entries()) {
|
|
455
|
+
if (!record(item) || item.status !== "completed" || record(item.error)) {
|
|
456
|
+
unproven = true;
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
const canonical = proven(itemPath(item));
|
|
460
|
+
const expected = requests[index];
|
|
461
|
+
if (canonical === void 0 || expected === void 0 || canonical !== expected) {
|
|
462
|
+
unproven = true;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
if (typeof item.revision !== "string" || !REVISION.test(item.revision) || !safeBytes(item.bytes)) {
|
|
466
|
+
unproven = true;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
provenIndexes.add(index);
|
|
470
|
+
targets.push({
|
|
471
|
+
path: canonical,
|
|
472
|
+
kind: "read"
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
for (const index of requests.keys()) if (!provenIndexes.has(index)) unproven = true;
|
|
476
|
+
const complete = !unproven && !outerPartial && provenIndexes.size === requests.length && receipt.complete === true && (receipt.failed === void 0 || receipt.failed === 0) && (receipt.pending === void 0 || receipt.pending === 0);
|
|
477
|
+
return {
|
|
478
|
+
targets: dedupe(targets),
|
|
479
|
+
complete
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
if (receipt.status !== "completed" || record(receipt.error)) return {
|
|
483
|
+
targets: [],
|
|
484
|
+
complete: false
|
|
485
|
+
};
|
|
486
|
+
const rows = topItems ?? [];
|
|
487
|
+
const request = requests?.[0];
|
|
488
|
+
let path = proven(itemPath(receipt));
|
|
489
|
+
let unprovenRow = false;
|
|
490
|
+
for (const row of rows) {
|
|
491
|
+
if (!record(row) || record(row.error)) {
|
|
492
|
+
unprovenRow = true;
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
const rowPath = proven(itemPath(row));
|
|
496
|
+
if (rowPath === void 0) {
|
|
497
|
+
unprovenRow = true;
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const textRow = Number.isSafeInteger(row.line) && row.line > 0 && typeof row.text === "string";
|
|
501
|
+
const hexRow = row.view === "hex" && Number.isSafeInteger(row.offset) && row.offset >= 0 && typeof row.text === "string";
|
|
502
|
+
if (!textRow && !hexRow) {
|
|
503
|
+
unprovenRow = true;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (path === void 0) path = rowPath;
|
|
507
|
+
else if (path !== rowPath) unprovenRow = true;
|
|
508
|
+
}
|
|
509
|
+
const provenInspect = path !== void 0 && request === path && !unprovenRow && typeof receipt.revision === "string" && REVISION.test(receipt.revision) && safeBytes(receipt.bytes);
|
|
510
|
+
return {
|
|
511
|
+
targets: provenInspect === true ? [{
|
|
512
|
+
path,
|
|
513
|
+
kind: "read"
|
|
514
|
+
}] : [],
|
|
515
|
+
complete: provenInspect === true && receipt.complete === true && !outerPartial
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
const PENDING_EMPTY = {
|
|
519
|
+
discover_tools: true,
|
|
520
|
+
load_sdk: true
|
|
521
|
+
};
|
|
522
|
+
const PENDING_PATH = {
|
|
523
|
+
inspect_local_file: true,
|
|
524
|
+
stat: true,
|
|
525
|
+
read_table: true,
|
|
526
|
+
read_docx: true,
|
|
527
|
+
read_pdf: true,
|
|
528
|
+
write_file: true,
|
|
529
|
+
edit_file: true,
|
|
530
|
+
remove: true
|
|
531
|
+
};
|
|
532
|
+
const PENDING_PAIR = {
|
|
533
|
+
copy: true,
|
|
534
|
+
move: true
|
|
535
|
+
};
|
|
536
|
+
function pendingInstructionTargets(name, args) {
|
|
537
|
+
if (FILE_TOUCH_TOOL_NAMES[name] === true) {
|
|
538
|
+
const parsed = typeof args === "string" ? (() => {
|
|
539
|
+
try {
|
|
540
|
+
return JSON.parse(args);
|
|
541
|
+
} catch {
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
})() : args;
|
|
545
|
+
if (!record(parsed) || typeof parsed.file_path !== "string" || parsed.file_path.trim() === "") return [];
|
|
546
|
+
return [parsed.file_path.trim()];
|
|
547
|
+
}
|
|
548
|
+
if (!name.startsWith("ops_")) return [];
|
|
549
|
+
const bare = name.slice(4);
|
|
550
|
+
if (PENDING_EMPTY[bare] === true) return [];
|
|
551
|
+
if (!record(args)) throw new Error(`review-context: ${name} arguments are incomplete`);
|
|
552
|
+
if (bare === "read_files") {
|
|
553
|
+
if (!Array.isArray(args.files) || args.files.length === 0 || args.files.length > 32) throw new Error(`review-context: ${name} files argument is incomplete`);
|
|
554
|
+
return args.files.map((file) => {
|
|
555
|
+
if (!record(file) || typeof file.path !== "string" || file.path.trim() === "") throw new Error(`review-context: ${name} files argument is incomplete`);
|
|
556
|
+
return file.path.trim();
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (PENDING_PAIR[bare] === true) {
|
|
560
|
+
const paths = [args.path, args.to];
|
|
561
|
+
if (paths.some((path) => typeof path !== "string" || path.trim() === "")) throw new Error(`review-context: ${name} path arguments are incomplete`);
|
|
562
|
+
return paths.map((path) => path.trim());
|
|
563
|
+
}
|
|
564
|
+
if (PENDING_PATH[bare] === true) {
|
|
565
|
+
if (typeof args.path !== "string" || args.path.trim() === "") throw new Error(`review-context: ${name} path argument is incomplete`);
|
|
566
|
+
return [args.path.trim()];
|
|
567
|
+
}
|
|
568
|
+
throw new Error(`review-context: ${name} has dynamic instruction targets that cannot be determined`);
|
|
569
|
+
}
|
|
113
570
|
//#endregion
|
|
114
571
|
//#region src/compat/instruction-overlap.ts
|
|
115
572
|
/** Recognize files already represented by the native instruction baseline in this step. */
|
|
@@ -216,6 +673,9 @@ const DEFAULT_MAX_BYTES = 65536;
|
|
|
216
673
|
const DEFAULT_MAX_SOURCE_BYTES = 32768;
|
|
217
674
|
/** Cap on unanswered `tool/call` records remembered per session. */
|
|
218
675
|
const MAX_PENDING_CALLS = 128;
|
|
676
|
+
const MAX_LIVE_OPS_CALLS = 128;
|
|
677
|
+
const MAX_LIVE_OPS_OBSERVED = 64;
|
|
678
|
+
const MAX_LIVE_OPS_EVIDENCE = 128;
|
|
219
679
|
/** Cap on touched paths remembered per session; beyond it, touches are dropped. */
|
|
220
680
|
const MAX_TOUCHED_PATHS = 64;
|
|
221
681
|
/** Cap on delivered scopes remembered per session; beyond it, nested scopes stop. */
|
|
@@ -310,7 +770,199 @@ function apply(ctx, config = {}) {
|
|
|
310
770
|
const states = /* @__PURE__ */ new WeakMap();
|
|
311
771
|
const callsBySession = /* @__PURE__ */ new WeakMap();
|
|
312
772
|
const executions = new AsyncLocalStorage();
|
|
313
|
-
|
|
773
|
+
const fingerprintOf = (value) => createHash("sha256").update(stableOpsJson(value)).digest("hex");
|
|
774
|
+
const record = (value) => value !== null && typeof value === "object";
|
|
775
|
+
const keyOf = (exec) => exec.token ?? exec;
|
|
776
|
+
let disposed = false;
|
|
777
|
+
const opsLive = /* @__PURE__ */ new Map();
|
|
778
|
+
const removeEntry = (key) => {
|
|
779
|
+
const call = opsLive.get(key);
|
|
780
|
+
if (call === void 0) return void 0;
|
|
781
|
+
if (call.signal !== void 0 && call.abort !== void 0) call.signal.removeEventListener("abort", call.abort);
|
|
782
|
+
opsLive.delete(key);
|
|
783
|
+
return call;
|
|
784
|
+
};
|
|
785
|
+
const boundedLiveSet = (key, call) => {
|
|
786
|
+
if (opsLive.size >= MAX_LIVE_OPS_CALLS) {
|
|
787
|
+
const oldest = opsLive.keys().next();
|
|
788
|
+
if (!oldest.done) {
|
|
789
|
+
const evicted = removeEntry(oldest.value);
|
|
790
|
+
if (evicted !== void 0 && evicted.mode === opsCompatibilityToken(ctx)) stateOf(evicted.session).activityIncomplete = true;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
opsLive.set(key, call);
|
|
794
|
+
};
|
|
795
|
+
const putEvidence = (state, key, entry) => {
|
|
796
|
+
const existing = state.liveOpsEvidence.get(key);
|
|
797
|
+
if (existing !== void 0) {
|
|
798
|
+
if (existing.complete === entry.complete && JSON.stringify(existing.targets) === JSON.stringify(entry.targets)) return;
|
|
799
|
+
if (existing.complete === true) {
|
|
800
|
+
state.activityIncomplete = true;
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
} else if (state.liveOpsEvidence.size >= MAX_LIVE_OPS_EVIDENCE) {
|
|
804
|
+
const oldest = state.liveOpsEvidence.keys().next();
|
|
805
|
+
if (!oldest.done) state.liveOpsEvidence.delete(oldest.value);
|
|
806
|
+
state.activityIncomplete = true;
|
|
807
|
+
}
|
|
808
|
+
state.liveOpsEvidence.set(key, entry);
|
|
809
|
+
};
|
|
810
|
+
const mark = (call) => {
|
|
811
|
+
if (call.mode !== opsCompatibilityToken(ctx)) return;
|
|
812
|
+
const state = stateOf(call.session);
|
|
813
|
+
state.activityIncomplete = true;
|
|
814
|
+
try {
|
|
815
|
+
putEvidence(state, opsActivityEvidenceKey(call.kind, call.callId, call.name, call.args), {
|
|
816
|
+
targets: [],
|
|
817
|
+
complete: false
|
|
818
|
+
});
|
|
819
|
+
} catch (error) {
|
|
820
|
+
ctx.logger.warn(`multi-root instructions: OPS activity key failed: ${error.message}`);
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
aroundTool(ctx, (exec, next) => executions.run(exec, () => runLive(exec, next)));
|
|
824
|
+
const runLive = async (exec, next) => {
|
|
825
|
+
const agent = exec.agent;
|
|
826
|
+
const bare = exec.name.startsWith("ops_") ? exec.name.slice(4) : "";
|
|
827
|
+
const modeToken = opsCompatibilityToken(ctx);
|
|
828
|
+
if (bare === "" || bare === "discover_tools" || bare === "load_sdk" || agent === void 0 || disposed || modeToken === void 0) return await next();
|
|
829
|
+
let argsJson = "";
|
|
830
|
+
let incomplete = false;
|
|
831
|
+
try {
|
|
832
|
+
argsJson = stableOpsJson(exec.arguments);
|
|
833
|
+
} catch {
|
|
834
|
+
incomplete = true;
|
|
835
|
+
}
|
|
836
|
+
const directories = ctx.get("workingDirectory");
|
|
837
|
+
const ensured = directories === void 0 ? void 0 : await directories.ensure(agent, exec.signal).catch(() => void 0);
|
|
838
|
+
if (disposed || opsCompatibilityToken(ctx) !== modeToken) return await next();
|
|
839
|
+
const cwd = ensured ?? effectiveDirectory(ctx, agent.session) ?? agent.session?.header?.cwd;
|
|
840
|
+
const call = {
|
|
841
|
+
exec,
|
|
842
|
+
session: agent.session,
|
|
843
|
+
agent,
|
|
844
|
+
token: keyOf(exec),
|
|
845
|
+
kind: exec.parent === void 0 ? "native" : "ptc",
|
|
846
|
+
callId: exec.callId,
|
|
847
|
+
name: exec.name,
|
|
848
|
+
args: incomplete ? void 0 : JSON.parse(argsJson),
|
|
849
|
+
argsJson,
|
|
850
|
+
cwd,
|
|
851
|
+
observed: /* @__PURE__ */ new Set(),
|
|
852
|
+
incomplete,
|
|
853
|
+
signal: exec.signal,
|
|
854
|
+
mode: modeToken
|
|
855
|
+
};
|
|
856
|
+
if (exec.signal?.aborted === true) {
|
|
857
|
+
mark(call);
|
|
858
|
+
return await next();
|
|
859
|
+
}
|
|
860
|
+
boundedLiveSet(call.token, call);
|
|
861
|
+
call.abort = () => {
|
|
862
|
+
const removed = removeEntry(call.token);
|
|
863
|
+
if (removed !== void 0) mark(removed);
|
|
864
|
+
};
|
|
865
|
+
call.signal?.addEventListener("abort", call.abort);
|
|
866
|
+
let result;
|
|
867
|
+
try {
|
|
868
|
+
result = await next();
|
|
869
|
+
} catch (error) {
|
|
870
|
+
removeEntry(call.token);
|
|
871
|
+
throw error;
|
|
872
|
+
}
|
|
873
|
+
if (call.mode !== opsCompatibilityToken(ctx)) {
|
|
874
|
+
removeEntry(call.token);
|
|
875
|
+
return result;
|
|
876
|
+
}
|
|
877
|
+
try {
|
|
878
|
+
const raw = result !== null && typeof result === "object" && "value" in result ? result.value : result;
|
|
879
|
+
call.fingerprint = fingerprintOf(raw);
|
|
880
|
+
} catch {
|
|
881
|
+
call.incomplete = true;
|
|
882
|
+
ctx.logger.warn("multi-root instructions: OPS activity fingerprint exceeded its budget");
|
|
883
|
+
}
|
|
884
|
+
return result;
|
|
885
|
+
};
|
|
886
|
+
ctx.on.call(ctx, "tools/result", (exec, result) => {
|
|
887
|
+
const call = removeEntry(keyOf(exec));
|
|
888
|
+
if (call === void 0) return;
|
|
889
|
+
if (call.mode !== opsCompatibilityToken(ctx)) return;
|
|
890
|
+
try {
|
|
891
|
+
const final = result;
|
|
892
|
+
const sameArgs = (() => {
|
|
893
|
+
try {
|
|
894
|
+
return stableOpsJson(exec.arguments) === call.argsJson;
|
|
895
|
+
} catch {
|
|
896
|
+
return false;
|
|
897
|
+
}
|
|
898
|
+
})();
|
|
899
|
+
const identity = exec === call.exec && keyOf(exec) === call.token && exec.agent?.session === call.session && exec.name === call.name && exec.callId === call.callId && sameArgs && exec.signal?.aborted !== true && !disposed && call.incomplete !== true;
|
|
900
|
+
const effectNone = final.isError === true && record(final.value) && record(final.value.error) && final.value.error.effectState === "none";
|
|
901
|
+
if (!identity || call.fingerprint === void 0) {
|
|
902
|
+
mark(call);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
let same = false;
|
|
906
|
+
try {
|
|
907
|
+
same = fingerprintOf(final.value) === call.fingerprint;
|
|
908
|
+
} catch {
|
|
909
|
+
ctx.logger.warn("multi-root instructions: OPS final fingerprint exceeded its budget");
|
|
910
|
+
}
|
|
911
|
+
if (!same) {
|
|
912
|
+
mark(call);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
if (final.isError === true) {
|
|
916
|
+
if (effectNone === true) try {
|
|
917
|
+
putEvidence(stateOf(call.session), opsActivityEvidenceKey(call.kind, call.callId, call.name, call.args), {
|
|
918
|
+
targets: [],
|
|
919
|
+
complete: true
|
|
920
|
+
});
|
|
921
|
+
} catch {
|
|
922
|
+
mark(call);
|
|
923
|
+
}
|
|
924
|
+
else mark(call);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const activity = normalizeOpsFileActivity({
|
|
928
|
+
name: call.name,
|
|
929
|
+
args: call.args,
|
|
930
|
+
cwd: call.cwd,
|
|
931
|
+
finalValue: final.value,
|
|
932
|
+
observedPaths: [...call.observed]
|
|
933
|
+
});
|
|
934
|
+
const state = stateOf(call.session);
|
|
935
|
+
if (!activity.complete) state.activityIncomplete = true;
|
|
936
|
+
for (const target of activity.targets) {
|
|
937
|
+
if (state.worked.size >= MAX_DELIVERED_SCOPES && !state.worked.has(target.path)) state.overflow = true;
|
|
938
|
+
else state.worked.add(target.path);
|
|
939
|
+
if (state.touched.size >= MAX_TOUCHED_PATHS) state.overflow = true;
|
|
940
|
+
else state.touched.add(target.path);
|
|
941
|
+
}
|
|
942
|
+
try {
|
|
943
|
+
putEvidence(state, opsActivityEvidenceKey(call.kind, call.callId, call.name, call.args), {
|
|
944
|
+
targets: activity.targets,
|
|
945
|
+
complete: activity.complete
|
|
946
|
+
});
|
|
947
|
+
} catch {
|
|
948
|
+
mark(call);
|
|
949
|
+
}
|
|
950
|
+
} catch (error) {
|
|
951
|
+
ctx.logger.warn(`multi-root instructions: live OPS activity observer failed: ${error.message}`);
|
|
952
|
+
mark(call);
|
|
953
|
+
}
|
|
954
|
+
}, { global: true });
|
|
955
|
+
const onDispose = ctx.on;
|
|
956
|
+
onDispose.call(ctx, "agent/disposed", (payload) => {
|
|
957
|
+
for (const [key, call] of [...opsLive]) if (call.agent === payload.agent) removeEntry(key);
|
|
958
|
+
}, { global: true });
|
|
959
|
+
onDispose.call(ctx, "session/disposed", (session) => {
|
|
960
|
+
for (const [key, call] of [...opsLive]) if (call.session === session) removeEntry(key);
|
|
961
|
+
}, { global: true });
|
|
962
|
+
ctx.effect(() => () => {
|
|
963
|
+
disposed = true;
|
|
964
|
+
for (const key of [...opsLive.keys()]) removeEntry(key);
|
|
965
|
+
});
|
|
314
966
|
const stateOf = (session) => {
|
|
315
967
|
const existing = states.get(session);
|
|
316
968
|
if (existing !== void 0) return existing;
|
|
@@ -318,7 +970,8 @@ function apply(ctx, config = {}) {
|
|
|
318
970
|
scopes: /* @__PURE__ */ new Map(),
|
|
319
971
|
provenanceRoots: /* @__PURE__ */ new Set(),
|
|
320
972
|
touched: /* @__PURE__ */ new Set(),
|
|
321
|
-
worked: /* @__PURE__ */ new Set()
|
|
973
|
+
worked: /* @__PURE__ */ new Set(),
|
|
974
|
+
liveOpsEvidence: /* @__PURE__ */ new Map()
|
|
322
975
|
};
|
|
323
976
|
states.set(session, created);
|
|
324
977
|
return created;
|
|
@@ -367,16 +1020,16 @@ function apply(ctx, config = {}) {
|
|
|
367
1020
|
knownRoots(session) {
|
|
368
1021
|
return [...states.get(session)?.provenanceRoots ?? []];
|
|
369
1022
|
},
|
|
370
|
-
async snapshot(agent, roots, target, signal) {
|
|
371
|
-
|
|
1023
|
+
async snapshot(agent, roots, target, signal, pending) {
|
|
1024
|
+
const modeToken = opsCompatibilityToken(scope);
|
|
1025
|
+
const snapshot = await withRegistryRead(scope, scope.sandboxPolicy.resolve({ session: agent.session }).workspaceRoot, "instructions", async () => {
|
|
1026
|
+
if (opsCompatibilityToken(scope) !== modeToken) throw new Error("review-context: OPS compatibility changed during the snapshot; retry");
|
|
372
1027
|
const allowed = new Set(scope.multiRootScope.scopeOf(scope.sandboxPolicy.resolve({ session: agent.session }).workspaceRoot));
|
|
373
1028
|
roots = roots.filter((root) => allowed.has(root));
|
|
374
1029
|
signal.throwIfAborted();
|
|
375
|
-
if (roots.length === 0) return [];
|
|
376
|
-
const api = await instructionsApi();
|
|
377
|
-
if (api === void 0) throw new Error("review-context: instruction discovery unavailable");
|
|
378
1030
|
const existing = states.get(agent.session);
|
|
379
1031
|
if (existing?.overflow) throw new Error("review-context: successful work index exceeded");
|
|
1032
|
+
if (modeToken !== void 0 && existing?.activityIncomplete === true) throw new Error("review-context: incomplete OPS file activity");
|
|
380
1033
|
const state = {
|
|
381
1034
|
scopes: new Map([...existing?.scopes ?? []].map(([key, value]) => [key, {
|
|
382
1035
|
...value,
|
|
@@ -384,10 +1037,15 @@ function apply(ctx, config = {}) {
|
|
|
384
1037
|
}])),
|
|
385
1038
|
provenanceRoots: new Set(existing?.provenanceRoots),
|
|
386
1039
|
touched: /* @__PURE__ */ new Set([...existing?.touched ?? [], ...existing?.worked ?? []]),
|
|
387
|
-
worked: /* @__PURE__ */ new Set()
|
|
1040
|
+
worked: /* @__PURE__ */ new Set(),
|
|
1041
|
+
liveOpsEvidence: new Map(existing?.liveOpsEvidence ?? [])
|
|
388
1042
|
};
|
|
389
|
-
for (const path of replayedInstructionTargets(agent.session.snapshotEvents(), agent.session.header.cwd)) state.touched.add(path);
|
|
390
|
-
|
|
1043
|
+
for (const path of replayedInstructionTargets(agent.session.snapshotEvents(), agent.session.header.cwd, modeToken === void 0 ? void 0 : existing?.liveOpsEvidence, modeToken === void 0 ? void 0 : pending, { opsCompatibility: modeToken !== void 0 })) state.touched.add(path);
|
|
1044
|
+
const targets = typeof target === "string" ? [target] : target ?? [];
|
|
1045
|
+
for (const item of targets) state.touched.add(item);
|
|
1046
|
+
if (roots.length === 0) return [];
|
|
1047
|
+
const api = await instructionsApi();
|
|
1048
|
+
if (api === void 0) throw new Error("review-context: instruction discovery unavailable");
|
|
391
1049
|
const cwd = effectiveDirectory(scope, agent.session);
|
|
392
1050
|
const reviewCwd = cwd === void 0 ? void 0 : canonicalPath(cwd);
|
|
393
1051
|
const result = [];
|
|
@@ -403,17 +1061,27 @@ function apply(ctx, config = {}) {
|
|
|
403
1061
|
}
|
|
404
1062
|
signal.throwIfAborted();
|
|
405
1063
|
if (Buffer.byteLength(JSON.stringify(result), "utf8") > maxBytes) throw new Error("review-context: instruction budget exceeded");
|
|
1064
|
+
if (opsCompatibilityToken(scope) !== modeToken) throw new Error("review-context: OPS compatibility changed during the snapshot; retry");
|
|
406
1065
|
return result;
|
|
407
1066
|
});
|
|
1067
|
+
if (opsCompatibilityToken(scope) !== modeToken) throw new Error("review-context: OPS compatibility changed during the snapshot; retry");
|
|
1068
|
+
return snapshot;
|
|
408
1069
|
}
|
|
409
1070
|
});
|
|
410
|
-
scope.on("fs/observed", (target, _observation, actor) => {
|
|
1071
|
+
scope.on.call(scope, "fs/observed", (target, _observation, actor) => {
|
|
411
1072
|
const exec = executions.getStore();
|
|
412
|
-
if (exec?.agent === void 0 ||
|
|
1073
|
+
if (exec?.agent === void 0 || actor !== exec) return;
|
|
1074
|
+
const live = opsLive.get(keyOf(exec));
|
|
1075
|
+
if (live !== void 0 && live.mode === opsCompatibilityToken(ctx)) {
|
|
1076
|
+
if (live.observed.size < MAX_LIVE_OPS_OBSERVED) live.observed.add(scope.fs.processPath(target));
|
|
1077
|
+
else live.incomplete = true;
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
if (FILE_TOUCH_TOOL_NAMES[exec.name] !== true) return;
|
|
413
1081
|
const calls = callsBySession.get(exec.agent.session);
|
|
414
1082
|
const call = calls?.get(exec.callId) ?? calls?.get(`ptc:${exec.callId}`);
|
|
415
1083
|
if (call !== void 0) call.observedPath = scope.fs.processPath(target);
|
|
416
|
-
});
|
|
1084
|
+
}, { global: true });
|
|
417
1085
|
scope.on("agent/pre-step", async ({ agent, messages, signal }, next) => {
|
|
418
1086
|
const decision = await next();
|
|
419
1087
|
if (decision.kind !== "enter") return decision;
|
|
@@ -448,7 +1116,8 @@ async function pending(ctx, states, agent, config, maxBytes, signal, messages, r
|
|
|
448
1116
|
scopes: /* @__PURE__ */ new Map(),
|
|
449
1117
|
provenanceRoots: /* @__PURE__ */ new Set(),
|
|
450
1118
|
touched: /* @__PURE__ */ new Set(),
|
|
451
|
-
worked: /* @__PURE__ */ new Set()
|
|
1119
|
+
worked: /* @__PURE__ */ new Set(),
|
|
1120
|
+
liveOpsEvidence: /* @__PURE__ */ new Map()
|
|
452
1121
|
};
|
|
453
1122
|
states.set(session, state);
|
|
454
1123
|
const evaluatedTouches = new Set(state.touched);
|
|
@@ -747,4 +1416,4 @@ async function loadDirectory(fs, api, root, config, signal, cwd, strict = false)
|
|
|
747
1416
|
};
|
|
748
1417
|
}
|
|
749
1418
|
//#endregion
|
|
750
|
-
export { composeInstructionMessage as a, apply as i, DEFAULT_MAX_SOURCE_BYTES as n, inject as o, PLUGIN_SOURCE as r,
|
|
1419
|
+
export { composeInstructionMessage as a, stableOpsJson as c, apply as i, touchedPathOfToolCall as l, DEFAULT_MAX_SOURCE_BYTES as n, inject as o, PLUGIN_SOURCE as r, pendingInstructionTargets as s, DEFAULT_MAX_BYTES as t };
|