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
@@ -22,6 +22,7 @@ import * as crypto from "node:crypto";
22
22
  import { promises as fs } from "node:fs";
23
23
  import * as path from "node:path";
24
24
  import { getSessionDir } from "../storage/session-dir.js";
25
+ import { logger } from "../utils/logger.js";
25
26
  // ─── stableCallId ─────────────────────────────────────────────────────────────
26
27
  /**
27
28
  * Derives a deterministic call_id from (turnId, toolName, input).
@@ -160,7 +161,7 @@ export function createPendingCallsLog(sessionId) {
160
161
  void finalExists;
161
162
  }
162
163
  catch (err) {
163
- console.warn(`[muonroi-cli] reconcile: could not clean staged path ${tmp}: ${err.message}`);
164
+ logger.warn("orchestrator", `reconcile: could not clean staged path ${tmp}`, { error: err });
164
165
  }
165
166
  }
166
167
  // Mark the entry as abandoned in the log.
@@ -0,0 +1,13 @@
1
+ import { runPipeline } from "../pil/pipeline.js";
2
+ import type { StreamChunk } from "../types/index.js";
3
+ import type { MessageProcessorDeps } from "./message-processor.js";
4
+ import { type ComplexitySize } from "./scope-ceiling.js";
5
+ export interface PreprocessorResult {
6
+ pilCtx: Awaited<ReturnType<typeof runPipeline>>;
7
+ _stepCeiling: number;
8
+ _pilStart: number;
9
+ _naturalCeiling: number;
10
+ _ceilingTaskType: string;
11
+ _ceilingSize: ComplexitySize;
12
+ }
13
+ export declare function prepareTurnContext(deps: MessageProcessorDeps, userMessage: string, _budgetOverride: any): AsyncGenerator<StreamChunk, PreprocessorResult, unknown>;
@@ -0,0 +1,146 @@
1
+ import { runPipeline } from "../pil/pipeline.js";
2
+ import { getSessionLastTask, recordSessionLastTask, resolveCeiling } from "./scope-ceiling.js";
3
+ import { logger } from "../utils/logger.js";
4
+ export async function* prepareTurnContext(deps, userMessage, _budgetOverride) {
5
+ // PIL: enrich prompt before pushing to messages (D-01, D-03, D-04)
6
+ // Promise.race timeout of 200ms is inside runPipeline — fail-open guaranteed
7
+ // --- PIL with discovery (interactive path) ---
8
+ const pilChunkQueue = [];
9
+ const pilResponder = deps.councilManager.createQuestionResponder();
10
+ const discoveryHandler = {
11
+ askQuestion: async (question) => {
12
+ pilChunkQueue.push({
13
+ type: "council_question",
14
+ content: question.question,
15
+ councilQuestion: question,
16
+ });
17
+ const text = await pilResponder(question.questionId);
18
+ return { questionId: question.questionId, text, kind: "choice" };
19
+ },
20
+ };
21
+ const _pilStart = Date.now();
22
+ let pilCtxResolved = null;
23
+ let pilDone = false;
24
+ const pilTask = (async () => {
25
+ try {
26
+ // Build Pass 4 LLM fallback closure using the orchestrator's already-
27
+ // constructed provider factory + current model. PIL stays ignorant of
28
+ // provider wiring — it just receives a `classify(prompt)` callback.
29
+ let llmFallback;
30
+ try {
31
+ const { createLlmClassifier } = await import("../pil/llm-classify.js");
32
+ llmFallback = createLlmClassifier(deps.requireProvider(), deps.modelId);
33
+ }
34
+ catch (err) {
35
+ logger.error("pil", "LLM fallback wiring failed", { error: err });
36
+ }
37
+ // Model-driven clarification proposer (for discovery interview).
38
+ // The actual task model (via the same provider + modelId) generates the
39
+ // questions based on raw + CLI enrichment. Then discovery asks user.
40
+ let clarificationProposer;
41
+ try {
42
+ const { createModelClarificationProposer } = await import("../pil/discovery.js");
43
+ clarificationProposer = createModelClarificationProposer(deps.requireProvider(), deps.modelId);
44
+ }
45
+ catch (err) {
46
+ logger.error("pil", "clarification proposer wiring failed", { error: err });
47
+ }
48
+ pilCtxResolved = await runPipeline(userMessage, {
49
+ resumeDigest: deps.getResumeDigest(),
50
+ activeRunId: deps.getActiveRunId(),
51
+ sessionId: deps.session?.id ?? null,
52
+ interactionHandler: discoveryHandler,
53
+ llmFallback,
54
+ clarificationProposer,
55
+ recentTurnsSummary: deps.buildRecentTurnsSummary(),
56
+ });
57
+ }
58
+ catch (err) {
59
+ pilCtxResolved = {
60
+ raw: userMessage,
61
+ enriched: userMessage,
62
+ taskType: null,
63
+ domain: null,
64
+ confidence: 0,
65
+ outputStyle: null,
66
+ tokenBudget: 500,
67
+ metrics: null,
68
+ layers: [],
69
+ gsdPhase: null,
70
+ activeRunId: null,
71
+ intentKind: null,
72
+ fallbackReason: err instanceof Error ? `orchestrator-catch:${err.name}` : "orchestrator-catch:unknown",
73
+ };
74
+ }
75
+ finally {
76
+ pilDone = true;
77
+ }
78
+ })();
79
+ while (!pilDone) {
80
+ while (pilChunkQueue.length > 0) {
81
+ yield pilChunkQueue.shift();
82
+ }
83
+ if (!pilDone) {
84
+ await new Promise((resolve) => setImmediate(resolve));
85
+ }
86
+ }
87
+ while (pilChunkQueue.length > 0) {
88
+ yield pilChunkQueue.shift();
89
+ }
90
+ await pilTask;
91
+ const pilCtx = pilCtxResolved;
92
+ // Phase 4 Plan 04 (4B) — resolve per-session step ceiling using
93
+ // (task_type × complexitySize) matrix. Override (from --budget-rounds N
94
+ // parsed earlier) wins. When the override differs from the natural
95
+ // ceiling, emit info toast so the user sees the explicit cap.
96
+ //
97
+ // Phase 5 Fix 2 — continuation phrases ("tiếp tục" / "continue") are
98
+ // classified `general/chitchat` by PIL Layer 1 Pass 0. Resolving the
99
+ // ceiling from that label collapses the budget to general × small = 5,
100
+ // which is wrong: the user wants the agent to RESUME the prior task,
101
+ // not start a generic chitchat. When this session has a recorded
102
+ // non-chitchat task row, inherit it for ceiling resolution. The Pass 0
103
+ // classification itself stays general so downstream code (style /
104
+ // chitchat skip / tools-empty optimization in `BUG-A guard`) reads the
105
+ // correct intent; only the ceiling row is borrowed.
106
+ const _pilTaskType = pilCtx.taskType ?? "general";
107
+ const _pilSize = pilCtx.complexitySize?.size ?? "medium";
108
+ const _sessionIdForLastTask = deps.session?.id ?? "";
109
+ const _isContinuationChitchat = _pilTaskType === "general" && pilCtx.intentKind === "chitchat" && _sessionIdForLastTask !== "";
110
+ const _lastTask = _isContinuationChitchat ? getSessionLastTask(_sessionIdForLastTask) : null;
111
+ const _ceilingTaskType = _lastTask?.taskType ?? _pilTaskType;
112
+ const _ceilingSize = _lastTask?.size ?? _pilSize;
113
+ const _naturalCeiling = resolveCeiling(_ceilingTaskType, _ceilingSize);
114
+ // Phase 5 Fix 4 (Option A) — make ceiling mutable so the stopWhen
115
+ // closure can bump it on auto-continue checkpoints. See checkpoint
116
+ // logic at dynamicStopWhen below for the bump policy.
117
+ const _stepCeiling = _budgetOverride.override ?? _naturalCeiling;
118
+ // Record this turn's task row for future continuation inheritance.
119
+ // Only non-chitchat task turns update the slot.
120
+ if (_sessionIdForLastTask && _pilTaskType !== "general" && pilCtx.intentKind === "task") {
121
+ recordSessionLastTask(_sessionIdForLastTask, _pilTaskType, _pilSize);
122
+ }
123
+ if (_budgetOverride.override !== undefined && _budgetOverride.override !== _naturalCeiling) {
124
+ try {
125
+ const _ar = globalThis.__muonroiAgentRuntime;
126
+ _ar?.emitEvent({
127
+ t: "event",
128
+ kind: "toast",
129
+ level: "info",
130
+ text: `override active: ceiling ${_budgetOverride.override}, default was ${_naturalCeiling} (task=${_ceilingTaskType}/size=${_ceilingSize})`,
131
+ });
132
+ }
133
+ catch {
134
+ /* best-effort */
135
+ }
136
+ }
137
+ return {
138
+ pilCtx,
139
+ _stepCeiling,
140
+ _pilStart,
141
+ _naturalCeiling,
142
+ _ceilingTaskType,
143
+ _ceilingSize: _ceilingSize,
144
+ };
145
+ }
146
+ //# sourceMappingURL=preprocessor.js.map
@@ -1,6 +1,8 @@
1
1
  import type { AgentMode, TaskRequest } from "../types/index.js";
