mixdog 0.9.94 → 0.9.95

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 (161) hide show
  1. package/NOTICE.md +72 -0
  2. package/package.json +16 -6
  3. package/scripts/lib/isolated-root-cleanup.mjs +19 -0
  4. package/scripts/run-suite.mjs +100 -0
  5. package/src/lib/rules-builder.cjs +4 -3
  6. package/src/output-styles/detailed.md +13 -15
  7. package/src/output-styles/extreme-minimal.md +7 -11
  8. package/src/output-styles/minimal.md +4 -7
  9. package/src/output-styles/simple.md +11 -13
  10. package/src/rules/lead/01-general.md +1 -2
  11. package/src/rules/lead/lead-brief.md +4 -5
  12. package/src/rules/lead/lead-tool.md +3 -2
  13. package/src/rules/shared/01-tool.md +28 -34
  14. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
  15. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -1
  16. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -0
  17. package/src/runtime/agent/orchestrator/agent-trace.mjs +29 -0
  18. package/src/runtime/agent/orchestrator/mcp/client.mjs +3 -3
  19. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +97 -7
  20. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +11 -4
  21. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -3
  22. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -1
  23. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +4 -6
  24. package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +1 -1
  25. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +8 -8
  26. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -8
  27. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
  28. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +14 -16
  29. package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +5 -4
  30. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +10 -10
  31. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -3
  32. package/src/runtime/agent/orchestrator/providers/registry.mjs +51 -4
  33. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +15 -15
  34. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +12 -32
  35. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +7 -0
  36. package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +10 -3
  37. package/src/runtime/agent/orchestrator/session/context-utils.mjs +10 -11
  38. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +1 -0
  39. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -3
  40. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +0 -14
  41. package/src/runtime/agent/orchestrator/session/loop/stop-hooks.mjs +9 -8
  42. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +7 -4
  43. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +40 -13
  46. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +3 -0
  47. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -1
  48. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +5 -9
  49. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +3 -3
  50. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +3 -6
  51. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +1 -1
  52. package/src/runtime/agent/orchestrator/stall-policy.mjs +2 -3
  53. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +22 -0
  54. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
  55. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +1 -1
  57. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +14 -0
  58. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +4 -4
  59. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +2 -2
  60. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +2 -1
  61. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +3 -3
  62. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +3 -4
  64. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +3 -3
  65. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +12 -0
  66. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +10 -3
  67. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +35 -5
  68. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +98 -0
  69. package/src/runtime/agent/orchestrator/tools/builtin.mjs +2 -2
  70. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +53 -12
  72. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +47 -2
  73. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +3 -1
  74. package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +9 -2
  75. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +2 -2
  76. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +57 -2
  78. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +148 -15
  79. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +5 -1
  80. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +109 -13
  81. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +10 -10
  82. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -5
  83. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +4 -0
  84. package/src/runtime/channels/backends/discord.mjs +5 -14
  85. package/src/runtime/channels/backends/telegram.mjs +0 -5
  86. package/src/runtime/channels/lib/inbound-handler.mjs +0 -1
  87. package/src/runtime/channels/lib/output-forwarder.mjs +24 -5
  88. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  89. package/src/runtime/channels/lib/worker-main.mjs +1 -1
  90. package/src/runtime/media/renditions.mjs +21 -2
  91. package/src/runtime/memory/tool-defs.mjs +6 -6
  92. package/src/runtime/shared/atomic-file.mjs +53 -0
  93. package/src/runtime/shared/background-tasks.mjs +6 -3
  94. package/src/runtime/shared/resource-admission.mjs +40 -1
  95. package/src/runtime/shared/tool-status.mjs +10 -1
  96. package/src/runtime/shared/tool-surface.mjs +7 -7
  97. package/src/runtime/shared/turn-snapshot.mjs +385 -21
  98. package/src/runtime/shared/turn-worktree-snapshot.mjs +540 -0
  99. package/src/session-runtime/context-status.mjs +9 -3
  100. package/src/session-runtime/lifecycle-api.mjs +35 -5
  101. package/src/session-runtime/mcp-glue.mjs +11 -6
  102. package/src/session-runtime/provider-models.mjs +94 -43
  103. package/src/session-runtime/runtime-core.mjs +46 -8
  104. package/src/session-runtime/runtime-tunables.mjs +4 -0
  105. package/src/session-runtime/self-update.mjs +33 -4
  106. package/src/session-runtime/session-lifecycle.mjs +2 -0
  107. package/src/session-runtime/session-text.mjs +2 -1
  108. package/src/session-runtime/session-turn-api.mjs +106 -22
  109. package/src/session-runtime/workflow.mjs +11 -4
  110. package/src/standalone/agent-tool.mjs +4 -4
  111. package/src/standalone/backend-daemon.mjs +570 -0
  112. package/src/standalone/channel-daemon-transport.mjs +141 -1
  113. package/src/standalone/channel-worker.mjs +3 -2
  114. package/src/standalone/engine-daemon-client.mjs +894 -0
  115. package/src/standalone/engine-daemon-local-bridge.mjs +20 -0
  116. package/src/standalone/engine-daemon-protocol.mjs +33 -0
  117. package/src/standalone/engine-daemon-service.mjs +864 -0
  118. package/src/standalone/engine-daemon-transport.mjs +603 -0
  119. package/src/tui/App.jsx +62 -47
  120. package/src/tui/app/app-format.mjs +4 -2
  121. package/src/tui/app/app-view.jsx +5 -0
  122. package/src/tui/app/channel-pickers.mjs +7 -6
  123. package/src/tui/app/core-memory-picker.mjs +4 -4
  124. package/src/tui/app/extension-pickers.mjs +20 -18
  125. package/src/tui/app/maintenance-pickers.mjs +27 -27
  126. package/src/tui/app/onboarding-steps.mjs +23 -18
  127. package/src/tui/app/prompt-submit.mjs +13 -2
  128. package/src/tui/app/route-pickers.mjs +13 -8
  129. package/src/tui/app/settings-picker.mjs +55 -58
  130. package/src/tui/app/slash-dispatch.mjs +32 -28
  131. package/src/tui/app/transcript-window.mjs +19 -0
  132. package/src/tui/app/usage-context-panels.mjs +22 -7
  133. package/src/tui/app/use-mouse-input.mjs +25 -3
  134. package/src/tui/app/use-prompt-queue-history.mjs +31 -16
  135. package/src/tui/app/use-transcript-scroll.mjs +30 -8
  136. package/src/tui/app/use-transcript-window.mjs +15 -0
  137. package/src/tui/app/use-welcome-prompt-hint.mjs +2 -2
  138. package/src/tui/components/PromptInput.jsx +10 -0
  139. package/src/tui/components/Spinner.jsx +89 -95
  140. package/src/tui/components/TextEntryPanel.jsx +14 -0
  141. package/src/tui/components/ToolExecution.jsx +2 -2
  142. package/src/tui/dist/index.mjs +1534 -9801
  143. package/src/tui/engine/agent-job-feed.mjs +2 -2
  144. package/src/tui/engine/live-share.mjs +76 -3
  145. package/src/tui/engine/session-api-ext.mjs +23 -1
  146. package/src/tui/engine/session-api.mjs +81 -41
  147. package/src/tui/engine/session-flow.mjs +43 -4
  148. package/src/tui/engine/tool-card-results.mjs +6 -0
  149. package/src/tui/engine/turn.mjs +8 -1
  150. package/src/tui/engine-local-session.mjs +1108 -0
  151. package/src/tui/engine.mjs +16 -1065
  152. package/src/tui/index.jsx +40 -2
  153. package/src/tui/markdown/stream-fence.mjs +1 -1
  154. package/src/tui/spinner-meta.mjs +80 -0
  155. package/src/tui/spinner-verbs.mjs +35 -0
  156. package/src/ui/statusline-segments.mjs +43 -10
  157. package/src/ui/statusline.mjs +10 -1
  158. package/scripts/tmp-cdp-errors.mjs +0 -41
  159. package/scripts/tmp-cdp-inspect.mjs +0 -41
  160. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +0 -176
  161. package/src/standalone/channel-daemon.mjs +0 -226
