mindweave 3.2.1 → 3.2.3
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/cli/App.js +2 -2
- package/dist/cli/App.js.map +1 -1
- package/dist/cli/agentLines.js +196 -0
- package/dist/cli/agentLines.js.map +1 -0
- package/dist/cli/components/BlockView.js +1 -1
- package/dist/cli/components/BlockView.js.map +1 -1
- package/dist/cli/components/SubagentView.js +43 -74
- package/dist/cli/components/SubagentView.js.map +1 -1
- package/dist/cli/components/WorkGroup.js +55 -11
- package/dist/cli/components/WorkGroup.js.map +1 -1
- package/dist/cli/help.js +6 -3
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/toolDisplay.js +8 -2
- package/dist/cli/toolDisplay.js.map +1 -1
- package/dist/cli/transcript.js +15 -5
- package/dist/cli/transcript.js.map +1 -1
- package/dist/cli/workLines.js +63 -8
- package/dist/cli/workLines.js.map +1 -1
- package/dist/cli/zz_show.js +24 -0
- package/dist/cli/zz_show.js.map +1 -0
- package/dist/core/turnRunner.js +22 -4
- package/dist/core/turnRunner.js.map +1 -1
- package/dist/dynamo/engine.js +6 -3
- package/dist/dynamo/engine.js.map +1 -1
- package/dist/dynamo/guard.js +6 -0
- package/dist/dynamo/guard.js.map +1 -1
- package/dist/dynamo/marathon.js +17 -4
- package/dist/dynamo/marathon.js.map +1 -1
- package/dist/memory/compaction.js +60 -6
- package/dist/memory/compaction.js.map +1 -1
- package/dist/memory/session.js +2 -0
- package/dist/memory/session.js.map +1 -1
- package/dist/tools/guard.js +13 -0
- package/dist/tools/guard.js.map +1 -1
- package/dist/tools/rebuildDetail.js +2 -3
- package/dist/tools/rebuildDetail.js.map +1 -1
- package/dist/tools/registry.js +1 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/runCommand.js +9 -0
- package/dist/tools/runCommand.js.map +1 -1
- package/dist/tools/screenshot.js +28 -9
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/screenshotWin.js +258 -175
- package/dist/tools/screenshotWin.js.map +1 -1
- package/dist/tools/subagent.js +55 -27
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagentOutcome.js +24 -0
- package/dist/tools/subagentOutcome.js.map +1 -0
- package/dist/tools/treeGuard.js +164 -0
- package/dist/tools/treeGuard.js.map +1 -0
- package/dist/tools/ui.js +34 -7
- package/dist/tools/ui.js.map +1 -1
- package/dist/tools/uiHide.js +19 -6
- package/dist/tools/uiHide.js.map +1 -1
- package/dist/tools/verifierCommands.js +188 -0
- package/dist/tools/verifierCommands.js.map +1 -0
- package/dist/tools/verifierRun.js +88 -0
- package/dist/tools/verifierRun.js.map +1 -0
- package/dist/tools/verifierTools.js +19 -0
- package/dist/tools/verifierTools.js.map +1 -0
- package/dist/tools/verifyReport.js +79 -9
- package/dist/tools/verifyReport.js.map +1 -1
- package/package.json +3 -3
package/dist/tools/subagent.js
CHANGED
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
import { randomUUID } from "node:crypto";
|
|
28
28
|
import { STATUS_INSTRUCTION, renderVerdict, verifySubagentReport } from "./subagentReport.js";
|
|
29
29
|
import { VERDICT_INSTRUCTION, VERIFIER_PROMPT, parseVerdict, renderVerdict as renderVerdictLine } from "./verifyReport.js";
|
|
30
|
+
import { verifierOutcome, workerOutcome } from "./subagentOutcome.js";
|
|
31
|
+
import { verifyWithGuards } from "./verifierRun.js";
|
|
30
32
|
import { fail, failQuietly } from "./results.js";
|
|
31
33
|
const MAX_SUBAGENT_DEPTH = 1;
|
|
32
34
|
/** Default step budget for a worker; the caller can scale it with `max_steps`. */
|
|
@@ -83,7 +85,9 @@ export const spawnSubagent = {
|
|
|
83
85
|
// worker must run alone (serial lane) so concurrent edits can't race. This is what
|
|
84
86
|
// lets the model emit several read-only spawn_subagent calls in one message and have
|
|
85
87
|
// them run concurrently.
|
|
86
|
-
|
|
88
|
+
// A verifier runs commands (a build, the app) and its files are checked before and after, so it
|
|
89
|
+
// runs alone even if it was also marked read-only: two at once would see each other's output.
|
|
90
|
+
isConcurrencySafe: (args) => args.read_only === true && args.verify !== true,
|
|
87
91
|
// Three limits were enforced in code and absent from the text: a child cannot spawn
|
|
88
92
|
// its own child, parallel workers are capped, and running out of steps is a clean
|
|
89
93
|
// PAUSE rather than a failure. That last one changes what the model should do about
|
|
@@ -139,11 +143,13 @@ export const spawnSubagent = {
|
|
|
139
143
|
},
|
|
140
144
|
verify: {
|
|
141
145
|
type: "boolean",
|
|
142
|
-
description: "Spawn an adversarial VERIFIER of work that has already been done, instead of a worker. It
|
|
143
|
-
"(implied — do not also pass read_only), it
|
|
144
|
-
"
|
|
145
|
-
"and
|
|
146
|
-
"
|
|
146
|
+
description: "Spawn an adversarial VERIFIER of work that has already been done, instead of a worker. It cannot edit or " +
|
|
147
|
+
"write files (implied — do not also pass read_only), but it CAN run commands and drive the app, because " +
|
|
148
|
+
"checking means running. It is told to try to BREAK the change rather than confirm it, and it must show the " +
|
|
149
|
+
"command and output behind every check it claims. It answers `VERDICT: pass|fail|partial`. The verdict is " +
|
|
150
|
+
"judged against what it really did: a check whose command never ran does not count, a pass with no command " +
|
|
151
|
+
"output is downgraded to partial, and a pass is also downgraded if any project file changed while it ran. " +
|
|
152
|
+
"Give it the ORIGINAL request, the files that changed, and what the approach was — it cannot see this conversation.",
|
|
147
153
|
},
|
|
148
154
|
},
|
|
149
155
|
},
|
|
@@ -177,7 +183,7 @@ export const spawnSubagent = {
|
|
|
177
183
|
// Verification is read-only whether or not the caller remembered to say so. A verifier
|
|
178
184
|
// that can edit is a verifier that can make the thing it is judging pass.
|
|
179
185
|
const readOnly = verifying || args.read_only === true;
|
|
180
|
-
const child = ctx.forkChild(framedTask, { readOnly, ...(verifying ? { agentPrompt: VERIFIER_PROMPT } : {}) });
|
|
186
|
+
const child = ctx.forkChild(framedTask, { readOnly, ...(verifying ? { agentPrompt: VERIFIER_PROMPT, verifier: true } : {}) });
|
|
181
187
|
// Dynamic import: the static registry ← engine edge would otherwise cycle. By the
|
|
182
188
|
// time execute() runs, both modules are fully loaded, so this is safe.
|
|
183
189
|
const { respond } = await import("../dynamo/engine.js");
|
|
@@ -185,27 +191,39 @@ export const spawnSubagent = {
|
|
|
185
191
|
// worker's nested rail distinct even when several fan out in parallel.
|
|
186
192
|
const agentId = randomUUID().slice(0, 8);
|
|
187
193
|
const taskLabel = task.replace(/\s+/g, " ").trim().slice(0, 100);
|
|
188
|
-
ctx.emitEvent?.({ type: "subagent", phase: "start", id: agentId, task: taskLabel, readOnly });
|
|
189
|
-
let reply;
|
|
194
|
+
ctx.emitEvent?.({ type: "subagent", phase: "start", id: agentId, task: taskLabel, readOnly, taskFull: task.replace(/\s+/g, " ").trim().slice(0, 600) });
|
|
195
|
+
let reply = "";
|
|
196
|
+
let verified;
|
|
197
|
+
// Run the child. `watch` sees every event first, so a verifier's own commands are known.
|
|
198
|
+
const runChild = (watch) =>
|
|
199
|
+
// Bounded: a wide fan-out queues here rather than launching every worker at once.
|
|
200
|
+
gate.run(() => respond(child, {
|
|
201
|
+
signal: ctx.abortSignal,
|
|
202
|
+
maxSteps: budget,
|
|
203
|
+
// Surface the child's activity, not its chatter: its usage counts toward the
|
|
204
|
+
// meter, and each of its tool calls is forwarded UP tagged with this worker's
|
|
205
|
+
// id so the UI nests them in the worker's rail (its prose/reasoning stays
|
|
206
|
+
// muted — only the final distilled report crosses back, as the tool result).
|
|
207
|
+
onEvent: (e) => {
|
|
208
|
+
watch?.(e);
|
|
209
|
+
if (e.type === "usage")
|
|
210
|
+
ctx.reportUsage?.(e);
|
|
211
|
+
else if (e.type === "tool")
|
|
212
|
+
ctx.emitEvent?.({ ...e, agent: agentId });
|
|
213
|
+
},
|
|
214
|
+
}));
|
|
190
215
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
onEvent: (e) => {
|
|
200
|
-
if (e.type === "usage")
|
|
201
|
-
ctx.reportUsage?.(e);
|
|
202
|
-
else if (e.type === "tool")
|
|
203
|
-
ctx.emitEvent?.({ ...e, agent: agentId });
|
|
204
|
-
},
|
|
205
|
-
}));
|
|
216
|
+
if (verifying) {
|
|
217
|
+
// A verifier's verdict is judged against what it DID: the commands it really ran, and whether
|
|
218
|
+
// the project's files are the same afterwards. See verifierRun.ts.
|
|
219
|
+
verified = await verifyWithGuards({ cwd: ctx.cwd, checkpoints: ctx.checkpoints, shells: ctx.backgroundShells, run: (watch) => runChild(watch) });
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
reply = await runChild();
|
|
223
|
+
}
|
|
206
224
|
}
|
|
207
225
|
catch (error) {
|
|
208
|
-
ctx.emitEvent?.({ type: "subagent", phase: "end", id: agentId, summary: "failed", error: true });
|
|
226
|
+
ctx.emitEvent?.({ type: "subagent", phase: "end", id: agentId, summary: "failed", error: true, headline: "Failed", verdict: "fail" });
|
|
209
227
|
return fail(`the sub-agent failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
210
228
|
}
|
|
211
229
|
const steps = child.transcript.filter((e) => e.role === "assistant").length;
|
|
@@ -218,7 +236,7 @@ export const spawnSubagent = {
|
|
|
218
236
|
if (verifying) {
|
|
219
237
|
// The verifier's report is judged on its EVIDENCE, not on its confidence: a pass
|
|
220
238
|
// carrying no command output is downgraded before the parent ever reads the word.
|
|
221
|
-
const report = parseVerdict(reply);
|
|
239
|
+
const report = verified ?? parseVerdict(reply);
|
|
222
240
|
const line = renderVerdictLine(report);
|
|
223
241
|
ctx.emitEvent?.({
|
|
224
242
|
type: "subagent",
|
|
@@ -226,6 +244,8 @@ export const spawnSubagent = {
|
|
|
226
244
|
id: agentId,
|
|
227
245
|
summary: `${stepLabel} · ${line}`,
|
|
228
246
|
error: report.verdict === "fail",
|
|
247
|
+
...verifierOutcome(report.verdict, report.evidence),
|
|
248
|
+
steps,
|
|
229
249
|
});
|
|
230
250
|
const header = [
|
|
231
251
|
report.concerns.length > 0 ? `Treat with care: ${report.concerns.join("; ")}.` : "",
|
|
@@ -240,7 +260,15 @@ export const spawnSubagent = {
|
|
|
240
260
|
}
|
|
241
261
|
const verdict = verifySubagentReport(reply, { steps, budget, outputFormat });
|
|
242
262
|
const flag = verdict.trustworthy ? "" : ` · ${verdict.status === "unstated" ? "unverified" : verdict.status}`;
|
|
243
|
-
ctx.emitEvent?.({
|
|
263
|
+
ctx.emitEvent?.({
|
|
264
|
+
type: "subagent",
|
|
265
|
+
phase: "end",
|
|
266
|
+
id: agentId,
|
|
267
|
+
summary: `${stepLabel}${flag}`,
|
|
268
|
+
error: verdict.status === "failed",
|
|
269
|
+
...workerOutcome({ status: verdict.status, trustworthy: verdict.trustworthy, exhausted: steps >= budget }),
|
|
270
|
+
steps,
|
|
271
|
+
});
|
|
244
272
|
return {
|
|
245
273
|
output: renderVerdict(verdict),
|
|
246
274
|
summary: `subagent · ${stepLabel}${flag}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent.js","sourceRoot":"","sources":["../../src/tools/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,IAAI,iBAAiB,
|
|
1
|
+
{"version":3,"file":"subagent.js","sourceRoot":"","sources":["../../src/tools/subagent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The headline for a VERIFIER's verdict about the code it checked. */
|
|
2
|
+
export function verifierOutcome(verdict, evidence) {
|
|
3
|
+
const checks = evidence === 1 ? "1 check" : `${evidence} checks`;
|
|
4
|
+
if (verdict === "pass")
|
|
5
|
+
return { headline: `Verified · ${checks}`, verdict: "pass" };
|
|
6
|
+
if (verdict === "fail")
|
|
7
|
+
return { headline: `Failed verification · ${checks}`, verdict: "fail" };
|
|
8
|
+
if (verdict === "partial")
|
|
9
|
+
return { headline: `Could not fully verify · ${checks}`, verdict: "partial" };
|
|
10
|
+
return { headline: "No verdict", verdict: "unknown" };
|
|
11
|
+
}
|
|
12
|
+
/** The headline for an ordinary worker, from the status it reported and what the run showed. */
|
|
13
|
+
export function workerOutcome(run) {
|
|
14
|
+
if (run.trustworthy)
|
|
15
|
+
return { headline: "Done", verdict: "pass" };
|
|
16
|
+
if (run.status === "failed")
|
|
17
|
+
return { headline: "Could not do it", verdict: "fail" };
|
|
18
|
+
if (run.exhausted)
|
|
19
|
+
return { headline: "Stopped at its step limit", verdict: "partial" };
|
|
20
|
+
if (run.status === "partial")
|
|
21
|
+
return { headline: "Only part of it", verdict: "partial" };
|
|
22
|
+
return { headline: "Did not say if it finished", verdict: "unknown" };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=subagentOutcome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagentOutcome.js","sourceRoot":"","sources":["../../src/tools/subagentOutcome.ts"],"names":[],"mappings":"AAaA,uEAAuE;AACvE,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,QAAgB;IAC/D,MAAM,MAAM,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,QAAQ,SAAS,CAAC;IACjE,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACrF,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,EAAE,QAAQ,EAAE,yBAAyB,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAChG,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,4BAA4B,MAAM,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IACzG,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACxD,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,GAAyE;IACrG,IAAI,GAAG,CAAC,WAAW;QAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClE,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACrF,IAAI,GAAG,CAAC,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,2BAA2B,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IACxF,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IACzF,OAAO,EAAE,QAAQ,EAAE,4BAA4B,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* treeGuard.ts — did a run change the project's files, and which?
|
|
3
|
+
*
|
|
4
|
+
* A verifier is told it cannot change anything, and its edit and write tools are removed. But it
|
|
5
|
+
* has a command tool now, because checking something means running it, and a command can write
|
|
6
|
+
* a file by other means: a redirect, a formatter, a script, a build step that rewrites a source
|
|
7
|
+
* file. "It cannot" is then a request, not a fact, and a verifier that can change the code it is
|
|
8
|
+
* judging can make it pass. So the project's files are looked at before the run and again after.
|
|
9
|
+
*
|
|
10
|
+
* What counts is the project's own tree: the same files search sees, which leaves out version
|
|
11
|
+
* control, installed packages, build output and whatever .gitignore excludes. A build that rewrites
|
|
12
|
+
* `out/` is doing its job; a script that rewrites `src/` is not.
|
|
13
|
+
*
|
|
14
|
+
* It does not undo anything itself. Whoever changed a file between the two looks cannot be known:
|
|
15
|
+
* the user may have been typing in their editor. So it reports, and it hands the files it holds a
|
|
16
|
+
* "before" for to the undo net (checkpoints.ts), which already refuses to overwrite a file that is
|
|
17
|
+
* no longer what we wrote.
|
|
18
|
+
*/
|
|
19
|
+
import { promises as fs } from "node:fs";
|
|
20
|
+
import { walkFiles } from "./walk.js";
|
|
21
|
+
/** Files looked at. A bigger tree is looked at in part, and says so. */
|
|
22
|
+
const MAX_FILES = 6000;
|
|
23
|
+
/** A file is held (so it can be put back) only up to this size, and only up to a total. */
|
|
24
|
+
const MAX_HELD_FILE_BYTES = 256 * 1024;
|
|
25
|
+
const MAX_HELD_TOTAL_BYTES = 24 * 1024 * 1024;
|
|
26
|
+
/** The text of a file when it is small and is valid UTF-8 text, or null (pure of the walk). */
|
|
27
|
+
async function readHeld(path, size, budget) {
|
|
28
|
+
if (size > MAX_HELD_FILE_BYTES || size > budget.left)
|
|
29
|
+
return null;
|
|
30
|
+
try {
|
|
31
|
+
const bytes = await fs.readFile(path);
|
|
32
|
+
if (bytes.includes(0))
|
|
33
|
+
return null;
|
|
34
|
+
const text = bytes.toString("utf8");
|
|
35
|
+
// Bytes that are not UTF-8 would not come back the same; not held, rather than held wrongly.
|
|
36
|
+
if (!Buffer.from(text, "utf8").equals(bytes))
|
|
37
|
+
return null;
|
|
38
|
+
budget.left -= size;
|
|
39
|
+
return text;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Look at the project's files now. Never throws: a file it cannot read is simply not covered. */
|
|
46
|
+
export async function snapshotTree(root) {
|
|
47
|
+
const files = new Map();
|
|
48
|
+
let found;
|
|
49
|
+
try {
|
|
50
|
+
found = await walkFiles(root, MAX_FILES);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return { root, files, truncated: true };
|
|
54
|
+
}
|
|
55
|
+
const budget = { left: MAX_HELD_TOTAL_BYTES };
|
|
56
|
+
for (const f of found.files) {
|
|
57
|
+
try {
|
|
58
|
+
const st = await fs.stat(f.abs);
|
|
59
|
+
files.set(f.abs, { size: st.size, mtimeMs: st.mtimeMs, content: await readHeld(f.abs, st.size, budget) });
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// gone between the walk and the look: not covered
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { root, files, truncated: found.truncated };
|
|
66
|
+
}
|
|
67
|
+
/** What changed since the snapshot (the files themselves are looked at again). */
|
|
68
|
+
export async function treeChanges(before) {
|
|
69
|
+
const out = { changed: [], created: [], deleted: [], heldBefore: new Map() };
|
|
70
|
+
let found;
|
|
71
|
+
try {
|
|
72
|
+
found = await walkFiles(before.root, MAX_FILES);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
const now = new Set(found.files.map((f) => f.abs));
|
|
78
|
+
for (const [abs, was] of before.files) {
|
|
79
|
+
if (!now.has(abs)) {
|
|
80
|
+
out.deleted.push(abs);
|
|
81
|
+
out.heldBefore.set(abs, was.content);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
let st;
|
|
85
|
+
try {
|
|
86
|
+
st = await fs.stat(abs);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
out.deleted.push(abs);
|
|
90
|
+
out.heldBefore.set(abs, was.content);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Same size and time is unchanged. A file touched but rewritten with the same text is not a change.
|
|
94
|
+
if (st.size === was.size && st.mtimeMs === was.mtimeMs)
|
|
95
|
+
continue;
|
|
96
|
+
if (was.content !== null) {
|
|
97
|
+
const budget = { left: MAX_HELD_FILE_BYTES };
|
|
98
|
+
const text = await readHeld(abs, st.size, budget);
|
|
99
|
+
if (text === was.content)
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
out.changed.push(abs);
|
|
103
|
+
out.heldBefore.set(abs, was.content);
|
|
104
|
+
}
|
|
105
|
+
// A new file can only be told from one past the limit when the whole tree was looked at.
|
|
106
|
+
if (!before.truncated) {
|
|
107
|
+
for (const abs of now)
|
|
108
|
+
if (!before.files.has(abs))
|
|
109
|
+
out.created.push(abs);
|
|
110
|
+
}
|
|
111
|
+
out.changed.sort();
|
|
112
|
+
out.created.sort();
|
|
113
|
+
out.deleted.sort();
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
/** How many files changed in all. */
|
|
117
|
+
export function changeCount(c) {
|
|
118
|
+
return c.changed.length + c.created.length + c.deleted.length;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Give the undo net what it needs to put these files back, where it can (see the header).
|
|
122
|
+
* A created file is "was nothing"; a changed or deleted one is what the snapshot held. A file
|
|
123
|
+
* whose "before" was too big to hold is left out, and the count says so.
|
|
124
|
+
*/
|
|
125
|
+
export async function giveToUndo(checkpoints, c) {
|
|
126
|
+
let covered = 0;
|
|
127
|
+
let notCovered = 0;
|
|
128
|
+
for (const abs of [...c.changed, ...c.deleted, ...c.created]) {
|
|
129
|
+
const was = c.created.includes(abs) ? null : (c.heldBefore.get(abs) ?? undefined);
|
|
130
|
+
if (was === undefined) {
|
|
131
|
+
notCovered++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
let now = "";
|
|
135
|
+
if (!c.deleted.includes(abs)) {
|
|
136
|
+
try {
|
|
137
|
+
now = await fs.readFile(abs, "utf8");
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
notCovered++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (!checkpoints) {
|
|
145
|
+
notCovered++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
checkpoints.backup(abs, was, now);
|
|
149
|
+
covered++;
|
|
150
|
+
}
|
|
151
|
+
return { covered, notCovered };
|
|
152
|
+
}
|
|
153
|
+
/** The changed files, as names under the root, each marked when it is new or gone (pure). */
|
|
154
|
+
export function listChanges(root, c) {
|
|
155
|
+
const rel = (p) => p.slice(root.length).replace(/^[\\/]+/, "").split("\\").join("/");
|
|
156
|
+
return [...c.changed.map(rel), ...c.created.map((p) => `${rel(p)} (new)`), ...c.deleted.map((p) => `${rel(p)} (deleted)`)];
|
|
157
|
+
}
|
|
158
|
+
/** The files, as one line: a few and then a count (pure). */
|
|
159
|
+
export function nameChanges(root, c, max = 4) {
|
|
160
|
+
const all = listChanges(root, c);
|
|
161
|
+
const shown = all.slice(0, max).join(", ");
|
|
162
|
+
return all.length > max ? `${shown} and ${all.length - max} more` : shown;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=treeGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"treeGuard.js","sourceRoot":"","sources":["../../src/tools/treeGuard.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/ui.js
CHANGED
|
@@ -87,6 +87,18 @@ function newLiveId() {
|
|
|
87
87
|
* as missing and knows how to leave it visible when the user wants that. */
|
|
88
88
|
const HIDE_NOTE = "You started this app, so its window was moved off the user's screen while you test it; it is still running " +
|
|
89
89
|
"and drawing, and goes back where it was when you close it. If the user asked to see it, call again with show: true.";
|
|
90
|
+
/**
|
|
91
|
+
* Told when the debugging port belongs to a program this session did not start. A port is a number, not
|
|
92
|
+
* an app: another application can hold 9222, and the page behind it then answers as if it were the one
|
|
93
|
+
* being tested. Said at the attach, where the title is the only other clue, and again on a click or a
|
|
94
|
+
* keystroke, which is the moment it would matter that it is somebody else's app.
|
|
95
|
+
*/
|
|
96
|
+
export function foreignNote(port, title) {
|
|
97
|
+
return (`Port ${port} is held by a program this session did not start${title ? ` (the page is "${title}")` : ""}: another application's, or one the user opened. ` +
|
|
98
|
+
"It may not be the app you launched. Check that the title is the app you mean before you read anything from it as a result, " +
|
|
99
|
+
"and do not click or type in it unless the user told you to test that app. Your own app is probably on a different port: start it with another one.");
|
|
100
|
+
}
|
|
101
|
+
const FOREIGN_REMINDER = "This page belongs to a program you did not start (see the note when it was opened).";
|
|
90
102
|
/** Told once, for an app started on the hidden desktop (hiddenDesktop.ts). */
|
|
91
103
|
const HIDDEN_DESKTOP_NOTE = "This app runs on a hidden desktop, so it never appears on the user's screen while you test it. " +
|
|
92
104
|
"It cannot be moved onto their screen: if the user asks to see it, stop it and start the same command again with hidden: false.";
|
|
@@ -548,6 +560,14 @@ async function runWindow(req, ctx, prior, startedAt, call) {
|
|
|
548
560
|
}
|
|
549
561
|
if (pick.kind === "ambiguous")
|
|
550
562
|
return fail(ambiguousMessage(req.window, pick.candidates));
|
|
563
|
+
// Never the window this conversation is typed into: a click or a key there lands in the prompt, and
|
|
564
|
+
// anything it reads from that window is the conversation itself coming back as if it were the app.
|
|
565
|
+
if (pick.kind === "match" && pick.window.host) {
|
|
566
|
+
return fail(`"${pick.window.title}" is the window Mindweave itself runs in. Driving it would type into this conversation, so it is refused. ` +
|
|
567
|
+
`Name the app under test instead. ${listTitles(windows.filter((w) => !w.host))}`);
|
|
568
|
+
}
|
|
569
|
+
if (pick.kind !== "match")
|
|
570
|
+
return fail(`No window could be chosen for "${req.window}".`);
|
|
551
571
|
target = { handle: pick.window.handle, title: pick.window.title };
|
|
552
572
|
// A different window: the old numbers mean nothing here.
|
|
553
573
|
if (state && state.handle !== target.handle)
|
|
@@ -742,7 +762,10 @@ async function runPage(req, ctx, prior, startedAt, call) {
|
|
|
742
762
|
const found = await hideAppWindows({ port: req.port }, !req.show);
|
|
743
763
|
state.hidden = found.moved;
|
|
744
764
|
state.hiddenDesktop = found.hiddenDesktop;
|
|
765
|
+
state.foreign = found.ours === false;
|
|
745
766
|
othersNote = found.moved.length ? HIDE_NOTE : found.hiddenDesktop ? (req.show ? CANNOT_SHOW_NOTE : HIDDEN_DESKTOP_NOTE) : "";
|
|
767
|
+
if (state.foreign)
|
|
768
|
+
othersNote = (othersNote ? `${othersNote}\n\n` : "") + foreignNote(req.port, state.title);
|
|
746
769
|
if (others.length) {
|
|
747
770
|
othersNote =
|
|
748
771
|
(othersNote ? `${othersNote}\n\n` : "") +
|
|
@@ -782,6 +805,10 @@ async function runPage(req, ctx, prior, startedAt, call) {
|
|
|
782
805
|
if (!state) {
|
|
783
806
|
return fail("Nothing is open yet. Pass `url` to open a web page or dev server, or `port` to use an Electron/Tauri app. " + PORT_RECIPES);
|
|
784
807
|
}
|
|
808
|
+
// Acting in somebody else's app, not only looking at it: said again, short, on each such call.
|
|
809
|
+
if (state.foreign && req.action !== "look" && !othersNote.includes("did not start")) {
|
|
810
|
+
othersNote = othersNote ? `${othersNote}\n\n${FOREIGN_REMINDER}` : FOREIGN_REMINDER;
|
|
811
|
+
}
|
|
785
812
|
// Asked to show it: put it back on screen if it was moved. One on the hidden desktop
|
|
786
813
|
// cannot be, and the model is told so rather than left believing it is visible.
|
|
787
814
|
if (req.show && state.hidden?.length) {
|
|
@@ -1268,19 +1295,19 @@ export function pageLine(counts, off, total) {
|
|
|
1268
1295
|
/**
|
|
1269
1296
|
* What the user's row shows, and what opens behind it (pure).
|
|
1270
1297
|
*
|
|
1271
|
-
* A run
|
|
1272
|
-
*
|
|
1273
|
-
* (an error, a warning, a dialog)
|
|
1274
|
-
* page looked like matters and a collapsed row would be hiding it.
|
|
1298
|
+
* A run is its steps, one row each, and one line about the page; the whole result is behind
|
|
1299
|
+
* a click. A run that did NOT go well keeps its reason on the row, and anything the page said
|
|
1300
|
+
* (an error, a warning, a dialog) is on the row too. Only the list of every control folds away.
|
|
1275
1301
|
*/
|
|
1276
1302
|
export function uiRowDetail(o) {
|
|
1277
1303
|
// A single action that failed is said too: its reason is the one thing the row exists to show.
|
|
1278
1304
|
const { total, off } = pageCounts(o.snap);
|
|
1279
1305
|
const whole = uiOpenedText({ did: o.batch || o.failed ? o.did : "", list: o.fullList, total, off, reports: o.reports });
|
|
1280
|
-
if (o.failed || o.errs > 0 || o.reports)
|
|
1281
|
-
return { detail: whole };
|
|
1282
1306
|
const done = o.batch ? oneLineSteps(o.did) : o.did ? [o.did.split("\n").map((l) => l.trim()).filter(Boolean).join("\u21b5")] : [];
|
|
1283
|
-
|
|
1307
|
+
// What went wrong, and what the page said, stay on the row. The list of every control does not:
|
|
1308
|
+
// it is the same sixteen lines for each failed step, and it is a click away.
|
|
1309
|
+
const said = o.failed || o.errs > 0 || o.reports ? o.reports.split("\n").map((l) => l.trim()).filter(Boolean) : [];
|
|
1310
|
+
const detail = [...done, ...said, pageSummary(o.snap)].join("\n");
|
|
1284
1311
|
return { detail, detailFull: whole };
|
|
1285
1312
|
}
|
|
1286
1313
|
/** One line for a batch on the user's row: "4 steps: click Open menu, type Name, …". Pure. */
|