mixdog 0.9.50 → 0.9.52

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 (134) hide show
  1. package/package.json +5 -3
  2. package/scripts/abort-recovery-test.mjs +17 -1
  3. package/scripts/agent-model-liveness-test.mjs +79 -2
  4. package/scripts/anthropic-admission-retry-integration-test.mjs +119 -0
  5. package/scripts/anthropic-transport-policy-test.mjs +466 -0
  6. package/scripts/atomic-lock-tryonce-test.mjs +60 -1
  7. package/scripts/build-tui.mjs +13 -1
  8. package/scripts/channel-daemon-smoke.mjs +630 -10
  9. package/scripts/code-graph-aggregate-cwd-test.mjs +41 -37
  10. package/scripts/code-graph-disk-hit-test.mjs +11 -0
  11. package/scripts/code-graph-root-federation-test.mjs +273 -0
  12. package/scripts/compact-pressure-test.mjs +55 -1
  13. package/scripts/compact-smoke.mjs +80 -0
  14. package/scripts/context-mcp-metering-test.mjs +1350 -19
  15. package/scripts/deferred-tool-loading-test.mjs +17 -0
  16. package/scripts/gemini-provider-test.mjs +1053 -0
  17. package/scripts/hook-bus-test.mjs +23 -0
  18. package/scripts/internal-tools-normalization-test.mjs +10 -0
  19. package/scripts/interrupted-turn-history-test.mjs +371 -0
  20. package/scripts/lifecycle-api-test.mjs +76 -0
  21. package/scripts/max-output-recovery-test.mjs +55 -0
  22. package/scripts/mcp-grace-deferred-test.mjs +89 -13
  23. package/scripts/memory-pg-recovery-test.mjs +59 -0
  24. package/scripts/openai-oauth-ws-1006-retry-test.mjs +391 -4
  25. package/scripts/process-lifecycle-test.mjs +389 -0
  26. package/scripts/provider-admission-scheduler-test.mjs +582 -0
  27. package/scripts/provider-contract-test.mjs +268 -0
  28. package/scripts/provider-toolcall-test.mjs +520 -3
  29. package/scripts/reactive-compact-persist-smoke.mjs +59 -0
  30. package/scripts/resource-admission-test.mjs +789 -0
  31. package/scripts/session-bench-cache-break-test.mjs +102 -0
  32. package/scripts/session-bench.mjs +101 -42
  33. package/scripts/shell-failure-diagnostics-test.mjs +73 -4
  34. package/scripts/shell-jobs-windows-hide-test.mjs +1 -1
  35. package/scripts/smoke-loop-failure-summary-test.mjs +38 -0
  36. package/scripts/smoke-loop-failure-summary.mjs +16 -0
  37. package/scripts/smoke-loop.mjs +2 -7
  38. package/scripts/steering-drain-buckets-test.mjs +18 -0
  39. package/scripts/tool-failures.mjs +15 -1
  40. package/scripts/toolcall-args-test.mjs +14 -6
  41. package/scripts/tui-transcript-perf-test.mjs +43 -7
  42. package/scripts/web-fetch-routing-test.mjs +158 -0
  43. package/src/cli.mjs +15 -2
  44. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +26 -0
  45. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +4 -1
  46. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +12 -0
  47. package/src/runtime/agent/orchestrator/internal-tools.mjs +2 -0
  48. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +331 -0
  49. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +118 -26
  50. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +29 -17
  51. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +136 -38
  52. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +24 -4
  53. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +554 -42
  54. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +67 -18
  55. package/src/runtime/agent/orchestrator/providers/gemini.mjs +95 -46
  56. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +12 -4
  57. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +54 -14
  58. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +1 -1
  59. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +14 -3
  60. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +10 -80
  61. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +89 -17
  62. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +93 -26
  63. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +144 -51
  64. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +22 -8
  65. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +74 -1
  66. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +111 -6
  67. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +13 -17
  68. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +78 -12
  69. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +44 -5
  70. package/src/runtime/agent/orchestrator/providers/registry.mjs +49 -8
  71. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +224 -104
  72. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +127 -55
  73. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +99 -32
  74. package/src/runtime/agent/orchestrator/session/context-utils.mjs +17 -1
  75. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +38 -0
  76. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -0
  77. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +69 -37
  78. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +10 -1
  79. package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +8 -28
  80. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +6 -7
  81. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +10 -1
  83. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +42 -1
  84. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +5 -1
  85. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +220 -0
  86. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -4
  87. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -0
  88. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +17 -0
  89. package/src/runtime/agent/orchestrator/session/store.mjs +89 -22
  90. package/src/runtime/agent/orchestrator/stall-policy.mjs +2 -12
  91. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +42 -4
  92. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +74 -37
  93. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +223 -2
  94. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +380 -37
  95. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +176 -21
  96. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +14 -0
  97. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +108 -2
  98. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +93 -0
  99. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +12 -3
  100. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +124 -14
  101. package/src/runtime/memory/index.mjs +22 -4
  102. package/src/runtime/memory/lib/pg/adapter.mjs +84 -10
  103. package/src/runtime/memory/lib/pg/process.mjs +91 -47
  104. package/src/runtime/memory/lib/pg/supervisor.mjs +50 -13
  105. package/src/runtime/search/index.mjs +41 -0
  106. package/src/runtime/search/lib/http-fetch.mjs +154 -0
  107. package/src/runtime/search/tool-defs.mjs +23 -0
  108. package/src/runtime/shared/atomic-file.mjs +28 -150
  109. package/src/runtime/shared/process-lifecycle.mjs +363 -0
  110. package/src/runtime/shared/process-shutdown.mjs +27 -4
  111. package/src/runtime/shared/resource-admission.mjs +359 -0
  112. package/src/runtime/shared/staged-child-result.mjs +19 -0
  113. package/src/session-runtime/context-status.mjs +38 -27
  114. package/src/session-runtime/lifecycle-api.mjs +26 -6
  115. package/src/session-runtime/provider-request-tools.mjs +72 -0
  116. package/src/session-runtime/runtime-core.mjs +43 -18
  117. package/src/session-runtime/session-turn-api.mjs +5 -4
  118. package/src/session-runtime/tool-catalog.mjs +375 -15
  119. package/src/standalone/agent-tool.mjs +17 -38
  120. package/src/standalone/channel-daemon-client.mjs +200 -38
  121. package/src/standalone/channel-daemon-transport.mjs +136 -9
  122. package/src/standalone/channel-worker.mjs +79 -12
  123. package/src/standalone/hook-bus/handlers.mjs +4 -0
  124. package/src/standalone/hook-bus/rules.mjs +7 -1
  125. package/src/standalone/hook-bus.mjs +10 -2
  126. package/src/tui/App.jsx +17 -11
  127. package/src/tui/app/live-spinner-visibility.mjs +20 -0
  128. package/src/tui/dist/index.mjs +101 -281
  129. package/src/tui/engine/session-api-ext.mjs +13 -2
  130. package/src/tui/engine/session-api.mjs +1 -1
  131. package/src/tui/engine/turn.mjs +10 -6
  132. package/src/tui/engine.mjs +13 -4
  133. package/src/tui/index.jsx +4 -1
  134. package/src/tui/lib/voice-setup.mjs +16 -0
