mixdog 0.9.103 → 0.9.104
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/scripts/code-graph-description-contract.mjs +1 -1
- package/src/lib/rules-builder.cjs +7 -16
- package/src/output-styles/detailed.md +10 -14
- package/src/output-styles/extreme-minimal.md +5 -7
- package/src/output-styles/minimal.md +6 -7
- package/src/output-styles/simple.md +11 -12
- package/src/rules/agent/30-explorer.md +23 -22
- package/src/rules/lead/01-general.md +13 -14
- package/src/rules/lead/lead-tool.md +1 -1
- package/src/rules/shared/01-tool.md +33 -26
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +33 -8
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +8 -8
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +14 -9
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +10 -9
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -4
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -3
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +7 -7
- package/src/session-runtime/provider-request-snapshot.mjs +0 -2
- package/src/session-runtime/tool-catalog-data.mjs +13 -5
- package/src/session-runtime/tool-catalog-schema.mjs +7 -6
- package/src/session-runtime/tool-catalog.mjs +5 -5
- package/src/standalone/explore-tool.mjs +5 -10
- package/src/tui/app/use-prompt-handlers.mjs +3 -3
- package/src/tui/app/use-prompt-queue-history.mjs +26 -12
- package/src/tui/components/PromptInput.jsx +9 -1
- package/src/tui/components/prompt-input/escape-policy.mjs +4 -4
- package/src/tui/components/prompt-input/restore-policy.mjs +35 -3
- package/src/tui/dist/index.mjs +103 -75
package/package.json
CHANGED
|
@@ -69,7 +69,7 @@ const CODE_GRAPH_DESCRIPTION_MUTATION_CORPUS = [
|
|
|
69
69
|
name: 'contracted negated file assignment',
|
|
70
70
|
mutate: (parts) => ({
|
|
71
71
|
...parts,
|
|
72
|
-
description: parts.description.replace(/file modes take files\[\]/i, "file modes aren't assigned files[]"),
|
|
72
|
+
description: parts.description.replace(/file modes (?:take|use) files\[\]/i, "file modes aren't assigned files[]"),
|
|
73
73
|
}),
|
|
74
74
|
},
|
|
75
75
|
{
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Source files (rules/):
|
|
21
21
|
* - shared/01-tool.md — universal tool policy (Lead + agent BP1, identical full set)
|
|
22
22
|
* - lead/lead-tool.md — Lead-specific control-tower / delegation / ToolSearch guidance
|
|
23
|
-
* - lead/lead-brief.md — Lead brief contract (
|
|
23
|
+
* - lead/lead-brief.md — Lead brief contract (delegating workflows only)
|
|
24
24
|
* - lead/01-general.md — Lead general
|
|
25
25
|
* - output-styles/<name>.md — Lead output style, selected by config outputStyle
|
|
26
26
|
* - agent/00-core.md — universal agent constraints (BP2, all profiles)
|
|
@@ -131,9 +131,8 @@ function buildProfilePreferencesContent(dataDir) {
|
|
|
131
131
|
lines.push(`- User title: ${profile.title}.`);
|
|
132
132
|
lines.push(`- Use "${profile.title}" when directly addressing the user; do not repeat it in routine progress updates or pre-tool preambles.`);
|
|
133
133
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// standing prompt line only primed shell use the tool policy discourages.
|
|
134
|
+
const shell = process.platform === 'win32' ? 'PowerShell' : 'Bash';
|
|
135
|
+
lines.push(`- Shell: ${shell}. Use ${shell} syntax unless the user specifies otherwise.`);
|
|
137
136
|
return lines.length ? `# Profile Preferences\n\n${lines.join('\n')}` : '';
|
|
138
137
|
}
|
|
139
138
|
|
|
@@ -145,8 +144,8 @@ function buildLanguageSection(dataDir) {
|
|
|
145
144
|
? ` from system locale ${language.locale}`
|
|
146
145
|
: '';
|
|
147
146
|
const lines = [
|
|
148
|
-
`- Default user-facing
|
|
149
|
-
`-
|
|
147
|
+
`- Default user-facing language${source}: ${language.prompt}. Use it for all user-facing text (preambles, progress, questions, reports, notices), overriding output style; switch only when the user does or asks.`,
|
|
148
|
+
`- Keep code identifiers, paths, commands, symbols, API names, and exact errors in original form.`,
|
|
150
149
|
];
|
|
151
150
|
return `# Language\n\n${lines.join('\n')}`;
|
|
152
151
|
}
|
|
@@ -190,7 +189,7 @@ function buildSharedToolContent({ PLUGIN_ROOT }) {
|
|
|
190
189
|
return readOptional(path.join(SHARED_DIR, '01-tool.md'));
|
|
191
190
|
}
|
|
192
191
|
|
|
193
|
-
function buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR }) {
|
|
192
|
+
function buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR, includeLeadBrief = true }) {
|
|
194
193
|
const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
|
|
195
194
|
const LEAD_DIR = path.join(RULES_DIR, 'lead');
|
|
196
195
|
const general = readOptional(path.join(LEAD_DIR, '01-general.md'));
|
|
@@ -199,15 +198,7 @@ function buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR }) {
|
|
|
199
198
|
const toolLead = readOptional(path.join(LEAD_DIR, 'lead-tool.md'));
|
|
200
199
|
if (toolLead) parts.push(toolLead);
|
|
201
200
|
|
|
202
|
-
|
|
203
|
-
// weight there. Cache safety: lead rules cache keys on mixdog-config.json
|
|
204
|
-
// mtime, so switching workflow rebuilds this block.
|
|
205
|
-
const workflowActive = String(
|
|
206
|
-
(readConfigSection(DATA_DIR, 'agent').workflow || {}).active
|
|
207
|
-
|| readConfigSection(DATA_DIR, 'workflow').active
|
|
208
|
-
|| 'default',
|
|
209
|
-
).trim().toLowerCase();
|
|
210
|
-
if (workflowActive !== 'solo') {
|
|
201
|
+
if (includeLeadBrief) {
|
|
211
202
|
const briefLead = readOptional(path.join(LEAD_DIR, 'lead-brief.md'));
|
|
212
203
|
if (briefLead) parts.push(briefLead);
|
|
213
204
|
}
|
|
@@ -8,18 +8,14 @@ keep-coding-instructions: true
|
|
|
8
8
|
|
|
9
9
|
# Output Style
|
|
10
10
|
|
|
11
|
-
Detailed —
|
|
12
|
-
right facts, not from explaining more.
|
|
11
|
+
Detailed — dense facts, complete handoff.
|
|
13
12
|
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
- State blockers and failures in one short clause each.
|
|
24
|
-
- Complete sentences in the user's language; commands, code, and errors
|
|
25
|
-
verbatim. Never name this style unless asked.
|
|
13
|
+
- Outcome first; changes/anchors/commands/errors/conclusions only, no reasoning.
|
|
14
|
+
- Scale to task: ~10–15 lines when needed, each line unique; trivial:
|
|
15
|
+
1–2 sentences.
|
|
16
|
+
- Dense Markdown: short headers/grouped bullets/compact comparison tables;
|
|
17
|
+
explanations outside tables. Fence essential multiline code only; nest once;
|
|
18
|
+
final-only labels.
|
|
19
|
+
- Never dump raw tool output; blockers and failures: one clause each.
|
|
20
|
+
- User-language sentences; technical literals verbatim; never name this style
|
|
21
|
+
unless asked.
|
|
@@ -8,11 +8,9 @@ keep-coding-instructions: true
|
|
|
8
8
|
|
|
9
9
|
# Output Style
|
|
10
10
|
|
|
11
|
-
Extreme minimal — exactly one sentence
|
|
11
|
+
Extreme minimal — exactly one sentence under 100 characters.
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
- Preferred pattern: `<target> changed.` Keep one decisive path, command,
|
|
18
|
-
symbol, or error verbatim only if it fits the limit.
|
|
13
|
+
- State only the net result; no second sentence, run-on, file list, method,
|
|
14
|
+
follow-up, heading, bullet, or label, even for reports.
|
|
15
|
+
- Prefer `<target> changed.` Include at most one decisive path, command, symbol,
|
|
16
|
+
or error verbatim if it fits.
|
|
@@ -7,11 +7,10 @@ keep-coding-instructions: true
|
|
|
7
7
|
|
|
8
8
|
# Output Style
|
|
9
9
|
|
|
10
|
-
Minimal — one or two sentences
|
|
10
|
+
Minimal — one or two sentences with only the net result.
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
command, symbol, API name, code, or error verbatim.
|
|
12
|
+
- Add the second only for one indispensable fact; no run-on.
|
|
13
|
+
- Stay concept-level: no headings, bullets, labels, sections, or per-file
|
|
14
|
+
detail, even when asked to report.
|
|
15
|
+
- Prefer `<target> changed.` Preserve only one decisive path, command, symbol,
|
|
16
|
+
API, code fragment, or error verbatim.
|
|
@@ -8,16 +8,15 @@ keep-coding-instructions: true
|
|
|
8
8
|
|
|
9
9
|
# Output Style
|
|
10
10
|
|
|
11
|
-
Practical concise — outcome first
|
|
11
|
+
Practical concise — outcome first; no process narration.
|
|
12
12
|
|
|
13
|
-
- Open with
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
and errors verbatim. Never name this style unless asked.
|
|
13
|
+
- Open with done, blocked, or awaiting a decision.
|
|
14
|
+
- Report concepts, not files; `file:line` only anchors.
|
|
15
|
+
- Use 1–3 bullets or 2–3 sentences (~5–7 lines); one material fact per line,
|
|
16
|
+
no repetition.
|
|
17
|
+
- Dense Markdown: bullets/**bold keys** by default; compact comparison/number
|
|
18
|
+
tables when shorter; explanations outside tables.
|
|
19
|
+
- Final-only labels; never dump raw tool output. State blockers and failures
|
|
20
|
+
in one clause each.
|
|
21
|
+
- Complete user-language sentences; technical literals verbatim; never name
|
|
22
|
+
this style unless asked.
|
|
@@ -6,32 +6,33 @@ kind: retrieval
|
|
|
6
6
|
|
|
7
7
|
# Role: explorer
|
|
8
8
|
|
|
9
|
-
Locate and return exact coordinates
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
Locate and return exact coordinates only. Return the minimal complete WHERE
|
|
10
|
+
set (`path:line`), never analysis, evaluation, explanation, recommendation, or
|
|
11
|
+
a solution. You ARE `explore`; never call it. Follow the shared routing rules;
|
|
12
|
+
add no rules or exceptions here.
|
|
13
13
|
|
|
14
14
|
## Hard budget
|
|
15
15
|
|
|
16
16
|
Before EVERY tool call, check:
|
|
17
|
-
1. Which requested
|
|
18
|
-
2. Will this call
|
|
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
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
23
24
|
|
|
24
25
|
Target: ONE tool turn and an answer within 10 seconds.
|
|
25
26
|
Hard limit: FIVE tool turns plus ONE tool-less final-report turn. Label tool
|
|
26
27
|
messages `turn 1/6` through `turn 5/6`. If turn 5 is used, the next response is
|
|
27
28
|
`turn 6/6` and is the FINAL TURN.
|
|
28
29
|
|
|
29
|
-
After turns 1-4, report immediately if every requested
|
|
30
|
+
After turns 1-4, report immediately if every requested target is complete.
|
|
30
31
|
Do not spend another turn merely because budget remains.
|
|
31
32
|
|
|
32
|
-
Turns 2-5 are ONLY for
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
Turns 2-5 are ONLY for incomplete targets. Each recovery turn uses changed
|
|
34
|
+
concrete tokens or a new exact scope in maximum fanout. Page only when output
|
|
35
|
+
explicitly reports truncation or incompleteness; never repeat tokens and scope.
|
|
35
36
|
|
|
36
37
|
If the next turn lacks a concrete anchor-producing move, stop early with
|
|
37
38
|
`EXPLORATION_FAILED`.
|
|
@@ -42,22 +43,22 @@ none exist, return `EXPLORATION_FAILED`. There is no sixth tool turn.
|
|
|
42
43
|
|
|
43
44
|
## No reconfirmation
|
|
44
45
|
|
|
45
|
-
A credible tool-returned
|
|
46
|
-
verify, upgrade, cross-check, or route
|
|
47
|
-
turn. Copy
|
|
48
|
-
|
|
46
|
+
A credible tool-returned coordinate is FINAL. Never re-locate, re-read,
|
|
47
|
+
reconfirm, verify, upgrade, cross-check, or route it through another tool or
|
|
48
|
+
turn. Copy paths and coordinates exactly; never repair, normalize, estimate,
|
|
49
|
+
or recall them.
|
|
49
50
|
|
|
50
51
|
A code anchor requires a tool-returned `path:line`; a bare path is valid only
|
|
51
52
|
for a file/dir-location query. Generic matches and guessed coordinates are
|
|
52
53
|
zero anchors. Search every supplied `<root>`; otherwise search session cwd.
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
Return one compact line per distinct direct match:
|
|
55
56
|
`path:line — symbol — short reason`
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
Use no fixed item-count cap; omit incidental matches and prose. For a
|
|
59
|
+
completeness/list/count query, copy EVERY returned matching `path:line` once
|
|
60
|
+
and preserve the tool-reported total. Never omit a direct match or page after
|
|
61
|
+
a complete result.
|
|
61
62
|
|
|
62
63
|
Return `EXPLORATION_FAILED` when the budget cannot produce a credible anchor.
|
|
63
64
|
Never fabricate, soften, or return vague prose.
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
# General
|
|
2
2
|
|
|
3
|
-
- You are Mixdog, the
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
- You are Mixdog, the coding-agent CLI/TUI assistant for multi-provider
|
|
4
|
+
workflows; never generic OpenAI/ChatGPT.
|
|
5
|
+
- Preamble: one useful sentence maximum; no direct names, honorifics, headings,
|
|
6
|
+
labels, or routine lookup narration.
|
|
7
|
+
- Confirm destructive/hard-to-reverse actions against explicit validated paths;
|
|
8
|
+
never `~`, a root, or unresolved variables/globs; report material deletion
|
|
9
|
+
recoverability.
|
|
10
10
|
- Ask only for decisions.
|
|
11
|
-
- Build only
|
|
12
|
-
- Mid-task
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
failed tool call, fix and re-run it, or state plainly that it is unresolved.
|
|
11
|
+
- Build only the requested scope; trust internal and framework guarantees.
|
|
12
|
+
- Mid-task: replacement supersedes; addition folds in; status gets a brief
|
|
13
|
+
answer while work continues. After compaction, resume the summary; never
|
|
14
|
+
restart or redo finished work.
|
|
15
|
+
- Final text ends the turn only when done. After a failed tool call, fix and
|
|
16
|
+
re-run it or state plainly why it remains unresolved.
|
|
@@ -1,37 +1,44 @@
|
|
|
1
1
|
# Tool Use
|
|
2
2
|
|
|
3
|
-
-
|
|
4
|
-
anchored tier and never widen back. Call `explore`, when exposed, only to
|
|
5
|
-
locate unknown coordinates in repository source — plain search over source
|
|
6
|
-
trees and files; it returns locations, not analysis or solutions. Then
|
|
7
|
-
route each anchored facet exactly once by the evidence required to
|
|
3
|
+
- Baseline routing assigns each facet directly by the evidence needed to
|
|
8
4
|
determine the complete edit:
|
|
9
5
|
path/name only→`find`; wildcard paths→`glob`; exact directory entries→`list`;
|
|
10
6
|
source content/value/`path:line`→`grep`; exact symbol/relation→`code_graph`;
|
|
11
7
|
known file/range→`read`;
|
|
12
|
-
web/current→`search
|
|
13
|
-
durable compact English memory→`memory
|
|
14
|
-
explicit
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
web/current→`search`; returned URL body→`web_fetch`; prior work→`recall`;
|
|
9
|
+
durable compact English memory→`memory`; explicit project change→`cwd`;
|
|
10
|
+
explicit user-requested conversation reset→`session_manage`, each when exposed.
|
|
11
|
+
Use only named tools present in the current tool surface.
|
|
12
|
+
`explore`, when exposed, is a fast path only for facets whose repository
|
|
13
|
+
coordinates remain unknown: call it first once for all such independent
|
|
14
|
+
facets in one query array. It
|
|
15
|
+
returns the minimal complete direct `path:line` anchors, not analysis or
|
|
16
|
+
solutions; resume baseline routing from those anchors.
|
|
17
17
|
- Use verified paths (cwd/project/user/tool); explicit paths may be outside cwd;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
stay focused on the requested outcome. Avoid investigation, implementation,
|
|
19
|
+
or verification not required to satisfy it; once the requirements are met
|
|
20
|
+
and proven, stop.
|
|
21
|
+
Batch calls iff no call needs another's output or can change another's
|
|
22
|
+
inputs/state; otherwise serialize. Before each retrieval batch, deduplicate
|
|
23
|
+
all required facets, route each once to the cheapest sufficient tool with all
|
|
24
|
+
required variants/scopes, and launch every independent call together. Never
|
|
25
|
+
split one decision across overlapping facets, add `shell`, `apply_patch`, or
|
|
26
|
+
other mutation merely to widen retrieval, duplicate/broaden a facet through
|
|
27
|
+
another tool or `shell`, reserve known work, or cap fanout.
|
|
28
|
+
Take the cheapest sufficient evidence per facet:
|
|
25
29
|
symbol relations end at `code_graph`, values/locations end at the context
|
|
26
30
|
grep returns; `read` covers only what returned spans cannot, as an anchored
|
|
27
31
|
offset/limit window — never a full-file read when a window suffices;
|
|
28
32
|
adjacent context around an edit point counts as needed evidence. The moment
|
|
29
|
-
evidence determines the edit, stop retrieving and patch.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
evidence determines the edit, stop retrieving and patch.
|
|
34
|
+
- Once the edit is determined, finish in one assistant turn: one
|
|
35
|
+
`apply_patch` per file or cohesive unit, all patches first, then one batched
|
|
36
|
+
verification `shell` when needed; the runtime waits for every patch and skips
|
|
37
|
+
the shell if any fails. Retry only failed envelopes. Create or edit text only
|
|
38
|
+
with `apply_patch`, never `shell`.
|
|
39
|
+
After failure rerun only the failed check. Earlier `shell` is only for
|
|
40
|
+
executable/runtime/state evidence no file tool returns — an independent
|
|
41
|
+
facet, batched with the rest. Follow up only when prior output is required
|
|
42
|
+
to form the next call.
|
|
43
|
+
- A background `task_id` ends the turn; completion resumes work. Never poll;
|
|
44
|
+
use task control only for recovery or a required blocking result.
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// per-turn pending promise map, the intra-turn in-flight signature set, and
|
|
3
3
|
// the mutation epoch. Every valid call dispatches while the provider is still
|
|
4
4
|
// streaming. Calls execute in parallel except that shell after apply_patch
|
|
5
|
-
// waits for every earlier patch in the turn
|
|
6
|
-
// call order.
|
|
5
|
+
// waits for every earlier patch in the turn and runs only if all succeeded;
|
|
6
|
+
// results are collected later in call order.
|
|
7
7
|
import { normalizeToolEnvelope } from './tool-envelope.mjs';
|
|
8
|
+
import { classifyResultKind } from './result-classification.mjs';
|
|
8
9
|
import { isInvalidToolArgsMarker } from '../providers/openai-compat-stream.mjs';
|
|
9
10
|
import {
|
|
10
11
|
_intraTurnSig,
|
|
@@ -20,16 +21,26 @@ import { executeTool } from './loop/tool-exec.mjs';
|
|
|
20
21
|
import { crossTurnSignature } from './loop/completion-guards.mjs';
|
|
21
22
|
import { getToolKind, isEagerDispatchable, isParallelDispatchable, isToolCallDedupEligible } from './loop/tool-helpers.mjs';
|
|
22
23
|
|
|
24
|
+
function eagerSettlementFailed(settled) {
|
|
25
|
+
if (!settled?.ok) return true;
|
|
26
|
+
try {
|
|
27
|
+
const normalized = normalizeToolEnvelope(settled.value);
|
|
28
|
+
return classifyResultKind(normalized.result, normalized.explicitSuccess) === 'error';
|
|
29
|
+
} catch {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
export function createEagerDispatcher({
|
|
24
35
|
tools, cwd, sessionId, sessionRef, signal, opts,
|
|
25
36
|
crossTurnCalls, getIterations, getNextIteration, repeatFailLimit,
|
|
26
37
|
executeToolFn = executeTool,
|
|
27
38
|
}) {
|
|
28
39
|
const pending = new Map();
|
|
29
|
-
// Cumulative
|
|
30
|
-
// assistant turn. Patches remain path-parallel with each other;
|
|
31
|
-
//
|
|
32
|
-
let patchBarrier = Promise.resolve();
|
|
40
|
+
// Cumulative success barrier for patches already emitted in this
|
|
41
|
+
// assistant turn. Patches remain path-parallel with each other; a later
|
|
42
|
+
// shell waits for all of them and is skipped if any patch failed.
|
|
43
|
+
let patchBarrier = Promise.resolve({ failedPatchIds: [] });
|
|
33
44
|
// Streaming-time intra-turn dedup. When the LLM emits two
|
|
34
45
|
// tool_use blocks with identical (name, args) signatures in
|
|
35
46
|
// sequence, the provider's onToolCall fires for both BEFORE
|
|
@@ -106,7 +117,16 @@ export function createEagerDispatcher({
|
|
|
106
117
|
if (_dedupEligible) _eagerInFlightSigs.set(_sig, call.id);
|
|
107
118
|
entry.promise = (async () => {
|
|
108
119
|
try {
|
|
109
|
-
if (precedingPatches)
|
|
120
|
+
if (precedingPatches) {
|
|
121
|
+
const patchState = await precedingPatches;
|
|
122
|
+
if (patchState.failedPatchIds.length > 0) {
|
|
123
|
+
return {
|
|
124
|
+
ok: true,
|
|
125
|
+
skipped: true,
|
|
126
|
+
value: `[patch-dependency-guard] \`${call.name}\` was not executed because earlier apply_patch call(s) failed in this assistant turn: ${patchState.failedPatchIds.join(', ')}. Fix the failed patch before verification.`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
110
130
|
await opts.beforeToolExecution?.();
|
|
111
131
|
return { ok: true, value: await executeToolFn(call.name, call.arguments, cwd, sessionId, sessionRef, { toolCallId: call.id, signal, notifyFn: opts.notifyFn, toolApprovalHook: opts.onToolApproval, iteration: getNextIteration(), deferShellCwdCommit: true }) };
|
|
112
132
|
} catch (error) {
|
|
@@ -167,8 +187,13 @@ export function createEagerDispatcher({
|
|
|
167
187
|
});
|
|
168
188
|
pending.set(call.id, entry);
|
|
169
189
|
if (_isMutationTool(call.name)) {
|
|
190
|
+
const precedingPatchState = patchBarrier;
|
|
170
191
|
const currentPatch = entry.promise;
|
|
171
|
-
patchBarrier = Promise.
|
|
192
|
+
patchBarrier = Promise.all([precedingPatchState, currentPatch]).then(([state, settled]) => ({
|
|
193
|
+
failedPatchIds: eagerSettlementFailed(settled)
|
|
194
|
+
? [...state.failedPatchIds, call.id]
|
|
195
|
+
: state.failedPatchIds,
|
|
196
|
+
}));
|
|
172
197
|
}
|
|
173
198
|
return entry;
|
|
174
199
|
};
|
|
@@ -26,8 +26,7 @@ const _rulesBuilder = (() => {
|
|
|
26
26
|
let _sharedRulesCache = null;
|
|
27
27
|
let _sharedRulesMtime = 0;
|
|
28
28
|
const _agentRulesCacheByProfile = new Map();
|
|
29
|
-
|
|
30
|
-
let _leadRulesMtime = 0;
|
|
29
|
+
const _leadRulesCacheByDelegation = new Map();
|
|
31
30
|
let _leadMetaCache = null;
|
|
32
31
|
let _leadMetaMtime = 0;
|
|
33
32
|
|
|
@@ -74,7 +73,7 @@ export function _buildAgentRules(profile = 'full') {
|
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
export function _buildLeadRules() {
|
|
76
|
+
export function _buildLeadRules({ includeLeadBrief = true } = {}) {
|
|
78
77
|
if (!_rulesBuilder || typeof _rulesBuilder.buildLeadRoleContent !== 'function') return '';
|
|
79
78
|
const PLUGIN_ROOT = mixdogRoot();
|
|
80
79
|
const DATA_DIR = resolvePluginData();
|
|
@@ -83,13 +82,14 @@ export function _buildLeadRules() {
|
|
|
83
82
|
join(RULES_DIR, 'lead'),
|
|
84
83
|
join(DATA_DIR, 'mixdog-config.json'),
|
|
85
84
|
]);
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
const key = includeLeadBrief ? 'delegating' : 'delegation-free';
|
|
86
|
+
const cached = _leadRulesCacheByDelegation.get(key);
|
|
87
|
+
if (cached && mtime <= cached.mtime) {
|
|
88
|
+
return cached.value;
|
|
88
89
|
}
|
|
89
90
|
try {
|
|
90
|
-
const built = _rulesBuilder.buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR });
|
|
91
|
-
|
|
92
|
-
_leadRulesMtime = mtime;
|
|
91
|
+
const built = _rulesBuilder.buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR, includeLeadBrief });
|
|
92
|
+
_leadRulesCacheByDelegation.set(key, { mtime, value: built });
|
|
93
93
|
return built;
|
|
94
94
|
} catch (e) {
|
|
95
95
|
throw new Error(`[session] lead role rules build failed: ${e.message}`);
|
|
@@ -210,7 +210,12 @@ export function createSession(opts) {
|
|
|
210
210
|
// what narrow retrieval roles need. Role docs (e.g. 30-explorer.md)
|
|
211
211
|
// override role-inapplicable entries such as the explore routing row.
|
|
212
212
|
const injectedRules = skipAgentRules ? '' : _buildSharedRules();
|
|
213
|
-
const
|
|
213
|
+
const delegationFree = !ownerIsAgent && workflowDisallowsAgentTool(opts.workflow);
|
|
214
|
+
const roleRules = skipAgentRules
|
|
215
|
+
? ''
|
|
216
|
+
: (ownerIsAgent
|
|
217
|
+
? _buildAgentRules(agentRulesProfile)
|
|
218
|
+
: _buildLeadRules({ includeLeadBrief: !delegationFree }));
|
|
214
219
|
const metaContext = skipAgentRules ? '' : (ownerIsAgent ? '' : _buildLeadMetaContext());
|
|
215
220
|
// Prompt permission is metadata for the write bundle, but a read-only role
|
|
216
221
|
// is stamped BEFORE the toolSpec decision so its schema ships the narrowed
|
|
@@ -41,12 +41,16 @@ function _getMcpTools() {
|
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Canonical route order (mirrors rules/shared/01-tool.md and the deferred
|
|
45
|
+
// catalog's ROUTE_TOOL_ORDER): locator → path → content → symbol → read →
|
|
46
|
+
// edit → execute.
|
|
44
47
|
const SESSION_ROUTE_TOOL_ORDER = [
|
|
45
|
-
'
|
|
48
|
+
'explore',
|
|
46
49
|
'find',
|
|
47
50
|
'glob',
|
|
48
51
|
'list',
|
|
49
52
|
'grep',
|
|
53
|
+
'code_graph',
|
|
50
54
|
'read',
|
|
51
55
|
'apply_patch',
|
|
52
56
|
'shell',
|
|
@@ -268,7 +272,7 @@ function _computeBaseTools(toolSpec, mcp, skillTools, { ownerIsAgentSession = fa
|
|
|
268
272
|
return _dedupByName([...skillTools]);
|
|
269
273
|
}
|
|
270
274
|
if (toolSpec.includes('full')) {
|
|
271
|
-
return _dedupByName([...ALL_BUILTIN_SESSION_TOOLS, ...mcp, ...skillTools]);
|
|
275
|
+
return orderSessionTools(_dedupByName([...ALL_BUILTIN_SESSION_TOOLS, ...mcp, ...skillTools]));
|
|
272
276
|
}
|
|
273
277
|
const byName = new Map();
|
|
274
278
|
const add = (tool) => { if (tool?.name && !byName.has(tool.name)) byName.set(tool.name, tool); };
|
|
@@ -306,12 +310,12 @@ function _computeBaseTools(toolSpec, mcp, skillTools, { ownerIsAgentSession = fa
|
|
|
306
310
|
process.stderr.write(`[session] unknown toolset id "${tag}" (profile.tools); skipping\n`);
|
|
307
311
|
}
|
|
308
312
|
}
|
|
309
|
-
return _dedupByName([...byName.values(), ...skillTools]);
|
|
313
|
+
return orderSessionTools(_dedupByName([...byName.values(), ...skillTools]));
|
|
310
314
|
}
|
|
311
315
|
|
|
312
316
|
switch (toolSpec) {
|
|
313
317
|
case 'mcp':
|
|
314
|
-
return _dedupByName([...mcp, ...skillTools]);
|
|
318
|
+
return orderSessionTools(_dedupByName([...mcp, ...skillTools]));
|
|
315
319
|
case 'readonly': {
|
|
316
320
|
const readTools = ALL_BUILTIN_SESSION_TOOLS.filter(t => READONLY_TOOL_NAMES.has(t.name));
|
|
317
321
|
// Read-ROLE agent sessions (reviewer) must self-verify, so
|
|
@@ -323,11 +327,11 @@ function _computeBaseTools(toolSpec, mcp, skillTools, { ownerIsAgentSession = fa
|
|
|
323
327
|
const verifyTools = ownerIsAgentSession
|
|
324
328
|
? ALL_BUILTIN_SESSION_TOOLS.filter(t => t.name === 'shell' || t.name === 'task')
|
|
325
329
|
: [];
|
|
326
|
-
return _dedupByName([...readTools, ...verifyTools, ...mcp, ...skillTools]);
|
|
330
|
+
return orderSessionTools(_dedupByName([...readTools, ...verifyTools, ...mcp, ...skillTools]));
|
|
327
331
|
}
|
|
328
332
|
case 'full':
|
|
329
333
|
default:
|
|
330
|
-
return _dedupByName([...ALL_BUILTIN_SESSION_TOOLS, ...mcp, ...skillTools]);
|
|
334
|
+
return orderSessionTools(_dedupByName([...ALL_BUILTIN_SESSION_TOOLS, ...mcp, ...skillTools]));
|
|
331
335
|
}
|
|
332
336
|
}
|
|
333
337
|
|