mixdog 0.9.92 → 0.9.93

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 (61) hide show
  1. package/README.md +147 -51
  2. package/package.json +6 -5
  3. package/scripts/code-graph-description-contract.mjs +6 -8
  4. package/scripts/tmp-cdp-errors.mjs +41 -0
  5. package/scripts/tmp-cdp-inspect.mjs +41 -0
  6. package/scripts/tui-transcript-jitter-harness.mjs +2 -18
  7. package/src/rules/agent/00-core.md +1 -2
  8. package/src/rules/lead/01-general.md +1 -0
  9. package/src/rules/shared/01-tool.md +19 -22
  10. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +16 -3
  11. package/src/runtime/agent/orchestrator/agent-trace.mjs +17 -0
  12. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  13. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +9 -1
  14. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +79 -21
  15. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -19
  16. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +18 -1
  17. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +44 -8
  18. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +3 -0
  19. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
  20. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +22 -5
  21. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +35 -29
  22. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +11 -2
  23. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +5 -6
  24. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +31 -2
  25. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +60 -0
  26. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +60 -31
  27. package/src/runtime/agent/orchestrator/session/manager.mjs +1 -1
  28. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +12 -3
  29. package/src/runtime/agent/orchestrator/session/store/listing.mjs +17 -0
  30. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +101 -0
  31. package/src/runtime/agent/orchestrator/session/store.mjs +30 -0
  32. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +19 -23
  33. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -21
  34. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  35. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +12 -6
  36. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -3
  37. package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +47 -14
  38. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +100 -0
  39. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +6 -5
  40. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -0
  41. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +32 -2
  42. package/src/runtime/channels/backends/discord-gateway.mjs +6 -32
  43. package/src/runtime/channels/lib/inbound-handler.mjs +19 -3
  44. package/src/runtime/channels/lib/scheduler.mjs +51 -3
  45. package/src/runtime/channels/lib/worker-main.mjs +4 -0
  46. package/src/runtime/channels/tool-defs.mjs +4 -2
  47. package/src/runtime/memory/lib/query-handlers.mjs +11 -3
  48. package/src/runtime/memory/tool-defs.mjs +5 -5
  49. package/src/runtime/shared/channel-notification-routing.mjs +8 -2
  50. package/src/runtime/shared/llm/http-agent.mjs +11 -0
  51. package/src/session-runtime/lifecycle-api.mjs +26 -1
  52. package/src/session-runtime/tool-catalog-data.mjs +5 -2
  53. package/src/session-runtime/workflow.mjs +7 -5
  54. package/src/standalone/agent-tool/tag-registry.mjs +5 -1
  55. package/src/standalone/explore-tool.mjs +1 -1
  56. package/src/tui/dist/index.mjs +68 -45
  57. package/src/tui/engine/agent-envelope.mjs +52 -3
  58. package/src/tui/engine/turn.mjs +8 -9
  59. package/src/tui/engine.mjs +18 -41
  60. package/src/workflows/solo/WORKFLOW.md +0 -6
  61. package/src/workflows/solo-bench/WORKFLOW.md +17 -0