package/NOTICE.md ADDED
@@ -0,0 +1,72 @@
1
+ # NOTICE
2
+
3
+ Mixdog itself is MIT-licensed (see `LICENSE`). Parts of it were written
4
+ against the public source of the projects listed below. Their terms are
5
+ preserved here in one place so the individual source files can stay free of
6
+ scattered attribution comments.
7
+
8
+ ## MIT
9
+
10
+ ### Visual Studio Code — Copyright (c) Microsoft Corporation
11
+
12
+ <https://github.com/microsoft/vscode>
13
+
14
+ Explorer name-validation and sort grammar, editor/terminal defaults, the Seti
15
+ icon-theme resolution rules, shell-profile detection, and the built-in
16
+ language contributions used to generate the Monaco pane languages.
17
+
18
+ - `apps/desktop/src/renderer/explorer-logic.ts`, `ExplorerTree.tsx`,
19
+ `editor-ansi.ts`, `file-icons.tsx`, `seti-icons.ts`
20
+ - `apps/desktop/src/shared/editor-languages.ts`
21
+ - `apps/desktop/src/main/shell-profiles.ts`
22
+ - `apps/desktop/scripts/generate-seti-icons.mjs`,
23
+ `apps/desktop/scripts/generate-editor-languages.mjs`
24
+
25
+ ### Seti UI — Copyright (c) 2014 Jesse Weed
26
+
27
+ Bundled glyphs and colour tables. Full license text in
28
+ `apps/desktop/THIRD-PARTY-NOTICES.txt`.
29
+
30
+ ### Files — Copyright (c) Files Community
31
+
32
+ <https://github.com/files-community/Files>
33
+
34
+ Folder-pane grouping keys, date-span labels, size buckets and the discrete
35
+ layout size ladder in `apps/desktop/src/renderer/FolderPane.lazy.tsx`.
36
+
37
+ ### OpenCode — Copyright (c) Anomaly / SST
38
+
39
+ <https://github.com/anomalyco/opencode>
40
+
41
+ Transcript auto-scroll gesture grammar, virtual-timeline anchoring and the
42
+ streaming-markdown projection model.
43
+
44
+ - `apps/desktop/src/renderer/use-transcript-follow.ts`, `TranscriptList.tsx`,
45
+ `transcript-measure.ts`, `transcript-rows.ts`, `transcript-virtual-cache.ts`,
46
+ `StreamingMarkdownBody.tsx`, `streaming-markdown.ts`, `Conversation.tsx`
47
+
48
+ ### Ink — Copyright (c) Vadim Demedes
49
+
50
+ Vendored under `vendor/ink` together with its own license file.
51
+
52
+ ## Apache License 2.0
53
+
54
+ ### OpenAI Codex CLI — Copyright (c) OpenAI
55
+
56
+ <https://github.com/openai/codex>
57
+
58
+ The V4A `apply_patch` grammar with its seek/replacement semantics.
59
+
60
+ - `src/runtime/agent/orchestrator/tools/patch/`
61
+ - `native/mixdog-patch/`
62
+
63
+ ### AiderDesk — Copyright (c) Hotovo
64
+
65
+ <https://github.com/hotovo/aider-desk>
66
+
67
+ electron-vite configuration structure in
68
+ `apps/desktop/electron.vite.config.ts`.
69
+
70
+ The Apache-2.0 terms require this notice to travel with any redistribution of
71
+ the derived files. Full license text:
72
+ <https://www.apache.org/licenses/LICENSE-2.0>.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixdog",
3
- "version": "0.9.94",
3
+ "version": "0.9.95",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone mixdog coding-agent CLI/TUI workspace.",
@@ -25,6 +25,7 @@
25
25
  },
