muonroi-cli 1.7.2 → 1.8.0

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 (322) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +122 -122
  3. package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
  4. package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
  5. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
  7. package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
  8. package/dist/packages/agent-harness-core/src/selector.js +2 -2
  9. package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
  10. package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
  11. package/dist/packages/agent-harness-opentui/src/install.js +1 -1
  12. package/dist/src/__test-stubs__/vitest-setup.js +4 -0
  13. package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
  14. package/dist/src/agent-harness/mock-model.d.ts +1 -1
  15. package/dist/src/agent-harness/mock-model.js +32 -3
  16. package/dist/src/agent-harness/test-spawn.js +3 -1
  17. package/dist/src/chat/chat-keychain.js +9 -1
  18. package/dist/src/cli/cost-forensics.js +12 -12
  19. package/dist/src/cli/keys.d.ts +2 -2
  20. package/dist/src/cli/keys.js +107 -28
  21. package/dist/src/cli/usage-report.js +2 -2
  22. package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
  23. package/dist/src/council/__tests__/abort-threading.test.js +193 -0
  24. package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
  25. package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
  26. package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
  27. package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
  28. package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
  29. package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
  30. package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
  31. package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
  32. package/dist/src/council/__tests__/research-tools.test.js +4 -0
  33. package/dist/src/council/__tests__/round-tools.test.js +6 -0
  34. package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
  35. package/dist/src/council/clarifier-question-cap.test.js +73 -0
  36. package/dist/src/council/clarifier.d.ts +29 -1
  37. package/dist/src/council/clarifier.js +64 -1
  38. package/dist/src/council/debate-planner.d.ts +2 -1
  39. package/dist/src/council/debate-planner.js +5 -3
  40. package/dist/src/council/debate.d.ts +9 -0
  41. package/dist/src/council/debate.js +88 -8
  42. package/dist/src/council/index.d.ts +46 -1
  43. package/dist/src/council/index.js +125 -19
  44. package/dist/src/council/llm.js +41 -13
  45. package/dist/src/council/prompts.js +56 -21
  46. package/dist/src/council/types.d.ts +14 -1
  47. package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
  48. package/dist/src/ee/bridge.d.ts +3 -1
  49. package/dist/src/ee/bridge.js +9 -3
  50. package/dist/src/ee/bridge.test.js +1 -2
  51. package/dist/src/ee/client.js +15 -2
  52. package/dist/src/ee/recall-ledger.d.ts +20 -0
  53. package/dist/src/ee/recall-ledger.js +40 -1
  54. package/dist/src/ee/search.d.ts +25 -0
  55. package/dist/src/ee/search.js +73 -0
  56. package/dist/src/ee/types.d.ts +15 -1
  57. package/dist/src/ee/who-am-i.d.ts +41 -0
  58. package/dist/src/ee/who-am-i.js +143 -0
  59. package/dist/src/ee/who-am-i.test.d.ts +1 -0
  60. package/dist/src/ee/who-am-i.test.js +101 -0
  61. package/dist/src/generated/version.d.ts +1 -1
  62. package/dist/src/generated/version.js +1 -1
  63. package/dist/src/headless/council-answers.js +1 -0
  64. package/dist/src/headless/output.d.ts +30 -1
  65. package/dist/src/headless/output.js +180 -0
  66. package/dist/src/headless/output.test.js +61 -1
  67. package/dist/src/hooks/index.js +48 -2
  68. package/dist/src/index.d.ts +2 -0
  69. package/dist/src/index.js +55 -60
  70. package/dist/src/lsp/client-normalize.test.d.ts +12 -0
  71. package/dist/src/lsp/client-normalize.test.js +34 -0
  72. package/dist/src/lsp/client.d.ts +1 -0
  73. package/dist/src/lsp/client.js +10 -2
  74. package/dist/src/lsp/manager.d.ts +1 -1
  75. package/dist/src/lsp/manager.js +2 -2
  76. package/dist/src/lsp/manager.test.js +2 -2
  77. package/dist/src/lsp/runtime.d.ts +1 -1
  78. package/dist/src/lsp/runtime.js +2 -2
  79. package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
  80. package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
  81. package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
  82. package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
  83. package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
  84. package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
  85. package/dist/src/mcp/client-pool.d.ts +1 -1
  86. package/dist/src/mcp/client-pool.js +25 -9
  87. package/dist/src/mcp/ee-tools.d.ts +10 -0
  88. package/dist/src/mcp/ee-tools.js +34 -0
  89. package/dist/src/mcp/oauth-callback.js +2 -2
  90. package/dist/src/mcp/parse-headers.test.js +14 -14
  91. package/dist/src/mcp/self-verify-jobs.js +1 -3
  92. package/dist/src/mcp/setup-guide-text.js +74 -74
  93. package/dist/src/mcp/smoke.test.js +43 -43
  94. package/dist/src/models/catalog-gemini.test.js +12 -10
  95. package/dist/src/models/catalog.json +106 -38
  96. package/dist/src/ops/doctor.js +7 -7
  97. package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
  98. package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
  99. package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +2 -0
  100. package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
  101. package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
  102. package/dist/src/orchestrator/__tests__/message-processor.test.js +1 -0
  103. package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
  104. package/dist/src/orchestrator/agent.test.js +5 -2
  105. package/dist/src/orchestrator/auto-commit.d.ts +110 -0
  106. package/dist/src/orchestrator/auto-commit.js +364 -0
  107. package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
  108. package/dist/src/orchestrator/batch-turn-runner.js +2 -1
  109. package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
  110. package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
  111. package/dist/src/orchestrator/compaction.d.ts +22 -0
  112. package/dist/src/orchestrator/compaction.js +42 -0
  113. package/dist/src/orchestrator/delegations.d.ts +1 -0
  114. package/dist/src/orchestrator/delegations.js +41 -0
  115. package/dist/src/orchestrator/error-utils.js +27 -2
  116. package/dist/src/orchestrator/message-processor.d.ts +21 -5
  117. package/dist/src/orchestrator/message-processor.js +219 -2573
  118. package/dist/src/orchestrator/orchestrator.d.ts +8 -2
  119. package/dist/src/orchestrator/orchestrator.js +187 -34
  120. package/dist/src/orchestrator/preprocessor.d.ts +13 -0
  121. package/dist/src/orchestrator/preprocessor.js +145 -0
  122. package/dist/src/orchestrator/prompts.d.ts +8 -0
  123. package/dist/src/orchestrator/prompts.js +76 -60
  124. package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
  125. package/dist/src/orchestrator/safety-askcard.js +74 -0
  126. package/dist/src/orchestrator/sandbox.test.js +8 -5
  127. package/dist/src/orchestrator/scope-ceiling.js +1 -1
  128. package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
  129. package/dist/src/orchestrator/stall-watchdog.js +23 -0
  130. package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
  131. package/dist/src/orchestrator/stream-runner.js +52 -30
  132. package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
  133. package/dist/src/orchestrator/subagent-compactor.d.ts +14 -0
  134. package/dist/src/orchestrator/subagent-compactor.js +83 -10
  135. package/dist/src/orchestrator/subagent-compactor.spec.js +132 -0
  136. package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
  137. package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
  138. package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
  139. package/dist/src/orchestrator/tool-args-repair.js +1 -1
  140. package/dist/src/orchestrator/tool-engine.d.ts +151 -0
  141. package/dist/src/orchestrator/tool-engine.js +3079 -0
  142. package/dist/src/orchestrator/turn-runner-deps.d.ts +1 -0
  143. package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
  144. package/dist/src/pil/__tests__/discovery.test.js +89 -145
  145. package/dist/src/pil/__tests__/dual-run.test.js +2 -0
  146. package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
  147. package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
  148. package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
  149. package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
  150. package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
  151. package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
  152. package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
  153. package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
  154. package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
  155. package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
  156. package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
  157. package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
  158. package/dist/src/pil/__tests__/pipeline.test.js +11 -8
  159. package/dist/src/pil/__tests__/response-tools.test.js +52 -0
  160. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  161. package/dist/src/pil/agent-operating-contract.js +5 -3
  162. package/dist/src/pil/agent-operating-contract.test.js +2 -2
  163. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  164. package/dist/src/pil/cheap-model-playbook.js +1 -1
  165. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  166. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  167. package/dist/src/pil/config.d.ts +20 -0
  168. package/dist/src/pil/config.js +35 -0
  169. package/dist/src/pil/discovery-types.d.ts +32 -9
  170. package/dist/src/pil/discovery.d.ts +3 -3
  171. package/dist/src/pil/discovery.js +138 -244
  172. package/dist/src/pil/index.d.ts +1 -1
  173. package/dist/src/pil/index.js +1 -1
  174. package/dist/src/pil/layer1-intent.d.ts +9 -1
  175. package/dist/src/pil/layer1-intent.js +99 -44
  176. package/dist/src/pil/layer1-intent.test.js +112 -2
  177. package/dist/src/pil/layer15-context-scan.js +1 -1
  178. package/dist/src/pil/layer16-clarity.d.ts +21 -25
  179. package/dist/src/pil/layer16-clarity.js +52 -88
  180. package/dist/src/pil/layer18-acceptance.d.ts +13 -3
  181. package/dist/src/pil/layer18-acceptance.js +1 -75
  182. package/dist/src/pil/layer2-personality.js +7 -8
  183. package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
  184. package/dist/src/pil/layer2_5-ponytail.js +41 -0
  185. package/dist/src/pil/layer3-ee-injection.js +180 -14
  186. package/dist/src/pil/layer4-gsd.js +22 -8
  187. package/dist/src/pil/layer5-context.d.ts +1 -0
  188. package/dist/src/pil/layer5-context.js +26 -11
  189. package/dist/src/pil/layer6-output.js +23 -5
  190. package/dist/src/pil/llm-classify.js +3 -3
  191. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  192. package/dist/src/pil/native-capabilities-workbook.js +4 -3
  193. package/dist/src/pil/pipeline.js +15 -1
  194. package/dist/src/pil/response-tools.d.ts +16 -0
  195. package/dist/src/pil/response-tools.js +41 -0
  196. package/dist/src/pil/schema.d.ts +8 -0
  197. package/dist/src/pil/schema.js +24 -3
  198. package/dist/src/pil/task-tier-map.js +7 -7
  199. package/dist/src/pil/types.d.ts +9 -3
  200. package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
  201. package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
  202. package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
  203. package/dist/src/product-loop/discovery-interview.d.ts +20 -0
  204. package/dist/src/product-loop/discovery-interview.js +121 -7
  205. package/dist/src/product-loop/done-gate.js +3 -3
  206. package/dist/src/product-loop/gather.js +19 -5
  207. package/dist/src/product-loop/loop-driver.js +20 -20
  208. package/dist/src/product-loop/progress-snapshot.js +4 -4
  209. package/dist/src/product-loop/sprint-self-verify.js +2 -2
  210. package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
  211. package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
  212. package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
  213. package/dist/src/providers/auth/gcloud.d.ts +28 -0
  214. package/dist/src/providers/auth/gcloud.js +102 -0
  215. package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
  216. package/dist/src/providers/auth/gemini-oauth.js +292 -105
  217. package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
  218. package/dist/src/providers/auth/grok-oauth.js +1 -1
  219. package/dist/src/providers/auth/registry.js +28 -6
  220. package/dist/src/providers/gemini.d.ts +1 -1
  221. package/dist/src/providers/gemini.js +10 -3
  222. package/dist/src/providers/keychain.d.ts +2 -2
  223. package/dist/src/providers/keychain.js +20 -2
  224. package/dist/src/providers/mcp-vision-bridge.js +48 -48
  225. package/dist/src/providers/openai.js +5 -1
  226. package/dist/src/providers/runtime.d.ts +1 -1
  227. package/dist/src/providers/runtime.js +11 -2
  228. package/dist/src/providers/siliconflow-sse-repair.js +1 -1
  229. package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
  230. package/dist/src/providers/strategies/google.strategy.js +159 -9
  231. package/dist/src/providers/strategies/thinking-mode.js +19 -6
  232. package/dist/src/providers/types.d.ts +1 -1
  233. package/dist/src/reporter/index.js +1 -1
  234. package/dist/src/router/decide.test.js +40 -8
  235. package/dist/src/router/step-router.d.ts +20 -0
  236. package/dist/src/router/step-router.js +73 -0
  237. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  238. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  239. package/dist/src/scaffold/continuation-prompt.js +60 -60
  240. package/dist/src/scaffold/init-new.js +453 -453
  241. package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
  242. package/dist/src/self-qa/agentic-loop.js +19 -19
  243. package/dist/src/self-qa/orchestrator.js +5 -5
  244. package/dist/src/self-qa/scenario-planner.js +1 -1
  245. package/dist/src/self-qa/spec-emitter.js +4 -4
  246. package/dist/src/storage/__tests__/migrations.test.js +2 -2
  247. package/dist/src/storage/index.d.ts +1 -1
  248. package/dist/src/storage/index.js +1 -1
  249. package/dist/src/storage/interaction-log.js +5 -5
  250. package/dist/src/storage/migrations.js +122 -122
  251. package/dist/src/storage/session-experience-store.js +4 -4
  252. package/dist/src/storage/sessions.js +43 -43
  253. package/dist/src/storage/transcript-view.js +7 -1
  254. package/dist/src/storage/transcript.d.ts +1 -0
  255. package/dist/src/storage/transcript.js +55 -0
  256. package/dist/src/storage/ui-interaction-log.d.ts +17 -0
  257. package/dist/src/storage/usage.js +14 -14
  258. package/dist/src/storage/workspaces.js +12 -12
  259. package/dist/src/tools/__tests__/native-tools.test.js +6 -0
  260. package/dist/src/tools/bash.d.ts +36 -23
  261. package/dist/src/tools/bash.js +59 -301
  262. package/dist/src/tools/bash.test.js +2 -257
  263. package/dist/src/tools/file-tracker.d.ts +5 -0
  264. package/dist/src/tools/file-tracker.js +14 -0
  265. package/dist/src/tools/file.js +2 -0
  266. package/dist/src/tools/git-safety.d.ts +38 -1
  267. package/dist/src/tools/git-safety.js +94 -4
  268. package/dist/src/tools/git-safety.test.js +45 -1
  269. package/dist/src/tools/native-tools.d.ts +1 -1
  270. package/dist/src/tools/native-tools.js +56 -0
  271. package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
  272. package/dist/src/tools/registry-git-safety.test.js +17 -6
  273. package/dist/src/tools/registry.d.ts +5 -0
  274. package/dist/src/tools/registry.js +185 -11
  275. package/dist/src/types/index.d.ts +7 -1
  276. package/dist/src/ui/app.d.ts +1 -0
  277. package/dist/src/ui/app.js +0 -0
  278. package/dist/src/ui/components/council-question-card.js +1 -0
  279. package/dist/src/ui/components/message-view.js +37 -3
  280. package/dist/src/ui/components/structured-response-view.js +7 -0
  281. package/dist/src/ui/components/task-list-panel.js +3 -12
  282. package/dist/src/ui/containers/chat-feed.d.ts +40 -0
  283. package/dist/src/ui/containers/chat-feed.js +66 -0
  284. package/dist/src/ui/containers/modals-layer.d.ts +87 -0
  285. package/dist/src/ui/containers/modals-layer.js +18 -0
  286. package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
  287. package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
  288. package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
  289. package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
  290. package/dist/src/ui/slash/council-inspect.js +4 -4
  291. package/dist/src/ui/slash/export.js +16 -1
  292. package/dist/src/ui/slash/menu-items.js +1 -0
  293. package/dist/src/ui/slash/ponytail.d.ts +2 -0
  294. package/dist/src/ui/slash/ponytail.js +19 -0
  295. package/dist/src/ui/status-bar/store.js +0 -1
  296. package/dist/src/ui/status-bar/store.test.js +2 -2
  297. package/dist/src/ui/use-app-logic.d.ts +26 -0
  298. package/dist/src/ui/use-app-logic.js +0 -0
  299. package/dist/src/ui/utils/relaunch.js +1 -1
  300. package/dist/src/ui/utils/text.d.ts +11 -0
  301. package/dist/src/ui/utils/text.js +21 -1
  302. package/dist/src/ui/utils/text.test.js +25 -1
  303. package/dist/src/ui/utils/tools.js +3 -1
  304. package/dist/src/usage/cost-log.js +2 -2
  305. package/dist/src/usage/estimator.d.ts +14 -0
  306. package/dist/src/usage/estimator.js +22 -0
  307. package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
  308. package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
  309. package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
  310. package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
  311. package/dist/src/utils/clipboard-image.js +23 -23
  312. package/dist/src/utils/llm-deadline.d.ts +9 -1
  313. package/dist/src/utils/llm-deadline.js +31 -2
  314. package/dist/src/utils/permission-mode.d.ts +33 -0
  315. package/dist/src/utils/permission-mode.js +164 -3
  316. package/dist/src/utils/permission-mode.test.js +92 -1
  317. package/dist/src/utils/settings.d.ts +27 -31
  318. package/dist/src/utils/settings.js +100 -109
  319. package/dist/src/utils/side-question.js +2 -2
  320. package/dist/src/utils/skills.js +3 -3
  321. package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
  322. package/package.json +1 -1
