mindweave 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -43
- package/dist/alternator/chassis/lsp.js +67 -0
- package/dist/alternator/chassis/lsp.js.map +1 -1
- package/dist/cli/App.js +1175 -99
- package/dist/cli/App.js.map +1 -1
- package/dist/cli/altScreen.js +80 -10
- package/dist/cli/altScreen.js.map +1 -1
- package/dist/cli/appIdentity.js +54 -0
- package/dist/cli/appIdentity.js.map +1 -0
- package/dist/cli/attachments.js +27 -3
- package/dist/cli/attachments.js.map +1 -1
- package/dist/cli/blockHeights.js +58 -0
- package/dist/cli/blockHeights.js.map +1 -0
- package/dist/cli/caretPark.js +94 -0
- package/dist/cli/caretPark.js.map +1 -0
- package/dist/cli/clipboard.js +68 -0
- package/dist/cli/clipboard.js.map +1 -0
- package/dist/cli/commandLabel.js +19 -2
- package/dist/cli/commandLabel.js.map +1 -1
- package/dist/cli/commands.js +1 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/components/BlockView.js +1 -1
- package/dist/cli/components/BlockView.js.map +1 -1
- package/dist/cli/components/KeyManager.js +11 -5
- package/dist/cli/components/KeyManager.js.map +1 -1
- package/dist/cli/components/Picker.js +18 -3
- package/dist/cli/components/Picker.js.map +1 -1
- package/dist/cli/components/PromptInput.js +300 -74
- package/dist/cli/components/PromptInput.js.map +1 -1
- package/dist/cli/components/TipLine.js +33 -0
- package/dist/cli/components/TipLine.js.map +1 -0
- package/dist/cli/components/ToolLine.js +30 -3
- package/dist/cli/components/ToolLine.js.map +1 -1
- package/dist/cli/dropHandles.js +76 -0
- package/dist/cli/dropHandles.js.map +1 -0
- package/dist/cli/exitCursor.js +56 -0
- package/dist/cli/exitCursor.js.map +1 -0
- package/dist/cli/framebuffer/overlay.js +42 -0
- package/dist/cli/framebuffer/overlay.js.map +1 -0
- package/dist/cli/framebuffer/paint.js +8 -1
- package/dist/cli/framebuffer/paint.js.map +1 -1
- package/dist/cli/framebuffer/parse.js +54 -4
- package/dist/cli/framebuffer/parse.js.map +1 -1
- package/dist/cli/framebuffer/writer.js +357 -6
- package/dist/cli/framebuffer/writer.js.map +1 -1
- package/dist/cli/groupReveal.js +29 -3
- package/dist/cli/groupReveal.js.map +1 -1
- package/dist/cli/inputView.js +45 -0
- package/dist/cli/inputView.js.map +1 -1
- package/dist/cli/livePad.js +53 -0
- package/dist/cli/livePad.js.map +1 -0
- package/dist/cli/memoryTuning.js +31 -0
- package/dist/cli/memoryTuning.js.map +1 -0
- package/dist/cli/messageQueue.js +76 -7
- package/dist/cli/messageQueue.js.map +1 -1
- package/dist/cli/mouse.js +40 -7
- package/dist/cli/mouse.js.map +1 -1
- package/dist/cli/quietConsole.js +50 -0
- package/dist/cli/quietConsole.js.map +1 -0
- package/dist/cli/screenMode.js +118 -0
- package/dist/cli/screenMode.js.map +1 -0
- package/dist/cli/screenShell.js +67 -0
- package/dist/cli/screenShell.js.map +1 -0
- package/dist/cli/screenStore.js +46 -0
- package/dist/cli/screenStore.js.map +1 -0
- package/dist/cli/scrollPill.js +101 -0
- package/dist/cli/scrollPill.js.map +1 -0
- package/dist/cli/selection.js +130 -0
- package/dist/cli/selection.js.map +1 -0
- package/dist/cli/startupFill.js +48 -0
- package/dist/cli/startupFill.js.map +1 -0
- package/dist/cli/terminalRestore.js +1 -1
- package/dist/cli/terminalRestore.js.map +1 -1
- package/dist/cli/toolDisplay.js +11 -2
- package/dist/cli/toolDisplay.js.map +1 -1
- package/dist/cli/toolItems.js +1 -0
- package/dist/cli/toolItems.js.map +1 -1
- package/dist/cli/transcript.js +54 -3
- package/dist/cli/transcript.js.map +1 -1
- package/dist/cli/wordEdit.js +95 -0
- package/dist/cli/wordEdit.js.map +1 -0
- package/dist/drivers/openai/manifest.js +4 -1
- package/dist/drivers/openai/manifest.js.map +1 -1
- package/dist/dynamo/engine.js +126 -20
- package/dist/dynamo/engine.js.map +1 -1
- package/dist/index.js +42 -3
- package/dist/index.js.map +1 -1
- package/dist/memory/pastedText.js +37 -0
- package/dist/memory/pastedText.js.map +1 -0
- package/dist/memory/presence.js +47 -0
- package/dist/memory/presence.js.map +1 -1
- package/dist/memory/session.js +78 -4
- package/dist/memory/session.js.map +1 -1
- package/dist/memory/store.js +22 -1
- package/dist/memory/store.js.map +1 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/tools/backgroundShells.js +105 -18
- package/dist/tools/backgroundShells.js.map +1 -1
- package/dist/tools/commandOutput.js +207 -0
- package/dist/tools/commandOutput.js.map +1 -0
- package/dist/tools/detail.js +2 -2
- package/dist/tools/detail.js.map +1 -1
- package/dist/tools/openDefault.js +150 -0
- package/dist/tools/openDefault.js.map +1 -0
- package/dist/tools/outputShape.js +31 -6
- package/dist/tools/outputShape.js.map +1 -1
- package/dist/tools/pathList.js +32 -0
- package/dist/tools/pathList.js.map +1 -0
- package/dist/tools/readFile.js +3 -15
- package/dist/tools/readFile.js.map +1 -1
- package/dist/tools/registry.js +3 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/runCommand.js +154 -58
- package/dist/tools/runCommand.js.map +1 -1
- package/dist/tools/screenshot.js +35 -3
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/viewImage.js +88 -0
- package/dist/tools/viewImage.js.map +1 -0
- package/dist/tools/writeFile.js +9 -1
- package/dist/tools/writeFile.js.map +1 -1
- package/package.json +74 -74
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wordEdit.ts — where the cursor lands when you move or delete by the chunk.
|
|
3
|
+
*
|
|
4
|
+
* Backspace one character at a time is fine for a typo and useless for a sentence, and
|
|
5
|
+
* the input had nothing between the two: Ctrl+U cleared the whole line and that was the
|
|
6
|
+
* only bulk operation. These are the missing middle, and they are here as pure functions
|
|
7
|
+
* on (text, cursor) rather than inside the reducer so the boundary rules can be pinned by
|
|
8
|
+
* tests instead of discovered by holding a key down and watching what disappears.
|
|
9
|
+
*
|
|
10
|
+
* A "word" here is a run of non-whitespace, which is coarser than a language-aware
|
|
11
|
+
* boundary and is the right coarseness for this input: `C:\Users\me\Pictures\shot.png`
|
|
12
|
+
* is one thing you want gone in one keystroke, not eight.
|
|
13
|
+
*/
|
|
14
|
+
const isSpace = (ch) => ch === " " || ch === "\t" || ch === "\n" || ch === "\r";
|
|
15
|
+
/**
|
|
16
|
+
* Where on a painted terminal row a piece of text begins, or -1 if it is not there.
|
|
17
|
+
*
|
|
18
|
+
* This is how a click in the input box finds its column WITHOUT anyone computing where
|
|
19
|
+
* the box was laid out. The box's border, padding and prompt marker all sit to the left
|
|
20
|
+
* of the text; rather than adding those up (and being wrong the day one of them changes),
|
|
21
|
+
* the row's own text is located in the row that was actually painted, and the difference
|
|
22
|
+
* IS the offset. A row that cannot be found returns -1, and the caller does nothing —
|
|
23
|
+
* which is the whole point: a mistaken guess moves nobody's cursor.
|
|
24
|
+
*
|
|
25
|
+
* One mismatched cell is tolerated because the caret takes the cell it sits on, so the
|
|
26
|
+
* row on screen differs from the row in the buffer by exactly one character whenever the
|
|
27
|
+
* caret is mid-row and lit.
|
|
28
|
+
*
|
|
29
|
+
* `rowAt` reads a cell as a string, so the caller decides where the row comes from.
|
|
30
|
+
*/
|
|
31
|
+
export function findTextColumn(rowAt, width, text, tolerance = 1) {
|
|
32
|
+
if (text === "")
|
|
33
|
+
return -1;
|
|
34
|
+
for (let start = 0; start + text.length <= width; start++) {
|
|
35
|
+
let wrong = 0;
|
|
36
|
+
for (let i = 0; i < text.length; i++) {
|
|
37
|
+
if (rowAt(start + i) !== text[i]) {
|
|
38
|
+
wrong++;
|
|
39
|
+
if (wrong > tolerance)
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (wrong <= tolerance)
|
|
44
|
+
return start;
|
|
45
|
+
}
|
|
46
|
+
return -1;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Start of the chunk behind the cursor: skip any whitespace immediately behind, then the
|
|
50
|
+
* run of non-whitespace before that. Trailing whitespace goes with the word it follows,
|
|
51
|
+
* so deleting from the end of `foo bar ` lands on `foo ` in one press rather than
|
|
52
|
+
* spending a press on the gap.
|
|
53
|
+
*/
|
|
54
|
+
export function wordStart(value, cursor) {
|
|
55
|
+
let i = Math.max(0, Math.min(cursor, value.length));
|
|
56
|
+
while (i > 0 && isSpace(value[i - 1]))
|
|
57
|
+
i--;
|
|
58
|
+
while (i > 0 && !isSpace(value[i - 1]))
|
|
59
|
+
i--;
|
|
60
|
+
return i;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* End of the chunk ahead of the cursor: skip whitespace, then the run of non-whitespace.
|
|
64
|
+
* The mirror of wordStart, so pressing forward then back returns you where you were.
|
|
65
|
+
*/
|
|
66
|
+
export function wordEnd(value, cursor) {
|
|
67
|
+
let i = Math.max(0, Math.min(cursor, value.length));
|
|
68
|
+
while (i < value.length && isSpace(value[i]))
|
|
69
|
+
i++;
|
|
70
|
+
while (i < value.length && !isSpace(value[i]))
|
|
71
|
+
i++;
|
|
72
|
+
return i;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* End of the LINE the cursor is on, not of the whole buffer. The input is multi-line
|
|
76
|
+
* (Shift+Enter), so "delete to the end" has to mean the line or it would eat the rest of
|
|
77
|
+
* a message from the middle of its first line.
|
|
78
|
+
*/
|
|
79
|
+
export function lineEnd(value, cursor) {
|
|
80
|
+
const from = Math.max(0, Math.min(cursor, value.length));
|
|
81
|
+
const nl = value.indexOf("\n", from);
|
|
82
|
+
return nl === -1 ? value.length : nl;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What Ctrl+K removes: the rest of the line, or — when the cursor already sits at the end
|
|
86
|
+
* of one — the line break itself, which is what joins this line to the next. Without that
|
|
87
|
+
* second case the key does nothing at a line end and reads as broken.
|
|
88
|
+
*/
|
|
89
|
+
export function killToLineEnd(value, cursor) {
|
|
90
|
+
const from = Math.max(0, Math.min(cursor, value.length));
|
|
91
|
+
const end = lineEnd(value, from);
|
|
92
|
+
const to = end === from ? Math.min(from + 1, value.length) : end;
|
|
93
|
+
return { value: value.slice(0, from) + value.slice(to), cursor: from };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=wordEdit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wordEdit.js","sourceRoot":"","sources":["../../src/cli/wordEdit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,OAAO,GAAG,CAAC,EAAU,EAAW,EAAE,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC;AAEjG;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAC5B,KAA4B,EAC5B,KAAa,EACb,IAAY,EACZ,SAAS,GAAG,CAAC;IAEb,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,CAAC,CAAC,CAAC;IAC3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjC,KAAK,EAAE,CAAC;gBACR,IAAI,KAAK,GAAG,SAAS;oBAAE,MAAM;YAC/B,CAAC;QACH,CAAC;QACD,IAAI,KAAK,IAAI,SAAS;YAAE,OAAO,KAAK,CAAC;IACvC,CAAC;IACD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,MAAc;IACrD,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;QAAE,CAAC,EAAE,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;QAAE,CAAC,EAAE,CAAC;IAC7C,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,MAAc;IACnD,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAAE,CAAC,EAAE,CAAC;IACnD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAAE,CAAC,EAAE,CAAC;IACpD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,MAAc;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,MAAc;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACjE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export const ASTRA = "gpt-6-astra";
|
|
1
2
|
export const SOL = "gpt-5.6-sol";
|
|
2
3
|
export const TERRA = "gpt-5.6-terra";
|
|
3
4
|
export const LUNA = "gpt-5.6-luna";
|
|
@@ -12,7 +13,8 @@ export const DEFAULT_MODEL = TERRA;
|
|
|
12
13
|
*/
|
|
13
14
|
export const MODELS = [
|
|
14
15
|
{ id: TERRA, label: "GPT-5.6 Terra", description: "balanced intelligence and cost — the default" },
|
|
15
|
-
{ id:
|
|
16
|
+
{ id: ASTRA, label: "GPT-6 Astra", description: "the newest flagship — the most capable, and the priciest" },
|
|
17
|
+
{ id: SOL, label: "GPT-5.6 Sol", description: "the GPT-5.6 frontier tier" },
|
|
16
18
|
{ id: LUNA, label: "GPT-5.6 Luna", description: "cheap and quick, for high-volume work" },
|
|
17
19
|
];
|
|
18
20
|
/**
|
|
@@ -40,6 +42,7 @@ export function thinkLevels(_model) {
|
|
|
40
42
|
* which is what keeps a re-sent conversation cheap.
|
|
41
43
|
*/
|
|
42
44
|
const PRICES = {
|
|
45
|
+
[ASTRA]: { cacheHit: 1, cacheMiss: 10, output: 50 },
|
|
43
46
|
[SOL]: { cacheHit: 0.5, cacheMiss: 5, output: 30 },
|
|
44
47
|
[TERRA]: { cacheHit: 0.2, cacheMiss: 2, output: 12 },
|
|
45
48
|
[LUNA]: { cacheHit: 0.02, cacheMiss: 0.2, output: 1.2 },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/drivers/openai/manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/drivers/openai/manifest.ts"],"names":[],"mappings":"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"}
|
package/dist/dynamo/engine.js
CHANGED
|
@@ -291,13 +291,35 @@ function taskLimits() {
|
|
|
291
291
|
maxSeconds: envNum("MINDWEAVE_MAX_TASK_SECONDS", 0),
|
|
292
292
|
};
|
|
293
293
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
294
|
+
/**
|
|
295
|
+
* How many tool rounds one turn may take, or undefined for no ceiling (pure).
|
|
296
|
+
*
|
|
297
|
+
* A ceiling is for a loop NOBODY IS WATCHING. That is the whole rule, and it is what
|
|
298
|
+
* decides who gets one:
|
|
299
|
+
*
|
|
300
|
+
* - A **sub-agent** always gets one. It runs unattended by definition: there is no
|
|
301
|
+
* prompt to interrupt, no screen showing its steps, and nothing between a worker
|
|
302
|
+
* that has misread its task and an unbounded bill. `subagent.ts` passes its budget
|
|
303
|
+
* explicitly, so a worker is capped whatever this returns.
|
|
304
|
+
* - The **interactive turn** does not, by default. Someone is sitting in front of it
|
|
305
|
+
* watching every tool row appear, and Esc stops the turn at the next boundary. A
|
|
306
|
+
* counter is a worse circuit breaker than the person already holding one.
|
|
307
|
+
*
|
|
308
|
+
* The previous default capped the interactive loop at fifty rounds, and what that
|
|
309
|
+
* actually stopped was work: a task across a dozen files spends fifty rounds without
|
|
310
|
+
* anything going wrong, and it ended mid-flight with a pause the user then had to
|
|
311
|
+
* step over. A guard that fires on ordinary work is not a guard, it is a limit.
|
|
312
|
+
*
|
|
313
|
+
* `MINDWEAVE_STEP_BUDGET` puts a ceiling back for anyone who wants one — an unattended
|
|
314
|
+
* run, a script, a machine that must not be able to spend past a point. Unset, the
|
|
315
|
+
* turn ends when the model is finished or the user stops it.
|
|
316
|
+
*/
|
|
317
|
+
export function resolveStepLimit(maxSteps, envValue) {
|
|
318
|
+
if (maxSteps !== undefined)
|
|
319
|
+
return maxSteps;
|
|
320
|
+
const n = Number(envValue);
|
|
321
|
+
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
322
|
+
}
|
|
301
323
|
// Verification gate: when the model edits files then tries to finish without
|
|
302
324
|
// running any check, nudge it once to verify. On by default; MINDWEAVE_VERIFY_GATE=0
|
|
303
325
|
// disables it. See verify.ts for the (pure, tested) fact detectors.
|
|
@@ -311,6 +333,40 @@ const BG_POLL_ALLOWANCE = envInt("MINDWEAVE_BG_POLL_LIMIT", 1);
|
|
|
311
333
|
// stop it. 3 is the threshold: repeated identical failures past that are a stuck loop,
|
|
312
334
|
// not progress. Env-overridable for tuning.
|
|
313
335
|
const REPEAT_FAIL_LIMIT = envInt("MINDWEAVE_REPEAT_FAIL_LIMIT", 3);
|
|
336
|
+
/**
|
|
337
|
+
* How a mid-turn message is put to the model (pure).
|
|
338
|
+
*
|
|
339
|
+
* The framing is the whole reason this exists. A bare user message appearing after a
|
|
340
|
+
* round of tool results is ambiguous: it reads as if it had always been the request, so
|
|
341
|
+
* the model either restarts on it or, having already been told to do something else,
|
|
342
|
+
* ignores it. Saying when it arrived resolves both, and the second sentence is what
|
|
343
|
+
* makes it a steer rather than an interruption — the work in flight is not thrown away.
|
|
344
|
+
*
|
|
345
|
+
* Applied when the request is BUILT, never stored. The transcript keeps what the person
|
|
346
|
+
* typed; see `steered` in `memory/types.ts`.
|
|
347
|
+
*/
|
|
348
|
+
export function steeredMessage(text) {
|
|
349
|
+
return (`The user sent this while you were working:\n\n${text}\n\n` +
|
|
350
|
+
`If it changes what you should be doing, change course now. Otherwise finish the ` +
|
|
351
|
+
`current step, then answer it before you stop.`);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* How a message sent straight after an interrupt is put to the model (pure).
|
|
355
|
+
*
|
|
356
|
+
* A different fact from a steer, and it has to read differently. The work was STOPPED —
|
|
357
|
+
* the model is not deciding whether to change course, that decision was made for it by
|
|
358
|
+
* the person who pressed the key. Telling it to "finish the current step" here would be
|
|
359
|
+
* telling it to resume the thing it was just stopped from doing.
|
|
360
|
+
*/
|
|
361
|
+
export function interruptedMessage(text) {
|
|
362
|
+
return (`The user stopped you and sent this:\n\n${text}\n\n` +
|
|
363
|
+
`The work you were doing was cut off on purpose. Take it from here rather than ` +
|
|
364
|
+
`picking up where you left off, unless this asks you to.`);
|
|
365
|
+
}
|
|
366
|
+
/** The wire form of a message that did not simply arrive in its turn (pure). */
|
|
367
|
+
export function arrivalNote(arrival, text) {
|
|
368
|
+
return arrival === "steered" ? steeredMessage(text) : interruptedMessage(text);
|
|
369
|
+
}
|
|
314
370
|
/** True if an error is an AbortError (the model call was cancelled). */
|
|
315
371
|
function isAbort(error) {
|
|
316
372
|
return error instanceof Error && error.name === "AbortError";
|
|
@@ -423,6 +479,10 @@ directoryNotes = []) {
|
|
|
423
479
|
// since it was attached simply isn't in the payload map. Either way the model is
|
|
424
480
|
// TOLD rather than quietly handed a message that claims an image it cannot see.
|
|
425
481
|
const refs = e.role === "user" ? e.images : undefined;
|
|
482
|
+
// A message that arrived mid-turn is framed HERE rather than being stored framed,
|
|
483
|
+
// so the transcript keeps what the person typed and only the wire carries the
|
|
484
|
+
// explanation. Deterministic from the entry, so the cached prefix is unaffected.
|
|
485
|
+
const said = e.role === "user" && e.arrival ? arrivalNote(e.arrival, e.content) : e.content;
|
|
426
486
|
if (refs && refs.length > 0) {
|
|
427
487
|
const images = [];
|
|
428
488
|
const missing = [];
|
|
@@ -447,13 +507,13 @@ directoryNotes = []) {
|
|
|
447
507
|
: []),
|
|
448
508
|
...(missing.length > 0 ? [`${missing.join(", ")} could not be read from disk`] : []),
|
|
449
509
|
];
|
|
450
|
-
const content = notes.length > 0 ? `${
|
|
510
|
+
const content = notes.length > 0 ? `${said}
|
|
451
511
|
|
|
452
|
-
[${notes.join("; ")}]` :
|
|
512
|
+
[${notes.join("; ")}]` : said;
|
|
453
513
|
messages.push({ role: "user", content, ...(images.length > 0 ? { images } : {}) });
|
|
454
514
|
continue;
|
|
455
515
|
}
|
|
456
|
-
messages.push({ role: "user", content:
|
|
516
|
+
messages.push({ role: "user", content: said });
|
|
457
517
|
}
|
|
458
518
|
else if (e.role === "assistant") {
|
|
459
519
|
messages.push({
|
|
@@ -715,7 +775,7 @@ async function respondTurn(session, options = {}) {
|
|
|
715
775
|
];
|
|
716
776
|
};
|
|
717
777
|
const lookup = (name) => findTool(name) ?? mcpTurn?.asTool(name);
|
|
718
|
-
const stepLimit = options.maxSteps
|
|
778
|
+
const stepLimit = resolveStepLimit(options.maxSteps, process.env["MINDWEAVE_STEP_BUDGET"]);
|
|
719
779
|
// Sinks the spawn_subagent tool reuses (it only ever gets the ToolContext, not the
|
|
720
780
|
// Session): fork a scoped child, forward the child's usage to this turn's meter,
|
|
721
781
|
// and share this turn's abort signal so Esc stops a sub-agent too.
|
|
@@ -755,8 +815,8 @@ async function respondTurn(session, options = {}) {
|
|
|
755
815
|
await sweepSessionMemory(session, options);
|
|
756
816
|
// Background shells that finished since last turn — surfaced to the model once.
|
|
757
817
|
const bgEvents = await backgroundEventNotes(session);
|
|
758
|
-
// Per-task guards: cost
|
|
759
|
-
// call's usage is summed so
|
|
818
|
+
// Per-task guards: cost and time ceilings, both opt-in, like the step ceiling above
|
|
819
|
+
// them. Every call's usage is summed so a ceiling reflects the whole task.
|
|
760
820
|
const limits = taskLimits();
|
|
761
821
|
const startedAt = Date.now();
|
|
762
822
|
const usages = [];
|
|
@@ -850,7 +910,10 @@ async function respondTurn(session, options = {}) {
|
|
|
850
910
|
// The turn's model↔tool loop. Kept as a closure so the try/finally above owns
|
|
851
911
|
// every exit path; it reads the flags/usages declared in the enclosing scope.
|
|
852
912
|
async function runTurn() {
|
|
853
|
-
|
|
913
|
+
// No ceiling means no ceiling: the loop's exits are then its own returns — the model
|
|
914
|
+
// finishing, a stop reason, the repeated-failure breaker, a cost or time ceiling if one
|
|
915
|
+
// is set, and the abort check on the line below. See `resolveStepLimit`.
|
|
916
|
+
for (let step = 0; stepLimit === undefined || step < stepLimit; step++) {
|
|
854
917
|
if (options.signal?.aborted)
|
|
855
918
|
return interrupted(session);
|
|
856
919
|
// Stop before another (billable) call if a cost/time ceiling is hit — pause
|
|
@@ -1216,7 +1279,13 @@ async function respondTurn(session, options = {}) {
|
|
|
1216
1279
|
// rather than fixing an observed bug, and the next tool added inherits it.
|
|
1217
1280
|
let result;
|
|
1218
1281
|
try {
|
|
1219
|
-
|
|
1282
|
+
// A per-call channel, so a tool that runs for minutes can say what it is doing.
|
|
1283
|
+
// Scoped to THIS call rather than hung on the shared context, which every tool in
|
|
1284
|
+
// the turn holds the same instance of — with two commands in flight there would be
|
|
1285
|
+
// no way to tell whose output was whose.
|
|
1286
|
+
result = await tool.execute(parseArgs(call.arguments), session.toolContext, {
|
|
1287
|
+
progress: (text) => options.onEvent?.({ type: "tool", phase: "progress", id: call.id, text }),
|
|
1288
|
+
});
|
|
1220
1289
|
}
|
|
1221
1290
|
catch (error) {
|
|
1222
1291
|
// An abort is the user, not a fault: let it travel so the loop's own handling
|
|
@@ -1237,6 +1306,7 @@ async function respondTurn(session, options = {}) {
|
|
|
1237
1306
|
images: result.images,
|
|
1238
1307
|
displayKind: result.displayKind,
|
|
1239
1308
|
displayName: result.displayName,
|
|
1309
|
+
awaitsModel: result.awaitsModel,
|
|
1240
1310
|
};
|
|
1241
1311
|
};
|
|
1242
1312
|
// Emit each tool's END the instant IT finishes — not batched after the whole
|
|
@@ -1532,12 +1602,48 @@ async function respondTurn(session, options = {}) {
|
|
|
1532
1602
|
await options.persist?.();
|
|
1533
1603
|
}
|
|
1534
1604
|
}
|
|
1605
|
+
// ── The user typed while this was running ────────────────────────────────
|
|
1606
|
+
//
|
|
1607
|
+
// LAST in the round, and that placement is the design. The tool results are all
|
|
1608
|
+
// recorded, so the conversation is well-formed and a user message may follow it —
|
|
1609
|
+
// one arriving between a call and its result is malformed and every provider
|
|
1610
|
+
// rejects it. Being last also puts the person's words closest to the next call,
|
|
1611
|
+
// which is where a change of course has to be read.
|
|
1612
|
+
//
|
|
1613
|
+
// Only reached on a round that ran tools. A round where the model answered instead
|
|
1614
|
+
// returns above, and the turn is over: what is queued then is not a steer, it is the
|
|
1615
|
+
// next request, and the caller sends it as one.
|
|
1616
|
+
if (options.steer) {
|
|
1617
|
+
let arrived = [];
|
|
1618
|
+
try {
|
|
1619
|
+
arrived = await options.steer();
|
|
1620
|
+
}
|
|
1621
|
+
catch {
|
|
1622
|
+
// Resolving an attachment can fail (a dropped file deleted since it was typed).
|
|
1623
|
+
// The turn is not the place to die for it: the message stays queued and the
|
|
1624
|
+
// caller sends it at the end, where it can report the fault to the user.
|
|
1625
|
+
arrived = [];
|
|
1626
|
+
}
|
|
1627
|
+
for (const message of arrived) {
|
|
1628
|
+
session.transcript.push({
|
|
1629
|
+
role: "user",
|
|
1630
|
+
content: message.content,
|
|
1631
|
+
arrival: "steered",
|
|
1632
|
+
...(message.images && message.images.length > 0 ? { images: message.images } : {}),
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
if (arrived.length > 0)
|
|
1636
|
+
await options.persist?.();
|
|
1637
|
+
}
|
|
1535
1638
|
}
|
|
1536
|
-
//
|
|
1537
|
-
//
|
|
1538
|
-
//
|
|
1539
|
-
//
|
|
1540
|
-
//
|
|
1639
|
+
// A ceiling was set and has been reached without the model finishing. Reached only in
|
|
1640
|
+
// that case: with no ceiling the loop condition never ends it.
|
|
1641
|
+
//
|
|
1642
|
+
// Don't spend another call forcing a (misleading) wrap-up the way a tools-off final
|
|
1643
|
+
// turn would — that reads as "done" when it isn't. Pause cleanly instead: the
|
|
1644
|
+
// transcript, task list, and working set are all intact, so telling Mindweave to
|
|
1645
|
+
// continue resumes exactly here with nothing lost, and the user stays in control of
|
|
1646
|
+
// the spend.
|
|
1541
1647
|
return pauseTask(session, options, `reached the step budget of ${stepLimit} tool steps in one turn`);
|
|
1542
1648
|
}
|
|
1543
1649
|
}
|