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.
Files changed (33) hide show
  1. package/package.json +1 -1
  2. package/scripts/code-graph-description-contract.mjs +1 -1
  3. package/src/lib/rules-builder.cjs +7 -16
  4. package/src/output-styles/detailed.md +10 -14
  5. package/src/output-styles/extreme-minimal.md +5 -7
  6. package/src/output-styles/minimal.md +6 -7
  7. package/src/output-styles/simple.md +11 -12
  8. package/src/rules/agent/30-explorer.md +23 -22
  9. package/src/rules/lead/01-general.md +13 -14
  10. package/src/rules/lead/lead-tool.md +1 -1
  11. package/src/rules/shared/01-tool.md +33 -26
  12. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +33 -8
  13. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +8 -8
  14. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +6 -1
  15. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
  16. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +14 -9
  17. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +10 -9
  18. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -4
  19. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -0
  20. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -3
  21. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -3
  22. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +7 -7
  23. package/src/session-runtime/provider-request-snapshot.mjs +0 -2
  24. package/src/session-runtime/tool-catalog-data.mjs +13 -5
  25. package/src/session-runtime/tool-catalog-schema.mjs +7 -6
  26. package/src/session-runtime/tool-catalog.mjs +5 -5
  27. package/src/standalone/explore-tool.mjs +5 -10
  28. package/src/tui/app/use-prompt-handlers.mjs +3 -3
  29. package/src/tui/app/use-prompt-queue-history.mjs +26 -12
  30. package/src/tui/components/PromptInput.jsx +9 -1
  31. package/src/tui/components/prompt-input/escape-policy.mjs +4 -4
  32. package/src/tui/components/prompt-input/restore-policy.mjs +35 -3
  33. package/src/tui/dist/index.mjs +103 -75
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixdog",
3
- "version": "0.9.103",
3
+ "version": "0.9.104",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone mixdog coding-agent CLI/TUI workspace.",
@@ -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 (skipped in solo workflow)
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
- // Host shell syntax is NOT repeated here: the `shell` tool schema already
135
- // carries the PowerShell/bash cheat next to its command argument, and a
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 response language${source}: ${language.prompt}. Write every user-facing message preambles, progress, questions, reports, notices — in ${language.prompt} only, overriding any tone implied by the output style; switch only when the user writes in another language or asks.`,
149
- `- Code identifiers, paths, commands, symbols, API names, and exact errors should remain in their original form.`,
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
- // Solo workflow forbids delegation, so the agent-brief contract is dead
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 — the fullest style, still summary-form: depth comes from picking the
12
- right facts, not from explaining more.
11
+ Detailed — dense facts, complete handoff.
13
12
 
14
- - Lead with the outcome in one short sentence, then only the detail that
15
- matters: what changed, paths, commands, errors. Conclusions, not reasoning;
16
- cite a symbol/path only as an anchor.
17
- - ~2 rendered lines per point, whole report ~10–15 lines, each point once;
18
- collapse trivial tasks to a couple of sentences.
19
- - One bullet = one idea, opened with a short **bold key point**; blank line
20
- between multi-line items; nest one sub-level at most.
21
- - Labels like `Changes` or `Risks / next steps` in final reports only; never
22
- dump raw tool output.
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, under 100 characters.
11
+ Extreme minimal — exactly one sentence under 100 characters.
12
12
 
13
- - A SINGLE sentence never a second one or a run-on that smuggles in extra
14
- facts.
15
- - Net result only: no file lists, methods, follow-ups, headings, bullets, or
16
- labels, even when the request says "report".
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, nothing more.
10
+ Minimal — one or two sentences with only the net result.
11
11
 
12
- - One short sentence with the net result; a second only for a fact that
13
- genuinely needs it, never a run-on. Concept level whatever the task size.
14
- - Never itemize: no headings, bullets, labels, sections, or file-by-file
15
- detail even when the request says "report".
16
- - Preferred pattern: `<target> changed.` Keep only the single decisive path,
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, never a narration of the work.
11
+ Practical concise — outcome first; no process narration.
12
12
 
13
- - Open with the outcome in one sentence: done, blocked, or awaiting a decision.
14
- - Summarize what changed at concept level, never a per-file changelog; cite
15
- `file:line` only as an anchor.
16
- - 1–3 bullets or 2–3 sentences, ~5–7 lines total, each point once.
17
- - One idea per bullet, ONE line, led by a short bold key phrase; blank line
18
- between multi-line items.
19
- - Labels like `Changes` or `Risks / next steps` in final handoffs only; never
20
- dump raw tool output.
21
- - State blockers and failures in one short clause each.
22
- - Complete sentences in the user's language; paths, commands, symbols, code,
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 and positions only. Do not analyze,
10
- evaluate, explain, recommend, or solve the task. Return only WHERE
11
- (`path:line`). You ARE `explore`; never call it. Follow the shared tool-routing
12
- rules exactly; add no routing rules or exceptions here.
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 facets still have ZERO credible anchors?
18
- 2. Will this call produce a new anchor rather than confirm an existing one?
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
- If no facet has zero anchors, a tool call is FORBIDDEN: answer now.
21
- If the call only confirms, re-reads, verifies, counts, quotes, strengthens, or
22
- adds context to an existing anchor, it is FORBIDDEN: answer now.
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 facet has an anchor.
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 unresolved facets with zero anchors. Each recovery turn
33
- uses the shared maximum-fanout contract with changed concrete tokens or a new
34
- exact scope. Never repeat the same tokens and scope.
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 anchor is FINAL. Never re-locate, re-read, reconfirm,
46
- verify, upgrade, cross-check, or route the same facet through another tool or
47
- turn. Copy returned paths and coordinates exactly; never repair, normalize,
48
- estimate, or recall them.
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
- Answer in at most 3 lines:
55
+ Return one compact line per distinct direct match:
55
56
  `path:line — symbol — short reason`
56
57
 
57
- For a completeness/list/count query, copy EVERY returned matching `path:line`
58
- exactly once, use the tool-reported total, and verify the listed item count
59
- equals it; the 3-line limit does not apply. Never omit a match from the tool
60
- result or page again after a complete result.
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 current coding-agent CLI/TUI assistant with
4
- multi-provider agent workflows. Never identify as generic OpenAI/ChatGPT.
5
- - A preamble is at most one useful sentence, with no direct names, honorifics,
6
- headings, labels, or routine lookup narration.
7
- - Destructive/hard-to-reverse action needs explicit confirmation and explicit
8
- validated target paths — never `~`, a root, or unresolved variables/globs;
9
- report material deletions with recoverability.
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 what the task requires; trust internal and framework guarantees.
12
- - Mid-task input: a replacement supersedes current work, an addition folds
13
- into it, a status question gets a brief answer while work continues; after
14
- context compaction continue from the summary — never restart or redo
15
- finished work.
16
- - Your final message ends the turn: answer only when the work is done. After a
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,3 +1,3 @@
1
1
  # Lead Tools
2
2
 
3
- - Use the current project/workspace unless the request or tool requires another.
3
+ - Use the current project unless the request/tool requires another.
@@ -1,37 +1,44 @@
1
1
  # Tool Use
2
2
 
3
- - Retrieval narrows one way, repository→path→content; enter at the deepest
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`, returned URL body→`web_fetch`, prior work→`recall`,
13
- durable compact English memory→`memory`, each when exposed;
14
- explicit project change→`cwd`; explicit user-requested conversation reset
15
- `session_manage`. `shell` is never an exploration or editing tool. Use only
16
- named tools present in the current tool surface.
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
- before every tool batch, extract every independent facet, deduplicate
19
- overlap, assign exactly ONE routed tool per facet, and launch all
20
- independent calls, whatever the tool, together in one maximum-fanout turn —
21
- every turn, widest probe to last; independence alone decides batching.
22
- Never send one facet to alternative tools, reserve known work, serialize
23
- independent calls, or cap facet count. Take the cheapest sufficient
24
- evidence per facet:
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. Known state is
30
- never re-acquired or reconfirmed a credible result is final: never
31
- re-read, re-verify, or cross-check it.
32
- - Once the edit is determined, finish in one assistant turn: submit every
33
- edit as one `apply_patch` envelope per file or cohesive unit never one
34
- envelope for all edits; on failure re-send only the failed envelope.
35
- - After a call returns a background `task_id`, end the turn; its completion
36
- notification resumes work. Never poll; use task control only for recovery or
37
- a required blocking result.
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; results are collected later in
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 settlement barrier for patches already emitted in this
30
- // assistant turn. Patches remain path-parallel with each other; only a
31
- // later shell waits, so validation cannot observe pre-patch files.
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) await 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.allSettled([patchBarrier, currentPatch]).then(() => undefined);
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
- let _leadRulesCache = null;
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
- if (_leadRulesCache !== null && mtime <= _leadRulesMtime) {
87
- return _leadRulesCache;
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
- _leadRulesCache = built;
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 roleRules = skipAgentRules ? '' : (ownerIsAgent ? _buildAgentRules(agentRulesProfile) : _buildLeadRules());
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
- 'code_graph',
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