muonroi-cli 1.8.3 → 1.8.5

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 (477) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +133 -122
  3. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  4. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  9. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  10. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  12. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  14. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  16. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  17. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  18. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  19. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  21. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  22. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  23. package/dist/src/agent-harness/mock-model.js +63 -1
  24. package/dist/src/agent-harness/test-spawn.js +31 -0
  25. package/dist/src/cli/config/screen-providers.js +1 -1
  26. package/dist/src/cli/cost-forensics.d.ts +10 -0
  27. package/dist/src/cli/cost-forensics.js +30 -15
  28. package/dist/src/cli/keys-bundle.d.ts +1 -1
  29. package/dist/src/cli/keys-bundle.js +1 -1
  30. package/dist/src/cli/keys.d.ts +2 -2
  31. package/dist/src/cli/keys.js +19 -81
  32. package/dist/src/council/clarifier.d.ts +28 -2
  33. package/dist/src/council/clarifier.js +81 -15
  34. package/dist/src/council/context.js +49 -15
  35. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  36. package/dist/src/council/debate-checkpoint.js +176 -0
  37. package/dist/src/council/debate-planner.js +51 -3
  38. package/dist/src/council/debate-summary.d.ts +25 -0
  39. package/dist/src/council/debate-summary.js +85 -0
  40. package/dist/src/council/debate.d.ts +169 -2
  41. package/dist/src/council/debate.js +1210 -134
  42. package/dist/src/council/index.d.ts +85 -1
  43. package/dist/src/council/index.js +634 -196
  44. package/dist/src/council/leader.d.ts +26 -0
  45. package/dist/src/council/leader.js +150 -9
  46. package/dist/src/council/llm.d.ts +32 -0
  47. package/dist/src/council/llm.js +231 -38
  48. package/dist/src/council/panel-select.d.ts +30 -0
  49. package/dist/src/council/panel-select.js +72 -0
  50. package/dist/src/council/planner.js +23 -0
  51. package/dist/src/council/preflight.d.ts +7 -0
  52. package/dist/src/council/preflight.js +14 -2
  53. package/dist/src/council/prompts.d.ts +30 -3
  54. package/dist/src/council/prompts.js +254 -84
  55. package/dist/src/council/stance-recall.d.ts +42 -0
  56. package/dist/src/council/stance-recall.js +57 -0
  57. package/dist/src/council/strip-think.d.ts +17 -0
  58. package/dist/src/council/strip-think.js +33 -0
  59. package/dist/src/council/types.d.ts +128 -0
  60. package/dist/src/ee/artifact-cache.d.ts +16 -0
  61. package/dist/src/ee/artifact-cache.js +32 -0
  62. package/dist/src/ee/auth.d.ts +1 -0
  63. package/dist/src/ee/auth.js +15 -2
  64. package/dist/src/ee/bridge.d.ts +10 -0
  65. package/dist/src/ee/bridge.js +58 -0
  66. package/dist/src/ee/client.js +81 -18
  67. package/dist/src/ee/export-transcripts.d.ts +1 -0
  68. package/dist/src/ee/export-transcripts.js +8 -10
  69. package/dist/src/ee/extract-session.js +29 -0
  70. package/dist/src/ee/extract-style.d.ts +58 -0
  71. package/dist/src/ee/extract-style.js +270 -0
  72. package/dist/src/ee/recall-ledger.d.ts +9 -0
  73. package/dist/src/ee/recall-ledger.js +3 -0
  74. package/dist/src/ee/scope.d.ts +1 -0
  75. package/dist/src/ee/scope.js +26 -1
  76. package/dist/src/ee/search.d.ts +7 -0
  77. package/dist/src/ee/search.js +24 -0
  78. package/dist/src/ee/transcript-emit.js +2 -0
  79. package/dist/src/ee/types.d.ts +22 -0
  80. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  81. package/dist/src/ee/who-am-i-brain.js +220 -0
  82. package/dist/src/ee/who-am-i.d.ts +10 -3
  83. package/dist/src/ee/who-am-i.js +12 -0
  84. package/dist/src/ee/workflow-event.d.ts +48 -0
  85. package/dist/src/ee/workflow-event.js +81 -0
  86. package/dist/src/flow/compaction/compress.d.ts +3 -3
  87. package/dist/src/flow/compaction/compress.js +45 -8
  88. package/dist/src/flow/compaction/extract.d.ts +4 -7
  89. package/dist/src/flow/compaction/extract.js +50 -10
  90. package/dist/src/flow/compaction/index.d.ts +13 -1
  91. package/dist/src/flow/compaction/index.js +70 -3
  92. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  93. package/dist/src/flow/compaction/input-guard.js +43 -0
  94. package/dist/src/flow/fold-planning.d.ts +36 -0
  95. package/dist/src/flow/fold-planning.js +83 -0
  96. package/dist/src/flow/hierarchy.d.ts +146 -0
  97. package/dist/src/flow/hierarchy.js +427 -0
  98. package/dist/src/flow/index.d.ts +1 -0
  99. package/dist/src/flow/index.js +2 -0
  100. package/dist/src/flow/run-artifacts.d.ts +102 -0
  101. package/dist/src/flow/run-artifacts.js +208 -0
  102. package/dist/src/generated/version.d.ts +1 -1
  103. package/dist/src/generated/version.js +1 -1
  104. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  105. package/dist/src/gsd/assessment-schema.js +134 -0
  106. package/dist/src/gsd/capability-registry.d.ts +45 -0
  107. package/dist/src/gsd/capability-registry.js +337 -0
  108. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  109. package/dist/src/gsd/complexity-assessor.js +152 -0
  110. package/dist/src/gsd/config-bridge.d.ts +7 -0
  111. package/dist/src/gsd/config-bridge.js +114 -0
  112. package/dist/src/gsd/config-loader.d.ts +27 -0
  113. package/dist/src/gsd/config-loader.js +50 -0
  114. package/dist/src/gsd/council-context.d.ts +44 -0
  115. package/dist/src/gsd/council-context.js +114 -0
  116. package/dist/src/gsd/ee-closure.d.ts +28 -0
  117. package/dist/src/gsd/ee-closure.js +49 -0
  118. package/dist/src/gsd/flags.d.ts +55 -0
  119. package/dist/src/gsd/flags.js +83 -0
  120. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  121. package/dist/src/gsd/gsd-dispatch.js +131 -0
  122. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  123. package/dist/src/gsd/gsd-runtime.js +37 -0
  124. package/dist/src/gsd/host-adapter.d.ts +11 -0
  125. package/dist/src/gsd/host-adapter.js +29 -0
  126. package/dist/src/gsd/index.d.ts +24 -1
  127. package/dist/src/gsd/index.js +27 -0
  128. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  129. package/dist/src/gsd/loop-host-contract.js +39 -0
  130. package/dist/src/gsd/loop-host.d.ts +69 -0
  131. package/dist/src/gsd/loop-host.js +245 -0
  132. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  133. package/dist/src/gsd/loop-resolver.js +79 -0
  134. package/dist/src/gsd/model-tier.d.ts +13 -0
  135. package/dist/src/gsd/model-tier.js +45 -0
  136. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  137. package/dist/src/gsd/mutation-gate.js +41 -0
  138. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  139. package/dist/src/gsd/native-roadmap.js +343 -0
  140. package/dist/src/gsd/native-state.d.ts +47 -0
  141. package/dist/src/gsd/native-state.js +220 -0
  142. package/dist/src/gsd/paths.d.ts +23 -0
  143. package/dist/src/gsd/paths.js +66 -0
  144. package/dist/src/gsd/phase-dag.d.ts +12 -0
  145. package/dist/src/gsd/phase-dag.js +94 -0
  146. package/dist/src/gsd/phase-sync.d.ts +42 -0
  147. package/dist/src/gsd/phase-sync.js +321 -0
  148. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  149. package/dist/src/gsd/pil-gate-context.js +64 -0
  150. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  151. package/dist/src/gsd/pil-gate-critic.js +74 -0
  152. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  153. package/dist/src/gsd/plan-council-prompts.js +79 -0
  154. package/dist/src/gsd/plan-council.d.ts +44 -0
  155. package/dist/src/gsd/plan-council.js +251 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  157. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  158. package/dist/src/gsd/product-workspace.d.ts +13 -0
  159. package/dist/src/gsd/product-workspace.js +124 -0
  160. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  161. package/dist/src/gsd/ship-bridge.js +65 -0
  162. package/dist/src/gsd/state-document.d.ts +40 -0
  163. package/dist/src/gsd/state-document.js +163 -0
  164. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  165. package/dist/src/gsd/verdict-schema.js +144 -0
  166. package/dist/src/gsd/verify-context.d.ts +22 -0
  167. package/dist/src/gsd/verify-context.js +27 -0
  168. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  169. package/dist/src/gsd/verify-council-prompts.js +85 -0
  170. package/dist/src/gsd/verify-council.d.ts +25 -0
  171. package/dist/src/gsd/verify-council.js +119 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  173. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  174. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  175. package/dist/src/gsd/workflow-engine.js +207 -0
  176. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  177. package/dist/src/gsd/workflow-tools.js +277 -0
  178. package/dist/src/hooks/index.js +1 -1
  179. package/dist/src/index.js +44 -11
  180. package/dist/src/maintain/pr-builder.js +23 -13
  181. package/dist/src/mcp/auto-setup.js +57 -32
  182. package/dist/src/mcp/client-pool.js +1 -1
  183. package/dist/src/mcp/ee-tools.js +1 -0
  184. package/dist/src/mcp/oauth-callback.js +2 -2
  185. package/dist/src/mcp/research-onboarding.js +8 -7
  186. package/dist/src/mcp/runtime.js +34 -2
  187. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  188. package/dist/src/mcp/setup-guide-text.js +77 -76
  189. package/dist/src/models/catalog-client.d.ts +87 -0
  190. package/dist/src/models/catalog-client.js +105 -38
  191. package/dist/src/models/catalog.json +528 -265
  192. package/dist/src/models/registry.d.ts +22 -7
  193. package/dist/src/models/registry.js +73 -10
  194. package/dist/src/ops/doctor.js +8 -8
  195. package/dist/src/orchestrator/auto-commit.js +1 -1
  196. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  197. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  198. package/dist/src/orchestrator/cache-prefix.js +83 -0
  199. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  200. package/dist/src/orchestrator/compact-request.js +41 -0
  201. package/dist/src/orchestrator/compaction.d.ts +10 -0
  202. package/dist/src/orchestrator/compaction.js +27 -7
  203. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  204. package/dist/src/orchestrator/council-manager.js +65 -24
  205. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  206. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  207. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  208. package/dist/src/orchestrator/error-utils.js +132 -24
  209. package/dist/src/orchestrator/grounding-check.js +39 -1
  210. package/dist/src/orchestrator/message-processor.js +242 -33
  211. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  212. package/dist/src/orchestrator/orchestrator.js +651 -102
  213. package/dist/src/orchestrator/preprocessor.js +1 -1
  214. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  215. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  216. package/dist/src/orchestrator/prompts.js +159 -159
  217. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  218. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  219. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  220. package/dist/src/orchestrator/retry-classifier.js +46 -2
  221. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  222. package/dist/src/orchestrator/safety-intercept.js +55 -0
  223. package/dist/src/orchestrator/scope-reminder.js +1 -1
  224. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  225. package/dist/src/orchestrator/session-experience.js +2 -1
  226. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  227. package/dist/src/orchestrator/should-run-gate.js +18 -0
  228. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  229. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  230. package/dist/src/orchestrator/stream-runner.js +62 -29
  231. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  232. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  233. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  234. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  235. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  236. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  237. package/dist/src/orchestrator/tool-engine.d.ts +26 -0
  238. package/dist/src/orchestrator/tool-engine.js +669 -56
  239. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  240. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  241. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  242. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  243. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  244. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  245. package/dist/src/pil/agent-operating-contract.js +1 -1
  246. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  247. package/dist/src/pil/cheap-model-playbook.js +5 -1
  248. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  249. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  250. package/dist/src/pil/discovery-types.d.ts +1 -0
  251. package/dist/src/pil/discovery.js +16 -11
  252. package/dist/src/pil/layer1-intent.d.ts +18 -6
  253. package/dist/src/pil/layer1-intent.js +66 -757
  254. package/dist/src/pil/layer15-context-scan.js +15 -1
  255. package/dist/src/pil/layer2_5-ponytail.js +8 -8
  256. package/dist/src/pil/layer3-ee-injection.js +23 -8
  257. package/dist/src/pil/layer4-gsd.js +69 -16
  258. package/dist/src/pil/layer5-context.js +7 -3
  259. package/dist/src/pil/layer6-output.d.ts +23 -0
  260. package/dist/src/pil/layer6-output.js +5 -1
  261. package/dist/src/pil/llm-classify.d.ts +33 -2
  262. package/dist/src/pil/llm-classify.js +123 -131
  263. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  264. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  265. package/dist/src/pil/pipeline.js +34 -2
  266. package/dist/src/pil/response-tools.js +5 -3
  267. package/dist/src/pil/schema.d.ts +1 -0
  268. package/dist/src/pil/schema.js +2 -0
  269. package/dist/src/pil/types.d.ts +18 -0
  270. package/dist/src/playbook/directives.d.ts +4 -0
  271. package/dist/src/playbook/directives.js +17 -5
  272. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  273. package/dist/src/product-loop/backlog-builder.js +30 -6
  274. package/dist/src/product-loop/discovery-context-format.js +3 -1
  275. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  276. package/dist/src/product-loop/discovery-interview.js +32 -3
  277. package/dist/src/product-loop/discovery-schema.js +5 -1
  278. package/dist/src/product-loop/done-gate.js +3 -3
  279. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  280. package/dist/src/product-loop/ideal-trace.js +64 -0
  281. package/dist/src/product-loop/index.d.ts +13 -1
  282. package/dist/src/product-loop/index.js +333 -52
  283. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  284. package/dist/src/product-loop/loop-driver.js +327 -116
  285. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  286. package/dist/src/product-loop/phase-plan.js +39 -2
  287. package/dist/src/product-loop/phase-runner.js +9 -1
  288. package/dist/src/product-loop/progress-snapshot.js +4 -4
  289. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  290. package/dist/src/product-loop/sprint-runner.js +559 -16
  291. package/dist/src/product-loop/types.d.ts +36 -5
  292. package/dist/src/providers/adapter.d.ts +1 -1
  293. package/dist/src/providers/adapter.js +3 -4
  294. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  295. package/dist/src/providers/auth/browser-flow.js +1 -1
  296. package/dist/src/providers/auth/openai-oauth.js +1 -1
  297. package/dist/src/providers/auth/registry.js +0 -34
  298. package/dist/src/providers/auth/token-store.js +4 -1
  299. package/dist/src/providers/auth/types.d.ts +1 -1
  300. package/dist/src/providers/auth/types.js +1 -1
  301. package/dist/src/providers/capabilities.d.ts +24 -5
  302. package/dist/src/providers/capabilities.js +42 -24
  303. package/dist/src/providers/endpoints.d.ts +2 -2
  304. package/dist/src/providers/endpoints.js +11 -10
  305. package/dist/src/providers/keychain.d.ts +1 -1
  306. package/dist/src/providers/keychain.js +7 -9
  307. package/dist/src/providers/mcp-vision-bridge.js +82 -172
  308. package/dist/src/providers/openai-compatible.js +8 -1
  309. package/dist/src/providers/pricing.d.ts +2 -2
  310. package/dist/src/providers/pricing.js +3 -13
  311. package/dist/src/providers/runtime.d.ts +27 -2
  312. package/dist/src/providers/runtime.js +78 -15
  313. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  314. package/dist/src/providers/strategies/base.strategy.js +24 -1
  315. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  316. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  317. package/dist/src/providers/strategies/registry.js +4 -4
  318. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  319. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  320. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  321. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  322. package/dist/src/providers/types.d.ts +5 -6
  323. package/dist/src/providers/types.js +2 -2
  324. package/dist/src/providers/vision-backend.d.ts +47 -0
  325. package/dist/src/providers/vision-backend.js +258 -0
  326. package/dist/src/providers/vision-proxy.d.ts +22 -9
  327. package/dist/src/providers/vision-proxy.js +63 -132
  328. package/dist/src/providers/wire-debug.js +95 -0
  329. package/dist/src/reporter/index.js +1 -1
  330. package/dist/src/router/decide.d.ts +13 -0
  331. package/dist/src/router/decide.js +138 -36
  332. package/dist/src/router/peak-hour.d.ts +38 -0
  333. package/dist/src/router/peak-hour.js +107 -0
  334. package/dist/src/router/step-router.js +3 -2
  335. package/dist/src/router/warm.js +4 -5
  336. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  337. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  338. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  339. package/dist/src/scaffold/continuation-prompt.js +86 -60
  340. package/dist/src/scaffold/init-new.js +453 -453
  341. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  342. package/dist/src/scaffold/point-to-existing.js +25 -0
  343. package/dist/src/self-qa/agentic-loop.js +22 -22
  344. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  345. package/dist/src/{ui/state → state}/active-run.js +21 -0
  346. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  347. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  348. package/dist/src/state/turn-trace.d.ts +43 -0
  349. package/dist/src/state/turn-trace.js +32 -0
  350. package/dist/src/storage/db.js +2 -1
  351. package/dist/src/storage/index.d.ts +1 -1
  352. package/dist/src/storage/index.js +1 -1
  353. package/dist/src/storage/interaction-log.d.ts +1 -1
  354. package/dist/src/storage/interaction-log.js +5 -5
  355. package/dist/src/storage/migrations.js +196 -126
  356. package/dist/src/storage/session-experience-store.js +4 -4
  357. package/dist/src/storage/sessions.d.ts +28 -10
  358. package/dist/src/storage/sessions.js +112 -55
  359. package/dist/src/storage/transcript-view.js +1 -1
  360. package/dist/src/storage/transcript.d.ts +51 -0
  361. package/dist/src/storage/transcript.js +383 -112
  362. package/dist/src/storage/usage.js +14 -14
  363. package/dist/src/storage/workspaces.js +12 -12
  364. package/dist/src/tools/file.d.ts +15 -0
  365. package/dist/src/tools/file.js +32 -0
  366. package/dist/src/tools/native-tools.js +5 -0
  367. package/dist/src/tools/registry.d.ts +3 -0
  368. package/dist/src/tools/registry.js +460 -22
  369. package/dist/src/tools/research.d.ts +29 -0
  370. package/dist/src/tools/research.js +233 -0
  371. package/dist/src/types/index.d.ts +118 -3
  372. package/dist/src/ui/app.js +0 -0
  373. package/dist/src/ui/cards/product-status-card.js +1 -1
  374. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  375. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  376. package/dist/src/ui/components/context-rail.d.ts +26 -0
  377. package/dist/src/ui/components/context-rail.js +33 -0
  378. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  379. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  380. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  381. package/dist/src/ui/components/council-debate-pill.js +34 -0
  382. package/dist/src/ui/components/council-info-card.js +2 -2
  383. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  384. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  385. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  386. package/dist/src/ui/components/council-message-bubble.js +16 -15
  387. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  388. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  389. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  390. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  391. package/dist/src/ui/components/council-question-card.js +12 -12
  392. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  393. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  394. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  395. package/dist/src/ui/components/council-round-group.js +88 -0
  396. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  397. package/dist/src/ui/components/council-status-list.js +36 -24
  398. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  399. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  400. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  401. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  402. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  403. package/dist/src/ui/components/prompt-box.js +18 -16
  404. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  405. package/dist/src/ui/components/session-tree-card.js +46 -0
  406. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  407. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  408. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  409. package/dist/src/ui/components/task-list-panel.js +22 -2
  410. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  411. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  412. package/dist/src/ui/mcp-modal.js +2 -4
  413. package/dist/src/ui/modals/api-key-modal.js +1 -1
  414. package/dist/src/ui/modals/connect-modal.js +4 -3
  415. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  416. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  417. package/dist/src/ui/picker-providers.d.ts +1 -1
  418. package/dist/src/ui/picker-providers.js +1 -1
  419. package/dist/src/ui/primitives/index.d.ts +1 -0
  420. package/dist/src/ui/primitives/index.js +2 -0
  421. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  422. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  423. package/dist/src/ui/slash/compact.js +5 -7
  424. package/dist/src/ui/slash/cost.js +1 -1
  425. package/dist/src/ui/slash/council-inspect.js +4 -4
  426. package/dist/src/ui/slash/council.js +19 -1
  427. package/dist/src/ui/slash/debug.d.ts +3 -31
  428. package/dist/src/ui/slash/debug.js +9 -20
  429. package/dist/src/ui/slash/ideal.d.ts +6 -2
  430. package/dist/src/ui/slash/ideal.js +97 -7
  431. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  432. package/dist/src/ui/slash/menu-items.js +12 -18
  433. package/dist/src/ui/slash/registry.d.ts +2 -0
  434. package/dist/src/ui/slash/registry.js +4 -0
  435. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  436. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  437. package/dist/src/ui/status-bar/index.d.ts +1 -1
  438. package/dist/src/ui/status-bar/index.js +7 -3
  439. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  440. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  441. package/dist/src/ui/theme.d.ts +1 -0
  442. package/dist/src/ui/theme.js +2 -0
  443. package/dist/src/ui/types.d.ts +7 -0
  444. package/dist/src/ui/use-app-logic.js +0 -0
  445. package/dist/src/ui/utils/format.d.ts +14 -0
  446. package/dist/src/ui/utils/format.js +23 -3
  447. package/dist/src/usage/downgrade.js +2 -2
  448. package/dist/src/usage/product-ledger.js +2 -2
  449. package/dist/src/utils/clipboard-image.js +23 -23
  450. package/dist/src/utils/install-manager.js +14 -11
  451. package/dist/src/utils/logger.js +2 -2
  452. package/dist/src/utils/permission-mode.js +5 -3
  453. package/dist/src/utils/redactor.js +1 -1
  454. package/dist/src/utils/settings.d.ts +153 -5
  455. package/dist/src/utils/settings.js +233 -29
  456. package/dist/src/utils/side-question.js +2 -2
  457. package/dist/src/utils/skills.js +3 -3
  458. package/dist/src/utils/visible-retry.d.ts +11 -0
  459. package/dist/src/utils/visible-retry.js +10 -1
  460. package/dist/src/verify/entrypoint.d.ts +1 -1
  461. package/dist/src/verify/entrypoint.js +1 -1
  462. package/dist/src/verify/recipes.d.ts +13 -0
  463. package/dist/src/verify/recipes.js +15 -0
  464. package/package.json +135 -132
  465. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  466. package/dist/src/providers/auth/gcloud.js +0 -102
  467. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  468. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  469. package/dist/src/providers/gemini.d.ts +0 -11
  470. package/dist/src/providers/gemini.js +0 -45
  471. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  472. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  473. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  474. package/dist/src/providers/strategies/google.strategy.js +0 -174
  475. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  476. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  477. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -46,21 +46,21 @@ export function collectCostForensics(sessionId) {
46
46
  const chain = getSessionChain(sessionId);
47
47
  const placeholders = chain.map(() => "?").join(",");
48
48
  const rows = db
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
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
56
56
  `)
57
57
  .all(...chain);
58
58
  const counts = db
59
- .prepare(`
60
- SELECT event_type, COUNT(*) AS c
61
- FROM interaction_logs
62
- WHERE session_id IN (${placeholders})
63
- 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
64
64
  `)
65
65
  .all(...chain);
66
66
  const countMap = new Map(counts.map((r) => [r.event_type, r.c]));
@@ -83,6 +83,7 @@ export function collectCostForensics(sessionId) {
83
83
  let totalCacheCreation = 0;
84
84
  let totalCostMicros = 0;
85
85
  let peakSingleCallInput = 0;
86
+ let peakSingleCallFresh = 0;
86
87
  for (const e of events) {
87
88
  totalInput += e.inputTokens;
88
89
  totalOutput += e.outputTokens;
@@ -91,6 +92,9 @@ export function collectCostForensics(sessionId) {
91
92
  totalCostMicros += e.costMicros;
92
93
  if (e.inputTokens > peakSingleCallInput)
93
94
  peakSingleCallInput = e.inputTokens;
95
+ const fresh = Math.max(0, e.inputTokens - e.cacheReadTokens);
96
+ if (fresh > peakSingleCallFresh)
97
+ peakSingleCallFresh = fresh;
94
98
  }
95
99
  const cacheable = totalInput;
96
100
  const cacheHitRatio = cacheable > 0 ? totalCacheRead / cacheable : 0;
@@ -106,6 +110,7 @@ export function collectCostForensics(sessionId) {
106
110
  totalCostUsd: totalCostMicros / 1_000_000,
107
111
  cacheHitRatio,
108
112
  peakSingleCallInput,
113
+ peakSingleCallFresh,
109
114
  events,
110
115
  experience: selectSessionExperience(sessionId),
111
116
  };
@@ -159,7 +164,8 @@ export function printCostForensics(summary, opts = {}) {
159
164
  w(`Total output tokens: ${formatNum(summary.totalOutput)}`);
160
165
  w(`Cache read tokens: ${formatNum(summary.totalCacheRead)} (${formatPct(summary.cacheHitRatio)} of input)`);
161
166
  w(`Cache create tokens: ${formatNum(summary.totalCacheCreation)}`);
162
- w(`Peak single call: ${formatNum(summary.peakSingleCallInput)} input`);
167
+ w(`Peak single call: ${formatNum(summary.peakSingleCallInput)} input ` +
168
+ `(${formatNum(summary.peakSingleCallFresh)} fresh)`);
163
169
  w(`Estimated cost: $${summary.totalCostUsd.toFixed(4)}`);
164
170
  // Cache-cadence diagnostic — surface fast-loop prompt-cache latency loss.
165
171
  const cadence = computeCacheCadence(summary.events);
@@ -199,8 +205,17 @@ export function printCostForensics(summary, opts = {}) {
199
205
  }
200
206
  // Acceptance hints — surface anomalies relative to Phase A/B/C targets.
201
207
  const anomalies = [];
202
- if (summary.peakSingleCallInput > 80_000) {
203
- anomalies.push(`peak single-call input ${formatNum(summary.peakSingleCallInput)} > 80,000 — sub-agent cap may not have kicked in (Phase B target breach)`);
208
+ // Phase B breach = a single call that sent >80K FRESH (uncached) tokens.
209
+ // Use FRESH, not raw input: `task`/`council` usage rows store `totalUsage`
210
+ // aggregated across a streamText call's internal steps, so a high raw input
211
+ // there is normally cached-prefix reuse across steps (high cache hit), NOT a
212
+ // cold cap breach. Evidence: session 6f17a13c1591 peaked at 378,613 raw input
213
+ // but only 54,389 fresh (324,224 cache-read) — aggregate reuse, no breach.
214
+ if (summary.peakSingleCallFresh > 80_000) {
215
+ anomalies.push(`peak single-call FRESH input ${formatNum(summary.peakSingleCallFresh)} > 80,000 — sub-agent cap may not have kicked in (Phase B target breach)`);
216
+ }
217
+ else if (summary.peakSingleCallInput > 80_000) {
218
+ anomalies.push(`peak single-call input ${formatNum(summary.peakSingleCallInput)} > 80,000 but only ${formatNum(summary.peakSingleCallFresh)} FRESH — aggregate multi-step reuse (task/council totalUsage), NOT a cap breach`);
204
219
  }
205
220
  if (summary.events.some((e) => e.messageSeq === null && e.source === "message")) {
206
221
  anomalies.push(`some 'message' events have NULL message_seq — Phase A5 message write-ahead bypassed (check persistMessageWriteAhead wiring)`);
@@ -7,7 +7,7 @@
7
7
  * Format (JSON):
8
8
  * { v:1, kdf:"scrypt", salt:hex, iv:hex, tag:hex, ct:base64 }
9
9
  *
10
- * Plaintext payload (JSON): { providers: { deepseek: "sk-...", siliconflow: "sf-..." } }
10
+ * Plaintext payload (JSON): { providers: { deepseek: "sk-...", xai: "xai-..." } }
11
11
  *
12
12
  * Crypto:
13
13
  * - scrypt(passphrase, salt, 32) → 256-bit key
@@ -7,7 +7,7 @@
7
7
  * Format (JSON):
8
8
  * { v:1, kdf:"scrypt", salt:hex, iv:hex, tag:hex, ct:base64 }
9
9
  *
10
- * Plaintext payload (JSON): { providers: { deepseek: "sk-...", siliconflow: "sf-..." } }
10
+ * Plaintext payload (JSON): { providers: { deepseek: "sk-...", xai: "xai-..." } }
11
11
  *
12
12
  * Crypto:
13
13
  * - scrypt(passphrase, salt, 32) → 256-bit key
@@ -15,8 +15,8 @@
15
15
  * keys delete <provider> — remove a stored key
16
16
  * keys import-bw [providers] — pull from Bitwarden vault, store in keychain
17
17
  * keys cleanup-settings — strip plaintext keys from user-settings.json
18
- * keys login <provider> — OAuth login (openai, google/agy)
19
- * keys logout <provider> — OAuth logout (openai, google/agy)
18
+ * keys login <provider> — OAuth login (openai, xai)
19
+ * keys logout <provider> — OAuth logout (openai, xai)
20
20
  */
21
21
  import { type ChatSecretId } from "../chat/chat-keychain.js";
22
22
  export interface KeysSetOptions {
@@ -15,8 +15,8 @@
15
15
  * keys delete <provider> — remove a stored key
16
16
  * keys import-bw [providers] — pull from Bitwarden vault, store in keychain
17
17
  * keys cleanup-settings — strip plaintext keys from user-settings.json
18
- * keys login <provider> — OAuth login (openai, google/agy)
19
- * keys logout <provider> — OAuth logout (openai, google/agy)
18
+ * keys login <provider> — OAuth login (openai, xai)
19
+ * keys logout <provider> — OAuth logout (openai, xai)
20
20
  */
21
21
  import { spawnSync } from "node:child_process";
22
22
  import { promises as fs } from "node:fs";
@@ -43,7 +43,7 @@ const CHAT_SECRET_IDS = ["discord-token", "discord-guild-id", "slack-token", "sl
43
43
  function isChatSecretId(value) {
44
44
  return CHAT_SECRET_IDS.includes(value);
45
45
  }
46
- const SETTINGS_PATH = path.join(os.homedir(), ".muonroi-cli", "user-settings.json");
46
+ const getSettingsPath = () => path.join(os.homedir(), ".muonroi-cli", "user-settings.json");
47
47
  function maskKey(key) {
48
48
  if (key.length <= 10)
49
49
  return "***";
@@ -51,8 +51,6 @@ function maskKey(key) {
51
51
  }
52
52
  function normalizeProvider(p) {
53
53
  const lower = p.toLowerCase();
54
- if (lower === "agy")
55
- return "google"; // agy is alias for google (OAuth + key)
56
54
  if (KEYCHAIN_PROVIDER_IDS.includes(lower)) {
57
55
  return lower;
58
56
  }
@@ -115,7 +113,7 @@ async function promptHidden(question) {
115
113
  export async function runKeysSet(provider, options = {}) {
116
114
  const norm = normalizeProvider(provider);
117
115
  if (!norm) {
118
- console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")} (agy is alias for google)`);
116
+ console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")}`);
119
117
  process.exit(1);
