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
@@ -26,7 +26,80 @@
26
26
  */
27
27
  import { getModelByTier } from "../models/registry.js";
28
28
  import { detectProviderForModel } from "../providers/runtime.js";
29
+ import { readTimeoutEnv } from "../utils/ee-logger.js";
29
30
  import { isModelDisabled, isProviderDisabled, loadUserSettings } from "../utils/settings.js";
31
+ // ─── EE-guided SAMR override ─────────────────────────────────────────────────
32
+ // EE_SAMR_TIMEOUT: per-call budget for consulting the EE brain about SAMR.
33
+ // Default 2s, range 500ms..5s. Falls back to heuristics on timeout/error.
34
+ const EE_SAMR_TIMEOUT_MS = readTimeoutEnv("MUONROI_EE_SAMR_TIMEOUT_MS", 2000, 500, 5000);
35
+ /**
36
+ * Consult the EE brain + local heuristics to decide whether SAMR should be
37
+ * enabled for this turn even when the user config has it disabled.
38
+ *
39
+ * Resolution order:
40
+ * 1. Fast heuristic — mechanical/simple tasks skip EE entirely (zero cost).
41
+ * 2. EE brain — asks classifyViaBrain for a yes/no + execution tier.
42
+ * 3. Heuristic fallback — when EE is unreachable, uses taskType + complexity.
43
+ */
44
+ export async function eeSamrGuidance(params) {
45
+ const { userMessage, taskType, taskConfidence, complexitySize, taskComplexity } = params;
46
+ // Step 1: fast heuristic — mechanical tasks never benefit from SAMR
47
+ const mechanicalTypes = new Set(["general", "documentation", "generate", "build", "chitchat"]);
48
+ if (taskType && mechanicalTypes.has(taskType)) {
49
+ return { overrideConfig: null, reason: `mechanical taskType=${taskType} — no SAMR benefit` };
50
+ }
51
+ // Step 2: fast heuristic — trivial tasks don't need a split
52
+ if (taskComplexity === "low" && (complexitySize === "small" || complexitySize === undefined)) {
53
+ return { overrideConfig: null, reason: "low complexity + small — no SAMR benefit" };
54
+ }
55
+ // Step 3: ask EE brain
56
+ try {
57
+ const { classifyViaBrain } = await import("../ee/bridge.js");
58
+ const eePrompt = [
59
+ `Task: ${userMessage.slice(0, 200)}`,
60
+ `Context: type=${taskType ?? "unknown"} complexity=${taskComplexity ?? "unknown"} size=${complexitySize ?? "unknown"}`,
61
+ `Question: Would splitting the work into (1) premium reasoning then (2) cheap execution save tokens without hurting quality?`,
62
+ `Reply valid JSON: {"samr":true,"executionTier":"balanced"} or {"samr":false}`,
63
+ ].join("\n");
64
+ const response = await classifyViaBrain(eePrompt, EE_SAMR_TIMEOUT_MS, {
65
+ systemPrompt: 'You are a SAMR (Step-Aware Model Routing) evaluator. Given a task description, context, and question, reply ONLY with valid JSON: {"samr":true,"executionTier":"balanced"} or {"samr":false}. No extra text, no markdown.',
66
+ responseFormat: { type: "json_object" },
67
+ maxTokens: 100,
68
+ });
69
+ if (!response) {
70
+ // Fallback heuristic when EE unreachable
71
+ return samrHeuristicFallback(taskType, taskComplexity, complexitySize);
72
+ }
73
+ const parsed = JSON.parse(response);
74
+ if (parsed.samr === true) {
75
+ const tier = parsed.executionTier === "fast" ? "fast" : "balanced";
76
+ return {
77
+ overrideConfig: { enabled: true, toolExecutionTier: tier, premiumSynthesis: false },
78
+ reason: `ee-guided: ${tier} execution phase`,
79
+ };
80
+ }
81
+ return { overrideConfig: null, reason: "ee-declined" };
82
+ }
83
+ catch {
84
+ return samrHeuristicFallback(taskType, taskComplexity, complexitySize);
85
+ }
86
+ }
87
+ /**
88
+ * Deterministic fallback when EE brain is unreachable.
89
+ * Reasoning-heavy tasks with high complexity are strong SAMR candidates.
90
+ */
91
+ function samrHeuristicFallback(taskType, taskComplexity, complexitySize) {
92
+ const reasoningTypes = new Set(["plan", "analyze", "refactor", "debug", "architect"]);
93
+ if (taskType && reasoningTypes.has(taskType)) {
94
+ if (taskComplexity === "high" || complexitySize === "large") {
95
+ return {
96
+ overrideConfig: { enabled: true, toolExecutionTier: "balanced", premiumSynthesis: false },
97
+ reason: `heuristic: ${taskType}/${taskComplexity ?? "?"}/${complexitySize ?? "?"} benefits from SAMR`,
98
+ };
99
+ }
100
+ }
101
+ return { overrideConfig: null, reason: "heuristic: no benefit" };
102
+ }
30
103
  // ─── Defaults ────────────────────────────────────────────────────────────────
