mixdog 1.2.2 → 1.2.3

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 (415) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/provider-pingpong-live.mjs +509 -0
  4. package/src/agents/advisor/AGENT.md +1 -1
  5. package/src/agents/advisor/agent.json +1 -1
  6. package/src/defaults/shipped-history.json +5 -0
  7. package/src/defaults/skills/setup/SKILL.md +6 -1
  8. package/src/defaults/skills/setup/references/actions.md +44 -22
  9. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  10. package/src/headless-exec.mjs +15 -8
  11. package/src/lib/keychain-cjs.cjs +7 -4
  12. package/src/output-styles/common.md +11 -10
  13. package/src/rules/agent/AGENT.md +5 -5
  14. package/src/rules/lead/LEAD.md +5 -5
  15. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  16. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  17. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  18. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  19. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  20. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  21. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  22. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  23. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +2 -0
  24. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +24 -17
  25. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +7 -6
  26. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +29 -10
  28. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  29. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  30. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  31. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  32. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  33. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  34. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  35. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -0
  36. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  37. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -0
  38. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +58 -7
  39. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +29 -10
  40. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +16 -3
  41. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  42. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +12 -6
  43. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +114 -72
  45. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  46. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +37 -5
  47. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +1 -0
  49. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  50. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +3 -0
  51. package/src/runtime/agent/orchestrator/providers/cursor.mjs +9 -2
  52. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  53. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  54. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  55. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  56. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  57. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -1
  58. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  59. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +25 -8
  60. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +87 -5
  61. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  62. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +13 -13
  63. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +355 -37
  64. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -2
  65. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +93 -47
  66. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +55 -10
  67. package/src/runtime/agent/orchestrator/providers/gemini.mjs +26 -8
  68. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -0
  70. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  71. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  72. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  73. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  74. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  75. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  76. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  77. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  78. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  80. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  81. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  82. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  83. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  84. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  85. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  86. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +121 -61
  89. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  90. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +5 -2
  91. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  92. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  93. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +42 -24
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +63 -25
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +53 -5
  103. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -1
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +103 -26
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +23 -4
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +11 -6
  108. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
  109. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +15 -23
  110. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  111. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +25 -4
  112. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  113. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  114. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +4 -0
  115. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +5 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  117. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +3 -1
  119. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +7 -1
  121. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +71 -24
  122. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  123. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  124. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  125. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  126. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  127. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  129. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  130. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +11 -3
  131. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  132. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  133. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  134. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +147 -8
  135. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  136. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +5 -0
  137. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  138. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  139. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  140. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  141. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  142. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  143. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  145. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  147. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  148. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  150. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  151. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  152. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  153. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  154. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  155. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -1
  156. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  157. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  158. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  159. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  160. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  161. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  162. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  163. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  164. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  165. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  167. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  168. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  169. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  170. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  171. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  172. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +63 -20
  173. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +2 -1
  174. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  175. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  179. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +4 -1
  180. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  181. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +16 -0
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  184. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  185. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  187. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  188. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +15 -3
  189. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  190. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  192. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  193. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  195. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  196. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +13 -0
  197. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  198. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  199. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  200. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  201. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  202. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  203. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  204. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  205. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  206. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  207. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  208. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +8 -0
  209. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +10 -1
  210. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  211. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  212. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  213. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  214. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  215. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  216. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  217. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  218. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  221. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  223. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  224. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  225. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  226. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  227. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  228. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  229. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  230. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  231. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  232. package/src/runtime/github/tool.mjs +92 -2
  233. package/src/runtime/memory/index.mjs +1 -0
  234. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  235. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  236. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  237. package/src/runtime/memory/lib/pg/process.mjs +31 -26
  238. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  239. package/src/runtime/memory/lib/pg/supervisor.mjs +108 -3
  240. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  241. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  242. package/src/runtime/office/README.md +1 -1
  243. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  244. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  245. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  246. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  247. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  248. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  249. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  250. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  251. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  252. package/src/runtime/office/core/office-sessions.mjs +5 -0
  253. package/src/runtime/office/core/office-transactions.mjs +3 -0
  254. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  255. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  256. package/src/runtime/office/index.mjs +30 -0
  257. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  258. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  259. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  260. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  261. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  262. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  263. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  264. package/src/runtime/shared/buffered-appender.mjs +18 -0
  265. package/src/runtime/shared/config.mjs +78 -18
  266. package/src/runtime/shared/err-text.mjs +6 -0
  267. package/src/runtime/shared/file-permissions.d.mts +6 -1
  268. package/src/runtime/shared/file-permissions.mjs +54 -6
  269. package/src/runtime/shared/llm/anthropic-effort.mjs +6 -0
  270. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  271. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  272. package/src/runtime/shared/loopback-listener.mjs +9 -0
  273. package/src/runtime/shared/provider-api-key.mjs +2 -0
  274. package/src/runtime/shared/review-diff.mjs +52 -0
  275. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  276. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  277. package/src/runtime/shared/slow-span.mjs +114 -0
  278. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  279. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  280. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  281. package/src/runtime/shared/tool-card-model.mjs +5 -2
  282. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  283. package/src/runtime/shared/tool-primitives.mjs +13 -0
  284. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  285. package/src/runtime/shared/tool-surface.mjs +18 -3
  286. package/src/runtime/shared/tool-work-units.mjs +40 -4
  287. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  288. package/src/runtime/shared/transcript-writer.mjs +10 -2
  289. package/src/runtime/shared/turn-changes.mjs +268 -100
  290. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  291. package/src/runtime/shared/turn-snapshot-store.mjs +19 -108
  292. package/src/runtime/shared/turn-snapshot.mjs +857 -683
  293. package/src/runtime/shared/turn-worktree-snapshot.mjs +183 -255
  294. package/src/runtime/shared/user-data-guard.mjs +262 -49
  295. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  296. package/src/runtime/shared/worktrees/contract.mjs +34 -0
  297. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  298. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  299. package/src/runtime/shared/worktrees/git.mjs +8 -3
  300. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  301. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  302. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  303. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  304. package/src/runtime/terminal-bridge/client.mjs +44 -11
  305. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  306. package/src/runtime/tidy/apply.mjs +47 -2
  307. package/src/runtime/tidy/run-engines.mjs +11 -5
  308. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  309. package/src/session-runtime/boot/apis.mjs +13 -5
  310. package/src/session-runtime/boot/facade.mjs +1 -0
  311. package/src/session-runtime/boot/infrastructure.mjs +6 -1
  312. package/src/session-runtime/boot/session.mjs +0 -1
  313. package/src/session-runtime/boot/tools.mjs +4 -1
  314. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  315. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  316. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  317. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  318. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  319. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  320. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  321. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  322. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -3
  323. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  324. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  325. package/src/session-runtime/lifecycle/teardown.mjs +72 -7
  326. package/src/session-runtime/lifecycle-api.mjs +1 -0
  327. package/src/session-runtime/mcp-glue/connect.mjs +17 -5
  328. package/src/session-runtime/model-route/set-route.mjs +2 -2
  329. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  330. package/src/session-runtime/native-web-search.mjs +10 -1
  331. package/src/session-runtime/new-session-config.mjs +4 -2
  332. package/src/session-runtime/provider-auth/login.mjs +4 -0
  333. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  334. package/src/session-runtime/runtime-review-api.mjs +8 -2
  335. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  336. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  337. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  338. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  339. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  340. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  341. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  342. package/src/session-runtime/services/agent-tool.mjs +3 -0
  343. package/src/session-runtime/services/channel-admin.mjs +19 -3
  344. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  345. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  346. package/src/session-runtime/services/provider-admin.mjs +81 -15
  347. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  348. package/src/session-runtime/session-turn-api.mjs +14 -5
  349. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  350. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  351. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  352. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  353. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  354. package/src/session-runtime/skills-api.mjs +3 -5
  355. package/src/session-runtime/tool-surface.mjs +5 -1
  356. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  357. package/src/session-runtime/turn/session-ops.mjs +7 -5
  358. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  359. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  360. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  361. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  362. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  363. package/src/session-runtime/turn/turn-run.mjs +25 -9
  364. package/src/session-runtime/usage-stats-api.mjs +43 -11
  365. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  366. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  367. package/src/session-runtime/workspace/transitions.mjs +190 -15
  368. package/src/standalone/daemon-agent-control.mjs +9 -1
  369. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  370. package/src/standalone/daemon-owner.mjs +17 -4
  371. package/src/standalone/daemon-pg-stop.mjs +51 -0
  372. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  373. package/src/standalone/daemon-stop-budget.mjs +18 -0
  374. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  375. package/src/standalone/daemon-telemetry.mjs +19 -0
  376. package/src/standalone/daemon.mjs +61 -1
  377. package/src/standalone/desktop-service-registry.mjs +5 -0
  378. package/src/standalone/local-session-runtime.mjs +70 -19
  379. package/src/standalone/session-client.mjs +42 -0
  380. package/src/standalone/session-protocol.mjs +7 -0
  381. package/src/standalone/session-proxy/projection.mjs +41 -4
  382. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  383. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  384. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  385. package/src/standalone/session-service-api.mjs +6 -1
  386. package/src/standalone/session-state-patch.d.mts +2 -1
  387. package/src/standalone/session-state-patch.mjs +41 -1
  388. package/src/tui/App.jsx +25 -0
  389. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  390. package/src/tui/app/btw-controller.mjs +118 -0
  391. package/src/tui/app/create-app-pickers.mjs +2 -0
  392. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  393. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  394. package/src/tui/app/slash-commands.mjs +13 -3
  395. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  396. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  397. package/src/tui/app/use-global-key-input.mjs +5 -12
  398. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  399. package/src/tui/components/BtwPanel.jsx +53 -0
  400. package/src/tui/components/QuestionPanel.jsx +275 -0
  401. package/src/tui/components/question-panel-model.mjs +156 -0
  402. package/src/tui/dist/index.mjs +2413 -1465
  403. package/src/tui/session/context-state.mjs +4 -2
  404. package/src/tui/session/session-api/commands.mjs +10 -0
  405. package/src/tui/session/session-api/controls.mjs +2 -2
  406. package/src/tui/session/session-api/integrations.mjs +9 -2
  407. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  408. package/src/tui/session/session-api/lifecycle.mjs +2 -1
  409. package/src/tui/session/session-api/settings.mjs +6 -0
  410. package/src/tui/session/session-api.mjs +1 -0
  411. package/src/tui/session/tool-approval.mjs +10 -7
  412. package/src/tui/session/transcript-spill.mjs +14 -7
  413. package/src/tui/session/turn-stream.mjs +174 -31
  414. package/src/tui/session/turn.mjs +12 -5
  415. package/src/workflows/default/WORKFLOW.md +3 -1
