mixdog 0.9.144 → 0.9.145

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 (37) hide show
  1. package/package.json +1 -1
  2. package/scripts/prepare-native-assets.mjs +15 -2
  3. package/src/output-styles/common.md +18 -27
  4. package/src/output-styles/detailed.md +5 -5
  5. package/src/output-styles/extreme-minimal.md +5 -6
  6. package/src/output-styles/minimal.md +4 -6
  7. package/src/output-styles/simple.md +4 -7
  8. package/src/rules/agent/00-common.md +3 -2
  9. package/src/rules/lead/01-general.md +3 -4
  10. package/src/rules/lead/02-persona.md +4 -5
  11. package/src/rules/shared/10-tool-workflow.md +3 -2
  12. package/src/rules/shared/30-exploration.md +2 -4
  13. package/src/rules/shared/70-delivery.md +1 -1
  14. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -0
  15. package/src/runtime/agent/orchestrator/session/evidence-union.mjs +1 -0
  16. package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +1 -0
  17. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +2 -2
  18. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -1
  19. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +6 -6
  20. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.test.mjs +25 -3
  21. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +3 -3
  22. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -4
  23. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.test.mjs +3 -0
  24. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +163 -4
  25. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +71 -2
  26. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +263 -0
  27. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +7 -0
  28. package/src/runtime/agent/orchestrator/tools/builtin.mjs +3 -1
  29. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -1
  30. package/src/runtime/shared/tool-surface.mjs +2 -0
  31. package/src/session-runtime/tool-catalog-data.mjs +1 -1
  32. package/src/session-runtime/tool-policy-surface.test.mjs +11 -2
  33. package/src/tui/app/live-spinner-visibility.mjs +8 -1
  34. package/src/tui/app/live-spinner-visibility.test.mjs +44 -0
  35. package/src/tui/app/shell-layout.mjs +2 -0
  36. package/src/tui/dist/index.mjs +10 -3
  37. package/src/tui/session/turn.mjs +13 -44
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixdog",
3
- "version": "0.9.144",
3
+ "version": "0.9.145",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone mixdog coding-agent CLI/TUI workspace.",
@@ -58,11 +58,24 @@ export async function prepareRequiredNativeAssets({
58
58
  return [name, join(target, fileName)];
59
59
  }),
60
60
  );
61
- await rm(target, { recursive: true, force: true });
61
+ await rm(target, {
62
+ recursive: true,
63
+ force: true,
64
+ maxRetries: 8,
65
+ retryDelay: 100,
66
+ });
62
67
  await rename(stagedToolsDir, target);
63
68
  return Object.fromEntries(entries);
64
69
  } finally {
65
- await rm(stagingRoot, { recursive: true, force: true });
70
+ // Antivirus and process scanners can briefly retain freshly copied .exe
71
+ // handles on Windows. Cleanup must not replace the installer's real error
72
+ // with a transient EBUSY from the staging directory.
73
+ await rm(stagingRoot, {
74
+ recursive: true,
75
+ force: true,
76
+ maxRetries: 8,
77
+ retryDelay: 100,
78
+ });
66
79
  }
67
80
  }
68
81
 
@@ -7,32 +7,23 @@ partial: true
7
7
 
8
8
  ## Shared Output Format
9
9
 
10
- - Lead with the answer or action, then the context needed to understand it, then
11
- lower-priority detail. Write for a person, not a console log: complete
12
- sentences, explained terms, and a register matched to the reader's language,
13
- tone, and apparent expertise.
14
- - Select content by the active depth variation before formatting it. Formatting
15
- never restores information that variation discarded.
16
- - Choose the shape from the content and the reader. Markdown is available, not
17
- mandatory; prose, bullets, numbered steps, headings, tables, and code may
18
- combine in any way that makes this particular answer easier to read, and no
19
- response shape is a default or a required mapping from question type.
20
- - Output renders as GitHub-flavored Markdown tables, task lists, footnotes,
21
- fenced code with a language tag, and `$$…$$` math — in a proportional-width
22
- surface, so space-aligned columns and ASCII art never line up; use a real table
23
- or a fenced block instead. Use tables only for short enumerable or quantitative
24
- facts, and keep explanatory reasoning outside the cells.
25
- - Keep simple answers visually simple and add structure only where it creates
26
- useful grouping, order, or contrast. Never produce an essay-shaped wall of
27
- text, and never overcorrect into fragments, decorative emphasis, tiny headings,
10
+ - Lead with the answer or action, then the context needed to understand it and
11
+ lower-priority detail. Use complete sentences and a register suited to the
12
+ reader.
13
+ - Select content by the active depth variation before formatting it; omitted
14
+ content stays omitted.
15
+ - Choose the shape from the content and the reader. Prose, lists, headings,
16
+ tables, and code may combine, and no response shape is a default. Output uses
17
+ GitHub-flavored Markdown with `$$…$$` math in a proportional-width surface;
18
+ use tables or fenced blocks instead of spacing. Use tables only for short
19
+ enumerable or quantitative facts, with explanation outside the cells.
20
+ - Keep simple answers visually simple. Never produce an essay-shaped wall of
21
+ text or overcorrect into fragments, decorative emphasis, tiny headings,
28
22
  needless nesting, or a recurring template.
