mixdog 0.9.41 → 0.9.44

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 (73) hide show
  1. package/package.json +3 -2
  2. package/scripts/agent-tag-reuse-smoke.mjs +124 -10
  3. package/scripts/ansi-color-capability-test.mjs +90 -0
  4. package/scripts/anthropic-oauth-refresh-race-test.mjs +59 -0
  5. package/scripts/arg-guard-test.mjs +16 -0
  6. package/scripts/compact-pressure-test.mjs +256 -1
  7. package/scripts/generate-runtime-manifest.mjs +39 -22
  8. package/scripts/grok-oauth-refresh-race-test.mjs +198 -0
  9. package/scripts/internal-comms-bench.mjs +0 -1
  10. package/scripts/internal-comms-smoke.mjs +38 -39
  11. package/scripts/internal-tools-normalization-test.mjs +52 -0
  12. package/scripts/maintenance-default-routes-test.mjs +164 -0
  13. package/scripts/max-output-recovery-test.mjs +49 -0
  14. package/scripts/mcp-client-normalization-test.mjs +45 -0
  15. package/scripts/openai-oauth-ws-1006-retry-test.mjs +123 -0
  16. package/scripts/provider-toolcall-test.mjs +23 -0
  17. package/scripts/result-classification-test.mjs +75 -0
  18. package/scripts/routing-corpus.mjs +1 -1
  19. package/scripts/shell-jobs-windows-hide-test.mjs +164 -0
  20. package/scripts/smoke.mjs +1 -1
  21. package/scripts/submit-commandbusy-race-test.mjs +99 -7
  22. package/scripts/tui-transcript-jitter-harness-entry.jsx +302 -0
  23. package/scripts/tui-transcript-jitter-harness.mjs +58 -0
  24. package/scripts/tui-transcript-perf-test.mjs +56 -1
  25. package/src/agents/reviewer/AGENT.md +5 -3
  26. package/src/rules/lead/lead-brief.md +5 -4
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +40 -3
  28. package/src/runtime/agent/orchestrator/config.mjs +29 -14
  29. package/src/runtime/agent/orchestrator/internal-tools.mjs +16 -3
  30. package/src/runtime/agent/orchestrator/mcp/client.mjs +17 -1
  31. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +49 -6
  32. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +14 -0
  33. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +46 -21
  34. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +59 -2
  35. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +5 -1
  36. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
  37. package/src/runtime/agent/orchestrator/session/context-utils.mjs +140 -81
  38. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +23 -5
  39. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +3 -0
  40. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +12 -3
  41. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +4 -2
  42. package/src/runtime/agent/orchestrator/session/result-classification.mjs +4 -1
  43. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +7 -2
  44. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +8 -6
  45. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +15 -3
  46. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +7 -5
  48. package/src/runtime/memory/data/runtime-manifest.json +11 -11
  49. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -2
  50. package/src/runtime/memory/tool-defs.mjs +4 -3
  51. package/src/runtime/shared/tool-surface.mjs +1 -2
  52. package/src/session-runtime/context-status.mjs +8 -2
  53. package/src/standalone/agent-tool/render.mjs +2 -0
  54. package/src/standalone/agent-tool.mjs +267 -72
  55. package/src/standalone/explore-tool.mjs +17 -16
  56. package/src/tui/app/provider-setup-picker.mjs +1 -0
  57. package/src/tui/app/use-transcript-window.mjs +5 -1
  58. package/src/tui/components/StatusLine.jsx +11 -32
  59. package/src/tui/dist/index.mjs +257 -106
  60. package/src/tui/engine/session-api.mjs +3 -0
  61. package/src/tui/engine/session-flow.mjs +19 -8
  62. package/src/tui/engine/turn.mjs +24 -2
  63. package/src/tui/engine.mjs +0 -1
  64. package/src/tui/index.jsx +3 -2
  65. package/src/tui/markdown/streaming-markdown.mjs +35 -9
  66. package/src/tui/statusline-ansi-bridge.mjs +17 -7
  67. package/src/ui/ansi.mjs +85 -5
  68. package/src/ui/statusline-agents.mjs +0 -8
  69. package/src/ui/statusline-format.mjs +7 -7
  70. package/src/ui/statusline.mjs +2 -4
  71. package/src/workflows/default/WORKFLOW.md +29 -20
  72. package/src/workflows/solo-review/WORKFLOW.md +47 -0
  73. package/src/workflows/bench/WORKFLOW.md +0 -60
