@vellumai/assistant 0.8.3 → 0.8.5

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 (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -82,6 +82,25 @@ mock.module("../../util/logger.js", () => ({
82
82
  }),
83
83
  }));
84
84
 
85
+ // Stub secure-keys so the `prepareAgentEnv` preflight finds a token without
86
+ // the test having to populate the real OS keyring. Driven via `secureKeyStore`
87
+ // per test in beforeEach; the default seeds a vault token so existing tests
88
+ // (which assume claude spawns succeed) keep passing.
89
+ //
90
+ // The real module's other exports are spread in so transitive importers
91
+ // (e.g. session-manager → pending-interactions → credential-routes, which
92
+ // imports `getSecureKeyResultAsync`) still resolve at parse time. Bun's
93
+ // `mock.module` is process-global and returns *exactly* the keys the factory
94
+ // returns — without the spread, any consumer pulling a non-`getSecureKeyAsync`
95
+ // export errors with "Export named '<X>' not found".
96
+ const secureKeyStore = new Map<string, string>();
97
+ const realSecureKeys = await import("../../security/secure-keys.js");
98
+
99
+ mock.module("../../security/secure-keys.js", () => ({
100
+ ...realSecureKeys,
101
+ getSecureKeyAsync: async (key: string) => secureKeyStore.get(key),
102
+ }));
103
+
85
104
  // Stub session manager so we don't actually spawn child processes.
86
105
  const spawnMock = mock(
87
106
  async (
@@ -131,6 +150,14 @@ beforeEach(() => {
131
150
  _resetAdapterVersionCacheForTests();
132
151
  config.setConfig({ agents: DEFAULT_TEST_AGENTS });
133
152
  which.setWhich((cmd) => `/usr/local/bin/${cmd}`);
153
+ // Default: vault has a claude token so the preflight in `prepareAgentEnv`
154
+ // succeeds for tests that don't care about env injection. Env-injection
155
+ // tests below clear/override this explicitly.
156
+ secureKeyStore.clear();
157
+ secureKeyStore.set(
158
+ "credential/acp/claude_oauth_token",
159
+ "default-test-token",
160
+ );
134
161
  });
135
162
 
136
163
  // ---------------------------------------------------------------------------
@@ -376,3 +403,95 @@ describe("executeAcpSpawn — per-agent resume hint", () => {
376
403
  expect(payload.message).not.toContain("To resume this session later");
377
404
  });
378
405
  });