29
- - Give each paragraph or list item one idea and keep the flow linear, so the
30
- reader builds meaning forward without re-parsing what came before.
31
- - Keep established facts separate from plausible inference. Never invent a
32
- mechanism, value, or certainty to make an explanation feel complete, preserve
33
- exact technical literals, and show code or file references only where their
34
- exact form helps.
23
+ - Give each paragraph or list item one idea and keep the flow linear.
24
+ - Keep established facts separate from plausible inference. Never invent
25
+ certainty, preserve exact technical literals, and show references only where
26
+ useful.
35
27
  - State each material point once and stop when the request is covered. Do not
36
- restate the request, close with a recap, or append an unrequested
37
- recommendation, next step, or offer of help; no emojis, raw output dumps, or
38
- narration of your own process unless asked.
28
+ restate the request, close with a recap, or append unrequested recommendations
29
+ or offers; avoid emojis, raw output dumps, and process narration unless asked.
@@ -9,8 +9,8 @@ aliases: verbose, full
9
9
 
10
10
  Detailed — concrete explanation.
11
11
 
12
- - Retain: the answer plus every established explanatory layer that materially
13
- improves understanding mechanism, evidence, trade-offs, implications, and
14
- useful examples. Deepen the requested subject rather than widening it.
15
- - Omit: speculation that fills an evidentiary gap, and any expansion into fixes,
16
- recommendations, or adjacent lessons that were not requested.
12
+ - Retain: the answer and every established layer that materially aids
13
+ understanding—mechanism, evidence, trade-offs, implications, and useful
14
+ examples. Deepen the subject rather than widening it.
15
+ - Omit: unsupported speculation and unrequested fixes, recommendations, or
16
+ adjacent lessons.
@@ -9,9 +9,8 @@ aliases: extreme, extreme-simple, one-line, mono
9
9
 
10
10
  Extreme minimal — final decision or answer.
11
11
 
12
- - Retain: only the final decision, result, or direct answer. For a cause
13
- question, that is the highest-level cause, not its concrete causal chain.
14
- - Omit: every heading, label, list, explanation, supporting fact, scope, caveat,
15
- recap, and follow-up, even when asked to explain or report.
16
- - Shape: one or two short sentences. Never pack discarded detail into a longer
17
- sentence.
12
+ - Retain: only the decision, result, or direct answer; for cause questions, only
13
+ the highest-level cause.
14
+ - Omit: headings, labels, lists, explanation, evidence, scope, caveats, recap,
15
+ and follow-up, even when requested.
16
+ - Shape: one or two short sentences; do not pack omitted detail into them.
@@ -9,9 +9,7 @@ aliases: brief, short
9
9
 
10
10
  Minimal — conclusion and core cause.
11
11
 
12
- - Retain: the conclusion, the single core cause that determines it, one or two
13
- decisive concrete facts that make the cause clear, and the immediate
14
- consequence when it completes the answer. Stop before a full explanation.
15
- - Omit: secondary evidence, deployment state, unaffected scope, broader impact,
16
- exceptions, remediation, and process, unless that category is the requested
17
- answer.
12
+ - Retain: the conclusion, determining cause, one or two decisive facts, and the
13
+ immediate consequence when needed.
14
+ - Omit: secondary evidence, unaffected scope, broader impact, exceptions,
15
+ remediation, and process unless requested.
@@ -9,10 +9,7 @@ aliases: default, concise, handoff
9
9
 
