muonroi-cli 1.7.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
  2. package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
  3. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
  4. package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
  5. package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
  6. package/dist/packages/agent-harness-core/src/selector.js +2 -2
  7. package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
  8. package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
  9. package/dist/packages/agent-harness-opentui/src/install.js +1 -1
  10. package/dist/src/__test-stubs__/vitest-setup.js +4 -0
  11. package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
  12. package/dist/src/agent-harness/mock-model.d.ts +1 -1
  13. package/dist/src/agent-harness/mock-model.js +143 -6
  14. package/dist/src/agent-harness/test-spawn.js +3 -1
  15. package/dist/src/chat/chat-keychain.js +9 -1
  16. package/dist/src/cli/cost-forensics.js +7 -4
  17. package/dist/src/cli/keys.d.ts +2 -2
  18. package/dist/src/cli/keys.js +107 -28
  19. package/dist/src/cli/usage-report.js +2 -2
  20. package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
  21. package/dist/src/council/__tests__/abort-threading.test.js +193 -0
  22. package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
  23. package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
  24. package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
  25. package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
  26. package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
  27. package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
  28. package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
  29. package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
  30. package/dist/src/council/__tests__/research-tools.test.js +4 -0
  31. package/dist/src/council/__tests__/round-tools.test.js +6 -0
  32. package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
  33. package/dist/src/council/clarifier-question-cap.test.js +73 -0
  34. package/dist/src/council/clarifier.d.ts +29 -1
  35. package/dist/src/council/clarifier.js +64 -1
  36. package/dist/src/council/debate-planner.d.ts +2 -1
  37. package/dist/src/council/debate-planner.js +5 -3
  38. package/dist/src/council/debate.d.ts +9 -0
  39. package/dist/src/council/debate.js +88 -8
  40. package/dist/src/council/index.d.ts +46 -1
  41. package/dist/src/council/index.js +125 -19
  42. package/dist/src/council/llm.js +41 -13
  43. package/dist/src/council/prompts.js +36 -1
  44. package/dist/src/council/types.d.ts +14 -1
  45. package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
  46. package/dist/src/ee/bridge.d.ts +3 -1
  47. package/dist/src/ee/bridge.js +9 -3
  48. package/dist/src/ee/bridge.test.js +1 -2
  49. package/dist/src/ee/client.js +15 -2
  50. package/dist/src/ee/recall-ledger.d.ts +20 -0
  51. package/dist/src/ee/recall-ledger.js +40 -1
  52. package/dist/src/ee/search.d.ts +25 -0
  53. package/dist/src/ee/search.js +73 -0
  54. package/dist/src/ee/types.d.ts +15 -1
  55. package/dist/src/ee/who-am-i.d.ts +41 -0
  56. package/dist/src/ee/who-am-i.js +143 -0
  57. package/dist/src/ee/who-am-i.test.d.ts +1 -0
  58. package/dist/src/ee/who-am-i.test.js +101 -0
  59. package/dist/src/flow/compaction/index.d.ts +1 -0
  60. package/dist/src/flow/compaction/index.js +4 -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 +59 -62
  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 +13 -5
  76. package/dist/src/lsp/manager.test.js +41 -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/auto-setup.js +0 -8
  86. package/dist/src/mcp/client-pool.d.ts +1 -1
  87. package/dist/src/mcp/client-pool.js +25 -9
  88. package/dist/src/mcp/ee-tools.d.ts +10 -0
  89. package/dist/src/mcp/ee-tools.js +34 -0
  90. package/dist/src/mcp/self-verify-jobs.d.ts +1 -0
  91. package/dist/src/mcp/self-verify-jobs.js +1 -3
  92. package/dist/src/mcp/self-verify-runner.js +1 -1
  93. package/dist/src/models/catalog-gemini.test.js +12 -10
  94. package/dist/src/models/catalog.json +106 -38
  95. package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
  96. package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
  97. package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +3 -0
  98. package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
  99. package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
  100. package/dist/src/orchestrator/__tests__/message-processor.test.js +2 -0
  101. package/dist/src/orchestrator/__tests__/prompts.test.d.ts +1 -0
  102. package/dist/src/orchestrator/__tests__/prompts.test.js +88 -0
  103. package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
  104. package/dist/src/orchestrator/__tests__/sub-session-delegation.test.d.ts +1 -0
  105. package/dist/src/orchestrator/__tests__/sub-session-delegation.test.js +507 -0
  106. package/dist/src/orchestrator/agent.test.js +5 -2
  107. package/dist/src/orchestrator/auto-commit.d.ts +110 -0
  108. package/dist/src/orchestrator/auto-commit.js +366 -0
  109. package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
  110. package/dist/src/orchestrator/batch-turn-runner.js +2 -1
  111. package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
  112. package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
  113. package/dist/src/orchestrator/compaction.d.ts +22 -0
  114. package/dist/src/orchestrator/compaction.js +42 -0
  115. package/dist/src/orchestrator/delegations.d.ts +1 -0
  116. package/dist/src/orchestrator/delegations.js +41 -0
  117. package/dist/src/orchestrator/error-utils.js +27 -2
  118. package/dist/src/orchestrator/message-processor.d.ts +23 -5
  119. package/dist/src/orchestrator/message-processor.js +226 -2573
  120. package/dist/src/orchestrator/orchestrator.d.ts +11 -2
  121. package/dist/src/orchestrator/orchestrator.js +530 -43
  122. package/dist/src/orchestrator/pending-calls.js +2 -1
  123. package/dist/src/orchestrator/preprocessor.d.ts +13 -0
  124. package/dist/src/orchestrator/preprocessor.js +146 -0
  125. package/dist/src/orchestrator/prompts.d.ts +16 -0
  126. package/dist/src/orchestrator/prompts.js +130 -67
  127. package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
  128. package/dist/src/orchestrator/safety-askcard.js +74 -0
  129. package/dist/src/orchestrator/sandbox.test.js +8 -5
  130. package/dist/src/orchestrator/scope-ceiling.js +1 -1
  131. package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
  132. package/dist/src/orchestrator/stall-watchdog.js +23 -0
  133. package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
  134. package/dist/src/orchestrator/stream-runner.js +52 -30
  135. package/dist/src/orchestrator/sub-agent-cap.d.ts +2 -0
  136. package/dist/src/orchestrator/sub-agent-cap.js +12 -4
  137. package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
  138. package/dist/src/orchestrator/subagent-compactor.d.ts +21 -0
  139. package/dist/src/orchestrator/subagent-compactor.js +148 -24
  140. package/dist/src/orchestrator/subagent-compactor.spec.js +249 -1
  141. package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
  142. package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
  143. package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
  144. package/dist/src/orchestrator/tool-args-repair.js +1 -1
  145. package/dist/src/orchestrator/tool-engine.d.ts +153 -0
  146. package/dist/src/orchestrator/tool-engine.js +3138 -0
  147. package/dist/src/orchestrator/turn-runner-deps.d.ts +3 -0
  148. package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
  149. package/dist/src/pil/__tests__/discovery.test.js +89 -145
  150. package/dist/src/pil/__tests__/dual-run.test.js +2 -0
  151. package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
  152. package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
  153. package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
  154. package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
  155. package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
  156. package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
  157. package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
  158. package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
  159. package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
  160. package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
  161. package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
  162. package/dist/src/pil/__tests__/llm-classify.test.js +79 -1
  163. package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
  164. package/dist/src/pil/__tests__/pipeline.test.js +11 -8
  165. package/dist/src/pil/__tests__/response-tools.test.js +52 -0
  166. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  167. package/dist/src/pil/agent-operating-contract.js +5 -3
  168. package/dist/src/pil/agent-operating-contract.test.js +2 -2
  169. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  170. package/dist/src/pil/cheap-model-playbook.js +1 -1
  171. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  172. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  173. package/dist/src/pil/config.d.ts +20 -0
  174. package/dist/src/pil/config.js +35 -0
  175. package/dist/src/pil/discovery-types.d.ts +32 -9
  176. package/dist/src/pil/discovery.d.ts +3 -3
  177. package/dist/src/pil/discovery.js +138 -244
  178. package/dist/src/pil/index.d.ts +1 -1
  179. package/dist/src/pil/index.js +1 -1
  180. package/dist/src/pil/layer1-intent.d.ts +9 -1
  181. package/dist/src/pil/layer1-intent.js +62 -7
  182. package/dist/src/pil/layer1-intent.test.js +112 -2
  183. package/dist/src/pil/layer15-context-scan.js +1 -1
  184. package/dist/src/pil/layer16-clarity.d.ts +21 -25
  185. package/dist/src/pil/layer16-clarity.js +52 -88
  186. package/dist/src/pil/layer18-acceptance.d.ts +13 -3
  187. package/dist/src/pil/layer18-acceptance.js +1 -75
  188. package/dist/src/pil/layer2-personality.js +7 -8
  189. package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
  190. package/dist/src/pil/layer2_5-ponytail.js +41 -0
  191. package/dist/src/pil/layer3-ee-injection.js +180 -14
  192. package/dist/src/pil/layer4-gsd.js +22 -8
  193. package/dist/src/pil/layer5-context.d.ts +1 -0
  194. package/dist/src/pil/layer5-context.js +26 -11
  195. package/dist/src/pil/layer6-output.js +23 -5
  196. package/dist/src/pil/llm-classify.d.ts +11 -0
  197. package/dist/src/pil/llm-classify.js +196 -3
  198. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  199. package/dist/src/pil/native-capabilities-workbook.js +4 -3
  200. package/dist/src/pil/pipeline.js +15 -1
  201. package/dist/src/pil/response-tools.d.ts +16 -0
  202. package/dist/src/pil/response-tools.js +41 -0
  203. package/dist/src/pil/schema.d.ts +8 -0
  204. package/dist/src/pil/schema.js +24 -3
  205. package/dist/src/pil/task-tier-map.js +7 -7
  206. package/dist/src/pil/types.d.ts +9 -3
  207. package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
  208. package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
  209. package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
  210. package/dist/src/product-loop/discovery-interview.d.ts +20 -0
  211. package/dist/src/product-loop/discovery-interview.js +121 -7
  212. package/dist/src/product-loop/gather.js +19 -5
  213. package/dist/src/product-loop/loop-driver.js +2 -2
  214. package/dist/src/product-loop/sprint-self-verify.js +2 -2
  215. package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
  216. package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
  217. package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
  218. package/dist/src/providers/auth/gcloud.d.ts +28 -0
  219. package/dist/src/providers/auth/gcloud.js +102 -0
  220. package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
  221. package/dist/src/providers/auth/gemini-oauth.js +292 -105
  222. package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
  223. package/dist/src/providers/auth/grok-oauth.js +1 -1
  224. package/dist/src/providers/auth/registry.js +28 -6
  225. package/dist/src/providers/gemini.d.ts +1 -1
  226. package/dist/src/providers/gemini.js +10 -3
  227. package/dist/src/providers/keychain.d.ts +2 -2
  228. package/dist/src/providers/keychain.js +20 -2
  229. package/dist/src/providers/openai.js +5 -1
  230. package/dist/src/providers/runtime.d.ts +1 -1
  231. package/dist/src/providers/runtime.js +11 -2
  232. package/dist/src/providers/siliconflow-sse-repair.js +1 -1
  233. package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
  234. package/dist/src/providers/strategies/google.strategy.js +159 -9
  235. package/dist/src/providers/strategies/thinking-mode.js +19 -6
  236. package/dist/src/providers/types.d.ts +1 -1
  237. package/dist/src/router/decide.test.js +40 -8
  238. package/dist/src/router/step-router.d.ts +20 -0
  239. package/dist/src/router/step-router.js +73 -0
  240. package/dist/src/self-qa/agentic-loop.js +30 -21
  241. package/dist/src/self-qa/orchestrator.js +5 -5
  242. package/dist/src/self-qa/scenario-planner.js +1 -1
  243. package/dist/src/self-qa/spec-emitter.js +4 -4
  244. package/dist/src/storage/__tests__/hierarchical-session.test.d.ts +1 -0
  245. package/dist/src/storage/__tests__/hierarchical-session.test.js +161 -0
  246. package/dist/src/storage/__tests__/sweep-stale-pending.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/migrations.js +13 -1
  250. package/dist/src/storage/transcript-response-entry.test.js +18 -5
  251. package/dist/src/storage/transcript-view.js +7 -1
  252. package/dist/src/storage/transcript.d.ts +3 -0
  253. package/dist/src/storage/transcript.js +254 -73
  254. package/dist/src/storage/ui-interaction-log.d.ts +17 -0
  255. package/dist/src/storage/usage.js +22 -5
  256. package/dist/src/tools/__tests__/native-tools.test.js +6 -0
  257. package/dist/src/tools/bash.d.ts +36 -23
  258. package/dist/src/tools/bash.js +59 -301
  259. package/dist/src/tools/bash.test.js +2 -257
  260. package/dist/src/tools/file-tracker.d.ts +5 -0
  261. package/dist/src/tools/file-tracker.js +14 -0
  262. package/dist/src/tools/file.js +2 -0
  263. package/dist/src/tools/git-safety.d.ts +38 -1
  264. package/dist/src/tools/git-safety.js +94 -4
  265. package/dist/src/tools/git-safety.test.js +45 -1
  266. package/dist/src/tools/native-tools.d.ts +1 -1
  267. package/dist/src/tools/native-tools.js +66 -1
  268. package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
  269. package/dist/src/tools/registry-git-safety.test.js +17 -6
  270. package/dist/src/tools/registry.d.ts +6 -0
  271. package/dist/src/tools/registry.js +215 -13
  272. package/dist/src/types/index.d.ts +8 -2
  273. package/dist/src/ui/app.d.ts +1 -0
  274. package/dist/src/ui/app.js +0 -0
  275. package/dist/src/ui/components/council-question-card.js +1 -0
  276. package/dist/src/ui/components/message-view.js +37 -3
  277. package/dist/src/ui/components/structured-response-view.js +7 -0
  278. package/dist/src/ui/components/task-list-panel.js +3 -12
  279. package/dist/src/ui/containers/chat-feed.d.ts +40 -0
  280. package/dist/src/ui/containers/chat-feed.js +66 -0
  281. package/dist/src/ui/containers/modals-layer.d.ts +87 -0
  282. package/dist/src/ui/containers/modals-layer.js +18 -0
  283. package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
  284. package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
  285. package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
  286. package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
  287. package/dist/src/ui/slash/expand.js +14 -1
  288. package/dist/src/ui/slash/export.js +16 -1
  289. package/dist/src/ui/slash/menu-items.js +1 -0
  290. package/dist/src/ui/slash/ponytail.d.ts +2 -0
  291. package/dist/src/ui/slash/ponytail.js +19 -0
  292. package/dist/src/ui/status-bar/store.js +0 -1
  293. package/dist/src/ui/status-bar/store.test.js +2 -2
  294. package/dist/src/ui/use-app-logic.d.ts +26 -0
  295. package/dist/src/ui/use-app-logic.js +0 -0
  296. package/dist/src/ui/utils/relaunch.js +1 -1
  297. package/dist/src/ui/utils/text.d.ts +11 -0
  298. package/dist/src/ui/utils/text.js +21 -1
  299. package/dist/src/ui/utils/text.test.js +25 -1
  300. package/dist/src/ui/utils/tools.js +3 -1
  301. package/dist/src/usage/cost-log.js +2 -2
  302. package/dist/src/usage/estimator.d.ts +14 -0
  303. package/dist/src/usage/estimator.js +22 -0
  304. package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
  305. package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
  306. package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
  307. package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
  308. package/dist/src/utils/__tests__/llm-deadline.test.js +16 -4
  309. package/dist/src/utils/__tests__/logger.test.d.ts +1 -0
  310. package/dist/src/utils/__tests__/logger.test.js +115 -0
  311. package/dist/src/utils/__tests__/settings-agent-first.test.d.ts +1 -0
  312. package/dist/src/utils/__tests__/settings-agent-first.test.js +48 -0
  313. package/dist/src/utils/install-manager.d.ts +1 -0
  314. package/dist/src/utils/install-manager.js +76 -7
  315. package/dist/src/utils/install-manager.test.js +43 -1
  316. package/dist/src/utils/llm-deadline.d.ts +9 -1
  317. package/dist/src/utils/llm-deadline.js +31 -2
  318. package/dist/src/utils/logger.d.ts +28 -0
  319. package/dist/src/utils/logger.js +171 -0
  320. package/dist/src/utils/permission-mode.d.ts +33 -0
  321. package/dist/src/utils/permission-mode.js +164 -3
  322. package/dist/src/utils/permission-mode.test.js +92 -1
  323. package/dist/src/utils/settings.d.ts +36 -32
  324. package/dist/src/utils/settings.js +151 -123
  325. package/dist/src/utils/update-checker.test.js +5 -1
  326. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { getCompactionSummaryText } from "../orchestrator/compaction.js";