406
+
407
+ // ---------------------------------------------------------------------------
408
+ // executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN env injection + preflight
409
+ //
410
+ // Mirrors the HTTP-route test block in
411
+ // `runtime/routes/acp-routes.test.ts` — the skill tool calls into the same
412
+ // `prepareAgentEnv` helper, and the contract must be identical so a
413
+ // missing token fails the spawn at the tool boundary (`isError: true`)
414
+ // instead of letting a token-less subprocess zombie out. PR-history
415
+ // context: the inline env-injection used to live in the route only; this
416
+ // tool path was bypassing it entirely, causing every skill-driven ACP
417
+ // spawn to die on first prompt with "Authentication required". Pin both
418
+ // the happy paths and the throw path here so future drift on either
419
+ // caller fails the suite loudly.
420
+ // ---------------------------------------------------------------------------
421
+
422
+ describe("executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN injection", () => {
423
+ test("injects CLAUDE_CODE_OAUTH_TOKEN from the secure store for the claude agent", async () => {
424
+ secureKeyStore.clear();
425
+ secureKeyStore.set(
426
+ "credential/acp/claude_oauth_token",
427
+ "tool-vault-token-abc",
428
+ );
429
+ execScripts.set("npm ls", { error: new Error("npm not installed") });
430
+ execScripts.set("npm view", { error: new Error("npm not installed") });
431
+
432
+ const result = await executeAcpSpawn(
433
+ { agent: "claude", task: "do something" },
434
+ makeContext(),
435
+ );
436
+
437
+ expect(result.isError).toBe(false);
438
+ expect(spawnMock).toHaveBeenCalledTimes(1);
439
+ const agentConfigArg = spawnMock.mock.calls[0][1] as {
440
+ env?: Record<string, string>;
441
+ };
442
+ expect(agentConfigArg.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
443
+ "tool-vault-token-abc",
444
+ );
445
+ });
446
+
447
+ test("accepts CLAUDE_CODE_OAUTH_TOKEN from config.json agent.env without a vault entry", async () => {
448
+ // Mirrors the route-level precedence test: a config.json env override
449
+ // is the first-priority provisioning route. The resolver surfaces it
450
+ // on `resolved.agent.env`, which the helper preserves.
451
+ secureKeyStore.clear();
452
+ config.setConfig({
453
+ agents: {
454
+ claude: {
455
+ command: "claude-agent-acp",
456
+ args: [],
457
+ env: { CLAUDE_CODE_OAUTH_TOKEN: "tool-config-token-xyz" },
458
+ },
459
+ codex: { command: "codex-acp", args: [] },
460
+ "unknown-agent": { command: "some-other-binary", args: [] },
461
+ },
462
+ });
463
+ execScripts.set("npm ls", { error: new Error("npm not installed") });
464
+ execScripts.set("npm view", { error: new Error("npm not installed") });
465
+
466
+ const result = await executeAcpSpawn(
467
+ { agent: "claude", task: "do something" },
468
+ makeContext(),
469
+ );
470
+
471
+ expect(result.isError).toBe(false);
472
+ const agentConfigArg = spawnMock.mock.calls[0][1] as {
473
+ env?: Record<string, string>;
474
+ };
475
+ expect(agentConfigArg.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
476
+ "tool-config-token-xyz",
477
+ );
478
+ });
479
+
480
+ test("returns isError when no token is available from either route (preflight throw mapped to tool result)", async () => {
481
+ // Both routes empty. The helper throws `FailedDependencyError`; the
482
+ // tool catches it and returns `{ isError: true, content: <msg> }`
483
+ // rather than letting it propagate (the tool boundary is a sync
484
+ // ToolExecutionResult, not an HTTP response).
485
+ secureKeyStore.clear();
486
+
487
+ const result = await executeAcpSpawn(
488
+ { agent: "claude", task: "do something" },
489
+ makeContext(),
490
+ );
491
+
492
+ expect(result.isError).toBe(true);
493
+ expect(result.content).toContain("CLAUDE_CODE_OAUTH_TOKEN");
494
+ // Spawn was NEVER called — preflight fired before the subprocess started.
495
+ expect(spawnMock).not.toHaveBeenCalled();
496
+ });
497
+ });
@@ -1,6 +1,7 @@
1
1
  import { execFile } from "node:child_process";
2
2
 
3
3
  import { getAcpSessionManager } from "../../acp/index.js";
4
+ import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
4
5
  import { resolveAcpAgent } from "../../acp/resolve-agent.js";
5
6
  import { DEFAULT_AGENT_NPM_PACKAGES } from "../../config/acp-defaults.js";
6
7
  import { getLogger } from "../../util/logger.js";
@@ -159,8 +160,6 @@ export async function executeAcpSpawn(
159
160
  }
160
161
  }
161
162
  }
162
- const agentConfig = resolved.agent;
163
-
164
163
  const sendToClient = context.sendToClient as
165
164
  | ((msg: { type: string; [key: string]: unknown }) => void)
166
165
  | undefined;
@@ -171,6 +170,20 @@ export async function executeAcpSpawn(
171
170
  };
172
171
  }
173
172
 
173
+ // Inject required env vars and preflight via the shared helper. Mirrors
174
+ // the HTTP route at `runtime/routes/acp-routes.ts:spawnSession` — both
175
+ // call sites MUST go through `prepareAgentEnv` before `manager.spawn`,
176
+ // otherwise the spawned subprocess starts with no auth and dies as a
177
+ // zombie after the first prompt. See `acp/prepare-agent-env.ts` for
178
+ // the full rationale.
179
+ let agentConfig;
180
+ try {
181
+ agentConfig = await prepareAgentEnv(resolved.agent);
182
+ } catch (err) {
183
+ const msg = err instanceof Error ? err.message : String(err);
184
+ return { content: msg, isError: true };
185
+ }
186
+
174
187
  // Best-effort version check — never blocks the spawn. If outdated, we
