mixdog 0.9.38 → 0.9.40

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 (124) hide show
  1. package/package.json +9 -4
  2. package/scripts/abort-recovery-test.mjs +43 -2
  3. package/scripts/agent-tag-reuse-smoke.mjs +146 -6
  4. package/scripts/agent-terminal-reap-test.mjs +127 -0
  5. package/scripts/agent-trace-io-test.mjs +69 -0
  6. package/scripts/code-graph-disk-hit-test.mjs +224 -0
  7. package/scripts/execution-completion-dedup-test.mjs +157 -0
  8. package/scripts/execution-pending-resume-kick-test.mjs +57 -2
  9. package/scripts/execution-resume-esc-integration-test.mjs +176 -0
  10. package/scripts/explore-bench.mjs +38 -2
  11. package/scripts/explore-prompt-policy-test.mjs +152 -11
  12. package/scripts/find-fuzzy-hidden-test.mjs +122 -0
  13. package/scripts/internal-comms-bench-test.mjs +226 -0
  14. package/scripts/internal-comms-bench.mjs +185 -58
  15. package/scripts/internal-comms-smoke.mjs +171 -23
  16. package/scripts/live-worker-smoke.mjs +38 -2
  17. package/scripts/memory-cycle-routing-test.mjs +111 -0
  18. package/scripts/memory-rule-contract-test.mjs +93 -0
  19. package/scripts/output-style-smoke.mjs +2 -2
  20. package/scripts/rg-runner-test.mjs +240 -0
  21. package/scripts/routing-corpus-test.mjs +349 -0
  22. package/scripts/routing-corpus.mjs +211 -32
  23. package/scripts/session-orphan-sweep-test.mjs +83 -0
  24. package/scripts/steering-drain-buckets-test.mjs +316 -2
  25. package/scripts/tool-smoke.mjs +21 -13
  26. package/scripts/tool-tui-presentation-test.mjs +202 -0
  27. package/scripts/tui-transcript-perf-test.mjs +279 -0
  28. package/src/agents/heavy-worker/AGENT.md +10 -7
  29. package/src/agents/reviewer/AGENT.md +6 -4
  30. package/src/agents/worker/AGENT.md +7 -5
  31. package/src/rules/agent/00-common.md +4 -4
  32. package/src/rules/agent/00-core.md +11 -14
  33. package/src/rules/agent/20-skip-protocol.md +3 -3
  34. package/src/rules/agent/30-explorer.md +50 -60
  35. package/src/rules/agent/40-cycle1-agent.md +15 -24
  36. package/src/rules/agent/41-cycle2-agent.md +33 -57
  37. package/src/rules/agent/42-cycle3-agent.md +28 -42
  38. package/src/rules/lead/01-general.md +7 -10
  39. package/src/rules/lead/lead-brief.md +11 -14
  40. package/src/rules/lead/lead-tool.md +6 -5
  41. package/src/rules/shared/01-tool.md +44 -45
  42. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +44 -1
  43. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +20 -5
  44. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +21 -1
  45. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +17 -38
  46. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +17 -8
  47. package/src/runtime/agent/orchestrator/session/context-utils.mjs +270 -78
  48. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +2 -1
  49. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +15 -0
  50. package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +123 -0
  51. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +15 -2
  52. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +67 -2
  53. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
  54. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  55. package/src/runtime/agent/orchestrator/session/store.mjs +232 -43
  56. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +43 -1
  57. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -1
  58. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +27 -13
  59. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -20
  60. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +6 -2
  61. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +118 -53
  62. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +106 -29
  63. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +8 -1
  64. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +4 -2
  65. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +3 -1
  66. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +4 -1
  67. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +33 -2
  68. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +151 -64
  69. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +37 -13
  70. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +84 -49
  71. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +172 -23
  72. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +3 -0
  73. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +68 -5
  74. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +17 -3
  75. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +24 -10
  76. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +6 -3
  77. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -7
  78. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +1 -0
  79. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
  80. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -4
  81. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +4 -3
  82. package/src/runtime/memory/lib/memory-cycle3.mjs +12 -2
  83. package/src/runtime/shared/buffered-appender.mjs +13 -2
  84. package/src/runtime/shared/tool-primitives.mjs +4 -1
  85. package/src/runtime/shared/tool-status.mjs +27 -0
  86. package/src/runtime/shared/tool-surface.mjs +6 -3
  87. package/src/session-runtime/config-helpers.mjs +14 -0
  88. package/src/session-runtime/context-status.mjs +1 -0
  89. package/src/session-runtime/effort.mjs +6 -2
  90. package/src/session-runtime/lifecycle-api.mjs +4 -0
  91. package/src/session-runtime/model-recency.mjs +5 -2
  92. package/src/session-runtime/runtime-core.mjs +36 -2
  93. package/src/session-runtime/session-turn-api.mjs +12 -0
  94. package/src/session-runtime/tool-catalog.mjs +34 -0
  95. package/src/standalone/agent-tool/notify.mjs +13 -0
  96. package/src/standalone/agent-tool.mjs +53 -70
  97. package/src/standalone/explore-tool.mjs +6 -7
  98. package/src/tui/App.jsx +33 -1
  99. package/src/tui/app/model-options.mjs +5 -3
  100. package/src/tui/app/model-picker.mjs +12 -24
  101. package/src/tui/app/transcript-window.mjs +10 -10
  102. package/src/tui/app/use-transcript-window.mjs +38 -56
  103. package/src/tui/components/ToolExecution.jsx +11 -6
  104. package/src/tui/components/TranscriptItem.jsx +1 -1
  105. package/src/tui/components/tool-execution/surface-detail.mjs +24 -10
  106. package/src/tui/components/tool-execution/text-format.mjs +10 -19
  107. package/src/tui/dist/index.mjs +866 -300
  108. package/src/tui/engine/agent-job-feed.mjs +216 -19
  109. package/src/tui/engine/agent-response-tail.mjs +68 -0
  110. package/src/tui/engine/notification-plan.mjs +16 -0
  111. package/src/tui/engine/session-api.mjs +14 -2
  112. package/src/tui/engine/session-flow.mjs +22 -2
  113. package/src/tui/engine/tool-card-results.mjs +64 -37
  114. package/src/tui/engine/tool-result-status.mjs +75 -21
  115. package/src/tui/engine/turn.mjs +172 -77
  116. package/src/tui/engine.mjs +199 -39
  117. package/src/tui/index.jsx +2 -2
  118. package/src/workflows/bench/WORKFLOW.md +25 -35
  119. package/src/workflows/default/WORKFLOW.md +38 -32
  120. package/src/workflows/solo/WORKFLOW.md +19 -22
  121. package/scripts/_jitter-fuzz.mjs +0 -44410
  122. package/scripts/_jitter-fuzz2.mjs +0 -44400
  123. package/scripts/_jitter-probe.mjs +0 -44397
  124. package/scripts/_jp2.mjs +0 -45614