31
104
  const DEFAULT_CONFIG = {
32
105
  enabled: false,
@@ -128,8 +128,8 @@ export async function createLLMBrain(opts) {
128
128
  // flows without reasoning prose leaking through.
129
129
  // biome-ignore lint/suspicious/noExplicitAny: global flag is AI SDK's docs convention
130
130
  globalThis.AI_SDK_LOG_WARNINGS = false;
131
- if (process.env["MUONROI_DEEPSEEK_DISABLE_THINKING"] === undefined) {
132
- process.env["MUONROI_DEEPSEEK_DISABLE_THINKING"] = "1";
131
+ if (process.env.MUONROI_DEEPSEEK_DISABLE_THINKING === undefined) {
132
+ process.env.MUONROI_DEEPSEEK_DISABLE_THINKING = "1";
133
133
  }
134
134
  // Resolve the catalog before any model lookup — resolveModelRuntime needs it
135
135
  // loaded or it throws "not found in catalog". self-verify --agentic is a
@@ -281,35 +281,35 @@ function extractJsonCandidates(text) {
281
281
  function tryParseDecisionSlice(slice) {
282
282
  try {
283
283
  const obj = JSON.parse(slice);
284
- if (typeof obj["action"] !== "string")
284
+ if (typeof obj.action !== "string")
285
285
  return null;
286
- const action = obj["action"];
287
- const reason = typeof obj["reason"] === "string" ? obj["reason"] : "";
286
+ const action = obj.action;
287
+ const reason = typeof obj.reason === "string" ? obj.reason : "";
288
288
  switch (action) {
289
289
  case "type":
290
- if (typeof obj["text"] === "string")
291
- return { action, text: obj["text"], reason };
290
+ if (typeof obj.text === "string")
291
+ return { action, text: obj.text, reason };
292
292
  return null;
293
293
  case "press":
294
- if (typeof obj["key"] === "string")
295
- return { action, key: obj["key"], reason };
294
+ if (typeof obj.key === "string")
295
+ return { action, key: obj.key, reason };
296
296
  return null;
297
297
  case "wait_for": {
298
298
  const w = {
299
299
  action,
300
300
  reason,
301
- timeoutMs: typeof obj["timeoutMs"] === "number" ? obj["timeoutMs"] : 5_000,
301
+ timeoutMs: typeof obj.timeoutMs === "number" ? obj.timeoutMs : 5_000,
302
302
  };
303
- if (obj["idle"] === true)
303
+ if (obj.idle === true)
304
304
  w.idle = true;
305
- if (typeof obj["selector"] === "string")
306
- w.selector = obj["selector"];
307
- if (typeof obj["event"] === "string")
308
- w.event = obj["event"];
305
+ if (typeof obj.selector === "string")
306
+ w.selector = obj.selector;
307
+ if (typeof obj.event === "string")
308
+ w.event = obj.event;
309
309
  return w;
310
310
  }
311
311
  case "done": {
312
- const v = obj["verdict"];
312
+ const v = obj.verdict;
313
313
  if (v === "pass" || v === "fail" || v === "inconclusive") {
314
314
  return { action, verdict: v, reason };
315
315
  }
@@ -330,8 +330,14 @@ export async function runAgenticLoop(opts) {
330
330
  const events = [];
331
331
  let lastFrame = null;
332
332
  const entry = opts.entry ?? resolve("src/index.ts");
333
- const mockDir = opts.mockLlmDir ?? resolve("tests/harness/fixtures/llm");
334
- const args = [entry, "--agent-mode", "--mock-llm", mockDir, ...(opts.extraArgs ?? [])];
333
+ const args = [entry, "--agent-mode"];
334
+ if (opts.mockLlmDir !== "none") {
335
+ const mockDir = opts.mockLlmDir ?? resolve("tests/harness/fixtures/llm");
336
+ args.push("--mock-llm", mockDir);
337
+ }
338
+ if (opts.extraArgs) {
339
+ args.push(...opts.extraArgs);
340
+ }
335
341
  const env = {
336
342
  ...process.env,
337
343
  MUONROI_TEST_NO_PERSIST: "1",
@@ -438,6 +444,9 @@ export async function runAgenticLoop(opts) {
438
444
  }
439
445
  try {
440
446
  await executeDecision(driver, decision);
447
+ if (decision.action === "type" || decision.action === "press") {
448
+ await new Promise((res) => setTimeout(res, 300));
449
+ }
441
450
  }
442
451
  catch (err) {
443
452
  const msg = err instanceof Error ? err.message : String(err);
@@ -501,11 +510,11 @@ function wireDriver(inWrite, outRead) {
501
510
  const splitter = createLineSplitter((line) => {
502
511
  try {
503
512
  const msg = JSON.parse(line);
504
- if (msg["mode"] === "live")
513
+ if (msg.mode === "live")
505
514
  driver._ingest({ kind: "frame", frame: msg });
506
- else if (msg["t"] === "idle")
515
+ else if (msg.t === "idle")
507
516
  driver._ingest({ kind: "idle" });
508
- else if (msg["t"] === "event")
517
+ else if (msg.t === "event")
509
518
  driver._ingest({ kind: "event", event: msg });
510
519
  }
511
520
  catch { }
@@ -119,19 +119,19 @@ export async function runScenarios(scenarios, opts = {}) {
119
119
  }
120
120
  function wireDriver(inWrite, outRead) {
121
121
  const driver = createDriver({
122
- sendKey: (k) => inWrite.write(JSON.stringify({ op: "press", key: k }) + "\n"),
123
- sendType: (t) => inWrite.write(JSON.stringify({ op: "type", text: t }) + "\n"),
122
+ sendKey: (k) => inWrite.write(`${JSON.stringify({ op: "press", key: k })}\n`),
123
+ sendType: (t) => inWrite.write(`${JSON.stringify({ op: "type", text: t })}\n`),
124
124
  });
125
125
  const splitter = createLineSplitter((line) => {
126
126
  try {
127
127
  const msg = JSON.parse(line);
128
- if (msg["mode"] === "live") {
128
+ if (msg.mode === "live") {
129
129
  driver._ingest({ kind: "frame", frame: msg });
130
130
  }
131
- else if (msg["t"] === "idle") {
131
+ else if (msg.t === "idle") {
132
132
  driver._ingest({ kind: "idle" });
133
133
  }
134
- else if (msg["t"] === "event") {
134
+ else if (msg.t === "event") {
135
135
  driver._ingest({ kind: "event", event: msg });
136
136
  }
137
137
  }
@@ -99,7 +99,7 @@ function parseAttrs(chunk) {
99
99
  }
100
100
  // `isModal` may appear without `={true}` in JSX (boolean shorthand). Detect.
101
101
  if (/\bisModal\b(?!\s*=)/.test(chunk))
102
- out["isModal"] = true;
102
+ out.isModal = true;
103
103
  return out;
104
104
  }
105
105
  function buildScenariosFromHits(hits, allFiles, maxScenarios) {
@@ -95,12 +95,12 @@ function stepToCode(step) {
95
95
  case "wait_for": {
96
96
  const obj = {};
97
97
  if (step.idle)
98
- obj["idle"] = true;
98
+ obj.idle = true;
99
99
  if (step.selector)
100
- obj["selector"] = step.selector;
100
+ obj.selector = step.selector;
101
101
  if (step.event)
102
- obj["event"] = step.event;
103
- obj["timeoutMs"] = step.timeoutMs ?? 5_000;
102
+ obj.event = step.event;
103
+ obj.timeoutMs = step.timeoutMs ?? 5_000;
104
104
  return `await driver.wait_for(${JSON.stringify(obj)});`;
105
105
  }
106
106
  }
@@ -0,0 +1,161 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ const sessionsDb = new Map();
3
+ const messagesDb = new Map();
4
+ const toolCallsDb = new Map();
5
+ const toolResultsDb = new Map();
6
+ const compactionsDb = new Map();
7
+ vi.mock("../db", () => {
8
+ return {
9
+ getDatabase: () => ({
10
+ prepare: (sql) => {
11
+ const lower = sql.toLowerCase();
12
+ return {
13
+ get: (param) => {
14
+ if (lower.includes("from sessions")) {
15
+ if (lower.includes("where parent_session_id =")) {
16
+ for (const [id, val] of sessionsDb.entries()) {
17
+ if (val.parent_session_id === param) {
18
+ return { id };
19
+ }
20
+ }
21
+ return undefined;
22
+ }
23
+ return sessionsDb.get(param) || { parent_session_id: null };
24
+ }
25
+ if (lower.includes("from tool_calls") && lower.includes("todo_write")) {
26
+ const calls = toolCallsDb.get(param) || [];
27
+ const todo = calls.find((c) => c.tool_name === "todo_write");
28
+ return todo ? { args_json: todo.args_json } : undefined;
29
+ }
30
+ if (lower.includes("from compactions")) {
31
+ return compactionsDb.get(param);
32
+ }
33
+ return undefined;
34
+ },
35
+ all: (...params) => {
36
+ if (lower.includes("from messages")) {
37
+ return messagesDb.get(params[0]) || [];
38
+ }
39
+ if (lower.includes("from tool_results")) {
40
+ return toolResultsDb.get(params[0]) || [];
41
+ }
42
+ if (lower.includes("from sessions")) {
43
+ if (lower.includes("where parent_session_id =")) {
44
+ const results = [];
45
+ for (const [id, val] of sessionsDb.entries()) {
46
+ if (val.parent_session_id === params[0]) {
47
+ results.push({ id });
48
+ }
49
+ }
50
+ return results;
51
+ }
52
+ if (lower.includes("where id in")) {
53
+ return params.map((id) => ({ id }));
54
+ }
55
+ }
56
+ return [];
57
+ },
58
+ run: () => ({ changes: 0 }),
59
+ };
60
+ },
61
+ exec: () => undefined,
62
+ pragma: () => undefined,
63
+ transaction: (fn) => fn,
64
+ }),
65
+ withTransaction: (fn) => fn({}),
66
+ };
67
+ });
68
+ import { buildChatEntries, getLastTodoWriteArgs, getSessionChain } from "../transcript.js";
69
+ describe("Hierarchical Sessions (Silent Rotation Support)", () => {
70
+ beforeEach(() => {
71
+ sessionsDb.clear();
72
+ messagesDb.clear();
73
+ toolCallsDb.clear();
74
+ toolResultsDb.clear();
75
+ compactionsDb.clear();
76
+ });
77
+ describe("getSessionChain", () => {
78
+ it("returns correct chain for a single session", () => {
79
+ sessionsDb.set("sessionA", { parent_session_id: null });
80
+ expect(getSessionChain("sessionA")).toEqual(["sessionA"]);
81
+ });
82
+ it("traverses up the parent session chain recursively", () => {
83
+ sessionsDb.set("sessionA", { parent_session_id: null });
84
+ sessionsDb.set("sessionB", { parent_session_id: "sessionA" });
85
+ sessionsDb.set("sessionC", { parent_session_id: "sessionB" });
86
+ expect(getSessionChain("sessionC")).toEqual(["sessionA", "sessionB", "sessionC"]);
87
+ });
88
+ it("handles loops gracefully and does not infinite loop", () => {
89
+ sessionsDb.set("sessionA", { parent_session_id: "sessionB" });
90
+ sessionsDb.set("sessionB", { parent_session_id: "sessionA" });
91
+ // Should break loop and return traversed list in some order
92
+ const chain = getSessionChain("sessionB");
93
+ expect(chain.length).toBe(2);
94
+ expect(chain).toContain("sessionA");
95
+ expect(chain).toContain("sessionB");
96
+ });
97
+ });
98
+ describe("getLastTodoWriteArgs", () => {
99
+ it("returns null if no todo_write exists in chain", () => {
100
+ sessionsDb.set("sessionA", { parent_session_id: null });
101
+ expect(getLastTodoWriteArgs("sessionA")).toBeNull();
102
+ });
103
+ it("retrieves todo_write arguments from the current session", () => {
104
+ sessionsDb.set("sessionA", { parent_session_id: null });
105
+ toolCallsDb.set("sessionA", [
106
+ { id: 1, tool_call_id: "call_1", tool_name: "todo_write", args_json: '{"items":[]}' },
107
+ ]);
108
+ expect(getLastTodoWriteArgs("sessionA")).toBe('{"items":[]}');
109
+ });
110
+ it("retrieves todo_write arguments recursively from parent session", () => {
111
+ sessionsDb.set("sessionA", { parent_session_id: null });
112
+ sessionsDb.set("sessionB", { parent_session_id: "sessionA" });
113
+ toolCallsDb.set("sessionA", [
114
+ { id: 1, tool_call_id: "call_1", tool_name: "todo_write", args_json: '{"items":["task1"]}' },
115
+ ]);
116
+ // sessionB has no todo_write calls
117
+ expect(getLastTodoWriteArgs("sessionB")).toBe('{"items":["task1"]}');
118
+ });
119
+ });
120
+ describe("buildChatEntries", () => {
121
+ it("merges messages from parent and child session chronologically", () => {
122
+ sessionsDb.set("sessionA", { parent_session_id: null });
123
+ sessionsDb.set("sessionB", { parent_session_id: "sessionA" });
124
+ messagesDb.set("sessionA", [
125
+ {
126
+ seq: 1,
127
+ role: "user",
128
+ message_json: JSON.stringify({ role: "user", content: "Hello A" }),
129
+ created_at: "2026-06-25T12:00:00Z",
130
+ },
131
+ {
132
+ seq: 2,
133
+ role: "assistant",
134
+ message_json: JSON.stringify({ role: "assistant", content: "Reply A" }),
135
+ created_at: "2026-06-25T12:01:00Z",
136
+ },
137
+ ]);
138
+ messagesDb.set("sessionB", [
139
+ {
140
+ seq: 1,
141
+ role: "system",
142
+ // compaction summary (should be skipped since it's the first message of a child)
143
+ message_json: JSON.stringify({ role: "system", content: "[Context checkpoint summary]\nEarlier work" }),
144
+ created_at: "2026-06-25T12:02:00Z",
145
+ },
146
+ {
147
+ seq: 2,
148
+ role: "user",
149
+ message_json: JSON.stringify({ role: "user", content: "Hello B" }),
150
+ created_at: "2026-06-25T12:03:00Z",
151
+ },
152
+ ]);
153
+ const entries = buildChatEntries("sessionB");
154
+ expect(entries.length).toBe(3);
155
+ expect(entries[0]).toEqual(expect.objectContaining({ type: "user", content: "Hello A" }));
156
+ expect(entries[1]).toEqual(expect.objectContaining({ type: "assistant", content: "Reply A" }));
157
+ expect(entries[2]).toEqual(expect.objectContaining({ type: "user", content: "Hello B" }));
158
+ });
159
+ });
160
+ });
161
+ //# sourceMappingURL=hierarchical-session.test.js.map
@@ -89,8 +89,8 @@ describe("sweepStalePendingRows", () => {
89
89
  vi.spyOn(Date, "now").mockReturnValue(fixedNow);
90
90
  sweepStalePendingRows(60_000);
91
91
  const toolCallCall = preparedCalls.find((c) => c.sql.toLowerCase().includes("update tool_calls"));
92
- // run args: [now, cutoff]
93
- const cutoff = toolCallCall?.runArgs[1];
92
+ // run args: [cutoff]
93
+ const cutoff = toolCallCall?.runArgs[0];
94
94
  expect(cutoff).toBe("2026-05-22T09:59:00.000Z");
95
95
  });
96
96
  });
@@ -3,7 +3,7 @@ export { loadConfig, type MuonroiConfig } from "./config.js";
3
3
  export { getDatabasePath } from "./db.js";
4
4
  export { type InteractionEventType, logInteraction } from "./interaction-log.js";
5
5
  export { SessionStore } from "./sessions.js";
6
- export { appendCompaction, appendMessages, appendSystemMessage, buildChatEntries, getNextMessageSequence, loadLatestCompaction, loadRawTranscript, loadTranscript, loadTranscriptState, markMessageCompleted, markMessageErrored, markToolCallErrored, persistMessageWriteAhead, persistToolCallWriteAhead, } from "./transcript.js";
6
+ export { appendCompaction, appendMessages, appendSystemMessage, buildChatEntries, getLastTodoWriteArgs, getNextMessageSequence, getSessionChain, loadLatestCompaction, loadRawTranscript, loadTranscript, loadTranscriptState, markMessageCompleted, markMessageErrored, markToolCallErrored, persistMessageWriteAhead, persistToolCallWriteAhead, revertLatestCompaction, } from "./transcript.js";
7
7
  export { buildEffectiveTranscript, type LoadedTranscriptState, type PersistedCompaction } from "./transcript-view.js";
8
8
  export { logUIInteraction, type UIInteractionSubtype } from "./ui-interaction-log.js";
9
9
  export { getSessionTotalTokens, listSessionUsage, recordUsageEvent, type TokenUsageLike } from "./usage.js";
@@ -4,7 +4,7 @@ export { loadConfig } from "./config.js";
4
4
  export { getDatabasePath } from "./db.js";
5
5
  export { logInteraction } from "./interaction-log.js";
6
6
  export { SessionStore } from "./sessions.js";
7
- export { appendCompaction, appendMessages, appendSystemMessage, buildChatEntries, getNextMessageSequence, loadLatestCompaction, loadRawTranscript, loadTranscript, loadTranscriptState, markMessageCompleted, markMessageErrored, markToolCallErrored, persistMessageWriteAhead, persistToolCallWriteAhead, } from "./transcript.js";
7
+ export { appendCompaction, appendMessages, appendSystemMessage, buildChatEntries, getLastTodoWriteArgs, getNextMessageSequence, getSessionChain, loadLatestCompaction, loadRawTranscript, loadTranscript, loadTranscriptState, markMessageCompleted, markMessageErrored, markToolCallErrored, persistMessageWriteAhead, persistToolCallWriteAhead, revertLatestCompaction, } from "./transcript.js";
8
8
  export { buildEffectiveTranscript } from "./transcript-view.js";
9
9
  export { logUIInteraction } from "./ui-interaction-log.js";
10
10
  export { getSessionTotalTokens, listSessionUsage, recordUsageEvent } from "./usage.js";
@@ -1,4 +1,4 @@
1
- const LATEST_DB_VERSION = 7;
1
+ const LATEST_DB_VERSION = 8;
2
2
  export function applyMigrations(db) {
3
3
  const version = Number(db.pragma("user_version", { simple: true })) || 0;
4
4
  if (version >= LATEST_DB_VERSION)
@@ -87,6 +87,15 @@ export function applyMigrations(db) {
87
87
  }
88
88
  db.pragma("user_version = 7");
89
89
  }
90
+ if (version < 8) {
91
+ const cols = db.prepare("PRAGMA table_info(sessions)").all();
92
+ const colNames = new Set(cols.map((c) => c.name));
93
+ if (!colNames.has("parent_session_id")) {
94
+ db.exec("ALTER TABLE sessions ADD COLUMN parent_session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL");
95
+ }
96
+ db.exec("CREATE INDEX IF NOT EXISTS idx_sessions_parent_id ON sessions(parent_session_id)");
97
+ db.pragma("user_version = 8");
98
+ }
90
99
  });
91
100
  migrate();
92
101
  }
@@ -104,6 +113,7 @@ function createInitialSchema(db) {
104
113
  CREATE TABLE IF NOT EXISTS sessions (
105
114
  id TEXT PRIMARY KEY,
106
115
  workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
116
+ parent_session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
107
117
  title TEXT,
108
118
  model TEXT NOT NULL,
109
119
  mode TEXT NOT NULL,
@@ -172,6 +182,8 @@ function createInitialSchema(db) {
172
182
 
173
183
  CREATE INDEX IF NOT EXISTS idx_sessions_workspace_updated
174
184
  ON sessions(workspace_id, updated_at DESC);
185
+ CREATE INDEX IF NOT EXISTS idx_sessions_parent_id
186
+ ON sessions(parent_session_id);
175
187
  CREATE INDEX IF NOT EXISTS idx_messages_session_seq
176
188
  ON messages(session_id, seq);
177
189
  CREATE INDEX IF NOT EXISTS idx_tool_calls_session_seq
@@ -18,11 +18,24 @@ import { afterEach, describe, expect, it, vi } from "vitest";
18
18
  let MESSAGE_ROWS = [];
19
19
  vi.mock("./db", () => ({
20
20
  getDatabase: () => ({
21
- prepare: (sql) => ({
22
- all: () => (sql.includes("FROM messages") ? MESSAGE_ROWS : []),
23
- get: () => undefined,
24
- run: () => undefined,
25
- }),
21
+ prepare: (sql) => {
22
+ const lower = sql.toLowerCase();
23
+ return {
24
+ all: (...params) => {
25
+ if (lower.includes("from messages")) {
26
+ return MESSAGE_ROWS;
27
+ }
28
+ if (lower.includes("from sessions")) {
29
+ if (lower.includes("where id in")) {
30
+ return params.map((id) => ({ id }));
31
+ }
32
+ }
33
+ return [];
34
+ },
35
+ get: () => undefined,
36
+ run: () => undefined,
37
+ };
38
+ },
26
39
  }),
27
40
  withTransaction: (fn) => fn({}),
28
41
  }));
@@ -9,7 +9,13 @@ export function buildEffectiveTranscript(messages, seqs, timestamps, compaction)
9
9
  };
10
10
  }
11
11
  const firstKeptIndex = seqs.findIndex((seq) => seq >= compaction.firstKeptSeq);
12
- const keptIndex = firstKeptIndex >= 0 ? firstKeptIndex : messages.length;
12
+ let keptIndex = firstKeptIndex >= 0 ? firstKeptIndex : messages.length;
13
+ // Anti-mù / Resilience: if firstKeptSeq landed on a `tool` message (e.g. due to seq array
14
+ // misalignment or forced cut), we MUST step back to include its preceding `assistant` message.
15
+ // Otherwise Vercel AI SDK throws: "Messages with role 'tool' must be a response to a preceding message with 'tool_calls'"
16
+ if (keptIndex > 0 && messages[keptIndex]?.role === "tool") {
17
+ keptIndex -= 1;
18
+ }
13
19
  const keptMessages = messages.slice(keptIndex);
14
20
  const keptSeqs = seqs.slice(keptIndex);
15
21
  const keptTimestamps = timestamps.slice(keptIndex);
@@ -104,4 +104,7 @@ export declare function sweepStalePendingRows(staleAfterMs?: number): {
104
104
  };
105
105
  export declare function markToolCallErrored(sessionId: string, toolCallId: string, errorMessage: string): void;
106
106
  export declare function appendCompaction(sessionId: string, firstKeptSeq: number, summary: string, tokensBefore: number): void;
107
+ export declare function revertLatestCompaction(sessionId: string): void;
108
+ export declare function getSessionChain(sessionId: string): string[];
107
109
  export declare function buildChatEntries(sessionId: string): ChatEntry[];
110
+ export declare function getLastTodoWriteArgs(sessionId: string): string | null;