2
2
  import { type CustomSubagentConfig, type SandboxMode, type SandboxSettings } from "../utils/settings.js";
3
3
  export declare const MAX_TOOL_ROUNDS: number;
4
+ export declare const HARD_MAX_TOOL_ROUNDS: number;
5
+ export declare const MAX_LLM_CALLS_PER_TURN: number;
4
6
  export declare const VISION_MODEL = "grok-4-1-fast-reasoning";
5
7
  export declare const COMPUTER_MODEL = "grok-4.20-0309-reasoning";
6
8
  /**
@@ -55,6 +57,18 @@ export interface SystemPromptOptions {
55
57
  * PIL Layer 1 (intentKind === "chitchat").
56
58
  */
57
59
  chitchat?: boolean;
60
+ /**
61
+ * When true (sub-agent), skip CUSTOM INSTRUCTIONS, skills catalog, and
62
+ * native capabilities — sub-agents don't need project-level instructions
63
+ * and can't run the full toolset anyway. Cuts ~6K tokens per sub-agent turn.
64
+ */
65
+ subAgent?: boolean;
66
+ /**
67
+ * When true (tool-turn, i.e. second+ LLM call in the same user-turn tool
68
+ * loop), skip native-capabilities and skills sections that were already
69
+ * shown in the first call. Cuts ~4K tokens per tool round-trip.
70
+ */
71
+ toolTurn?: boolean;
58
72
  }