@@ -2,17 +2,17 @@ import { beforeAll, describe, expect, it } from "vitest";
2
2
  import { detectProviderForModel } from "../providers/runtime.js";
3
3
  import { getModelInfo, loadCatalog } from "./registry.js";
4
4
  /**
5
- * Regression guard for the Gemini catalog entries (added 2026-06-04). Model IDs
6
- * verified against ai.google.dev/gemini-api/docs/models on that date. Without a
7
- * catalog entry a model cannot be selected or routed (Zero-Hardcode Rule), so
8
- * these assertions lock in that Gemini is actually usable end-to-end.
5
+ * Regression guard for the Agy/Google Gemini catalog entries.
6
+ * These are the models Agy supports for the "google" provider (via agy.exe OAuth + cloudcode-pa).
7
+ * Without a catalog entry a model cannot be selected or routed (Zero-Hardcode Rule).
9
8
  */
10
9
  const GEMINI_IDS = [
11
- "gemini-2.5-flash",
12
- "gemini-2.5-pro",
13
- "gemini-3.5-flash",
14
- "gemini-3.1-flash-lite",
15
- "gemini-3.1-pro-preview",
10
+ "gemini-3.5-flash-high",
11
+ "gemini-3.5-flash-medium",
12
+ "gemini-3.5-flash-low",
13
+ "gemini-3.1-pro-high",
14
+ "gemini-3.1-pro-low",
15
+ "gemini-3-flash",
16
16
  ];