@@ -46,6 +46,62 @@ export function agentContextOverflowError(
46
46
  );
47
47
  }
48
48
 
49
+ export const TOOL_SURFACE_EXCEEDS_CONTEXT = 'TOOL_SURFACE_EXCEEDS_CONTEXT';
50
+
51
+ const tokens = (value) => `${Number(value || 0).toLocaleString('en-US')} tokens`;
52
+
53
+ // Distinct from AgentContextOverflowError: the request's FIXED overhead (tool
54
+ // definitions, protected system context, configured headroom) leaves no room
55
+ // for any history, so compacting the history cannot help and is not tried.
56
+ // The wording avoids provider context-overflow phrasing on purpose: this is
57
+ // not a "conversation too long" refusal and must not be routed as one.
58
+ export class ToolSurfaceExceedsContextError extends Error {
59
+ constructor({ sessionId, provider, model, overhead }) {
60
+ const target = [provider, model].filter(Boolean).join('/') || 'this model';
61
+ const reserve = overhead.kind === 'configured_reserve';
62
+ const headline = reserve
63
+ ? 'The configured compaction headroom leaves no room for conversation history'
64
+ : 'Tool definitions and system context leave no room for conversation history';
65
+ const remedy = reserve
66
+ ? 'Lower the configured headroom (compaction.reservedTokens or MIXDOG_AGENT_COMPACT_RESERVED_TOKENS), or use a model with a larger context window.'
67
+ : `Use a model with a larger context window, or send a smaller eager tool surface (disable MCP servers or custom tools this session does not need)${
68
+ overhead.configuredReserveTokens > 0 ? ', or configure fewer reserved tokens (compaction.reservedTokens)' : ''
69
+ }.`;
70
+ const largest = overhead.largestTools.map((tool) => `${tool.name} (${tokens(tool.tokens)})`).join(', ');
71
+ super(
72
+ [
73
+ `${headline} (${TOOL_SURFACE_EXCEEDS_CONTEXT}).`,
74
+ `${target}: context window ${tokens(overhead.contextWindowTokens)}, compaction boundary ${tokens(overhead.boundaryTokens)}.`,
75
+ `Fixed per-request overhead: tool definitions ${tokens(overhead.toolTokens)} (${overhead.toolCount} tools), ` +
76
+ `protected system context ${tokens(overhead.systemTokens)}, configured headroom ${tokens(overhead.configuredReserveTokens)}; ` +
77
+ 'nothing remains for conversation history.',
78
+ ...(overhead.measuredRequestTokens
79
+ ? [`The provider measured the last request at ${tokens(overhead.measuredRequestTokens)}.`]
80
+ : []),
81
+ 'Compacting the conversation history cannot reduce these.',
82
+ remedy,
83
+ ...(largest ? [`Largest tool definitions: ${largest}.`] : []),
84
+ ].join('\n')
85
+ );
86
+ this.name = 'ToolSurfaceExceedsContextError';
87
+ this.code = TOOL_SURFACE_EXCEEDS_CONTEXT;
88
+ this.sessionId = sessionId || null;
89
+ this.provider = provider || null;
90
+ this.model = model || null;
91
+ this.overheadKind = overhead.kind;
92
+ this.overhead = overhead;
93
+ }
94
+ }
95
+
96
+ export function toolSurfaceExceedsContextError({ sessionId, sessionRef, model, overhead }) {
97
+ return new ToolSurfaceExceedsContextError({
98
+ sessionId,
99
+ provider: sessionRef?.provider || null,
100
+ model: sessionRef?.model || model || null,
101
+ overhead,
102
+ });
103
+ }
104
+
49
105
  // Distinct from AgentContextOverflowError: the compaction pipeline itself