59
73
  /**
60
74
  * Render the LIVE per-turn MCP tool roster as a system-prompt block.
@@ -76,6 +90,8 @@ export interface SystemPromptOptions {
76
90
  */
77
91
  export declare function buildMcpCapabilityBlock(toolNames: readonly string[]): string;
78
92
  export declare function buildSystemPromptParts(cwd: string, mode: AgentMode, sandboxMode: SandboxMode, planContext?: string | null, subagents?: CustomSubagentConfig[], sandboxSettings?: SandboxSettings, providerId?: string, resumeDigest?: string | null, options?: SystemPromptOptions): SystemPromptParts;
93
+ /** Reset the static prefix cache (for tests). */
94
+ export declare function resetStaticPrefixCache(): void;
79
95
  export declare function buildSystemPrompt(cwd: string, mode: AgentMode, sandboxMode: SandboxMode, planContext?: string | null, subagents?: CustomSubagentConfig[], sandboxSettings?: SandboxSettings, providerId?: string, resumeDigest?: string | null, options?: SystemPromptOptions): string;
80
96
  export declare function buildSubagentPrompt(request: TaskRequest, cwd: string, custom: CustomSubagentConfig | null, sandboxMode: SandboxMode, subagents?: CustomSubagentConfig[], sandboxSettings?: SandboxSettings, providerId?: string): string;
81
97
  export declare function formatSandboxPromptSection(sandboxMode: SandboxMode, settings?: SandboxSettings): string;
@@ -4,22 +4,67 @@ import { buildContractSection } from "../pil/agent-operating-contract.js";
4
4
  import { buildNativeCapabilitiesSection } from "../pil/native-capabilities-workbook.js";
5
5
  import { getProviderCapabilities } from "../providers/capabilities.js";
6
6
  import { loadCustomInstructions } from "../utils/instructions.js";
7
- import { loadValidSubAgents, } from "../utils/settings.js";
7
+ import { logger } from "../utils/logger.js";
8
+ import { loadUserSettings, loadValidSubAgents, } from "../utils/settings.js";
8
9
  import { resolveShell } from "../utils/shell.js";
9
10
  import { discoverSkills, formatSkillsForPrompt } from "../utils/skills.js";
10
- // F3 — hard cap on tool rounds per user turn. Default reduced 75 50
11
- // after session bca83bcbaad1 logged 178 tool calls in a single turn while
12
- // monotonically growing billed input. Env override allowed range 10..200.
11
+ // F3a — hard cap on tool rounds per user turn. Reduced 10040
12
+ // after session 526a83cf22df logged 2.44M input tokens over 46 LLM calls
13
+ // with 3 turns (seq 33/81/1) consuming 82% of tokens.
14
+ // Env override allowed range 10..400.
15
+ // Env override allowed range 10..400 (or up to 2000 in agent-first).
13
16
  function readMaxToolRoundsFromEnv() {
14
- const raw = process.env.MUONROI_MAX_TOOL_ROUNDS;
17
+ const settings = loadUserSettings();
18
+ const agentFirst = settings.agentFirst !== false &&
19
+ process.env.MUONROI_AGENT_FIRST !== "0" &&
20
+ process.env.MUONROI_AGENT_FIRST !== "false";
21
+ const raw = process.env.MUONROI_MAX_TOOL_ROUNDS || settings.maxToolRounds;
15
22
  if (!raw)
16
- return 12;
23
+ return agentFirst ? 200 : 40;
17
24
  const n = Number(raw);
18
25
  if (!Number.isFinite(n))
19
- return 50;
20
- return Math.max(10, Math.min(200, Math.floor(n)));
26
+ return agentFirst ? 200 : 40;
27
+ const maxLimit = agentFirst ? 2000 : 400;
28
+ return Math.max(10, Math.min(maxLimit, Math.floor(n)));
21
29
  }
22
30
  export const MAX_TOOL_ROUNDS = readMaxToolRoundsFromEnv();
