mixdog 0.9.45 → 0.9.46

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 (116) hide show
  1. package/package.json +5 -2
  2. package/scripts/agent-dispatch-abort-compose-test.mjs +31 -0
  3. package/scripts/agent-model-liveness-test.mjs +618 -0
  4. package/scripts/agent-trace-io-test.mjs +68 -4
  5. package/scripts/bench-run.mjs +30 -3
  6. package/scripts/compact-pressure-test.mjs +187 -4
  7. package/scripts/compact-prior-context-flatten-test.mjs +252 -0
  8. package/scripts/compact-trigger-migration-smoke.mjs +8 -6
  9. package/scripts/compacted-placeholder-scrub-test.mjs +20 -34
  10. package/scripts/context-mcp-metering-test.mjs +75 -0
  11. package/scripts/explore-prompt-policy-test.mjs +15 -16
  12. package/scripts/explore-timeout-cancel-test.mjs +345 -0
  13. package/scripts/headless-pristine-execution-test.mjs +614 -0
  14. package/scripts/internal-comms-smoke.mjs +15 -6
  15. package/scripts/live-worker-smoke.mjs +1 -1
  16. package/scripts/memory-core-input-test.mjs +137 -0
  17. package/scripts/memory-rule-contract-test.mjs +5 -5
  18. package/scripts/openai-oauth-refresh-race-test.mjs +120 -0
  19. package/scripts/openai-oauth-ws-1006-retry-test.mjs +26 -0
  20. package/scripts/parent-abort-link-test.mjs +22 -0
  21. package/scripts/provider-toolcall-test.mjs +22 -0
  22. package/scripts/reactive-compact-persist-smoke.mjs +8 -4
  23. package/scripts/session-bench.mjs +3 -70
  24. package/scripts/task-bench.mjs +0 -2
  25. package/scripts/terminal-bench-isolation-guards-test.mjs +102 -0
  26. package/scripts/tool-smoke.mjs +21 -21
  27. package/scripts/tool-tui-presentation-test.mjs +68 -0
  28. package/scripts/tui-transcript-jitter-harness-entry.jsx +91 -10
  29. package/src/app.mjs +28 -103
  30. package/src/cli.mjs +17 -13
  31. package/src/headless-command.mjs +139 -0
  32. package/src/headless-role.mjs +121 -10
  33. package/src/help.mjs +4 -1
  34. package/src/rules/agent/00-common.md +3 -3
  35. package/src/rules/agent/00-core.md +8 -9
  36. package/src/rules/agent/20-skip-protocol.md +2 -3
  37. package/src/rules/agent/30-explorer.md +50 -56
  38. package/src/rules/agent/40-cycle1-agent.md +10 -12
  39. package/src/rules/agent/41-cycle2-agent.md +12 -9
  40. package/src/rules/agent/42-cycle3-agent.md +4 -6
  41. package/src/rules/lead/01-general.md +5 -6
  42. package/src/rules/lead/02-channels.md +1 -1
  43. package/src/rules/lead/lead-brief.md +14 -17
  44. package/src/rules/lead/lead-tool.md +3 -3
  45. package/src/rules/shared/01-tool.md +41 -43
  46. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +46 -10
  47. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +44 -31
  48. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +18 -3
  49. package/src/runtime/agent/orchestrator/config.mjs +96 -30
  50. package/src/runtime/agent/orchestrator/context/collect.mjs +9 -0
  51. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
  52. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +18 -5
  53. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -6
  54. package/src/runtime/agent/orchestrator/providers/gemini.mjs +6 -6
  55. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +123 -3
  56. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -3
  57. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +148 -30
  58. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +5 -7
  59. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +62 -14
  60. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +141 -19
  61. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -4
  62. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +19 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +85 -17
  64. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -8
  65. package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -17
  66. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +159 -20
  67. package/src/runtime/agent/orchestrator/session/context-utils.mjs +83 -10
  68. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +75 -7
  69. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +4 -374
  70. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +0 -75
  71. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +0 -5
  72. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +20 -3
  73. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +32 -16
  74. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +5 -2
  75. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +86 -15
  76. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +30 -27
  77. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +1 -14
  78. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +21 -27
  79. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +1 -3
  80. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -51
  81. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -4
  82. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  83. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +26 -0
  84. package/src/runtime/memory/index.mjs +0 -1
  85. package/src/runtime/memory/lib/core-memory-store.mjs +44 -24
  86. package/src/runtime/memory/lib/http-router.mjs +0 -193
  87. package/src/runtime/memory/lib/memory-action-handlers.mjs +37 -11
  88. package/src/runtime/memory/tool-defs.mjs +5 -6
  89. package/src/runtime/shared/config.mjs +11 -34
  90. package/src/runtime/shared/pristine-execution-contract.json +75 -0
  91. package/src/runtime/shared/pristine-execution.mjs +356 -0
  92. package/src/runtime/shared/provider-api-key.mjs +43 -0
  93. package/src/runtime/shared/provider-auth-binding.mjs +21 -0
  94. package/src/session-runtime/context-status.mjs +61 -13
  95. package/src/session-runtime/mcp-glue.mjs +29 -2
  96. package/src/session-runtime/plugin-mcp.mjs +7 -0
  97. package/src/session-runtime/resource-api.mjs +38 -5
  98. package/src/session-runtime/runtime-core.mjs +5 -1
  99. package/src/session-runtime/session-turn-api.mjs +14 -2
  100. package/src/session-runtime/settings-api.mjs +5 -0
  101. package/src/session-runtime/tool-catalog.mjs +13 -2
  102. package/src/session-runtime/tool-defs.mjs +1 -3
  103. package/src/standalone/agent-task-status.mjs +50 -11
  104. package/src/standalone/agent-tool/tool-def.mjs +1 -1
  105. package/src/standalone/explore-tool.mjs +257 -49
  106. package/src/standalone/seeds.mjs +1 -0
  107. package/src/tui/App.jsx +23 -10
  108. package/src/tui/app/use-transcript-scroll.mjs +4 -3
  109. package/src/tui/app/use-transcript-window.mjs +12 -21
  110. package/src/tui/components/ContextPanel.jsx +19 -25
  111. package/src/tui/dist/index.mjs +77 -65
  112. package/src/tui/engine/agent-envelope.mjs +16 -5
  113. package/src/tui/engine/labels.mjs +1 -1
  114. package/src/workflows/default/WORKFLOW.md +21 -51
  115. package/src/workflows/solo/WORKFLOW.md +12 -17
  116. package/src/workflows/solo-review/WORKFLOW.md +0 -47
