mindweave 3.2.2 → 3.2.4
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 +37 -7
- 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/approvalText.js +31 -0
- package/dist/cli/approvalText.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 +1 -1
- package/dist/cli/components/WorkGroup.js.map +1 -1
- package/dist/cli/help.js +3 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/transcript.js +13 -3
- package/dist/cli/transcript.js.map +1 -1
- package/dist/core/turnRunner.js +2 -2
- package/dist/core/turnRunner.js.map +1 -1
- package/dist/dynamo/engine.js +28 -4
- 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 +22 -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/childReport.js +44 -0
- package/dist/tools/childReport.js.map +1 -0
- package/dist/tools/guard.js +9 -2
- package/dist/tools/guard.js.map +1 -1
- package/dist/tools/registry.js +1 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/runCommand.js +92 -56
- 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 +73 -31
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagentOutcome.js +27 -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 +32 -1
- 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,10 +27,15 @@
|
|
|
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";
|
|
32
|
+
import { reportOf } from "./childReport.js";
|
|
30
33
|
import { fail, failQuietly } from "./results.js";
|
|
31
34
|
const MAX_SUBAGENT_DEPTH = 1;
|
|
32
35
|
/** Default step budget for a worker; the caller can scale it with `max_steps`. */
|
|
33
36
|
const SUBAGENT_BUDGET = envInt("MINDWEAVE_SUBAGENT_BUDGET", 20);
|
|
37
|
+
/** A verifier runs things, one call at a time, so it needs more than a reader does. */
|
|
38
|
+
const VERIFIER_BUDGET = envInt("MINDWEAVE_VERIFIER_BUDGET", 40);
|
|
34
39
|
/** Ceiling on `max_steps` so one worker can't run away. */
|
|
35
40
|
const MAX_SUBAGENT_STEPS = envInt("MINDWEAVE_SUBAGENT_MAX_STEPS", 60);
|
|
36
41
|
/** How many workers may run concurrently, process-wide. */
|
|
@@ -83,7 +88,9 @@ export const spawnSubagent = {
|
|
|
83
88
|
// worker must run alone (serial lane) so concurrent edits can't race. This is what
|
|
84
89
|
// lets the model emit several read-only spawn_subagent calls in one message and have
|
|
85
90
|
// them run concurrently.
|
|
86
|
-
|
|
91
|
+
// A verifier runs commands (a build, the app) and its files are checked before and after, so it
|
|
92
|
+
// runs alone even if it was also marked read-only: two at once would see each other's output.
|
|
93
|
+
isConcurrencySafe: (args) => args.read_only === true && args.verify !== true,
|
|
87
94
|
// Three limits were enforced in code and absent from the text: a child cannot spawn
|
|
88
95
|
// its own child, parallel workers are capped, and running out of steps is a clean
|
|
89
96
|
// PAUSE rather than a failure. That last one changes what the model should do about
|
|
@@ -103,8 +110,9 @@ export const spawnSubagent = {
|
|
|
103
110
|
"than doing the work inline, and most editing is sequential, so delegate for " +
|
|
104
111
|
"independent READ and discovery rather than to parallelise edits.\n" +
|
|
105
112
|
`LIMITS: a child gets ${SUBAGENT_BUDGET} tool rounds by default (raise with ` +
|
|
106
|
-
"max_steps). If it runs out it
|
|
107
|
-
"
|
|
113
|
+
"max_steps; a verifier gets more). If it runs out it is given one last turn, with no " +
|
|
114
|
+
"tools, to report what it found so far, and the result says that the limit was hit. " +
|
|
115
|
+
"That is not a failure: the fix is a bigger max_steps or a narrower task, not the same " +
|
|
108
116
|
"call again. A child cannot spawn its own children, so plan one level of " +
|
|
109
117
|
"delegation and do the rest yourself.\n" +
|
|
110
118
|
"BRIEF IT WELL: write to the child as you would to a competent colleague who just " +
|
|
@@ -139,11 +147,13 @@ export const spawnSubagent = {
|
|
|
139
147
|
},
|
|
140
148
|
verify: {
|
|
141
149
|
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
|
-
"
|
|
150
|
+
description: "Spawn an adversarial VERIFIER of work that has already been done, instead of a worker. It cannot edit or " +
|
|
151
|
+
"write files (implied — do not also pass read_only), but it CAN run commands and drive the app, because " +
|
|
152
|
+
"checking means running. It is told to try to BREAK the change rather than confirm it, and it must show the " +
|
|
153
|
+
"command and output behind every check it claims. It answers `VERDICT: pass|fail|partial`. The verdict is " +
|
|
154
|
+
"judged against what it really did: a check whose command never ran does not count, a pass with no command " +
|
|
155
|
+
"output is downgraded to partial, and a pass is also downgraded if any project file changed while it ran. " +
|
|
156
|
+
"Give it the ORIGINAL request, the files that changed, and what the approach was — it cannot see this conversation.",
|
|
147
157
|
},
|
|
148
158
|
},
|
|
149
159
|
},
|
|
@@ -173,11 +183,11 @@ export const spawnSubagent = {
|
|
|
173
183
|
]
|
|
174
184
|
.filter(Boolean)
|
|
175
185
|
.join("\n\n");
|
|
176
|
-
const budget = clampSteps(args.max_steps) ?? SUBAGENT_BUDGET;
|
|
186
|
+
const budget = clampSteps(args.max_steps) ?? (verifying ? VERIFIER_BUDGET : SUBAGENT_BUDGET);
|
|
177
187
|
// Verification is read-only whether or not the caller remembered to say so. A verifier
|
|
178
188
|
// that can edit is a verifier that can make the thing it is judging pass.
|
|
179
189
|
const readOnly = verifying || args.read_only === true;
|
|
180
|
-
const child = ctx.forkChild(framedTask, { readOnly, ...(verifying ? { agentPrompt: VERIFIER_PROMPT } : {}) });
|
|
190
|
+
const child = ctx.forkChild(framedTask, { readOnly, ...(verifying ? { agentPrompt: VERIFIER_PROMPT, verifier: true } : {}) });
|
|
181
191
|
// Dynamic import: the static registry ← engine edge would otherwise cycle. By the
|
|
182
192
|
// time execute() runs, both modules are fully loaded, so this is safe.
|
|
183
193
|
const { respond } = await import("../dynamo/engine.js");
|
|
@@ -185,27 +195,47 @@ export const spawnSubagent = {
|
|
|
185
195
|
// worker's nested rail distinct even when several fan out in parallel.
|
|
186
196
|
const agentId = randomUUID().slice(0, 8);
|
|
187
197
|
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;
|
|
198
|
+
ctx.emitEvent?.({ type: "subagent", phase: "start", id: agentId, task: taskLabel, readOnly, taskFull: task.replace(/\s+/g, " ").trim().slice(0, 600) });
|
|
199
|
+
let reply = "";
|
|
200
|
+
let verified;
|
|
201
|
+
// Run the child. `watch` sees every event first, so a verifier's own commands are known.
|
|
202
|
+
let exhausted = false;
|
|
203
|
+
const runOnce = (steps, watch) =>
|
|
204
|
+
// Bounded: a wide fan-out queues here rather than launching every worker at once.
|
|
205
|
+
gate.run(() => respond(child, {
|
|
206
|
+
signal: ctx.abortSignal,
|
|
207
|
+
maxSteps: steps,
|
|
208
|
+
// Surface the child's activity, not its chatter: its usage counts toward the
|
|
209
|
+
// meter, and each of its tool calls is forwarded UP tagged with this worker's
|
|
210
|
+
// id so the UI nests them in the worker's rail (its prose/reasoning stays
|
|
211
|
+
// muted — only the final distilled report crosses back, as the tool result).
|
|
212
|
+
onEvent: (e) => {
|
|
213
|
+
watch?.(e);
|
|
214
|
+
if (e.type === "usage")
|
|
215
|
+
ctx.reportUsage?.(e);
|
|
216
|
+
else if (e.type === "tool")
|
|
217
|
+
ctx.emitEvent?.({ ...e, agent: agentId });
|
|
218
|
+
},
|
|
219
|
+
}));
|
|
220
|
+
// A child that runs out of steps is given one more turn, with no tools, to say what it found (childReport.ts).
|
|
221
|
+
const runChild = async (watch) => {
|
|
222
|
+
const first = await runOnce(budget, watch);
|
|
223
|
+
const done = await reportOf(child, first, () => runOnce(2, watch));
|
|
224
|
+
exhausted = done.exhausted;
|
|
225
|
+
return done.reply;
|
|
226
|
+
};
|
|
190
227
|
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
|
-
}));
|
|
228
|
+
if (verifying) {
|
|
229
|
+
// A verifier's verdict is judged against what it DID: the commands it really ran, and whether
|
|
230
|
+
// the project's files are the same afterwards. See verifierRun.ts.
|
|
231
|
+
verified = await verifyWithGuards({ cwd: ctx.cwd, checkpoints: ctx.checkpoints, shells: ctx.backgroundShells, run: (watch) => runChild(watch) });
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
reply = await runChild();
|
|
235
|
+
}
|
|
206
236
|
}
|
|
207
237
|
catch (error) {
|
|
208
|
-
ctx.emitEvent?.({ type: "subagent", phase: "end", id: agentId, summary: "failed", error: true });
|
|
238
|
+
ctx.emitEvent?.({ type: "subagent", phase: "end", id: agentId, summary: "failed", error: true, headline: "Failed", verdict: "fail" });
|
|
209
239
|
return fail(`the sub-agent failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
210
240
|
}
|
|
211
241
|
const steps = child.transcript.filter((e) => e.role === "assistant").length;
|
|
@@ -218,7 +248,7 @@ export const spawnSubagent = {
|
|
|
218
248
|
if (verifying) {
|
|
219
249
|
// The verifier's report is judged on its EVIDENCE, not on its confidence: a pass
|
|
220
250
|
// carrying no command output is downgraded before the parent ever reads the word.
|
|
221
|
-
const report = parseVerdict(reply);
|
|
251
|
+
const report = verified ?? parseVerdict(reply);
|
|
222
252
|
const line = renderVerdictLine(report);
|
|
223
253
|
ctx.emitEvent?.({
|
|
224
254
|
type: "subagent",
|
|
@@ -226,9 +256,13 @@ export const spawnSubagent = {
|
|
|
226
256
|
id: agentId,
|
|
227
257
|
summary: `${stepLabel} · ${line}`,
|
|
228
258
|
error: report.verdict === "fail",
|
|
259
|
+
...verifierOutcome(report.verdict, report.evidence, exhausted),
|
|
260
|
+
steps,
|
|
229
261
|
});
|
|
230
262
|
const header = [
|
|
231
|
-
report.concerns.length > 0
|
|
263
|
+
report.concerns.length > 0 || exhausted
|
|
264
|
+
? `Treat with care: ${[...(exhausted ? [`it ran out of its ${budget} steps and was asked for what it had found so far, so some checks may not have been made`] : []), ...report.concerns].join("; ")}.`
|
|
265
|
+
: "",
|
|
232
266
|
`VERDICT: ${report.verdict} (${report.evidence} check${report.evidence === 1 ? "" : "s"} with evidence)`,
|
|
233
267
|
]
|
|
234
268
|
.filter(Boolean)
|
|
@@ -240,7 +274,15 @@ export const spawnSubagent = {
|
|
|
240
274
|
}
|
|
241
275
|
const verdict = verifySubagentReport(reply, { steps, budget, outputFormat });
|
|
242
276
|
const flag = verdict.trustworthy ? "" : ` · ${verdict.status === "unstated" ? "unverified" : verdict.status}`;
|
|
243
|
-
ctx.emitEvent?.({
|
|
277
|
+
ctx.emitEvent?.({
|
|
278
|
+
type: "subagent",
|
|
279
|
+
phase: "end",
|
|
280
|
+
id: agentId,
|
|
281
|
+
summary: `${stepLabel}${flag}`,
|
|
282
|
+
error: verdict.status === "failed",
|
|
283
|
+
...workerOutcome({ status: verdict.status, trustworthy: verdict.trustworthy, exhausted: exhausted || steps >= budget }),
|
|
284
|
+
steps,
|
|
285
|
+
});
|
|
244
286
|
return {
|
|
245
287
|
output: renderVerdict(verdict),
|
|
246
288
|
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,27 @@
|
|
|
1
|
+
/** The headline for a VERIFIER's verdict about the code it checked. */
|
|
2
|
+
export function verifierOutcome(verdict, evidence, exhausted = false) {
|
|
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
|
+
// Ran out of steps: say so, instead of "no verdict", which hides why. A partial one still counts what it did.
|
|
9
|
+
if (exhausted)
|
|
10
|
+
return { headline: `Stopped at its step limit · ${checks}`, verdict: "partial" };
|
|
11
|
+
if (verdict === "partial")
|
|
12
|
+
return { headline: `Could not fully verify · ${checks}`, verdict: "partial" };
|
|
13
|
+
return { headline: "No verdict", verdict: "unknown" };
|
|
14
|
+
}
|
|
15
|
+
/** The headline for an ordinary worker, from the status it reported and what the run showed. */
|
|
16
|
+
export function workerOutcome(run) {
|
|
17
|
+
if (run.trustworthy)
|
|
18
|
+
return { headline: "Done", verdict: "pass" };
|
|
19
|
+
if (run.status === "failed")
|
|
20
|
+
return { headline: "Could not do it", verdict: "fail" };
|
|
21
|
+
if (run.exhausted)
|
|
22
|
+
return { headline: "Stopped at its step limit", verdict: "partial" };
|
|
23
|
+
if (run.status === "partial")
|
|
24
|
+
return { headline: "Only part of it", verdict: "partial" };
|
|
25
|
+
return { headline: "Did not say if it finished", verdict: "unknown" };
|
|
26
|
+
}
|
|
27
|
+
//# 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,EAAE,SAAS,GAAG,KAAK;IAClF,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,8GAA8G;IAC9G,IAAI,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,+BAA+B,MAAM,EAAE,EAAE,OAAO,EAAE,SAAS,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.";
|
|
@@ -225,7 +237,11 @@ export function findControl(controls, raw) {
|
|
|
225
237
|
return { error: `more than one control matches "${raw}": ${hits.slice(0, 6).map((h) => controlLine(h.n, h.c)).join("; ")}. Use the number of the one you mean.` };
|
|
226
238
|
}
|
|
227
239
|
}
|
|
228
|
-
return {
|
|
240
|
+
return {
|
|
241
|
+
error: `no control in the latest list is named "${raw}". Look again, or use a number from the list. ` +
|
|
242
|
+
`A control that only appears while something is pointed at (a row's "⋯" button, a delete icon) is not in the list until ` +
|
|
243
|
+
`you hover that row with \`hover\`; do that first, then click it.`,
|
|
244
|
+
};
|
|
229
245
|
}
|
|
230
246
|
/** Why a file:// address may not be shown (the read_file guard on its path), or null. */
|
|
231
247
|
async function fileUrlReason(url) {
|
|
@@ -548,6 +564,14 @@ async function runWindow(req, ctx, prior, startedAt, call) {
|
|
|
548
564
|
}
|
|
549
565
|
if (pick.kind === "ambiguous")
|
|
550
566
|
return fail(ambiguousMessage(req.window, pick.candidates));
|
|
567
|
+
// Never the window this conversation is typed into: a click or a key there lands in the prompt, and
|
|
568
|
+
// anything it reads from that window is the conversation itself coming back as if it were the app.
|
|
569
|
+
if (pick.kind === "match" && pick.window.host) {
|
|
570
|
+
return fail(`"${pick.window.title}" is the window Mindweave itself runs in. Driving it would type into this conversation, so it is refused. ` +
|
|
571
|
+
`Name the app under test instead. ${listTitles(windows.filter((w) => !w.host))}`);
|
|
572
|
+
}
|
|
573
|
+
if (pick.kind !== "match")
|
|
574
|
+
return fail(`No window could be chosen for "${req.window}".`);
|
|
551
575
|
target = { handle: pick.window.handle, title: pick.window.title };
|
|
552
576
|
// A different window: the old numbers mean nothing here.
|
|
553
577
|
if (state && state.handle !== target.handle)
|
|
@@ -742,7 +766,10 @@ async function runPage(req, ctx, prior, startedAt, call) {
|
|
|
742
766
|
const found = await hideAppWindows({ port: req.port }, !req.show);
|
|
743
767
|
state.hidden = found.moved;
|
|
744
768
|
state.hiddenDesktop = found.hiddenDesktop;
|
|
769
|
+
state.foreign = found.ours === false;
|
|
745
770
|
othersNote = found.moved.length ? HIDE_NOTE : found.hiddenDesktop ? (req.show ? CANNOT_SHOW_NOTE : HIDDEN_DESKTOP_NOTE) : "";
|
|
771
|
+
if (state.foreign)
|
|
772
|
+
othersNote = (othersNote ? `${othersNote}\n\n` : "") + foreignNote(req.port, state.title);
|
|
746
773
|
if (others.length) {
|
|
747
774
|
othersNote =
|
|
748
775
|
(othersNote ? `${othersNote}\n\n` : "") +
|
|
@@ -782,6 +809,10 @@ async function runPage(req, ctx, prior, startedAt, call) {
|
|
|
782
809
|
if (!state) {
|
|
783
810
|
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
811
|
}
|
|
812
|
+
// Acting in somebody else's app, not only looking at it: said again, short, on each such call.
|
|
813
|
+
if (state.foreign && req.action !== "look" && !othersNote.includes("did not start")) {
|
|
814
|
+
othersNote = othersNote ? `${othersNote}\n\n${FOREIGN_REMINDER}` : FOREIGN_REMINDER;
|
|
815
|
+
}
|
|
785
816
|
// Asked to show it: put it back on screen if it was moved. One on the hidden desktop
|
|
786
817
|
// cannot be, and the model is told so rather than left believing it is visible.
|
|
787
818
|
if (req.show && state.hidden?.length) {
|