17
17
  describe("Gemini catalog entries", () => {
18
18
  beforeAll(async () => {
@@ -26,8 +26,10 @@ describe("Gemini catalog entries", () => {
26
26
  }
27
27
  });
28
28
  it("maps gemini ids to the google provider via runtime detection", () => {
29
- expect(detectProviderForModel("gemini-2.5-flash")).toBe("google");
29
+ // direct + alias (gemini-3.5-flash aliases to the high variant)
30
+ expect(detectProviderForModel("gemini-3.5-flash-high")).toBe("google");
30
31
  expect(detectProviderForModel("gemini-3.5-flash")).toBe("google");
32
+ expect(detectProviderForModel("gemini-3.1-pro-high")).toBe("google");
31
33
  });
32
34
  it("marks all Gemini models multimodal (vision) and 1M context", () => {
33
35
  for (const id of GEMINI_IDS) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "2.2",
3
3
  "updated_at": "2026-06-04",
4
- "description": "Local fallback catalog: deepseek + siliconflow + openai (ChatGPT OAuth) + google (Gemini, via AI Studio key or Google OAuth). SiliconFlow pricing verified 2026-05-21; Gemini 2.5 pricing per ai.google.dev/pricing 2026-06-04 (3.x flash/pro entries APPROXIMATE see per-model descriptions). Model IDs verified against ai.google.dev/gemini-api/docs/models 2026-06-04. See ./catalog.README.md for curation rationale.",
4
+ "description": "Local fallback catalog: deepseek + siliconflow + openai (ChatGPT OAuth) + google (Agy via agy.exe OAuth + cloudcode-pa). Agy supports: Gemini 3.5 Flash (High/Medium/Low), Gemini 3.1 Pro (High/Low), Gemini 3 Flash, Claude Sonnet 4.6 (Thinking), Claude Opus 4.6 (Thinking), GPT-OSS 120B (Medium+). Non-Gemini models under google provider use Agy token for proxied access. Other providers unchanged. Pricing APPROXIMATE.",
5
5
  "models": [
6
6
  {
7
7
  "id": "deepseek-v4-flash",
@@ -227,42 +227,42 @@
227
227
  "supports_vision": false
228
228
  },
229
229
  {
230
- "id": "gemini-2.5-flash",
231
- "name": "Gemini 2.5 Flash",
230
+ "id": "gemini-3.5-flash-high",
231
+ "name": "Gemini 3.5 Flash (High)",
232
232
  "provider": "google",
233
- "tier": "balanced",
233
+ "tier": "fast",
234
234
  "context_window": 1048576,
235
235
  "max_output_tokens": 65536,
236
- "input_price_per_million": 0.3,
237
- "output_price_per_million": 2.5,
236
+ "input_price_per_million": 0.5,
237
+ "output_price_per_million": 3.0,
238
238
  "reasoning": true,
239
239
  "thinking_type": "enabled",
240
- "supports_effort": false,
241
- "default_reasoning_effort": null,
242
- "description": "Google Gemini 2.5 Flash via Generative Language API (AI Studio key — console aistudio.google.com/app/apikey, or stored Google OAuth). Multimodal, 1M context, thinking-capable. Pricing per ai.google.dev/pricing (2026-06-04).",
243
- "aliases": ["gemini-flash", "gemini-2.5-flash-latest"],
240
+ "supports_effort": true,
241
+ "default_reasoning_effort": "high",
242
+ "description": "Agy-supported Gemini 3.5 Flash High via Agy OAuth (cloudcode-pa backend, not public Generative Language). Multimodal, 1M context. Matches official agy cli model list.",
243
+ "aliases": ["gemini-3.5-flash"],
244
244
  "supports_vision": true
245
245
  },
246
246
  {
247
- "id": "gemini-2.5-pro",
248
- "name": "Gemini 2.5 Pro",
247
+ "id": "gemini-3.5-flash-medium",
248
+ "name": "Gemini 3.5 Flash (Medium)",
249
249
  "provider": "google",
250
- "tier": "premium",
250
+ "tier": "balanced",
251
251
  "context_window": 1048576,
252
252
  "max_output_tokens": 65536,
253
- "input_price_per_million": 1.25,
254
- "output_price_per_million": 10.0,
253
+ "input_price_per_million": 0.5,
254
+ "output_price_per_million": 3.0,
255
255
  "reasoning": true,
256
256
  "thinking_type": "enabled",
257
- "supports_effort": false,
258
- "default_reasoning_effort": null,
259
- "description": "Google Gemini 2.5 Pro via Generative Language API (AI Studio key or stored Google OAuth). Most advanced 2.5 reasoning model, multimodal, 1M context. Pricing per ai.google.dev/pricing (2026-06-04).",
260
- "aliases": ["gemini-pro", "gemini-2.5-pro-latest"],
257
+ "supports_effort": true,
258
+ "default_reasoning_effort": "medium",
259
+ "description": "Agy-supported Gemini 3.5 Flash Medium via Agy OAuth (cloudcode-pa). Multimodal, 1M context. Matches official agy cli model list.",
260
+ "aliases": ["gemini-3.5-flash-med"],
261
261
  "supports_vision": true
262
262
  },
263
263
  {
264
- "id": "gemini-3.5-flash",
265
- "name": "Gemini 3.5 Flash",
264
+ "id": "gemini-3.5-flash-low",
265
+ "name": "Gemini 3.5 Flash (Low)",
266
266
  "provider": "google",
267
267
  "tier": "fast",
268
268
  "context_window": 1048576,
@@ -271,32 +271,32 @@
271
271
  "output_price_per_million": 3.0,
272
272
  "reasoning": true,
273
273
  "thinking_type": "enabled",
274
- "supports_effort": false,
275
- "default_reasoning_effort": null,
276
- "description": "Google Gemini 3.5 Flash via Generative Language API. Frontier agentic/coding flash model, multimodal, 1M context, thinking-capable. Pricing APPROXIMATE (verify at ai.google.dev/pricing before cost-sensitive use).",
277
- "aliases": ["gemini-flash-3.5"],
274
+ "supports_effort": true,
275
+ "default_reasoning_effort": "low",
276
+ "description": "Agy-supported Gemini 3.5 Flash Low via Agy OAuth (cloudcode-pa). Multimodal, 1M context. Matches official agy cli model list.",
277
+ "aliases": ["gemini-3.5-flash-low"],
278
278
  "supports_vision": true
279
279
  },
280
280
  {
281
- "id": "gemini-3.1-flash-lite",
282
- "name": "Gemini 3.1 Flash-Lite",
281
+ "id": "gemini-3.1-pro-high",
282
+ "name": "Gemini 3.1 Pro (High)",
283
283
  "provider": "google",
284
- "tier": "fast",
284
+ "tier": "premium",
285
285
  "context_window": 1048576,
286
286
  "max_output_tokens": 65536,
287
- "input_price_per_million": 0.15,
288
- "output_price_per_million": 0.6,
287
+ "input_price_per_million": 2.0,
288
+ "output_price_per_million": 12.0,
289
289
  "reasoning": true,
290
290
  "thinking_type": "enabled",
291
- "supports_effort": false,
292
- "default_reasoning_effort": null,
293
- "description": "Google Gemini 3.1 Flash-Lite via Generative Language API. Cheapest multimodal Gemini, 1M context, limited thinking. Pricing APPROXIMATE (verify at ai.google.dev/pricing before cost-sensitive use).",
294
- "aliases": ["gemini-flash-lite"],
291
+ "supports_effort": true,
292
+ "default_reasoning_effort": "high",
293
+ "description": "Agy-supported Gemini 3.1 Pro High via Agy OAuth (cloudcode-pa backend). Strongest agentic Gemini. Matches official agy cli model list. Pricing approximate.",
294
+ "aliases": ["gemini-3.1-pro", "gemini-3.1-pro-preview"],
295
295
  "supports_vision": true
296
296
  },
297
297
  {
298
- "id": "gemini-3.1-pro-preview",
299
- "name": "Gemini 3.1 Pro Preview",
298
+ "id": "gemini-3.1-pro-low",
299
+ "name": "Gemini 3.1 Pro (Low)",
300
300
  "provider": "google",
301
301
  "tier": "premium",
302
302
  "context_window": 1048576,
@@ -305,12 +305,80 @@
305
305
  "output_price_per_million": 12.0,
306
306
  "reasoning": true,
307
307
  "thinking_type": "enabled",
308
+ "supports_effort": true,
309
+ "default_reasoning_effort": "low",
310
+ "description": "Agy-supported Gemini 3.1 Pro Low via Agy OAuth (cloudcode-pa). Matches official agy cli model list.",
311
+ "aliases": ["gemini-3.1-pro-low"],
312
+ "supports_vision": true
313
+ },
314
+ {
315
+ "id": "gemini-3-flash",
316
+ "name": "Gemini 3 Flash",
317
+ "provider": "google",
318
+ "tier": "fast",
319
+ "context_window": 1048576,
320
+ "max_output_tokens": 65536,
321
+ "input_price_per_million": 0.3,
322
+ "output_price_per_million": 2.0,
323
+ "reasoning": true,
324
+ "thinking_type": "enabled",
308
325
  "supports_effort": false,
309
326
  "default_reasoning_effort": null,
310
- "description": "Google Gemini 3.1 Pro Preview via Generative Language API. Strongest agentic/reasoning Gemini, multimodal, 1M context. Preview model — id/pricing may change; pricing APPROXIMATE (verify at ai.google.dev/pricing).",
311
- "aliases": ["gemini-pro-preview"],
327
+ "description": "Agy-supported Gemini 3 Flash via Agy OAuth (cloudcode-pa). Multimodal. Matches official agy cli model list.",
328
+ "aliases": ["gemini-3-flash"],
329
+ "supports_vision": true
330
+ },
331
+ {
332
+ "id": "claude-sonnet-4.6-thinking",
333
+ "name": "Claude Sonnet 4.6 (Thinking)",
334
+ "provider": "google",
335
+ "tier": "premium",
336
+ "context_window": 200000,
337
+ "max_output_tokens": 32000,
338
+ "input_price_per_million": 3.0,
339
+ "output_price_per_million": 15.0,
340
+ "reasoning": true,
341
+ "thinking_type": "enabled",
342
+ "supports_effort": true,
343
+ "default_reasoning_effort": "medium",
344
+ "description": "Agy-supported Claude Sonnet 4.6 Thinking via Agy OAuth (cloudcode-pa backend proxy). Matches official agy cli model list.",
345
+ "aliases": ["claude-sonnet-4-6-thinking", "claude-4.6-sonnet-thinking"],
346
+ "supports_vision": true
347
+ },
348
+ {
349
+ "id": "claude-opus-4.6-thinking",
350
+ "name": "Claude Opus 4.6 (Thinking)",
351
+ "provider": "google",
352
+ "tier": "premium",
353
+ "context_window": 200000,
354
+ "max_output_tokens": 32000,
355
+ "input_price_per_million": 15.0,
356
+ "output_price_per_million": 75.0,
357
+ "reasoning": true,
358
+ "thinking_type": "enabled",
359
+ "supports_effort": true,
360
+ "default_reasoning_effort": "medium",
361
+ "description": "Agy-supported Claude Opus 4.6 Thinking via Agy OAuth (cloudcode-pa backend proxy). Matches official agy cli model list.",
362
+ "aliases": ["claude-opus-4-6-thinking", "claude-4.6-opus-thinking"],
312
363
  "supports_vision": true
313
364
  },
365
+ {
366
+ "id": "gpt-oss-120b-medium",
367
+ "name": "GPT-OSS 120B (Medium)",
368
+ "provider": "google",
369
+ "tier": "balanced",
370
+ "context_window": 128000,
371
+ "max_output_tokens": 32000,
372
+ "input_price_per_million": 0.2,
373
+ "output_price_per_million": 0.8,
374
+ "reasoning": true,
375
+ "thinking_type": "enabled",
376
+ "supports_effort": true,
377
+ "default_reasoning_effort": "medium",
378
+ "description": "Agy-supported GPT-OSS 120B Medium via Agy OAuth (cloudcode-pa). Matches official agy cli model list.",
379
+ "aliases": ["gpt-oss-120b", "gpt-oss-medium"],
380
+ "supports_vision": false
381
+ },
314
382
  {
315
383
  "id": "grok-4.3",
316
384
  "name": "Grok 4.3 (xAI)",
@@ -169,9 +169,9 @@ async function checkBrainEmptiness() {
169
169
  // Count ee_injection events with event_subtype='no_match' in last 30 days
170
170
  const cutoff = new Date(Date.now() - 30 * 86_400_000).toISOString();
171
171
  const row = db
172
- .prepare(`SELECT COUNT(*) as cnt FROM interaction_logs
173
- WHERE event_type = 'ee_injection'
174
- AND event_subtype = 'no_match'
172
+ .prepare(`SELECT COUNT(*) as cnt FROM interaction_logs
173
+ WHERE event_type = 'ee_injection'
174
+ AND event_subtype = 'no_match'
175
175
  AND created_at >= ?`)
176
176
  .get(cutoff);
177
177
  const noMatchCount = row?.cnt ?? 0;
@@ -293,10 +293,10 @@ async function checkCouncilMcpNudge() {
293
293
  // 2. Query DB for [Council Memory] records with URL or research topics
294
294
  const db = getDatabase();
295
295
  const rows = db
296
- .prepare(`SELECT message_json FROM messages
297
- WHERE role = 'system'
298
- AND message_json LIKE '%[Council Memory]%'
299
- ORDER BY created_at DESC
296
+ .prepare(`SELECT message_json FROM messages
297
+ WHERE role = 'system'
298
+ AND message_json LIKE '%[Council Memory]%'
299
+ ORDER BY created_at DESC
300
300
  LIMIT 50`)
301
301
  .all();
302
302
  let qualifyingCount = 0;
@@ -0,0 +1,142 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+ import { blockingErrorsForFile, buildFileListSubject, isAutoCommitEnabled, isCliArtifactPath, isCommitGateEnabled, isExcludedPath, isSensitivePath, parsePorcelainPaths, pathsForCommitGate, splitCommitMessage, } from "../auto-commit.js";
7
+ function diag(severity) {
8
+ return {
9
+ message: "x",
10
+ severity,
11
+ range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } },
12
+ };
13
+ }
14
+ function diagFile(filePath, severities) {
15
+ return { filePath, serverId: "ts", diagnostics: severities.map(diag) };
16
+ }
17
+ describe("auto-commit pure helpers", () => {
18
+ it("parses porcelain output incl. rename + quoted paths", () => {
19
+ const out = ' M src/a.ts\n?? new file.txt\nR old.ts -> src/b.ts\nA "with space.ts"\n';
20
+ const set = parsePorcelainPaths(out);
21
+ expect(set.has("src/a.ts")).toBe(true);
22
+ expect(set.has("new file.txt")).toBe(true);
23
+ // rename keeps ONLY the new path
24
+ expect(set.has("src/b.ts")).toBe(true);
25
+ expect(set.has("old.ts")).toBe(false);
26
+ // quoted path with a space is unquoted
27
+ expect(set.has("with space.ts")).toBe(true);
28
+ });
29
+ it("flags sensitive paths and lets normal source through", () => {
30
+ expect(isSensitivePath(".env")).toBe(true);
31
+ expect(isSensitivePath(".env.local")).toBe(true);
32
+ expect(isSensitivePath("config/app.key")).toBe(true);
33
+ expect(isSensitivePath("certs/server.pem")).toBe(true);
34
+ expect(isSensitivePath("src/db-secret.ts")).toBe(true);
35
+ // .muonroi-cli/ is categorized as a CLI artifact (excluded), not a "secret".
36
+ expect(isSensitivePath(".muonroi-cli/state.json")).toBe(false);
37
+ expect(isExcludedPath(".muonroi-cli/state.json")).toBe(true);
38
+ expect(isSensitivePath("src/index.ts")).toBe(false);
39
+ expect(isSensitivePath("README.md")).toBe(false);
40
+ });
41
+ it("excludes CLI-generated artifacts + build junk (the 11-file-leak fix)", () => {
42
+ expect(isCliArtifactPath(".muonroi-flow/state.md")).toBe(true);
43
+ expect(isCliArtifactPath(".muonroi-flow/runs/abc/manifest.md")).toBe(true);
44
+ expect(isCliArtifactPath(".muonroi-cli/session.db")).toBe(true);
45
+ expect(isCliArtifactPath(".muonroi-harness-roots.json")).toBe(true);
46
+ expect(isCliArtifactPath("node_modules/x/index.js")).toBe(true);
47
+ expect(isCliArtifactPath("dist/bundle.js")).toBe(true);
48
+ expect(isCliArtifactPath("debug.log")).toBe(true);
49
+ expect(isCliArtifactPath("src/greeting.txt")).toBe(false);
50
+ // isExcludedPath unions secrets + artifacts
51
+ expect(isExcludedPath(".env")).toBe(true);
52
+ expect(isExcludedPath(".muonroi-flow/state.md")).toBe(true);
53
+ expect(isExcludedPath("greeting.txt")).toBe(false);
54
+ });
55
+ it("backstop subject lists files + stays bounded (never the raw prompt)", () => {
56
+ expect(buildFileListSubject(["src/a.ts", "src/b.ts"])).toBe("chore: update 2 file(s) — a.ts, b.ts");
57
+ expect(buildFileListSubject(["a.ts", "b.ts", "c.ts", "d.ts", "e.ts"])).toContain("+2 more");
58
+ expect(buildFileListSubject(Array.from({ length: 20 }, (_, i) => `file${i}.ts`)).length).toBeLessThanOrEqual(72);
59
+ });
60
+ it("splits an agent-authored message: subject <=72, body kept, attribution deduped", () => {
61
+ expect(splitCommitMessage("feat: add X\r\n\r\nbody line")).toEqual({ subject: "feat: add X", body: "body line" });
62
+ expect(splitCommitMessage("x".repeat(200)).subject.length).toBeLessThanOrEqual(72);
63
+ // an attribution the agent already added is dropped (we append exactly one)
64
+ expect(splitCommitMessage("feat: y\nCoding by - Muonroi-CLI")).toEqual({ subject: "feat: y", body: "" });
65
+ });
66
+ it("is disabled under the unit-test runner so the suite never commits", () => {
67
+ // VITEST is set while this runs → must be false regardless of MUONROI_AUTO_COMMIT.
68
+ expect(isAutoCommitEnabled()).toBe(false);
69
+ });
70
+ });
71
+ describe("G1 commit quality gate", () => {
72
+ it("commit gate is disabled under the unit-test runner (specs commit fixtures freely)", () => {
73
+ // Mirrors isAutoCommitEnabled — VITEST is set, so the gate must be off here.
74
+ expect(isCommitGateEnabled()).toBe(false);
75
+ });
76
+ describe("blockingErrorsForFile — errors-only + per-file scope", () => {
77
+ it("returns severity-1 (error) diagnostics for the staged file", () => {
78
+ const abs = resolve("foo.ts");
79
+ const errs = blockingErrorsForFile([diagFile("foo.ts", [1, 1])], abs);
80
+ expect(errs).toHaveLength(2);
81
+ });
82
+ it("treats a missing severity as an error (LSP default is 1)", () => {
83
+ const abs = resolve("foo.ts");
84
+ expect(blockingErrorsForFile([diagFile("foo.ts", [undefined])], abs)).toHaveLength(1);
85
+ });
86
+ it("ignores warnings/infos (severity >= 2) — they never block a commit", () => {
87
+ const abs = resolve("foo.ts");
88
+ expect(blockingErrorsForFile([diagFile("foo.ts", [2, 3, 4])], abs)).toHaveLength(0);
89
+ });
90
+ it("ignores errors reported against a DIFFERENT file (cross-file breakage doesn't block)", () => {
91
+ const abs = resolve("foo.ts");
92
+ // An error on other.ts must not block a commit of foo.ts.
93
+ expect(blockingErrorsForFile([diagFile("other.ts", [1, 1])], abs)).toHaveLength(0);
94
+ });
95
+ it("mixes: keeps only this file's errors out of a multi-file, multi-severity set", () => {
96
+ const abs = resolve("foo.ts");
97
+ const errs = blockingErrorsForFile([diagFile("foo.ts", [1, 2, 3]), diagFile("bar.ts", [1, 1])], abs);
98
+ expect(errs).toHaveLength(1); // only foo.ts's single severity-1
99
+ });
100
+ it("empty diagnostics → no blockers (non-source / no-LSP-server files pass)", () => {
101
+ expect(blockingErrorsForFile([], resolve("README.md"))).toEqual([]);
102
+ });
103
+ });
104
+ });
105
+ describe("pathsForCommitGate — bash `git commit` gate path set (real git)", () => {
106
+ let dir;
107
+ const g = (args) => execFileSync("git", args, { cwd: dir, stdio: "pipe" });
108
+ beforeEach(() => {
109
+ dir = mkdtempSync(join(tmpdir(), "commit-gate-"));
110
+ g(["init", "-q"]);
111
+ g(["config", "user.email", "t@t.t"]);
112
+ g(["config", "user.name", "t"]);
113
+ });
114
+ afterEach(() => rmSync(dir, { recursive: true, force: true }));
115
+ it("returns only the already-staged set for a plain `git commit`", async () => {
116
+ writeFileSync(join(dir, "a.ts"), "export const a = 1;\n");
117
+ writeFileSync(join(dir, "b.ts"), "export const b = 2;\n");
118
+ g(["add", "a.ts"]); // b.ts left untracked
119
+ const paths = await pathsForCommitGate(dir, { broadAdd: false, commitAll: false });
120
+ expect(paths).toEqual(["a.ts"]);
121
+ });
122
+ it("adds tracked modifications that `git commit -a` will auto-stage", async () => {
123
+ writeFileSync(join(dir, "a.ts"), "export const a = 1;\n");
124
+ g(["add", "a.ts"]);
125
+ g(["commit", "-qm", "init", "--no-verify"]); // temp fixture repo — hook-free by design
126
+ writeFileSync(join(dir, "a.ts"), "export const a = 99;\n"); // modify tracked, NOT re-staged
127
+ // plain commit sees nothing staged; commit -a includes the modification.
128
+ expect(await pathsForCommitGate(dir, { broadAdd: false, commitAll: false })).toEqual([]);
129
+ expect(await pathsForCommitGate(dir, { broadAdd: false, commitAll: true })).toContain("a.ts");
130
+ });
131
+ it("includes the whole working tree for a chained broad `git add -A` (add not yet run)", async () => {
132
+ writeFileSync(join(dir, "a.ts"), "export const a = 1;\n"); // untracked
133
+ writeFileSync(join(dir, "c.ts"), "export const c = 3;\n"); // untracked
134
+ // Nothing staged at pre-exec time → plain set is empty…
135
+ expect(await pathsForCommitGate(dir, { broadAdd: false, commitAll: false })).toEqual([]);
136
+ // …but `git add -A` would stage both, so the broad path enumerates them.
137
+ const broad = await pathsForCommitGate(dir, { broadAdd: true, commitAll: false });
138
+ expect(broad).toContain("a.ts");
139
+ expect(broad).toContain("c.ts");
140
+ });
141
+ });
142
+ //# sourceMappingURL=auto-commit.test.js.map
@@ -56,6 +56,7 @@ function makeDeps(overrides = {}) {
56
56
  runDelegation: async () => ({ success: true, output: "" }),
57
57
  readDelegation: async () => ({ success: true, output: "" }),
58
58
  listDelegations: async () => ({ success: true, output: "" }),
59
+ killDelegation: async () => ({ success: true, output: "" }),
59
60
  executeBatchToolCall: async (_tools, _call) => ({
60
61
  input: {},
61
62
  result: { success: true, output: "" },
@@ -78,6 +79,7 @@ function makeArgs(signal, providerStub) {
78
79
  const testProviders = getTestProviders();
79
80
  return {
80
81
  userModelMessage: { role: "user", content: "hello" },
82
+ userEnrichedMessage: { role: "user", content: "hello" },
81
83
  observer: undefined,
82
84
  provider: (providerStub ?? {}),
83
85
  subagents: [],
@@ -0,0 +1,96 @@
1
+ import { createHash } from "crypto";
2
+ import { promises as fs } from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import { describe, expect, it } from "vitest";
6
+ import { DelegationManager } from "../delegations.js";
7
+ function getProjectId(cwd) {
8
+ const base = path
9
+ .basename(cwd)
10
+ .replace(/[^a-zA-Z0-9._-]+/g, "-")
11
+ .replace(/^-+|-+$/g, "") || "project";
12
+ const hash = createHash("sha1").update(cwd).digest("hex").slice(0, 10);
13
+ return `${base}-${hash}`;
14
+ }
15
+ async function getDelegationsDir(cwd) {
16
+ const projectId = getProjectId(cwd);
17
+ return path.join(os.homedir(), ".muonroi-cli", "delegations", projectId);
18
+ }
19
+ describe("DelegationManager.kill", () => {
20
+ it("terminates a running delegation and updates its status to error", async () => {
21
+ const tempCwd = path.join(os.tmpdir(), "muonroi-test-" + Math.random().toString(36).slice(2));
22
+ const manager = new DelegationManager(() => tempCwd);
23
+ const dir = await getDelegationsDir(tempCwd);
24
+ await fs.mkdir(dir, { recursive: true });
25
+ const id = "test-amber-panda";
26
+ const jobPath = path.join(dir, `${id}.json`);
27
+ const outputPath = path.join(dir, `${id}.md`);
28
+ // Create a mock running delegation
29
+ const record = {
30
+ id,
31
+ agent: "explore",
32
+ description: "test delegation",
33
+ prompt: "noop",
34
+ cwd: tempCwd,
35
+ model: "grok-4",
36
+ sandboxMode: "off",
37
+ maxToolRounds: 10,
38
+ maxTokens: 4000,
39
+ status: "running",
40
+ startedAt: new Date().toISOString(),
41
+ outputPath,
42
+ pid: 99999, // dummy PID
43
+ };
44
+ await fs.writeFile(jobPath, JSON.stringify(record, null, 2), "utf8");
45
+ // Call cancel
46
+ const result = await manager.kill(id);
47
+ expect(result.success).toBe(true);
48
+ expect(result.output).toContain(`Delegation "${id}" (PID: 99999) has been terminated.`);
49
+ // Verify the record was updated
50
+ const updatedRaw = await fs.readFile(jobPath, "utf8");
51
+ const updated = JSON.parse(updatedRaw);
52
+ expect(updated.status).toBe("error");
53
+ expect(updated.error).toBe("Cancelled by user.");
54
+ // Cleanup
55
+ await fs.rm(jobPath, { force: true });
56
+ await fs.rm(outputPath, { force: true });
57
+ await fs.rm(dir, { recursive: true, force: true });
58
+ });
59
+ it("returns failure if the delegation ID is not found", async () => {
60
+ const tempCwd = path.join(os.tmpdir(), "muonroi-test-" + Math.random().toString(36).slice(2));
61
+ const manager = new DelegationManager(() => tempCwd);
62
+ const result = await manager.kill("non-existent-id");
63
+ expect(result.success).toBe(false);
64
+ expect(result.output).toContain('Delegation "non-existent-id" not found.');
65
+ });
66
+ it("returns failure if the delegation is already finished", async () => {
67
+ const tempCwd = path.join(os.tmpdir(), "muonroi-test-" + Math.random().toString(36).slice(2));
68
+ const manager = new DelegationManager(() => tempCwd);
69
+ const dir = await getDelegationsDir(tempCwd);
70
+ await fs.mkdir(dir, { recursive: true });
71
+ const id = "test-steady-owl";
72
+ const jobPath = path.join(dir, `${id}.json`);
73
+ const record = {
74
+ id,
75
+ agent: "explore",
76
+ description: "test delegation",
77
+ prompt: "noop",
78
+ cwd: tempCwd,
79
+ model: "grok-4",
80
+ sandboxMode: "off",
81
+ maxToolRounds: 10,
82
+ maxTokens: 4000,
83
+ status: "complete",
84
+ startedAt: new Date().toISOString(),
85
+ outputPath: path.join(dir, `${id}.md`),
86
+ };
87
+ await fs.writeFile(jobPath, JSON.stringify(record, null, 2), "utf8");
88
+ const result = await manager.kill(id);
89
+ expect(result.success).toBe(false);
90
+ expect(result.output).toContain(`Delegation "${id}" is not running (status: complete).`);
91
+ // Cleanup
92
+ await fs.rm(jobPath, { force: true });
93
+ await fs.rm(dir, { recursive: true, force: true });
94
+ });
95
+ });
96
+ //# sourceMappingURL=delegations.test.js.map
@@ -99,6 +99,7 @@ function makeDeps(overrides = {}) {
99
99
  runDelegation: async () => ({ success: true, output: "" }),
100
100
  readDelegation: async () => ({ success: true, output: "" }),
101
101
  listDelegations: async () => ({ success: true, output: "" }),
102
+ killDelegation: async () => ({ success: true, output: "" }),
102
103
  appendCompletedTurn: () => { },
103
104
  discardAbortedTurn: () => { },
104
105
  recordUsage: () => { },
@@ -169,5 +169,34 @@ describe("StreamRunner — DI surface", () => {
169
169
  const b = new StreamRunner(makeDeps());
170
170
  expect(a).not.toBe(b);
171
171
  });
172
+ describe("StreamRunner — maxSteps custom overrides", () => {
173
+ it("respects default maxSteps logic when request.maxToolRounds is undefined", async () => {
174
+ const deps = makeDeps({ getMaxToolRounds: () => 50 });
175
+ const runner = new StreamRunner(deps);
176
+ const outcome = await runner.setup({
177
+ agent: "explore",
178
+ description: "test",
179
+ prompt: "test",
180
+ });
181
+ expect(outcome.kind).toBe("prepared");
182
+ if (outcome.kind === "prepared") {
183
+ expect(outcome.prepared.maxSteps).toBe(100); // deps.getMaxToolRounds() * 2
184
+ }
185
+ });
186
+ it("uses request.maxToolRounds when it is explicitly defined", async () => {
187
+ const deps = makeDeps({ getMaxToolRounds: () => 50 });
188
+ const runner = new StreamRunner(deps);
189
+ const outcome = await runner.setup({
190
+ agent: "explore",
191
+ description: "test",
192
+ prompt: "test",
193
+ maxToolRounds: 75,
194
+ });
195
+ expect(outcome.kind).toBe("prepared");
196
+ if (outcome.kind === "prepared") {
197
+ expect(outcome.prepared.maxSteps).toBe(75);
198
+ }
199
+ });
200
+ });
172
201
  });
173
202
  //# sourceMappingURL=stream-runner.test.js.map
@@ -76,13 +76,16 @@ describe("Agent class", { timeout: 30_000 }, () => {
76
76
  agent.setModel("claude-sonnet-4-6-20250514");
77
77
  expect(agent.getModel()).toBe("claude-sonnet-4-6-20250514");
78
78
  });
79
- it("constructs with sandbox mode", async () => {
79
+ it("constructs with sandbox mode — always returns 'off' (sandbox removed)", async () => {
80
80
  const { Agent } = await importAgentModule();
81
+ // Sandbox has been removed; passing sandboxMode: "shuru" is accepted for
82
+ // back-compat but getSandboxMode() always returns "off" until a new
83
+ // sandbox implementation is wired in.
81
84
  const agent = new Agent(undefined, undefined, undefined, undefined, {
82
85
  persistSession: false,
83
86
  sandboxMode: "shuru",
84
87
  });
85
- expect(agent.getSandboxMode()).toBe("shuru");
88
+ expect(agent.getSandboxMode()).toBe("off");
86
89
  });
87
90
  it("defaults sandbox mode to off", async () => {
88
91
  const { Agent } = await importAgentModule();