mixdog 1.4.0 → 1.4.2

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 (493) hide show
  1. package/package.json +3 -2
  2. package/scripts/build-shipped-history.mjs +115 -33
  3. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  4. package/scripts/gate-local.mjs +183 -63
  5. package/scripts/latest-guard.mjs +21 -9
  6. package/scripts/lib/gate-scheduler.mjs +138 -32
  7. package/scripts/lib/gate-timing-reporter.mjs +16 -0
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +91 -66
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +124 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/lib/test-failure-records.mjs +14 -1
  14. package/scripts/lib/test-failure-summary.mjs +16 -4
  15. package/scripts/lib/test-spawn-ledger.mjs +12 -3
  16. package/scripts/post-release-check.mjs +1 -6
  17. package/scripts/pptx-kit-index.mjs +17 -5
  18. package/scripts/suite-health.mjs +115 -24
  19. package/scripts/test-data-dir.mjs +10 -7
  20. package/scripts/test.mjs +9 -14
  21. package/src/agents/advisor/AGENT.md +4 -4
  22. package/src/agents/advisor/agent.json +1 -1
  23. package/src/agents/explore/AGENT.md +22 -0
  24. package/src/agents/explore/agent.json +6 -0
  25. package/src/app.mjs +4 -0
  26. package/src/cli.mjs +7 -0
  27. package/src/defaults/agents.json +7 -0
  28. package/src/defaults/shipped-history.json +23 -2
  29. package/src/defaults/skills/browser-use/SKILL.md +10 -5
  30. package/src/defaults/skills/code-tidy/SKILL.md +71 -102
  31. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
  32. package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
  33. package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
  34. package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
  35. package/src/defaults/skills/computer-use/SKILL.md +16 -4
  36. package/src/defaults/skills/goal-management/SKILL.md +1 -1
  37. package/src/defaults/skills/setup/references/actions.md +12 -6
  38. package/src/defaults/skills/setup/references/surfaces.md +2 -2
  39. package/src/defaults/skills/setup-guide/SKILL.md +5 -2
  40. package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
  41. package/src/defaults/skills/skill-creator/SKILL.md +85 -190
  42. package/src/defaults/skills/skill-creator/references/design.md +70 -162
  43. package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
  44. package/src/defaults/skills/test-suite/SKILL.md +194 -0
  45. package/src/defaults/skills/test-suite/references/case-management.md +51 -0
  46. package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
  47. package/src/defaults/skills/test-suite/references/reliability.md +49 -0
  48. package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
  49. package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
  50. package/src/defaults/skills/visualize/SKILL.md +6 -2
  51. package/src/lib/rules-builder.cjs +35 -42
  52. package/src/output-styles/common.md +2 -2
  53. package/src/output-styles/simple.md +1 -1
  54. package/src/rules/agent/AGENT.md +5 -4
  55. package/src/rules/lead/LEAD.md +12 -7
  56. package/src/rules/shared/10-tool-workflow.md +3 -1
  57. package/src/rules/shared/20-research.md +3 -6
  58. package/src/rules/shared/40-editing.md +1 -0
  59. package/src/rules/shared/45-destructive.md +0 -2
  60. package/src/rules/shared/50-execution.md +2 -0
  61. package/src/rules/shared/60-verification.md +10 -9
  62. package/src/rules/shared/70-delivery.md +8 -9
  63. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
  64. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
  65. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +156 -24
  66. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
  67. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  69. package/src/runtime/agent/orchestrator/config.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +8 -8
  71. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
  72. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
  74. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
  75. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
  76. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
  77. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
  78. package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
  79. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +88 -54
  80. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
  81. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +54 -8
  82. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  83. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +37 -31
  84. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  85. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  86. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  87. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
  89. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  90. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
  91. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
  92. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  93. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
  94. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
  95. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  97. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
  99. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
  100. package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
  101. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
  102. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
  103. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
  104. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  105. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
  106. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +248 -41
  108. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  109. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  110. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
  113. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
  114. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  115. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
  117. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
  119. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  122. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  125. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  127. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  129. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  130. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  131. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  132. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  133. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +160 -31
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +22 -12
  135. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  136. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +49 -22
  137. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
  139. package/src/runtime/agent/orchestrator/providers/registry.mjs +45 -2
  140. package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
  141. package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
  142. package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
  143. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
  145. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -6
  147. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
  148. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -1
  150. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
  151. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
  152. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  153. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
  154. package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
  155. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  156. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +102 -12
  157. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  158. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  159. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  160. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  161. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
  162. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  163. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +164 -23
  164. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +139 -19
  165. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  166. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  167. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +100 -22
  168. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
  169. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
  170. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
  171. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +9 -2
  172. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  174. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
  175. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +23 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
  179. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
  180. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
  181. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  184. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
  185. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
  186. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
  187. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +30 -13
  188. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +63 -6
  189. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
  190. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +35 -13
  192. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +24 -14
  193. package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
  194. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
  195. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
  196. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  197. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
  198. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +56 -29
  199. package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
  200. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
  201. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
  202. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
  203. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  204. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +252 -39
  209. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
  210. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
  211. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
  212. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
  213. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  214. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
  215. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  217. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
  218. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
  219. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +67 -31
  221. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +25 -13
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  223. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
  224. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  225. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  226. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  227. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  228. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  229. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
  231. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
  232. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
  233. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  234. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  235. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  236. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  237. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
  238. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
  239. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
  240. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
  241. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
  243. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
  244. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +28 -4
  245. package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
  246. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  248. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  249. package/src/runtime/channels/lib/whisper-port.mjs +29 -11
  250. package/src/runtime/channels/lib/whisper-server.mjs +10 -1
  251. package/src/runtime/computer-bridge/action-schema.mjs +2 -2
  252. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  253. package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
  254. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  255. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  256. package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
  257. package/src/runtime/connectors/gmail-app-password.mjs +3 -3
  258. package/src/runtime/explore/explorer.mjs +93 -0
  259. package/src/runtime/explore/tool-defs.mjs +35 -0
  260. package/src/runtime/explore/tool.mjs +123 -0
  261. package/src/runtime/local-provider/server-process.mjs +2 -1
  262. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  263. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  264. package/src/runtime/memory/index.mjs +4 -0
  265. package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
  266. package/src/runtime/memory/lib/embedding-worker.mjs +10 -3
  267. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  268. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  269. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  270. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  271. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  272. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  273. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  274. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  275. package/src/runtime/office/quality/assurance-structure-docx.mjs +4 -2
  276. package/src/runtime/office/quality/design-review.mjs +1 -5
  277. package/src/runtime/office/quality/document-brief.mjs +1 -5
  278. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  279. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  280. package/src/runtime/shared/agent-route-config.mjs +17 -2
  281. package/src/runtime/shared/atomic-file.mjs +103 -2
  282. package/src/runtime/shared/background-tasks.mjs +15 -6
  283. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  284. package/src/runtime/shared/connector-accounts.mjs +5 -5
  285. package/src/runtime/shared/connector-catalog.d.mts +11 -4
  286. package/src/runtime/shared/connector-catalog.mjs +60 -52
  287. package/src/runtime/shared/custom-provider-keys.mjs +20 -0
  288. package/src/runtime/shared/defaults-separation.mjs +1 -4
  289. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  290. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  291. package/src/runtime/shared/file-has-data.mjs +26 -2
  292. package/src/runtime/shared/file-lock.mjs +13 -15
  293. package/src/runtime/shared/git-launch.d.mts +9 -0
  294. package/src/runtime/shared/git-launch.mjs +213 -0
  295. package/src/runtime/shared/git-runner-worker.mjs +2 -1
  296. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  297. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  298. package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
  299. package/src/runtime/shared/llm/cost.mjs +11 -2
  300. package/src/runtime/shared/llm/http-agent.mjs +313 -29
  301. package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
  302. package/src/runtime/shared/llm/model-catalog.mjs +46 -6
  303. package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
  304. package/src/runtime/shared/llm/system-proxy.mjs +452 -0
  305. package/src/runtime/shared/loopback-listener.mjs +25 -2
  306. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  307. package/src/runtime/shared/model-route-chain.mjs +22 -4
  308. package/src/runtime/shared/open-url.mjs +10 -0
  309. package/src/runtime/shared/provider-accounts.mjs +52 -2
  310. package/src/runtime/shared/session-activity-signal.mjs +14 -0
  311. package/src/runtime/shared/session-scratch.mjs +37 -0
  312. package/src/runtime/shared/staged-install-worker.mjs +5 -0
  313. package/src/runtime/shared/staged-update.mjs +47 -2
  314. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  315. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  316. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  317. package/src/runtime/shared/tool-surface.mjs +113 -36
  318. package/src/runtime/shared/tool-work-units.mjs +6 -0
  319. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  320. package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
  321. package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
  322. package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
  323. package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
  324. package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
  325. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  326. package/src/runtime/shared/worktrees/git.mjs +72 -0
  327. package/src/runtime/shared/worktrees/manager.mjs +285 -168
  328. package/src/runtime/shared/worktrees/test-support.mjs +6 -10
  329. package/src/runtime/tidy/install.mjs +95 -15
  330. package/src/runtime/tidy/languages.mjs +17 -15
  331. package/src/runtime/tidy/process.mjs +7 -4
  332. package/src/runtime/tidy/resolve.mjs +19 -16
  333. package/src/runtime/tidy/runners/biome.mjs +3 -3
  334. package/src/runtime/tidy/tool.mjs +1 -1
  335. package/src/runtime/tidy/worktree.mjs +10 -6
  336. package/src/runtime/visualize-bridge/client.mjs +6 -1
  337. package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
  338. package/src/runtime/web-search/index.mjs +1 -1
  339. package/src/session-runtime/agent-home-delivery.mjs +41 -0
  340. package/src/session-runtime/agent-task-sessions.mjs +458 -312
  341. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  342. package/src/session-runtime/boot/tools.mjs +7 -1
  343. package/src/session-runtime/bot-status.mjs +107 -101
  344. package/src/session-runtime/bot-target.mjs +38 -0
  345. package/src/session-runtime/channel-config-api.mjs +6 -1
  346. package/src/session-runtime/connector-merge-migration.mjs +18 -15
  347. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  348. package/src/session-runtime/conversation-import/discovery.mjs +401 -36
  349. package/src/session-runtime/conversation-import/service.mjs +35 -8
  350. package/src/session-runtime/handoff-attachments.mjs +82 -31
  351. package/src/session-runtime/held-requests.mjs +3 -1
  352. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
  353. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
  354. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
  355. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
  356. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  357. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +145 -112
  358. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  359. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
  360. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
  361. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  362. package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
  363. package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
  364. package/src/session-runtime/mixy-heartbeat.mjs +1 -1
  365. package/src/session-runtime/model-route/set-route.mjs +11 -2
  366. package/src/session-runtime/provider-auth/login.mjs +2 -2
  367. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  368. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  369. package/src/session-runtime/provider-models.mjs +4 -2
  370. package/src/session-runtime/resource-mcp-api.mjs +98 -35
  371. package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
  372. package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
  373. package/src/session-runtime/resource-mcp-servers.mjs +5 -4
  374. package/src/session-runtime/resource-recall-api.mjs +6 -1
  375. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  376. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  377. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  378. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  379. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  380. package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
  381. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
  382. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
  383. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
  384. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  385. package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
  386. package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
  387. package/src/session-runtime/services/daemon-candidate.mjs +4 -1
  388. package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
  389. package/src/session-runtime/services/plugin-admin.mjs +10 -6
  390. package/src/session-runtime/services/provider-admin.mjs +283 -11
  391. package/src/session-runtime/session-turn-api.mjs +2 -0
  392. package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
  393. package/src/session-runtime/settings-system-api.mjs +1 -1
  394. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
  395. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
  396. package/src/session-runtime/setup-tool/executor.mjs +6 -2
  397. package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
  398. package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
  399. package/src/session-runtime/turn/agent-ops.mjs +23 -0
  400. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
  401. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  402. package/src/session-runtime/workflow-agents-api/agent-route.mjs +25 -5
  403. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  404. package/src/session-runtime/workspace/activity-guards.mjs +176 -0
  405. package/src/session-runtime/workspace/session-view.mjs +149 -0
  406. package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
  407. package/src/session-runtime/workspace/transition-runner.mjs +188 -0
  408. package/src/session-runtime/workspace/transitions.mjs +108 -1041
  409. package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
  410. package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
  411. package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
  412. package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
  413. package/src/session-runtime/workspace/worktree-tool.mjs +73 -56
  414. package/src/standalone/daemon-desktop-runtime.mjs +2 -0
  415. package/src/standalone/daemon-session-control.mjs +169 -138
  416. package/src/standalone/daemon.mjs +7 -0
  417. package/src/standalone/desktop-service-registry.mjs +9 -0
  418. package/src/standalone/local-session-runtime.mjs +5 -1
  419. package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
  420. package/src/standalone/session-client.mjs +5 -1
  421. package/src/standalone/session-frame-stream.mjs +83 -11
  422. package/src/standalone/session-protocol.mjs +4 -0
  423. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  424. package/src/standalone/session-service/agent-tree.mjs +45 -9
  425. package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
  426. package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
  427. package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
  428. package/src/standalone/session-service/stored-reader.mjs +127 -40
  429. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  430. package/src/tui/App.jsx +154 -164
  431. package/src/tui/app/app-view/floating-panel.jsx +10 -4
  432. package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
  433. package/src/tui/app/clipboard.mjs +21 -10
  434. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  435. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  436. package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
  437. package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
  438. package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
  439. package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
  440. package/src/tui/app/provider-setup/oauth-actions.mjs +58 -21
  441. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  442. package/src/tui/app/provider-setup-picker.mjs +29 -18
  443. package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
  444. package/src/tui/app/slash-commands.mjs +1 -0
  445. package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
  446. package/src/tui/app/slash-dispatch.mjs +1 -1
  447. package/src/tui/app/use-btw-panel.mjs +29 -0
  448. package/src/tui/app/use-context-panel-refresh.mjs +31 -0
  449. package/src/tui/app/use-model-catalog-cache.mjs +13 -10
  450. package/src/tui/app/use-mouse-input.mjs +11 -0
  451. package/src/tui/app/use-onboarding-start.mjs +8 -3
  452. package/src/tui/app/use-panel-transition.mjs +10 -1
  453. package/src/tui/app/use-pasted-buffers.mjs +28 -20
  454. package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
  455. package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
  456. package/src/tui/app/use-prompt-queue-history.mjs +3 -2
  457. package/src/tui/app/use-selection-focus.mjs +11 -2
  458. package/src/tui/app/use-selection-geometry.mjs +18 -9
  459. package/src/tui/app/use-selection-paint.mjs +4 -4
  460. package/src/tui/app/use-selection-stitch.mjs +3 -3
  461. package/src/tui/app/use-slash-palette.mjs +62 -0
  462. package/src/tui/app/use-terminal-chrome.mjs +2 -2
  463. package/src/tui/app/use-transcript-activity.mjs +47 -32
  464. package/src/tui/app/use-transcript-follow.mjs +12 -4
  465. package/src/tui/app/use-transcript-scroll.mjs +46 -17
  466. package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
  467. package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
  468. package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
  469. package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
  470. package/src/tui/app/use-transcript-window.mjs +21 -11
  471. package/src/tui/app/use-tui-ready.mjs +18 -0
  472. package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
  473. package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
  474. package/src/tui/components/Picker.jsx +27 -3
  475. package/src/tui/components/QuestionPanel.jsx +13 -4
  476. package/src/tui/components/SignInUrl.jsx +68 -0
  477. package/src/tui/components/TextEntryPanel.jsx +76 -46
  478. package/src/tui/components/sign-in-url-layout.mjs +24 -0
  479. package/src/tui/dist/index.mjs +1963 -1361
  480. package/src/tui/session/goal-turn-state.mjs +6 -2
  481. package/src/tui/session/session-api/controls.mjs +4 -0
  482. package/src/tui/session/session-api/extensions.mjs +8 -0
  483. package/src/tui/session/session-api/intake/abort.mjs +2 -1
  484. package/src/tui/session/session-api/intake.mjs +2 -0
  485. package/src/tui/session/session-api/integrations.mjs +2 -2
  486. package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
  487. package/src/tui/session/session-api/settings.mjs +2 -2
  488. package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
  489. package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
  490. package/src/tui/session/transcript-spill.mjs +92 -4
  491. package/src/tui/session/transcript-store.mjs +2 -0
  492. package/src/ui/statusline-agents.mjs +1 -3
  493. package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
