muonroi-cli 1.8.0 → 1.8.1

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 (101) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +122 -122
  3. package/dist/src/agent-harness/mock-model.js +112 -4
  4. package/dist/src/cli/cost-forensics.js +17 -14
  5. package/dist/src/council/prompts.js +20 -20
  6. package/dist/src/flow/compaction/index.d.ts +1 -0
  7. package/dist/src/flow/compaction/index.js +4 -0
  8. package/dist/src/generated/version.d.ts +1 -1
  9. package/dist/src/generated/version.js +1 -1
  10. package/dist/src/index.js +4 -2
  11. package/dist/src/lsp/manager.js +11 -3
  12. package/dist/src/lsp/manager.test.js +39 -0
  13. package/dist/src/mcp/auto-setup.js +0 -8
  14. package/dist/src/mcp/oauth-callback.js +2 -2
  15. package/dist/src/mcp/parse-headers.test.js +14 -14
  16. package/dist/src/mcp/self-verify-jobs.d.ts +1 -0
  17. package/dist/src/mcp/self-verify-runner.js +1 -1
  18. package/dist/src/mcp/setup-guide-text.js +74 -74
  19. package/dist/src/mcp/smoke.test.js +43 -43
  20. package/dist/src/ops/doctor.js +7 -7
  21. package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +1 -0
  22. package/dist/src/orchestrator/__tests__/message-processor.test.js +1 -0
  23. package/dist/src/orchestrator/__tests__/prompts.test.d.ts +1 -0
  24. package/dist/src/orchestrator/__tests__/prompts.test.js +88 -0
  25. package/dist/src/orchestrator/__tests__/sub-session-delegation.test.d.ts +1 -0
  26. package/dist/src/orchestrator/__tests__/sub-session-delegation.test.js +507 -0
  27. package/dist/src/orchestrator/auto-commit.js +9 -7
  28. package/dist/src/orchestrator/message-processor.d.ts +2 -0
  29. package/dist/src/orchestrator/message-processor.js +7 -0
  30. package/dist/src/orchestrator/orchestrator.d.ts +3 -0
  31. package/dist/src/orchestrator/orchestrator.js +362 -28
  32. package/dist/src/orchestrator/pending-calls.js +2 -1
  33. package/dist/src/orchestrator/preprocessor.js +3 -2
  34. package/dist/src/orchestrator/prompts.d.ts +8 -0
  35. package/dist/src/orchestrator/prompts.js +65 -18
  36. package/dist/src/orchestrator/sub-agent-cap.d.ts +2 -0
  37. package/dist/src/orchestrator/sub-agent-cap.js +12 -4
  38. package/dist/src/orchestrator/subagent-compactor.d.ts +7 -0
  39. package/dist/src/orchestrator/subagent-compactor.js +65 -14
  40. package/dist/src/orchestrator/subagent-compactor.spec.js +125 -9
  41. package/dist/src/orchestrator/tool-engine.d.ts +2 -0
  42. package/dist/src/orchestrator/tool-engine.js +80 -21
  43. package/dist/src/orchestrator/turn-runner-deps.d.ts +2 -0
  44. package/dist/src/pil/__tests__/llm-classify.test.js +79 -1
  45. package/dist/src/pil/layer1-intent.js +37 -37
  46. package/dist/src/pil/layer2_5-ponytail.js +8 -8
  47. package/dist/src/pil/llm-classify.d.ts +11 -0
  48. package/dist/src/pil/llm-classify.js +193 -0
  49. package/dist/src/product-loop/done-gate.js +3 -3
  50. package/dist/src/product-loop/loop-driver.js +18 -18
  51. package/dist/src/product-loop/progress-snapshot.js +4 -4
  52. package/dist/src/providers/mcp-vision-bridge.js +48 -48
  53. package/dist/src/reporter/index.js +1 -1
  54. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  55. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  56. package/dist/src/scaffold/continuation-prompt.js +60 -60
  57. package/dist/src/scaffold/init-new.js +453 -453
  58. package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
  59. package/dist/src/self-qa/agentic-loop.js +11 -2
  60. package/dist/src/storage/__tests__/hierarchical-session.test.d.ts +1 -0
  61. package/dist/src/storage/__tests__/hierarchical-session.test.js +161 -0
  62. package/dist/src/storage/__tests__/migrations.test.js +2 -2
  63. package/dist/src/storage/__tests__/sweep-stale-pending.test.js +2 -2
  64. package/dist/src/storage/index.d.ts +1 -1
  65. package/dist/src/storage/index.js +1 -1
  66. package/dist/src/storage/interaction-log.js +5 -5
  67. package/dist/src/storage/migrations.js +135 -123
  68. package/dist/src/storage/session-experience-store.js +4 -4
  69. package/dist/src/storage/sessions.js +43 -43
  70. package/dist/src/storage/transcript-response-entry.test.js +18 -5
  71. package/dist/src/storage/transcript.d.ts +2 -0
  72. package/dist/src/storage/transcript.js +223 -97
  73. package/dist/src/storage/usage.js +34 -17
  74. package/dist/src/storage/workspaces.js +12 -12
  75. package/dist/src/tools/native-tools.js +10 -1
  76. package/dist/src/tools/registry.d.ts +1 -0
  77. package/dist/src/tools/registry.js +30 -2
  78. package/dist/src/types/index.d.ts +1 -1
  79. package/dist/src/ui/app.js +0 -0
  80. package/dist/src/ui/slash/council-inspect.js +4 -4
  81. package/dist/src/ui/slash/expand.js +14 -1
  82. package/dist/src/ui/use-app-logic.js +0 -0
  83. package/dist/src/utils/__tests__/compaction-caps.test.js +9 -9
  84. package/dist/src/utils/__tests__/llm-deadline.test.js +16 -4
  85. package/dist/src/utils/__tests__/logger.test.d.ts +1 -0
  86. package/dist/src/utils/__tests__/logger.test.js +115 -0
  87. package/dist/src/utils/__tests__/settings-agent-first.test.d.ts +1 -0
  88. package/dist/src/utils/__tests__/settings-agent-first.test.js +48 -0
  89. package/dist/src/utils/clipboard-image.js +23 -23
  90. package/dist/src/utils/install-manager.d.ts +1 -0
  91. package/dist/src/utils/install-manager.js +76 -7
  92. package/dist/src/utils/install-manager.test.js +43 -1
  93. package/dist/src/utils/logger.d.ts +28 -0
  94. package/dist/src/utils/logger.js +171 -0
  95. package/dist/src/utils/settings.d.ts +9 -1
  96. package/dist/src/utils/settings.js +59 -22
  97. package/dist/src/utils/side-question.js +2 -2
  98. package/dist/src/utils/skills.js +3 -3
  99. package/dist/src/utils/update-checker.test.js +5 -1
  100. package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
  101. package/package.json +1 -1
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 muonroi
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 muonroi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,122 +1,122 @@
1
- <p align="center">
2
- <h1 align="center">muonroi-cli</h1>
3
- <p align="center">
4
- <em>An AI coding agent where models argue with each other before answering.</em>
5
- </p>
6
- <p align="center">
7
- <a href="https://github.com/muonroi/muonroi-cli/actions/workflows/ci-matrix.yml"><img alt="CI" src="https://github.com/muonroi/muonroi-cli/actions/workflows/ci-matrix.yml/badge.svg"></a>
8
- <a href="https://www.npmjs.com/package/muonroi-cli"><img alt="npm" src="https://img.shields.io/npm/v/muonroi-cli.svg"></a>
9
- <img alt="Providers" src="https://img.shields.io/badge/providers-7%20supported-blue">
10
- <img alt="License" src="https://img.shields.io/badge/license-MIT-yellow">
11
- <img alt="Runtime" src="https://img.shields.io/badge/runtime-Bun%201.3%2B-orange">
12
- </p>
13
- </p>
14
-
15
- ---
16
-
17
- > Routes each task to the optimal model, runs adversarial multi-model debates for high-stakes decisions, and persists behavioral memory across sessions. Bring your own API keys. Total cost: ~$5/month.
18
-
19
- <p align="center">
20
- <img src="https://raw.githubusercontent.com/muonroi/muonroi-cli/master/docs/demo.gif" alt="Council debate — REST vs gRPC decision" width="840" />
21
- </p>
22
-
23
- ## Quick Start
24
-
25
- ### Install
26
-
27
- **Recommended — prebuilt standalone binary (zero runtime deps, no prereqs):**
28
-
29
- Linux / macOS:
30
- ```bash
31
- curl -fsSL https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.sh | bash
32
- ```
33
-
34
- Windows PowerShell:
35
- ```powershell
36
- irm https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.ps1 | iex
37
- ```
38
-
39
- The installers download a `bun --compile` binary from GitHub Releases — single executable, all native deps bundled, no Node/Bun/build tools required.
40
-
41
- **Bun runtime (requires Bun ≥ 1.x):**
42
- ```bash
43
- bun add -g muonroi-cli
44
- muonroi-cli
45
- ```
46
-
47
- > **⚠ Why no `npm install -g`?** The TUI engine (`@opentui/core`) uses Bun-only `import ... with { type: "file" }` for Tree-sitter syntax highlight queries (`.scm` files). Node ESM cannot load these, so running under bare Node throws `ERR_UNKNOWN_FILE_EXTENSION`. Use the standalone installer (above) or `bun add -g`.
48
-
49
- ### First run
50
-
51
- On first launch the CLI shows a wizard that:
52
- 1. Lists supported providers (currently **DeepSeek** and **SiliconFlow**).
53
- 2. Offers four ways to add credentials: paste an API key, import an encrypted bundle (`keys export`/`keys import`), sync from Bitwarden, or skip and add later via `/providers` inside the TUI.
54
-
55
- After setup, role routing auto-balances across enabled providers:
56
-
57
- ```json
58
- // ~/.muonroi-cli/user-settings.json (generated by the wizard — edit if needed)
59
- {
60
- "defaultProvider": "deepseek",
61
- "providers": {
62
- "deepseek": { "enabled": true },
63
- "siliconflow": { "enabled": true }
64
- },
65
- "roleModels": {
66
- "leader": "deepseek-v4-pro",
67
- "implement": "deepseek-v4-flash",
68
- "verify": "deepseek-v4-pro",
69
- "research": "deepseek-v4-flash"
70
- }
71
- }
72
- ```
73
-
74
- ### Moving keys between devices
75
-
76
- ```bash
77
- # Source device — encrypts every stored key into one passphrase-protected file
78
- muonroi-cli keys export ~/muonroi-keys.json
79
-
80
- # Move the file via any channel (USB, Drive, AirDrop, email...)
81
-
82
- # Target device — same passphrase rehydrates the OS keychain
83
- muonroi-cli keys import ~/muonroi-keys.json
84
- ```
85
-
86
- Inside the TUI press `/providers` then `B` to sync directly from a Bitwarden vault instead.
87
-
88
- ### Updates
89
-
90
- The CLI checks npm once per day on startup and prompts when a newer version is available. Set `autoUpdate: true` in `user-settings.json` to skip the prompt and update silently. Manual: `muonroi-cli update`.
91
-
92
- ## Documentation
93
-
94
- Full documentation at **[docs.muonroi.com/docs/cli](https://docs.muonroi.com/docs/cli/overview)**
95
-
96
- | Topic | Link |
97
- |---|---|
98
- | Overview & architecture | [CLI Overview](https://docs.muonroi.com/docs/cli/overview) |
99
- | Multi-Model Council | [Council Debate Guide](https://docs.muonroi.com/docs/cli/guides/council-debate) |
100
- | Prompt Intelligence Layer | [PIL Pipeline Guide](https://docs.muonroi.com/docs/cli/guides/pil-pipeline) |
101
- | Experience Engine | [Experience Engine Guide](https://docs.muonroi.com/docs/cli/guides/experience-engine) |
102
- | Agent Harness | [Agent Harness Guide](https://docs.muonroi.com/docs/cli/guides/agent-harness) |
103
- | Settings reference | [CLI Settings Reference](https://docs.muonroi.com/docs/cli/reference/cli-settings-reference) |
104
- | Commands reference | [Commands Reference](https://docs.muonroi.com/docs/cli/reference/commands-reference) |
105
- | Providers reference | [Providers Reference](https://docs.muonroi.com/docs/cli/reference/providers-reference) |
106
-
107
- ## Development
108
-
109
- ```bash
110
- git clone https://github.com/muonroi/muonroi-cli.git
111
- cd muonroi-cli && bun install
112
-
113
- bun run dev # run from source
114
- bun run typecheck # type check
115
- bun run test # vitest
116
- bun run lint # biome check
117
- bun run build:binary # standalone binary
118
- ```
119
-
120
- ## License
121
-
122
- MIT
1
+ <p align="center">
2
+ <h1 align="center">muonroi-cli</h1>
3
+ <p align="center">
4
+ <em>An AI coding agent where models argue with each other before answering.</em>
5
+ </p>
6
+ <p align="center">
7
+ <a href="https://github.com/muonroi/muonroi-cli/actions/workflows/ci-matrix.yml"><img alt="CI" src="https://github.com/muonroi/muonroi-cli/actions/workflows/ci-matrix.yml/badge.svg"></a>
8
+ <a href="https://www.npmjs.com/package/muonroi-cli"><img alt="npm" src="https://img.shields.io/npm/v/muonroi-cli.svg"></a>
9
+ <img alt="Providers" src="https://img.shields.io/badge/providers-7%20supported-blue">
10
+ <img alt="License" src="https://img.shields.io/badge/license-MIT-yellow">
11
+ <img alt="Runtime" src="https://img.shields.io/badge/runtime-Bun%201.3%2B-orange">
12
+ </p>
13
+ </p>
14
+
15
+ ---
16
+
17
+ > Routes each task to the optimal model, runs adversarial multi-model debates for high-stakes decisions, and persists behavioral memory across sessions. Bring your own API keys. Total cost: ~$5/month.
18
+
19
+ <p align="center">
20
+ <img src="https://raw.githubusercontent.com/muonroi/muonroi-cli/master/docs/demo.gif" alt="Council debate — REST vs gRPC decision" width="840" />
21
+ </p>
22
+
23
+ ## Quick Start
24
+
25
+ ### Install
26
+
27
+ **Recommended — prebuilt standalone binary (zero runtime deps, no prereqs):**
28
+
29
+ Linux / macOS:
30
+ ```bash
31
+ curl -fsSL https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.sh | bash
32
+ ```
33
+
34
+ Windows PowerShell:
35
+ ```powershell
36
+ irm https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.ps1 | iex
37
+ ```
38
+
39
+ The installers download a `bun --compile` binary from GitHub Releases — single executable, all native deps bundled, no Node/Bun/build tools required.
40
+
41
+ **Bun runtime (requires Bun ≥ 1.x):**
42
+ ```bash
43
+ bun add -g muonroi-cli
44
+ muonroi-cli
45
+ ```
46
+
47
+ > **⚠ Why no `npm install -g`?** The TUI engine (`@opentui/core`) uses Bun-only `import ... with { type: "file" }` for Tree-sitter syntax highlight queries (`.scm` files). Node ESM cannot load these, so running under bare Node throws `ERR_UNKNOWN_FILE_EXTENSION`. Use the standalone installer (above) or `bun add -g`.
48
+
49
+ ### First run
50
+
51
+ On first launch the CLI shows a wizard that:
52
+ 1. Lists supported providers (currently **DeepSeek** and **SiliconFlow**).
53
+ 2. Offers four ways to add credentials: paste an API key, import an encrypted bundle (`keys export`/`keys import`), sync from Bitwarden, or skip and add later via `/providers` inside the TUI.
54
+
55
+ After setup, role routing auto-balances across enabled providers:
56
+
57
+ ```json
58
+ // ~/.muonroi-cli/user-settings.json (generated by the wizard — edit if needed)
59
+ {
60
+ "defaultProvider": "deepseek",
61
+ "providers": {
62
+ "deepseek": { "enabled": true },
63
+ "siliconflow": { "enabled": true }
64
+ },
65
+ "roleModels": {
66
+ "leader": "deepseek-v4-pro",
67
+ "implement": "deepseek-v4-flash",
68
+ "verify": "deepseek-v4-pro",
69
+ "research": "deepseek-v4-flash"
70
+ }
71
+ }
72
+ ```
73
+
74
+ ### Moving keys between devices
75
+
76
+ ```bash
77
+ # Source device — encrypts every stored key into one passphrase-protected file
78
+ muonroi-cli keys export ~/muonroi-keys.json
79
+
80
+ # Move the file via any channel (USB, Drive, AirDrop, email...)
81
+
82
+ # Target device — same passphrase rehydrates the OS keychain
83
+ muonroi-cli keys import ~/muonroi-keys.json
84
+ ```
85
+
86
+ Inside the TUI press `/providers` then `B` to sync directly from a Bitwarden vault instead.
87
+
88
+ ### Updates
89
+
90
+ The CLI checks npm once per day on startup and prompts when a newer version is available. Set `autoUpdate: true` in `user-settings.json` to skip the prompt and update silently. Manual: `muonroi-cli update`.
91
+
92
+ ## Documentation
93
+
94
+ Full documentation at **[docs.muonroi.com/docs/cli](https://docs.muonroi.com/docs/cli/overview)**
95
+
96
+ | Topic | Link |
97
+ |---|---|
98
+ | Overview & architecture | [CLI Overview](https://docs.muonroi.com/docs/cli/overview) |
99
+ | Multi-Model Council | [Council Debate Guide](https://docs.muonroi.com/docs/cli/guides/council-debate) |
100
+ | Prompt Intelligence Layer | [PIL Pipeline Guide](https://docs.muonroi.com/docs/cli/guides/pil-pipeline) |
101
+ | Experience Engine | [Experience Engine Guide](https://docs.muonroi.com/docs/cli/guides/experience-engine) |
102
+ | Agent Harness | [Agent Harness Guide](https://docs.muonroi.com/docs/cli/guides/agent-harness) |
103
+ | Settings reference | [CLI Settings Reference](https://docs.muonroi.com/docs/cli/reference/cli-settings-reference) |
104
+ | Commands reference | [Commands Reference](https://docs.muonroi.com/docs/cli/reference/commands-reference) |
105
+ | Providers reference | [Providers Reference](https://docs.muonroi.com/docs/cli/reference/providers-reference) |
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ git clone https://github.com/muonroi/muonroi-cli.git
111
+ cd muonroi-cli && bun install
112
+
113
+ bun run dev # run from source
114
+ bun run typecheck # type check
115
+ bun run test # vitest
116
+ bun run lint # biome check
117
+ bun run build:binary # standalone binary
118
+ ```
119
+
120
+ ## License
121
+
122
+ MIT
@@ -199,13 +199,121 @@ export function toolCallStream(opts) {
199
199
  * string payloads here so the rest of the pipeline sees a uniform shape.
200
200
  */
201
201
  function normalizeStreamChunks(chunks) {
202
- return chunks.map((c) => {
203
- if (c && c.type === "error") {
204
- const raw = c.error;
205
- return { ...c, error: buildFixtureError(raw) };
202
+ const normalized = chunks.map((c) => {
203
+ if (!c)
204
+ return c;
205
+ const part = c;
206
+ if (part.type === "error") {
207
+ const raw = part.error;
208
+ return { ...part, error: buildFixtureError(raw) };
209
+ }
210
+ if (part.type === "text-delta") {
211
+ const delta = part.delta ?? part.textDelta ?? part.text ?? "";
212
+ const id = part.id ?? "txt-0";
213
+ return { ...part, id, delta };
214
+ }
215
+ if (part.type === "reasoning-delta") {
216
+ const delta = part.delta ?? part.textDelta ?? part.text ?? "";
217
+ const id = part.id ?? "reasoning-0";
218
+ return { ...part, id, delta };
206
219
  }
207
220
  return c;
208
221
  });
222
+ const result = [];
223
+ const textIds = new Set();
224
+ const reasoningIds = new Set();
225
+ for (const part of normalized) {
226
+ if (!part)
227
+ continue;
228
+ const p = part;
229
+ if (p.type === "text-delta") {
230
+ textIds.add(p.id ?? "txt-0");
231
+ }
232
+ else if (p.type === "reasoning-delta") {
233
+ reasoningIds.add(p.id ?? "reasoning-0");
234
+ }
235
+ }
236
+ const seenTextStart = new Set();
237
+ const seenTextEnd = new Set();
238
+ const seenReasoningStart = new Set();
239
+ const seenReasoningEnd = new Set();
240
+ for (const part of normalized) {
241
+ if (!part)
242
+ continue;
243
+ const p = part;
244
+ if (p.type === "text-start") {
245
+ seenTextStart.add(p.id);
246
+ }
247
+ else if (p.type === "text-end") {
248
+ seenTextEnd.add(p.id);
249
+ }
250
+ else if (p.type === "reasoning-start") {
251
+ seenReasoningStart.add(p.id);
252
+ }
253
+ else if (p.type === "reasoning-end") {
254
+ seenReasoningEnd.add(p.id);
255
+ }
256
+ }
257
+ const firstTextDeltaIdx = new Map();
258
+ const lastTextDeltaIdx = new Map();
259
+ const firstReasoningDeltaIdx = new Map();
260
+ const lastReasoningDeltaIdx = new Map();
261
+ for (let i = 0; i < normalized.length; i++) {
262
+ const p = normalized[i];
263
+ if (!p)
264
+ continue;
265
+ if (p.type === "text-delta") {
266
+ const id = p.id ?? "txt-0";
267
+ if (!firstTextDeltaIdx.has(id))
268
+ firstTextDeltaIdx.set(id, i);
269
+ lastTextDeltaIdx.set(id, i);
270
+ }
271
+ else if (p.type === "reasoning-delta") {
272
+ const id = p.id ?? "reasoning-0";
273
+ if (!firstReasoningDeltaIdx.has(id))
274
+ firstReasoningDeltaIdx.set(id, i);
275
+ lastReasoningDeltaIdx.set(id, i);
276
+ }
277
+ }
278
+ for (let i = 0; i < normalized.length; i++) {
279
+ const p = normalized[i];
280
+ if (!p) {
281
+ result.push(p);
282
+ continue;
283
+ }
284
+ if (p.type === "text-delta") {
285
+ const id = p.id ?? "txt-0";
286
+ if (firstTextDeltaIdx.get(id) === i && !seenTextStart.has(id)) {
287
+ result.push({ type: "text-start", id });
288
+ }
289
+ }
290
+ else if (p.type === "reasoning-delta") {
291
+ const id = p.id ?? "reasoning-0";
292
+ if (firstReasoningDeltaIdx.get(id) === i && !seenReasoningStart.has(id)) {
293
+ result.push({ type: "reasoning-start", id });
294
+ }
295
+ }
296
+ if (p.type === "finish") {
297
+ const usage = p.usage ?? buildUsage(10, 10);
298
+ result.push({ ...p, usage });
299
+ }
300
+ else {
301
+ result.push(p);
302
+ }
303
+ if (p.type === "text-delta") {
304
+ const id = p.id ?? "txt-0";
305
+ if (lastTextDeltaIdx.get(id) === i && !seenTextEnd.has(id)) {
306
+ result.push({ type: "text-end", id });
307
+ }
308
+ }
309
+ else if (p.type === "reasoning-delta") {
310
+ const id = p.id ?? "reasoning-0";
311
+ if (lastReasoningDeltaIdx.get(id) === i && !seenReasoningEnd.has(id)) {
312
+ result.push({ type: "reasoning-end", id });
313
+ }
314
+ }
315
+ }
316
+ return result;
209
317
  }
210
318
  /**
211
319
  * Turn a fixture `error` payload into the right runtime shape:
@@ -11,6 +11,7 @@
11
11
  import { getProviderCapabilities } from "../providers/capabilities.js";
12
12
  import { detectProviderForModel } from "../providers/runtime.js";
13
13
  import { getDatabase } from "../storage/db.js";
14
+ import { getSessionChain } from "../storage/index.js";
14
15
  import { selectSessionExperience } from "../storage/session-experience-store.js";
15
16
  function resolveSessionId(prefix) {
16
17
  const rows = getDatabase()
@@ -42,24 +43,26 @@ export function collectCostForensics(sessionId) {
42
43
  const session = db.prepare(`SELECT id, model, created_at FROM sessions WHERE id = ?`).get(sessionId);
43
44
  if (!session)
44
45
  throw new Error(`Session not found: ${sessionId}`);
46
+ const chain = getSessionChain(sessionId);
47
+ const placeholders = chain.map(() => "?").join(",");
45
48
  const rows = db
46
- .prepare(`
47
- SELECT id, source, model, message_seq, input_tokens, output_tokens,
48
- cache_read_tokens, cache_creation_tokens, cost_micros, created_at,
49
- provider_options_shape
50
- FROM usage_events
51
- WHERE session_id = ?
52
- ORDER BY id ASC
49
+ .prepare(`
50
+ SELECT id, source, model, message_seq, input_tokens, output_tokens,
51
+ cache_read_tokens, cache_creation_tokens, cost_micros, created_at,
52
+ provider_options_shape
53
+ FROM usage_events
54
+ WHERE session_id IN (${placeholders})
55
+ ORDER BY id ASC
53
56
  `)
54
- .all(sessionId);
57
+ .all(...chain);
55
58
  const counts = db
56
- .prepare(`
57
- SELECT event_type, COUNT(*) AS c
58
- FROM interaction_logs
59
- WHERE session_id = ?
60
- GROUP BY event_type
59
+ .prepare(`
60
+ SELECT event_type, COUNT(*) AS c
61
+ FROM interaction_logs
62
+ WHERE session_id IN (${placeholders})
63
+ GROUP BY event_type
61
64
  `)
62
- .all(sessionId);
65
+ .all(...chain);
63
66
  const countMap = new Map(counts.map((r) => [r.event_type, r.c]));
64
67
  const events = rows.map((r) => ({
65
68
  id: r.id,
@@ -548,33 +548,33 @@ export function buildSynthesisPrompt(ctx) {
548
548
  }
549
549
  let extraContext = "";
550
550
  if (ctx.refineContext) {
551
- extraContext += `
552
- ## User Refinements
553
- ${ctx.refineContext}
551
+ extraContext += `
552
+ ## User Refinements
553
+ ${ctx.refineContext}
554
554
  `;
555
555
  }
556
556
  if (ctx.planEmphasis) {
557
- extraContext += `
558
- ## Additional Instruction
559
- The user has requested a concrete action plan with executable steps. Each action item MUST be an object with these fields:
560
- {
561
- "step": "<imperative action>",
562
- "owner_lens": "<which stance owns this — frontend / backend / architecture / etc>",
563
- "time_estimate": "<rough — e.g. '2h', '1d', '~30min'>",
564
- "depends_on": ["<step keys this requires>"] or [],
565
- "acceptance_criteria": "<how we know it's done>"
566
- }
567
- Order action items by dependency: predecessors first, dependents after.
568
- Risks MUST be objects with: {"description", "severity": "High|Medium|Low", "mitigation"}.
569
- Do NOT emit loose strings for these fields — the user needs structured plan output.
557
+ extraContext += `
558
+ ## Additional Instruction
559
+ The user has requested a concrete action plan with executable steps. Each action item MUST be an object with these fields:
560
+ {
561
+ "step": "<imperative action>",
562
+ "owner_lens": "<which stance owns this — frontend / backend / architecture / etc>",
563
+ "time_estimate": "<rough — e.g. '2h', '1d', '~30min'>",
564
+ "depends_on": ["<step keys this requires>"] or [],
565
+ "acceptance_criteria": "<how we know it's done>"
566
+ }
567
+ Order action items by dependency: predecessors first, dependents after.
568
+ Risks MUST be objects with: {"description", "severity": "High|Medium|Low", "mitigation"}.
569
+ Do NOT emit loose strings for these fields — the user needs structured plan output.
570
570
  `;
571
571
  }
572
572
  return {
573
573
  system,
574
- prompt: `Final positions:
575
- ${ctx.finalPositions}
576
-
577
- Full discussion:
574
+ prompt: `Final positions:
575
+ ${ctx.finalPositions}
576
+
577
+ Full discussion:
578
578
  ${ctx.allExchanges}${extraContext}`,
579
579
  };
580
580
  }
@@ -11,6 +11,7 @@ export interface CompactionResult {
11
11
  tokensBeforeCompress: number;
12
12
  tokensAfterCompress: number;
13
13
  historyPath: string;
14
+ summary: string;
14
15
  }
15
16
  /**
16
17
  * Orchestrate two-pass deliberate compaction.
@@ -49,6 +49,9 @@ export async function deliberateCompact(flowDir, messages, systemPrompt, tokenBu
49
49
  const historyPath = path.join(historyDir, `${timestamp}.md`);
50
50
  const fullSerialized = serializeConversation(messages);
51
51
  await atomicWriteText(historyPath, fullSerialized);
52
+ // Save full JSON history next to the md file for exact expand/restore
53
+ const jsonPath = path.join(historyDir, `${timestamp}.json`);
54
+ await atomicWriteText(jsonPath, JSON.stringify(messages, null, 2));
52
55
  // Token estimation before
53
56
  const tokensBefore = estimateConversationTokens(systemPrompt, messages);
54
57
  // Pass 2: Compress
@@ -58,6 +61,7 @@ export async function deliberateCompact(flowDir, messages, systemPrompt, tokenBu
58
61
  tokensBeforeCompress: tokensBefore,
59
62
  tokensAfterCompress: compressed.tokensAfter,
60
63
  historyPath,
64
+ summary: compressed.summary,
61
65
  };
62
66
  }
63
67
  //# sourceMappingURL=index.js.map
@@ -1,2 +1,2 @@
1
- export declare const PACKAGE_VERSION = "1.8.0";
1
+ export declare const PACKAGE_VERSION = "1.8.1";
2
2
  export declare const PACKAGE_DESCRIPTION = "BYOK AI coding agent with multi-model council debate, role-based routing, and auto-compact.";
@@ -1,5 +1,5 @@
1
1
  // AUTO-GENERATED by scripts/sync-version.cjs. DO NOT EDIT BY HAND.
2
2
  // Sourced from package.json at build time so it survives bun --compile bundling.
3
- export const PACKAGE_VERSION = "1.8.0";
3
+ export const PACKAGE_VERSION = "1.8.1";
4
4
  export const PACKAGE_DESCRIPTION = "BYOK AI coding agent with multi-model council debate, role-based routing, and auto-compact.";
5
5
  //# sourceMappingURL=version.js.map
package/dist/src/index.js CHANGED
@@ -51,6 +51,7 @@ process.on("SIGTERM", exitCleanlyOnSigterm);
51
51
  let _tuiActive = false;
52
52
  export function setTuiActive(active) {
53
53
  _tuiActive = active;
54
+ globalThis.__muonroiTuiActive = active;
54
55
  }
55
56
  export function appendCrashLog(label, msg) {
56
57
  try {
@@ -972,7 +973,8 @@ program
972
973
  console.log("Checking for updates...");
973
974
  const result = await runUpdate(packageJson.version);
974
975
  console.log(result.output);
975
- process.exit(result.success ? 0 : 1);
976
+ process.exitCode = result.success ? 0 : 1;
977
+ return;
976
978
  }
977
979
  changeDirectoryOrExit(options.directory);
978
980
  // Boot model registry BEFORE any key resolution path runs —
@@ -1159,7 +1161,7 @@ program
1159
1161
  console.log("Checking for updates...");
1160
1162
  const result = await runUpdate(packageJson.version);
1161
1163
  console.log(result.output);
1162
- process.exit(result.success ? 0 : 1);
1164
+ process.exitCode = result.success ? 0 : 1;
1163
1165
  });
1164
1166
  program
1165
1167
  .command("uninstall")
@@ -64,8 +64,16 @@ export function createWorkspaceLspManager(cwd, settings, options = {}) {
64
64
  return resolved.filter((value) => value !== null);
65
65
  }
66
66
  async function touchFile(filePath, waitForDiagnostics = true) {
67
- const content = await readFile(filePath, "utf8");
68
- return syncFile(filePath, content, false, waitForDiagnostics);
67
+ try {
68
+ const content = await readFile(filePath, "utf8");
69
+ return await syncFile(filePath, content, false, waitForDiagnostics);
70
+ }
71
+ catch (err) {
72
+ if (err instanceof Error && err.code === "ENOENT") {
73
+ return [];
74
+ }
75
+ throw err;
76
+ }
69
77
  }
70
78
  async function syncFile(filePath, content, save = true, waitForDiagnostics = true, diagnosticsTimeoutMs) {
71
79
  const records = await getClientsForFile(filePath);
@@ -104,7 +112,7 @@ export function createWorkspaceLspManager(cwd, settings, options = {}) {
104
112
  output: `No LSP server available for ${path.extname(normalizedPath) || "this file type"}.`,
105
113
  };
106
114
  }
107
- const lspDiagnostics = await touchFile(normalizedPath, true);
115
+ const lspDiagnostics = input.operation === "workspaceSymbol" ? [] : await touchFile(normalizedPath, true);
108
116
  const params = createOperationParams(input, normalizedPath);
109
117
  const timeoutMs = settings.requestTimeoutMs;
110
118
  const results = (await Promise.all(records.map(async ({ key, client }) => {