mixdog 0.9.108 → 0.9.109
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/package.json +1 -1
- package/src/output-styles/detailed.md +14 -11
- package/src/output-styles/extreme-minimal.md +2 -4
- package/src/output-styles/minimal.md +6 -7
- package/src/output-styles/simple.md +11 -12
- package/src/rules/agent/30-explorer.md +19 -28
- package/src/rules/agent/41-cycle2-agent.md +1 -2
- package/src/rules/agent/42-cycle3-agent.md +3 -4
- package/src/rules/shared/01-tool.md +30 -24
- package/src/runtime/agent/orchestrator/session/result-classification.mjs +20 -25
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +18 -20
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +11 -7
- package/src/tui/session/tool-result-status.mjs +5 -12
- package/src/tui/session/tool-result-text.mjs +7 -5
package/package.json
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: detailed
|
|
3
3
|
title: Detailed
|
|
4
|
-
description:
|
|
4
|
+
description: Claude Code default-depth responses
|
|
5
5
|
aliases: verbose, full
|
|
6
6
|
keep-coding-instructions: true
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Output Style
|
|
10
10
|
|
|
11
|
-
Detailed —
|
|
11
|
+
Detailed — a teammate's update; clarity outranks terseness.
|
|
12
12
|
|
|
13
|
-
- Outcome first
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
- Outcome first, then what a cold reader needs: complete user-language
|
|
14
|
+
sentences, expanded jargon, rationale where it adds value; no filler or
|
|
15
|
+
process narration.
|
|
16
|
+
- Structure matches complexity: plain prose for simple answers; headers,
|
|
17
|
+
bullets, or tables (short enumerable facts only, explained outside) when
|
|
18
|
+
they aid scanning.
|
|
19
|
+
- No hard cap, but brevity first: the shortest report understood without
|
|
20
|
+
rereads (~10 lines is plenty for most tasks); expand only when complexity
|
|
21
|
+
demands; trivial results stay 1–2 sentences.
|
|
22
|
+
- Cite `file:line`; snippets only when load-bearing.
|
|
23
|
+
- Never dump raw tool output; blockers and failures in one clause each.
|
|
24
|
+
- Never name this style unless asked.
|
|
@@ -10,7 +10,5 @@ keep-coding-instructions: true
|
|
|
10
10
|
|
|
11
11
|
Extreme minimal — exactly one sentence under 100 characters.
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
- Prefer `<target> changed.` Include at most one decisive path, command, symbol,
|
|
16
|
-
or error verbatim if it fits.
|
|
13
|
+
- Net result only; no second sentence, list, heading, label, or follow-up.
|
|
14
|
+
- At most one decisive path, command, symbol, or error verbatim if it fits.
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: minimal
|
|
3
3
|
title: Minimal
|
|
4
|
-
description:
|
|
4
|
+
description: Net result within 400 characters
|
|
5
5
|
keep-coding-instructions: true
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Output Style
|
|
9
9
|
|
|
10
|
-
Minimal —
|
|
10
|
+
Minimal — net result in plain sentences; hard cap 400 characters.
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
API, code fragment, or error verbatim.
|
|
12
|
+
- Concept level only: no headings, bullets, labels, or per-file detail, even
|
|
13
|
+
for reports.
|
|
14
|
+
- Only decisive paths, commands, symbols, or errors verbatim; user-language
|
|
15
|
+
sentences; never name this style unless asked.
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: simple
|
|
3
3
|
title: Simple
|
|
4
|
-
description:
|
|
4
|
+
description: Lightly trimmed default-style updates within 800 characters
|
|
5
5
|
aliases: concise, handoff
|
|
6
6
|
keep-coding-instructions: true
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Output Style
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Simple — a teammate's update, tightened; hard cap 800 characters.
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this style unless asked.
|
|
13
|
+
- Outcome first, then only what the reader needs: complete user-language
|
|
14
|
+
sentences, no filler or process narration.
|
|
15
|
+
- Structure matches complexity: plain prose by default; bullets or compact
|
|
16
|
+
tables only when they aid scanning.
|
|
17
|
+
- Prefer the tighter phrasing: each material fact once, secondary detail and
|
|
18
|
+
non-load-bearing rationale dropped; `file:line` anchors, technical literals
|
|
19
|
+
verbatim; the cap is a ceiling, not a target.
|
|
20
|
+
- Never dump raw tool output; blockers and failures in one clause each.
|
|
21
|
+
Never name this style unless asked.
|
|
@@ -13,40 +13,31 @@ add no rules or exceptions here.
|
|
|
13
13
|
|
|
14
14
|
## Hard budget
|
|
15
15
|
|
|
16
|
-
Before EVERY tool call, check:
|
|
17
|
-
1. Which requested targets still lack a complete direct anchor set?
|
|
18
|
-
2. Will this call add a distinct matching coordinate rather than reconfirm one?
|
|
19
|
-
|
|
20
|
-
A target is complete only when every distinct coordinate directly satisfying
|
|
21
|
-
its query is held; one anchor suffices only when the target is singular by
|
|
22
|
-
construction. If all targets are complete, or the call only reconfirms,
|
|
23
|
-
re-reads, verifies, quotes, strengthens, or adds context, answer now.
|
|
24
|
-
|
|
25
16
|
Target: ONE tool turn and an answer within 10 seconds.
|
|
26
17
|
Hard limit: FIVE tool turns plus ONE tool-less final-report turn. Label tool
|
|
27
|
-
messages `turn 1/6` through `turn 5/6
|
|
28
|
-
`turn 6/6
|
|
18
|
+
messages `turn 1/6` through `turn 5/6`; the response after turn 5 is
|
|
19
|
+
`turn 6/6`, the FINAL REPORT TURN: no tools, report the credible anchors
|
|
20
|
+
currently held, or `EXPLORATION_FAILED` if none exist.
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
A target is complete only when every distinct coordinate directly satisfying
|
|
23
|
+
its query is held; one anchor suffices only when the target is singular by
|
|
24
|
+
construction. Before EVERY tool call, check which targets still lack a
|
|
25
|
+
complete direct anchor set and whether the call adds a distinct matching
|
|
26
|
+
coordinate; once every target is complete, answer immediately — never spend
|
|
27
|
+
a turn merely because budget remains.
|
|
32
28
|
|
|
33
|
-
Turns 2-5 are ONLY for incomplete targets
|
|
29
|
+
Turns 2-5 are ONLY for incomplete targets: each recovery turn uses changed
|
|
34
30
|
concrete tokens or a new exact scope in maximum fanout. Page only when output
|
|
35
|
-
explicitly reports truncation or incompleteness; never repeat tokens and
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
`EXPLORATION_FAILED`.
|
|
39
|
-
|
|
40
|
-
After turn 5, stop tools unconditionally. Turn 6 (`turn 6/6`) is the FINAL
|
|
41
|
-
REPORT TURN and the last turn: report the credible anchors currently held; if
|
|
42
|
-
none exist, return `EXPLORATION_FAILED`. There is no sixth tool turn.
|
|
31
|
+
explicitly reports truncation or incompleteness; never repeat tokens and
|
|
32
|
+
scope. If the next turn lacks a concrete anchor-producing move, stop early
|
|
33
|
+
with `EXPLORATION_FAILED`.
|
|
43
34
|
|
|
44
35
|
## No reconfirmation
|
|
45
36
|
|
|
46
37
|
A credible tool-returned coordinate is FINAL. Never re-locate, re-read,
|
|
47
|
-
reconfirm, verify, upgrade, cross-check, or
|
|
48
|
-
turn. Copy paths and coordinates exactly; never repair,
|
|
49
|
-
or recall them.
|
|
38
|
+
reconfirm, verify, upgrade, cross-check, quote, or strengthen it through
|
|
39
|
+
another tool or turn. Copy paths and coordinates exactly; never repair,
|
|
40
|
+
normalize, estimate, or recall them.
|
|
50
41
|
|
|
51
42
|
A code anchor requires a tool-returned `path:line`; a bare path is valid only
|
|
52
43
|
for a file/dir-location query. Generic matches and guessed coordinates are
|
|
@@ -57,8 +48,8 @@ Return one compact line per distinct direct match:
|
|
|
57
48
|
|
|
58
49
|
Use no fixed item-count cap; omit incidental matches and prose. For a
|
|
59
50
|
completeness/list/count query, copy EVERY returned matching `path:line` once
|
|
60
|
-
and preserve the tool-reported total
|
|
51
|
+
and preserve the tool-reported total; never omit a direct match or page after
|
|
61
52
|
a complete result.
|
|
62
53
|
|
|
63
|
-
Return `EXPLORATION_FAILED` when the budget cannot produce a credible anchor
|
|
64
|
-
|
|
54
|
+
Return `EXPLORATION_FAILED` when the budget cannot produce a credible anchor;
|
|
55
|
+
never fabricate, soften, or return vague prose.
|
|
@@ -41,5 +41,4 @@ Use only input IDs; never invent IDs. `update` supplies fresh `element` and a
|
|
|
41
41
|
uses only one `project_id`. Summaries are complete sentences in input language,
|
|
42
42
|
preserve important specifics verbatim, and omit actor/meta filler. Category
|
|
43
43
|
priority: `rule > constraint > decision > fact > goal > preference > task >
|
|
44
|
-
issue`. Replace literal `|` with `/`; fields contain no newlines.
|
|
45
|
-
verdict with a digit.
|
|
44
|
+
issue`. Replace literal `|` with `/`; fields contain no newlines.
|
|
@@ -28,7 +28,6 @@ Verbose durable is always `update`, never `keep`.
|
|
|
28
28
|
`<id>|merge|<target_id>|<source_ids_csv>`
|
|
29
29
|
`<id>|delete`
|
|
30
30
|
|
|
31
|
-
IDs match input rows; never invent them. An `update`
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
fields contain no newlines. Emit a digit-starting verdict for every input row.
|
|
31
|
+
IDs match input rows; never invent them. An `update` supplies a compact
|
|
32
|
+
`element`. A `merge` retains `target_id`, absorbs sources, and stays within
|
|
33
|
+
one `project_id`. Replace literal `|` with `/`; fields contain no newlines.
|
|
@@ -2,31 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
- Baseline routing assigns each facet directly by the evidence needed to
|
|
4
4
|
determine the complete edit:
|
|
5
|
-
path/name only→`find`; wildcard paths→`glob`; exact directory
|
|
5
|
+
path/name only→`find`; wildcard/recursive paths→`glob`; exact directory
|
|
6
|
+
entries→`list`;
|
|
6
7
|
source content/value/`path:line`→`grep`; exact symbol/relation→`code_graph`;
|
|
7
8
|
known file/range→`read`;
|
|
8
9
|
web/current→`search`; returned URL body→`web_fetch`; prior work→`recall`;
|
|
9
10
|
durable compact English memory→`memory`; explicit project change→`cwd`;
|
|
10
|
-
explicit user-requested conversation reset→`session_manage
|
|
11
|
+
explicit user-requested conversation reset→`session_manage`.
|
|
11
12
|
Use only named tools present in the current tool surface.
|
|
12
13
|
`explore`, when exposed, is a fast path only for facets whose repository
|
|
13
14
|
coordinates remain unknown: call it first once for all such independent
|
|
14
|
-
facets in one query array. It
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
15
|
+
facets in one query array. It returns the minimal complete direct
|
|
16
|
+
`path:line` anchors, not analysis or solutions; resume baseline routing
|
|
17
|
+
from those anchors.
|
|
18
|
+
- Act only on verified identities (cwd/project/user/tool-returned) — paths,
|
|
19
|
+
module specifiers, symbols, data/record shapes alike; a guessed identity is
|
|
20
|
+
itself a facet, verified by the cheapest batched probe (one lookup or sample
|
|
21
|
+
record) before anything depends on it. Within the current project, pass
|
|
18
22
|
project-relative paths and omit optional scopes equal to its root; explicit
|
|
19
|
-
paths may be outside cwd only for targets outside the project
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
paths may be outside cwd only for targets outside the project.
|
|
24
|
+
- A conclusive result ends its facet, and known state — task/brief-supplied
|
|
25
|
+
facts, returned content, and the effects of your own successful calls — is
|
|
26
|
+
never re-acquired: never broaden, repeat, or reconfirm. Follow up only when
|
|
27
|
+
prior output is needed to form the next call; on failure rerun only the
|
|
28
|
+
failed check.
|
|
29
|
+
Batch calls iff no call needs another's output (as input or to decide its
|
|
30
|
+
need/scope) or can change another's inputs/state; otherwise serialize, and
|
|
31
|
+
drop a call whose deciding evidence already suffices. Before each retrieval
|
|
32
|
+
batch, deduplicate every facet the task still requires, route each once to
|
|
33
|
+
the cheapest sufficient tool with all required variants/scopes, and launch
|
|
34
|
+
every independent call together. Never
|
|
35
|
+
split one decision across overlapping facets, duplicate/broaden a facet
|
|
36
|
+
through another tool, add `shell`/`apply_patch` mutation merely to widen
|
|
37
|
+
retrieval, reserve known work, or cap fanout.
|
|
30
38
|
Take the cheapest sufficient evidence per facet:
|
|
31
39
|
symbol relations end at `code_graph`, values/locations end at the context
|
|
32
40
|
grep returns; `read` covers only what returned spans cannot, as an anchored
|
|
@@ -35,12 +43,10 @@
|
|
|
35
43
|
evidence determines the edit, stop retrieving and patch.
|
|
36
44
|
- Once the edit is determined, finish in one assistant turn: one
|
|
37
45
|
`apply_patch` per file or cohesive unit, all patches first, then one batched
|
|
38
|
-
verification `shell`
|
|
39
|
-
the shell if any fails. Retry only failed envelopes.
|
|
40
|
-
with `apply_patch`, never `shell`.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
facet, batched with the rest. Follow up only when prior output is required
|
|
44
|
-
to form the next call.
|
|
46
|
+
verification `shell` for required postconditions only; runtime waits
|
|
47
|
+
for every patch and skips the shell if any fails. Retry only failed envelopes.
|
|
48
|
+
Create or edit text only with `apply_patch`, never `shell`. Earlier `shell`
|
|
49
|
+
is only for executable/runtime/state evidence unavailable to file tools—an
|
|
50
|
+
independent facet, batched with the rest.
|
|
45
51
|
- A background `task_id` ends the turn; completion resumes work. Never poll;
|
|
46
52
|
use task control only for recovery or a required blocking result.
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* Matches documented tool-return error conventions:
|
|
10
10
|
* "Error: ..." — Node/MCP tool errors (grep, find_symbol, read, code_graph, etc.)
|
|
11
11
|
* "Error: [shell-tool-failed] ..." — shell tool/control-plane failure
|
|
12
|
-
* "Error: [shell-run-failed] ..." — shell
|
|
12
|
+
* "Error: [shell-run-failed] ..." — interrupted shell execution
|
|
13
13
|
* "Error [code N]:" — structured builtin tool errors
|
|
14
14
|
* "[error ..." — bracketed error format
|
|
15
|
-
* "[exit code: ..." —
|
|
15
|
+
* "[exit code: ..." — normal completed shell command result
|
|
16
16
|
*
|
|
17
17
|
* Leading whitespace is stripped before testing (trimStart). Mid-body
|
|
18
18
|
* occurrences of these patterns are NOT treated as errors — only the very
|
|
@@ -62,7 +62,7 @@ export function classifyResultKind(result, explicitSuccess = false) {
|
|
|
62
62
|
if (explicitSuccess === true) return 'normal';
|
|
63
63
|
if (typeof result !== 'string') return 'normal';
|
|
64
64
|
const trimmed = result.trimStart();
|
|
65
|
-
if (/^error(?:\s+\[code\b|\s*:)/i.test(trimmed) || /^\[error/i.test(trimmed)
|
|
65
|
+
if (/^error(?:\s+\[code\b|\s*:)/i.test(trimmed) || /^\[error/i.test(trimmed)) return 'error';
|
|
66
66
|
for (const prefix of ZERO_MATCH_PREFIXES) {
|
|
67
67
|
if (trimmed.startsWith(prefix)) return 'zero-match';
|
|
68
68
|
}
|
|
@@ -98,14 +98,9 @@ export function isInformationalShellExitOne(result) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
|
-
* Shell-
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* `[exit code: N]` / `[timeout: …]` / `[signal: …]` marker — optionally behind
|
|
105
|
-
* ⚠️ destructive-warning lines, which are prepended AFTER the error prefix is
|
|
106
|
-
* composed. A generic leading `Error:` is NOT a shell failure: it is just as
|
|
107
|
-
* likely the command's own stdout, which is why classifyResultKind (correct
|
|
108
|
-
* for tools that own the `Error:` convention) over-reports on shell output.
|
|
101
|
+
* Shell TOOL/control-plane failure test. A completed process exit is not a
|
|
102
|
+
* tool failure regardless of its exit code or output. Only tool markers and
|
|
103
|
+
* interrupted execution (timeout/signal/abort) count.
|
|
109
104
|
*
|
|
110
105
|
* @param {unknown} result
|
|
111
106
|
* @returns {boolean}
|
|
@@ -113,8 +108,14 @@ export function isInformationalShellExitOne(result) {
|
|
|
113
108
|
export function isShellFailureResult(result) {
|
|
114
109
|
if (typeof result !== 'string') return false;
|
|
115
110
|
const body = result.replace(/^(?:\s*⚠️[^\n]*\n)+/, '').trimStart();
|
|
116
|
-
if (/^error:\s*\[shell-
|
|
117
|
-
|
|
111
|
+
if (/^error:\s*\[shell-tool-failed\]/i.test(body)) return true;
|
|
112
|
+
if (/^error:\s*\[shell-run-failed\]/i.test(body)) {
|
|
113
|
+
const header = body.split('\n', 1)[0] || '';
|
|
114
|
+
if (/\[exit code:/i.test(header)
|
|
115
|
+
&& !/\[timeout:|\[signal:|timed out|aborted|interrupted/i.test(header)) return false;
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return /^\[(?:timeout:|signal:)/i.test(body);
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
// Evidence that a command's OUTPUT reports a real failure. Used to separate a
|
|
@@ -147,17 +148,11 @@ export function shellOutputReportsFailure(text) {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* exit code, a signal, any stderr, an empty capture, or a failure banner —
|
|
154
|
-
* stays a failure.
|
|
151
|
+
* A process that started and completed produced a command result. Any non-zero
|
|
152
|
+
* exit code is legitimate tool output; timeout/signal remains an interrupted
|
|
153
|
+
* execution and tool/control-plane failures are filtered by the caller.
|
|
155
154
|
*/
|
|
156
|
-
export function isLegitimateShellExit({ exitCode, signal,
|
|
157
|
-
if (signal) return false;
|
|
158
|
-
|
|
159
|
-
if (String(stderr ?? '').trim()) return false;
|
|
160
|
-
const out = String(stdout ?? '').trim();
|
|
161
|
-
if (!out || out === '(no output)') return false;
|
|
162
|
-
return !shellOutputReportsFailure(out);
|
|
155
|
+
export function isLegitimateShellExit({ exitCode, signal, timedOut } = {}) {
|
|
156
|
+
if (signal || timedOut === true) return false;
|
|
157
|
+
return Number.isInteger(exitCode) && exitCode !== 0;
|
|
163
158
|
}
|
|
@@ -219,10 +219,9 @@ export function formatShellToolFailure(message) {
|
|
|
219
219
|
return `Error: [shell-tool-failed] ${text}`;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
// A
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
// channel for output that looks like an error and is not one.
|
|
222
|
+
// A completed non-zero process exit keeps its `[exit code: N]` marker but is
|
|
223
|
+
// not a TOOL failure. The explicit-success envelope preserves that structural
|
|
224
|
+
// distinction even when command output itself looks like an error.
|
|
226
225
|
function _finalizeShellResult(legitExit, text) {
|
|
227
226
|
return legitExit ? makeToolEnvelope(text, [], { explicitSuccess: true }) : text;
|
|
228
227
|
}
|
|
@@ -774,18 +773,19 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
774
773
|
const stderr = stripAnsi(result.stderr || '');
|
|
775
774
|
const failureStatus = _shellFailureStatus(result, timeout);
|
|
776
775
|
const { signal, exitCode, shellToolFailed } = failureStatus;
|
|
777
|
-
|
|
778
|
-
//
|
|
779
|
-
//
|
|
780
|
-
// `git diff --exit-code`-shaped checks). It keeps its `[exit code: 1]`
|
|
781
|
-
// marker so the caller sees the code, but it is NOT framed as a tool
|
|
782
|
-
// error and NOT classified as a failure — measured as the dominant
|
|
783
|
-
// shape among non-zero exits.
|
|
776
|
+
// The shell tool succeeded once it spawned and observed the process to
|
|
777
|
+
// completion. A non-zero process exit is command data — regardless of
|
|
778
|
+
// stderr or failure banners — and never a tool/control-plane failure.
|
|
784
779
|
const legitExit = !shellToolFailed
|
|
785
|
-
&&
|
|
786
|
-
|
|
780
|
+
&& isLegitimateShellExit({
|
|
781
|
+
exitCode,
|
|
782
|
+
signal,
|
|
783
|
+
timedOut: result.timedOut,
|
|
784
|
+
stdout,
|
|
785
|
+
stderr,
|
|
786
|
+
});
|
|
787
787
|
const shellRunFailed = !shellToolFailed
|
|
788
|
-
&& (!!signal ||
|
|
788
|
+
&& (!!signal || result.timedOut);
|
|
789
789
|
const isReallyErrored = shellToolFailed || shellRunFailed;
|
|
790
790
|
// Filter-swallow rescue: the tee file is ALWAYS consumed (deleted)
|
|
791
791
|
// here; its tail is attached only when the run failed with an empty
|
|
@@ -812,13 +812,11 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
812
812
|
? `[shell-tool-failed] ${statusDetail}`
|
|
813
813
|
: (shellRunFailed ? `[shell-run-failed] ${statusDetail}` : (legitExit ? statusDetail : ''));
|
|
814
814
|
const errorPrefix = isReallyErrored ? 'Error: ' : '';
|
|
815
|
-
// Three outcomes
|
|
816
|
-
// a
|
|
817
|
-
//
|
|
818
|
-
// one keeps its exit code but says so in words, so neither the model
|
|
819
|
-
// nor the failure log treats a finished report as a failure.
|
|
815
|
+
// Three outcomes: TOOL/control-plane failure, interrupted execution,
|
|
816
|
+
// and a process that completed (zero or non-zero). Completed non-zero
|
|
817
|
+
// exits keep their code but never carry Error:/shell-run-failed.
|
|
820
818
|
const completionNote = legitExit
|
|
821
|
-
? '\n[completed: the command
|
|
819
|
+
? '\n[completed: shell executed the command; its non-zero exit code and output are command results, not a tool failure]'
|
|
822
820
|
: '';
|
|
823
821
|
if (mergeStderr) {
|
|
824
822
|
// Post-exit concatenation. True chunk-level interleaving would
|
|
@@ -754,12 +754,6 @@ async function apply_patch(args, cwd, options = {}) {
|
|
|
754
754
|
if (abortSignal?.aborted) {
|
|
755
755
|
throw new Error(abortSignal.reason?.message || abortSignal.reason || 'apply_patch aborted');
|
|
756
756
|
}
|
|
757
|
-
const basePath = resolveBasePath(cwd, args?.base_path);
|
|
758
|
-
try {
|
|
759
|
-
await assertPathReachable(basePath);
|
|
760
|
-
} catch (err) {
|
|
761
|
-
return `Error: ${err?.message || String(err)}`;
|
|
762
|
-
}
|
|
763
757
|
// Write-root gate: a patch may only touch paths inside a DECLARED root —
|
|
764
758
|
// the session directory by default, or `root`/`base_path` when the caller
|
|
765
759
|
// names one. Anything outside is refused before a single byte is written, so
|
|
@@ -770,6 +764,15 @@ async function apply_patch(args, cwd, options = {}) {
|
|
|
770
764
|
if (explicitRoot && isFilesystemRootSpecifier(explicitRoot, cwd)) {
|
|
771
765
|
throw new Error(`apply_patch: refusing filesystem root as write root: ${normalizeOutputPath(explicitRoot)}`);
|
|
772
766
|
}
|
|
767
|
+
// A declared root is the coordinate frame as well as the boundary: relative
|
|
768
|
+
// section paths resolve against it (an internal `base_path` overrides the
|
|
769
|
+
// frame), so the refusal advice "set root" is sufficient on its own.
|
|
770
|
+
const basePath = resolveBasePath(cwd, args?.base_path || explicitRoot);
|
|
771
|
+
try {
|
|
772
|
+
await assertPathReachable(basePath);
|
|
773
|
+
} catch (err) {
|
|
774
|
+
return `Error: ${err?.message || String(err)}`;
|
|
775
|
+
}
|
|
773
776
|
const writeRoot = resolveBasePath(cwd, explicitRoot ?? args?.base_path ?? null);
|
|
774
777
|
if (explicitRoot) {
|
|
775
778
|
try {
|
|
@@ -792,7 +795,8 @@ async function apply_patch(args, cwd, options = {}) {
|
|
|
792
795
|
const more = shown.length > 3 ? ` (+${shown.length - 3} more)` : '';
|
|
793
796
|
throw new Error(
|
|
794
797
|
`apply_patch: ${shown.length} target(s) fall outside the write root ${normalizeOutputPath(writeRoot)}: ${head}${more}. `
|
|
795
|
-
+ 'Check the paths first; if intended, set root (JSON) or add "*** Root: <containing directory>" after "*** Begin Patch" (freeform)
|
|
798
|
+
+ 'Check the paths first; if intended, set root (JSON) or add "*** Root: <containing directory>" after "*** Begin Patch" (freeform); '
|
|
799
|
+
+ 'relative section paths then resolve against that root.',
|
|
796
800
|
);
|
|
797
801
|
}
|
|
798
802
|
const rejectPartial = args?.reject_partial !== false;
|
|
@@ -17,22 +17,15 @@ const CANCELLED_RESULT_STATUS_LINE = '[status: cancelled]';
|
|
|
17
17
|
|
|
18
18
|
// Detect a shell command that RAN but exited non-zero (a process exit code)
|
|
19
19
|
// as opposed to a real tool-call failure (`[shell-tool-failed]`) or a
|
|
20
|
-
// timeout/abort.
|
|
21
|
-
//
|
|
22
|
-
// `[
|
|
23
|
-
// exit code (>= 0) for a command-exit, or null otherwise.
|
|
20
|
+
// timeout/abort. New results use bare `[exit code: N]`; legacy transcripts may
|
|
21
|
+
// use `Error: [shell-run-failed] [exit code: N]`. Persistent shell results can
|
|
22
|
+
// prefix `[session: …]`. Returns the numeric exit code, or null otherwise.
|
|
24
23
|
export function shellCommandExitCode(text) {
|
|
25
24
|
const body = String(text || '');
|
|
26
|
-
|
|
27
|
-
// QUOTES the marker mid-output is never misclassified. bash-tool emits
|
|
28
|
-
// `Error: [shell-run-failed] [exit code: N]` as the leading marker header.
|
|
29
|
-
if (!/^\s*(?:Error:\s*)?\[shell-run-failed\]/i.test(body)) return null;
|
|
30
|
-
// Restrict marker parsing to the header region (first line) so only the
|
|
31
|
-
// session runtime-emitted status header — not quoted command output below — counts.
|
|
32
|
-
const header = body.split('\n', 1)[0] || '';
|
|
25
|
+
const header = body.split('\n').slice(0, 3).join('\n');
|
|
33
26
|
// Timeout / signal / abort are NOT a plain command exit — keep them "Failed".
|
|
34
27
|
if (/\[timeout:|\[signal:|timed out|aborted|interrupted/i.test(header)) return null;
|
|
35
|
-
const m = header.match(
|
|
28
|
+
const m = header.match(/^\s*(?:\[session:[^\n]*\]\s*\n)?(?:Error:\s*)?(?:\[shell-run-failed\]\s*)?\[exit code:\s*(\d+)\]/i);
|
|
36
29
|
if (!m) return null;
|
|
37
30
|
const code = Number(m[1]);
|
|
38
31
|
return Number.isFinite(code) ? code : null;
|
|
@@ -126,11 +126,13 @@ export function toolErrorDisplay(value, surface = 'tool') {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// Display-only: a command-exit card already renders `Exit N` as its state, so
|
|
129
|
-
// the machine header (`
|
|
130
|
-
// in the body
|
|
131
|
-
// the
|
|
132
|
-
// result keeps the header untouched; only the rendered body drops it.
|
|
129
|
+
// the machine header (`[exit code: N]`, or its legacy shell-run-failed form) is
|
|
130
|
+
// redundant in the body. The model-facing result keeps it; only display drops
|
|
131
|
+
// the header and explanatory completion note.
|
|
133
132
|
export function stripShellExitHeader(text) {
|
|
134
133
|
const body = String(text ?? '');
|
|
135
|
-
return body.replace(
|
|
134
|
+
return body.replace(
|
|
135
|
+
/(^|\n)(?:(?:Error:\s*)?\[shell-run-failed\]\s*)?\[exit code:\s*\d+\][^\n]*\n{0,2}(?:\[completed:[^\n]*\]\n{0,2})?/i,
|
|
136
|
+
'$1',
|
|
137
|
+
);
|
|
136
138
|
}
|