31
+ // F3b — HARD cap: absolute non-bumpable ceiling per user turn.
32
+ // Fires AFTER the soft cap has been bumped by the user.
33
+ // Env override allowed range 20..400 (or up to 3000 in agent-first).
34
+ function readHardMaxToolRoundsFromEnv() {
35
+ const settings = loadUserSettings();
36
+ const agentFirst = settings.agentFirst !== false &&
37
+ process.env.MUONROI_AGENT_FIRST !== "0" &&
38
+ process.env.MUONROI_AGENT_FIRST !== "false";
39
+ const raw = process.env.MUONROI_HARD_MAX_TOOL_ROUNDS || settings.hardMaxToolRounds;
40
+ if (!raw)
41
+ return agentFirst ? 300 : 60;
42
+ const n = Number(raw);
43
+ if (!Number.isFinite(n))
44
+ return agentFirst ? 300 : 60;
45
+ const maxLimit = agentFirst ? 3000 : 400;
46
+ return Math.max(20, Math.min(maxLimit, Math.floor(n)));
47
+ }
48
+ export const HARD_MAX_TOOL_ROUNDS = readHardMaxToolRoundsFromEnv();
49
+ // F3c — per-turn LLM call cap: how many streamText() invocations are
50
+ // allowed per user turn.
51
+ // Default 12 (or 100 in agent-first).
52
+ // Env override MUONROI_MAX_LLM_CALLS_PER_TURN, range 3..100 (or up to 500 in agent-first).
53
+ function readMaxLlmCallsPerTurn() {
54
+ const settings = loadUserSettings();
55
+ const agentFirst = settings.agentFirst !== false &&
56
+ process.env.MUONROI_AGENT_FIRST !== "0" &&
57
+ process.env.MUONROI_AGENT_FIRST !== "false";
58
+ const raw = process.env.MUONROI_MAX_LLM_CALLS_PER_TURN || settings.maxLlmCallsPerTurn;
59
+ if (!raw)
60
+ return agentFirst ? 100 : 12;
61
+ const n = Number(raw);
62
+ if (!Number.isFinite(n))
63
+ return agentFirst ? 100 : 12;
64
+ const maxLimit = agentFirst ? 500 : 100;
65
+ return Math.max(3, Math.min(maxLimit, Math.floor(n)));
66
+ }
67
+ export const MAX_LLM_CALLS_PER_TURN = readMaxLlmCallsPerTurn();
23
68
  export const VISION_MODEL = "grok-4-1-fast-reasoning";
24
69
  export const COMPUTER_MODEL = "grok-4.20-0309-reasoning";