50
106
  // failed (handoff-summary error, dead Memory runtime,
51
107
  // etc.). This is NOT "latest turn cannot fit the context budget" — masking it
@@ -1,12 +1,10 @@
1
1
  // Deferred catalog call-through: inactive catalog tool_use → discovery bookkeeping
2
2
  // then normal executeTool routing (runtime errors only; no pre-dispatch schema).
3
3
  import { clean } from '../../runtime-core/session-text.mjs';
4
- import {
5
- deferredCatalogUnion,
6
- isReadonlySelectable,
7
- selectDeferredTools,
8
- } from '../../runtime-core/tool-catalog.mjs';
4
+ import { deferredCatalogUnion, isReadonlySelectable, selectDeferredTools } from '../../runtime-core/tool-catalog.mjs';
9
5
  import { isDeferredToolAvailable } from '../../runtime-core/deferred-tool-availability.mjs';
6
+ import { routeToolExclusionReason } from './provider-tool-exclusions.mjs';
7
+ import { isBridgeReadonlySession, isToolBridgeEnabled } from '../../runtime-core/tool-bridge.mjs';
10
8
 
11
9
  /** Skill-list plumbing only; mutation/MCP/builtins must use the catalog+mode gate. */
12
10
  const INACTIVE_INFRA_BYPASS = new Set(['skills_list', 'skill_view']);
@@ -41,7 +39,7 @@ function toolExecutesWhenInactive(name) {
41
39
  return INACTIVE_INFRA_BYPASS.has(name);
42
40
  }
43
41
 