@@ -81,6 +81,7 @@ import {
81
81
  import { createToolApproval } from './engine/tool-approval.mjs';
82
82
  import { createToolCardResults } from './engine/tool-card-results.mjs';
83
83
  import { createAgentJobFeed } from './engine/agent-job-feed.mjs';
84
+ import { appendAgentResponseTail } from './engine/agent-response-tail.mjs';
84
85
  import {
85
86
  appendTuiSteeringPersist,
86
87
  dropTuiSteeringPersist,
@@ -141,6 +142,87 @@ export { parseBackgroundTaskEnvelope };
141
142
  // so importers/tests keep resolving resolveTuiRuntimeNotificationDelivery from engine.mjs.
142
143
  export { resolveTuiRuntimeNotificationDelivery };
143
144
 
145
+ export function replaceEngineItemsState({
146
+ state,
147
+ items,
148
+ itemIndexById,
149
+ preserveStreamingTail = false,
150
+ extra = {},
151
+ }) {
152
+ const nextItems = Array.isArray(items) ? items : [];
153
+ itemIndexById.clear();
154
+ for (let i = 0; i < nextItems.length; i++) {
155
+ const id = nextItems[i]?.id;
156
+ if (id != null) itemIndexById.set(id, i);
157
+ }
158
+ return {
159
+ ...state,
160
+ ...extra,
161
+ items: nextItems,
162
+ structureRevision: (Number(state.structureRevision) || 0) + 1,
163
+ streamingTail: preserveStreamingTail ? state.streamingTail : null,
164
+ };
165
+ }
166
+
167
+ // Shared by the live engine and focused transcript tests so revision/tail
168
+ // regressions exercise the exact mutation implementation used in production.
169
+ export function createEngineItemMutators({ getState, set, itemIndexById }) {
170
+ const patchItem = (id, patch) => {
171
+ const state = getState();
172
+ let index = itemIndexById.get(id);
173
+ if (!Number.isInteger(index) || state.items[index]?.id !== id) {
174
+ index = state.items.findIndex((it) => it.id === id);
175
+ if (index >= 0) itemIndexById.set(id, index);
176
+ }
177
+ if (index < 0) return false;
178
+ const current = state.items[index];
179
+ let changed = false;
180
+ for (const [key, value] of Object.entries(patch || {})) {
181
+ if (!Object.is(current[key], value)) {
182
+ changed = true;
183
+ break;
184
+ }
185
+ }
186
+ if (!changed) return false;
187
+ const items = state.items.slice();
188
+ items[index] = { ...current, ...patch };
189
+ set({ items, structureRevision: (Number(state.structureRevision) || 0) + 1 });
190
+ return true;
191
+ };
192
+
193
+ const settleStreamingTail = (id, patch = {}, extra = {}) => {
194
+ const state = getState();
195
+ const tail = state.streamingTail?.id === id ? state.streamingTail : null;
196
+ // Bulk transcript replacement/reset owns the new transcript. A stale turn
197
+ // must never append into it after replaceItems deliberately cleared its tail.
198
+ if (!tail) return false;
199
+ let existingIndex = itemIndexById.get(id);
200
+ if (!Number.isInteger(existingIndex) || state.items[existingIndex]?.id !== id) {
201
+ existingIndex = state.items.findIndex((item) => item?.id === id);
202
+ }
203
+ if (existingIndex >= 0) return false;
204
+ const item = {
205
+ ...(tail || {}),
206
+ ...patch,
207
+ kind: 'assistant',
208
+ id,
209
+ streaming: false,
210
+ };
211
+ const index = state.items.length;
212
+ const items = [...state.items, item];
213
+ itemIndexById.set(id, index);
214
+ set({
215
+ items,
216
+ structureRevision: (Number(state.structureRevision) || 0) + 1,
217
+ streamingTail: null,
218
+ ...extra,
219
+ });
220
+ return true;
221
+ };
222
+
223
+ return { patchItem, settleStreamingTail };
224
+ }
225
+
144
226
  export async function createEngineSession({
145
227
  provider: providerName,
146
228
  model,
@@ -204,6 +286,8 @@ export async function createEngineSession({
204
286
  };
205
287
  let state = {
206
288
  items: [],
289
+ structureRevision: 0,
290
+ streamingTail: null,
207
291
  toasts: [],
208
292
  progressHint: null,
209
293
  busy: false,
@@ -282,13 +366,8 @@ export async function createEngineSession({
282
366
  };
283
367
 
284
368
  const itemIndexById = new Map();
285
- const replaceItems = (items) => {
369
+ const replaceItems = (items, { preserveStreamingTail = false } = {}) => {
286
370
  const nextItems = Array.isArray(items) ? items : [];
287
- itemIndexById.clear();
288
- for (let i = 0; i < nextItems.length; i++) {
289
- const id = nextItems[i]?.id;
290
- if (id != null) itemIndexById.set(id, i);
291
- }
292
371
  // Bulk item swap (session load / clear / compact). Derive the prompt-history
293
372
  // list from the NEW items and stage it onto state here so App never rescans;
294
373
  // the callers that invoke replaceItems always follow with a set({items:...,
@@ -296,12 +375,21 @@ export async function createEngineSession({
296
375
  // emit (the accompanying set() diffs the full patch). A bulk swap also
297
376
  // discards the old transcript, so drop any tracked active tool calls.
298
377
  activeToolCalls.clear();
299
- state = {
300
- ...state,
378
+ state = replaceEngineItemsState({
379
+ state,
301
380
  items: nextItems,
302
- promptHistoryList: buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
303
- activeToolSummary: null,
304
- };
381
+ itemIndexById,
382
+ preserveStreamingTail,
383
+ extra: {
384
+ promptHistoryList: buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
385
+ activeToolSummary: null,
386
+ },
387
+ });
388
+ // replaceItems stages the bulk state before its callers compose their
389
+ // accompanying patch. Emit here as well so an items-only replacement
390
+ // (for example removeNotice) cannot be hidden by the outer set seeing the
391
+ // already-installed array identity.
392
+ emit();
305
393
  return nextItems;
306
394
  };
307
395
  // --- Prompt-history list (newest-first, deduped) maintained incrementally ---
@@ -370,11 +458,39 @@ export async function createEngineSession({
370
458
  // first — set() diffs against the current state, so a pre-assign would make
371
459
  // the references identical and skip emit().
372
460
  const promptHistoryList = buildMergedPromptHistory(recomputePromptHistory(items), loadPromptHistory(state.cwd));
373
- set({ items, promptHistoryList });
461
+ set({ items, structureRevision: state.structureRevision + 1, promptHistoryList });
374
462
  } else {
375
- set({ items });
463
+ set({ items, structureRevision: state.structureRevision + 1 });
376
464
  }
377
465
  };
466
+ const updateStreamingTail = (id, patch = {}, extra = {}) => {
467
+ if (id == null) return false;
468
+ const current = state.streamingTail?.id === id
469
+ ? state.streamingTail
470
+ : { kind: 'assistant', id, text: '', streaming: true };
471
+ const next = { ...current, ...patch, kind: 'assistant', id, streaming: true };
472
+ let changed = state.streamingTail !== current;
473
+ if (!changed) {
474
+ for (const [key, value] of Object.entries(next)) {
475
+ if (!Object.is(current[key], value)) {
476
+ changed = true;
477
+ break;
478
+ }
479
+ }
480
+ }
481
+ return set(changed ? { streamingTail: next, ...extra } : extra);
482
+ };
483
+ const clearStreamingTail = (id = null, extra = {}) => {
484
+ if (!state.streamingTail || (id != null && state.streamingTail.id !== id)) {
485
+ return set(extra);
486
+ }
487
+ return set({ streamingTail: null, ...extra });
488
+ };
489
+ const { patchItem, settleStreamingTail } = createEngineItemMutators({
490
+ getState: () => state,
491
+ set,
492
+ itemIndexById,
493
+ });
378
494
  const upsertSyntheticToolItem = (text, id = nextId(), parsed = null) => {
379
495
  const synthetic = parseSyntheticAgentMessage(text);
380
496
  if (!synthetic) return false;
@@ -424,6 +540,64 @@ export async function createEngineSession({
424
540
  if (origin === 'user') appendPromptHistory(state.cwd, text);
425
541
  pushItem({ kind: 'user', id, text });
426
542
  };
543
+ const pushAsyncAgentResponse = (text, id = nextId(), origin = 'injected', metadata = {}) => {
544
+ const synthetic = parseSyntheticAgentMessage(text);
545
+ const isAgent = synthetic?.name === 'agent';
546
+ if (!isAgent) return pushUserOrSyntheticItem(text, id, origin);
547
+ const responseHasBody = /\n\s*\n[\s\S]*\S/.test(String(text || ''));
548
+ const rawResult = synthetic.rawResult ?? text;
549
+ const args = {
550
+ ...(synthetic.args && typeof synthetic.args === 'object' ? synthetic.args : {}),
551
+ type: 'result',
552
+ };
553
+ const responseKey = String(metadata.responseKey || metadata.executionId || args.task_id || '').trim();
554
+ const previous = state.items.at(-1);
555
+ // Tail-only aggregation prevents a later completion from mutating a card
556
+ // above any outbound tool, assistant, user, or preview/body boundary.
557
+ if (
558
+ previous?.kind === 'tool'
559
+ && previous.agentDirection === 'inbound'
560
+ ) {
561
+ const patch = appendAgentResponseTail(previous, {
562
+ key: responseKey,
563
+ args,
564
+ result: synthetic.result,
565
+ rawResult,
566
+ hasBody: responseHasBody,
567
+ isError: synthetic.isError === true,
568
+ });
569
+ if (patch) {
570
+ patchItem(previous.id, patch);
571
+ return true;
572
+ }
573
+ }
574
+ pushItem({
575
+ kind: 'tool',
576
+ id,
577
+ name: 'agent',
578
+ args,
579
+ result: synthetic.result,
580
+ rawResult,
581
+ isError: synthetic.isError === true,
582
+ expanded: false,
583
+ count: 1,
584
+ completedCount: 1,
585
+ startedAt: Date.now(),
586
+ completedAt: Date.now(),
587
+ agentDirection: 'inbound',
588
+ agentResponseKey: responseKey,
589
+ agentResponseHasBody: responseHasBody,
590
+ agentResponseAggregate: false,
591
+ agentResponseEntries: [{
592
+ key: responseKey,
593
+ raw: String(rawResult ?? '').trim(),
594
+ result: synthetic.result,
595
+ hasBody: responseHasBody,
596
+ isError: synthetic.isError === true,
597
+ }],
598
+ });
599
+ return true;
600
+ };
427
601
  const pushToast = (text, tone = 'info', ttlMs = 3000) => {
428
602
  const id = nextId();
429
603
  const value = String(text ?? '').trim();
@@ -454,7 +628,7 @@ export async function createEngineSession({
454
628
  if (id == null) return false;
455
629
  const items = state.items.filter((it) => !(it?.kind === 'notice' && it?.id === id));
456
630
  if (items.length === state.items.length) return false;
457
- set({ items: replaceItems(items) });
631
+ set({ items: replaceItems(items, { preserveStreamingTail: true }) });
458
632
  return true;
459
633
  };
460
634
  // Sticky (non-TTL) input-hint-line progress state, for long-running
@@ -479,27 +653,6 @@ export async function createEngineSession({
479
653
  getDisposed: () => flags.disposed,
480
654
  timeoutMs: TOOL_APPROVAL_TIMEOUT_MS,
481
655
  });
482
- const patchItem = (id, patch) => {
483
- let index = itemIndexById.get(id);
484
- if (!Number.isInteger(index) || state.items[index]?.id !== id) {
485
- index = state.items.findIndex((it) => it.id === id);
486
- if (index >= 0) itemIndexById.set(id, index);
487
- }
488
- if (index < 0) return false;
489
- const current = state.items[index];
490
- let changed = false;
491
- for (const [key, value] of Object.entries(patch || {})) {
492
- if (!Object.is(current[key], value)) {
493
- changed = true;
494
- break;
495
- }
496
- }
497
- if (!changed) return false;
498
- const items = state.items.slice();
499
- items[index] = { ...current, ...patch };
500
- set({ items });
501
- return true;
502
- };
503
656
  const toastTimers = new Set();
504
657
  lifecycle.runtimePulseTimer = setInterval(() => {
505
658
  if (flags.disposed) return;
@@ -524,9 +677,11 @@ export async function createEngineSession({
524
677
  kickExecutionPendingResume,
525
678
  flushDeferredExecutionPendingResumeKick,
526
679
  scheduleExecutionPendingResumeKick,
680
+ discardExecutionPendingResume,
527
681
  updateAgentJobCard,
528
682
  buildAgentJobCardPatch,
529
683
  subscribeRuntimeNotifications,
684
+ clearExecutionDedupState,
530
685
  } = createAgentJobFeed({
531
686
  runtime,
532
687
  getState: () => state,
@@ -537,11 +692,13 @@ export async function createEngineSession({
537
692
  enqueue: (...args) => bag.enqueue(...args),
538
693
  drain: (...args) => bag.drain(...args),
539
694
  pushUserOrSyntheticItem,
695
+ pushAsyncAgentResponse,
540
696
  makeQueueEntry: (...args) => bag.makeQueueEntry(...args),
541
697
  getPending: () => pending,
542
698
  agentStatusState,
543
699
  displayedExecutionNotificationKeys,
544
700
  pushNotice,
701
+ itemIndexById,
545
702
  });
546
703
  lifecycle.unsubscribeRuntimeNotifications = subscribeRuntimeNotifications();
547
704
 
@@ -566,20 +723,23 @@ export async function createEngineSession({
566
723
  updateAgentJobCard,
567
724
  buildAgentJobCardPatch,
568
725
  agentStatusState,
726
+ itemIndexById,
569
727
  });
570
728
 
571
729
 
572
730
  Object.assign(bag, {
573
731
  runtime, nextId, tuiDebug, LEAD_TURN_TIMEOUT_MS,
574
- flags, lifecycle, pending, pendingNotificationKeys, displayedExecutionNotificationKeys, listeners, itemIndexById,
732
+ flags, lifecycle, pending, pendingNotificationKeys, displayedExecutionNotificationKeys, clearExecutionDedupState, listeners, itemIndexById,
575
733
  getState: () => state, set,
576
- pushItem, patchItem, replaceItems, pushToast, pushNotice, removeNotice, setProgressHint,
577
- pushUserOrSyntheticItem, upsertSyntheticToolItem,
734
+ pushItem, patchItem, replaceItems, updateStreamingTail, settleStreamingTail, clearStreamingTail,
735
+ pushToast, pushNotice, removeNotice, setProgressHint,
736
+ pushUserOrSyntheticItem, pushAsyncAgentResponse, upsertSyntheticToolItem,
578
737
  markToolCallActive, markToolCallDone, clearActiveToolSummary, clearToastTimers,
579
738
  autoClearState, agentStatusState, baseRouteState, routeState, syncContextStats,
580
739
  presentNextToolApproval, finishToolApproval, denyAllToolApprovals, requestToolApproval,
581
740
  patchToolCardResult, flushToolResults,
582
- kickExecutionPendingResume, flushDeferredExecutionPendingResumeKick, scheduleExecutionPendingResumeKick, updateAgentJobCard, subscribeRuntimeNotifications,
741
+ clearExecutionDedupState,
742
+ kickExecutionPendingResume, flushDeferredExecutionPendingResumeKick, scheduleExecutionPendingResumeKick, discardExecutionPendingResume, updateAgentJobCard, subscribeRuntimeNotifications,
583
743
  });
584
744
  Object.assign(bag, createSessionFlow(bag));
585
745
  bag.runTurn = createRunTurn(bag);
package/src/tui/index.jsx CHANGED
@@ -281,7 +281,7 @@ function paintBootSplash() {
281
281
  const rows = Math.max(1, Number(process.stdout.rows) || 24);
282
282
  const windowsLikeTerminal = process.platform === 'win32' || Boolean(process.env.WT_SESSION);
283
283
  const frameCols = Math.max(1, cols - (windowsLikeTerminal ? 1 : 0));
284
- const center = (s) => `${' '.repeat(Math.max(0, Math.floor((frameCols - displayWidth(s)) / 2)))}${s}`;
284
+ const center = (s, reserve = 0) => `${' '.repeat(Math.max(0, Math.floor((frameCols - reserve - displayWidth(s)) / 2)))}${s}`;
285
285
  const logo = [
286
286
  '███╗ ███╗██╗██╗ ██╗██████╗ ██████╗ ██████╗ ',
287
287
  '████╗ ████║██║╚██╗██╔╝██╔══██╗██╔═══██╗██╔════╝ ',
@@ -300,7 +300,7 @@ function paintBootSplash() {
300
300
  out += `${bold}${fg}${center(logo[i])}${reset}\r\n`;
301
301
  }
302
302
  out += '\r\n';
303
- out += `${subtleFg}${center(`mixdog coding agent · v${localPackageVersion()} · ${process.cwd()}`)}${reset}`;
303
+ out += `${subtleFg}${center(`mixdog coding agent · v${localPackageVersion()} · ${process.cwd()}`, 4)}${reset}`;
304
304
 
305
305
  // Park the cursor at home so ink's first frame paints top-down over the
306
306
  // splash instead of starting at the bottom row and scrolling the screen.
@@ -6,41 +6,31 @@ hidden: true
6
6
  agents: worker, heavy-worker, reviewer, debugger, maintainer
7
7
  ---
8
8
 
9
- # Bench Workflow
9
+ # Bench
10
10
 
11
- Autonomous run: no user is present. Never wait for approval or ask
12
- questions — decide and proceed immediately. Loop until the task is verified
13
- complete or provably blocked.
11
+ Autonomous: no user. Never wait for approval or ask questions; decide/proceed.
12
+ Loop until verified complete or provably blocked.
14
13
 
15
- Lead supervises: delegates, coordinates, judges, decides. Route by complexity:
16
- - Lead directly: simple 1–2 step work, plus coordination, pre-planning, config
17
- changes, and final git deployment.
18
- - Worker: implementation that takes several steps. Heavy Worker:
19
- high-complexity, multi-step implementation.
20
- - Reviewer: verify implementation scopes (diff, regressions, missing checks).
21
- - Debugger: very high complexity, or when root-causing has already failed at
22
- least once.
14
+ Lead supervises, delegates, coordinates, judges, decides. Lead directly handles
15
+ simple 1–2-step work, coordination, pre-planning, config, final git deployment.
16
+ Worker: several-step implementation; Heavy Worker: high-complexity multi-step
17
+ implementation; Reviewer: implementation diff/regression/missing-check review;
18
+ Debugger: very high complexity or root cause already failed once.
23
19
 
24
- 1. Plan form the plan yourself and start executing immediately; no approval
25
- step exists.
26
- 2. Delegate split into the maximum number of independent scopes.
27
- - PARALLEL across independent scopes by default (implementation, analysis,
28
- review, debugging alike); spawn every scope in the SAME turn. Shared or
29
- cross-cutting code does NOT justify merging split per path and verify
30
- the shared parts yourself. The only single scope is a genuinely
31
- inseparable dependency; then state it.
32
- - SEQUENTIAL within a single complex scope: ordered steps with a
33
- build/test-green gate between them, not one shot.
34
- - Write briefs per the Lead brief contract.
35
- - After spawning async agents, END THE TURN — no polling, guessing, or
36
- dependent work until the completion notification resumes you.
37
- 3. Review pair one reviewer 1:1 with each implementation scope, spawned in the
38
- same turn, never deferred or batched; wait for its result. Fact-check agent
39
- responses and cross-check implementation and review results yourself before
40
- acting. Send fixes back to the original scope and loop verify -> fix ->
41
- re-verify until clean. Skip review only for simple, low-risk tasks. If a bug
42
- survives 2+ fix cycles, have the debugger investigate first instead of
43
- another fix round.
44
- 4. Finish — verify the final state yourself; only stop when the task is
45
- complete and verified, or a hard blocker makes progress impossible. State
46
- the outcome and evidence in the final message.
20
+ 1. Plan: form it and execute immediately; no approval step.
21
+ 2. Delegate: split to the maximum independent scopes. Parallelize independent
22
+ implementation/analysis/review/debugging, spawning every scope in one turn.
23
+ Shared/cross-cutting code does not justify merging: split by path and verify
24
+ shared parts yourself. A single scope needs a genuinely inseparable
25
+ dependency; state it. Within one complex scope, sequence steps with a
26
+ build/test-green gate between them. Briefs follow the Lead brief contract.
27
+ After async spawn, end turn: no polling, guessing, or dependent work until
28
+ completion notification.
29
+ 3. Review: one Reviewer per implementation scope, spawned 1:1 in the same turn,
30
+ never deferred/batched; wait. Fact-check agent responses and cross-check
31
+ implementation/review yourself before acting. Return fixes to the original
32
+ scope; loop verify -> fix -> re-verify until clean. Skip only simple,
33
+ low-risk review. If a bug survives 2+ fix cycles, Debugger investigates
34
+ before another fix round.
35
+ 4. Finish: verify final state yourself; stop only verified complete or hard
36
+ blocked. Final states outcome and evidence.
@@ -5,39 +5,45 @@ description: "Default agent workflow — fan-out parallel delegation across inde
5
5
  agents: worker, heavy-worker, reviewer, debugger, maintainer
6
6
  ---
7
7
 
8
- # Default Workflow
8
+ # Default
9
9
 
10
- HARD APPROVAL GATE investigation/planning may proceed only as read-only
11
- exploration while consulting with the user. After user consultation produces a
12
- conclusion and plan, execution is still forbidden until explicit go-ahead
13
- ("do it", "proceed", "go ahead"). Diagnosis agreement, problem-pointing, or plan
14
- agreement is NOT execution approval. No changes, state mutations, or delegation
15
- before explicit go-ahead.
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.
16
15
 
17
- Lead supervises/delegates/coordinates/judges/decides. After approval route by
18
- complexity: Lead directly only one-step single-turn fixes plus coordination,
19
- config, and git deployment; everything else delegates. Worker = multi-step or
20
- multi-file implementation; Heavy Worker = high-complexity scopes; Reviewer =
21
- implementation verification; Debugger = very high complexity or root-cause
22
- after a failed fix.
16
+ Lead supervises, delegates, coordinates, judges, decides. After approval,
17
+ delegate by default; Lead only coordinates, does git, or an obvious 1-edit/
18
+ 1-check change. Route other implementation/research/debugging to its matching
19
+ agent. Worker: bounded established path, low local risk/coupling/verification,
20
+ clear local check. Heavy Worker: high risk/coupling/verification (including any
21
+ high-risk scope), or coupled staged work needing coordinated verification.
22
+ Architecture, contracts, storage, concurrency, security, lifecycle are
23
+ indicators, not automatic categories. Reviewer verifies an implementation;
24
+ Debugger handles requested debugging or root cause after a failed fix.
23
25
 
24
- 1. Plan — present a draft before ANY implementation; revise/re-present until
25
- user consultation is complete and a conclusion/plan is agreed. Then wait for
26
- explicit go-ahead before executing. If ambiguous, restate the plan and ask.
27
- 2. Delegate maximize parallel distribution: split independent implementation
28
- scopes to separate worker/heavy-worker agents, all spawned in the SAME turn.
29
- Sequential steps only inside one inseparable complex scope, gated
30
- build/test-green; say when a scope is inseparable. Briefs follow Lead brief
31
- contract. After spawning async agents, END THE TURN.
32
- 3. Review once scopes land, spawn one reviewer per implementation scope, all
33
- in the same turn. Cross-check results yourself; send fixes to the original
34
- scope and loop fix -> re-verify until clean. Skip only simple low-risk work.
35
- Use Debugger first when asked for debugging or a bug survives 2+ fix cycles.
36
- On each agent report, relay scope+verdict and next work as in-progress,
37
- never as a conclusion.
38
- 4. Report final report covers work vs approved plan and verified result,
39
- separate from interim updates. Never forward raw agent output. Ask about
40
- ship/deploy when relevant; deploy/build/commit only on explicit user request
41
- after feedback with no issues.
26
+ 1. Plan: draft before any implementation; settle scope/plan, ask if ambiguous,
27
+ then await the gate.
28
+ 2. Delegate: maximize useful fan-out—one suitable Worker/Heavy Worker per ready
29
+ independent scope whose parallel gain exceeds coordination/merge cost; spawn
30
+ all in one turn, with no count cap. Serialize only a real dependency,
31
+ overlapping write, or inseparable coupling. Briefs follow the Lead brief
32
+ contract. After async spawn, end the turn.
33
+ 3. Review: after approval, complete delegation, review, self-verification, and
34
+ in-scope fixes without reapproval. Every delegated implementation gets one
35
+ Reviewer (all ready reviewers in one turn) and Lead integration/cross-scope
36
+ verification in parallel. Reviewer independently judges risk, intent,
37
+ boundaries; Lead checks acceptance/interactions, not duplicate same-scope
38
+ work. High-risk scopes add distinct lenses. Synthesize one verdict; send
39
+ merged fixes to the original live session; loop fix -> re-verify (same
40
+ Reviewer + Lead re-check) until clean. Debugger first for requested debugging
41
+ or a bug surviving 2+ fix cycles. Agent reports relay scope, verdict, next
42
+ work as in-progress, never conclusions.
43
+ 4. Report: final (not interim) report compares work to approved plan and gives
44
+ verified result; never forward raw agent output. Ask about ship/deploy when
45
+ relevant. Build/deploy/commit/push require an explicit user request after
46
+ issue-free feedback; implementation approval alone is insufficient.
42
47
 
43
- On major direction shifts mid-work, pause and re-consult the user.
48
+ On outcome/direction change, pause and re-consult; otherwise continue approved
49
+ work without reapproval.
@@ -1,31 +1,28 @@
1
1
  ---
2
2
  id: solo
3
3
  name: Solo
4
- description: "Solo workflow — Lead handles everything directly; delegation forbidden."
4
+ description: "Solo workflow — Lead handles everything directly."
5
5
  agents:
6
6
  ---
7
7
 
8
- # Solo Workflow
8
+ # Solo
9
9
 
10
- HARD APPROVAL GATE investigation/planning may proceed only as read-only
11
- exploration while consulting with the user. After user consultation produces a
12
- conclusion and plan, execution is still forbidden until explicit go-ahead
13
- ("do it", "proceed", "go ahead"). Diagnosis agreement, problem-pointing, or plan
14
- agreement is NOT execution approval. No changes or state mutations before
15
- explicit go-ahead.
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.
16
15
 
17
- 1. Plan — present a draft before ANY implementation; revise/re-present until
18
- user consultation is complete and a conclusion/plan is agreed. Then wait for
19
- explicit go-ahead before executing. If ambiguous, restate the plan and ask.
20
- 2. Execute Lead does all work directly; delegation forbidden. Interim updates
21
- are in-progress, never conclusions.
22
- 3. Verify check and fix directly until clean or a blocker is reported.
23
- 4. Report final report covers work vs approved plan, verification result, and
24
- remaining risk/next step, distinct from interim updates. Deploy/build/commit
25
- only after user feedback with no issues.
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.
26
26
 
27
- On major direction shifts mid-work, pause and re-consult the user.
28
-
29
- Delegation rule:
30
- - Never delegate/spawn/send or ask agents to work; ignore available-agent
31
- sections while this workflow is active.
27
+ On outcome/direction change, pause and re-consult; otherwise continue approved
28
+ work without reapproval.