175
188
  // append a non-blocking warning to the success payload.
176
189
  const versionInfo = await checkAdapterVersion(agentConfig.command);
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  import { RiskLevel } from "../../permissions/types.js";
12
- import type { ToolDefinition } from "../../providers/types.js";
13
12
  import type { Tool, ToolExecutionResult } from "../types.js";
14
13
 
15
14
  // ---------------------------------------------------------------------------
@@ -33,12 +32,9 @@ const appOpenTool: Tool = {
33
32
  category: "apps",
34
33
  defaultRiskLevel: RiskLevel.Low,
35
34
  executionMode: "proxy",
35
+ executionTarget: "host",
36
36
 
37
- getDefinition(): ToolDefinition {
38
- return {
39
- name: this.name,
40
- description: this.description,
41
- input_schema: {
37
+ input_schema: {
42
38
  type: "object",
43
39
  properties: {
44
40
  app_id: {
@@ -54,8 +50,6 @@ const appOpenTool: Tool = {
54
50
  },
55
51
  required: ["app_id"],
56
52
  },
57
- };
58
- },
59
53
 
60
54
  execute: proxyExecute,
61
55
  };
@@ -51,7 +51,7 @@ const singleQ = {
51
51
 
52
52
  describe("AskQuestionTool definition", () => {
53
53
  test("exposes the expected schema shape and description language", () => {
54
- const def = new AskQuestionTool().getDefinition();
54
+ const def = new AskQuestionTool();
55
55
  expect(def.name).toBe("ask_question");
56
56
  expect(def.description).toContain("free-text fallback is always added");
57
57
  expect(def.description).toContain("do not");
@@ -463,8 +463,8 @@ describe("AskQuestionTool batched input", () => {
463
463
 
464
464
  describe("AskQuestionTool definition (batched schema)", () => {
465
465
  test("exposes `questions[]` shape, keeps legacy fields, omits per-question id", () => {
466
- const def = new AskQuestionTool().getDefinition();
467
- const schema = def.input_schema as {
466
+ const def = new AskQuestionTool();
467
+ const schema = def.input_schema as unknown as {
468
468
  properties: Record<
469
469
  string,
470
470
  {
@@ -2,13 +2,12 @@ import { z } from "zod";
2
2
 
3
3
  import { QuestionPrompter } from "../../permissions/question-prompter.js";
4
4
  import { RiskLevel } from "../../permissions/types.js";
5
- import type { ToolDefinition } from "../../providers/types.js";
6
5
  import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
7
6
  import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
8
7
 
9
8
  // ── Input schema ────────────────────────────────────────────────────
10
9
  // Runtime validation lives in Zod; the wire-level definition surfaced
11
- // to the LLM is the hand-written JSON Schema in getDefinition() below.
10
+ // to the LLM is the hand-written JSON Schema in `input_schema` below.
12
11
  // (The codebase does not currently use zod-to-json-schema for tool defs,
13
12
  // so the two are kept in sync manually.)
14
13
 
@@ -109,53 +108,37 @@ const DESCRIPTION = [
109
108
  "context shown beneath the label.",
110
109
  ].join("\n");
111
110
 
111
+ // Shared option-schema fragment used by both the batched `questions[]`
112
+ // shape and the legacy flat `options` field.
113
+ const OPTION_ITEMS_SCHEMA = {
114
+ type: "object",
115
+ properties: {
116
+ id: {
117
+ type: "string",
118
+ description:
119
+ "Stable identifier for this option (returned verbatim in the response).",
120
+ },
121
+ label: {
122
+ type: "string",
123
+ description: "Short human-readable label.",
124
+ },
125
+ description: {
126
+ type: "string",
127
+ description: "Optional one-line context shown beneath the label.",
128
+ },
129
+ },
130
+ required: ["id", "label"],
131
+ } as const;
132
+
112
133
  // ── Tool ────────────────────────────────────────────────────────────
113
134
 
114
135
  export class AskQuestionTool implements Tool {
115
136
  name = "ask_question";
116
137
  description = DESCRIPTION;
117
138
  category = "interaction";
139
+ executionTarget = "sandbox" as const;
118
140
  defaultRiskLevel = RiskLevel.Low;
119
-
120
- // Override hook for tests: lets a test replace the prompter factory
121
- // without monkey-patching the module. Default factory wires the real
122
- // broadcastMessage so the question reaches every connected client.
123
- private prompterFactory: () => Pick<QuestionPrompter, "prompt">;
124
-
125
- constructor(
126
- prompterFactory: () => Pick<QuestionPrompter, "prompt"> = () =>
127
- new QuestionPrompter({ broadcastMessage }),
128
- ) {
129
- this.prompterFactory = prompterFactory;
130
- }
131
-
132
- getDefinition(): ToolDefinition {
133
- // Shared option-schema fragment used by both the batched `questions[]`
134
- // shape and the legacy flat `options` field.
135
- const optionItemsSchema = {
136
- type: "object",
137
- properties: {
138
- id: {
139
- type: "string",
140
- description:
141
- "Stable identifier for this option (returned verbatim in the response).",
142
- },
143
- label: {
144
- type: "string",
145
- description: "Short human-readable label.",
146
- },
147
- description: {
148
- type: "string",
149
- description: "Optional one-line context shown beneath the label.",
150
- },
151
- },
152
- required: ["id", "label"],
153
- } as const;
154
-
155
- return {
156
- name: this.name,
157
- description: this.description,
158
- input_schema: {
141
+ input_schema = {
159
142
  type: "object",
160
143
  properties: {
161
144
  // ── Recommended shape ─────────────────────────────────────
@@ -182,7 +165,7 @@ export class AskQuestionTool implements Tool {
182
165
  maxItems: 4,
183
166
  description:
184
167
  "2–4 structured options. The UI always appends a free-text fallback slot, so do not include a 'something else' option here.",
185
- items: optionItemsSchema,
168
+ items: OPTION_ITEMS_SCHEMA,
186
169
  },
187
170
  freeTextPlaceholder: {
188
171
  type: "string",
@@ -212,7 +195,7 @@ export class AskQuestionTool implements Tool {
212
195
  maxItems: 4,
213
196
  description:
214
197
  "Legacy: 2–4 structured options. Prefer `questions[].options`. The UI always appends a free-text fallback slot, so do not include a 'something else' option here.",
215
- items: optionItemsSchema,
198
+ items: OPTION_ITEMS_SCHEMA,
216
199
  },
217
200
  freeTextPlaceholder: {
218
201
  type: "string",
@@ -222,8 +205,18 @@ export class AskQuestionTool implements Tool {
222
205
  },
223
206
  // No top-level `required` — caller must supply either `questions`
224
207
  // or the legacy flat trio (`question` + `options`). Enforced in Zod.
225
- },
226
208
  };
209
+
210
+ // Override hook for tests: lets a test replace the prompter factory
211
+ // without monkey-patching the module. Default factory wires the real
212
+ // broadcastMessage so the question reaches every connected client.
213
+ private prompterFactory: () => Pick<QuestionPrompter, "prompt">;
214
+
215
+ constructor(
216
+ prompterFactory: () => Pick<QuestionPrompter, "prompt"> = () =>
217
+ new QuestionPrompter({ broadcastMessage }),
218
+ ) {
219
+ this.prompterFactory = prompterFactory;
227
220
  }
228
221
 
229
222
  async execute(
@@ -0,0 +1,150 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ __resetPinnedTabsForTests,
5
+ clearPinnedTab,
6
+ clearPinnedTabByTabId,
7
+ getPinnedTab,
8
+ setPinnedTab,
9
+ } from "../pinned-tabs.js";
10
+
11
+ describe("pinned-tabs", () => {
12
+ afterEach(() => {
13
+ __resetPinnedTabsForTests();
14
+ });
15
+
16
+ test("setPinnedTab / getPinnedTab round-trips", () => {
17
+ setPinnedTab("conv-a", "42");
18
+ expect(getPinnedTab("conv-a")).toBe("42");
19
+ });
20
+
21
+ test("getPinnedTab returns undefined for unset conversation", () => {
22
+ expect(getPinnedTab("unset-conv")).toBeUndefined();
23
+ });
24
+
25
+ test("conversations have independent pins", () => {
26
+ setPinnedTab("conv-a", "42");
27
+ setPinnedTab("conv-b", "99");
28
+ expect(getPinnedTab("conv-a")).toBe("42");
29
+ expect(getPinnedTab("conv-b")).toBe("99");
30
+ });
31
+
32
+ test("setPinnedTab overwrites a prior pin for the same conversation", () => {
33
+ setPinnedTab("conv-a", "42");
34
+ setPinnedTab("conv-a", "100");
35
+ expect(getPinnedTab("conv-a")).toBe("100");
36
+ });
37
+
38
+ test("clearPinnedTab removes the pin", () => {
39
+ setPinnedTab("conv-a", "42");
40
+ clearPinnedTab("conv-a");
41
+ expect(getPinnedTab("conv-a")).toBeUndefined();
42
+ });
43
+
44
+ test("clearPinnedTab on a non-existent conversation is a no-op", () => {
45
+ // Should not throw or affect other conversations.
46
+ setPinnedTab("conv-a", "42");
47
+ clearPinnedTab("nonexistent");
48
+ expect(getPinnedTab("conv-a")).toBe("42");
49
+ });
50
+
51
+ test("clearPinnedTabByTabId clears every matching conversation", () => {
52
+ setPinnedTab("conv-a", "42");
53
+ setPinnedTab("conv-b", "42");
54
+ setPinnedTab("conv-c", "99");
55
+
56
+ const cleared = clearPinnedTabByTabId("42");
57
+
58
+ expect(cleared).toBe(2);
59
+ expect(getPinnedTab("conv-a")).toBeUndefined();
60
+ expect(getPinnedTab("conv-b")).toBeUndefined();
61
+ expect(getPinnedTab("conv-c")).toBe("99"); // unchanged
62
+ });
63
+
64
+ test("clearPinnedTabByTabId on a non-matching tab is a no-op returning 0", () => {
65
+ setPinnedTab("conv-a", "42");
66
+ const cleared = clearPinnedTabByTabId("999");
67
+ expect(cleared).toBe(0);
68
+ expect(getPinnedTab("conv-a")).toBe("42");
69
+ });
70
+
71
+ test("empty conversationId is rejected on set", () => {
72
+ setPinnedTab("", "42");
73
+ expect(getPinnedTab("")).toBeUndefined();
74
+ });
75
+
76
+ test("empty tabId is rejected on set", () => {
77
+ setPinnedTab("conv-a", "");
78
+ expect(getPinnedTab("conv-a")).toBeUndefined();
79
+ });
80
+
81
+ describe("cross-client isolation (#31361)", () => {
82
+ test("same tabId on different clients are stored independently", () => {
83
+ setPinnedTab("conv-a", "99", "clientA");
84
+ setPinnedTab("conv-a", "99", "clientB");
85
+ expect(getPinnedTab("conv-a", "clientA")).toBe("99");
86
+ expect(getPinnedTab("conv-a", "clientB")).toBe("99");
87
+ });
88
+
89
+ test("clearPinnedTabByTabId with clientId only clears that client", () => {
90
+ setPinnedTab("conv-a", "99", "clientA");
91
+ setPinnedTab("conv-b", "99", "clientB");
92
+
93
+ const cleared = clearPinnedTabByTabId("99", "clientA");
94
+
95
+ expect(cleared).toBe(1);
96
+ expect(getPinnedTab("conv-a", "clientA")).toBeUndefined();
97
+ expect(getPinnedTab("conv-b", "clientB")).toBe("99"); // clientB's pin survives
98
+ });
99
+
100
+ test("getPinnedTab with matching clientId returns correct tabId", () => {
101
+ setPinnedTab("conv-x", "42", "clientA");
102
+ setPinnedTab("conv-x", "77", "clientB");
103
+
104
+ expect(getPinnedTab("conv-x", "clientA")).toBe("42");
105
+ expect(getPinnedTab("conv-x", "clientB")).toBe("77");
106
+ });
107
+
108
+ test("clearPinnedTabByTabId without clientId clears all matching entries (backward compat)", () => {
109
+ setPinnedTab("conv-a", "99", "clientA");
110
+ setPinnedTab("conv-b", "99", "clientB");
111
+
112
+ const cleared = clearPinnedTabByTabId("99");
113
+
114
+ expect(cleared).toBe(2);
115
+ expect(getPinnedTab("conv-a", "clientA")).toBeUndefined();
116
+ expect(getPinnedTab("conv-b", "clientB")).toBeUndefined();
117
+ });
118
+
119
+ test("clearPinnedTab with clientId only removes that client slot", () => {
120
+ setPinnedTab("conv-a", "42", "clientA");
121
+ setPinnedTab("conv-a", "77", "clientB");
122
+
123
+ clearPinnedTab("conv-a", "clientA");
124
+
125
+ expect(getPinnedTab("conv-a", "clientA")).toBeUndefined();
126
+ expect(getPinnedTab("conv-a", "clientB")).toBe("77");
127
+ });
128
+
129
+ test("clearPinnedTab without clientId removes all slots", () => {
130
+ setPinnedTab("conv-a", "42", "clientA");
131
+ setPinnedTab("conv-a", "77", "clientB");
132
+
133
+ clearPinnedTab("conv-a");
134
+
135
+ expect(getPinnedTab("conv-a", "clientA")).toBeUndefined();
136
+ expect(getPinnedTab("conv-a", "clientB")).toBeUndefined();
137
+ });
138
+
139
+ test("getPinnedTab with unknown clientId falls back to __default__ slot", () => {
140
+ setPinnedTab("conv-a", "42"); // stored in __default__ slot
141
+ expect(getPinnedTab("conv-a", "clientA")).toBe("42");
142
+ });
143
+
144
+ test("getPinnedTab without clientId returns first entry", () => {
145
+ setPinnedTab("conv-a", "42", "clientA");
146
+ const result = getPinnedTab("conv-a");
147
+ expect(result).toBe("42");
148
+ });
149
+ });
150
+ });
@@ -70,6 +70,7 @@ import type {
70
70
  CdpClient,
71
71
  CdpClientKind,
72
72
  } from "./cdp-client/types.js";
73
+ import { clearPinnedTab, setPinnedTab } from "./pinned-tabs.js";
73
74
  import { checkBrowserRuntime } from "./runtime-check.js";
74
75
 
75
76
  const log = getLogger("headless-browser");
@@ -471,6 +472,24 @@ function wrapWithKindMemo(
471
472
  dispose(): void {
472
473
  inner.dispose();
473
474
  },
475
+ // Proxy the optional setCdpSessionId through to the underlying
476
+ // client so the navigate executor's --new-tab path can pin the
477
+ // freshly-created tab onto the extension CDP client. We define
478
+ // the method unconditionally here (rather than only when the
479
+ // inner client implements it) so callers can use a simple optional
480
+ // chain on this wrapper without re-walking the inner reference.
481
+ setCdpSessionId(cdpSessionId: string): void {
482
+ inner.setCdpSessionId?.(cdpSessionId);
483
+ },
484
+ listTabs() {
485
+ return inner.listTabs();
486
+ },
487
+ selectTab(tabId: number) {
488
+ return inner.selectTab(tabId);
489
+ },
490
+ closeTab(tabId: number) {
491
+ return inner.closeTab(tabId);
492
+ },
474
493
  };
475
494
  }
476
495
 
@@ -624,6 +643,93 @@ export async function executeBrowserNavigate(
624
643
  if (acquired.errorResult) return acquired.errorResult;
625
644
  const { cdp, browserMode } = acquired;
626
645
 
646
+ // --new-tab: open a fresh tab via the extension's Vellum.createTab
647
+ // pseudo-CDP method, then pin this client (and the conversation) to
648
+ // the returned tabId so this Page.navigate and every subsequent
649
+ // command on the same conversation routes to the new tab instead of
650
+ // the user's currently-active tab. Extension backend only; the local
651
+ // (Playwright) backend manages its own isolated browser and the
652
+ // cdp-inspect backend connects to a single tab by URL pattern.
653
+ const newTab = input.new_tab === true;
654
+ if (newTab && cdp.kind === "extension") {
655
+ try {
656
+ const result = await cdp.send<{ tabId?: number | string; clientId?: string }>(
657
+ "Vellum.createTab",
658
+ {},
659
+ context.signal,
660
+ );
661
+ const tabId =
662
+ typeof result?.tabId === "number"
663
+ ? String(result.tabId)
664
+ : typeof result?.tabId === "string"
665
+ ? result.tabId
666
+ : undefined;
667
+ const clientId =
668
+ typeof result?.clientId === "string" && result.clientId.length > 0
669
+ ? result.clientId
670
+ : undefined;
671
+ if (!tabId) {
672
+ // Malformed createTab response (no tabId). We're nominally falling
673
+ // back to active-tab routing — but the live `cdp` instance was
674
+ // already constructed with whatever pin was in scope for this
675
+ // conversation, AND the pin store still holds it for future
676
+ // client construction. Clear both: the pin store (so the next
677
+ // executeBrowserNavigate builds a clean client) AND the current
678
+ // cdp instance's session (so the Page.navigate that runs in a
679
+ // few lines targets the active tab rather than the stale pin).
680
+ // Without the setCdpSessionId(undefined) call, the warn message
681
+ // is a lie: navigation would still route to the dead tab via the
682
+ // already-injected cdpSessionId and likely fail with
683
+ // cdp_session_not_found.
684
+ clearPinnedTab(context.conversationId);
685
+ cdp.setCdpSessionId?.(undefined);
686
+ log.warn(
687
+ { conversationId: context.conversationId, result },
688
+ "Vellum.createTab returned no tabId; cleared stale pin and live session, falling back to active-tab routing",
689
+ );
690
+ } else {
691
+ cdp.setCdpSessionId?.(tabId);
692
+ setPinnedTab(context.conversationId, tabId, clientId);
693
+ log.debug(
694
+ { conversationId: context.conversationId, tabId, clientId },
695
+ "Opened new tab via --new-tab; pinned subsequent ops to it",
696
+ );
697
+ }
698
+ } catch (err) {
699
+ // Surface the failure rather than silently clobbering the active
700
+ // tab — that's exactly the behavior --new-tab is supposed to
701
+ // avoid. Clear any stale pin so subsequent ops don't route to a
702
+ // dead tab. Note: an old extension build without Vellum.createTab
703
+ // support will land here (CDP returns an "unknown method" error).
704
+ // We're early-returning before the main try/finally block below,
705
+ // so we must dispose the cdp client manually to avoid leaking it.
706
+ clearPinnedTab(context.conversationId);
707
+ const message =
708
+ err instanceof Error ? err.message : String(err);
709
+ log.warn(
710
+ { conversationId: context.conversationId, err },
711
+ "Vellum.createTab failed; aborting --new-tab navigate",
712
+ );
713
+ try {
714
+ cdp.dispose();
715
+ } catch (disposeErr) {
716
+ log.warn(
717
+ { conversationId: context.conversationId, err: disposeErr },
718
+ "Failed to dispose CDP client after Vellum.createTab failure",
719
+ );
720
+ }
721
+ return {
722
+ content: `Error: Failed to open a new tab for navigation: ${message}. The Chrome extension may need an update to support --new-tab.`,
723
+ isError: true,
724
+ };
725
+ }
726
+ } else if (newTab && cdp.kind !== "extension") {
727
+ log.debug(
728
+ { conversationId: context.conversationId, backendKind: cdp.kind },
729
+ "--new-tab requested but backend does not support it; ignoring",
730
+ );
731
+ }
732
+
627
733
  // Screencast + handoff are Playwright-backed and only meaningful
628
734
  // for the local sacrificial-profile path. On the extension path the
629
735
  // user already has their own Chrome window, so both are no-ops.