package/README.md CHANGED
@@ -4,14 +4,17 @@
4
4
  ![node](https://img.shields.io/badge/node-%3E%3D22-brightgreen)
5
5
  ![license](https://img.shields.io/badge/license-MIT-blue)
6
6
 
7
- Standalone coding-agent CLI/TUI that runs an orchestrated, multi-provider
8
- agent workflow from one terminal — built for maximum performance at minimum
9
- cost.
7
+ Standalone coding agent that runs an orchestrated, multi-provider agent
8
+ workflow from one terminal — or from a full desktop workbench — built to
9
+ get the same quality out of the same models with less time, cost, and
10
+ context.
10
11
 
11
12
  Mixdog combines an Ink-based terminal UI, per-role model routing across
12
13
  providers, workflow agents, MCP/plugin/skill/hook support, lightweight
13
14
  memory, web search, channel integrations, and repo-native tools for reading,
14
- editing, testing, and reviewing code.
15
+ editing, testing, and reviewing code. Mixdog Desktop wraps the same runtime
16
+ in an Electron workbench with editor, git, terminal, file-explorer, media,
17
+ and automation surfaces.
15
18
 
16
19
  ## Quick start
17
20
 
@@ -25,34 +28,59 @@ mixdog
25
28
  First run walks you through onboarding: provider auth, model pick, and
26
29
  workflow setup.
27
30
 
28
- ## Terminal-Bench 2.1 — 89.9% (self-reported)
31
+ ## Terminal-Bench 2.1 — controlled full-run comparisons
29
32
 
30
- ![Terminal-Bench 2.1 leaderboard with mixdog](https://raw.githubusercontent.com/tribgames/mixdog/main/benchmarks/terminal-bench-2.1/tb21-leaderboard.svg)
33
+ **Same model, same quality — in a fraction of the time, context, and
34
+ cost.** On the same 89 tasks, mixdog scored on par with both native
35
+ harnesses — **78/89** vs Claude Code's **77/89** (within single-run noise)
36
+ and **75/89** matching Codex CLI — while finishing faster, ending leaner,
37
+ and costing less.
31
38
 
32
- Single-run score of **80/89 = 89.9%** (k=1) on
33
- `terminal-bench/terminal-bench-2-1`, using a cost-reduced per-role routing
34
- config:
39
+ Each comparison matches the primary model and reasoning level on
40
+ both sides, comparing each product as shipped: mixdog routes scoped
41
+ read-only Explorer lookups to a smaller model, mirroring Claude Code's
42
+ built-in Explore subagent (Haiku 4.5 by default in the 2.1.x baseline).
43
+ Codex CLI ships no equivalent helper — the Sol-led mixdog run used GPT-5.6
44
+ Luna for that scoped Explorer work. Results are self-reported single runs
45
+ (`k=1`, 2026-08), not leaderboard submissions.
35
46
 
36
- | Role | Model | Effort |
37
- |---------------------|-----------------|-------------|
38
- | Lead (orchestrator) | Claude Fable 5 | high |
39
- | Explorer | Claude Haiku 4.5| default |
40
- | Worker | Claude Opus 4.8 | medium |
41
- | Heavy worker | Claude Opus 4.8 | high |
42
- | Reviewer | GPT-5.5 | high (fast) |
47
+ #### Claude Opus 5 vs Claude Code
43
48
 
44
- Against published model scores this places second overall behind GPT-5.6
45
- Sol Ultra (91.9%), ahead of GPT-5.6 Sol (88.8%) and Claude Mythos 5 (88%),
46
- and well above the same primary model run standalone (Claude Fable 5,
47
- 84.3%). Measured once (k=1) due to cost, so treat it as indicative — a
48
- max-effort k=5 run is planned and is expected to land higher. Per-task
49
- results, raw Harbor jobs, and the harness adapter live in
50
- `benchmarks/terminal-bench-2.1/`.
49
+ ![Terminal-Bench 2.1 comparison of mixdog with Claude Opus 5 and Claude Code](https://raw.githubusercontent.com/tribgames/mixdog/main/benchmarks/terminal-bench-2.1/tb21-opus-vs-claude-code.svg)
51
50
 
52
- Full transparency: 4 tasks refused by the primary model's safety layer were
53
- re-run routed to Claude Opus 4.8 (effort xhigh) and passed; timeouts and
54
- task resources were left unmodified. Raw Harbor `result.json`/`config.json`
55
- for every constituent run are included.
51
+ #### GPT-5.6 Sol xhigh vs Codex CLI
52
+
53
+ ![Terminal-Bench 2.1 comparison of mixdog with GPT-5.6 Sol xhigh and Codex CLI](https://raw.githubusercontent.com/tribgames/mixdog/main/benchmarks/terminal-bench-2.1/tb21-sol-vs-codex.svg)
54
+
55
+ - Speed: **1.43×** vs Claude Code, **1.27×** vs Codex CLI
56
+ (baseline elapsed agent time ÷ mixdog elapsed agent time)
57
+ - Final context: **40–47% smaller** at task end (median tokens, measured
58
+ from both harnesses' session logs)
59
+ - Priced cost: **29% lower** vs Claude Code, **at least 39.7% lower** vs
60
+ Codex CLI (mixdog $54.50–$58.84 vs Codex's recorded $97.54)
61
+
62
+ Both sides run their standard single-agent loop. Anthropic cost includes
63
+ measured cache writes; the archived OpenAI runs did not retain
64
+ `cache_write_tokens`, so the Codex cost delta is a lower bound. Raw
65
+ artifacts, the exact run commands, and the metric scripts that recompute
66
+ every number above live under `benchmarks/terminal-bench-2.1/`.
67
+
68
+ ## Boot overhead — measured, not estimated
69
+
70
+ Fixed cost of the default system prompt + tool schemas, measured with a
71
+ minimal one-turn ping ("hi"). Every number below is what the provider's own
72
+ usage accounting reported for the first API call — same tokenizer on both
73
+ sides of each row, no offline estimates (2026-08).
74
+
75
+ | Tokenizer | Baseline harness | First-call prompt | mixdog Lead | mixdog worker agent |
76
+ |-----------|------------------|------------------:|------------:|--------------------:|
77
+ | Anthropic — pristine containers, TB2.1 harness | Claude Code 2.1.220 | 19,149 tok (median, n=89) | **7,295 (−62%)** | 4,856 (−75%) |
78
+ | OpenAI — same host, same model (`gpt-5.6-sol`) | Codex CLI 0.145.0 | 17,706 tok | **7,488 (−58%)** | 4,616 (−74%) |
79
+
80
+ The Lead surface carries the full default rule set, skills manifest, and
81
+ per-user profile; spawning a sub-agent costs a flat ~4.6–4.9k tokens. Low
82
+ boot overhead compounds: it is re-read on every request, so it is the
83
+ baseline of every cache write, compaction, and long-session turn.
56
84
 
57
85
  ## Why mixdog
58
86
 
@@ -68,9 +96,10 @@ for every constituent run are included.
68
96
 
69
97
  **Any provider**
70
98
 
99
+ - Sign in with the subscriptions you already pay for: OAuth device flows for
100
+ Claude and ChatGPT/Codex accounts work alongside plain API keys.
71
101
  - Anthropic, OpenAI, Google/Gemini, xAI/Grok, DeepSeek, OpenCode Go,
72
- OAuth-backed providers, OpenAI-compatible APIs, Ollama, and LM Studio/local
73
- endpoints.
102
+ OpenAI-compatible APIs, Ollama, and LM Studio/local endpoints.
74
103
  - Live model catalog from provider `/models` endpoints, enriched with
75
104
  LiteLLM/models.dev metadata for context windows, output limits, pricing,
76
105
  tool support, reasoning, and recency.
@@ -81,20 +110,34 @@ for every constituent run are included.
81
110
  - Full-screen TUI with slash commands, provider setup, model/workflow
82
111
  pickers, statusline integration, and detailed tool cards — plus headless
83
112
  role mode for scripting.
84
- - Optional Discord/Telegram channels, webhook endpoints, cron schedules, and
85
- voice-message transcription for remote/event-driven workflows.
113
+ - Mixdog Desktop: a full agent workbench for Windows/macOS/Linux (see
114
+ below).
115
+ - Web/mobile companion over relay pairing — scan a QR code to open your
116
+ running sessions in a phone browser and keep going from any network.
117
+ - Optional Discord/Telegram channels, webhook endpoints, and cron schedules
118
+ with quiet hours for remote/event-driven workflows; channel voice messages
119
+ are transcribed locally with a managed Whisper server.
120
+ - First-class Windows support: ConPTY terminals, PowerShell-aware shell
121
+ profiles, and a one-click desktop installer.
86
122
 
87
123
  **Memory**
88
124
 
89
- - Lightweight memory restores prior work context across sessions.
90
- - Important memories are automatically promoted and demoted when stale.
125
+ - Every session is ingested into a local memory store in the background, so
126
+ prior work, decisions, and fixes stay recallable across sessions via the
127
+ `recall` tool and `/memory`.
128
+ - Semantic + lexical recall with local embeddings, time-window queries, and
129
+ project-scoped pools — multilingual, including Korean morphology.
130
+ - A multi-pass consolidation cycle promotes important memories into a
131
+ compact core set — and demotes them when stale — so memory stays small
132
+ and current instead of growing without bound.
91
133
 
92
134
  **Agent-ecosystem compatible**
93
135
 
94
136
  - Skills, MCP servers, hooks, and plugins load through standard-compatible
95
137
  interfaces.
96
138
  - Workflow delegation through the `agent` tool and `/agents`: worker,
97
- heavy-worker, reviewer, debugger, maintainer, and explorer roles.
139
+ heavy-worker, reviewer, debugger, maintainer, explorer, and
140
+ web-researcher roles.
98
141
 
99
142
  ## Run
100
143
 
@@ -112,20 +155,31 @@ mixdog
112
155
  # Start with an explicit route
113
156
  mixdog --provider anthropic-oauth --model claude-haiku-4-5-20251001
114
157
 
158
+ # Start with a specific workflow active
159
+ mixdog --workflow solo
160
+
115
161
  # Read-only tool surface
116
162
  mixdog --readonly
117
163
 
118
164
  # Enable remote/channel mode for this session
119
165
  mixdog --remote
166
+
167
+ # Re-run the first-run setup wizard
168
+ mixdog --onboarding
120
169
  ```
121
170
 
122
- Headless role mode is also supported:
171
+ Headless role mode is also supported. It requires an explicit
172
+ provider/model pair and runs with ephemeral config — host behavioral config
173
+ and personal state are not loaded:
123
174
 
124
175
  ```bash
125
- mixdog worker "fix the failing test"
126
- mixdog reviewer "review the current diff"
176
+ mixdog --provider anthropic-oauth --model claude-opus-5 worker "fix the failing test"
177
+ mixdog --provider openai-oauth --model gpt-5.6-sol reviewer "review the current diff"
127
178
  ```
128
179
 
180
+ Roles: `explore`, `worker`, `heavy-worker`, `reviewer`, `debugger`,
181
+ `maintainer`, `web-researcher`.
182
+
129
183
  ## TUI basics
130
184
 
131
185
  Common slash commands:
@@ -135,15 +189,26 @@ Common slash commands:
135
189
  /model choose the main provider/model (/effort, /fast tune it)
136
190
  /workflow choose the active workflow
137
191
  /agents show workflow agents and per-agent model overrides
192
+ /project switch working directory (project)
193
+ /resume resume a saved chat
194
+ /usage show total provider quota / balance
195
+ /context show the current context surface
196
+ /memory list and edit core memories
138
197
  /setting open the runtime settings hub
139
198
  /mcp manage MCP servers and tools
140
199
  /skills choose a skill for the next request
141
200
  /channels manage Discord, Telegram, and voice
142
201
  /compact compact older conversation context
202
+ /autoclear reduce cache-miss cost after long idle gaps
203
+ /theme change the TUI color theme
143
204
  /clear reset the conversation and screen
144
205
  /OutputStyle show or switch Lead output style
206
+ /update check version and update mixdog
207
+ /doctor diagnose installation health
145
208
  ```
146
209
 
210
+ Run `mixdog --help` for the full command and option reference.
211
+
147
212
  Use `/providers` first if no model is configured, then `/model` to pick the
148
213
  route. The model picker warms the provider catalog in the background and keeps
149
214
  Claude families such as Opus, Sonnet, Haiku, and Fable separate when filtering
@@ -155,6 +220,44 @@ directory (`workflows/<id>/`, `agents/<id>/`) and are edited on the desktop
155
220
  app's Workflows page. Schedules and webhooks are also managed in the desktop
156
221
  app.
157
222
 
223
+ ## Desktop app
224
+
225
+ Mixdog Desktop (Electron) runs the same runtime as the CLI inside a full
226
+ agent workbench. Installers are published on GitHub Releases (Windows
227
+ one-click NSIS, macOS dmg/zip, Linux AppImage), and a guided onboarding
228
+ wizard covers first-run setup. For development run `npm run dev` inside
229
+ `apps/desktop`.
230
+
231
+ - **Workbench shell** — VS Code-style activity rail and tab strip,
232
+ drag-and-drop tabs across pane groups, and unlimited splits that run
233
+ parallel agent sessions side by side — every pane hosts a live session
234
+ surface with its own draft and model controls — plus a command surface,
235
+ bottom panel, and problems view.
236
+ - **Sessions and projects** — project-scoped session lists, resumable
237
+ sessions with per-pane route restore, live agent-activity indicators, and
238
+ usage dashboards in the sidebar.
239
+ - **Editor and review** — Monaco editor pane with LSP integration, git and
240
+ inline diff viewers, and turn-by-turn review of agent edits with approval
241
+ cards.
242
+ - **Source control** — git dock for staging/commits/branches,
243
+ auto-generated commit messages, GitHub CLI integration, pull-request
244
+ browsing, and a dedicated review pane.
245
+ - **File explorer** — Windows-Explorer-grade folder pane: breadcrumbs and
246
+ path box, ribbon toolbar, places/drives/tree sidebar, grouped grid and
247
+ details views with shell icons and thumbnails, rubber-band selection,
248
+ clipboard and OS drag-and-drop, preview pane, and file properties.
249
+ - **Terminal** — integrated terminal tabs on the native shell (ConPTY on
250
+ Windows) with shell-profile detection, isolated in a worker process so a
251
+ runaway shell never takes the app down.
252
+ - **Studio** — media studio for image and video generation over
253
+ authenticated provider lanes, with a persistent local gallery, reference
254
+ images, and per-model resolution/aspect/duration controls.
255
+ - **Automation** — visual editors for workflow and agent packs, cron
256
+ schedules, webhooks, and channel integrations.
257
+ - **Settings hub** — provider auth, capability sweep, git identity, and
258
+ QR device pairing for the web/mobile companion, preloaded so every
259
+ category opens instantly.
260
+
158
261
  ## Scripts
159
262
 
160
263
  ```bash
@@ -206,6 +309,10 @@ src/
206
309
  agents/ # workflow agent definitions
207
310
  workflows/ # workflow definitions
208
311
  rules/ # Lead and agent instructions
312
+ apps/
313
+ desktop/ # Mixdog Desktop — Electron workbench (main/preload/renderer)
314
+ mobile/ # mobile companion shell
315
+ relay/ # relay server for remote/web/mobile access
209
316
  scripts/
210
317
  smoke*.mjs # smoke checks
211
318
  *test.mjs # focused node:test checks
@@ -216,20 +323,9 @@ vendor/
216
323
 
217
324
  ## Published package contents
218
325
 
219
- The npm package is limited by `package.json#files` to:
220
-
221
- ```text
222
- README.md
223
- scripts/
224
- src/
225
- vendor/
226
- ```
227
-
228
- Test, smoke, and bench scripts are excluded from the tarball; they live in the
229
- repository only.
230
-
231
- `docs/` is not included in the published package unless `package.json#files` is
232
- changed.
326
+ The npm tarball ships `README.md`, `src/`, `vendor/`, and runtime `scripts/`
327
+ only (`package.json#files`); tests, smokes, benches, and `docs/` stay in the
328
+ repository.
233
329
 
234
330
  ## License
235
331
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixdog",
3
- "version": "0.9.92",
3
+ "version": "0.9.93",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone mixdog coding-agent CLI/TUI workspace.",
@@ -66,6 +66,7 @@
66
66
  "test:agent-job-views": "node --test scripts/agent-job-terminal-view-test.mjs",
67
67
  "test:agent-fanout": "node scripts/agent-parallel-smoke.mjs && node --test scripts/agent-route-batch-test.mjs scripts/execution-completion-dedup-test.mjs",
68
68
  "test:toolcall": "node --test scripts/toolcall-args-test.mjs",
69
+ "test:tool-batching": "node --test scripts/eager-patch-shell-order-test.mjs scripts/session-bench-batching-test.mjs",
69
70
  "test:shipmode": "node --test scripts/ship-mode-test.mjs",
70
71
  "test:shellhardening": "node --test scripts/shell-hardening-test.mjs scripts/shell-failure-diagnostics-test.mjs scripts/windows-hide-spawn-options-test.mjs",
71
72
  "test:placeholder": "node --test scripts/compacted-placeholder-scrub-test.mjs",
@@ -85,10 +86,10 @@
85
86
  "test:code-graph-clean-cache": "node --test scripts/code-graph-dispatch-test.mjs",
86
87
  "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",
87
88
  "test:tui-input-render": "node --test scripts/prompt-immediate-render-test.mjs",
88
- "test:tui-streaming-window": "node --test scripts/streaming-tail-window-test.mjs",
89
+ "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",
89
90
  "test:tui-ambiguous-width": "node --test scripts/tui-ambiguous-width-test.mjs",
90
91
  "test:release-assets": "node --check scripts/verify-release-assets.mjs && node --check scripts/verify-release-assets-test.mjs && node --check scripts/deploy-workflow-test.mjs && node --test scripts/verify-release-assets-test.mjs scripts/deploy-workflow-test.mjs",
91
- "test:release-focused": "npm run test:release-assets && npm run test:tool-contracts && 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",
92
+ "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",
92
93
  "test:native-edit-wire": "node --test scripts/native-edit-wire-test.mjs",
93
94
  "test:patch-binary-cache": "node --test scripts/patch-binary-cache-test.mjs",
94
95
  "test:patch-parity": "node --test scripts/v4a-codex-parity-test.mjs",
@@ -150,10 +151,10 @@
150
151
  },
151
152
  "overrides": {
152
153
  "discord.js": {
153
- "undici": "6.27.0"
154
+ "undici": "6.28.0"
154
155
  },
155
156
  "@discordjs/rest": {
156
- "undici": "6.27.0"
157
+ "undici": "6.28.0"
157
158
  },
158
159
  "onnxruntime-node": {
159
160
  "global-agent": "^4.1.3"
@@ -85,7 +85,7 @@ const CODE_GRAPH_DESCRIPTION_MUTATION_CORPUS = [
85
85
  allPositiveProbes: true,
86
86
  mutate: (parts) => ({
87
87
  ...parts,
88
- description: parts.description.replace(/keywords\s+(?:use|route through|select)/i, "keywords won't use"),
88
+ description: parts.description.replace(/keywords\s+(?:use|route through|select|via)/i, "keywords won't use"),
89
89
  }),
90
90
  },
91
91
  {
@@ -101,7 +101,7 @@ const CODE_GRAPH_DESCRIPTION_MUTATION_CORPUS = [
101
101
  allPositiveProbes: true,
102
102
  mutate: (parts) => ({
103
103
  ...parts,
104
- symbolsDescription: parts.symbolsDescription.replace(/one symbols\[\] call/i, 'one files[] call'),
104
+ symbolsDescription: parts.symbolsDescription.replace(/one symbols\[\] call|one symbols\[\]/i, 'one files[] call'),
105
105
  }),
106
106
  },
107
107
  {
@@ -143,18 +143,16 @@ const CODE_GRAPH_DESCRIPTION_MUTATION_CORPUS = [
143
143
  function hasCodeGraphDescriptionContract({ description, modeDescription, symbolsDescription }) {
144
144
  return (
145
145
  hasPositiveClause(description, ['file modes', 'files[]'])
146
- && hasModeClause(description, 'symbol modes', SYMBOL_MODES, 'symbols[]')
146
+ && hasPositiveClause(description, ['symbol modes', 'symbols[]'])
147
147
  && hasPositiveClause(description, ['exact identifiers', ...EXACT_MODES])
148
148
  && hasPositiveClause(description, ['keywords', ...KEYWORD_MODES])
149
149
  && !hasContradictoryTargetAssignment(description)
150
150
  && hasModeClause(modeDescription, 'file modes', FILE_MODES)
151
151
  && hasPositiveClause(modeDescription, ['symbols with files', 'files[]', 'file outline'])
152
- && hasModeClause(modeDescription, 'symbol modes', SYMBOL_MODES)
153
- && hasPositiveClause(modeDescription, ['fileless symbols', 'symbol_search', 'keywords'])
154
152
  && !hasContradictoryTargetAssignment(modeDescription)
155
- && hasPositiveClause(symbolsDescription, ['exact identifiers', ...EXACT_MODES])
156
- && hasPositiveClause(symbolsDescription, ['keywords', ...KEYWORD_MODES])
157
- && hasPositiveClause(symbolsDescription, ['multiple exact symbols', 'one symbols[] call'])
153
+ && hasPositiveClause(symbolsDescription, ['exact identifiers'])
154
+ && hasPositiveClause(symbolsDescription, ['keywords'])
155
+ && hasPositiveClause(symbolsDescription, ['symbols[]'])
158
156
  && !hasContradictoryTargetAssignment(symbolsDescription)
159
157
  );
160
158
  }
@@ -0,0 +1,41 @@
1
+ import WebSocket from "ws";
2
+
3
+ const list = await fetch("http://127.0.0.1:9342/json/list").then((r) => r.json());
4
+ const page = list.find((t) => t.type === "page");
5
+ const ws = new WebSocket(page.webSocketDebuggerUrl);
6
+ let id = 0; const pending = new Map();
7
+ function send(method, params) {
8
+ return new Promise((res) => { const mid = ++id; pending.set(mid, res); ws.send(JSON.stringify({ id: mid, method, params })); });
9
+ }
10
+ const seen = new Map();
11
+ ws.on("message", (raw) => {
12
+ const msg = JSON.parse(raw);
13
+ if (msg.id && pending.has(msg.id)) { pending.get(msg.id)(msg.result); pending.delete(msg.id); return; }
14
+ if (msg.method === "Runtime.exceptionThrown") {
15
+ const d = msg.params.exceptionDetails;
16
+ const desc = d.exception?.description || d.text || "";
17
+ const key = desc.slice(0, 200);
18
+ seen.set(key, (seen.get(key) || 0) + 1);
19
+ } else if (msg.method === "Runtime.consoleAPICalled" && (msg.params.type === "error" || msg.params.type === "warning")) {
20
+ const text = msg.params.args.map((a) => a.value ?? a.description ?? "").join(" ");
21
+ const key = "[console." + msg.params.type + "] " + text.slice(0, 300);
22
+ seen.set(key, (seen.get(key) || 0) + 1);
23
+ } else if (msg.method === "Log.entryAdded") {
24
+ const e = msg.params.entry;
25
+ if (e.level === "error" || e.level === "warning") {
26
+ const key = "[log." + e.level + "] " + (e.text || "").slice(0, 300) + " @" + (e.url || "");
27
+ seen.set(key, (seen.get(key) || 0) + 1);
28
+ }
29
+ }
30
+ });
31
+ ws.on("open", async () => {
32
+ await send("Runtime.enable", {});
33
+ await send("Log.enable", {});
34
+ setTimeout(() => {
35
+ for (const [key, count] of [...seen.entries()].sort((a, b) => b[1] - a[1])) {
36
+ console.log(`x${count} ${key.replace(/\n/g, " | ")}`);
37
+ }
38
+ if (!seen.size) console.log("(no errors captured in window)");
39
+ process.exit(0);
40
+ }, 12000);
41
+ });
@@ -0,0 +1,41 @@
1
+ import WebSocket from "ws";
2
+
3
+ const list = await fetch("http://127.0.0.1:9342/json/list").then((r) => r.json());
4
+ const page = list.find((t) => t.type === "page");
5
+ if (!page) { console.error("no page target"); process.exit(1); }
6
+ const ws = new WebSocket(page.webSocketDebuggerUrl);
7
+ let id = 0; const pending = new Map();
8
+ function send(method, params) {
9
+ return new Promise((res) => { const mid = ++id; pending.set(mid, res); ws.send(JSON.stringify({ id: mid, method, params })); });
10
+ }
11
+ ws.on("message", (raw) => {
12
+ const msg = JSON.parse(raw);
13
+ if (msg.id && pending.has(msg.id)) { pending.get(msg.id)(msg.result); pending.delete(msg.id); }
14
+ });
15
+ ws.on("open", async () => {
16
+ const expr = `(() => {
17
+ const gate = document.querySelector('.desktop-boot-gate');
18
+ const covers = [...document.querySelectorAll('.pane-surface-cover, .desktop-boot-cover')].map(c => ({
19
+ cls: c.className,
20
+ rect: Math.round(c.getBoundingClientRect().width) + 'x' + Math.round(c.getBoundingClientRect().height),
21
+ parent: (c.parentElement?.className || '').slice(0, 90),
22
+ hasSpinner: !!c.querySelector('.desktop-loading-spinner'),
23
+ }));
24
+ const gates = [...document.querySelectorAll('.pane-surface-gate[data-ready="false"], .stable-surface-switch[data-ready="false"], .stable-content-swap[data-ready="false"]')]
25
+ .map(g => g.className + ' | parent=' + (g.parentElement?.className || '').slice(0, 70));
26
+ return JSON.stringify({
27
+ bootGate: gate ? { ready: gate.dataset.ready, pending: gate.dataset.pending, timeout: gate.dataset.timeout } : null,
28
+ revealed: window.__mixdogDesktopRevealed,
29
+ shown: window.__mixdogWindowShown,
30
+ covers, gates,
31
+ metrics: (window.__mixdogBootMetrics || []).slice(-30),
32
+ recovery: !!document.querySelector('.desktop-recovery-screen'),
33
+ rootKids: document.getElementById('root')?.childElementCount,
34
+ url: location.href,
35
+ }, null, 1);
36
+ })()`;
37
+ const r = await send("Runtime.evaluate", { expression: expr, returnByValue: true });
38
+ console.log(r?.result?.value ?? JSON.stringify(r));
39
+ process.exit(0);
40
+ });
41
+ setTimeout(() => { console.error("timeout"); process.exit(1); }, 8000);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { build } from 'esbuild';
3
- import { readFile, rm } from 'node:fs/promises';
3
+ import { rm } from 'node:fs/promises';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { fileURLToPath, pathToFileURL } from 'node:url';
6
6
 
@@ -18,22 +18,6 @@ const inkAlias = {
18
18
  },
19
19
  };
20
20
 
21
- // Build-only probe: records the exact delta seen by the production helper
22
- // without adding another stateful estimator call from the harness.
23
- const growthProbe = {
24
- name: 'streaming-tail-growth-probe',
25
- setup(ctx) {
26
- ctx.onLoad({ filter: /transcript-window\.mjs$/ }, async (args) => {
27
- let source = (await readFile(args.path, 'utf8')).replace(/\r\n/g, '\n');
28
- const target = ' return { tailRows: idEntry.rows, delta };';
29
- const replacement = ` globalThis.__mixdogTailGrowthProbe = { live, baseline, delta, tailRows: idEntry.rows };\n${target}`;
30
- if (!source.includes(target)) return null;
31
- source = source.replace(target, replacement);
32
- return { contents: source, loader: 'js' };
33
- });
34
- },
35
- };
36
-
37
21
  try {
38
22
  await build({
39
23
  entryPoints: [entry],
@@ -44,7 +28,7 @@ try {
44
28
  target: 'node22',
45
29
  jsx: 'automatic',
46
30
  packages: 'external',
47
- plugins: [inkAlias, growthProbe],
31
+ plugins: [inkAlias],
48
32
  banner: {
49
33
  js: "import { createRequire as __mixdogCreateRequire } from 'node:module';\nconst require = __mixdogCreateRequire(import.meta.url);",
50
34
  },
@@ -1,7 +1,6 @@
1
1
  # Agent Constraints
2
2
 
3
- - Agent communication is English. One turn is one batch; include every
4
- compatible read-only call.
3
+ - Agent communication is English.
5
4
  - Call tools immediately: no preamble/progress; text only in final handoff.
6
5
  - Final handoff is fragments: outcome, key `file:line`, verification
7
6
  command+result, material risk/blocker. Never repeat the brief, process,
@@ -8,6 +8,7 @@
8
8
  validated target paths — never `~`, a root, or unresolved variables/globs;
9
9
  report material deletions with recoverability.
10
10
  - Act proactively; ask only for decisions.
11
+ - Build only what the task requires; trust internal and framework guarantees.
11
12
  - Mid-task input: a replacement supersedes current work, an addition folds
12
13
  into it, a status question gets a brief answer while work continues; after
13
14
  context compaction continue from the summary — never restart or redo
@@ -1,22 +1,18 @@
1
1
  # Tool Use
2
2
 
3
- - Before the first call, gather every known facet in one tool message; one
4
- shortest route per facet: broad/uncertain→`explore` (roles without it:
5
- `find`); partial path/name→`find`; verified root+wildcard→`glob`;
6
- quoted/non-identifier literal or regex→`grep`; exact code identifier/
7
- relation→`code_graph` before grep; known file/span→`read` directly without
8
- `grep`; verified directory→`list`; known edit→`apply_patch` (span already
9
- seen; else `read`/`grep` first); program/state change→`shell`; web/current
10
- external info→`search`.
11
- - Shortest total calls, maximum batching — every turn: all independent calls
12
- in one concurrent message (shell included); combine variants/symbols/
13
- scopes/paths/queries per call; same-file regions as one real
14
- `{path,offset,limit}` array; graph targets as arrays; `explore` facets in
15
- one `query[]` (max 8, no rephrased duplicates); all new edits in one patch.
16
- Distinct facets, not alternative routes; sequential singles only for a
17
- step whose arguments require the previous result — fixed follow-ups
18
- (pinned installs, known writes) go in the same batch; only apply_patch
19
- executes in order.
3
+ - Before the first call, gather every known facet environment, capability,
4
+ artifact, and failure checks in one bounded tool message. Use one shortest
5
+ route per facet: broad/uncertain→`explore` (roles without it: `find`);
6
+ partial path/name→`find`; verified root+wildcard→`glob`;
7
+ quoted/non-identifier literal or regex→`grep`; exact code
8
+ identifier/relation→`code_graph` before grep; known file/span→`read`
9
+ directly without `grep`; verified directory→`list`; known
10
+ edit→`apply_patch`; program/state change→`shell`; web/current external
11
+ info→`search`.
12
+ - Shortest total calls, maximum batching every turn: every determined call
13
+ in one concurrent message (mix `shell` in), merged per tool — one `shell`
14
+ chain, one `read`, one `apply_patch` carrying full verification in
15
+ `post_shell`; a later turn only for steps needing unseen output.
20
16
  - Verified paths: project root, session cwd, user-provided, tool-returned.
21
17
  `find` first for guessed path/name fragments; on ENOENT, find the basename.
22
18
  Retry `EXPLORATION_FAILED` once with changed tokens.
@@ -24,11 +20,12 @@
24
20
  nonzero `content_with_context` result is final — act on it (inspecting it
25
21
  via read/code_graph is valid); only zero/error results justify changed
26
22
  tokens or scope. Don't re-locate, re-verify, or reread returned spans.
27
- - `apply_patch` is the primary edit tool: send the patch as soon as target
28
- path and new content are known. Hunk context comes verbatim from the newest
29
- tool output of that span (`read`/`grep`/your own patch — post-patch content
30
- after edits), never retyped from memory; one look-up beats a failed patch.
31
- A same-turn shell after `apply_patch` runs once the patch lands.
23
+ - Verify changes in proportion to risk with one decisive batched boundary
24
+ probe. A pass is final; on failure, fix and rerun only what failed. Keep
25
+ optional diagnostics non-fatal; report verified vs assumed.
26
+ - `apply_patch` is the primary edit tool: once target path and new content are
27
+ known, include the patch in the current tool batch, hunk context verbatim
28
+ from the newest tool output of that span (post-patch content after edits).
32
29
  - After starting or receiving a background task, end the turn — its
33
30
  completion notification resumes the work. Never poll, sleep-loop, or block;
34
31
  explicit wait only for a result the current turn cannot proceed without.
@@ -120,18 +120,31 @@ export function resolveCacheStrategy(agent, { autoClear } = {}) {
120
120
  if (isOneShotMaintenanceAgent(agent)) {
121
121
  return { tools: 'none', system: 'none', tier3: 'none', messages: 'none' };
122
122
  }
123
+ // Operator override for the BP4 (messages-tail) TTL. Short-lived
124
+ // rapid-turn deployments (bench-style: session dies in <15min) never
125
+ // benefit from a 1h tail across its premium window, so '5m' trades the
126
+ // 2x write premium (1h, $10/M) down to 1.25x ($6.25/M) and deactivates
127
+ // the 1h volatile-content anchor guard. Product defaults below stay
128
+ // untouched when the env is unset.
129
+ const envMessagesTtl = (process.env.MIXDOG_CACHE_MESSAGES_TTL || '').trim();
130
+ const applyEnv = (strategy) => {
131
+ if (envMessagesTtl === '1h' || envMessagesTtl === '5m' || envMessagesTtl === 'none') {
132
+ return { ...strategy, messages: envMessagesTtl };
133
+ }
134
+ return strategy;
135
+ };
123
136
  if (getHiddenAgent(agent)) {
124
- return { tools: 'none', system: '1h', tier3: '1h', messages: '1h' };
137
+ return applyEnv({ tools: 'none', system: '1h', tier3: '1h', messages: '1h' });
125
138
  }
126
139
  if (agent && agent !== 'lead') {
127
140
  // Public (non-hidden, non-lead) agents keep the flat 1h tail — only
128
141
  // the Lead session's tail is linked to autoClear.
129
- return { tools: 'none', system: '1h', tier3: '1h', messages: '1h' };
142
+ return applyEnv({ tools: 'none', system: '1h', tier3: '1h', messages: '1h' });
130
143
  }
131
144
  // Lead session (agent === 'lead', or no agent — raw/CLI callers default
132
145
  // to Lead behavior): message tail TTL is linked to autoClear (see
133
146
  // resolveLeadMessagesTtl).
134
- return { tools: 'none', system: '1h', tier3: '1h', messages: resolveLeadMessagesTtl(autoClear) };
147
+ return applyEnv({ tools: 'none', system: '1h', tier3: '1h', messages: resolveLeadMessagesTtl(autoClear) });
135
148
  }
136
149
 
137
150
  /**