25
70
  /**
@@ -62,9 +107,7 @@ export function detectProjectStack(cwd) {
62
107
  // Best-effort enrichment: a missing/unreadable cwd simply omits the stack
63
108
  // line (the ENVIRONMENT cwd line already surfaces "<unknown>"). Debug-gated
64
109
  // so prompt assembly never corrupts the TUI at startup.
65
- if (process.env.MUONROI_DEBUG === "1") {
66
- console.error(`[orchestrator/prompts] detectProjectStack failed for ${cwd}: ${err?.message}`);
67
- }
110
+ logger.error("orchestrator", `detectProjectStack failed for ${cwd}`, { error: err });
68
111
  return "";
69
112
  }
70
113
  const has = (name) => entries.includes(name);
@@ -143,6 +186,7 @@ function buildEnvironmentBlock() {
143
186
  if (platform === "win32") {
144
187
  shellRules.push('- When a Windows-native command is genuinely needed, invoke it explicitly: `cmd.exe /c "command"` or `powershell -NoProfile -Command "command"`.');
145
188
  }
189
+ shellRules.push("- CRITICAL: Batch independent commands in ONE call with `&&` or `;` instead of N sequential calls — each extra call adds ~500 tokens of overhead and prevents cross-request cache reuse. Examples: `ls && cat file` or `a; b; c; d` instead of four separate bash calls.");
146
190
  }
147
191
  else if (shell.kind === "powershell") {
148
192
  shellRules.push("- The bash tool runs PowerShell. Use PowerShell cmdlets: Get-ChildItem, Select-String, Measure-Object, ConvertTo-Json, $env:VAR.", "- DO NOT use POSIX-only commands: grep, sed, awk, wc (use Select-String / Measure-Object / -split instead).", "- For pipe redirection, use PowerShell syntax: `cmd | Select-Object -First 10`, not `cmd | head -10`.");
@@ -272,11 +316,28 @@ IMPORTANT:
272
316
  - Use write_file only for new files or when most of the file is changing. For very large files (>500 lines), split into multiple edit_file calls or write smaller chunks.
273
317
  - Use read_file instead of cat/head/tail for reading files.
274
318
  - When the user asks for an automated recurring or one-time run, use the schedule tools instead of only describing the setup.
275
- - Commit incrementally (in any git repo, without being asked): the moment a cohesive, working chunk is finished and its checks pass, commit it before starting the next chunk. This is part of doing the work, not a separate request — prefer several small, logically-scoped commits over one large catch-all commit at the end. Group related file changes per commit; keep unrelated changes in separate commits.
276
- - Every commit message you create MUST end with this attribution line, verbatim, on its own final line: "Coding by - Muonroi-CLI".
319
+ - If you have worked for a long time or hit a tool execution limit, DO NOT tell the user to move to a new session. Instead, advise them to run the \`/compact\` command to free up memory before continuing.
320
+ - Use the experience brain actively (it is how you stop repeating mistakes across sessions): at the start of an unfamiliar or risky step call ee_query to recall past lessons, and after acting on a recalled \`[id col]\` rate it with ee_feedback. The MOMENT you hit a mistake / error / dead-end and find the working fix, call ee_write to save the lesson (the pitfall AND the fix, concise and generalizable) it is embedded immediately and recallable via ee_query in this and future sessions. Saving a hard-won fix is part of doing the work, not optional.
321
+ - Commit your own work as you go (in any git repo, without being asked): use the git_commit tool — YOU write the commit message — the moment a cohesive, working chunk passes its checks, and after EACH step of a multi-step plan. Prefer several small, logically-scoped commits with clear messages (describe WHAT changed) over one catch-all at the end. git_commit stages only the files you wrote, excludes secrets/artifacts, and appends the "Coding by - Muonroi-CLI" attribution for you. (Any commit you instead make by hand via bash must still end with that attribution line, verbatim, on its own final line.)
277
322
  - After creating a recurring schedule, check the daemon status and start it with \`schedule_daemon_start\` if needed.
278
323
 
279
- Be direct. Execute, don't just describe. Show results, not plans.`,
324
+
325
+ Be direct. Execute, don't just describe. Show results, not plans.
326
+
327
+ TOKEN BUDGET:
328
+ - Each tool round sends ~17K system prompt tokens + accumulated tool results to the model.
329
+ - Task(explore) / task(general) isolates context in a sub-agent — much cheaper than 5+ top-level rounds.
330
+ - Consider: 1-2 rounds → direct; 3-5 rounds → consider task(explore); >5 rounds → should use task(explore).
331
+ WORKFLOW RULES:
332
+ - RESEARCH FIRST: Always prioritize research before proposing edits. DeepSeek and other models have knowledge cutoffs; do not assume you know the exact codebase structure or latest external libraries. Use 'grep', 'lsp', and 'read_file' to search the local codebase. Use MCP tools (like web search or documentation readers) to research external knowledge, APIs, or libraries. Use 'delegate' for deep background research. Read before you write.
333
+ - CLARIFY GRAY AREAS: If the user's request is ambiguous or leaves critical design decisions unspecified, STOP and ask the user for clarification before writing code. Do not hallucinate requirements.
334
+
335
+ SELF-LIMIT:
336
+ - When you've read 5+ files and haven't concluded, summarize findings and propose next step instead of reading more.
337
+ - BATCH TOOL CALLS: You MUST combine and invoke independent tool calls in parallel (e.g. read multiple files, or run grep and read a file concurrently) in a SINGLE turn. Do not wait for the result of one tool call before invoking another if you already know both are needed. This dramatically reduces conversation turns, roundtrip latency, and input token accumulation.
338
+ - BATCH BASH COMMANDS: Combine independent commands into ONE bash call (a; b; c) rather than sequential single calls — each separate call adds ~500 tokens of overhead and prevents prompt-cache reuse across the session.
339
+ - Read only specific file sections (start_line/end_line) instead of whole files.
340
+ - When a clear direction emerges from the first 2-3 tool results, act on it — don't over-investigate.`,
280
341
  plan: `You are muonroi-cli in Plan mode — you analyze and plan but DO NOT execute changes.
281
342
 
282
343
  ${ENVIRONMENT}
@@ -386,40 +447,64 @@ export function buildMcpCapabilityBlock(toolNames) {
386
447
  "to bash or hand-write JSON-RPC to reach an MCP server:\n" +
387
448
  lines.join("\n"));
388
449
  }
389
- export function buildSystemPromptParts(cwd, mode, sandboxMode, planContext, subagents, sandboxSettings, providerId, resumeDigest, options) {
390
- const chitchat = options?.chitchat === true;
450
+ const _staticPrefixCache = new Map();
451
+ const STATIC_PREFIX_CACHE_TTL_MS = 300_000; // 5 min — ample; inputs are session-stable
452
+ function staticPrefixCacheKey(cwd, mode, providerId, isChitchat, subagentsHash, subAgent = false, toolTurn = false) {
453
+ return `${cwd}|${mode}|${providerId}|${isChitchat}|${subagentsHash}|${subAgent}|${toolTurn}`;
454
+ }
455
+ function computeStaticPrefix(cwd, mode, subagents, providerId, chitchat, subAgent = false, toolTurn = false) {
391
456
  const custom = loadCustomInstructions(cwd);
392
- const customSection = custom
393
- ? `\n\nCUSTOM INSTRUCTIONS:\n${custom}\n\nFollow the above alongside standard instructions.\n`
394
- : "";
395
- // Skip skills + subagents catalogs for chitchat these are pure tool/agent
396
- // routing context the model doesn't need to answer "Hi". On this codebase
397
- // they account for ~3K tokens per call.
398
- const skillsText = chitchat ? "" : formatSkillsForPrompt(discoverSkills(cwd));
457
+ const customSection = subAgent || !custom
458
+ ? ""
459
+ : `\n\nCUSTOM INSTRUCTIONS:\n${custom}\n\nFollow the above alongside standard instructions.\n`;
460
+ // Tool-turn: skip agent-skills catalog (~2K tokens) and native-capabilities block (~2K tokens).
461
+ // The agent was already shown these in the first call of this turn and does not need
462
+ // to re-read them on every tool round-trip.
463
+ const skillsText = chitchat || subAgent || toolTurn ? "" : formatSkillsForPrompt(discoverSkills(cwd));
399
464
  const skillsSection = skillsText ? `\n\n${skillsText}\n` : "";
400
465
  const subagentsSection = chitchat ? "" : formatCustomSubagentsPromptSection(subagents ?? loadValidSubAgents());
401
- const sandboxSection = formatSandboxPromptSection(sandboxMode, sandboxSettings);
402
466
  let modePrompt = MODE_PROMPTS[mode];
403
- // Phase 12.2-G5: defer the "is this an anthropic-style prompt or not?"
404
- // decision to the provider capability. Default (no providerId) keeps the
405
- // pre-G5 behaviour by treating the prompt as anthropic-native.
406
467
  if (!providerId)
407
468
  throw new Error("providerId is required to build system prompt — cannot determine prompt style.");
408
469
  const promptStyle = getProviderCapabilities(providerId).systemPromptStyle();
409
470
  if (promptStyle !== "anthropic") {
410
471
  modePrompt = stripToolsSection(modePrompt) + NON_ANTHROPIC_TOOL_PREAMBLE;
411
472
  }
412
- // Front-load the Agent Operating Contract (all tiers) so the
413
- // grounding/evidence discipline registers the same rules buried in
414
- // CUSTOM INSTRUCTIONS get underweighted when not front-loaded. Skipped for
415
- // chitchat (no tools, no factual claims). See agent-operating-contract.ts.
473
+ // Agent mode: strip tool descriptions for tools rarely needed in coding tasks
474
+ // to reduce system-prompt bloat. The tools remain available via API.
475
+ if (mode === "agent") {
476
+ modePrompt = modePrompt.replace(/\n- (wallet_|paid_|fetch_payment|schedule_|generate_|computer_|search_x).*/g, "");
477
+ }
416
478
  const contractSection = buildContractSection({ chitchat });