44
- function resolveDeferredSelectMode(session) {
42
+ export function resolveDeferredSelectMode(session) {
45
43
  const spec = session?.toolSpec;
46
44
  if (spec == null || spec === '') return null;
47
45
  if (spec === 'readonly') return 'readonly';
@@ -89,20 +87,32 @@ export function prepareDeferredToolCallThrough(sessionRef, name) {
89
87
  }
90
88
  const tool = lookupDeferredCatalogTool(sessionRef, name);
91
89
  if (!tool) return null;
90
+ const routeExclusion = isToolBridgeEnabled(sessionRef) ? null : routeToolExclusionReason(sessionRef, tool);
91
+ if (routeExclusion) return denyDeferredCallThrough(`Error: ${routeExclusion}`);
92
92
  if (isActiveSessionTool(sessionRef, name)) return null;
93
93
  if (toolExecutesWhenInactive(name)) return null;
94
94
 
95
95
  const toolLabel = clean(tool.name) || clean(name) || 'tool';
96
+ const bridge = isToolBridgeEnabled(sessionRef);
96
97
  const resolvedMode = resolveDeferredSelectMode(sessionRef);
98
+ // Whatever the exposure, a deferred mutating tool never runs without a
99
+ // resolved tool mode, and never in a read-only one.
97
100
  if (resolvedMode === null) {
98
101
  if (!isReadonlySelectable(tool)) {
102
+ const remedy = bridge ? 'no call was sent.' : `load it with load_tool names:["${toolLabel}"].`;
99
103
  return denyDeferredCallThrough(
100
- `Error: tool "${toolLabel}" is deferred and cannot be auto-loaded without a resolved tool mode; load it with load_tool names:["${toolLabel}"].`
104
+ `Error: tool "${toolLabel}" is deferred and cannot be auto-loaded without a resolved tool mode; ${remedy}`
101
105
  );
102
106
  }
103
- } else if (resolvedMode === 'readonly' && !isReadonlySelectable(tool)) {
107
+ } else if (
108
+ (resolvedMode === 'readonly' || (bridge && isBridgeReadonlySession(sessionRef))) &&
109
+ !isReadonlySelectable(tool)
110
+ ) {
104
111
  return denyDeferredCallThrough(`Error: tool "${toolLabel}" is not available in readonly mode`);
105
112
  }
113
+ // Bridge sessions reach deferred tools through call_tool: the fixed
114
+ // provider surface is never extended.
115
+ if (bridge) return null;
106
116
 
107
117
  const selectMode = resolvedMode === null ? 'readonly' : resolvedMode;
108
118
  selectDeferredTools(sessionRef, [toolLabel], selectMode);
@@ -114,6 +114,7 @@ export function createLoopState({ provider, messages, model, tools, cwd, sendOpt
114
114
  // One-shot repair of an assistant turn whose stored reasoning replay the
115
115
  // API refuses to take back (see thinking-replay-recovery.mjs).
116
116
  thinkingReplayRepairUsed: false,
117
+ toolSchemaDropUsed: false,
117
118
  // Pending image-strip retry: identities of the images every attempt leaves
118
119
  // out until a send succeeds, and whether to heal them from history then.
119
120
  imageStrip: null,
@@ -13,8 +13,12 @@
13
13
  // registry knows its name.
14
14
  import { isAgentOwner } from '../../agent-owner.mjs';
15
15
  import { isOffReadonlySurface } from './deferred-call-through.mjs';
16
+ import { BRIDGE_CALL_TOOL_NAME, bridgeErrorText, isToolBridgeEnabled } from '../../runtime-core/tool-bridge.mjs';
17
+ import { sessionAllowlistExcludes, sessionDisallows } from '../../runtime-core/deferred-tool-availability.mjs';
18
+ import { providerToolExclusionReason } from './provider-tool-exclusions.mjs';
16
19
 
17
- const WORKER_DENIED_TOOLS = new Set(['agent']);
20
+ const WORKER_DENIED_TOOLS = new Set(['agent', 'ask_user', 'session']);
21
+ const BRIDGE_DISCOVERY_TOOL_NAMES = new Set(['load_tool']);
18
22
 
19
23
  const IMAGE_PATH_RE = /\.(?:png|jpe?g|gif|webp)(?:$|[?#])/i;
20
24
  const COMPACTED_PATCH_LINE_RE = /^\s*\[mixdog compacted\b[^\]\n]*\]/;
@@ -95,17 +99,24 @@ export function routeWebFetchCall(call) {
95
99
  export function preDispatchDenyForSession(sessionRef, call) {
96
100
  const name = call?.name;
97
101
  if (typeof name !== 'string' || !name) return null;
102
+ // A call_tool request that could not be projected onto its target answers
103
+ // its wire id with the projection error; call_tool itself never dispatches.
104
+ if (typeof call.bridgeError === 'string') return call.bridgeError;
105
+ if (name === BRIDGE_CALL_TOOL_NAME && isToolBridgeEnabled(sessionRef)) {
106
+ return bridgeErrorText('BRIDGE_RECURSION', 'call_tool runs one tool named in its arguments and cannot be dispatched itself.');
107
+ }
98
108
  const schemaName = String(call?.schemaName || name);
99
- if (Array.isArray(sessionRef?.schemaAllowedTools)) {
100
- const allowed = new Set(sessionRef.schemaAllowedTools.map((toolName) => String(toolName).toLowerCase()));
101
- if (!allowed.has(schemaName.toLowerCase())) {
102
- return `Error: tool "${name}" is not available on this session's schema allowlist.`;
103
- }
109
+ // A tool the provider left out of this request still exists in the session;
110
+ // a call to it is answered with why it is unavailable.
111
+ const excluded = providerToolExclusionReason(sessionRef, schemaName);
112
+ if (excluded) return `Error: ${excluded}`;
113
+ // The bridge's discovery tools grant no authority (every call_tool target
114
+ // is judged here under its own name), so an allowlist never strands them.
115
+ const bridgeDiscovery = isToolBridgeEnabled(sessionRef) && BRIDGE_DISCOVERY_TOOL_NAMES.has(schemaName);
116
+ if (!bridgeDiscovery && sessionAllowlistExcludes(sessionRef, schemaName)) {
117
+ return `Error: tool "${name}" is not available on this session's schema allowlist.`;
104
118
  }
105
- if (
106
- Array.isArray(sessionRef?.disallowedTools) &&
107
- sessionRef.disallowedTools.some((toolName) => String(toolName).toLowerCase() === schemaName.toLowerCase())
108
- ) {
119
+ if (sessionDisallows(sessionRef, schemaName)) {
109
120
  return `Error: tool "${name}" is not available in this session.`;
110
121
  }
111
122
  if (isOffReadonlySurface(sessionRef, schemaName)) {
@@ -0,0 +1,132 @@
1
+ // Tools a provider left out of a request because it cannot accept their input
2
+ // schema even after the wire's normalization (a schema with an unknown type
3
+ // name, an unresolvable $ref, a non-object root), or that the provider
4
+ // rejected with an HTTP 400 naming the schema (dropped for the session).
5
+ // The user is told once per session and tool on the turn's display-only stage
6
+ // channel (stage 'notice'); a call the model still makes to such a tool is
7
+ // answered with the reason instead of running it.
8
+ import { geminiToolExclusion } from '../../providers/gemini-schema.mjs';
9
+ import { isGeminiFamilyModel } from '../../providers/gemini-family.mjs';
10
+ import { anthropicToolExclusion } from '../../providers/lib/anthropic-request-utils.mjs';
11
+ import { claudeBridgeInputSchema, wireInputSchema } from '../../providers/lib/claude-bridge-schema.mjs';
12
+ import { actionInputUnionFor } from '../../providers/lib/action-input-contract.mjs';
13
+ import { isAntigravityClaude } from '../../providers/antigravity-request.mjs';
14
+ import { isResponsesFreeformTool } from '../../providers/custom-tool-wire.mjs';
15
+ import { ANTHROPIC_NATIVE_PROVIDERS } from '../../runtime-core/tool-catalog-schema.mjs';
16
+ import { finalizeProviderRequestTools, providerNativeToolPrefixCount } from '../../runtime-core/provider-request-tools.mjs';
17
+
18
+ const sessions = new WeakMap();
19
+
20
+ function exclusionsFor(sessionRef) {
21
+ let state = sessions.get(sessionRef);
22
+ if (!state) {
23
+ state = { noticed: new Set(), current: new Map(), dropped: new Map() };
24
+ sessions.set(sessionRef, state);
25
+ }
26
+ return state;
27
+ }
28
+
29
+ const isSessionRef = (value) => value !== null && typeof value === 'object';
30
+
31
+ /** A new provider request: exclusions describe the latest request only. */
32
+ export function beginProviderToolExclusions(sessionRef) {
33
+ if (isSessionRef(sessionRef)) sessions.get(sessionRef)?.current.clear();
34
+ }
35
+
36
+ export function excludedToolNoticeText({ name, pointer, reason }) {
37
+ return `Tool "${name}" was excluded from this session's requests: its input schema is invalid at ${pointer || '/'} (${reason}). Fix the tool's schema or disable the tool.`;
38
+ }
39
+
40
+ /** Record `exclusion` ({ name, pointer, reason }) for the session's current
41
+ * request and raise the user notice the first time this tool is excluded. */
42
+ export function recordProviderToolExclusion(sessionRef, exclusion, onStageChange) {
43
+ const name = String(exclusion?.name || '');
44
+ if (!name || !isSessionRef(sessionRef)) return;
45
+ const state = exclusionsFor(sessionRef);
46
+ state.current.set(name, exclusion);
47
+ if (state.noticed.has(name)) return;
48
+ state.noticed.add(name);
49
+ try {
50
+ onStageChange?.('notice', { tone: 'warn', message: excludedToolNoticeText(exclusion) });
51
+ } catch {
52
+ /* display-only */
53
+ }
54
+ }
55
+
56
+ const unavailableText = (exclusion) =>
57
+ `tool "${exclusion.name}" is unavailable with the current model: the provider rejected its input schema at ${exclusion.pointer || '/'} (${exclusion.reason}), so it was left out of the request; no call was sent. Use another tool, or ask the user to fix the tool's schema or disable it.`;
58
+
59
+ /** Why `name` cannot be called in the session's current request, or null. */
60
+ export function providerToolExclusionReason(sessionRef, name) {
61
+ if (!isSessionRef(sessionRef)) return null;
62
+ const state = sessions.get(sessionRef);
63
+ const key = String(name || '');
64
+ const exclusion = state?.current.get(key) || state?.dropped.get(key);
65
+ return exclusion ? unavailableText(exclusion) : null;
66
+ }
67
+
68
+ /** The provider rejected `exclusion.name`'s schema with an HTTP 400: leave the
69
+ * tool out of every later request of the session and tell the user once. */
70
+ export function dropSessionTool(sessionRef, exclusion, onStageChange) {
71
+ if (!isSessionRef(sessionRef)) return;
72
+ exclusionsFor(sessionRef).dropped.set(exclusion.name, exclusion);
73
+ recordProviderToolExclusion(sessionRef, exclusion, onStageChange);
74
+ }
75
+
76
+ /** The request's tool surface without the tools dropped for the session
77
+ * ({ sendTools, requestToolScope }, the same objects when none apply). */
78
+ export function withoutDroppedTools(sessionRef, { sendTools, requestToolScope }) {
79
+ const dropped = isSessionRef(sessionRef) ? sessions.get(sessionRef)?.dropped : null;
80
+ if (!dropped?.size) return { sendTools, requestToolScope };
81
+ const prefix = providerNativeToolPrefixCount(sendTools);
82
+ const kept = sendTools.filter((tool, index) => index < prefix || !dropped.has(tool?.name));
83
+ if (kept.length === sendTools.length) return { sendTools, requestToolScope };
84
+ const next = finalizeProviderRequestTools(kept, prefix);
85
+ return { sendTools: next, requestToolScope: { ...requestToolScope, requestTools: next } };
86
+ }
87
+
88
+ // The schema rule of the wire a session's requests use: 'gemini', 'anthropic'
89
+ // (Messages), 'antigravity-claude' (Anthropic bridge behind Antigravity) or
90
+ // 'openai' (Responses, Codex and every OpenAI-compatible chat route).
91
+ function routeSchemaFamily(session) {
92
+ const provider = String(session?.provider || '').toLowerCase();
93
+ const wire = session?.wireRoute?.wire;
94
+ if (provider === 'antigravity-oauth' && isAntigravityClaude(session?.model)) return 'antigravity-claude';
95
+ if (provider === 'gemini' || isGeminiFamilyModel(session?.model)) return 'gemini';
96
+ if (ANTHROPIC_NATIVE_PROVIDERS.has(provider) || wire === 'messages') return 'anthropic';
97
+ return 'openai';
98
+ }
99
+
100
+ function wireExclusion(tool, result) {
101
+ return result.schema ? null : { name: tool.name, pointer: result.pointer || '/', reason: result.reason };
102
+ }
103
+
104
+ // The same normalization + validation the wire applies to `tool`, as
105
+ // { name, pointer, reason } when the route leaves it out of its requests.
106
+ function schemaExclusion(session, family, tool) {
107
+ switch (family) {
108
+ case 'gemini':
109
+ return geminiToolExclusion(tool);
110
+ case 'anthropic':
111
+ return anthropicToolExclusion(tool, String(session?.provider || 'anthropic').toLowerCase());
112
+ case 'antigravity-claude':
113
+ return wireExclusion(tool, claudeBridgeInputSchema(actionInputUnionFor(tool.inputSchema)));
114
+ default:
115
+ if (tool.inputSchema == null) return null;
116
+ if (/^openai(-oauth)?$/.test(String(session?.provider || '')) && isResponsesFreeformTool(tool)) return null;
117
+ return wireExclusion(tool, wireInputSchema(tool.inputSchema, 'openai'));
118
+ }
119
+ }
120
+
121
+ /** Why `tool` is left out of every request on the session's route, or null.
122
+ * Validation is a pure function of the tool's schema and the route's wire, so
123
+ * this holds before the tool was ever sent and stays stable across turns; a
124
+ * tool the provider rejected with a 400 is dropped for the session. Built-in
125
+ * tools are never excluded by the schema rule. */
126
+ export function routeToolExclusionReason(session, tool) {
127
+ if (!tool) return null;
128
+ const dropped = isSessionRef(session) ? sessions.get(session)?.dropped.get(String(tool.name || '')) : null;
129
+ if (dropped) return unavailableText(dropped);
130
+ const exclusion = schemaExclusion(session, routeSchemaFamily(session), tool);
131
+ return exclusion ? unavailableText(exclusion) : null;
132
+ }
@@ -14,6 +14,10 @@ import {
14
14
  providerNativeToolPrefixCount,
15
15
  runWithProviderRequestToolsScope,
16
16
  } from '../../runtime-core/provider-request-tools.mjs';
17
+ import { TOOL_SURFACE_MIGRATION_INTENT } from '../../runtime-core/tool-exposure.mjs';
18
+ import { migrateToolExposureAtBoundary } from './tool-exposure-migration.mjs';
19
+ import { fixedRequestOverhead, fixedRequestOverheadInputs, resolveWorkerCompactPolicy } from './compact-policy.mjs';
20
+ import { toolSurfaceExceedsContextError } from './context-overflow.mjs';
17
21
 
18
22
  const isDynamicTool = (tool) =>
19
23
  tool.deferLoading === true || tool.defer_loading === true || String(tool.name || '').startsWith('mcp__');
@@ -61,6 +65,18 @@ export async function prepareProviderRequest(state) {
61
65
  let compactChanged;
62
66
  let sendTools;
63
67
  let requestToolScope;
68
+ // Fixed inputs of the last passing overhead verdict in this boundary.
69
+ let passedOverheadInputs;
70
+ // A session persisted with another tool exposure migrates here, once, at a
71
+ // boundary with no pending calls: new surface and fixed guidance, a fresh
72
+ // tool snapshot and provider state, and a tagged cache break.
73
+ if (migrateToolExposureAtBoundary(sessionRef, messages)) {
74
+ fixedProviderToolSurface = null;
75
+ providerState = undefined;
76
+ providerStateCleared = true;
77
+ delete sessionRef.pendingCacheBreakIntent;
78
+ opts.cacheBreakIntent = TOOL_SURFACE_MIGRATION_INTENT;
79
+ }
64
80
  refreshDeferredMcpToolCatalog(sessionRef);
65
81
  do {
66
82
  // Provider-history normalization is part of the request boundary:
@@ -90,6 +106,20 @@ export async function prepareProviderRequest(state) {
90
106
  requestTools: sendTools,
91
107
  nativePrefixCount: providerNativeToolPrefixCount(sendTools),
92
108
  };
109
+ // A request whose fixed overhead alone leaves no room for history fails
110
+ // here with an actionable error: no compaction could make it fit. A
111
+ // passing verdict holds while its fixed inputs (tool schemas, protected
112
+ // system context, headroom, boundary) are unchanged — a later pass whose
113
+ // compaction only removed history (and the provider reading with it) is
114
+ // not re-judged — but adopted MCP schema changes or a re-rooted system
115
+ // prompt are.
116
+ const overheadPolicy = resolveWorkerCompactPolicy(sessionRef, sendTools);
117
+ const overheadInputs = fixedRequestOverheadInputs(overheadPolicy, messages);
118
+ if (overheadInputs === null || overheadInputs !== passedOverheadInputs) {
119
+ const overhead = fixedRequestOverhead(overheadPolicy, messages, sendTools, sessionRef);
120
+ if (overhead) throw toolSurfaceExceedsContextError({ sessionId, sessionRef, model, overhead });
121
+ passedOverheadInputs = overheadInputs;
122
+ }
93
123
  const pass = await runWithProviderRequestToolsScope(requestToolScope, () =>
94
124
  runPreSendCompactPass({
95
125
  provider,
@@ -17,6 +17,13 @@ import { stripInlineImages } from '../image-strip-recovery.mjs';
17
17
  import { runWithProviderRequestToolsScope } from '../../runtime-core/provider-request-tools.mjs';
18
18
  import { REPEAT_FAIL_LIMIT } from './loop-state.mjs';
19
19
  import { createToolCallIdResolver } from './tool-call-ids.mjs';
20
+ import { captureSideQuestionRequest } from '../side-question-store.mjs';
21
+ import {
22
+ beginProviderToolExclusions,
23
+ dropSessionTool,
24
+ recordProviderToolExclusion,
25
+ withoutDroppedTools,
26
+ } from './provider-tool-exclusions.mjs';
20
27
 
21
28
  /**
22
29
  * Open an iteration: refresh the live cwd, drain queued steering after a
@@ -73,6 +80,8 @@ export async function beginIteration(state) {
73
80
  loopUsageMetricsTurnId: state.usageMetricsTurnId,
74
81
  loopUsageMetricsEpoch: state.usageMetricsEpoch,
75
82
  });
83
+ // A tool the provider rejected with a schema 400 stays out of the session.
84
+ Object.assign(boundary, withoutDroppedTools(sessionRef, boundary));
76
85
  state.iterations = boundary.iterations;
77
86
  state.lastUsage = boundary.lastUsage;
78
87
  state.firstTurnUsage = boundary.firstTurnUsage;
@@ -92,6 +101,10 @@ export async function beginIteration(state) {
92
101
  // callers omit the flag and retain legacy live deferred resolution.
93
102
  opts.providerToolSnapshotAuthoritative = true;
94
103
  opts.providerNativeToolPrefixCount = boundary.requestToolScope.nativePrefixCount;
104
+ // A tool the provider leaves out of this request (a schema it cannot
105
+ // accept) is noticed once per session and refused if the model calls it.
106
+ beginProviderToolExclusions(sessionRef);
107
+ opts.onToolExcluded = (exclusion) => recordProviderToolExclusion(sessionRef, exclusion, opts.onStageChange);
95
108
  state.lastSendTools = boundary.sendTools;
96
109
  // Eager-dispatch queue (see ../eager-dispatch.mjs): read-only tools start
97
110
  // the instant the provider streams a tool-call event; writes and unknown
@@ -156,26 +169,46 @@ export async function sendProviderRequest(state, round) {
156
169
  try {
157
170
  opts.onProviderSendStarted?.();
158
171
  } catch {}
159
- const result = await runWithProviderRequestToolsScope(round.requestToolScope, () =>
160
- sendWithRecovery({
161
- provider,
162
- messages: providerMessages,
163
- recoveryMessages: messageSource,
164
- model,
165
- sendTools: round.sendTools,
166
- tools: round.sendTools,
167
- opts,
168
- sessionId,
169
- sessionRef,
170
- nextIteration: round.nextIteration,
171
- contextOverflowRetryUsed: state.contextOverflowRetryUsed,
172
- transportRetriesUsed: state.transportRetriesUsed,
173
- transportRetryMax: state.transportRetryMax,
174
- imageStripUsed: state.imageStripUsed,
175
- thinkingReplayRepairUsed: state.thinkingReplayRepairUsed,
176
- signal: state.signal,
177
- })
178
- );
172
+ // The request a /btw side question forks: kept while it is in flight, with
173
+ // its reply once it proceeds, and dropped again if it fails.
174
+ const sideQuestion = captureSideQuestionRequest({
175
+ sessionId,
176
+ sessionRef,
177
+ provider,
178
+ model,
179
+ messages: providerMessages,
180
+ tools: round.sendTools,
181
+ opts,
182
+ });
183
+ let result;
184
+ try {
185
+ result = await runWithProviderRequestToolsScope(round.requestToolScope, () =>
186
+ sendWithRecovery({
187
+ provider,
188
+ messages: providerMessages,
189
+ recoveryMessages: messageSource,
190
+ model,
191
+ sendTools: round.sendTools,
192
+ tools: round.sendTools,
193
+ opts,
194
+ sessionId,
195
+ sessionRef,
196
+ nextIteration: round.nextIteration,
197
+ contextOverflowRetryUsed: state.contextOverflowRetryUsed,
198
+ transportRetriesUsed: state.transportRetriesUsed,
199
+ transportRetryMax: state.transportRetryMax,
200
+ imageStripUsed: state.imageStripUsed,
201
+ thinkingReplayRepairUsed: state.thinkingReplayRepairUsed,
202
+ toolSchemaDropUsed: state.toolSchemaDropUsed,
203
+ signal: state.signal,
204
+ })
205
+ );
206
+ } catch (error) {
207
+ sideQuestion?.discard();
208
+ throw error;
209
+ }
210
+ if (result.action === 'proceed') sideQuestion?.settle(result.response);
211
+ else sideQuestion?.discard();
179
212
  return {
180
213
  result,
181
214
  providerMessages,
@@ -217,6 +250,16 @@ export function applyRetryAction(state, result) {
217
250
  // transport budget is consumed: nothing was generated.
218
251
  state.thinkingReplayRepairUsed = true;
219
252
  return true;
253
+ case 'retry_tool_schema_drop':
254
+ // The provider named a tool whose schema it rejects: the session drops
255
+ // it (with the notice) and the turn is rebuilt without it, once. The
256
+ // frozen tool surface and provider state are rebuilt around the change.
257
+ state.toolSchemaDropUsed = true;
258
+ dropSessionTool(state.sessionRef, result.exclusion, state.opts.onStageChange);
259
+ state.fixedProviderToolSurface = null;
260
+ state.providerState = undefined;
261
+ state.providerStateUpdated = true;
262
+ return true;
220
263
  case 'retry_image_strip':
221
264
  state.transportRetriesUsed += 1;
222
265
  state.imageStripUsed = true;
@@ -16,7 +16,7 @@
16
16
  import { resolveAutoEffort } from '../../providers/auto-effort.mjs';
17
17
  import { isCustomProviderName, resolveCustomReasoning } from '../../providers/custom-reasoning.mjs';
18
18
  import { autoEffortModel, stepEffortConfiguration } from '../../providers/effort-configuration.mjs';
19
- import { effortOptionsFor } from '../../runtime-core/effort.mjs';
19
+ import { effortOptionsFor, warnEmptyAutoEffortLadder } from '../../runtime-core/effort.mjs';
20
20
  import { judgeTurn, recordEffortDecision } from '../../../../effort-judge/judge-client.mjs';
21
21
  import { stepResultWindow } from '../../../../effort-judge/judge-input.mjs';
22
22
 
@@ -174,6 +174,7 @@ function stepResult({ auto, snapshot, step, provider, model, providerClient, ses
174
174
  ms: judged.ms,
175
175
  probs: judged.probs.map((value) => Number(value.toFixed(3))),
176
176
  });
177
+ if (!resolved) warnEmptyAutoEffortLadder(provider, model, auto.base);
177
178
  if (isCustomProviderName(provider)) {
178
179
  const level =
179
180
  resolved &&
@@ -0,0 +1,95 @@
1
+ // Execution-call projection for the fixed tool bridge. A wire call
2
+ // `call_tool {name: T, arguments: A}` (id X) becomes the execution call
3
+ // `{ id: X, name: T, arguments: A, wireCall }` before eager admission and
4
+ // classification, so every policy, ordering, cache, hook, approval and audit
5
+ // layer sees the target itself. The tool result still answers wire id X and
6
+ // the transcript keeps the original wire call (`wireCall`, an immutable copy).
7
+ // Non-bridge sessions and every other call pass through unchanged.
8
+ import { formatInvalidToolArgsResult, isInvalidToolArgsMarker } from '../../providers/lib/openai-tool-args.mjs';
9
+ import {
10
+ BRIDGE_CALL_TOOL_NAME,
11
+ bridgeErrorText,
12
+ isToolBridgeEnabled,
13
+ resolveBridgeTarget,
14
+ } from '../../runtime-core/tool-bridge.mjs';
15
+
16
+ const CALL_TOOL_KEYS = new Set(['name', 'arguments']);
17
+
18
+ const isPlainObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
19
+
20
+ function deepFreeze(value) {
21
+ if (value && typeof value === 'object' && !Object.isFrozen(value)) {
22
+ Object.freeze(value);
23
+ for (const key of Object.keys(value)) deepFreeze(value[key]);
24
+ }
25
+ return value;
26
+ }
27
+
28
+ function projectionFailure(wireCall, wire, text) {
29
+ return { id: wireCall.id, name: wireCall.name, arguments: wireCall.arguments, wireCall: wire, bridgeError: text };
30
+ }
31
+
32
+ /**
33
+ * @returns the execution call for `wireCall`: the projected target call, a
34
+ * call carrying `bridgeError` (answered with that error, never executed),
35
+ * or `wireCall` itself when no projection applies. Idempotent.
36
+ */
37
+ export function resolveExecutionToolCall(wireCall, session) {
38
+ if (!wireCall || wireCall.name !== BRIDGE_CALL_TOOL_NAME || wireCall.wireCall || !isToolBridgeEnabled(session)) {
39
+ return wireCall;
40
+ }
41
+ const wire = deepFreeze(structuredClone(wireCall));
42
+ const args = wireCall.arguments;
43
+ if (isInvalidToolArgsMarker(args)) return projectionFailure(wireCall, wire, formatInvalidToolArgsResult(wireCall));
44
+ if (!isPlainObject(args)) {
45
+ return projectionFailure(
46
+ wireCall,
47
+ wire,
48
+ bridgeErrorText('INVALID_ARGUMENTS', 'call_tool requires {name, arguments}.')
49
+ );
50
+ }
51
+ const extra = Object.keys(args).filter((key) => !CALL_TOOL_KEYS.has(key));
52
+ if (extra.length) {
53
+ return projectionFailure(
54
+ wireCall,
55
+ wire,
56
+ bridgeErrorText('INVALID_ARGUMENTS', `call_tool does not accept: ${extra.join(', ')}.`)
57
+ );
58
+ }
59
+ if (!isPlainObject(args.arguments)) {
60
+ return projectionFailure(
61
+ wireCall,
62
+ wire,
63
+ bridgeErrorText('INVALID_ARGUMENTS', 'call_tool arguments must be an object of the target tool arguments.')
64
+ );
65
+ }
66
+ const target = resolveBridgeTarget(session, args.name);
67
+ if (target.error) return projectionFailure(wireCall, wire, bridgeErrorText(target.error.code, target.error.message));
68
+ const call = { id: wireCall.id, name: target.name, arguments: structuredClone(args.arguments), wireCall: wire };
69
+ // The target definition drives read-only classification of the call; it is
70
+ // never serialized with the call.
71
+ Object.defineProperty(call, 'bridgeTool', { value: target.tool, enumerable: false });
72
+ return call;
73
+ }
74
+
75
+ export function resolveExecutionToolCalls(calls, session) {
76
+ return Array.isArray(calls) ? calls.map((call) => resolveExecutionToolCall(call, session)) : calls;
77
+ }
78
+
79
+ /**
80
+ * The classification tool list for `calls`: `tools` plus the definitions of
81
+ * bridged targets that are not on it, so a bridged target is classified
82
+ * (read-only, dedup, stale-read invalidation) exactly like a direct call.
83
+ */
84
+ export function bridgeClassificationTools(tools, calls) {
85
+ const base = Array.isArray(tools) ? tools : [];
86
+ const seen = new Set(base.map((tool) => tool?.name));
87
+ const extra = [];
88
+ for (const call of calls || []) {
89
+ const definition = call?.bridgeTool;
90
+ if (!definition || seen.has(definition.name)) continue;
91
+ seen.add(definition.name);
92
+ extra.push(definition);
93
+ }
94
+ return extra.length ? [...base, ...extra] : tools;
95
+ }
@@ -4,11 +4,12 @@
4
4
  */
5
5
  import { resolvePreToolAskApproval } from '../tool-helpers.mjs';
6
6
  import { runAbortable, throwIfAborted } from '../../../../../shared/abort-race.mjs';
7
+ import { evaluateExecPolicy } from '../../../tools/bash-policy-scan.mjs';
7
8
  import { beginSessionApprovalWait } from '../../manager/runtime-liveness.mjs';
8
9
 
9
10
  const isArgsObject = (value) => value && typeof value === 'object' && !Array.isArray(value);
10
11
 
11
- async function askApproval(call, { callerSessionId, executeOpts, toolApprovalHook, decision }) {
12
+ async function askApproval(call, { callerSessionId, executeOpts, toolApprovalHook, decision, keepArgs = false }) {
12
13
  const askReason = String(decision?.reason || 'approval requested by hook').trim();
13
14
  // While the user decides, the session is waiting on them, not working.
14
15
  const releaseApprovalWait = beginSessionApprovalWait(callerSessionId);
@@ -30,10 +31,35 @@ async function askApproval(call, { callerSessionId, executeOpts, toolApprovalHoo
30
31
  }
31
32
  if (askOutcome.denial) return { denial: askOutcome.denial };
32
33
  const approval = askOutcome.approval;
33
- if (approval && typeof approval === 'object' && isArgsObject(approval.args)) call.args = approval.args;
34
+ if (!keepArgs && approval && typeof approval === 'object' && isArgsObject(approval.args)) call.args = approval.args;
34
35
  return null;
35
36
  }
36
37
 
38
+ /**
39
+ * Approval gate for a shell call (or a terminal `run`) whose exec policy
40
+ * decision is 'ask' (elevated launch). Runs after the final call is known. The
41
+ * approval is granted for the exact command being asked about: an approval
42
+ * reply cannot rewrite the args.
43
+ * @returns {Promise<{ denial: string } | { elevationApproved: boolean }>}
44
+ */
45
+ export async function applyShellElevationGate(call, { callerSessionId, executeOpts, toolApprovalHook }) {
46
+ const gated = call.name === 'shell' || (call.name === 'terminal' && call.args?.action === 'run');
47
+ if (!gated || !isArgsObject(call.args) || call.args.command == null) {
48
+ return { elevationApproved: false };
49
+ }
50
+ const decision = evaluateExecPolicy(String(call.args.command));
51
+ if (decision.decision !== 'ask') return { elevationApproved: false };
52
+ const denied = await askApproval(call, {
53
+ callerSessionId,
54
+ executeOpts,
55
+ toolApprovalHook,
56
+ decision,
57
+ keepArgs: true,
58
+ });
59
+ if (denied) return denied;
60
+ return { elevationApproved: true };
61
+ }
62
+
37
63
  /**
38
64
  * @returns {Promise<{ denial: string } | { name: string, args: any }>}
39
65
  * the call as the hook left it, or the denial text to return instead
@@ -48,7 +74,8 @@ export async function applyBeforeToolHook({
48
74
  toolApprovalHook,
49
75
  }) {
50
76
  const call = { name, args, cwd };
51
- if (!beforeToolHook) return call;
77
+ // ask_user is itself the user prompt; it never goes through the ordinary approval gate.
78
+ if (!beforeToolHook || name === 'ask_user') return call;
52
79
  try {
53
80
  const decision = await runAbortable(executeOpts.signal, () =>
54
81
  beforeToolHook(