@@ -63,7 +63,8 @@ function usageColor(pct) {
63
63
 
64
64
  function ProgressBar({ value = 0, total = 0, width = 24 }) {
65
65
  const pct = percent(value, total) ?? 0;
66
- const safeWidth = Math.max(8, Math.floor(width));
66
+ const safeWidth = Math.max(0, Math.floor(width));
67
+ if (safeWidth === 0) return null;
67
68
  const filled = Math.max(0, Math.min(safeWidth, Math.round((pct / 100) * safeWidth)));
68
69
  const empty = safeWidth - filled;
69
70
  return (
@@ -95,28 +96,27 @@ function bucketTokens(map, names) {
95
96
  return names.reduce((sum, name) => sum + finiteNumber(map?.[name]?.tokens), 0);
96
97
  }
97
98
 
98
- function bucketCount(map, names) {
99
- return names.reduce((sum, name) => sum + finiteNumber(map?.[name]?.count), 0);
100
- }
101
-
102
99
  function semanticTokens(semantic, names) {
103
100
  return names.reduce((sum, name) => sum + finiteNumber(semantic?.[name]?.tokens), 0);
104
101
  }
105
102
 
106
103
  function CategoryItem({ label, tokens, total, width }) {
107
- const labelWidth = Math.min(11, Math.max(7, Math.floor(width * 0.22)));
104
+ const rowWidth = Math.max(0, Math.floor(width));
105
+ const labelWidth = Math.min(11, Math.max(7, Math.floor(rowWidth * 0.22)));
108
106
  const pctWidth = 6;
109
- const tokenWidth = 7;
110
- const barWidth = Math.max(6, Math.min(20, width - labelWidth - pctWidth - tokenWidth - 4));
107
+ const structuralWidth = labelWidth + 1 + pctWidth + 1;
108
+ const flexibleWidth = Math.max(0, rowWidth - structuralWidth);
109
+ const tokenWidth = Math.min(7, flexibleWidth);
110
+ const barWidth = Math.max(0, flexibleWidth - tokenWidth);
111
111
  const pct = percent(tokens, total);
112
112
  return (
113
- <Box flexDirection="row" width={width}>
113
+ <Box flexDirection="row" width={rowWidth}>
114
114
  <Text color={theme.subtle}>{padCells(truncateText(label, labelWidth), labelWidth)}</Text>
115
115
  <Text color={theme.inactive}> </Text>
116
116
  <Text color={usageColor(pct)}>{padCells(percentLabel(tokens, total), pctWidth)}</Text>
117
117
  <ProgressBar value={tokens} total={total} width={barWidth} />
118
118
  <Text color={theme.inactive}> </Text>
119
- <Text color={theme.text}>{padCells(formatTokens(tokens), tokenWidth)}</Text>
119
+ <Text color={theme.text}>{padCells(truncateText(formatTokens(tokens), tokenWidth), tokenWidth)}</Text>
120
120
  </Box>
121
121
  );
122
122
  }
@@ -156,13 +156,9 @@ function ContextUsageView({ detail, columns }) {
156
156
  const usage = detail?.usage || {};
157
157
  const compaction = detail?.compaction || {};
158
158
  const messages = detail?.messages || {};
159
- const tools = detail?.tools || {};
160
- const toolIo = detail?.toolIo || {};
161
159
  const request = detail?.request || {};
162
160
  const lastApi = detail?.lastApi || {};
163
161
  const cache = detail?.cache || {};
164
- const extensions = detail?.extensions || {};
165
- const mcp = detail?.mcp || {};
166
162
  const semantic = messages.semantic || {};
167
163
  const schema = request.toolSchemaBreakdown || {};
168
164
  const usedTokens = finiteNumber(usage.usedTokens);
@@ -173,7 +169,6 @@ function ContextUsageView({ detail, columns }) {
173
169
  const pctText = `${percentLabel(usedTokens, windowTokens)} used`;
174
170
  const barWidth = Math.max(12, Math.min(34, innerWidth - stringWidth(summaryText) - stringWidth(pctText) - 5));
175
171
  const builtInToolTokens = bucketTokens(schema, ['code', 'web', 'mutation', 'channels', 'setup', 'other']);
176
- const builtInToolCount = bucketCount(schema, ['code', 'web', 'mutation', 'channels', 'setup', 'other']);
177
172
  const sessionTokens = semanticTokens(semantic, ['workspace', 'environment', 'other']);
178
173
  const compactionLine = metricValue([
179
174
  compaction.stage && compaction.stage !== 'pending' ? compaction.stage : '',
@@ -194,16 +189,15 @@ function ContextUsageView({ detail, columns }) {
194
189
  `cache ${cache.hitRate || 'N/A'}`,
195
190
  ]);
196
191
  const categories = [
197
- { label: 'Messages', tokens: semanticTokens(semantic, ['chat', 'assistant']), meta: '' },
198
- { label: 'Tools', tokens: builtInToolTokens, meta: `${tools.active || 0}/${tools.count || 0} active${builtInToolCount ? ` · ${builtInToolCount} defs` : ''}` },
199
- { label: 'MCP', tokens: bucketTokens(schema, ['mcp']), meta: `${mcp.connected || 0}/${mcp.configured || 0} servers` },
200
- { label: 'Skills', tokens: bucketTokens(schema, ['skills']), meta: `${extensions.skills || 0} skills` },
201
- { label: 'Memory', tokens: semanticTokens(semantic, ['memory']) + bucketTokens(schema, ['memory']), meta: 'core + recall tools' },
202
- { label: 'Session', tokens: sessionTokens, meta: 'workspace · environment' },
203
- { label: 'Workflow', tokens: semanticTokens(semantic, ['workflow']) + bucketTokens(schema, ['agents']), meta: 'workflow · agents' },
204
- { label: 'System', tokens: semanticTokens(semantic, ['system']), meta: 'rules · role catalog' },
205
- { label: 'Overhead', tokens: finiteNumber(request.overheadTokens), meta: 'request frame' },
206
- { label: 'Tool I/O', tokens: semanticTokens(semantic, ['toolResults']), meta: `${toolIo.calls || 0} calls · ${toolIo.results || 0} results` },
192
+ { label: 'Messages', tokens: semanticTokens(semantic, ['chat', 'assistant']) },
193
+ { label: 'Tools', tokens: builtInToolTokens },
194
+ { label: 'MCP', tokens: bucketTokens(schema, ['mcp']) },
195
+ { label: 'Skills', tokens: bucketTokens(schema, ['skills']) },
196
+ { label: 'Memory', tokens: semanticTokens(semantic, ['memory']) + bucketTokens(schema, ['memory']) },
197
+ { label: 'Session', tokens: sessionTokens },
198
+ { label: 'Workflow', tokens: semanticTokens(semantic, ['workflow']) + bucketTokens(schema, ['agents']) },
199
+ { label: 'System', tokens: semanticTokens(semantic, ['system']) },
200
+ { label: 'Tool I/O', tokens: semanticTokens(semantic, ['toolResults']) },
207
201
  ];
208
202
 
209
203
  return (
@@ -6120,7 +6120,8 @@ function usageColor(pct) {
6120
6120
  }
6121
6121
  function ProgressBar({ value = 0, total = 0, width = 24 }) {
6122
6122
  const pct = percent(value, total) ?? 0;
6123
- const safeWidth2 = Math.max(8, Math.floor(width));
6123
+ const safeWidth2 = Math.max(0, Math.floor(width));
6124
+ if (safeWidth2 === 0) return null;
6124
6125
  const filled = Math.max(0, Math.min(safeWidth2, Math.round(pct / 100 * safeWidth2)));
6125
6126
  const empty = safeWidth2 - filled;
6126
6127
  return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width: safeWidth2, children: [
@@ -6144,25 +6145,25 @@ function DetailLine({ label, value, columns }) {
6144
6145
  function bucketTokens(map, names) {
6145
6146
  return names.reduce((sum, name) => sum + finiteNumber(map?.[name]?.tokens), 0);
6146
6147
  }
6147
- function bucketCount(map, names) {
6148
- return names.reduce((sum, name) => sum + finiteNumber(map?.[name]?.count), 0);
6149
- }
6150
6148
  function semanticTokens(semantic, names) {
6151
6149
  return names.reduce((sum, name) => sum + finiteNumber(semantic?.[name]?.tokens), 0);
6152
6150
  }
6153
6151
  function CategoryItem({ label, tokens, total, width }) {
6154
- const labelWidth = Math.min(11, Math.max(7, Math.floor(width * 0.22)));
6152
+ const rowWidth = Math.max(0, Math.floor(width));
6153
+ const labelWidth = Math.min(11, Math.max(7, Math.floor(rowWidth * 0.22)));
6155
6154
  const pctWidth = 6;
6156
- const tokenWidth = 7;
6157
- const barWidth = Math.max(6, Math.min(20, width - labelWidth - pctWidth - tokenWidth - 4));
6155
+ const structuralWidth = labelWidth + 1 + pctWidth + 1;
6156
+ const flexibleWidth = Math.max(0, rowWidth - structuralWidth);
6157
+ const tokenWidth = Math.min(7, flexibleWidth);
6158
+ const barWidth = Math.max(0, flexibleWidth - tokenWidth);
6158
6159
  const pct = percent(tokens, total);
6159
- return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width, children: [
6160
+ return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width: rowWidth, children: [
6160
6161
  /* @__PURE__ */ jsx8(Text8, { color: theme.subtle, children: padCells3(truncateText3(label, labelWidth), labelWidth) }),
6161
6162
  /* @__PURE__ */ jsx8(Text8, { color: theme.inactive, children: " " }),
6162
6163
  /* @__PURE__ */ jsx8(Text8, { color: usageColor(pct), children: padCells3(percentLabel(tokens, total), pctWidth) }),
6163
6164
  /* @__PURE__ */ jsx8(ProgressBar, { value: tokens, total, width: barWidth }),
6164
6165
  /* @__PURE__ */ jsx8(Text8, { color: theme.inactive, children: " " }),
6165
- /* @__PURE__ */ jsx8(Text8, { color: theme.text, children: padCells3(formatTokens(tokens), tokenWidth) })
6166
+ /* @__PURE__ */ jsx8(Text8, { color: theme.text, children: padCells3(truncateText3(formatTokens(tokens), tokenWidth), tokenWidth) })
6166
6167
  ] });
6167
6168
  }
6168
6169
  function CategoryGrid({ categories, columns, total }) {
@@ -6187,13 +6188,9 @@ function ContextUsageView({ detail, columns }) {
6187
6188
  const usage = detail?.usage || {};
6188
6189
  const compaction = detail?.compaction || {};
6189
6190
  const messages = detail?.messages || {};
6190
- const tools = detail?.tools || {};
6191
- const toolIo = detail?.toolIo || {};
6192
6191
  const request = detail?.request || {};
6193
6192
  const lastApi = detail?.lastApi || {};
6194
6193
  const cache = detail?.cache || {};
6195
- const extensions = detail?.extensions || {};
6196
- const mcp = detail?.mcp || {};
6197
6194
  const semantic = messages.semantic || {};
6198
6195
  const schema = request.toolSchemaBreakdown || {};
6199
6196
  const usedTokens = finiteNumber(usage.usedTokens);
@@ -6204,7 +6201,6 @@ function ContextUsageView({ detail, columns }) {
6204
6201
  const pctText = `${percentLabel(usedTokens, windowTokens)} used`;
6205
6202
  const barWidth = Math.max(12, Math.min(34, innerWidth - stringWidth4(summaryText) - stringWidth4(pctText) - 5));
6206
6203
  const builtInToolTokens = bucketTokens(schema, ["code", "web", "mutation", "channels", "setup", "other"]);
6207
- const builtInToolCount = bucketCount(schema, ["code", "web", "mutation", "channels", "setup", "other"]);
6208
6204
  const sessionTokens = semanticTokens(semantic, ["workspace", "environment", "other"]);
6209
6205
  const compactionLine = metricValue([
6210
6206
  compaction.stage && compaction.stage !== "pending" ? compaction.stage : "",
@@ -6225,16 +6221,15 @@ function ContextUsageView({ detail, columns }) {
6225
6221
  `cache ${cache.hitRate || "N/A"}`
6226
6222
  ]);
6227
6223
  const categories = [
6228
- { label: "Messages", tokens: semanticTokens(semantic, ["chat", "assistant"]), meta: "" },
6229
- { label: "Tools", tokens: builtInToolTokens, meta: `${tools.active || 0}/${tools.count || 0} active${builtInToolCount ? ` \xB7 ${builtInToolCount} defs` : ""}` },
6230
- { label: "MCP", tokens: bucketTokens(schema, ["mcp"]), meta: `${mcp.connected || 0}/${mcp.configured || 0} servers` },
6231
- { label: "Skills", tokens: bucketTokens(schema, ["skills"]), meta: `${extensions.skills || 0} skills` },
6232
- { label: "Memory", tokens: semanticTokens(semantic, ["memory"]) + bucketTokens(schema, ["memory"]), meta: "core + recall tools" },
6233
- { label: "Session", tokens: sessionTokens, meta: "workspace \xB7 environment" },
6234
- { label: "Workflow", tokens: semanticTokens(semantic, ["workflow"]) + bucketTokens(schema, ["agents"]), meta: "workflow \xB7 agents" },
6235
- { label: "System", tokens: semanticTokens(semantic, ["system"]), meta: "rules \xB7 role catalog" },
6236
- { label: "Overhead", tokens: finiteNumber(request.overheadTokens), meta: "request frame" },
6237
- { label: "Tool I/O", tokens: semanticTokens(semantic, ["toolResults"]), meta: `${toolIo.calls || 0} calls \xB7 ${toolIo.results || 0} results` }
6224
+ { label: "Messages", tokens: semanticTokens(semantic, ["chat", "assistant"]) },
6225
+ { label: "Tools", tokens: builtInToolTokens },
6226
+ { label: "MCP", tokens: bucketTokens(schema, ["mcp"]) },
6227
+ { label: "Skills", tokens: bucketTokens(schema, ["skills"]) },
6228
+ { label: "Memory", tokens: semanticTokens(semantic, ["memory"]) + bucketTokens(schema, ["memory"]) },
6229
+ { label: "Session", tokens: sessionTokens },
6230
+ { label: "Workflow", tokens: semanticTokens(semantic, ["workflow"]) + bucketTokens(schema, ["agents"]) },
6231
+ { label: "System", tokens: semanticTokens(semantic, ["system"]) },
6232
+ { label: "Tool I/O", tokens: semanticTokens(semantic, ["toolResults"]) }
6238
6233
  ];
6239
6234
  return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", width: "100%", children: [
6240
6235
  /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width: "100%", children: [
@@ -11588,7 +11583,7 @@ function useTranscriptScroll({
11588
11583
  if (appliedDelta !== 0) cancelTranscriptFollow();
11589
11584
  scrollTargetRef.current = target;
11590
11585
  if (appliedDelta !== 0) {
11591
- if (target <= Math.max(0, Number(transcriptBottomSlackRowsRef.current) || 0)) {
11586
+ if (target === 0) {
11592
11587
  transcriptAnchorRef.current = null;
11593
11588
  transcriptAnchorDirtyRef.current = false;
11594
11589
  } else {
@@ -11799,7 +11794,6 @@ function useTranscriptWindow({
11799
11794
  setMeasuredRowsVersion
11800
11795
  }) {
11801
11796
  const transcriptTotalRowsRef = useRef9(0);
11802
- const preservedScrollDeltaRef = useRef9(0);
11803
11797
  const committedMaxScrollRowsRef = useRef9(0);
11804
11798
  const incrementalRowIndexCacheRef = useRef9(null);
11805
11799
  const prevViewportGeomRef = useRef9({ contentHeight: 0, floatingPanelRows: 0 });
@@ -11831,7 +11825,7 @@ function useTranscriptWindow({
11831
11825
  }, []);
11832
11826
  const streamingTailItem = streamingTail?.kind === "assistant" && streamingTail.streaming ? streamingTail : null;
11833
11827
  const scrolledUpRowsForPin = Math.max(0, Number(scrollTargetRef.current) || 0);
11834
- const transcriptPinnedForStreaming = followingRef.current || scrolledUpRowsForPin <= transcriptBottomSlackRows;
11828
+ const transcriptPinnedForStreaming = followingRef.current || scrolledUpRowsForPin === 0;
11835
11829
  setStreamingBottomPinned(transcriptPinnedForStreaming);
11836
11830
  const tailRows = streamingTailItem ? estimateTranscriptItemRowsCached(streamingTailItem, frameColumns, toolOutputExpanded) : 0;
11837
11831
  const tailSig = streamingTailItem ? `${streamingTailItem.id}:${tailRows}` : "_";
@@ -11843,7 +11837,7 @@ function useTranscriptWindow({
11843
11837
  );
11844
11838
  const transcriptStructureSig = `${revision}#${tailSig}`;
11845
11839
  const transcriptStreamingActive = !!streamingTailItem;
11846
- const scrolledUpForStreamingMeasure = scrolledUpRowsForPin > transcriptBottomSlackRows;
11840
+ const scrolledUpForStreamingMeasure = scrolledUpRowsForPin > 0;
11847
11841
  const prevEstimateGeometry = transcriptGeomRef.current?.suppressMeasuredRowHeights === true;
11848
11842
  const hasStreamingReadingAnchor = !!transcriptAnchorRef.current || transcriptAnchorDirtyRef.current;
11849
11843
  const bottomPinnedForMeasure = transcriptPinnedForStreaming;
@@ -11859,7 +11853,7 @@ function useTranscriptWindow({
11859
11853
  }), [revision, tailSig, frameColumns, toolOutputExpanded, measuredRowsVersion, suppressMeasuredRowHeights]);
11860
11854
  const hasReadingAnchor = !!transcriptAnchorRef.current && !transcriptAnchorDirtyRef.current;
11861
11855
  const scrolledUpRows = Math.max(0, Number(scrollTargetRef.current) || 0);
11862
- const scrolledUp = scrolledUpRows > transcriptBottomSlackRows;
11856
+ const scrolledUp = scrolledUpRows > 0;
11863
11857
  const anchorLockActive = hasReadingAnchor && !followingRef.current && scrolledUp;
11864
11858
  const targetNearBottom = followingRef.current || !scrolledUp;
11865
11859
  const nearBottomWithoutAnchor = !transcriptAnchorRef.current && !transcriptAnchorDirtyRef.current && targetNearBottom;
@@ -12026,7 +12020,7 @@ function useTranscriptWindow({
12026
12020
  overlayHintAttachItemIndex = i;
12027
12021
  break;
12028
12022
  }
12029
- const transcriptTailPinned = Math.max(0, Number(transcriptWindow.effectiveScrollOffset) || 0) <= transcriptBottomSlackRows;
12023
+ const transcriptTailPinned = Math.max(0, Number(transcriptWindow.effectiveScrollOffset) || 0) === 0;
12030
12024
  const overlayHintOnLastItem = overlayHintRequested && floatingPanelRows <= 0 && transcriptWindow.bottomSpacerRows === 0 && transcriptTailPinned && overlayHintAttachItemIndex >= 0;
12031
12025
  const overlayHintFallbackRow = overlayHintRequested && floatingPanelRows <= 0 && transcriptGuardRows > 0 && !overlayHintOnLastItem;
12032
12026
  useLayoutEffect3(() => {
@@ -12113,7 +12107,7 @@ function useTranscriptWindow({
12113
12107
  const currentPosition = Math.max(0, Number(scrollPositionRef.current) || 0);
12114
12108
  const currentOffset = Math.max(0, Number(scrollOffset) || 0);
12115
12109
  const maxRows = Math.max(0, Number(transcriptWindow.maxScrollRows) || 0);
12116
- const nearBottom = followingRef.current || currentTarget <= transcriptBottomSlackRows;
12110
+ const nearBottom = followingRef.current || currentTarget === 0;
12117
12111
  const pinnedToBottom = nearBottom;
12118
12112
  const activeReadingAnchor = !!transcriptAnchorRef.current && !transcriptAnchorDirtyRef.current && !followingRef.current && !nearBottom;
12119
12113
  const followOnGrowth = followingRef.current && rowDelta > 0 && !activeReadingAnchor;
@@ -12171,22 +12165,20 @@ function useTranscriptWindow({
12171
12165
  stopSmoothScroll();
12172
12166
  scrollTargetRef.current = desired;
12173
12167
  scrollPositionRef.current = Math.max(0, Math.min(maxRows, currentPosition + appliedDelta));
12174
- preservedScrollDeltaRef.current += appliedDelta;
12175
12168
  setScrollOffset(Math.max(0, Math.round(desired)));
12176
- }, [transcriptWindow.totalRows, transcriptWindow.maxScrollRows, transcriptRowIndex, transcriptContentHeight, transcriptBottomSlackRows, scrollOffset, stopSmoothScroll]);
12169
+ }, [transcriptWindow.totalRows, transcriptWindow.maxScrollRows, transcriptRowIndex, transcriptContentHeight, scrollOffset, stopSmoothScroll]);
12177
12170
  useLayoutEffect3(() => {
12178
- if (transcriptBottomSlackRows <= 0) return;
12179
12171
  if (transcriptAnchorRef.current || transcriptAnchorDirtyRef.current || followingRef.current) return;
12180
12172
  const currentTarget = Math.max(0, Number(scrollTargetRef.current) || 0);
12181
12173
  const currentPosition = Math.max(0, Number(scrollPositionRef.current) || 0);
12182
12174
  const currentOffset = Math.max(0, Number(scrollOffset) || 0);
12175
+ if (currentTarget !== 0) return;
12183
12176
  if (Math.max(currentTarget, currentPosition, currentOffset) === 0) return;
12184
- if (Math.max(currentTarget, currentPosition, currentOffset) > transcriptBottomSlackRows) return;
12185
12177
  stopSmoothScroll();
12186
12178
  scrollTargetRef.current = 0;
12187
12179
  scrollPositionRef.current = 0;
12188
12180
  setScrollOffset(0);
12189
- }, [transcriptBottomSlackRows, scrollOffset, stopSmoothScroll]);
12181
+ }, [scrollOffset, stopSmoothScroll]);
12190
12182
  useLayoutEffect3(() => {
12191
12183
  const top = Math.max(0, Number(transcriptViewportRef.current?.top) || 0);
12192
12184
  const next = {
@@ -12195,11 +12187,6 @@ function useTranscriptWindow({
12195
12187
  totalRows: Math.max(0, Number(transcriptWindow.totalRows) || 0),
12196
12188
  scrollOffset: Math.max(0, Number(transcriptWindow.effectiveScrollOffset) || 0)
12197
12189
  };
12198
- const preservedDelta = Number(preservedScrollDeltaRef.current) || 0;
12199
- if (preservedDelta !== 0) {
12200
- next.scrollOffset = Math.max(0, next.scrollOffset + preservedDelta);
12201
- preservedScrollDeltaRef.current = 0;
12202
- }
12203
12190
  const previous = selectionLayoutRef.current;
12204
12191
  selectionLayoutRef.current = next;
12205
12192
  if (!previous || !dragRef.current.rect || dragRef.current.active) return;
@@ -14371,7 +14358,7 @@ function createOnboardingSteps({
14371
14358
  // src/runtime/shared/config.mjs
14372
14359
  import { readFileSync as readFileSync5, statSync as statSync4, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
14373
14360
  import { join as join6, dirname as dirname6 } from "path";
14374
- import { createRequire as createRequire2 } from "module";
14361
+ import { createRequire as createRequire3 } from "module";
14375
14362
 
14376
14363
  // src/runtime/shared/atomic-file.mjs
14377
14364
  import {
@@ -15080,8 +15067,24 @@ function backupUserData(dataDir, reason = "snapshot") {
15080
15067
  }
15081
15068
  var CASE_INSENSITIVE = process.platform === "win32";
15082
15069
 
15070
+ // src/runtime/shared/provider-api-key.mjs
15071
+ import { createRequire as createRequire2 } from "node:module";
15072
+ var require2 = createRequire2(import.meta.url);
15073
+ var { getSecret } = require2("../../lib/keychain-cjs.cjs");
15074
+ var AGENT_PROVIDER_ENV = Object.freeze({
15075
+ openai: "OPENAI_API_KEY",
15076
+ anthropic: "ANTHROPIC_API_KEY",
15077
+ gemini: "GEMINI_API_KEY",
15078
+ deepseek: "DEEPSEEK_API_KEY",
15079
+ xai: "XAI_API_KEY",
15080
+ "opencode-go": "OPENCODE_API_KEY"
15081
+ });
15082
+ var AGENT_PROVIDER_ENV_ALIASES = Object.freeze({
15083
+ xai: ["GROK_API_KEY"]
15084
+ });
15085
+
15083
15086
  // src/runtime/shared/config.mjs
15084
- var _require = createRequire2(import.meta.url);
15087
+ var _require = createRequire3(import.meta.url);
15085
15088
  var { getSecret: _getSecret, setSecret: _setSecret, deleteSecret: _deleteSecret, hasSecret: _hasSecret } = _require("../../lib/keychain-cjs.cjs");
15086
15089
  var DATA_DIR = resolvePluginData();
15087
15090
  var CONFIG_PATH = join6(DATA_DIR, "mixdog-config.json");
@@ -15280,17 +15283,6 @@ var SECRET_ACCOUNTS = Object.freeze({
15280
15283
  openaiUsageSessionKey: "agent.openai.usageSessionKey",
15281
15284
  opencodeGoAuthCookie: "agent.opencode-go.authCookie"
15282
15285
  });
15283
- var AGENT_PROVIDER_ENV = Object.freeze({
15284
- openai: "OPENAI_API_KEY",
15285
- anthropic: "ANTHROPIC_API_KEY",
15286
- gemini: "GEMINI_API_KEY",
15287
- deepseek: "DEEPSEEK_API_KEY",
15288
- xai: "XAI_API_KEY",
15289
- "opencode-go": "OPENCODE_API_KEY"
15290
- });
15291
- var AGENT_PROVIDER_ENV_ALIASES = Object.freeze({
15292
- xai: ["GROK_API_KEY"]
15293
- });
15294
15286
 
15295
15287
  // src/tui/lib/voice-setup.mjs
15296
15288
  var _voiceRuntimeFetcherPromise = null;
@@ -20274,7 +20266,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20274
20266
  return;
20275
20267
  }
20276
20268
  if (count === previousCount || dragRef.current.active) return;
20277
- if (scrollTargetRef.current <= transcriptBottomSlackRows || followingRef.current) followingRef.current = true;
20269
+ if (scrollTargetRef.current === 0 || followingRef.current) followingRef.current = true;
20278
20270
  }, [state.items.length, resetTranscriptScroll]);
20279
20271
  const requestExit = useCallback6(() => {
20280
20272
  if (exitRequestedRef.current) return;
@@ -20511,7 +20503,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20511
20503
  }, 0);
20512
20504
  };
20513
20505
  const openContextPicker = () => {
20514
- const tools = store.toolsStatus?.() || { activeCount: 0, count: 0, activeTools: [] };
20506
+ const tools = store.toolsStatus?.() || {
20507
+ activeCount: 0,
20508
+ count: 0,
20509
+ mcpToolCount: 0,
20510
+ activeMcpToolCount: 0,
20511
+ activeTools: []
20512
+ };
20515
20513
  const mcp = store.mcpStatus?.() || { connectedCount: 0, configuredCount: 0, failedCount: 0 };
20516
20514
  const skills = store.skillsStatus?.() || { count: 0 };
20517
20515
  const plugins = store.pluginsStatus?.() || { count: 0 };
@@ -20519,6 +20517,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20519
20517
  const usage = context.usage || {};
20520
20518
  const messages = context.messages || {};
20521
20519
  const request = context.request || {};
20520
+ const schemaBreakdown = request.toolSchemaBreakdown || {};
20521
+ const schemaTokensFor = (buckets) => buckets.reduce(
20522
+ (sum, bucket) => sum + Number(schemaBreakdown?.[bucket]?.tokens || 0),
20523
+ 0
20524
+ );
20525
+ const builtInToolSchemaTokens = schemaTokensFor(["code", "web", "mutation", "channels", "setup", "other"]);
20526
+ const mcpToolSchemaTokens = schemaTokensFor(["mcp"]);
20522
20527
  const compaction = context.compaction || {};
20523
20528
  const windowTokens = Number(context.contextWindow || state.contextWindow || context.rawContextWindow || state.rawContextWindow || 0);
20524
20529
  const rawWindowTokens = Number(context.rawContextWindow || state.rawContextWindow || windowTokens || 0);
@@ -20586,7 +20591,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20586
20591
  {
20587
20592
  value: "tools",
20588
20593
  label: "Tools",
20589
- description: `${fmt(request.toolSchemaTokens)} schema tokens (${pct(request.toolSchemaTokens)}) \xB7 ${tools.activeCount || 0}/${tools.count || 0} active`,
20594
+ description: `${fmt(builtInToolSchemaTokens)} schema tokens (${pct(builtInToolSchemaTokens)}) \xB7 ${tools.activeCount || 0}/${tools.count || 0} active`,
20590
20595
  _action: "tools"
20591
20596
  },
20592
20597
  {
@@ -20660,7 +20665,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20660
20665
  semantic: messages.semantic
20661
20666
  },
20662
20667
  tools: {
20663
- schemaTokens: request.toolSchemaTokens,
20668
+ schemaTokens: builtInToolSchemaTokens,
20664
20669
  active: tools.activeCount,
20665
20670
  count: tools.count
20666
20671
  },
@@ -20691,7 +20696,10 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20691
20696
  mcp: {
20692
20697
  connected: mcp.connectedCount,
20693
20698
  configured: mcp.configuredCount,
20694
- failed: mcp.failedCount
20699
+ failed: mcp.failedCount,
20700
+ tools: tools.mcpToolCount,
20701
+ activeTools: tools.activeMcpToolCount,
20702
+ schemaTokens: mcpToolSchemaTokens
20695
20703
  }
20696
20704
  },
20697
20705
  rows: contextRows
@@ -22225,7 +22233,7 @@ function compactEventDetail(event = {}) {
22225
22233
  const trigger = String(event.trigger || "").toLowerCase();
22226
22234
  if (trigger === "reactive") parts.push("reactive");
22227
22235
  else if (trigger === "manual") parts.push("manual");
22228
- const before = Number(event.beforeTokens ?? event.pressureTokens ?? 0);
22236
+ const before = Number(event.pressureTokens ?? event.beforeTokens ?? 0);
22229
22237
  const after = Number(event.afterTokens ?? 0);
22230
22238
  const fmtTok = (n) => {
22231
22239
  const v = Number(n) || 0;
@@ -22683,7 +22691,10 @@ function parseAgentJob(text) {
22683
22691
  const targetMatch = /^target:\s*(.+)$/m.exec(value);
22684
22692
  const agentMatch = /^agent:\s*(.+)$/m.exec(value);
22685
22693
  const presetMatch = /^preset:\s*(.+)$/m.exec(value);
22686
- const modelMatch = /^model:\s*([^/\s]+)\/(.+)$/m.exec(value);
22694
+ const isAgentEnvelope = /^agent task:\s*/mi.test(value) || /^agent result\b/mi.test(value) || /^surface:\s*agent\s*$/mi.test(value);
22695
+ const providerMatch = isAgentEnvelope ? /^provider:\s*(.+)$/m.exec(value) : null;
22696
+ const modelLineMatch = isAgentEnvelope ? /^model:\s*(.+)$/m.exec(value) : null;
22697
+ const providerModelMatch = /^([^/\s]+)\/(.+)$/.exec(modelLineMatch?.[1]?.trim() || "");
22687
22698
  const effortMatch = /^effort:\s*(.+)$/m.exec(value);
22688
22699
  const fastMatch = /^fast:\s*(on|off|true|false)$/m.exec(value);
22689
22700
  return {
@@ -22693,8 +22704,8 @@ function parseAgentJob(text) {
22693
22704
  target: (targetMatch?.[1] || "").trim(),
22694
22705
  agent: (agentMatch?.[1] || "").trim(),
22695
22706
  preset: (presetMatch?.[1] || "").trim(),
22696
- provider: (modelMatch?.[1] || "").trim(),
22697
- model: (modelMatch?.[2] || "").trim(),
22707
+ provider: (providerMatch?.[1] || providerModelMatch?.[1] || "").trim(),
22708
+ model: (providerModelMatch?.[2] || modelLineMatch?.[1] || "").trim(),
22698
22709
  effort: (effortMatch?.[1] || "").trim(),
22699
22710
  fast: fastMatch ? /^(on|true)$/i.test(fastMatch[1]) : void 0
22700
22711
  };
@@ -22711,8 +22722,9 @@ function agentArgsWithResultMetadata(args, parsed) {
22711
22722
  if (parsed.taskId) next.task_id = parsed.taskId;
22712
22723
  if (parsed.agent) next.agent = parsed.agent;
22713
22724
  if (parsed.preset) next.preset = parsed.preset;
22714
- if (parsed.provider) next.provider = parsed.provider;
22715
- if (parsed.model) next.model = parsed.model;
22725
+ const hasExplicitProvider = [next.provider, next.providerId, next.provider_id].some((value) => String(value || "").trim());
22726
+ if (parsed.provider && !hasExplicitProvider) next.provider = parsed.provider;
22727
+ if (parsed.model && !String(next.model || "").trim()) next.model = parsed.model;
22716
22728
  if (parsed.effort) next.effort = parsed.effort;
22717
22729
  if (parsed.fast !== void 0) next.fast = parsed.fast;
22718
22730
  if (!next.tag && parsed.target) {
@@ -251,7 +251,16 @@ export function parseAgentJob(text) {
251
251
  const targetMatch = /^target:\s*(.+)$/m.exec(value);
252
252
  const agentMatch = /^agent:\s*(.+)$/m.exec(value);
253
253
  const presetMatch = /^preset:\s*(.+)$/m.exec(value);
254
- const modelMatch = /^model:\s*([^/\s]+)\/(.+)$/m.exec(value);
254
+ // Spawn acknowledgements normally use `model: provider/model`, but agent
255
+ // completion envelopes carry the resolved route as separate provider/model
256
+ // fields. A generic tool result can also contain task_id/provider/model
257
+ // diagnostics, so only recognize either route shape on an agent envelope.
258
+ const isAgentEnvelope = /^agent task:\s*/mi.test(value)
259
+ || /^agent result\b/mi.test(value)
260
+ || /^surface:\s*agent\s*$/mi.test(value);
261
+ const providerMatch = isAgentEnvelope ? /^provider:\s*(.+)$/m.exec(value) : null;
262
+ const modelLineMatch = isAgentEnvelope ? /^model:\s*(.+)$/m.exec(value) : null;
263
+ const providerModelMatch = /^([^/\s]+)\/(.+)$/.exec(modelLineMatch?.[1]?.trim() || '');
255
264
  const effortMatch = /^effort:\s*(.+)$/m.exec(value);
256
265
  const fastMatch = /^fast:\s*(on|off|true|false)$/m.exec(value);
257
266
  return {
@@ -261,8 +270,8 @@ export function parseAgentJob(text) {
261
270
  target: (targetMatch?.[1] || '').trim(),
262
271
  agent: (agentMatch?.[1] || '').trim(),
263
272
  preset: (presetMatch?.[1] || '').trim(),
264
- provider: (modelMatch?.[1] || '').trim(),
265
- model: (modelMatch?.[2] || '').trim(),
273
+ provider: (providerMatch?.[1] || providerModelMatch?.[1] || '').trim(),
274
+ model: (providerModelMatch?.[2] || modelLineMatch?.[1] || '').trim(),
266
275
  effort: (effortMatch?.[1] || '').trim(),
267
276
  fast: fastMatch ? /^(on|true)$/i.test(fastMatch[1]) : undefined,
268
277
  };
@@ -284,8 +293,10 @@ export function agentArgsWithResultMetadata(args, parsed) {
284
293
  if (parsed.taskId) next.task_id = parsed.taskId;
285
294
  if (parsed.agent) next.agent = parsed.agent;
286
295
  if (parsed.preset) next.preset = parsed.preset;
287
- if (parsed.provider) next.provider = parsed.provider;
288
- if (parsed.model) next.model = parsed.model;
296
+ const hasExplicitProvider = [next.provider, next.providerId, next.provider_id]
297
+ .some((value) => String(value || '').trim());
298
+ if (parsed.provider && !hasExplicitProvider) next.provider = parsed.provider;
299
+ if (parsed.model && !String(next.model || '').trim()) next.model = parsed.model;
289
300
  if (parsed.effort) next.effort = parsed.effort;
290
301
  if (parsed.fast !== undefined) next.fast = parsed.fast;
291
302
  if (!next.tag && parsed.target) {
@@ -50,7 +50,7 @@ export function compactEventDetail(event = {}) {
50
50
  const trigger = String(event.trigger || '').toLowerCase();
51
51
  if (trigger === 'reactive') parts.push('reactive');
52
52
  else if (trigger === 'manual') parts.push('manual');
53
- const before = Number(event.beforeTokens ?? event.pressureTokens ?? 0);
53
+ const before = Number(event.pressureTokens ?? event.beforeTokens ?? 0);
54
54
  const after = Number(event.afterTokens ?? 0);
55
55
  const fmtTok = (n) => {
56
56
  const v = Number(n) || 0;
@@ -7,57 +7,27 @@ agents: worker, heavy-worker, reviewer, debugger, maintainer
7
7
 
8
8
  # Default
9
9
 
10
- GATE: Before approval, only read-only investigation/planning while consulting.
11
- Approval is a later explicit user message after the latest plan ("do it",
12
- "proceed", "go ahead"). Initial/additional/changed requests reset planning;
13
- approval with a scope change needs a revised plan and fresh approval. Before
14
- approval: no edits, state mutation, or delegation.
10
+ Lead is the orchestrator: consult the user and build the plan together.
11
+ Before the user explicitly approves the latest plan, work is read-only
12
+ investigation and planning — no edits, no state mutation, no delegation.
13
+ A new or changed request resets planning; a scope change requires fresh
14
+ approval.
15
15
 
16
- Lead owns three duties, in order: plan set the direction of the work from its own
17
- understanding of the task; delegate hand execution to the matching agent; verify —
18
- judge what comes back. Understanding is never delegated: without its own plan a Lead
19
- can neither brief nor judge. Lead-direct work is allowed only for pure read/analysis,
20
- git/configuration, or when the user explicitly supplies both the exact target and exact
21
- replacement/output. Never infer an exemption from a task name, file count, or
22
- perceived difficulty. Every other implementation, reverse engineering, debugging
23
- application, or artifact generation delegates to the matching agent. Debugger is an
24
- escalation role reserved for explicitly requested debugging or a bug surviving 2+ fix
25
- cycles — not a default owner of analysis or reverse engineering, which route to
26
- Worker/Heavy Worker like any implementation. Worker applies an established bounded
27
- change or fully specified artifact; Heavy Worker owns implementation that must establish
28
- the change through investigation or staged delivery. Applying a Debugger result is
29
- implementation, not diagnosis.
16
+ On approval, fan out at maximum width: one agent per independent scope, all
17
+ spawned in one turn; only a scope that depends on another's output waits.
30
18
 
31
- 1. Plan: draft before any implementation; settle scope/plan, ask if ambiguous,
32
- then await the gate.
33
- 2. Delegate: one agent per ready independent scope; spawn all ready scopes in
34
- one turn, with no count cap. Serialize only a real dependency, overlapping
35
- write, or inseparable coupling. Briefs follow the Lead brief contract.
36
- After async spawn, end the turn.
37
- 3. Review: triage by the work performed, never by the size, form, or destination
38
- of its result — a one-line answer can carry non-trivial work, and a
39
- conversational reply is not automatically exempt. Trivial work — a routine
40
- lookup answered directly, routine git/configuration confirmed by its own
41
- mechanical check, a change whose correctness is test/build/diff-obvious, or
42
- applying an exact target and replacement the user supplied — ships on shell
43
- self-verification alone, and only after that check actually ran and passed.
44
- Non-trivial work — multi-step reasoning, interpretation, investigation, or
45
- anything whose correctness is not verifiable at a glance — gets a Reviewer
46
- cross-check (one reviewer per scope, kept across the fix loop; all ready
47
- reviewers spawn in one turn) and Lead integration/cross-scope verification
48
- in parallel, whoever performed it, Lead solo included. Debugger analysis
49
- cannot substitute for implementation review: applying a Debugger result triggers
50
- the same Reviewer + Lead verification. Reviewer independently judges risk,
51
- intent, boundaries; Lead checks acceptance/interactions, not duplicate
52
- same-scope work. High-risk scopes add distinct lenses. Synthesize one
53
- verdict; send merged fixes to the original live session; loop fix ->
54
- re-verify (same Reviewer + Lead re-check) until clean. Debugger first for
55
- requested debugging or a bug surviving 2+ fix cycles. Agent reports relay
56
- scope, verdict, next work as in-progress, never conclusions.
57
- 4. Report: final (not interim) report compares work to approved plan and gives
58
- verified result; never forward raw agent output. Ask about ship/deploy when
59
- relevant. Build/deploy/commit/push require an explicit user request after
60
- issue-free feedback; implementation approval alone is insufficient.
19
+ Route by complexity: simple, well-understood implementation goes to Worker;
20
+ complex or investigative implementation goes to Heavy Worker; Lead itself
21
+ edits only a local, one-turn configuration/git change. Debugger only on a
22
+ defect needing deep root-cause analysis or a bug surviving 2+ review/fix
23
+ cycles.
61
24
 
62
- On outcome/direction change, pause and re-consult; otherwise continue approved
63
- work without reapproval.
25
+ Every implementation gets its own Reviewer, attached per scope only the
26
+ local Lead-direct edits above are exempt. Keep the same reviewer through the
27
+ fix loop and repeat fix -> re-verify until clean; Lead cross-verifies in
28
+ parallel with the Reviewer.
29
+
30
+ Report the verified result against the approved plan. Build, deploy, commit,
31
+ and push happen only on an explicit user request.
32
+
33
+ On direction change, pause and re-consult the user.
@@ -7,22 +7,17 @@ agents:
7
7
 
8
8
  # Solo
9
9
 
10
- GATE: Before approval, only read-only investigation/planning while consulting.
11
- Approval is a later explicit user message after the latest plan ("do it",
12
- "proceed", "go ahead"). Initial/additional/changed requests reset planning;
13
- approval with a scope change needs a revised plan and fresh approval. Before
14
- approval: no edits or state mutation.
10
+ Lead handles everything directly: consult the user and build the plan
11
+ together. Before the user explicitly approves the latest plan, work is
12
+ read-only investigation and planning — no edits, no state mutation. A new or
13
+ changed request resets planning; a scope change requires fresh approval.
15
14
 
16
- 1. Plan: draft before any implementation; settle scope/plan, ask if ambiguous,
17
- then await the gate.
18
- 2. Execute: after approval, Lead does all work. Never spawn, send, delegate, or
19
- ask agents to work. Complete execution/in-scope fixes without reapproval;
20
- interim updates are in-progress, never conclusions.
21
- 3. Verify: Lead checks/fixes directly until clean or reports a blocker.
22
- 4. Report: final (not interim) report compares work to approved plan and gives
23
- verification, remaining risk/next step. Build/deploy/commit/push require an
24
- explicit user request after issue-free feedback; implementation approval
25
- alone is insufficient.
15
+ On approval, Lead executes all work itself — never spawn, send, or delegate
16
+ to agents. Complete in-scope fixes without reapproval.
26
17
 
27
- On outcome/direction change, pause and re-consult; otherwise continue approved
28
- work without reapproval.
18
+ Verify directly: check and fix until clean, or report the blocker.
19
+
20
+ Report the verified result against the approved plan. Build, deploy, commit,
21
+ and push happen only on an explicit user request.
22
+
23
+ On direction change, pause and re-consult the user.