2
2
  import { getResponseTaskType, isResponseTool } from "../pil/response-tools.js";
3
+ import { logger } from "../utils/logger.js";
3
4
  import { getDatabase, withTransaction } from "./db.js";
4
5
  import { extractToolResultFromOutput, getOutputKind, isOutputSuccess } from "./tool-results.js";
5
6
  import { buildEffectiveTranscript } from "./transcript-view.js";
@@ -327,16 +328,15 @@ export function markMessageErrored(sessionId, seq) {
327
328
  */
328
329
  export function sweepStalePendingRows(staleAfterMs = 5 * 60 * 1000) {
329
330
  const cutoff = new Date(Date.now() - staleAfterMs).toISOString();
330
- const now = new Date().toISOString();
331
331
  try {
332
332
  const db = getDatabase();
333
333
  const toolCalls = db
334
334
  .prepare(`
335
335
  UPDATE tool_calls
336
- SET status = 'aborted', completed_at = ?
336
+ SET status = 'aborted', completed_at = started_at
337
337
  WHERE status = 'pending' AND started_at < ?
338
338
  `)
339
- .run(now, cutoff);
339
+ .run(cutoff);
340
340
  const messages = db
341
341
  .prepare(`
342
342
  UPDATE messages
@@ -379,90 +379,247 @@ export function appendCompaction(sessionId, firstKeptSeq, summary, tokensBefore)
379
379
  `).run(new Date().toISOString(), sessionId);
380
380
  });
381
381
  }