120
118
  }
121
119
  const key = (await promptHidden(`Paste ${provider} API key (hidden): `)).trim();
@@ -199,30 +197,11 @@ export async function runKeysList() {
199
197
  const oauthIds = await getOAuthProviderIds();
200
198
  for (const p of oauthIds) {
201
199
  try {
202
- let tokens = await loadTokens(p);
203
- if (!tokens && p === "google") {
204
- // Import from agy storage only for fresh (non-expired) tokens.
205
- // Stale agy tokens should be cleaned by running `keys logout google` + removing old .gemini file.
206
- const { loadAgyTokensWithRefresh } = await import("../providers/auth/gemini-oauth.js");
207
- const agyTokens = await loadAgyTokensWithRefresh();
208
- if (agyTokens && Date.now() < agyTokens.expiresAt) {
209
- tokens = agyTokens;
210
- }
211
- }
212
- if (tokens) {
213
- if (Date.now() < tokens.expiresAt) {
214
- const cfg = await getOAuthProviderConfig(p);
215
- const display = cfg?.displayName ?? p;
216
- oauthRows.push({ provider: p, display, email: tokens.email, expiresAt: tokens.expiresAt });
217
- }
218
- else if (p === "google") {
219
- // auto-clean stale Agy token from our store (imported from old ~/.gemini)
220
- try {
221
- const { deleteTokens } = await import("../providers/auth/token-store.js");
222
- await deleteTokens("google");
223
- }
224
- catch { }
225
- }
200
+ const tokens = await loadTokens(p);
201
+ if (tokens && Date.now() < tokens.expiresAt) {
202
+ const cfg = await getOAuthProviderConfig(p);
203
+ const display = cfg?.displayName ?? p;
204
+ oauthRows.push({ provider: p, display, email: tokens.email, expiresAt: tokens.expiresAt });
226
205
  }
227
206
  }
228
207
  catch {
@@ -234,7 +213,6 @@ export async function runKeysList() {
234
213
  console.log("No keys stored in OS keychain.");
235
214
  console.log("Run 'muonroi-cli keys set <provider>' or 'muonroi-cli keys import-bw' to add some.");
236
215
  console.log("Run 'muonroi-cli keys login openai' to log in with your OpenAI subscription.");
237
- console.log("Run 'muonroi-cli keys login google' or 'keys login agy' to log in with Agy (Google OAuth).");
238
216
  console.log("Run 'muonroi-cli keys login xai' to log in with your SuperGrok / X Premium+ subscription.");
239
217
  return;
240
218
  }
@@ -288,35 +266,11 @@ export async function runKeysList() {
288
266
  export async function runKeysDelete(provider) {
289
267
  const norm = normalizeProvider(provider);
290
268
  if (!norm) {
291
- console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")} (agy is alias for google)`);
269
+ console.error(`Unknown provider '${provider}'. Valid: ${KEYCHAIN_PROVIDER_IDS.join(", ")}`);
292
270
  process.exit(1);
293
271
  }
294
272
  const ok = await deleteKeyForProvider(norm);
295
273
  console.log(ok ? `Deleted ${provider} key from keychain.` : `No ${provider} key was stored.`);
296
- // If deleting google/agy, also clean the OAuth token (Agy uses the google slot for its OAuth)
297
- if (norm === "google") {
298
- try {
299
- const { loadTokens, deleteTokens } = await import("../providers/auth/token-store.js");
300
- const { getOAuthProviderConfig } = await import("../providers/auth/registry.js");
301
- const tokens = await loadTokens("google");
302
- if (tokens) {
303
- const cfg = await getOAuthProviderConfig("google");
304
- if (cfg?.provider) {
305
- try {
306
- await cfg.provider.revoke(tokens);
307
- }
308
- catch {
309
- // best effort
310
- }
311
- }
312
- await deleteTokens("google");
313
- console.log(`Also cleared Agy/Google OAuth token.`);
314
- }
315
- }
316
- catch {
317
- // ignore secondary errors
318
- }
319
- }
320
274
  }
321
275
  export async function runKeysExport(filePath) {
322
276
  const providers = {};
@@ -679,9 +633,9 @@ export async function runChatImportBw(opts = {}) {
679
633
  */
680
634
  export async function runKeysLogin(provider) {
681
635
  const oauthIds = await getOAuthProviderIds();
682
- const norm = provider.toLowerCase() === "agy" ? "google" : provider;
636
+ const norm = provider;
683
637
  if (!oauthIds.includes(norm)) {
684
- const supported = oauthIds.map((id) => (id === "google" ? "google (or agy)" : id)).join(", ");
638
+ const supported = oauthIds.join(", ");
685
639
  console.error(`OAuth login not supported for '${provider}'. Supported: ${supported}`);
686
640
  process.exit(1);
687
641
  }
@@ -693,22 +647,6 @@ export async function runKeysLogin(provider) {
693
647
  process.exit(1);
694
648
  }
695
649
  const name = cfg.displayName;
696
- // Special handling for google: prefer importing/reusing token from agy local config
697
- // (~/.gemini/oauth_creds.json) so existing agy login "just works" without browser.
698
- if (provider === "google") {
699
- const { loadAgyTokensWithRefresh } = await import("../providers/auth/gemini-oauth.js");
700
- const existing = await loadAgyTokensWithRefresh();
701
- if (existing?.accessToken) {
702
- console.log(`Found existing agy token for ${existing.email || "(no email)"}.`);
703
- console.log("Reusing it (refreshed if near expiry). No browser needed.");
704
- const expiry = new Date(existing.expiresAt).toLocaleString();
705
- const isExpired = Date.now() > existing.expiresAt;
706
- console.log(`Token expires: ${expiry}${isExpired ? " (was expired, refresh attempted)" : ""}`);
707
- console.log("Run 'muonroi-cli keys list' to verify.");
708
- return;
709
- }
710
- // No existing agy token — fall through to normal browser flow (using agy client)
711
- }
712
650
  console.log(`Logging in to ${name} via OAuth...`);
713
651
  console.log("A browser window will open. Complete the sign-in to continue.");
714
652
  const tokens = await cfg.provider.login({
@@ -742,9 +680,9 @@ export async function runKeysLogin(provider) {
742
680
  */
743
681
  export async function runKeysLogout(provider) {
744
682
  const oauthIds = await getOAuthProviderIds();
745
- const norm = provider.toLowerCase() === "agy" ? "google" : provider;
683
+ const norm = provider;
746
684
  if (!oauthIds.includes(norm)) {
747
- const supported = oauthIds.map((id) => (id === "google" ? "google (or agy)" : id)).join(", ");
685
+ const supported = oauthIds.join(", ");
748
686
  console.error(`OAuth logout not supported for '${provider}'. Supported: ${supported}`);
749
687
  process.exit(1);
750
688
  }
@@ -766,7 +704,7 @@ export async function runKeysLogout(provider) {
766
704
  export async function runKeysCleanupSettings() {
767
705
  let raw;
768
706
  try {
769
- raw = await fs.readFile(SETTINGS_PATH, "utf8");
707
+ raw = await fs.readFile(getSettingsPath(), "utf8");
770
708
  }
771
709
  catch (e) {
772
710
  if (e.code === "ENOENT") {
@@ -780,11 +718,11 @@ export async function runKeysCleanupSettings() {
780
718
  json = JSON.parse(raw);
781
719
  }
782
720
  catch {
783
- console.error(`Settings file is not valid JSON: ${SETTINGS_PATH}`);
721
+ console.error(`Settings file is not valid JSON: ${getSettingsPath()}`);
784
722
  process.exit(1);
785
723
  }
786
724
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
787
- const backup = `${SETTINGS_PATH}.bak.${ts}`;
725
+ const backup = `${getSettingsPath()}.bak.${ts}`;
788
726
  await fs.writeFile(backup, raw, "utf8");
789
727
  let removed = 0;
790
728
  if ("apiKey" in json) {
@@ -806,9 +744,9 @@ export async function runKeysCleanupSettings() {
806
744
  delete json.providers;
807
745
  }
808
746
  }
809
- await fs.writeFile(SETTINGS_PATH, `${JSON.stringify(json, null, 2)}\n`, "utf8");
747
+ await fs.writeFile(getSettingsPath(), `${JSON.stringify(json, null, 2)}\n`, "utf8");
810
748
  console.log(`Backed up to: ${backup}`);
811
- console.log(`Removed ${removed} plaintext key field(s) from: ${SETTINGS_PATH}`);
749
+ console.log(`Removed ${removed} plaintext key field(s) from: ${getSettingsPath()}`);
812
750
  if (removed === 0) {
813
751
  console.log("(File was already clean.)");
814
752
  }
@@ -69,6 +69,26 @@ export interface ClarifyOptionsResult {
69
69
  * so the UI knows to suppress the "(Recommended)" tag.
70
70
  */
71
71
  export declare function buildClarifyOptions(suggestions: string[] | undefined, recommended?: string): ClarifyOptionsResult;
72
+ /**
73
+ * Model-designed option object: a choice the user picks between, carrying its
74
+ * own explanation. `recommended:true` marks the pre-selected default. This is
75
+ * the richer shape the clarifier prompt now asks for so every choice shows a
76
+ * per-option "why" (not just the question-level `why`).
77
+ */
78
+ export interface ClarifyOptionSpec {
79
+ label: string;
80
+ description?: string;
81
+ recommended?: boolean;
82
+ }
83
+ /**
84
+ * Build council options from the model's richer `{label, description,
85
+ * recommended}` objects, preserving each choice's explanation as `description`
86
+ * and pointing `defaultIndex` at the option the model flagged `recommended`
87
+ * (falling back to the first when none is flagged). The same "Type something" /
88
+ * "Chat about this" escape-hatches are appended as in `buildClarifyOptions`.
89
+ * Pure + exported for unit testing.
90
+ */
91
+ export declare function buildClarifyOptionsRich(specs: ClarifyOptionSpec[] | undefined): ClarifyOptionsResult;
72
92
  export declare function runClarification(topic: string, leaderModelId: string, conversationContext: string, respondToQuestion: QuestionResponder, llm: CouncilLLM, signal?: AbortSignal, seedQuestions?: GrayAreaQuestion[], maxRounds?: number,
73
93
  /**
74
94
  * Optional pre-filled answers keyed by seed-question id. When the loop's
@@ -83,5 +103,11 @@ prefillAnswers?: Map<string, string>,
83
103
  * a cheaper tier model on the same provider as the leader. Falls back to
84
104
  * the leader model if no cheaper model is cataloged. Default false.
85
105
  */
86
- costAware?: boolean): AsyncGenerator<StreamChunk, ClarifiedSpec, unknown>;
87
- export declare function buildSpecFromTopic(topic: string, _conversationContext: string): ClarifiedSpec;
106
+ costAware?: boolean,
107
+ /**
108
+ * Healthy panel models to fall back to when the leader's cost-tier spec-synth
109
+ * model is on a failing proxy — keeps the spec's ≥3 observable criteria (and
110
+ * thus a meaningful leader eval) instead of degrading to one generic criterion.
111
+ */
112
+ fallbackModels?: string[]): AsyncGenerator<StreamChunk, ClarifiedSpec, unknown>;
113
+ export declare function buildSpecFromTopic(topic: string, conversationContext: string): ClarifiedSpec;
@@ -1,5 +1,5 @@
1
1
  import { pickCouncilTaskModel } from "./leader.js";
2
- import { tracedGenerate } from "./llm.js";
2
+ import { tracedGenerate, tracedGenerateWithFallback } from "./llm.js";
3
3
  import { phaseDone, phaseError, phaseStart } from "./phase-events.js";
4
4
  import { buildClarificationPrompt, buildReadinessJudgePrompt, buildSpecSynthesisPrompt } from "./prompts.js";
5
5
  /** P5: Hard cap on clarification rounds regardless of judge verdict. */
@@ -138,6 +138,45 @@ export function buildClarifyOptions(suggestions, recommended) {
138
138
  }
139
139
  return { options, defaultIndex };
140
140
  }
141
+ /**
142
+ * Build council options from the model's richer `{label, description,
143
+ * recommended}` objects, preserving each choice's explanation as `description`
144
+ * and pointing `defaultIndex` at the option the model flagged `recommended`
145
+ * (falling back to the first when none is flagged). The same "Type something" /
146
+ * "Chat about this" escape-hatches are appended as in `buildClarifyOptions`.
147
+ * Pure + exported for unit testing.
148
+ */
149
+ export function buildClarifyOptionsRich(specs) {
150
+ const cleaned = (specs ?? []).filter((s) => !!s && typeof s.label === "string" && s.label.trim().length > 0);
151
+ const choices = cleaned.map((s) => ({
152
+ label: s.label.trim(),
153
+ value: s.label.trim(),
154
+ kind: "choice",
155
+ ...(typeof s.description === "string" && s.description.trim().length > 0
156
+ ? { description: s.description.trim() }
157
+ : {}),
158
+ }));
159
+ const options = [
160
+ ...choices,
161
+ {
162
+ label: "Type something",
163
+ description: "Nhập câu trả lời tự do",
164
+ value: "",
165
+ kind: "freetext",
166
+ },
167
+ {
168
+ label: "Chat about this",
169
+ description: "Thảo luận thêm trước khi trả lời",
170
+ value: "",
171
+ kind: "chat",
172
+ },
173
+ ];
174
+ let defaultIndex;
175
+ const recIdx = cleaned.findIndex((s) => s.recommended === true);
176
+ if (recIdx !== -1)
177
+ defaultIndex = recIdx;
178
+ return { options, defaultIndex };
179
+ }
141
180
  export async function* runClarification(topic, leaderModelId, conversationContext, respondToQuestion, llm, signal, seedQuestions, maxRounds,
142
181
  /**
143
182
  * Optional pre-filled answers keyed by seed-question id. When the loop's
@@ -152,7 +191,13 @@ prefillAnswers,
152
191
  * a cheaper tier model on the same provider as the leader. Falls back to
153
192
  * the leader model if no cheaper model is cataloged. Default false.
154
193
  */
155
- costAware = false) {
194
+ costAware = false,
195
+ /**
196
+ * Healthy panel models to fall back to when the leader's cost-tier spec-synth
197
+ * model is on a failing proxy — keeps the spec's ≥3 observable criteria (and
198
+ * thus a meaningful leader eval) instead of degrading to one generic criterion.
199
+ */
200
+ fallbackModels = []) {
156
201
  // P5: use MAX_CLARIFY_ROUNDS (12) as the hard cap; respect explicit override
157
202
  // from callers that pass maxRounds (e.g. tests that want old 3-round behavior).
158
203
  const max = typeof maxRounds === "number" && maxRounds > 0 ? maxRounds : MAX_CLARIFY_ROUNDS;
@@ -284,7 +329,16 @@ costAware = false) {
284
329
  continue;
285
330
  }
286
331
  const questionId = crypto.randomUUID();
287
- const { options, defaultIndex } = buildClarifyOptions(q.suggestions, q.recommended);
332
+ // Prefer the model's richer {label, description, recommended} options so
333
+ // every choice shows its own explanation + the recommended default is
334
+ // pre-selected. Fall back to the legacy string `suggestions` shape (PIL
335
+ // gray-area seeds and any model still emitting the old format).
336
+ const { options, defaultIndex } = q.options && q.options.length > 0
337
+ ? buildClarifyOptionsRich(q.options)
338
+ : buildClarifyOptions(q.suggestions, q.recommended);
339
+ // Keep the deprecated `suggestions` mirror populated for consumers that
340
+ // still read it (audit/replay), deriving labels from rich options.
341
+ const suggestionLabels = q.suggestions ?? (q.options ? q.options.map((o) => o.label).filter((l) => typeof l === "string") : undefined);
288
342
  yield {
289
343
  type: "council_question",
290
344
  content: `**${q.question}**\n${q.why ? `> ${q.why}` : ""}`,
@@ -293,7 +347,7 @@ costAware = false) {
293
347
  phase: "clarify",
294
348
  question: q.question,
295
349
  context: q.why,
296
- suggestions: q.suggestions,
350
+ suggestions: suggestionLabels,
297
351
  options,
298
352
  isRequired: q.isRequired,
299
353
  defaultIndex,
@@ -329,7 +383,7 @@ costAware = false) {
329
383
  startedAt: phaseStartedAt,
330
384
  detail: allQA.length > 0 ? `${allQA.length} Q&A` : "no clarification needed",
331
385
  });
332
- const spec = yield* synthesizeSpec(topic, conversationContext, allQA, leaderModelId, llm, costAware);
386
+ const spec = yield* synthesizeSpec(topic, conversationContext, allQA, leaderModelId, llm, costAware, fallbackModels);
333
387
  // P5: attach ready-gate metadata to the returned spec
334
388
  spec.confidenceScore = gateConfidence;
335
389
  spec.remainingGaps = gateGaps;
@@ -355,13 +409,18 @@ costAware = false) {
355
409
  };
356
410
  return spec;
357
411
  }
358
- export function buildSpecFromTopic(topic, _conversationContext) {
412
+ export function buildSpecFromTopic(topic, conversationContext) {
359
413
  return {
360
414
  problemStatement: topic,
361
415
  constraints: [],
362
416
  successCriteria: [`Address the topic: ${topic.slice(0, 100)}`],
363
417
  scope: "Determined by conversation context",
364
418
  rawQA: [],
419
+ // Carry the session/task context onto the spec so every debate stage (not
420
+ // just the opening) can stay anchored to the parent task. Previously this
421
+ // argument was dropped (prefixed `_`), which is why auto-council debates saw
422
+ // only the isolated sub-task and drifted off-topic.
423
+ parentContext: conversationContext?.trim() || undefined,
365
424
  };
366
425
  }
367
426
  /**
@@ -374,7 +433,7 @@ export function buildSpecFromTopic(topic, _conversationContext) {
374
433
  * We LLM-extract criteria/constraints/scope from the topic when QA is empty.
375
434
  * Falls back to the original buildSpecFromTopic if the LLM call fails.
376
435
  */
377
- async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware) {
436
+ async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware, fallbackModels = []) {
378
437
  const system = `You are extracting an implicit specification from a short topic statement. ` +
379
438
  `The user did not answer clarification questions, but the topic itself often ` +
380
439
  `implies scope, criteria, and constraints. Tease them out.\n\n` +
@@ -396,16 +455,21 @@ async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId
396
455
  `- Write all fields in the user's language (detected from the topic).`;
397
456
  const prompt = `## Topic\n${topic}\n\n${conversationContext ? `## Context\n${conversationContext}\n` : ""}`;
398
457
  try {
458
+ // Model-fallback: the cost-tier synth model can sit on a flaky proxy
459
+ // (Console Go glm/kimi → "Upstream request failed"). Falling back to
460
+ // buildSpecFromTopic yields a single generic criterion, which makes the
461
+ // leader eval read a vague "1/1 criteria met". Retry on healthy panel models
462
+ // first so the spec keeps its ≥3 observable criteria.
399
463
  const synthModel = pickCouncilTaskModel("spec_synthesis", leaderModelId, costAware);
400
- const raw = yield* tracedGenerate(llm, {
464
+ const raw = yield* tracedGenerateWithFallback(llm, {
401
465
  phase: "synthesis",
402
466
  label: "Inferring spec from topic (no clarification answers)",
403
- modelId: synthModel,
467
+ models: [synthModel, ...fallbackModels],
404
468
  system,
405
469
  prompt,
406
470
  maxTokens: 1024,
407
471
  });
408
- const match = raw.match(/\{[\s\S]*\}/);
472
+ const match = raw?.match(/\{[\s\S]*\}/);
409
473
  if (match) {
410
474
  const parsed = JSON.parse(match[0]);
411
475
  const criteria = Array.isArray(parsed.successCriteria) && parsed.successCriteria.length >= 1
@@ -429,12 +493,12 @@ async function* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId
429
493
  }
430
494
  return buildSpecFromTopic(topic, conversationContext);
431
495
  }
432
- async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, llm, costAware = false) {
496
+ async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, llm, costAware = false, fallbackModels = []) {
433
497
  if (qa.length === 0) {
434
498
  // Clarifier asked 0 questions OR user skipped all of them. Use the LLM to
435
499
  // pull implicit criteria/constraints/scope from the topic alone instead of
436
500
  // returning the single-criterion fallback that makes leader-eval trivial.
437
- return yield* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware);
501
+ return yield* inferSpecFromTopicOnly(topic, conversationContext, leaderModelId, llm, costAware, fallbackModels);
438
502
  }
439
503
  const { system, prompt } = buildSpecSynthesisPrompt(topic, conversationContext, qa);
440
504
  const resolved = {};
@@ -444,16 +508,18 @@ async function* synthesizeSpec(topic, conversationContext, qa, leaderModelId, ll
444
508
  }
445
509
  }
446
510
  try {
511
+ // Model-fallback (see inferSpecFromTopicOnly): keep the ≥3-criteria spec even
512
+ // when the leader's cost-tier synth model is on a failing proxy.
447
513
  const synthModel = pickCouncilTaskModel("spec_synthesis", leaderModelId, costAware);
448
- const raw = yield* tracedGenerate(llm, {
514
+ const raw = yield* tracedGenerateWithFallback(llm, {
449
515
  phase: "synthesis",
450
516
  label: "Synthesizing clarified spec",
451
- modelId: synthModel,
517
+ models: [synthModel, ...fallbackModels],
452
518
  system,
453
519
  prompt,
454
520
  maxTokens: 2048,
455
521
  });
456
- const match = raw.match(/\{[\s\S]*\}/);
522
+ const match = raw?.match(/\{[\s\S]*\}/);
457
523
  if (match) {
458
524
  const parsed = JSON.parse(match[0]);
459
525
  return {
@@ -98,7 +98,7 @@ function getCompactionText(msg) {
98
98
  return null;
99
99
  return msg.content;
100
100
  }
101
- function extractUserText(content) {
101
+ function extractText(content) {
102
102
  if (typeof content === "string")
103
103
  return content;
104
104
  if (Array.isArray(content)) {
@@ -109,40 +109,74 @@ function extractUserText(content) {
109
109
  }
110
110
  return "";
111
111
  }
112
+ /** Filter assistant messages to exclude tool-call-only turns (tool_use + tool_result
113
+ * pairs that carry no analytical text). An assistant turn with ≥80 chars of text
114
+ * content is considered "analytical" (contains decisions, analysis, code, or plans). */
115
+ function isAnalyticalAssistant(content) {
116
+ const text = extractText(content);
117
+ return text.trim().length >= 80;
118
+ }
119
+ /** Maximum total characters for the built context string. Raised from 12K to 32K
120
+ * (~8K tokens) so council participants get meaningful session history rather than
121
+ * only 5 user-message snippets. */
122
+ const MAX_CONTEXT_CHARS = 32_000;
112
123
  export function buildCouncilContext(messages) {
113
124
  const parts = [];
125
+ // 1. Compaction summary (first message if it's a system compaction)
114
126
  if (messages.length > 0 && isCompactionSummary(messages[0])) {
115
127
  const summary = getCompactionText(messages[0]);
116
128
  if (summary) {
117
129
  parts.push(`## Session Context (from compaction summary)\n${summary}`);
118
130
  }
119
131
  }
120
- const userMessages = [];
121
- for (let i = messages.length - 1; i >= 0 && userMessages.length < 5; i--) {
132
+ // 2. Recent conversation turns — scan BOTH user + analytical assistant messages
133
+ // from the tail, capturing at most 8 meaningful entries.
134
+ // Previously only captured 5 user messages — lost ALL assistant analysis,
135
+ // code changes, and decisions made during the session.
136
+ const conversationTurns = [];
137
+ for (let i = messages.length - 1; i >= 0 && conversationTurns.length < 8; i--) {
122
138
  const msg = messages[i];
123
139
  if (msg.role === "user") {
124
- const text = extractUserText(msg.content);
140
+ const text = extractText(msg.content);
141
+ if (text.trim()) {
142
+ conversationTurns.unshift(`[User] ${text.slice(0, 3000).trim()}`);
143
+ }
144
+ }
145
+ else if (msg.role === "assistant" && isAnalyticalAssistant(msg.content)) {
146
+ const text = extractText(msg.content);
125
147
  if (text.trim()) {
126
- userMessages.unshift(`- ${text.slice(0, 2000).trim()}`);
148
+ // Cap assistant text to 1500 chars — enough to convey analysis/decisions
149
+ // without blowing the context budget on long code outputs.
150
+ conversationTurns.unshift(`[Assistant] ${text.slice(0, 1500).trim()}`);
127
151
  }
128
152
  }
129
153
  }
130
- if (userMessages.length > 0) {
131
- parts.push(`## Recent User Messages\n${userMessages.join("\n")}`);
154
+ if (conversationTurns.length > 0) {
155
+ parts.push(`## Recent Conversation\n${conversationTurns.join("\n\n")}`);
132
156
  }
133
- const councilMemories = [];
157
+ // 3. Key decisions from system messages (task outcomes, decisions, plans)
158
+ const decisionMsgs = [];
134
159
  for (const msg of messages) {
135
- if (msg.role === "system" && typeof msg.content === "string" && msg.content.includes("[Council Memory]")) {
136
- councilMemories.push(msg.content);
160
+ if (msg.role !== "system" || typeof msg.content !== "string")
161
+ continue;
162
+ const c = msg.content;
163
+ if (c.includes("[Council Memory]") ||
164
+ c.includes("[Council Decision]") ||
165
+ c.includes("[Council Action Items]") ||
166
+ c.includes("[Council Plan Update]") ||
167
+ c.includes("[Decision]") ||
168
+ c.includes("[Task Completed]") ||
169
+ c.includes("[NEEDS HUMAN REVIEW]")) {
170
+ decisionMsgs.push(c);
137
171
  }
138
172
  }
139
- if (councilMemories.length > 0) {
140
- const digests = councilMemories.slice(-2).map(formatCouncilMemoryDigest);
141
- parts.push(`## Previous Council Outcomes (cite by role/round)\n${digests.join("\n\n")}`);
173
+ if (decisionMsgs.length > 0) {
174
+ const digests = decisionMsgs.slice(-4).map(formatCouncilMemoryDigest);
175
+ parts.push(`## Key Decisions\n${digests.join("\n\n")}`);
142
176
  }
143
177
  const combined = parts.join("\n\n---\n\n");
144
- if (combined.length > 12000) {
145
- return `${combined.slice(0, 12000)}\n\n[... context truncated to fit token budget]`;
178
+ if (combined.length > MAX_CONTEXT_CHARS) {
179
+ return `${combined.slice(0, MAX_CONTEXT_CHARS)}\n\n[... context truncated to fit token budget]`;
146
180
  }
147
181
  return combined;
148
182
  }