10
10
  Simple — concise summary.
11
11
 
12
- - Retain: the main answer, the essential causal sequence, the strongest
13
- supporting evidence, direct consequences, and any material scope or caveat.
14
- Preserve enough context for the answer to stand on its own, and prefer a
15
- complete explanation over compression that makes the reader infer a missing
16
- step.
17
- - Omit: secondary evidence, exhaustive implementation detail, examples, edge
18
- cases, adjacent implications, alternatives, and unrequested remediation.
12
+ - Retain: the answer, core causal sequence, strongest evidence, direct
13
+ consequences, and material scope or caveat. Keep it self-contained.
14
+ - Omit: secondary evidence, implementation detail, examples, edge cases,
15
+ adjacent implications, alternatives, and unrequested remediation.
@@ -1,7 +1,8 @@
1
1
  # Public Agent Constraints
2
2
 
3
- - Never touch git/Ship. Refuse `git add`/`commit`/`push`/`stash` with `git
4
- operations deferred to Lead`.
3
+ - Use `git` only for read-only repository evidence. Refuse Git mutations
4
+ including `add`/`commit`/`push`/`stash`, and Ship, with `git operations
5
+ deferred to Lead`.
5
6
  - `permission: read` agents use shell only for verification and never change
6
7
  state; other agents never use it to explore, install, or change state beyond
7
8
  the brief.
@@ -2,10 +2,9 @@
2
2
 
3
3
  - You are Mixdog, the coding-agent CLI/TUI assistant for multi-provider
4
4
  workflows; never generic OpenAI/ChatGPT.
5
- - Before the first tool call, state in one short sentence what you are about
6
- to do; add a short update when you find something load-bearing, change
7
- direction, or work a stretch without one. No direct names, honorifics,
8
- headings, labels, or a colon before a tool call.
5
+ - Before the first tool call, briefly state what you are about to do; add a
6
+ short update when you find something load-bearing, change direction, or
7
+ work a stretch without one. Do not use a colon before a tool call.
9
8
  - Confirm destructive/hard-to-reverse actions against explicit validated paths;
10
9
  never `~`, a root, or unresolved variables/globs; report material deletion
11
10
  recoverability.
@@ -1,7 +1,6 @@
1
1
  # Persona
2
2
 
3
- You are a curious, thoughtful, and grounded collaborator with a distinct point
4
- of view. Your presence is warm, candid, and natural, with understated humor when
5
- it fits. Conversation with you feels attentive and alive rather than scripted,
6
- ingratiating, or performative. You are attentive to the user's linguistic and
7
- cultural context and level of expertise, without mimicry or stereotyping.
3
+ You are a curious, grounded collaborator with a distinct point of view. Be warm,
4
+ candid, natural, and attentive, with understated humor when it fits—never
5
+ scripted, ingratiating, or performative. Respect the user's linguistic and
6
+ cultural context without mimicry or stereotyping.
@@ -14,8 +14,9 @@
14
14
  summary, overview, or enumeration is not a prerequisite when that operation's
15
15
  complete inputs are already known; if independently required, batch it with
16
16
  the detailed operation.
17
- - Never duplicate a facet, widen retrieval speculatively, or cap fanout; apply
18
- one analysis to many targets as one parameterized call when supported.
17
+ - Never duplicate a facet, widen retrieval speculatively, or arbitrarily omit
18
+ required fanout. Respect tool/schema limits; split only when necessary, and
19
+ apply one analysis to many targets as one parameterized call when supported.
19
20
  1. Determine the required outcome and missing information; requirements are
20
21
  not evidence.
21
22
  2. If needed, gather only missing information through Research or Exploration;
@@ -2,10 +2,8 @@
2
2
 
3
3
  - Use read-only means for inspection; never mutate to clear an obstacle or
4
4
  unexpected state. Preserve evidence before a required mutation can destroy it.
5
- - Ownership is exclusive: each evidence type has one owner, another tool's
6
- ability to reach the same target is never an alternative route, and a
7
- successful owner result closes that facet — only a different evidence type
8
- routes elsewhere.
5
+ - Ownership is exclusive: each evidence type has one owner;
6
+ a successful owner result closes that facet.
9
7
  - Route the missing evidence to its primary owner:
10
8
  repository state, history, or diff→`git`;
11
9
  exact symbol declaration, body, usage, or relation→`code_graph`;
@@ -2,4 +2,4 @@
2
2
 
3
3
  - Commit, push, release, and deployment happen only on the user's explicit
4
4
  request.
5
- - Every repository mutation→`git`.
5
+ - Every Git operation→`git`; source-file edits stay with `edit`/`apply_patch`.
@@ -590,6 +590,9 @@ export function buildShellOutputTelemetryPayload({
590
590
  reduction_pct: commandOutputBytes > 0
591
591
  ? Math.round((1 - visibleBytes / commandOutputBytes) * 100)
592
592
  : null,
593
+ exit_code: Number.isInteger(telemetry?.exitCode) ? telemetry.exitCode : null,
594
+ signal: telemetry?.signal || null,
595
+ timed_out: telemetry?.timedOut === true,
593
596
  spilled: telemetry?.spilled === true,
594
597
  offloaded: offloaded === true,
595
598
  };
@@ -167,6 +167,7 @@ function mutationBatch(toolCalls) {
167
167
  return name === 'apply_patch'
168
168
  || name === 'shell'
169
169
  || name === 'bash_session'
170
+ || name === 'git_stage'
170
171
  || (name === 'git' && gitCallMutates(call));
171
172
  });
172
173
  }