@@ -0,0 +1,567 @@
1
+ import { resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
2
+ import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
3
+ import { sanitizeToolPairs } from '../session/context-utils.mjs';
4
+ import { noteRequestServiceTier } from '../../../shared/llm/usage-context.mjs';
5
+ import { buildGeminiRequest } from './gemini-request-body.mjs';
6
+ import { geminiEndsWithFunctionResponse, runGeminiStreamAttempts } from './gemini-retry-policy.mjs';
7
+ import { consumeGeminiRestStreamResponse } from './gemini-stream.mjs';
8
+ import { geminiTextLeakGuardFor } from './gemini-sdk-request.mjs';
9
+ import { createGeminiReasoningRelay } from './gemini-reasoning-relay.mjs';
10
+ import {
11
+ geminiFailureUsage,
12
+ geminiIncompleteError,
13
+ geminiSendResult,
14
+ parseGeminiCandidate,
15
+ resolveGeminiUsage,
16
+ } from './gemini-response.mjs';
17
+ import { emitGeminiToolCalls } from './gemini-schema.mjs';
18
+ import { geminiRestError } from './gemini.mjs';
19
+ import {
20
+ ANTHROPIC_RETRY_BACKOFF_MS,
21
+ ANTHROPIC_RETRY_JITTER_RATIO,
22
+ anthropicMaxAttempts,
23
+ canFallbackNonStreaming,
24
+ emitProviderRetryStage,
25
+ withRetry,
26
+ } from './retry-classifier.mjs';
27
+ import { buildAnthropicApiRequest } from './anthropic-api-request.mjs';
28
+ import { applyAnthropicErrorContract } from './anthropic-error-contract.mjs';
29
+ import { createAnthropicMidState, assertAnthropicStreamNotEmpty } from './anthropic-midstream-recovery.mjs';
30
+ import { parseSSEStream } from './anthropic-sse.mjs';
31
+ import { buildAnthropicTurnResult } from './anthropic-turn-result.mjs';
32
+ import { streamCallbacks } from './lib/send-callbacks.mjs';
33
+ import { preparePdfTextForProvider } from './media-normalization.mjs';
34
+ import { VERTEX_API_KEY_MODE, VertexAccessTokenManager } from './vertex-ai-auth.mjs';
35
+ import { VertexMaasRoute, toVertexExclusiveUsage, vertexErrorBody } from './vertex-ai-maas.mjs';
36
+ import {
37
+ VERTEX_FLEX_TIER,
38
+ claudeNeedsInterleavedBeta,
39
+ VERTEX_MODELS,
40
+ VERTEX_MULTI_REGIONS,
41
+ VERTEX_PRIORITY_TIER,
42
+ assertVertexServing,
43
+ vertexEnvLocation,
44
+ vertexModelAccessError,
45
+ vertexPricingModel,
46
+ vertexRequestedTier,
47
+ vertexRoute,
48
+ vertexServingProblem,
49
+ vertexTierOption,
50
+ } from './vertex-ai-models.mjs';
51
+
52
+ export {
53
+ VERTEX_FLEX_TIER,
54
+ VERTEX_MODELS,
55
+ VERTEX_PRIORITY_TIER,
56
+ VERTEX_REGIONAL_PRICING_SUFFIX,
57
+ VERTEX_SERVICE_TIER_PARAMETER,
58
+ } from './vertex-ai-models.mjs';
59
+
60
+ const VERTEX_ANTHROPIC_VERSION = 'vertex-2023-10-16';
61
+
62
+ // Vertex takes Anthropic betas in the body (`anthropic_beta`), never as a
63
+ // header, and only these (effort, fast mode, server fallback and the other
64
+ // first-party betas are not offered); tool search has its own name there.
65
+ const INTERLEAVED_THINKING_BETA = 'interleaved-thinking-2025-05-14';
66
+ const VERTEX_CLAUDE_BETAS = new Map([
67
+ [INTERLEAVED_THINKING_BETA, INTERLEAVED_THINKING_BETA],
68
+ ['advanced-tool-use-2025-11-20', 'tool-search-tool-2025-10-19'],
69
+ ['context-1m-2025-08-07', 'context-1m-2025-08-07'],
70
+ ['context-management-2025-06-27', 'context-management-2025-06-27'],
71
+ ['compact-2026-01-12', 'compact-2026-01-12'],
72
+ ]);
73
+
74
+ // Interleaved thinking needs the beta only for a manual thinking budget with
75
+ // tools on the models that document it (adaptive thinking interleaves on its own).
76
+ function needsInterleavedThinking(body, model) {
77
+ return (
78
+ body.thinking?.type === 'enabled' &&
79
+ Array.isArray(body.tools) &&
80
+ body.tools.length > 0 &&
81
+ claudeNeedsInterleavedBeta(model)
82
+ );
83
+ }
84
+
85
+ /** The Vertex names of the selected `betas` this request body needs. */
86
+ function vertexClaudeBetas(betas, body, model) {
87
+ const out = new Set();
88
+ for (const beta of betas || []) {
89
+ const mapped = VERTEX_CLAUDE_BETAS.get(beta);
90
+ if (!mapped || (mapped === INTERLEAVED_THINKING_BETA && !needsInterleavedThinking(body, model))) continue;
91
+ out.add(mapped);
92
+ }
93
+ return [...out];
94
+ }
95
+
96
+ // Flex and Priority PayGo are selected per request with
97
+ // X-Vertex-AI-LLM-Shared-Request-Type (Provisioned Throughput is still used
98
+ // first when the project has it). The served tier is usageMetadata.trafficType:
99
+ // ON_DEMAND_FLEX, ON_DEMAND_PRIORITY, or ON_DEMAND for a Priority request
100
+ // downgraded to Standard PayGo (billed at Standard rates).
101
+ const SERVED_TIER = Object.freeze({
102
+ ON_DEMAND_FLEX: VERTEX_FLEX_TIER,
103
+ FLEX: VERTEX_FLEX_TIER,
104
+ BATCH: VERTEX_FLEX_TIER,
105
+ ON_DEMAND_PRIORITY: VERTEX_PRIORITY_TIER,
106
+ ON_DEMAND: 'default',
107
+ });
108
+
109
+ // A Flex request queues server-side and may sit silent for minutes before its
110
+ // first chunk (the docs allow request timeouts up to 30 minutes), so the shared
111
+ // 60s first-chunk window would kill and replay a healthy one. Kept under the
112
+ // 600s socket body timeout (MIXDOG_LLM_BODY_TIMEOUT_MS).
113
+ const VERTEX_FLEX_FIRST_CHUNK_TIMEOUT_MS = 570_000;
114
+
115
+ // Multi-region locations are served by representative endpoints, not by
116
+ // <location>-aiplatform.googleapis.com.
117
+ function vertexHost(location) {
118
+ if (location === 'global') return 'aiplatform.googleapis.com';
119
+ if (VERTEX_MULTI_REGIONS.has(location)) return `aiplatform.${location}.rep.googleapis.com`;
120
+ return `${location}-aiplatform.googleapis.com`;
121
+ }
122
+
123
+ // Express mode: an API key on the global endpoint, with no project or location in the path.
124
+ const EXPRESS_BASE = 'https://aiplatform.googleapis.com/v1';
125
+
126
+ /**
127
+ * The request's location, and whether the user configured it (`pinned`):
128
+ * without one, the environment's region (for a Claude model, its own first),
129
+ * else global.
130
+ */
131
+ function vertexLocation(config, claudeId = '') {
132
+ const configured = String(config?.location || '').trim();
133
+ const location = (configured || vertexEnvLocation(process.env, claudeId) || 'global').trim().toLowerCase();
134
+ // The location names the host the bearer token is sent to: only a region
135
+ // label (or global / a multi-region) may reach the host template.
136
+ if (!/^[a-z][a-z0-9-]{0,62}$/.test(location)) throw new Error('[vertex-ai] location is invalid');
137
+ return { location, pinned: Boolean(configured) };
138
+ }
139
+
140
+ const servesAt = (route, location, tier) => !vertexServingProblem(route, location, tier);
141
+
142
+ const isNotFound = (error) => [error?.status, error?.httpStatus, error?.response?.status].map(Number).includes(404);
143
+
144
+ function vertexProjectOverride(config) {
145
+ const projectId = String(config?.projectId || '').trim();
146
+ if (projectId && (!/^[a-zA-Z0-9._:-]{1,128}$/.test(projectId) || projectId.includes('/'))) {
147
+ throw new Error('[vertex-ai] project ID override is invalid');
148
+ }
149
+ return projectId;
150
+ }
151
+
152
+ function geminiCallbacks(opts) {
153
+ const reasoning = createGeminiReasoningRelay(opts);
154
+ return {
155
+ onStreamDelta: typeof opts.onStreamDelta === 'function' ? opts.onStreamDelta : null,
156
+ onToolCall: typeof opts.onToolCall === 'function' ? opts.onToolCall : null,
157
+ onTextDelta: typeof opts.onTextDelta === 'function' ? opts.onTextDelta : null,
158
+ onReasoningDelta: reasoning.onReasoningDelta,
159
+ retractReasoning: reasoning.retract,
160
+ };
161
+ }
162
+
163
+ // Vertex rejects (400) functionCall.id / functionResponse.id, which the
164
+ // Gemini API request builder carries; calls pair by name and order here.
165
+ function withoutFunctionIds(contents) {
166
+ const strip = (key, part) => {
167
+ const { id: _id, ...rest } = part[key];
168
+ return { ...part, [key]: rest };
169
+ };
170
+ return (contents || []).map((content) =>
171
+ Array.isArray(content?.parts)
172
+ ? {
173
+ ...content,
174
+ parts: content.parts.map((part) => {
175
+ if (part?.functionCall && 'id' in part.functionCall) return strip('functionCall', part);
176
+ if (part?.functionResponse && 'id' in part.functionResponse) return strip('functionResponse', part);
177
+ return part;
178
+ }),
179
+ }
180
+ : content
181
+ );
182
+ }
183
+
184
+ function geminiVertexBody(request) {
185
+ return {
186
+ contents: withoutFunctionIds(request.contents),
187
+ // Vertex accepts only a Content object here, never a bare string.
188
+ ...(request.systemInstruction ? { systemInstruction: { parts: [{ text: request.systemInstruction }] } } : {}),
189
+ ...(request.generationConfig ? { generationConfig: request.generationConfig } : {}),
190
+ ...(request.geminiTools ? { tools: request.geminiTools } : {}),
191
+ ...(request.toolConfig ? { toolConfig: request.toolConfig } : {}),
192
+ ...(request.safetySettings ? { safetySettings: request.safetySettings } : {}),
193
+ };
194
+ }
195
+
196
+ export class VertexAIProvider {
197
+ static inputExcludesCache = true;
198
+ nativePdf = true;
199
+ name = 'vertex-ai';
200
+
201
+ constructor(config = {}) {
202
+ this.config = config;
203
+ const customFetch = typeof config.fetchFn === 'function' ? config.fetchFn : null;
204
+ this._fetch = customFetch ?? fetch;
205
+ this._auth = new VertexAccessTokenManager({
206
+ credentialResolver: () => resolveProviderApiKey('vertex-ai', this.config),
207
+ authMode: () => this.config.authMode,
208
+ fetchFn: this._fetch,
209
+ now: typeof config.now === 'function' ? config.now : () => Date.now(),
210
+ });
211
+ this._maas = new VertexMaasRoute({
212
+ name: this.name,
213
+ config,
214
+ auth: this._auth,
215
+ fetchFn: customFetch,
216
+ });
217
+ }
218
+
219
+ _projectId() {
220
+ return vertexProjectOverride(this.config) || this._auth.projectId();
221
+ }
222
+
223
+ // Only a Flex route has a budget; the shared window covers the rest. Vertex
224
+ // never marks transport progress at the response headers, so the transport
225
+ // deadline must already cover the headers wait plus the first-chunk wait.
226
+ firstResponseBudget(_model, { modelParameters } = {}) {
227
+ if (vertexRequestedTier({ modelParameters }) !== VERTEX_FLEX_TIER) return null;
228
+ const ms = 2 * VERTEX_FLEX_FIRST_CHUNK_TIMEOUT_MS;
229
+ return { transportMs: ms, semanticMs: ms };
230
+ }
231
+
232
+ _express() {
233
+ return this.config.authMode === VERTEX_API_KEY_MODE;
234
+ }
235
+
236
+ _expressKey() {
237
+ const key = String(resolveProviderApiKey('vertex-ai', this.config) || '').trim();
238
+ if (!key) throw new Error('[vertex-ai] the Vertex AI API key is not configured');
239
+ return key;
240
+ }
241
+
242
+ /** `target`: the send's { identity, project } and the location it goes to. */
243
+ _locationBase({ location, project }) {
244
+ if (this._express()) return EXPRESS_BASE;
245
+ return `https://${vertexHost(location)}/v1/projects/${encodeURIComponent(project)}/locations/${location}`;
246
+ }
247
+
248
+ _url(publisher, model, method, target) {
249
+ // A version suffix (claude-…@default, @YYYYMMDD) stays a literal `@` in the path.
250
+ const id = encodeURIComponent(model).replace(/%40/g, '@');
251
+ return `${this._locationBase(target)}/publishers/${publisher}/models/${id}:${method}`;
252
+ }
253
+
254
+ /**
255
+ * `identity` is the account `url` was built for, read in the same tick as
256
+ * the URL before the first attempt; every attempt (retries included) must
257
+ * still run as that account, so a switch in between aborts the request.
258
+ * Express mode sends its API key instead (its URL names no project).
259
+ */
260
+ async _authorizedFetch(url, init, identity) {
261
+ if (this._express()) {
262
+ const headers = new Headers(init?.headers || {});
263
+ headers.set('x-goog-api-key', this._expressKey());
264
+ return await this._fetch(url, { ...init, headers, dispatcher: getLlmDispatcher() });
265
+ }
266
+ let token = '';
267
+ const run = async () => {
268
+ token = await this._auth.getAccessToken({ identity });
269
+ this._auth.assertIdentity(identity);
270
+ const headers = new Headers(init?.headers || {});
271
+ headers.set('Authorization', `Bearer ${token}`);
272
+ return await this._fetch(url, { ...init, headers, dispatcher: getLlmDispatcher() });
273
+ };
274
+ let response = await run();
275
+ if (response.status === 401) {
276
+ this._auth.invalidate(token);
277
+ response = await run();
278
+ }
279
+ return response;
280
+ }
281
+
282
+ async send(messages, model, tools, sendOpts = {}) {
283
+ const route = vertexRoute(model);
284
+ const tier = vertexRequestedTier(sendOpts);
285
+ const express = this._express();
286
+ if (express && route.wire !== 'gemini') {
287
+ throw new Error(
288
+ `[vertex-ai] ${route.publisher}/${route.id} needs a service-account key or the gcloud login: ` +
289
+ 'a Vertex AI express mode API key serves Gemini models only'
290
+ );
291
+ }
292
+ const requested = vertexLocation(this.config, route.wire === 'claude' ? route.id : '');
293
+ const { pinned } = requested;
294
+ if (express && pinned && requested.location !== 'global') {
295
+ throw new Error(
296
+ '[vertex-ai] a Vertex AI express mode API key serves only the global location; set the location to global'
297
+ );
298
+ }
299
+ // A configured location is never moved. A region from the environment
300
+ // gives way to global when it cannot serve the request (or for express mode).
301
+ let location = express ? 'global' : requested.location;
302
+ if (!pinned && location !== 'global' && !servesAt(route, location, tier) && servesAt(route, 'global', tier)) {
303
+ location = 'global';
304
+ }
305
+ assertVertexServing(route, location, tier);
306
+ await this._auth.ready();
307
+ let result;
308
+ try {
309
+ // One account for the whole send, the region fallback included: its
310
+ // identity and project are read in one tick, and every attempt (on any
311
+ // location) must still run as it.
312
+ const account = express
313
+ ? { identity: null, project: '' }
314
+ : { identity: this._auth.identity(), project: this._projectId() };
315
+ const sendAt = (at) => {
316
+ const target = { ...account, location: at };
317
+ if (route.wire === 'gemini') return this._sendGemini(messages, route.id, tools, sendOpts, tier, target);
318
+ if (route.wire === 'claude') return this._sendClaude(messages, route.id, tools, sendOpts, target);
319
+ return this._sendMaas(messages, route, tools, sendOpts, target);
320
+ };
321
+ try {
322
+ result = await sendAt(location);
323
+ } catch (error) {
324
+ // A region from the environment that does not have the model (404) is retried once on global.
325
+ if (pinned || location === 'global' || !isNotFound(error) || !servesAt(route, 'global', tier)) throw error;
326
+ location = 'global';
327
+ result = await sendAt(location);
328
+ }
329
+ } catch (error) {
330
+ let project = '(unknown project)';
331
+ try {
332
+ project = this._projectId();
333
+ } catch {
334
+ // No resolvable project: keep the placeholder; the original error is rethrown below.
335
+ }
336
+ throw this._auth.accessError(vertexModelAccessError(error, route, { project, location }), { project });
337
+ }
338
+ const pricingModel = vertexPricingModel(route, location);
339
+ if (pricingModel && result && typeof result === 'object') result.pricingModel = pricingModel;
340
+ return result;
341
+ }
342
+
343
+ async _sendMaas(messages, route, tools, opts, target) {
344
+ // Built for the account it is sent with.
345
+ const baseURL = `${this._locationBase(target)}/endpoints/openapi`;
346
+ return await this._maas.send(messages, `${route.publisher}/${route.id}`, tools, opts, {
347
+ baseURL,
348
+ identity: target.identity,
349
+ });
350
+ }
351
+
352
+ async _sendGemini(messages, model, tools, opts, tier, target) {
353
+ const signal = opts.signal || null;
354
+ const headers = {
355
+ 'Content-Type': 'application/json',
356
+ ...(tier ? { 'X-Vertex-AI-LLM-Shared-Request-Type': tier } : {}),
357
+ };
358
+ const failureUsage = geminiFailureUsage(opts, null, model);
359
+ const vertexFailureUsage = {
360
+ ...failureUsage,
361
+ normalize: (um) => toVertexExclusiveUsage(failureUsage.normalize(um)),
362
+ };
363
+ const callbacks = geminiCallbacks(opts);
364
+ const request = buildGeminiRequest(messages, model, tools, opts, null);
365
+ const body = geminiVertexBody(request);
366
+ // One account for every attempt and the unary fallback: the send's.
367
+ const { identity } = target;
368
+ const streamUrl = `${this._url('google', model, 'streamGenerateContent', target)}?alt=sse`;
369
+ const unaryUrl = this._url('google', model, 'generateContent', target);
370
+ const stream = { ...request, opts, signal, useModel: model, tools, callbacks, cachedContent: null };
371
+ try {
372
+ opts.onStageChange?.('requesting');
373
+ } catch {
374
+ // A throwing progress callback must not fail the request.
375
+ }
376
+ // Every attempt sends the same tier; the served trafficType settles the price.
377
+ noteRequestServiceTier(tier === VERTEX_PRIORITY_TIER ? tier : '');
378
+
379
+ let outcome;
380
+ try {
381
+ outcome = await runGeminiStreamAttempts({
382
+ signal,
383
+ sleepFn: this.config.sleepFn,
384
+ afterToolResult: geminiEndsWithFunctionResponse(request.contents),
385
+ failureUsage: vertexFailureUsage,
386
+ run: async ({ signal: attemptSignal, connected }) => {
387
+ callbacks.retractReasoning();
388
+ const res = await this._authorizedFetch(
389
+ streamUrl,
390
+ { method: 'POST', headers, body: JSON.stringify(body), signal: attemptSignal },
391
+ identity
392
+ );
393
+ if (!res.ok) {
394
+ const text = vertexErrorBody(await res.text().catch(() => ''));
395
+ throw geminiRestError(res, text, 'Vertex AI Gemini streamGenerateContent');
396
+ }
397
+ connected();
398
+ try {
399
+ opts.onStageChange?.('streaming');
400
+ } catch {
401
+ // A throwing progress callback must not fail the request.
402
+ }
403
+ const textLeakGuard = geminiTextLeakGuardFor(stream);
404
+ const response = await consumeGeminiRestStreamResponse(res, {
405
+ signal: attemptSignal,
406
+ onStreamDelta: callbacks.onStreamDelta,
407
+ onTextDelta: callbacks.onTextDelta,
408
+ onReasoningDelta: callbacks.onReasoningDelta,
409
+ textLeakGuard,
410
+ label: 'Vertex AI Gemini streamGenerateContent',
411
+ failureUsage: vertexFailureUsage,
412
+ ...(tier === VERTEX_FLEX_TIER
413
+ ? { firstByteTimeoutMs: VERTEX_FLEX_FIRST_CHUNK_TIMEOUT_MS, idleAfterFirstChunk: true }
414
+ : {}),
415
+ });
416
+ return { response, textLeakGuard };
417
+ },
418
+ });
419
+ } catch (streamError) {
420
+ if (!canFallbackNonStreaming(streamError, { signal })) throw streamError;
421
+ callbacks.retractReasoning();
422
+ const res = await this._authorizedFetch(
423
+ unaryUrl,
424
+ { method: 'POST', headers, body: JSON.stringify(body), signal },
425
+ identity
426
+ );
427
+ if (!res.ok) {
428
+ const text = vertexErrorBody(await res.text().catch(() => ''));
429
+ throw geminiRestError(res, text, 'Vertex AI Gemini generateContent');
430
+ }
431
+ outcome = { response: await res.json(), textLeakGuard: null };
432
+ }
433
+
434
+ const parsed = parseGeminiCandidate(outcome.response, outcome.textLeakGuard);
435
+ const usage = resolveGeminiUsage(outcome.response, opts, null, model, this.name);
436
+ const incomplete = geminiIncompleteError(outcome.response, parsed, model, usage);
437
+ if (incomplete) throw incomplete;
438
+ outcome.textLeakGuard?.releaseDeferredToolCalls?.();
439
+ emitGeminiToolCalls(parsed.nativeToolCalls, callbacks.onToolCall);
440
+ const result = geminiSendResult(parsed, model, opts, toVertexExclusiveUsage(usage));
441
+ // The served tier decides the price: a Flex or Priority request can be
442
+ // served by Provisioned Throughput first, and a Priority one downgraded.
443
+ const served = SERVED_TIER[usage?.raw?.trafficType];
444
+ if (served) result.serviceTier = served;
445
+ return result;
446
+ }
447
+
448
+ async _sendClaude(messages, model, tools, opts, target) {
449
+ messages = sanitizeToolPairs(messages);
450
+ messages = await preparePdfTextForProvider(messages, this);
451
+ const requestOpts = { ...opts, fast: false, serverFallback: false };
452
+ const { params, betas, knownToolNames } = await buildAnthropicApiRequest({
453
+ name: this.name,
454
+ config: { ...this.config, disableBetaHeaders: true },
455
+ messages,
456
+ useModel: model,
457
+ tools,
458
+ opts: requestOpts,
459
+ fastModeLatched: false,
460
+ capabilityModel: model,
461
+ });
462
+ const { model: _model, ...body } = params;
463
+ body.anthropic_version = VERTEX_ANTHROPIC_VERSION;
464
+ const vertexBetas = vertexClaudeBetas(betas, body, model);
465
+ if (vertexBetas.length) body.anthropic_beta = vertexBetas;
466
+ const callbacks = streamCallbacks(opts);
467
+ try {
468
+ callbacks.onStageChange?.('requesting');
469
+ } catch {
470
+ // A throwing progress callback must not fail the request.
471
+ }
472
+ // One account for every attempt: the send's.
473
+ const { identity } = target;
474
+ const url = this._url('anthropic', model, 'streamRawPredict', target);
475
+ const payload = JSON.stringify(body);
476
+ // The initial response retries like the first-party Anthropic route: 429
477
+ // and 529 (and 5xx) on its backoff, Retry-After honored.
478
+ const response = await withRetry(
479
+ async ({ signal: attemptSignal }) => {
480
+ const res = await this._authorizedFetch(
481
+ url,
482
+ { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload, signal: attemptSignal },
483
+ identity
484
+ );
485
+ if (res.ok) return res;
486
+ const text = await res.text().catch(() => '');
487
+ let data = null;
488
+ try {
489
+ data = JSON.parse(vertexErrorBody(text));
490
+ } catch {
491
+ // A non-JSON body carries no google.rpc.Status; the status code stands alone.
492
+ }
493
+ const error = Object.assign(
494
+ new Error(`Vertex AI Claude streamRawPredict ${res.status}: ${text.slice(0, 300)}`),
495
+ {
496
+ ...(data ? { data } : {}),
497
+ status: res.status,
498
+ httpStatus: res.status,
499
+ headers: res.headers,
500
+ response: { status: res.status, headers: res.headers },
501
+ initialResponseError: true,
502
+ }
503
+ );
504
+ throw applyAnthropicErrorContract(error, { status: res.status, headers: res.headers, bodyText: text });
505
+ },
506
+ {
507
+ signal: opts.signal || null,
508
+ maxAttempts: anthropicMaxAttempts(),
509
+ backoffMs: ANTHROPIC_RETRY_BACKOFF_MS,
510
+ retryJitterRatio: ANTHROPIC_RETRY_JITTER_RATIO,
511
+ retryJitterMode: 'positive',
512
+ recoveryOwner: 'vertex-ai-claude-initial-response',
513
+ retry529: opts.retry529,
514
+ sleepFn: this.config.sleepFn,
515
+ onRetry: ({ attempt, maxAttempts, lastErr, delayMs }) =>
516
+ emitProviderRetryStage(callbacks.onStageChange, { attempt: attempt + 1, maxAttempts, lastErr, delayMs }),
517
+ }
518
+ );
519
+ try {
520
+ callbacks.onStageChange?.('streaming');
521
+ } catch {
522
+ // A throwing progress callback must not fail the request.
523
+ }
524
+ const controller = new AbortController();
525
+ const externalSignal = opts.signal || null;
526
+ const abort = () => controller.abort(externalSignal?.reason);
527
+ if (externalSignal) {
528
+ if (externalSignal.aborted) abort();
529
+ else externalSignal.addEventListener('abort', abort, { once: true });
530
+ }
531
+ const midState = createAnthropicMidState(0);
532
+ try {
533
+ const parsed = await parseSSEStream(
534
+ response,
535
+ controller.signal,
536
+ (reason) => controller.abort(reason),
537
+ callbacks.onStreamDelta,
538
+ callbacks.onToolCall,
539
+ midState,
540
+ callbacks.onTextDelta,
541
+ knownToolNames,
542
+ { onStreamItemStart: callbacks.onStreamItemStart }
543
+ );
544
+ assertAnthropicStreamNotEmpty(midState, parsed, 'Vertex AI Claude');
545
+ return buildAnthropicTurnResult(parsed, { provider: this.name, useModel: model, opts });
546
+ } finally {
547
+ externalSignal?.removeEventListener?.('abort', abort);
548
+ }
549
+ }
550
+
551
+ async listModels() {
552
+ return VERTEX_MODELS.map((model) => {
553
+ const tierOption = vertexTierOption(model.id);
554
+ return { ...model, provider: this.name, ...(tierOption ? { modelParameterOptions: [tierOption] } : {}) };
555
+ });
556
+ }
557
+
558
+ async isAvailable() {
559
+ try {
560
+ if (this._express()) return Boolean(this._expressKey());
561
+ await this._auth.ready();
562
+ return Boolean(this._projectId());
563
+ } catch {
564
+ return false;
565
+ }
566
+ }
567
+ }
@@ -8,9 +8,9 @@ export const ASK_USER_TOOL_DEFS = Object.freeze([
8
8
  name: 'ask_user',
9
9
  title: 'Ask User',
10
10
  trigger:
11
- 'Ask the user 1–4 multiple-choice questions and wait; when a plan needs the user to pick between clear options, instead of listing them in chat. Also hands the user a page or app step (CAPTCHA, sign-in, 2FA, a secret).',
11
+ 'Ask the user 1–4 multiple-choice questions and wait; when a plan needs the user to pick between clear options, instead of listing them in chat.',
12
12
  description:
13
- 'Ask the user multiple-choice questions and wait. Only for a decision only the user can make, with clear options; never for minor choices or approval of requested work, and open questions go in chat. Do not repeat the options in your message. A step only the user can do on a page or app (CAPTCHA, human check, sign-in, 2FA, a password) is a handoff here, never a request in plain chat.',
13
+ 'Ask the user multiple-choice questions and wait. Only for a decision only the user can make, with clear options; never for minor choices or approval of requested work, and open questions go in chat. Do not repeat the options in your message. A step only the user can do on a page or app (CAPTCHA, human check, sign-in, 2FA, a password) goes to handoff_user, never a request in plain chat. Load handoff_user directly if needed; browser/computer skills are not prerequisites.',
14
14
  annotations: {
15
15
  title: 'Ask User',
16
16
  readOnlyHint: true,
@@ -52,10 +52,37 @@ export const ASK_USER_TOOL_DEFS = Object.freeze([
52
52
  required: ['question', 'header', 'options'],
53
53
  },
54
54
  },
55
+ },
56
+ additionalProperties: false,
57
+ },
58
+ },
59
+ ]);
60
+
61
+ // The deferred handoff tool: loaded with the browser-use and computer-use
62
+ // skills (dependencies.tools), the only surfaces a handoff targets.
63
+ export const HANDOFF_USER_TOOL_DEFS = Object.freeze([
64
+ {
65
+ name: 'handoff_user',
66
+ title: 'Hand Off To User',
67
+ trigger:
68
+ 'Hand the user a page or app step (CAPTCHA, sign-in, 2FA, a secret); loads with the browser-use and computer-use skills.',
69
+ description:
70
+ 'A step only the user can do on a page or app (CAPTCHA, human check, sign-in, 2FA, a password) is a handoff here, never a request in plain chat.',
71
+ annotations: {
72
+ title: 'Hand Off To User',
73
+ readOnlyHint: true,
74
+ destructiveHint: false,
75
+ idempotentHint: false,
76
+ openWorldHint: false,
77
+ agentHidden: true,
78
+ },
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
55
82
  handoff: {
56
83
  type: 'object',
57
84
  description:
58
- 'Instead of questions: secret = the user types a password, sign-in, new password or one-time code into that field of the page (you never see it; result entered/user_skipped/no_field/page_changed/blocked/expired); takeover = the user does a step (sign-in, CAPTCHA, 2FA) and says done.',
85
+ 'secret = the user types a password, sign-in, new password or one-time code into that field of the page (you never see it; result entered/user_skipped/no_field/page_changed/blocked/expired); takeover = the user does a step and says done.',
59
86
  properties: {
60
87
  kind: { type: 'string', enum: ['secret', 'takeover'] },
61
88
  surface: { type: 'string', enum: ['browser', 'computer'] },
@@ -83,26 +110,26 @@ export const ASK_USER_TOOL_DEFS = Object.freeze([
83
110
  additionalProperties: false,
84
111
  },
85
112
  },
113
+ required: ['handoff'],
86
114
  additionalProperties: false,
87
115
  },
88
116
  },
89
117
  ]);
90
118
 
91
- /** The handoff of an ask_user call ({ kind, surface, target, field, instructions }), or null. */
92
- export function validateAskUserHandoff(args) {
119
+ /** The handoff of a handoff_user call ({ kind, surface, target, field, instructions, … }). */
120
+ export function validateUserHandoff(args) {
93
121
  const handoff = args?.handoff;
94
- if (handoff == null) return null;
95
- if (args.questions != null) throw new Error('ask_user takes questions or a handoff, not both');
96
- if (typeof handoff !== 'object' || Array.isArray(handoff)) throw new Error('ask_user handoff must be an object');
122
+ if (handoff == null) throw new Error('handoff_user needs a handoff');
123
+ if (typeof handoff !== 'object' || Array.isArray(handoff)) throw new Error('handoff_user handoff must be an object');
97
124
  if (handoff.kind !== 'secret' && handoff.kind !== 'takeover') {
98
- throw new Error('ask_user handoff kind is secret or takeover');
125
+ throw new Error('handoff_user handoff kind is secret or takeover');
99
126
  }
100
127
  if (handoff.surface !== 'browser' && handoff.surface !== 'computer') {
101
- throw new Error('ask_user handoff surface is browser or computer');
128
+ throw new Error('handoff_user handoff surface is browser or computer');
102
129
  }
103
130
  const text = (key, limit) => (typeof handoff[key] === 'string' ? handoff[key].trim().slice(0, limit) : '');
104
131
  if (handoff.kind === 'takeover' && !text('instructions', 2000)) {
105
- throw new Error('ask_user takeover needs instructions');
132
+ throw new Error('handoff_user takeover needs instructions');
106
133
  }
107
134
  const login = handoffLogin(handoff);
108
135
  let need = login ? 'sign_in' : 'action';
@@ -125,10 +152,10 @@ export function validateAskUserHandoff(args) {
125
152
  function handoffLogin(handoff) {
126
153
  if (handoff.purpose == null) return null;
127
154
  if (handoff.purpose !== 'login' || handoff.kind !== 'takeover' || handoff.surface !== 'browser') {
128
- throw new Error('ask_user purpose login is a browser takeover');
155
+ throw new Error('handoff_user purpose login is a browser takeover');
129
156
  }
130
157
  const page = loginPageUrl(handoff.url);
131
- if (!page) throw new Error('ask_user login needs an https url (http only on localhost)');
158
+ if (!page) throw new Error('handoff_user login needs an https url (http only on localhost)');
132
159
  return page;
133
160
  }
134
161
 
@@ -159,6 +186,7 @@ function handoffIdentity(handoff) {
159
186
 
160
187
  /** Throws a clear error when the questions are malformed; returns the list. */
161
188
  export function validateAskUserQuestions(args) {
189
+ if (args?.handoff != null) throw new Error('ask_user takes questions only; a page or app step goes to handoff_user');
162
190
  const questions = args?.questions;
163
191
  if (!Array.isArray(questions) || questions.length < 1 || questions.length > ASK_USER_MAX_QUESTIONS) {
164
192
  throw new Error(`ask_user requires 1-${ASK_USER_MAX_QUESTIONS} questions`);