382
- export function buildChatEntries(sessionId) {
383
- const toolResults = loadStoredToolResults(sessionId);
384
- const callMap = new Map();
385
- const entries = [];
386
- for (const row of buildEffectiveMessageRecords(sessionId)) {
387
- const { message, timestamp } = row;
388
- if (message.role === "user") {
389
- const content = renderUserContent(message.content);
390
- if (content) {
391
- entries.push({ type: "user", content, timestamp });
382
+ export function revertLatestCompaction(sessionId) {
383
+ withTransaction((db) => {
384
+ db.prepare(`
385
+ DELETE FROM compactions
386
+ WHERE session_id = ?
387
+ AND id = (SELECT MAX(id) FROM compactions WHERE session_id = ?)
388
+ `).run(sessionId, sessionId);
389
+ db.prepare(`
390
+ UPDATE sessions
391
+ SET updated_at = ?
392
+ WHERE id = ?
393
+ `).run(new Date().toISOString(), sessionId);
394
+ });
395
+ }
396
+ export function getSessionChain(sessionId) {
397
+ logger.debug("storage", "Resolving session chain", { sessionId });
398
+ const db = getDatabase();
399
+ // 1. Walk up to the root parent
400
+ let rootId = sessionId;
401
+ const visitedUp = new Set();
402
+ while (rootId && !visitedUp.has(rootId)) {
403
+ visitedUp.add(rootId);
404
+ try {
405
+ const row = db.prepare("SELECT parent_session_id FROM sessions WHERE id = ?").get(rootId);
406
+ if (row?.parent_session_id) {
407
+ rootId = row.parent_session_id;
392
408
  }
393
- continue;
409
+ else {
410
+ break;
411
+ }
412
+ }
413
+ catch {
414
+ break;
394
415
  }
395
- if (message.role === "system") {
396
- const summaryText = getCompactionSummaryText(message);
397
- const content = summaryText ?? (typeof message.content === "string" ? message.content.trim() : "");
398
- if (content && !isInternalCouncilMarker(content)) {
399
- // A compaction checkpoint is internal context-management, NOT the
400
- // assistant's answer. Tag it with a source label so the UI renders it
401
- // as a clearly-marked, collapsible checkpoint instead of dumping the
402
- // raw "## Goal / ## Context For Suffix" scaffolding as a chat reply.
403
- entries.push(summaryText !== null
404
- ? { type: "assistant", content, timestamp, sourceLabel: "⋯ context checkpoint (auto-compacted)" }
405
- : { type: "assistant", content, timestamp });
416
+ }
417
+ // 2. Find all descendants of the root session using a BFS queue to handle multiple child branches
418
+ const allIds = new Set([rootId]);
419
+ const queue = [rootId];
420
+ while (queue.length > 0) {
421
+ const currentId = queue.shift();
422
+ try {
423
+ const children = db.prepare("SELECT id FROM sessions WHERE parent_session_id = ?").all(currentId);
424
+ for (const child of children) {
425
+ if (!allIds.has(child.id)) {
426
+ allIds.add(child.id);
427
+ queue.push(child.id);
428
+ }
406
429
  }
407
- continue;
408
430
  }
409
- if (message.role === "assistant") {
410
- const text = renderAssistantContent(message.content, callMap);
411
- if (text) {
412
- entries.push({ type: "assistant", content: text, timestamp });
431
+ catch (err) {
432
+ logger.error("storage", `Failed to query child sessions for ${currentId} during walk`, { error: err });
433
+ }
434
+ }
435
+ // 3. Sort chronologically by querying the database for all matched sessions ordered by created_at
436
+ let chain = [];
437
+ try {
438
+ const placeholders = Array.from(allIds)
439
+ .map(() => "?")
440
+ .join(",");
441
+ const sortedRows = db
442
+ .prepare(`
443
+ SELECT id FROM sessions
444
+ WHERE id IN (${placeholders})
445
+ ORDER BY created_at ASC
446
+ `)
447
+ .all(...Array.from(allIds));
448
+ chain = sortedRows.map((r) => r.id);
449
+ }
450
+ catch (err) {
451
+ logger.error("storage", `Failed to sort session chain for root ${rootId}`, { error: err });
452
+ // Fallback to unsorted array if query fails
453
+ chain = Array.from(allIds);
454
+ }
455
+ logger.debug("storage", "Resolved session chain successfully", { sessionId, chainLength: chain.length, chain });
456
+ return chain;
457
+ }
458
+ export function buildChatEntries(sessionId) {
459
+ const chain = getSessionChain(sessionId);
460
+ logger.debug("storage", "Building chat entries from session chain", { sessionId, chainLength: chain.length });
461
+ const toolResults = new Map();
462
+ for (const sid of chain) {
463
+ const results = loadStoredToolResults(sid);
464
+ for (const [k, v] of results) {
465
+ toolResults.set(k, v);
466
+ }
467
+ }
468
+ // Load parent/child session metadata to reconcile user prompt timestamps across rotation/sub-session boundaries
469
+ const db = getDatabase();
470
+ const sessionMeta = new Map();
471
+ try {
472
+ if (chain.length > 1) {
473
+ const placeholders = chain.map(() => "?").join(",");
474
+ const rows = db
475
+ .prepare(`SELECT id, parent_session_id, created_at FROM sessions WHERE id IN (${placeholders})`)
476
+ .all(...chain);
477
+ for (const r of rows) {
478
+ sessionMeta.set(r.id, { parent_session_id: r.parent_session_id, created_at: r.created_at });
413
479
  }
414
- continue;
415
480
  }
416
- if (message.role === "tool" && Array.isArray(message.content)) {
417
- for (const part of message.content) {
418
- if (part.type !== "tool-result")
481
+ }
482
+ catch (err) {
483
+ logger.error("storage", "Failed to load session metadata for chain in buildChatEntries", { error: err });
484
+ }
485
+ // Map each child sub-session's initial user prompt to its creation timestamp
486
+ const childPrompts = new Map();
487
+ for (let i = 1; i < chain.length; i++) {
488
+ const sid = chain[i];
489
+ const meta = sessionMeta.get(sid);
490
+ if (meta && meta.parent_session_id) {
491
+ const records = buildEffectiveMessageRecords(sid);
492
+ const firstUser = records.find((r) => r.message.role === "user");
493
+ if (firstUser) {
494
+ const content = renderUserContent(firstUser.message.content);
495
+ if (content) {
496
+ childPrompts.set(content, new Date(meta.created_at));
497
+ }
498
+ }
499
+ }
500
+ }
501
+ const callMap = new Map();
502
+ const entries = [];
503
+ const renderedResponseCallIds = new Set();
504
+ let lastTimestamp;
505
+ for (let i = 0; i < chain.length; i++) {
506
+ const sid = chain[i];
507
+ const records = buildEffectiveMessageRecords(sid);
508
+ const isChildSession = i > 0;
509
+ for (let j = 0; j < records.length; j++) {
510
+ const { message, timestamp } = records[j];
511
+ lastTimestamp = timestamp;
512
+ if (message.role === "user") {
513
+ const content = renderUserContent(message.content);
514
+ if (isChildSession && childPrompts.has(content)) {
515
+ // Skip B's user message because it's a duplicate of A's user message
516
+ continue;
517
+ }
518
+ if (content) {
519
+ entries.push({ type: "user", content, timestamp });
520
+ }
521
+ continue;
522
+ }
523
+ if (message.role === "system") {
524
+ const summaryText = getCompactionSummaryText(message);
525
+ if (isChildSession && j === 0 && summaryText !== null) {
526
+ // Skip the first message in a child session if it's the parent compaction summary
527
+ // to keep the visual timeline clean, since the user already sees the parent history.
419
528
  continue;
420
- // Response tools (respond_general/analyze/plan/...) are the model's
421
- // TERMINAL structured answer (identity execute the payload lives in
422
- // the call args and is echoed in the result). The live stream yields
423
- // them as a `structured_response` chunk (message-processor.ts:2733),
424
- // which the UI renders as the answer block. Persisted history MUST
425
- // rebuild the SAME entry: finalizeActiveTurn (app.tsx) replaces the
426
- // live message list with getChatEntries() on every normal turn, so if
427
- // we rebuilt a respond_* result as a bare tool_result the rendered
428
- // answer would be silently dropped the instant streaming ended (live
429
- // repro session 9d3d371ca1bd: grok investigated, emitted a 10K-char
430
- // respond_general, the answer flashed then vanished on turn finalize —
431
- // the user saw only the "→ respond_general" indicator). AI SDK v5/v6
432
- // wraps tool outputs as `{type:"json", value:{...}}`; unwrap to expose
433
- // the schema-shaped payload to the renderer.
434
- if (isResponseTool(part.toolName)) {
435
- const rawOutput = part.output;
436
- const unwrapped = rawOutput && typeof rawOutput === "object" && rawOutput.type === "json"
437
- ? (rawOutput.value ?? {})
438
- : (rawOutput ?? {});
529
+ }
530
+ const content = summaryText ?? (typeof message.content === "string" ? message.content.trim() : "");
531
+ if (content && !isInternalCouncilMarker(content)) {
532
+ entries.push(summaryText !== null
533
+ ? { type: "assistant", content, timestamp, sourceLabel: "⋯ context checkpoint (auto-compacted)" }
534
+ : { type: "assistant", content, timestamp });
535
+ }
536
+ continue;
537
+ }
538
+ if (message.role === "assistant") {
539
+ const text = renderAssistantContent(message.content, callMap);
540
+ if (text) {
541
+ entries.push({ type: "assistant", content: text, timestamp });
542
+ }
543
+ continue;
544
+ }
545
+ if (message.role === "tool" && Array.isArray(message.content)) {
546
+ for (const part of message.content) {
547
+ if (part.type !== "tool-result")
548
+ continue;
549
+ if (isResponseTool(part.toolName)) {
550
+ renderedResponseCallIds.add(part.toolCallId);
551
+ const rawOutput = part.output;
552
+ const unwrapped = rawOutput && typeof rawOutput === "object" && rawOutput.type === "json"
553
+ ? (rawOutput.value ?? {})
554
+ : (rawOutput ?? {});
555
+ entries.push({
556
+ type: "structured_response",
557
+ content: "",
558
+ timestamp,
559
+ structuredResponse: {
560
+ taskType: getResponseTaskType(part.toolName) ?? part.toolName,
561
+ data: unwrapped,
562
+ },
563
+ });
564
+ continue;
565
+ }
566
+ const toolCall = callMap.get(part.toolCallId) ?? toFallbackToolCall(part.toolCallId, part.toolName);
567
+ const toolResult = toolResults.get(part.toolCallId) ??
568
+ extractToolResultFromOutput(part.output) ?? {
569
+ success: isOutputSuccess(part.output),
570
+ output: JSON.stringify(part.output),
571
+ };
439
572
  entries.push({
440
- type: "structured_response",
441
- content: "",
573
+ type: "tool_result",
574
+ content: toolResult.success ? toolResult.output || "Success" : toolResult.error || "Error",
442
575
  timestamp,
443
- structuredResponse: {
444
- taskType: getResponseTaskType(part.toolName) ?? part.toolName,
445
- data: unwrapped,
446
- },
576
+ toolCall,
577
+ toolResult,
447
578
  });
448
- continue;
449
579
  }
450
- const toolCall = callMap.get(part.toolCallId) ?? toFallbackToolCall(part.toolCallId, part.toolName);
451
- const toolResult = toolResults.get(part.toolCallId) ??
452
- extractToolResultFromOutput(part.output) ?? {
453
- success: isOutputSuccess(part.output),
454
- output: JSON.stringify(part.output),
455
- };
456
- entries.push({
457
- type: "tool_result",
458
- content: toolResult.success ? toolResult.output || "Success" : toolResult.error || "Error",
459
- timestamp,
460
- toolCall,
461
- toolResult,
462
- });
463
580
  }
464
581
  }
465
582
  }
583
+ // Recover response-tool answers that were CALLED but never produced a
584
+ // tool-result row (execution ERRORED). The payload lives in the call args.
585
+ // Without this, a respond_* that errored after the council debate is dropped
586
+ // on finalize and only the last debate round stays visible.
587
+ for (const [callId, call] of callMap) {
588
+ if (renderedResponseCallIds.has(callId))
589
+ continue;
590
+ if (!isResponseTool(call.function.name))
591
+ continue;
592
+ let data;
593
+ try {
594
+ data = JSON.parse(call.function.arguments || "{}");
595
+ }
596
+ catch {
597
+ // Args unparseable — nothing to recover for this call.
598
+ continue;
599
+ }
600
+ entries.push({
601
+ type: "structured_response",
602
+ content: "",
603
+ timestamp: lastTimestamp ?? new Date(),
604
+ structuredResponse: {
605
+ taskType: getResponseTaskType(call.function.name) ?? call.function.name,
606
+ data,
607
+ },
608
+ });
609
+ }
610
+ // Adjust parent user message timestamps to match their corresponding child session creation time
611
+ for (const entry of entries) {
612
+ if (entry.type === "user") {
613
+ const content = entry.content.trim();
614
+ const childTime = childPrompts.get(content);
615
+ if (childTime) {
616
+ entry.timestamp = childTime;
617
+ }
618
+ }
619
+ }
620
+ // Sort entries chronologically by timestamp (stable sort)
621
+ entries.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
622
+ logger.debug("storage", "Built chat entries successfully", { sessionId, count: entries.length });
466
623
  return entries;
467
624
  }
468
625
  function getNextSequence(db, sessionId) {
@@ -540,4 +697,28 @@ function toFallbackToolCall(toolCallId, toolName) {
540
697
  },
541
698
  };
542
699
  }
700
+ export function getLastTodoWriteArgs(sessionId) {
701
+ const chain = getSessionChain(sessionId);
702
+ const db = getDatabase();
703
+ for (let i = chain.length - 1; i >= 0; i--) {
704
+ try {
705
+ const row = db
706
+ .prepare(`
707
+ SELECT args_json
708
+ FROM tool_calls
709
+ WHERE session_id = ? AND tool_name = 'todo_write'
710
+ ORDER BY id DESC
711
+ LIMIT 1
712
+ `)
713
+ .get(chain[i]);
714
+ if (row?.args_json) {
715
+ return row.args_json;
716
+ }
717
+ }
718
+ catch {
719
+ // ignore and check parent
720
+ }
721
+ }
722
+ return null;
723
+ }
543
724
  //# sourceMappingURL=transcript.js.map
@@ -107,6 +107,20 @@ interface InitNewRetryPayload {
107
107
  feStack: string;
108
108
  bbTemplate: string | null;
109
109
  }
110
+ /** Auto-resolved loop-cap event (no user prompt). Replaces the old askcard_open for tool-loop-cap. */
111
+ interface LoopCapAutoPayload {
112
+ questionId: string;
113
+ phase: "tool-loop-cap";
114
+ autoVerdict: "continue" | "stop";
115
+ kind: "pattern" | "cap";
116
+ stepNumber: number;
117
+ /** Only present for pattern kind. */
118
+ toolName?: string;
119
+ count?: number;
120
+ naturalCeiling?: number | null;
121
+ /** Only present for cap kind. */
122
+ cap?: number;
123
+ }
110
124
  type Payload = {
111
125
  subtype: "route_decision";
112
126
  data: RouteDecisionPayload;
@@ -146,6 +160,9 @@ type Payload = {
146
160
  } | {
147
161
  subtype: "init_new_retry";
148
162
  data: InitNewRetryPayload;
163
+ } | {
164
+ subtype: "loop_cap_auto";
165
+ data: LoopCapAutoPayload;
149
166
  };
150
167
  /**
151
168
  * Persist a UI lifecycle event. Caller passes a discriminated payload so
@@ -1,5 +1,7 @@
1
1
  import { getModelInfo } from "../models/registry.js";
2
+ import { logger } from "../utils/logger.js";
2
3
  import { getDatabase } from "./db.js";
4
+ import { getSessionChain } from "./transcript.js";
3
5
  export function recordUsageEvent(sessionId, source, model, usage, messageSeq, pilActive = false, enrichmentDelta = 0, providerOptionsShape = null) {
4
6
  if (!usage)
5
7
  return;
@@ -21,24 +23,39 @@ export function recordUsageEvent(sessionId, source, model, usage, messageSeq, pi
21
23
  .run(sessionId, messageSeq ?? null, source, model, inputTokens, outputTokens, totalTokens, costMicros, new Date().toISOString(), pilActive ? 1 : 0, enrichmentDelta, cacheReadTokens, cacheCreationTokens, providerOptionsShape);
22
24
  }
23
25
  export function getSessionTotalTokens(sessionId) {
26
+ const chain = getSessionChain(sessionId);
27
+ const placeholders = chain.map(() => "?").join(",");
24
28
  const row = getDatabase()
25
29
  .prepare(`
26
30
  SELECT COALESCE(SUM(total_tokens), 0) AS total_tokens
27
31
  FROM usage_events
28
- WHERE session_id = ?
32
+ WHERE session_id IN (${placeholders})
29
33
  `)
30
- .get(sessionId);
31
- return row?.total_tokens ?? 0;
34
+ .get(...chain);
35
+ const total = row?.total_tokens ?? 0;
36
+ logger.debug("storage", "Calculated session total tokens from chain", {
37
+ sessionId,
38
+ chainLength: chain.length,
39
+ totalTokens: total,
40
+ });
41
+ return total;
32
42
  }
33
43
  export function listSessionUsage(sessionId) {
44
+ const chain = getSessionChain(sessionId);
45
+ const placeholders = chain.map(() => "?").join(",");
34
46
  const rows = getDatabase()
35
47
  .prepare(`
36
48
  SELECT id, session_id, message_seq, source, model, input_tokens, output_tokens, total_tokens, cost_micros, created_at, cache_read_tokens, cache_creation_tokens
37
49
  FROM usage_events
38
- WHERE session_id = ?
50
+ WHERE session_id IN (${placeholders})
39
51
  ORDER BY id ASC
40
52
  `)
41
- .all(sessionId);
53
+ .all(...chain);
54
+ logger.debug("storage", "Retrieved session usage events from chain", {
55
+ sessionId,
56
+ chainLength: chain.length,
57
+ eventCount: rows.length,
58
+ });
42
59
  return rows.map((row) => ({
43
60
  id: row.id,
44
61
  sessionId: row.session_id,
@@ -33,6 +33,12 @@ describe("registerNativeMuonroiTools", () => {
33
33
  registerNativeMuonroiTools(tools);
34
34
  expect(await exec(tools, "ee_feedback", { verdict: "followed" })).toMatch(/invalid_args/);
35
35
  });
36
+ it("ee_write rejects a too-short lesson (no network call)", async () => {
37
+ const tools = {};
38
+ registerNativeMuonroiTools(tools);
39
+ expect(await exec(tools, "ee_write", { lesson: "oops" })).toMatch(/invalid_args/);
40
+ expect(await exec(tools, "ee_write", {})).toMatch(/invalid_args/);
41
+ });
36
42
  it("selfverify_status returns not_found for an unknown runId (shared JobManager)", async () => {
37
43
  const tools = {};
38
44
  registerNativeMuonroiTools(tools);
@@ -14,7 +14,13 @@ export interface BackgroundProcess {
14
14
  exitCode: number | null;
15
15
  }
16
16
  interface BashToolOptions {
17
- sandboxMode?: SandboxMode;
17
+ /**
18
+ * Accepted for backward compatibility with callers that pass sandboxMode.
19
+ * Sandbox has been removed; this value is ignored — mode is always "off".
20
+ * Will be repurposed when the new sandbox is implemented.
21
+ * @deprecated
22
+ */
23
+ sandboxMode?: string;
18
24
  sandboxSettings?: SandboxSettings;
19
25
  shellSettings?: ShellSettings;
20
26
  }
@@ -22,12 +28,17 @@ export declare class BashTool {
22
28
  private cwd;
23
29
  private bgProcesses;
24
30
  private tmpDir;
25
- private sandboxMode;
26
- private sandboxSettings;
27
31
  private shellSettings;
28
32
  private resolvedShell;
29
- private sandboxUnsupportedWarned;
33
+ private sandboxSettings;
30
34
  constructor(initialCwd?: string, options?: BashToolOptions);
35
+ /**
36
+ * Sandbox has been removed. Always returns "off".
37
+ * Kept for backward compatibility with orchestrator / stream-runner callers.
38
+ */
39
+ getSandboxMode(): SandboxMode;
40
+ getSandboxSettings(): SandboxSettings;
41
+ setSandboxSettings(settings: SandboxSettings): void;
31
42
  private ensureTmpDir;
32
43
  execute(command: string, timeout?: number, abortSignal?: AbortSignal): Promise<ToolResult>;
33
44
  startBackground(command: string): Promise<ToolResult>;
@@ -37,29 +48,31 @@ export declare class BashTool {
37
48
  cleanup(): Promise<void>;
38
49
  getCwd(): string;
39
50
  setCwd(next: string): void;
40
- getSandboxMode(): SandboxMode;
41
- setSandboxMode(mode: SandboxMode): void;
42
- /**
43
- * The sandbox mode that actually takes effect. When "shuru" is configured but
44
- * the current platform cannot run Shuru (non-macOS/arm64), the sandbox is
45
- * inert: every wrapped command would fail with the macOS-only error, and the
46
- * tool description would still advertise a `/workspace` mount that does not
47
- * exist — so the model `cd`s into a phantom dir and abandons verification.
48
- * Degrade to "off" (host execution) with a one-time warning so bash keeps
49
- * working and the description reflects reality. A stale checked-in project
50
- * `sandboxMode: "shuru"` therefore no longer bricks the shell off-platform.
51
- */
52
- private effectiveSandboxMode;
53
- getSandboxSettings(): SandboxSettings;
54
- setSandboxSettings(settings: SandboxSettings): void;
55
51
  getToolDescription(): string;
56
52
  getResolvedShell(): ResolvedShell;
57
53
  private spawnInvocation;
54
+ /**
55
+ * Pre-execution command preparation.
56
+ *
57
+ * Layer 1 — Catastrophic hard-block: checks the command against
58
+ * CATASTROPHIC_PATTERNS in permission-mode.ts. These are irreversible or
59
+ * severely dangerous operations that are blocked regardless of permission
60
+ * mode (safe / auto-edit / yolo) and regardless of sandbox state.
61
+ *
62
+ * Layer 2 — Future sandbox: when a new sandbox is wired in, wrap the
63
+ * command here before returning.
64
+ */
65
+ /**
66
+ * Pre-execution command preparation with two-layer safety.
67
+ *
68
+ * Layer 1 — Catastrophic hard-block: checks the command against
69
+ * CATASTROPHIC_PATTERNS in permission-mode.ts. Returns structured block
70
+ * result so callers can route to askcard approval flow.
71
+ *
72
+ * Layer 2 — Future sandbox: when a new sandbox is wired in, wrap the
73
+ * command here before returning.
74
+ */
58
75
  private prepareCommand;
59
- private formatSandboxRuntimeError;
60
76
  }
61
- export declare function wrapCommandForShuru(cwd: string, command: string, settings?: SandboxSettings): string;
62
- export declare function shouldRunOnHostInSandboxMode(command: string, settings?: SandboxSettings): boolean;
63
77
  export declare function wrapHostBrowserCommand(command: string): string;
64
- export declare function getSandboxMutationBlockReason(command: string, settings?: SandboxSettings): string | null;
65
78
  export {};