@@ -141,6 +141,7 @@ test('apply_patch, shell, and mutating git batches invalidate all earlier eviden
141
141
  for (const [mutationName, mutationArgs] of [
142
142
  ['apply_patch', {}],
143
143
  ['shell', {}],
144
+ ['git_stage', { diff_id: 'diff_test', change_ids: ['chg_test'] }],
144
145
  ['git', { command: 'git commit -m test' }],
145
146
  ['git', { command: "git reflog delete 'HEAD@{1}'" }],
146
147
  ]) {
@@ -16,11 +16,11 @@ export function _isReadTool(name) {
16
16
  }
17
17
  export function _isMutationTool(name, args = null) {
18
18
  const n = String(_stripMcpPrefix(name) || '').toLowerCase();
19
- return n === 'apply_patch' || n === 'edit' || (n === 'git' && gitCommandMutates(args));
19
+ return n === 'apply_patch' || n === 'edit' || n === 'git_stage' || (n === 'git' && gitCommandMutates(args));
20
20
  }
21
21
  export function _isGitMutationTool(name, args = null) {
22
22
  const n = String(_stripMcpPrefix(name) || '').toLowerCase();
23
- return n === 'git' && gitCommandMutates(args);
23
+ return n === 'git_stage' || (n === 'git' && gitCommandMutates(args));
24
24
  }
25
25
  export function _isEditTool(name) {
26
26
  return String(_stripMcpPrefix(name) || '').toLowerCase() === 'edit';
@@ -55,6 +55,7 @@ const SESSION_ROUTE_TOOL_ORDER = [
55
55
  'edit',
56
56
  'apply_patch',
57
57
  'git',
58
+ 'git_stage',
58
59
  'shell',
59
60
  'task',
60
61
  'web_search',
@@ -137,6 +138,7 @@ const AGENT_STRING_PERMISSION_READ_WRITE_ALLOW = Object.freeze([
137
138
  'edit',
138
139
  'apply_patch',
139
140
  'git',
141
+ 'git_stage',
140
142
  'shell',
141
143
  'task',
142
144
  'web_search',
@@ -284,7 +286,7 @@ function _computeBaseTools(toolSpec, mcp, skillTools, { ownerIsAgentSession = fa
284
286
  addMany(ALL_BUILTIN_SESSION_TOOLS.filter(t => t.name === 'shell' || t.name === 'task'));
285
287
  break;
286
288
  case 'tools:git':
287
- addMany(ALL_BUILTIN_SESSION_TOOLS.filter(t => t.name === 'git' || t.name === 'shell' || t.name === 'task'));
289
+ addMany(ALL_BUILTIN_SESSION_TOOLS.filter(t => t.name === 'git' || t.name === 'git_stage' || t.name === 'shell' || t.name === 'task'));
288
290
  break;
289
291
  case 'tools:mcp':
290
292
  addMany(mcp);
@@ -39,12 +39,6 @@ export function prepareProviderPrefixGuard(previous, messages, requestPrefix, op
39
39
  const next = snapshot(Array.isArray(messages) ? messages : [], requestPrefix);
40
40
  if (!previous || isCompactionPrefixReset(options.cacheBreakIntent)) return next;
41
41
 
42
- if (previous.requestPrefixHash !== next.requestPrefixHash) {
43
- throw new ProviderPrefixMutationError('provider request prefix changed outside compaction', {
44
- provider: options.provider || null,
45
- kind: 'request_prefix',
46
- });
47
- }
48
42
  if (next.messageHashes.length < previous.messageHashes.length) {
49
43
  throw new ProviderPrefixMutationError('provider message history shrank outside compaction', {
50
44
  provider: options.provider || null,
@@ -59,5 +53,11 @@ export function prepareProviderPrefixGuard(previous, messages, requestPrefix, op
59
53
  index,
60
54
  });
61
55
  }
56
+ if (previous.requestPrefixHash !== next.requestPrefixHash) {
57
+ // Tool schemas are request metadata, not durable conversation state.
58
+ // App updates may change them between turns, so rebaseline the provider
59
+ // cache prefix after the transcript itself has passed integrity checks.
60
+ return next;
61
+ }
62
62
  return next;
63
63
  }
@@ -40,7 +40,7 @@ test('accepts append-only provider history for every provider surface', () => {
40
40
  }
41
41
  });
42
42
 
43
- test('rejects prior-message and tool-prefix rewrites outside compaction', () => {
43
+ test('rejects prior-message rewrites outside compaction', () => {
44
44
  const first = prepareProviderPrefixGuard(
45
45
  null,
46
46
  [{ role: 'user', content: 'one' }],
@@ -54,10 +54,32 @@ test('rejects prior-message and tool-prefix rewrites outside compaction', () =>
54
54
  ),
55
55
  ProviderPrefixMutationError,
56
56
  );
57
+ });
58
+
59
+ test('rebaselines tool-prefix changes while preserving transcript integrity checks', () => {
60
+ const first = prepareProviderPrefixGuard(
61
+ null,
62
+ [{ role: 'user', content: 'one' }],
63
+ { tools: [{ name: 'read' }], nativeTools: [] },
64
+ );
65
+ const changed = prepareProviderPrefixGuard(
66
+ first,
67
+ [{ role: 'user', content: 'one' }],
68
+ { tools: [{ name: 'read' }, { name: 'shell' }], nativeTools: [] },
69
+ );
70
+ assert.notEqual(changed.requestPrefixHash, first.requestPrefixHash);
71
+ assert.doesNotThrow(() => prepareProviderPrefixGuard(
72
+ changed,
73
+ [
74
+ { role: 'user', content: 'one' },
75
+ { role: 'assistant', content: 'two' },
76
+ ],
77
+ { tools: [{ name: 'read' }, { name: 'shell' }], nativeTools: [] },
78
+ ));
57
79
  assert.throws(
58
80
  () => prepareProviderPrefixGuard(
59
- first,
60
- [{ role: 'user', content: 'one' }],
81
+ changed,
82
+ [{ role: 'user', content: 'rewritten' }],
61
83
  { tools: [{ name: 'read' }, { name: 'shell' }], nativeTools: [] },
62
84
  ),
63
85
  ProviderPrefixMutationError,
@@ -802,10 +802,10 @@ function guardGlob(a) {
802
802
  }
803
803
  for (const k of globPatternKeys) {
804
804
  if (hasOwn(a, k)) a[k] = coercePatternStringValues(a[k]);
805
- if (hasOwn(a, k) && !isString(a[k])) {
806
- return `Error: glob arg "${k}" must be string (got ${describeType(a[k])})`;
805
+ if (hasOwn(a, k) && !isStringOrStringArray(a[k])) {
806
+ return `Error: glob arg "${k}" must be string or string[] (got ${describeType(a[k])})`;
807
807
  }
808
- if (hasOwn(a, k) && a[k].trim().length === 0) {
808
+ if (hasOwn(a, k) && typeof a[k] === 'string' && a[k].trim().length === 0) {
809
809
  return `Error: glob arg "${k}" must be a non-empty string`;
810
810
  }
811
811
  }
@@ -5,7 +5,7 @@
5
5
  // behavior, argument shapes, and the usage boundaries that only apply to that
6
6
  // tool; cross-tool policy lives in rules/shared/*.md.
7
7
  // Platform-specific command syntax belongs next to the command argument.
8
- import { GIT_TOOL_DEF } from './git-command-tool.mjs';
8
+ import { GIT_STAGE_TOOL_DEF, GIT_TOOL_DEF } from './git-command-tool.mjs';
9
9
  import { SHELL_MONITOR_INTERVAL_MAX_MS } from './shell-monitor.mjs';
10
10
  const _shellSyntaxCheat =
11
11
  process.platform === 'win32'
@@ -81,7 +81,7 @@ export const BUILTIN_TOOLS = [
81
81
  name: 'shell',
82
82
  title: 'Shell',
83
83
  annotations: { title: 'Shell', readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true, compressible: true },
84
- description: `Run programs, runtime/state operations, calculations, transformations, file generation, and unsupported-format inspection. ${_shellToolRouting} Avoid file operations covered by dedicated tools unless explicitly instructed or after verifying that a dedicated tool cannot do the job. An already-open shell is never a reason to route work to it. ${_shellBackgroundDisabled ? 'Commands run in the foreground until completion.' : 'Commands use a 10s foreground window by default—not a timeout. Still-running work continues as a tracked task_id and completes by notification. Use task monitor only after promotion when periodic progress is needed, task read for an extra current snapshot, and never poll in a loop.'}`,
84
+ description: `Run programs, runtime/state operations, calculations, transformations, file generation, and unsupported-format inspection. ${_shellToolRouting} Avoid file operations covered by dedicated tools unless explicitly instructed or after verifying that a dedicated tool cannot do the job. An already-open shell is never a reason to route work to it. ${_shellBackgroundDisabled ? 'Commands run in the foreground until completion.' : 'Commands use a 10s foreground window by default—not a timeout. Still-running work continues as a tracked task_id. Completion is automatic; do not poll with task read/monitor unless the user explicitly requests polling or periodic progress. Otherwise, use task read only for an immediate decision and task monitor only for explicitly requested periodic progress.'}`,
85
85
  inputSchema: {
86
86
  type: 'object',
87
87
  properties: {
@@ -97,11 +97,12 @@ export const BUILTIN_TOOLS = [
97
97
  },
98
98
  },
99
99
  GIT_TOOL_DEF,
100
+ GIT_STAGE_TOOL_DEF,
100
101
  {
101
102
  name: 'task',
102
103
  title: 'Task',
103
104
  annotations: { title: 'Task', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
104
- description: 'List shell tasks, read one current output snapshot, change periodic monitoring, or cancel by task_id. Call it only when a snapshot must drive a decision; never poll in a loop completion always arrives by notification.',
105
+ description: 'List shell tasks, read one current snapshot, change periodic monitoring, or cancel by task_id. Completion is automatic; do not poll with read/monitor unless the user explicitly requests polling or periodic progress. Otherwise, use read only for an immediate decision and monitor only for explicitly requested periodic progress.',
105
106
  inputSchema: {
106
107
  type: 'object',
107
108
  properties: {
@@ -157,7 +158,11 @@ export const BUILTIN_TOOLS = [
157
158
  type: 'object',
158
159
  properties: {
159
160
  pattern: {
160
- type: 'string', description: 'Glob.',
161
+ anyOf: [
162
+ { type: 'string' },
163
+ { type: 'array', items: { type: 'string' }, maxItems: 10 },
164
+ ],
165
+ description: 'Glob or array of globs (max 10).',
161
166
  },
162
167
  path: {
163
168
  type: 'string', description: 'Known existing base directory; omit for the current Project; unknown location → find.',
@@ -23,5 +23,8 @@ test('git mutation policy is shared by orchestration and evidence projection', (
23
23
  assert.equal(_isGitMutationTool('git', { command }), true, command);
24
24
  }
25
25
  assert.equal(gitCommandMutates({ command: 'git status && git clean -fd' }), true);
26
+ const stage = { diff_id: 'diff_test', change_ids: ['chg_test'] };
27
+ assert.equal(_isMutationTool('git_stage', stage), true);
28
+ assert.equal(_isGitMutationTool('git_stage', stage), true);
26
29
  assert.equal(_isMutationTool('apply_patch', {}), true);
27
30
  });