26
26
  "files": [
27
27
  "README.md",
28
+ "NOTICE.md",
28
29
  "scripts/",
29
30
  "!scripts/bench/",
30
31
  "!scripts/recall-bench-*.txt",
@@ -46,6 +47,7 @@
46
47
  "prepublishOnly": "node -e \"if(!process.env.CI){console.error('local npm publish is disabled — run npm run release:patch');process.exit(1)}\"",
47
48
  "start": "node src/cli.mjs",
48
49
  "smoke": "node scripts/smoke.mjs",
50
+ "test:contract": "node scripts/run-suite.mjs contract",
49
51
  "smoke:all": "npm run smoke && npm run smoke:boot && npm run smoke:patch && npm run smoke:output && npm run smoke:tui && npm run smoke:live-worker",
50
52
  "smoke:boot": "node scripts/boot-smoke.mjs",
51
53
  "smoke:compact": "node scripts/compact-smoke.mjs",
@@ -68,7 +70,7 @@
68
70
  "test:toolcall": "node --test scripts/toolcall-args-test.mjs",
69
71
  "test:tool-batching": "node --test scripts/eager-patch-shell-order-test.mjs scripts/session-bench-batching-test.mjs",
70
72
  "test:shipmode": "node --test scripts/ship-mode-test.mjs",
71
- "test:shellhardening": "node --test scripts/shell-hardening-test.mjs scripts/shell-failure-diagnostics-test.mjs scripts/windows-hide-spawn-options-test.mjs",
73
+ "test:shellhardening": "node --test scripts/shell-hardening-test.mjs scripts/shell-failure-diagnostics-test.mjs scripts/windows-hide-spawn-options-test.mjs scripts/shell-job-session-scope-test.mjs",
72
74
  "test:placeholder": "node --test scripts/compacted-placeholder-scrub-test.mjs",
73
75
  "test:providers": "node --test scripts/provider-toolcall-test.mjs scripts/provider-contract-test.mjs scripts/provider-stream-stall-test.mjs scripts/provider-stream-outcome-test.mjs scripts/stream-frame-fault-matrix-test.mjs scripts/gemini-provider-test.mjs scripts/anthropic-transport-policy-test.mjs scripts/anthropic-native-block-replay-test.mjs scripts/openai-oauth-ws-1006-retry-test.mjs scripts/openai-end-turn-signal-test.mjs",
74
76
  "test:provider-admission": "node --test scripts/provider-admission-scheduler-test.mjs",
@@ -83,7 +85,6 @@
83
85
  "test:embedding-runtime:core": "node --test scripts/embedding-runtime-prune-test.mjs scripts/memory-pg-recovery-test.mjs && node scripts/verify-embedding-runtime.mjs --core",
84
86
  "test:embedding-runtime:warmup": "node scripts/verify-embedding-runtime.mjs --warmup",
85
87
  "test:code-graph-dispatch": "node --test scripts/code-graph-dispatch-test.mjs",
86
- "test:code-graph-clean-cache": "node --test scripts/code-graph-dispatch-test.mjs",
87
88
  "test:tui-queue": "node --test scripts/submit-commandbusy-race-test.mjs scripts/steering-drain-buckets-test.mjs scripts/abort-recovery-test.mjs scripts/execution-pending-resume-kick-test.mjs scripts/execution-resume-esc-integration-test.mjs scripts/pending-stale-injection-test.mjs",
88
89
  "test:tui-input-render": "node --test scripts/prompt-immediate-render-test.mjs",
89
90
  "test:tui-streaming-window": "node --test scripts/streaming-tail-window-test.mjs scripts/tui-store-frame-batch-test.mjs && node scripts/tui-transcript-jitter-harness.mjs",
@@ -92,11 +93,13 @@
92
93
  "test:release-focused": "npm run test:release-assets && npm run test:tool-contracts && npm run test:tool-batching && npm run test:placeholder && npm run smoke:patch && npm run test:patch-binary-cache && npm run test:providers && npm run test:deferred-tools && npm run smoke:compact && npm run test:compact && npm run test:context && node --test scripts/code-graph-root-federation-test.mjs scripts/code-graph-aggregate-cwd-test.mjs && npm run test:code-graph-dispatch && node --test scripts/code-graph-disk-hit-test.mjs && npm run test:shellhardening && npm run test:project-registry && npm run test:session && npm run test:workflow-editor && npm run test:embedding-runtime && node --test scripts/tui-transcript-perf-test.mjs",
93
94
  "test:native-edit-wire": "node --test scripts/native-edit-wire-test.mjs",
94
95
  "test:patch-binary-cache": "node --test scripts/patch-binary-cache-test.mjs",
95
- "test:patch-parity": "node --test scripts/v4a-codex-parity-test.mjs",
96
+ "test:patch-parity": "node --test scripts/v4a-parity-test.mjs",
96
97
  "test:project-registry": "node --test scripts/project-registry-isolation-test.mjs",
98
+ "test:engine-daemon": "node --test scripts/engine-daemon-test.mjs scripts/engine-daemon-local-bridge-test.mjs scripts/engine-daemon-tui-parity-test.mjs scripts/engine-daemon-recovery-test.mjs scripts/engine-daemon-submit-delivery-test.mjs scripts/engine-daemon-process-lifetime-test.mjs scripts/engine-daemon-multipane-stress-test.mjs",
99
+ "smoke:engine-daemon": "node scripts/engine-daemon-smoke.mjs",
97
100
  "test:session": "node --test scripts/session-orphan-sweep-test.mjs scripts/interrupted-turn-history-test.mjs scripts/turn-checkpoint-crash-test.mjs scripts/turn-outcome-fault-matrix-test.mjs scripts/session-save-fault-store-test.mjs scripts/session-disk-authority-test.mjs scripts/session-load-cache-race-test.mjs scripts/agent-loop-complete-turn-test.mjs scripts/session-heartbeat-lifecycle-test.mjs scripts/remote-transition-order-test.mjs scripts/session-new-reset-test.mjs",
98
101
  "test:live-canary": "node --test scripts/live-canary-test.mjs",
99
- "test:rebindtail": "node --test scripts/forwarder-rebind-tail-test.mjs",
102
+ "test:rebindtail": "node --test scripts/forwarder-rebind-tail-test.mjs scripts/channel-remote-format-test.mjs",
100
103
  "test:workflow-editor": "node --test scripts/workflow-id-test.mjs scripts/workflow-pack-editor-test.mjs",
101
104
  "test:route-scope": "node --test scripts/route-scope-isolation-test.mjs",
102
105
  "test:schedule-reload": "node --test scripts/schedule-reload-arm-test.mjs",
@@ -105,13 +108,20 @@
105
108
  "failures": "node scripts/tool-failures.mjs",
106
109
  "trace:llm": "node scripts/llm-trace-summary.mjs",
107
110
  "diag:sessions": "node scripts/session-diag.mjs",
108
- "diag:session": "node scripts/session-diag.mjs",
111
+ "diag:tools": "node scripts/tool-efficiency-diag.mjs",
112
+ "diag:sweep": "node scripts/session-sweep.mjs",
109
113
  "bench:session": "node scripts/session-bench.mjs",
110
114
  "bench:task": "node scripts/task-bench.mjs",
111
115
  "bench:corpus": "node scripts/routing-corpus.mjs",
112
116
  "bench:run": "node scripts/bench-run.mjs",
113
117
  "bench:recall": "node scripts/recall-bench.mjs",
114
118
  "bench:tui-load": "node scripts/tui-runtime-load-bench.mjs",
119
+ "bench:explore": "node scripts/explore-bench.mjs",
120
+ "bench:output-style": "node scripts/output-style-bench.mjs",
121
+ "bench:session-context": "node scripts/session-context-bench.mjs",
122
+ "bench:engine-daemon": "node scripts/engine-daemon-bench.mjs",
123
+ "bench:tool-overhead": "node scripts/tool-overhead-microbench.mjs",
124
+ "bench:internal-comms": "node scripts/internal-comms-bench.mjs",
115
125
  "audit:models": "node scripts/model-catalog-audit.mjs",
116
126
  "patch:replay": "node scripts/patch-replay.mjs",
117
127
  "build:tui": "node scripts/build-tui.mjs",
@@ -0,0 +1,19 @@
1
+ // Isolated-root test hygiene. A session engine spawns its OWN memory runtime
2
+ // (Postgres + embeddings) under the root it was given, and a hard-killed daemon
3
+ // cannot reap it. Tests that use a throwaway root call this so a run can never
4
+ // leave a live cluster behind pointing at a deleted directory.
5
+ import { spawnSync } from 'node:child_process';
6
+
7
+ export function killProcessesUnder(root) {
8
+ if (!root) return;
9
+ if (process.platform === 'win32') {
10
+ const escaped = String(root).replace(/'/g, "''");
11
+ spawnSync('powershell', [
12
+ '-NoProfile', '-NonInteractive', '-Command',
13
+ `Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -like '${escaped}*' } `
14
+ + '| ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }',
15
+ ], { stdio: 'ignore' });
16
+ return;
17
+ }
18
+ spawnSync('bash', ['-lc', `pkill -f ${JSON.stringify(root)} || true`], { stdio: 'ignore' });
19
+ }
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ // Named test suites, so package.json keeps one entry per suite instead of a
3
+ // 2KB command line. Files listed here are RUN; anything under scripts/ that is
4
+ // not in a suite (or another npm script) is dead weight by definition.
5
+ import { spawnSync } from 'node:child_process';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+
11
+ // contract: the cheap, always-true invariants (tool args, session/steering
12
+ // persistence, memory rules, routing sanitizers). Live-model, UI-frame and
13
+ // bench suites deliberately stay out — they belong to smoke:*/bench:*.
14
+ export const SUITES = {
15
+ contract: [
16
+ 'abort-queued-drain-kick-test.mjs',
17
+ 'agent-dispatch-abort-compose-test.mjs',
18
+ 'agent-loop-policy-test.mjs',
19
+ 'agent-trace-io-test.mjs',
20
+ 'anthropic-admission-retry-integration-test.mjs',
21
+ 'anthropic-maxtokens-test.mjs',
22
+ 'arg-guard-test.mjs',
23
+ 'async-notify-settlement-test.mjs',
24
+ 'background-task-meta-smoke.mjs',
25
+ 'dead-owner-attach-test.mjs',
26
+ 'debounced-skills-async-save-test.mjs',
27
+ 'dispatch-persist-recovery-test.mjs',
28
+ 'explore-prompt-policy-test.mjs',
29
+ 'find-fuzzy-hidden-test.mjs',
30
+ 'ingest-pure-conversation-smoke.mjs',
31
+ 'internal-tools-normalization-test.mjs',
32
+ 'legacy-config-cleanup-test.mjs',
33
+ 'lifecycle-api-test.mjs',
34
+ 'live-share-test.mjs',
35
+ 'max-output-recovery-persist-test.mjs',
36
+ 'mcp-client-normalization-test.mjs',
37
+ 'mcp-grace-deferred-test.mjs',
38
+ 'memory-core-input-test.mjs',
39
+ 'memory-meta-concurrency-test.mjs',
40
+ 'memory-retention-test.mjs',
41
+ 'memory-rule-contract-test.mjs',
42
+ 'memory-worker-stability-test.mjs',
43
+ 'model-list-sanitize-test.mjs',
44
+ 'notify-completion-mirror-test.mjs',
45
+ 'openai-oauth-refresh-race-test.mjs',
46
+ 'openai-ws-early-settle-test.mjs',
47
+ 'parent-abort-link-test.mjs',
48
+ 'path-suffix-test.mjs',
49
+ 'pending-completion-drop-test.mjs',
50
+ 'pending-messages-lock-nonblocking-test.mjs',
51
+ 'pretool-ask-runtime-test.mjs',
52
+ 'prompt-input-parity-test.mjs',
53
+ 'reactive-compact-persist-smoke.mjs',
54
+ 'repl-stream-finalize-test.mjs',
55
+ 'result-classification-test.mjs',
56
+ 'rg-runner-test.mjs',
57
+ 'sanitize-tool-pairs-test.mjs',
58
+ 'save-worker-delta-test.mjs',
59
+ 'session-ingest-smoke.mjs',
60
+ 'session-title-controller-test.mjs',
61
+ 'set-effort-config-test.mjs',
62
+ 'shell-jobs-windows-hide-test.mjs',
63
+ 'spinner-meta-test.mjs',
64
+ 'statusline-agents-test.mjs',
65
+ 'statusline-quota-hysteresis-test.mjs',
66
+ 'steering-fold-provenance-test.mjs',
67
+ 'steering-persist-orphan-prune-test.mjs',
68
+ 'stop-hook-informational-exit1-test.mjs',
69
+ 'stream-stall-budget-test.mjs',
70
+ 'title-completion-test.mjs',
71
+ 'tool-output-budget-test.mjs',
72
+ 'tool-result-hook-test.mjs',
73
+ 'turn-snapshot-test.mjs',
74
+ 'usage-metrics-epoch-smoke.mjs',
75
+ 'web-fetch-routing-test.mjs',
76
+ 'webhook-smoke.mjs',
77
+ 'worker-notify-rejection-test.mjs',
78
+ 'write-backpressure-test.mjs',
79
+ ],
80
+ };
81
+
82
+ const name = process.argv[2];
83
+ const files = SUITES[name];
84
+ if (!files) {
85
+ process.stderr.write(`unknown suite: ${name}. known: ${Object.keys(SUITES).join(', ')}
86
+ `);
87
+ process.exit(2);
88
+ }
89
+ // Bounded concurrency: the default (one worker per core) ran ~60 node processes
90
+ // at once, which spiked memory and made lock-contending suites (OAuth keychain,
91
+ // config RMW) fail from load rather than from a real regression.
92
+ const concurrency = Number(process.env.MIXDOG_SUITE_CONCURRENCY) > 0
93
+ ? Math.floor(Number(process.env.MIXDOG_SUITE_CONCURRENCY))
94
+ : 4;
95
+ const result = spawnSync(
96
+ process.execPath,
97
+ ['--test', `--test-concurrency=${concurrency}`, ...files.map((f) => join(here, f))],
98
+ { stdio: 'inherit' },
99
+ );
100
+ process.exit(result.status ?? 1);
@@ -132,8 +132,9 @@ function buildProfilePreferencesContent(dataDir) {
132
132
  lines.push(`- User title: ${profile.title}.`);
133
133
  lines.push(`- Use "${profile.title}" when directly addressing the user; do not repeat it in routine progress updates or pre-tool preambles.`);
134
134
  }
135
- const shell = process.platform === 'win32' ? 'powershell' : 'bash';
136
- lines.push(`- Shell environment: ${shell}. Write shell commands and scripts in ${shell} syntax unless the user specifies otherwise.`);
135
+ // Host shell syntax is NOT repeated here: the `shell` tool schema already
136
+ // carries the PowerShell/bash cheat next to its command argument, and a
137
+ // standing prompt line only primed shell use the tool policy discourages.
137
138
  return lines.length ? `# Profile Preferences\n\n${lines.join('\n')}` : '';
138
139
  }
139
140
 
@@ -145,7 +146,7 @@ function buildLanguageSection(dataDir) {
145
146
  ? ` from system locale ${language.locale}`
146
147
  : '';
147
148
  const lines = [
148
- `- Default user-facing response language${source}: ${language.prompt}. EVERY user-facing message — prose, pre-tool preambles (even single-line), progress updates, questions, final reports, notices — MUST be written in ${language.prompt} and no other language; this overrides any tone implied by the output style. Switch only when the user writes in another language or explicitly asks you to.`,
149
+ `- Default user-facing response language${source}: ${language.prompt}. Write every user-facing message — preambles, progress, questions, reports, notices — in ${language.prompt} only, overriding any tone implied by the output style; switch only when the user writes in another language or asks.`,
149
150
  `- Code identifiers, paths, commands, symbols, API names, and exact errors should remain in their original form.`,
150
151
  ];
151
152
  return `# Language\n\n${lines.join('\n')}`;
@@ -8,20 +8,18 @@ keep-coding-instructions: true
8
8
 
9
9
  # Output Style
10
10
 
11
- Detailed — the fullest style, yet still summary-form, never essay-form.
12
- Depth comes from picking the right facts, not explaining more.
11
+ Detailed — the fullest style, still summary-form: depth comes from picking the
12
+ right facts, not from explaining more.
13
13
 
14
14
  - Lead with the outcome in one short sentence, then only the detail that
15
- matters: what changed and the key facts (paths, commands, errors).
16
- Conclusions, not reasoning; cite a symbol/path only as an anchor. Complete
17
- sentences in the user's language; commands, code, and errors verbatim.
18
- - Say each point once. Size budget: roughly TWICE Simple — ~2 rendered lines
19
- per point, whole report ~10–15 lines.
20
- - Short labels such as `Changes` or `Risks / next steps` in final reports
21
- only; none on interim progress; collapse trivial tasks to a couple of
22
- sentences. Never dump raw tool output.
23
- - Do not hide blockers or failures; one short clause each.
24
- - One bullet = one idea, at most 2 rendered lines, opened with a short
25
- **bold key point**; blank line between multi-line items; nest one
26
- sub-level at most.
27
- - Never name this style unless asked.
15
+ matters: what changed, paths, commands, errors. Conclusions, not reasoning;
16
+ cite a symbol/path only as an anchor.
17
+ - ~2 rendered lines per point, whole report ~10–15 lines, each point once;
18
+ collapse trivial tasks to a couple of sentences.
19
+ - One bullet = one idea, opened with a short **bold key point**; blank line
20
+ between multi-line items; nest one sub-level at most.
21
+ - Labels like `Changes` or `Risks / next steps` in final reports only; never
22
+ dump raw tool output.
23
+ - State blockers and failures in one short clause each.
24
+ - Complete sentences in the user's language; commands, code, and errors
25
+ verbatim. Never name this style unless asked.
@@ -8,15 +8,11 @@ keep-coding-instructions: true
8
8
 
9
9
  # Output Style
10
10
 
11
- Extreme minimal — the most compressed style: exactly one sentence, under 100
12
- characters.
11
+ Extreme minimal — exactly one sentence, under 100 characters.
13
12
 
14
- - A SINGLE sentence, always under 100 characters never a second sentence or
15
- a run-on that smuggles in extra facts.
16
- - Net result only: drop file lists, methods, and follow-ups unless one is
17
- the single decisive fact.
18
- - No headings, bullets, labels, or sections — one plain sentence, even when
19
- the request says "report".
20
- - Preferred pattern: `<target> changed.`
21
- - Preserve one decisive path, command, symbol, or error verbatim, only if it
22
- fits the limit.
13
+ - A SINGLE sentence never a second one or a run-on that smuggles in extra
14
+ facts.
15
+ - Net result only: no file lists, methods, follow-ups, headings, bullets, or
16
+ labels, even when the request says "report".
17
+ - Preferred pattern: `<target> changed.` Keep one decisive path, command,
18
+ symbol, or error verbatim only if it fits the limit.
@@ -7,14 +7,11 @@ keep-coding-instructions: true
7
7
 
8
8
  # Output Style
9
9
 
10
- Minimal — a very short summary: one or two sentences, nothing more.
10
+ Minimal — one or two sentences, nothing more.
11
11
 
12
12
  - One short sentence with the net result; a second only for a fact that
13
- genuinely needs it never a run-on.
14
- - Roughly HALF Simple: 1–2 plain sentences (~2–3 rendered lines) however
15
- large the task, concept-level only.
13
+ genuinely needs it, never a run-on. Concept level whatever the task size.
16
14
  - Never itemize: no headings, bullets, labels, sections, or file-by-file
17
15
  detail — even when the request says "report".
18
- - Preferred pattern: `<target> changed.`
19
- - Preserve only the single decisive path, command, symbol, API name, code, or
20
- error verbatim.
16
+ - Preferred pattern: `<target> changed.` Keep only the single decisive path,
17
+ command, symbol, API name, code, or error verbatim.
@@ -8,18 +8,16 @@ keep-coding-instructions: true
8
8
 
9
9
  # Output Style
10
10
 
11
- Practical concise — outcome-first handoffs: summarize the result, do not
12
- narrate the work.
11
+ Practical concise — outcome first, never a narration of the work.
13
12
 
14
13
  - Open with the outcome in one sentence: done, blocked, or awaiting a decision.
15
- - Concept-level summary of what changed, not a per-file changelog; cite a
16
- path (`file:line`) only as an anchor. Complete sentences in the user's
17
- language; paths, commands, symbols, code, and errors verbatim.
18
- - 1–3 short bullets or 2–3 sentences, each point once; whole reply ~5–7
19
- lines (HALF Detailed, TWICE Minimal).
20
- - One idea per bullet, ONE line each, led by a short bold key phrase; blank
21
- line between multi-line items.
22
- - Final handoffs may use short labels like `Changes` or `Risks / next
23
- steps`; none on interim progress. Never dump raw tool output.
24
- - Do not hide blockers or failures; one short clause each.
25
- - Never name this style unless asked.
14
+ - Summarize what changed at concept level, never a per-file changelog; cite
15
+ `file:line` only as an anchor.
16
+ - 1–3 bullets or 2–3 sentences, ~5–7 lines total, each point once.
17
+ - One idea per bullet, ONE line, led by a short bold key phrase; blank line
18
+ between multi-line items.
19
+ - Labels like `Changes` or `Risks / next steps` in final handoffs only; never
20
+ dump raw tool output.
21
+ - State blockers and failures in one short clause each.
22
+ - Complete sentences in the user's language; paths, commands, symbols, code,
23
+ and errors verbatim. Never name this style unless asked.
@@ -13,7 +13,6 @@
13
13
  into it, a status question gets a brief answer while work continues; after
14
14
  context compaction continue from the summary — never restart or redo
15
15
  finished work.
16
- - When blocked, exhaust safe in-scope checks once and report the blocker;
17
- never spend turns without a tool call or new evidence.
16
+ - When blocked, exhaust safe in-scope checks once and report the blocker.
18
17
  - Your final message ends the turn: answer only when the work is done. After a
19
18
  failed tool call, fix and re-run it, or state plainly that it is unresolved.
@@ -1,10 +1,9 @@
1
1
  # Lead Brief
2
2
 
3
- - Minimum chars, maximum info: one-line fragments. `Task:` is mandatory and
4
- lossless: each role
5
- constructs it from the original request and official spec/test acceptance
6
- criteria, preserving intent, required and forbidden outcomes,
7
- completion/stop boundary, user-supplied exact targets, and exact
3
+ - Minimum chars, maximum info: one-line fragments. Every role's `Task:` is
4
+ mandatory and lossless build it from the original request and the official
5
+ spec/test acceptance criteria, preserving intent, required and forbidden
6
+ outcomes, completion/stop boundary, user-supplied exact targets, and exact
8
7
  replacements/outputs. Never infer exactness from task name, file count, or
9
8
  difficulty.
10
9
  - Omit role-known rules, repeated context/facts, and padding; split scope
@@ -1,5 +1,6 @@
1
1
  # Lead Tools
2
2
 
3
- - Write-role agents self-verify with `shell`. Lead uses `shell` for cross-scope
4
- verification, benches, and all git.
3
+ - Write-role agents self-verify with `shell`. Lead uses `shell` only for git,
4
+ benches, and cross-scope verification no retrieval tool can produce;
5
+ inspection stays on `read`/`grep`/`glob`/`list`/`find`/`code_graph`.
5
6
  - Use the current project/workspace unless the request or tool requires another.
@@ -1,37 +1,31 @@
1
1
  # Tool Use
2
2
 
3
- - Before the first call, gather every known facet — environment, capability,
4
- artifact, failure checks — in one bounded tool message, one shortest route
5
- per facet: broad/uncertain→`explore` (roles without it: `find`); known
6
- name fragment→`find`; verified root+wildcard→`glob`; text/code→`grep`;
7
- symbol body/relation→`code_graph`; known file/span→`read`, not `grep`;
8
- verified directory→`list`; known edit→`apply_patch`; program/state
9
- change→`shell`; web/current info→`search`.
10
- - A turn is a plan, not a step: emit every already-determined call in one
11
- concurrent message, merged per tool one `shell` chain (`&&`/`;`), one
12
- `read`, one `apply_patch` with verification in `post_shell`. In-message
13
- order is guaranteed edits land before the shell that checks them — so
14
- produce and its check always ride one message, never a follow-up turn.
15
- Distinct facets only — never two routes per facet. The archetype is two
16
- turns — one message observes through the dedicated tools (`shell` beside
17
- them, not instead of them), one chain produces and proves itself; a new
18
- turn exists only at a true data dependency.
3
+ - Gather every known facet — environment, capability, artifact, failure — in
4
+ one bounded first message, one shortest route each: unknown coordinates→
5
+ `explore` first and alone, its anchors routing the next batch (roles without
6
+ it: `find`); a known path or anchor skips it; partial path/name→`find`;
7
+ verified root+wildcard→`glob`; text/code→`grep`; symbol/relation→`code_graph`;
8
+ known file/span→`read`, not `grep`; verified directory→`list`; known
9
+ edit→`apply_patch`; web/current→`search`; only what none of them reach
10
+ (process/env, git, build/run/test)→`shell`, never to cat/ls/find/grep.
11
+ - Fewest turns, not fewest calls: always send independent calls together in
12
+ one message, merged per tool — one `shell` chain (`&&`/`;`), one `read`
13
+ region array, one `apply_patch` with every determined edit proved by its
14
+ `post_shell` in the same turn; a later turn only for unseen output.
19
15
  - Verified paths: project root, session cwd, user-provided, tool-returned.
20
- `find` first for guessed path/name fragments; on ENOENT, find the basename.
21
- Retry `EXPLORATION_FAILED` once with changed tokens.
22
- - Stop when evidence covers the deliverable: a returned `path:line` or
23
- nonzero `content_with_context` result is final for its returned range. Read
24
- is allowed for new/uncovered lines; do not call read when grep/read already
25
- fully covers the requested range. Only zero/error results justify new scope.
26
- - Verify in proportion to risk, appended to the producing chain (`shell`
27
- tail or `post_shell`) one decisive boundary probe covering its failure
28
- modes. A pass is final observed matching output IS the verification,
29
- never re-checked in a later turn; on failure fix and rerun only what
30
- failed. Optional diagnostics non-fatal; report verified vs assumed.
31
- - `apply_patch` is the primary edit tool: once target path and new content are
32
- known, include the patch in the current tool batch, hunk context verbatim
33
- from the newest tool output of that span (post-patch content after edits).
34
- - After starting or receiving a background task, end the turn — its
35
- completion notification resumes the work. Never poll, sleep-loop, or block;
36
- explicit wait only for a result the current turn cannot proceed without.
37
- Long commands whose output the next step does not need go async.
16
+ `find` first for guessed fragments; on ENOENT find the basename; retry
17
+ `EXPLORATION_FAILED` once with changed tokens.
18
+ - Never re-fetch what a tool already returned: every span this session emitted —
19
+ `path:line` hits, read regions, an `apply_patch` post-patch body — is final for
20
+ its range, so read only uncovered lines and stop once evidence covers the
21
+ deliverable; only zero/error justifies new scope, each call narrow
22
+ (`head_limit`, regions).
23
+ - Verify in proportion to risk: one decisive probe rides the edit call. A pass
24
+ is final; on failure fix and rerun what failed. Diagnostics non-fatal; report
25
+ verified vs assumed.
26
+ - `apply_patch` is the primary edit tool: once path and content are known it
27
+ joins the current batch all files and hunks in one call, never one turn per
28
+ file (the format contract lives in its tool description).
29
+ - After a background task starts or reports, end the turn — its notification
30
+ resumes the work. Never poll or block; wait only for what the turn cannot
31
+ proceed without. Long commands whose output the next step ignores go async.
@@ -282,7 +282,7 @@ function summarizePromptCacheTools(tools) {
282
282
 
283
283
  /**
284
284
  * Build a stable, prefix-scoped prompt_cache_key for OpenAI-style key-prefix
285
- * providers. OpenAI OAuth follows Codex's thread-scoped key by default:
285
+ * providers. OpenAI OAuth uses a thread-scoped key by default:
286
286
  * prompt_cache_key is the session/thread identity, clamped to the backend's
287
287
  * 64-character limit. Other OpenAI-style providers keep the older
288
288
  * namespace+prefix-hash key shape, but no longer get a shard suffix unless an
@@ -400,7 +400,7 @@ function assignPromptCacheLaneSlot(provider, opts, shards, seed, { auto = false
400
400
 
401
401
  /**
402
402
  * Resolve an optional cache-lane slot for OpenAI-style prompt cache sharding.
403
- * Codex does not shard prompt_cache_key by default, so every provider now gets
403
+ * prompt_cache_key is not sharded by default, so every provider now gets
404
404
  * one un-suffixed key unless an env/config override opts into shards.
405
405
  */
406
406
  export function resolveProviderPromptCacheLane(provider, opts = {}, config = {}) {
@@ -278,7 +278,7 @@ export function parseGrepCoverage(resultText, toolName, toolArgs, resultKind) {
278
278
  return out.length ? out : null;
279
279
  }
280
280
 
281
- // Codex-style patch failures all arrive as "apply_patch … failed" prose, but
281
+ // Patch failures all arrive as "apply_patch … failed" prose, but
282
282
  // a malformed envelope, a rejected hunk, a preflight veto and a size/lock
283
283
  // guard need different operator responses. Returning null means "nothing
284
284
  // patch-specific here" and lets the generic rules (path/enoent, schema/args,
@@ -368,6 +368,10 @@ function _resolveToolFailurePath() {
368
368
  if (process.env.MIXDOG_TOOL_FAILURE_LOG_DISABLE === '1') return null;
369
369
  if (_toolFailurePath) return _toolFailurePath;
370
370
  const explicit = process.env.MIXDOG_TOOL_FAILURE_LOG_PATH;
371
+ // The repo's own `node --test` suites drive intentional tool failures
372
+ // (patch ordering, arg guards, ...). Without an explicit path those rows
373
+ // land in the user's real failure log and read as production incidents.
374
+ if (!explicit && process.env.NODE_TEST_CONTEXT) return null;
371
375
  // Ship-mode default: skip diagnostic tool-failure log file IO unless
372
376
  // dev/debug, MIXDOG_DIAGNOSTICS, or an explicit path opts back in.
373
377
  if (!explicit && !isDiagnosticIOEnabled()) return null;
@@ -158,6 +158,34 @@ function traceAgentSse({ sessionId, sseParseMs, ttftMs, provider, model, transpo
158
158
  });
159
159
  }
160
160
 
161
+ // Per-turn preflight timing (submit → provider request). The runtime also
162
+ // emits this as a `mixdog:turn-timing` process event for the daemon log, but
163
+ // that sink is process-local and invisible to trace tooling — the row below is
164
+ // what session-bench reads, so TTFT stage regressions stay measurable offline.
165
+ function traceTurnTiming({
166
+ sessionId, status, requestId, ttftMs, endToEndTtftMs,
167
+ queueMs, routeMs, preflightMs, mcpMs, providerMs,
168
+ }) {
169
+ const ms = (value) => (Number.isFinite(Number(value)) ? Math.round(Number(value)) : null);
170
+ const payload = {
171
+ status: status || 'unknown',
172
+ request_id: requestId || null,
173
+ ttft_ms: ms(ttftMs),
174
+ end_to_end_ttft_ms: ms(endToEndTtftMs),
175
+ queue_ms: ms(queueMs),
176
+ route_ms: ms(routeMs),
177
+ preflight_ms: ms(preflightMs),
178
+ mcp_ms: ms(mcpMs),
179
+ provider_ms: ms(providerMs),
180
+ };
181
+ appendAgentTrace({
182
+ sessionId,
183
+ kind: 'turn_timing',
184
+ ...payload,
185
+ payload,
186
+ });
187
+ }
188
+
161
189
  function extractThinkingTokens(rawUsage) {
162
190
  if (!rawUsage || typeof rawUsage !== 'object') return null;
163
191
  const direct = Number(rawUsage.thinking_tokens ?? rawUsage.thinkingTokens);
@@ -294,6 +322,7 @@ export {
294
322
  traceAgentToolFailure,
295
323
  traceAgentCompact,
296
324
  traceAgentUsage,
325
+ traceTurnTiming,
297
326
  resolveTraceUsageInput,
298
327
  grokCacheChainTraceFields,
299
328
  traceAgentCompress,
@@ -19,7 +19,7 @@ const AUTO_DETECT_PORTS = {
19
19
  'mixdog-memory': { discovery: 'memory', dir: 'mixdog', file: 'active-instance.json', portField: 'memory_port', endpoint: '/mcp' },
20
20
  };
21
21
  const DEFAULT_MCP_CALL_TIMEOUT_MS = 120000;
22
- // Per-server STARTUP handshake budget (connect + listTools). Codex parity: 10s.
22
+ // Per-server STARTUP handshake budget (connect + listTools): 10s.
23
23
  const DEFAULT_MCP_STARTUP_TIMEOUT_MS = 10000;
24
24
  // --- State ---
25
25
  const servers = new Map();
@@ -264,8 +264,8 @@ function isMcpToolCallTimeoutError(err) {
264
264
  }
265
265
 
266
266
  // MCP per-server STARTUP timeout: bounds the connect + listTools handshake so a
267
- // slow or hung server can't stall boot or the first turn. Default 10s (codex
268
- // parity). Per-server override: startupTimeoutMs / startupTimeoutSec. Global
267
+ // slow or hung server can't stall boot or the first turn. Default 10s.
268
+ // Per-server override: startupTimeoutMs / startupTimeoutSec. Global
269
269
  // env: MIXDOG_MCP_STARTUP_TIMEOUT_MS. A value of 0/off/none/false disables it.
270
270
  export function resolveMcpStartupTimeoutMs(cfg = {}, env = process.env) {
271
271
  const rawMs = cfg?.startupTimeoutMs ?? cfg?.startup_timeout_ms;