@@ -148,7 +148,7 @@ export function createRunTurn(bag) {
148
148
  const elapsed = Date.now() - startedAt;
149
149
  tuiDebug(`runTurn WATCHDOG TRIP turn=${turnIndex} elapsedMs=${elapsed} idleMs=${idleMs} lastProgress=${lastProgressLabel} — aborting stuck turn`);
150
150
  pushNotice(`Turn timed out after ${Math.round(idleMs / 1000)}s idle (last progress: ${lastProgressLabel}) — aborting stuck request. Input will be released shortly if abort does not unwind.`, 'warn', { transcript: true });
151
- try { runtime.abort('cli-react-abort-watchdog'); } catch {}
151
+ try { runtime.abort('watchdog'); } catch {}
152
152
  // Belt-and-suspenders: if runtime.abort() did not reject runtime.ask()
153
153
  // (unwind starved), hard-release the turn after a short grace so the
154
154
  // React store is never left with busy=true and no drain in flight.
@@ -207,6 +207,7 @@ export function createRunTurn(bag) {
207
207
  let thinkingSegmentStartedAt = 0;
208
208
  let accumulatedThinkingMs = 0;
209
209
  let cancelled = false;
210
+ let failed = false;
210
211
  let askResult = null;
211
212
  let turnFinishedNormally = false;
212
213
  let transcriptCompactedThisTurn = false;
@@ -1187,8 +1188,9 @@ export function createRunTurn(bag) {
1187
1188
  flushStreamBatch(); // ensure any batched text lands before the error notice
1188
1189
  if (error?.name === 'SessionClosedError') {
1189
1190
  cancelled = true;
1190
- if (assistantText.trim() && currentAssistantId) {
1191
- settleStreamingTail(currentAssistantId, { text: currentAssistantText || assistantText });
1191
+ if (assistantText.trim()) {
1192
+ const id = currentAssistantId || ensureAssistant(assistantText);
1193
+ settleStreamingTail(id, { text: currentAssistantText || assistantText });
1192
1194
  }
1193
1195
  // Finalize pending tool cards so they don't stay "Running..." forever
1194
1196
  // after cancellation. Without this, the spinner vanishes and TurnDone
@@ -1198,6 +1200,7 @@ export function createRunTurn(bag) {
1198
1200
  flushToolResults([], toolCards, cardByCallId, toolGroups, resultsDone, { finalize: true, cancelled: true });
1199
1201
  finalizeToolHeaders();
1200
1202
  } else {
1203
+ failed = true;
1201
1204
  finalizeToolHeaders();
1202
1205
  pushNotice(toolErrorDisplay(error, 'turn'), 'error');
1203
1206
  }
@@ -1255,7 +1258,7 @@ export function createRunTurn(bag) {
1255
1258
  const finalAssistantLen = Math.max(assistantText.length, currentAssistantText.length);
1256
1259
  const finalResponseLength = finalAssistantLen + thinkingText.length;
1257
1260
  const finalOutputTokens = Math.max(0, Number(getState().spinner?.outputTokens || 0), Math.round(finalResponseLength / 4));
1258
- const turnStatus = cancelled ? 'cancelled' : 'done';
1261
+ const turnStatus = cancelled ? 'cancelled' : (failed ? 'failed' : 'done');
1259
1262
  const resultContent = askResult?.content != null ? String(askResult.content).trim() : '';
1260
1263
  const assistantOutput = (currentAssistantText || assistantText || '').trim();
1261
1264
  // Suppress only true pending-resume no-ops: no transcript items added and no model output; cancelled/error turns and any visible turn stay marked.
@@ -1293,8 +1296,9 @@ export function createRunTurn(bag) {
1293
1296
  // tool-summary line (same epoch rule as the shared-getState() block above).
1294
1297
  if (flags.leadTurnEpoch === turnEpoch) clearActiveToolSummary();
1295
1298
  _publishedThinkingActive = false; // turn teardown cleared getState().thinking
1296
- tuiDebug(`runTurn end turn=${turnIndex} status=${cancelled ? 'cancelled' : 'done'} elapsedMs=${Date.now() - startedAt}${watchdogTripped ? ' watchdogTripped=1' : ''} pending=${pending.length}`);
1297
- return cancelled ? 'cancelled' : 'done';
1299
+ const finalStatus = cancelled ? 'cancelled' : (failed ? 'failed' : 'done');
1300
+ tuiDebug(`runTurn end turn=${turnIndex} status=${finalStatus} elapsedMs=${Date.now() - startedAt}${watchdogTripped ? ' watchdogTripped=1' : ''} pending=${pending.length}`);
1301
+ return finalStatus;
1298
1302
  }
1299
1303
 
1300
1304
  return runTurn;
@@ -228,6 +228,8 @@ export async function createEngineSession({
228
228
  model,
229
229
  toolMode = 'full',
230
230
  remote = false,
231
+ cwd,
232
+ desktopSession,
231
233
  } = {}) {
232
234
  const startedAt = performance.now();
233
235
  bootProfile('engine:create:start', { provider: providerName, model, toolMode, remote });
@@ -242,9 +244,16 @@ export async function createEngineSession({
242
244
  const importStartedAt = performance.now();
243
245
  const { createMixdogSessionRuntime } = await import(SESSION_RUNTIME_MODULE);
244
246
  bootProfile('session-runtime:imported', { ms: (performance.now() - importStartedAt).toFixed(1) });
245
- const runtime = await createMixdogSessionRuntime({ provider: providerName, model, toolMode, remote });
247
+ const runtime = await createMixdogSessionRuntime({
248
+ provider: providerName,
249
+ model,
250
+ toolMode,
251
+ remote,
252
+ ...(cwd ? { cwd } : {}),
253
+ ...(desktopSession ? { desktopSession } : {}),
254
+ });
246
255
  bootProfile('engine:create:runtime-ready', { ms: (performance.now() - startedAt).toFixed(1) });
247
- const cwd = runtime.cwd || process.cwd();
256
+ const runtimeCwd = runtime.cwd || process.cwd();
248
257
  const stateStartedAt = performance.now();
249
258
  const flags = {
250
259
  disposed: false,
@@ -308,14 +317,14 @@ export async function createEngineSession({
308
317
  activeToolSummary: null,
309
318
  // Seed from the persisted cwd-scoped store so up-arrow history is available
310
319
  // on a fresh start, before any bulk swap / first submit republishes it.
311
- promptHistoryList: buildMergedPromptHistory([], loadPromptHistory(cwd)),
320
+ promptHistoryList: buildMergedPromptHistory([], loadPromptHistory(runtimeCwd)),
312
321
  ...baseRouteState(),
313
322
  displayContextWindow: 0,
314
323
  compactBoundaryTokens: 0,
315
324
  autoCompactTokenLimit: 0,
316
325
  ...initialAgentState,
317
326
  toolMode: runtime.toolMode,
318
- cwd,
327
+ cwd: runtimeCwd,
319
328
  themeEpoch: 0,
320
329
  };
321
330
  bootProfile('engine:route-state-ready', { ms: (performance.now() - stateStartedAt).toFixed(1) });
package/src/tui/index.jsx CHANGED
@@ -15,6 +15,7 @@ import { App } from './App.jsx';
15
15
  import { createEngineSession } from './engine.mjs';
16
16
  import { scheduleRenderFrameAck } from './engine/render-timing.mjs';
17
17
  import { installProcessSignalCleanup } from '../runtime/shared/process-shutdown.mjs';
18
+ import { finishProcessLifecycle } from '../runtime/shared/process-lifecycle.mjs';
18
19
  import { rgbSgr } from '../ui/ansi.mjs';
19
20
  import { emitTerminalBackground, loadThemeSettingFromConfig, theme } from './theme.mjs';
20
21
  import { POP_KITTY, DISABLE_MODIFY_OTHER_KEYS } from './keyboard-protocol.mjs';
@@ -522,7 +523,8 @@ export async function runTui({ provider, model, toolMode, remote, forceOnboardin
522
523
  const disposeStoreOnce = async (reason = 'cli-react-exit') => {
523
524
  if (storeDisposed) return;
524
525
  storeDisposed = true;
525
- await waitWithTimeout(store.dispose?.(reason), EXIT_WAIT_TIMEOUT_MS);
526
+ const completed = await waitWithTimeout(store.dispose?.(reason), EXIT_WAIT_TIMEOUT_MS);
527
+ if (!completed) throw new Error(`mixdog-tui dispose timed out after ${EXIT_WAIT_TIMEOUT_MS}ms`);
526
528
  };
527
529
  const signalCleanup = installProcessSignalCleanup({
528
530
  name: 'mixdog-tui',
@@ -627,6 +629,7 @@ export async function runTui({ provider, model, toolMode, remote, forceOnboardin
627
629
  await disposeStoreOnce('cli-react-exit');
628
630
  } catch (error) {
629
631
  tuiExitDebug('store:dispose:failed', { error: error?.message || String(error) });
632
+ finishProcessLifecycle('forced-cleanup', 0);
630
633
  }
631
634
  restoreTerminal();
632
635
  dumpActiveHandles('after-restore');
@@ -47,6 +47,22 @@ export function isVoiceEnabled() {
47
47
  }
48
48
  }
49
49
 
50
+ /** Read the managed runtime state without installing or mutating anything. */
51
+ export async function getVoiceStatus({ dataDir = resolvePluginData() } = {}) {
52
+ const fetcher = await loadVoiceRuntimeFetcher();
53
+ const runtime = fetcher.resolveVoiceRuntime(dataDir);
54
+ return {
55
+ enabled: isVoiceEnabled(),
56
+ busy: isVoiceInstallBusy(),
57
+ installed: runtime.installed === true,
58
+ components: {
59
+ whisper: Boolean(runtime.binary && runtime.serverCmd),
60
+ model: Boolean(runtime.model),
61
+ ffmpeg: Boolean(runtime.ffmpeg),
62
+ },
63
+ };
64
+ }
65
+
50
66
  // Coalesce ensure*'s onProgress ({ phase, downloaded, total } | { phase:'extra', ... })
51
67
  // into a sticky, in-place input-hint-line progress bar (setProgressHint) so a
52
68
  // multi-hundred-MB model download doesn't spam the notice toast on every