417
- // Native capability manifest (agent mode only) gives the in-CLI agent a
418
- // self-model of the tools, sub-agents, and CLI subsystems it has, so it
419
- // wields them instead of reconstructing its own runtime by grepping source.
420
- // See native-capabilities-workbook.ts (session d95113d3be09 motivation).
421
- const nativeCapabilitiesSection = buildNativeCapabilitiesSection({ mode, chitchat });
422
- const staticPrefix = `${contractSection}${nativeCapabilitiesSection}${modePrompt}${sandboxSection}${customSection}${skillsSection}${subagentsSection}`;
479
+ const nativeCapabilitiesSection = toolTurn ? "" : buildNativeCapabilitiesSection({ mode, chitchat });
480
+ const prefix = `${contractSection}${nativeCapabilitiesSection}${modePrompt}${customSection}${skillsSection}${subagentsSection}`;
481
+ return { prefix };
482
+ }
483
+ export function buildSystemPromptParts(cwd, mode, sandboxMode, planContext, subagents, sandboxSettings, providerId, resumeDigest, options) {
484
+ const chitchat = options?.chitchat === true;
485
+ const subAgent = options?.subAgent ?? false;
486
+ const toolTurn = options?.toolTurn === true;
487
+ const pid = providerId ?? "default";
488
+ // Subagents rarely change mid-session, but when they do we need a cache miss.
489
+ // JSON-stable stringify is fast for typical configs (< 10 entries, no circular refs).
490
+ const subagentsHash = subagents ? JSON.stringify(subagents) : "none";
491
+ // Try cache for the static prefix
492
+ const key = staticPrefixCacheKey(cwd, mode, pid, chitchat, subagentsHash, subAgent, toolTurn);
493
+ const now = Date.now();
494
+ const cached = _staticPrefixCache.get(key);
495
+ let staticPrefix;
496
+ if (cached && now - cached.cachedAt < STATIC_PREFIX_CACHE_TTL_MS) {
497
+ staticPrefix = cached.prefix;
498
+ }
499
+ else {
500
+ // Cache miss — compute and store
501
+ const result = computeStaticPrefix(cwd, mode, subagents, pid, chitchat, subAgent, toolTurn);
502
+ staticPrefix = result.prefix;
503
+ _staticPrefixCache.set(key, {
504
+ prefix: staticPrefix,
505
+ cachedAt: now,
506
+ });
507
+ }
423
508
  const planSection = planContext
424
509
  ? `\n\nAPPROVED PLAN:\nThe following plan has been approved by the user. Execute it now.\n${planContext}\n`
425
510
  : "";
@@ -429,6 +514,10 @@ export function buildSystemPromptParts(cwd, mode, sandboxMode, planContext, suba
429
514
  const dynamicSuffix = `${planSection}${resumeSection}\n\nCurrent working directory: ${cwd}`;
430
515
  return { staticPrefix, dynamicSuffix };
431
516
  }
517
+ /** Reset the static prefix cache (for tests). */
518
+ export function resetStaticPrefixCache() {
519
+ _staticPrefixCache.clear();
520
+ }
432
521
  export function buildSystemPrompt(cwd, mode, sandboxMode, planContext, subagents, sandboxSettings, providerId, resumeDigest, options) {
433
522
  const { staticPrefix, dynamicSuffix } = buildSystemPromptParts(cwd, mode, sandboxMode, planContext, subagents, sandboxSettings, providerId, resumeDigest, options);
434
523
  return `${staticPrefix}${dynamicSuffix}`;
@@ -556,39 +645,13 @@ export function buildSubagentPrompt(request, cwd, custom, sandboxMode, subagents
556
645
  "",
557
646
  `Delegated task: ${request.description}`,
558
647
  "",
559
- buildSystemPrompt(cwd, mode, sandboxMode, undefined, subagents, sandboxSettings, providerId),
648
+ buildSystemPrompt(cwd, mode, sandboxMode, undefined, subagents, sandboxSettings, providerId, undefined, {
649
+ subAgent: true,
650
+ }),
560
651
  ].join("\n");
561
652
  }
562
653
  export function formatSandboxPromptSection(sandboxMode, settings) {
563
- if (sandboxMode === "off")
564
- return "";
565
- const s = settings ?? {};
566
- let networkLine;
567
- if (s.allowNet) {
568
- networkLine = s.allowedHosts?.length
569
- ? `- Network access is restricted to: ${s.allowedHosts.join(", ")}.`
570
- : "- Network access is enabled.";
571
- }
572
- else {
573
- networkLine = "- Network is disabled.";
574
- }
575
- const lines = [
576
- "",
577
- "SANDBOX MODE:",
578
- "- Bash commands run inside a Shuru sandbox.",
579
- networkLine,
580
- "- The current workspace is mounted inside the sandbox at `/workspace`.",
581
- "- Shell-side workspace file changes do not persist back to the host in this version.",
582
- "- Use `read_file`, `edit_file`, and `write_file` for durable source edits.",
583
- "- If a task needs a host-persistent shell mutation, explain that sandbox mode blocks that workflow and ask whether to disable sandbox mode.",
584
- ];
585
- if (s.ports?.length) {
586
- lines.push(`- Port forwards: ${s.ports.join(", ")}.`);
587
- }
588
- if (s.from) {
589
- lines.push(`- Starting from checkpoint: ${s.from}.`);
590
- }
591
- return lines.join("\n");
654
+ return "";
592
655
  }
593
656
  export function applyModelConstraints(system, modelId) {
594
657
  const modelInfo = getModelInfo(modelId);
@@ -0,0 +1,66 @@
1
+ /**
2
+ * src/orchestrator/safety-askcard.ts
3
+ *
4
+ * Plans an askcard layout when a tool call is blocked by the safety filter
5
+ * (catastrophic, dangerous, git-safety, empty-bash).
6
+ *
7
+ * The askcard gives the user a choice: allow the block once, or confirm the block.
8
+ *
9
+ * Pure — no React, no side effects. Unit-testable in isolation.
10
+ */
11
+ export type SafetyBlockKind = "catastrophic" | "dangerous" | "git-safety" | "empty-bash";
12
+ export interface SafetyBlockInfo {
13
+ kind: SafetyBlockKind;
14
+ /** The blocked command or tool call description */
15
+ blockedItem: string;
16
+ /** Human-readable reason from the filter */
17
+ reason: string;
18
+ /** Suggested "safe" alternative, if any */
19
+ alternative?: string;
20
+ }
21
+ export interface SafetyAskcardLayout {
22
+ /** Question shown in the dialog title */
23
+ question: string;
24
+ /** Detail text shown below the question */
25
+ detail: string;
26
+ /** Options in render order. First = default (Enter applies). */
27
+ options: SafetyAskcardOption[];
28
+ /** Index of the default-selected option (0 = first). */
29
+ defaultIndex: number;
30
+ }
31
+ export interface SafetyAskcardOption {
32
+ label: string;
33
+ value: SafetyAskResult;
34
+ description?: string;
35
+ }
36
+ export type SafetyAskResult = "allow-once" | "allow-session" | "block";
37
+ /**
38
+ * Plan the askcard layout for a safety block.
39
+ * Pure function — easy to unit test and swap UI strategies.
40
+ */
41
+ export declare function planSafetyAskcard(info: SafetyBlockInfo): SafetyAskcardLayout;
42
+ /** Info passed to the safety-override handler when a tool block is detected. */
43
+ export interface SafetyOverrideAskInfo {
44
+ kind: SafetyBlockKind;
45
+ /** The blocked tool name (e.g. "bash", "edit_file"). */
46
+ toolName: string;
47
+ /** The command/args that were blocked. */
48
+ blockedItem: string;
49
+ /** Human-readable reason from the filter. */
50
+ reason: string;
51
+ /** Source block kind tag for the agent to decide retry. */
52
+ source: "bash.execute" | "registry.precheck";
53
+ }
54
+ /** Verdict the UI returns after the user answers the askcard. */
55
+ export type SafetyOverrideVerdict = {
56
+ action: "allow-once";
57
+ } | {
58
+ action: "allow-session";
59
+ } | {
60
+ action: "block";
61
+ };
62
+ /**
63
+ * Build a safe alternative suggestion for a blocked command.
64
+ * Returns null when no canned alternative exists.
65
+ */
66
+ export declare function suggestAlternative(kind: SafetyBlockKind, command: string): string | undefined;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * src/orchestrator/safety-askcard.ts
3
+ *
4
+ * Plans an askcard layout when a tool call is blocked by the safety filter
5
+ * (catastrophic, dangerous, git-safety, empty-bash).
6
+ *
7
+ * The askcard gives the user a choice: allow the block once, or confirm the block.
8
+ *
9
+ * Pure — no React, no side effects. Unit-testable in isolation.
10
+ */
11
+ const CATEGORY_LABELS = {
12
+ catastrophic: "Lỗi bảo mật nghiêm trọng",
13
+ dangerous: "Lệnh nguy hiểm",
14
+ "git-safety": "Git safety gate",
15
+ "empty-bash": "Bash call trống",
16
+ };
17
+ /**
18
+ * Plan the askcard layout for a safety block.
19
+ * Pure function — easy to unit test and swap UI strategies.
20
+ */
21
+ export function planSafetyAskcard(info) {
22
+ const kindLabel = CATEGORY_LABELS[info.kind];
23
+ const cmdPreview = info.blockedItem.length > 120 ? `${info.blockedItem.slice(0, 117)}...` : info.blockedItem;
24
+ const question = `⚠️ ${kindLabel}: Cho phép thực thi?`;
25
+ const detailLines = [`Lệnh bị chặn: \`${cmdPreview}\``, `Lý do: ${info.reason}`];
26
+ if (info.alternative) {
27
+ detailLines.push(`Gợi ý thay thế: ${info.alternative}`);
28
+ }
29
+ const options = [
30
+ {
31
+ label: "Cho phép 1 lần (Allow once)",
32
+ value: "allow-once",
33
+ description: "Chỉ chạy lệnh này một lần duy nhất",
34
+ },
35
+ {
36
+ label: "Chặn (Block)",
37
+ value: "block",
38
+ description: "Không chạy lệnh này, trả về lỗi cho agent",
39
+ },
40
+ ];
41
+ // Allow-session only for less severe blocks
42
+ if (info.kind !== "catastrophic") {
43
+ options.splice(1, 0, {
44
+ label: "Cho phép cả phiên (Allow session)",
45
+ value: "allow-session",
46
+ description: "Cho phép tất cả lệnh tương tự trong phiên này",
47
+ });
48
+ }
49
+ return {
50
+ question,
51
+ detail: detailLines.join("\n"),
52
+ options,
53
+ defaultIndex: options.findIndex((o) => o.value === "block"),
54
+ };
55
+ }
56
+ /**
57
+ * Build a safe alternative suggestion for a blocked command.
58
+ * Returns null when no canned alternative exists.
59
+ */
60
+ export function suggestAlternative(kind, command) {
61
+ if (kind === "empty-bash") {
62
+ return 'Provide a real command, e.g. {"command":"ls -la"}';
63
+ }
64
+ // sqlite3 / .db access on Windows
65
+ if (/sqlite3|\.db\b/i.test(command)) {
66
+ return "Use the filesystem MCP or write_file to read the DB file instead of shelling sqlite3";
67
+ }
68
+ // rm -rf patterns
69
+ if (/rm\s+-rf/i.test(command)) {
70
+ return "Explicitly list files to remove, or use move to trash";
71
+ }
72
+ return undefined;
73
+ }
74
+ //# sourceMappingURL=safety-askcard.js.map