@@ -1,7 +1,7 @@
1
1
  import { isAbsolute, resolve } from 'node:path';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
- import { makeAgentDispatch } from '../runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs';
4
+ import { makeAgentDispatch, resolveMaintenanceRoute } from '../runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs';
5
5
  import { loadConfig } from '../runtime/agent/orchestrator/config.mjs';
6
6
  import { initProviders } from '../runtime/agent/orchestrator/providers/registry.mjs';
7
7
  import { presentErrorText } from '../runtime/shared/err-text.mjs';
@@ -195,10 +195,13 @@ function exploreResultCacheEnabled() {
195
195
  && !/^(?:0|false|off|no)$/i.test(String(process.env.MIXDOG_EXPLORE_RESULT_CACHE || '1'));
196
196
  }
197
197
 
198
- function exploreResultCacheKey({ cwd, presetName, query }) {
198
+ export function exploreResultCacheKey({ cwd, route, query }) {
199
199
  return JSON.stringify({
200
200
  cwd: String(cwd || ''),
201
- presetName: String(presetName || ''),
201
+ provider: clean(route?.provider),
202
+ model: clean(route?.model),
203
+ effort: clean(route?.effort),
204
+ fast: route?.fast === true,
202
205
  query: String(query || ''),
203
206
  });
204
207
  }
@@ -211,13 +214,14 @@ function findConfigPreset(config, presetName) {
211
214
  || null;
212
215
  }
213
216
 
214
- async function ensureExploreProviderReady(config) {
215
- const route = config?.maintenance?.explore;
216
- // Route object ({provider,model}) is the current shape; a string is a
217
- // legacy preset NAME resolved against config.presets.
218
- const provider = (route && typeof route === 'object')
219
- ? clean(route.provider)
220
- : clean(findConfigPreset(config, route)?.provider);
217
+ export function resolveExploreRoute(config) {
218
+ const routeOrName = resolveMaintenanceRoute({ agent: 'explorer', config });
219
+ if (routeOrName && typeof routeOrName === 'object') return routeOrName;
220
+ return findConfigPreset(config, routeOrName);
221
+ }
222
+
223
+ async function ensureExploreProviderReady(config, route) {
224
+ const provider = clean(route?.provider);
221
225
  if (!provider) return;
222
226
  const providers = { ...(config?.providers || {}) };
223
227
  providers[provider] = { ...(providers[provider] || {}), enabled: true };
@@ -411,11 +415,8 @@ async function runExploreSync(args = {}, ctx = {}) {
411
415
  scheduleExploreCodeGraphPrewarm(resolvedCwd);
412
416
  scheduleExploreFindPrewarm(resolvedCwd);
413
417
  const config = loadConfig();
414
- await ensureExploreProviderReady(config);
415
- const route = config?.maintenance?.explore;
416
- const presetName = (route && typeof route === 'object')
417
- ? `${clean(route.provider)}/${clean(route.model)}`
418
- : (route || '');
418
+ const route = resolveExploreRoute(config);
419
+ await ensureExploreProviderReady(config, route);
419
420
  // Turn budget is enforced BOTH ways: the prompt contract (rules/agent/
420
421
  // 30-explorer.md, "hard max 3 tool turns, expected 1") steers the model,
421
422
  // and maxLoopIterations mechanically backstops it — live traces showed
@@ -439,7 +440,7 @@ async function runExploreSync(args = {}, ctx = {}) {
439
440
  // query resolves without delay regardless of index.
440
441
  const stagger = working.length > 1 ? EXPLORE_FANOUT_STAGGER_MS : 0;
441
442
  const settled = await Promise.allSettled(working.map((q, i) => {
442
- const key = exploreResultCacheKey({ cwd: resolvedCwd, presetName, query: q });
443
+ const key = exploreResultCacheKey({ cwd: resolvedCwd, route, query: q });
443
444
  return runExploreCached(key, () => {
444
445
  const delay = (stagger > 0 && i > 0) ? new Promise((r) => setTimeout(r, stagger)) : null;
445
446
  return delay
@@ -49,6 +49,7 @@ export function createProviderSetupPicker({
49
49
  let setup = options.preloadedSetup && typeof options.preloadedSetup === 'object'
50
50
  ? options.preloadedSetup
51
51
  : null;
52
+ options.preloadedSetup = null;
52
53
  if (!setup) {
53
54
  setPicker({
54
55
  title: options.title || 'Providers',
@@ -352,7 +352,11 @@ export function useTranscriptWindow({
352
352
  // measuredRowsVersion bumps, the row index absorbs the growth (idEntry.rows ==
353
353
  // the new measured height) and the live estimate matches it, so delta → 0.
354
354
  if (scrolledUp && !followingRef.current) {
355
- const growth = streamingTailMountedGrowth(transcriptItems, frameColumns, toolOutputExpanded);
355
+ const growth = streamingTailMountedGrowth(
356
+ streamingTailItem ? [streamingTailItem] : transcriptItems,
357
+ frameColumns,
358
+ toolOutputExpanded,
359
+ );
356
360
  // Only compensate when the tail is actually mounted in the rendered slice
357
361
  // (viewport + overscan). Off-slice it is represented by a row-index-sized
358
362
  // bottom spacer that does NOT physically grow this frame, so shifting the
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import React, { useEffect, useRef, useState } from 'react';
10
10
  import { Box, Text } from 'ink';
11
+ import { rgbSgr } from '../../ui/ansi.mjs';
11
12
  import { displayModelName, shortenModelName } from '../../ui/model-display.mjs';
12
13
  import { theme, surfaceBackground } from '../theme.mjs';
13
14
  import {
@@ -116,7 +117,7 @@ function scheduleBootFullRetry(backoffMsRef, nextAttemptAtRef) {
116
117
  function ansiRgb(value, fallback) {
117
118
  const match = /^rgb\((\d+),(\d+),(\d+)\)$/.exec(String(value || '').replace(/\s+/g, ''));
118
119
  if (!match) return fallback;
119
- return `\x1b[38;2;${match[1]};${match[2]};${match[3]}m`;
120
+ return rgbSgr(match[1], match[2], match[3]);
120
121
  }
121
122
 
122
123
  // SGR escapes derived from the active theme. Resolved per call (not captured at
@@ -124,11 +125,11 @@ function ansiRgb(value, fallback) {
124
125
  // render. `theme` is mutated in-place on switch.
125
126
  function statusColors() {
126
127
  return {
127
- STATUS: ansiRgb(theme.statusText, '\x1b[38;2;198;198;198m'),
128
- SUBTLE: ansiRgb(theme.statusSubtle, '\x1b[38;2;136;136;136m'),
129
- SUCCESS: ansiRgb(theme.success, '\x1b[38;2;0;170;75m'),
130
- WARNING: ansiRgb(theme.warning, '\x1b[38;2;255;193;7m'),
131
- ERROR: ansiRgb(theme.error, '\x1b[38;2;220;70;88m'),
128
+ STATUS: ansiRgb(theme.statusText, rgbSgr(198, 198, 198)),
129
+ SUBTLE: ansiRgb(theme.statusSubtle, rgbSgr(136, 136, 136)),
130
+ SUCCESS: ansiRgb(theme.success, rgbSgr(0, 170, 75)),
131
+ WARNING: ansiRgb(theme.warning, rgbSgr(255, 193, 7)),
132
+ ERROR: ansiRgb(theme.error, rgbSgr(220, 70, 88)),
132
133
  };
133
134
  }
134
135
 
@@ -250,26 +251,6 @@ function localRunningWorkerCount(agentWorkers = [], agentJobs = []) {
250
251
  return seen.size;
251
252
  }
252
253
 
253
- function localRunningWorkerTags(agentWorkers = [], agentJobs = [], limit = 3) {
254
- const tags = [];
255
- const seen = new Set();
256
- for (const worker of Array.isArray(agentWorkers) ? agentWorkers : []) {
257
- const tag = String(worker?.tag || worker?.agent || worker?.name || '').trim();
258
- if (!tag || isTerminalStatus(worker?.stage || worker?.status) || seen.has(tag)) continue;
259
- seen.add(tag);
260
- tags.push(tag);
261
- }
262
- for (const job of Array.isArray(agentJobs) ? agentJobs : []) {
263
- if (!/running/i.test(String(job?.status || job?.stage || ''))) continue;
264
- const tag = String(job?.tag || job?.agent || job?.type || job?.task_id || job?.taskId || '').trim();
265
- if (!tag || seen.has(tag)) continue;
266
- seen.add(tag);
267
- tags.push(tag);
268
- }
269
- if (tags.length <= limit) return tags.join(', ');
270
- return `${tags.slice(0, limit).join(', ')}, +${tags.length - limit}`;
271
- }
272
-
273
254
  function localTimeMs(value) {
274
255
  if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value;
275
256
  const n = Date.parse(String(value || ''));
@@ -311,11 +292,9 @@ function localStatusLineL2({
311
292
  const runningCount = localRunningWorkerCount(agentWorkers, agentJobs);
312
293
  if (runningCount > 0) {
313
294
  const label = `Running ${runningCount} Agent${runningCount === 1 ? '' : 's'}`;
314
- const tagSummary = localRunningWorkerTags(agentWorkers, agentJobs);
315
- const tags = tagSummary ? ` ${SUBTLE}(${RESET}${STATUS}${tagSummary}${RESET}${SUBTLE})${RESET}` : '';
316
295
  const oldestStart = localOldestWorkerStartMs(agentWorkers, agentJobs);
317
296
  const elapsed = oldestStart > 0 ? localFormatElapsed(now - oldestStart) : '';
318
- l2Parts.push(`${spin} ${STATUS}${label}${RESET}${tags}${elapsedSuffix(elapsed)}`);
297
+ l2Parts.push(`${spin} ${STATUS}${label}${RESET}${elapsedSuffix(elapsed)}`);
319
298
  }
320
299
  const tools = activeTools && typeof activeTools === 'object' ? activeTools : {};
321
300
  const exploreInfo = tools.explore || null;
@@ -369,9 +348,9 @@ function extractCachedShellSegments(cachedL2 = '', now = Date.now(), capturedAt
369
348
  // Start-anchored so only a REAL standalone shell segment matches. A visible
370
349
  // segment is `<spinner-glyph> Running N … [· elapsed]`, so allow the single
371
350
  // leading spinner token via `^(?:\S+\s+)?` then pin `Running N Shell(s)` to
372
- // that position. An Agents segment is `<glyph> Running N Agents (tags) …` →
373
- // `Shells?` cannot match `Agents`; a tag-injected `Running 3 Shells · 9s`
374
- // sits AFTER `Running N Agents (` (not at the pinned start) no false match.
351
+ // that position. An Agents segment is `<glyph> Running N Agents · …` →
352
+ // `Shells?` cannot match `Agents`; any shell-like text after the Agents label
353
+ // is not at the pinned start, so it cannot produce a false match.
375
354
  const m = /^(?:\S+\s+)?Running (\d+) Shells?\b(?:\s·\s(.+?))?\s*$/.exec(seg.trim());
376
355
  if (!m) continue;
377
356
  const n = Number(m[1]) || 0;