@vellumai/assistant 0.8.10 → 0.8.11-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. package/bun.lock +62 -1
  2. package/docs/workspace-tools.md +196 -0
  3. package/examples/plugins/echo/README.md +3 -3
  4. package/knip.json +1 -0
  5. package/openapi.yaml +460 -128
  6. package/package.json +2 -1
  7. package/scripts/build-plugin-api.ts +299 -0
  8. package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
  9. package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
  10. package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
  12. package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
  13. package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
  14. package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
  15. package/src/__tests__/agent-loop-thinking.test.ts +4 -0
  16. package/src/__tests__/agent-loop.test.ts +578 -5
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  20. package/src/__tests__/btw-routes.test.ts +0 -1
  21. package/src/__tests__/build-persisted-content.test.ts +75 -1
  22. package/src/__tests__/catalog-install-normalize.test.ts +141 -0
  23. package/src/__tests__/ces-startup-timeout.test.ts +60 -0
  24. package/src/__tests__/compaction-events.test.ts +1 -0
  25. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  26. package/src/__tests__/context-overflow-reducer.test.ts +264 -124
  27. package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
  28. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  29. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
  30. package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
  31. package/src/__tests__/conversation-agent-loop.test.ts +131 -551
  32. package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
  33. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  34. package/src/__tests__/conversation-fork-crud.test.ts +259 -0
  35. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  36. package/src/__tests__/conversation-lifecycle.test.ts +257 -1
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
  39. package/src/__tests__/conversation-queue.test.ts +1 -39
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
  41. package/src/__tests__/conversation-skill-tools.test.ts +491 -5
  42. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  43. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  44. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  45. package/src/__tests__/conversation-store.test.ts +74 -0
  46. package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
  47. package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
  48. package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
  49. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
  52. package/src/__tests__/corrected-target.test.ts +93 -0
  53. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
  54. package/src/__tests__/credential-execution-tools.test.ts +23 -11
  55. package/src/__tests__/credential-security-invariants.test.ts +6 -1
  56. package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
  57. package/src/__tests__/device-id.test.ts +70 -1
  58. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  59. package/src/__tests__/empty-response-hook.test.ts +242 -66
  60. package/src/__tests__/external-plugin-loader.test.ts +0 -31
  61. package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
  62. package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
  63. package/src/__tests__/history-repair-hook.test.ts +228 -3
  64. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +254 -9
  66. package/src/__tests__/identity-routes.test.ts +1 -0
  67. package/src/__tests__/image-recovery-hook.test.ts +387 -0
  68. package/src/__tests__/injector-chain.test.ts +5 -4
  69. package/src/__tests__/injector-v3-suppression.test.ts +373 -47
  70. package/src/__tests__/intent-routing.test.ts +7 -0
  71. package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
  72. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  73. package/src/__tests__/oauth-store.test.ts +0 -85
  74. package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
  75. package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
  76. package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
  77. package/src/__tests__/persist-unsendable-image.test.ts +4 -4
  78. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  79. package/src/__tests__/plugin-bootstrap.test.ts +82 -73
  80. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  81. package/src/__tests__/plugin-types.test.ts +0 -8
  82. package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
  83. package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
  84. package/src/__tests__/registry.test.ts +240 -1
  85. package/src/__tests__/require-fresh-approval.test.ts +3 -0
  86. package/src/__tests__/schedule-routes.test.ts +116 -1
  87. package/src/__tests__/schedule-store.test.ts +28 -0
  88. package/src/__tests__/schedule-tools.test.ts +94 -1
  89. package/src/__tests__/server-history-render.test.ts +39 -0
  90. package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
  91. package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
  92. package/src/__tests__/skills.test.ts +202 -0
  93. package/src/__tests__/slim-skill-category.test.ts +195 -0
  94. package/src/__tests__/strip-memory-injections.test.ts +33 -39
  95. package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
  96. package/src/__tests__/title-generate-hook.test.ts +9 -7
  97. package/src/__tests__/tool-audit-listener.test.ts +264 -1
  98. package/src/__tests__/tool-error-hook.test.ts +4 -3
  99. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
  100. package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
  101. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
  102. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  103. package/src/__tests__/tools-get-route.test.ts +202 -0
  104. package/src/__tests__/workspace-tool-loader.test.ts +319 -0
  105. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  106. package/src/agent/loop.ts +569 -319
  107. package/src/api/events/tool-result.ts +9 -0
  108. package/src/api/events/tool-use-start.ts +7 -0
  109. package/src/api/index.ts +10 -0
  110. package/src/api/responses/conversation-message.ts +135 -27
  111. package/src/api/responses/memory-v3-selection-log.ts +4 -4
  112. package/src/approvals/guardian-request-resolvers.ts +26 -0
  113. package/src/browser-session/backends/host-bridge.ts +29 -0
  114. package/src/browser-session/index.ts +1 -0
  115. package/src/browser-session/types.ts +5 -1
  116. package/src/cli/commands/__tests__/schedules.test.ts +62 -4
  117. package/src/cli/commands/__tests__/skills.test.ts +53 -0
  118. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  119. package/src/cli/commands/inference-providers.ts +0 -8
  120. package/src/cli/commands/plugins.ts +2 -2
  121. package/src/cli/commands/schedules.ts +27 -4
  122. package/src/cli/commands/skills.ts +187 -146
  123. package/src/cli/commands/tools.ts +106 -0
  124. package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
  125. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
  126. package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
  127. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
  128. package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
  129. package/src/cli/lib/install-from-github.ts +187 -117
  130. package/src/cli/lib/plugin-catalog-cache.ts +9 -9
  131. package/src/cli/lib/plugin-details.ts +38 -68
  132. package/src/cli/lib/plugin-marketplace.ts +42 -14
  133. package/src/cli/lib/search-plugins.ts +29 -129
  134. package/src/cli/program.ts +2 -0
  135. package/src/config/bundled-skills/acp/SKILL.md +1 -0
  136. package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
  137. package/src/config/bundled-skills/app-control/SKILL.md +1 -0
  138. package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
  139. package/src/config/bundled-skills/contacts/SKILL.md +1 -0
  140. package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
  141. package/src/config/bundled-skills/followups/SKILL.md +1 -0
  142. package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
  143. package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
  144. package/src/config/bundled-skills/messaging/SKILL.md +1 -0
  145. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
  146. package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +1 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
  149. package/src/config/bundled-skills/sequences/SKILL.md +1 -0
  150. package/src/config/bundled-skills/settings/SKILL.md +1 -0
  151. package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
  152. package/src/config/bundled-skills/subagent/SKILL.md +1 -0
  153. package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
  154. package/src/config/env-registry.ts +23 -0
  155. package/src/config/feature-flag-registry.json +17 -81
  156. package/src/config/loader.ts +5 -22
  157. package/src/config/schema.ts +2 -0
  158. package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
  159. package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
  160. package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
  161. package/src/config/schemas/compaction-logs.ts +79 -0
  162. package/src/config/schemas/memory-v2.ts +0 -8
  163. package/src/config/schemas/memory-v3.ts +104 -33
  164. package/src/config/seed-inference-profiles.ts +1 -1
  165. package/src/config/skills.ts +117 -47
  166. package/src/context/compactor.ts +11 -0
  167. package/src/context/strip-injections.ts +38 -4
  168. package/src/credential-execution/feature-gates.ts +0 -21
  169. package/src/credential-execution/startup-timeout.ts +32 -4
  170. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
  171. package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
  172. package/src/daemon/conversation-agent-loop.ts +123 -658
  173. package/src/daemon/conversation-error.ts +6 -33
  174. package/src/daemon/conversation-lifecycle.ts +1 -1
  175. package/src/daemon/conversation-runtime-assembly.ts +183 -22
  176. package/src/daemon/conversation-skill-tools.ts +137 -8
  177. package/src/daemon/conversation-slash.ts +0 -14
  178. package/src/daemon/conversation-store.ts +2 -19
  179. package/src/daemon/conversation-tool-setup.ts +87 -1
  180. package/src/daemon/conversation.ts +119 -50
  181. package/src/daemon/external-plugins-bootstrap.ts +36 -95
  182. package/src/daemon/handlers/config-channels.ts +11 -2
  183. package/src/daemon/handlers/shared.ts +9 -1
  184. package/src/daemon/handlers/skills.ts +10 -4
  185. package/src/daemon/host-app-control-proxy.ts +72 -57
  186. package/src/daemon/host-browser-proxy.ts +117 -22
  187. package/src/daemon/lifecycle.ts +34 -5
  188. package/src/daemon/message-protocol.ts +0 -7
  189. package/src/daemon/message-types/schedules.ts +1 -0
  190. package/src/daemon/message-types/skills.ts +17 -0
  191. package/src/daemon/providers-setup.ts +3 -0
  192. package/src/daemon/server.ts +3 -3
  193. package/src/daemon/tool-setup-types.ts +9 -3
  194. package/src/daemon/trust-context.ts +23 -0
  195. package/src/daemon/workspace-tools-watcher.ts +324 -0
  196. package/src/events/tool-audit-listener.ts +78 -16
  197. package/src/events/tool-metrics-listener.ts +2 -5
  198. package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
  199. package/src/memory/__tests__/conversation-queries.test.ts +176 -0
  200. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
  201. package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
  202. package/src/memory/conversation-crud.ts +134 -3
  203. package/src/memory/conversation-queries.ts +64 -7
  204. package/src/memory/db-init.ts +14 -0
  205. package/src/memory/embedding-backend.test.ts +130 -1
  206. package/src/memory/embedding-backend.ts +79 -106
  207. package/src/memory/embedding-gemini.ts +5 -0
  208. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
  209. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
  210. package/src/memory/graph/conversation-graph-memory.ts +36 -25
  211. package/src/memory/graph/tool-handlers.ts +3 -0
  212. package/src/memory/job-handlers/cleanup.ts +3 -1
  213. package/src/memory/jobs-store.ts +0 -28
  214. package/src/memory/jobs-worker.ts +10 -22
  215. package/src/memory/memory-marker.ts +29 -0
  216. package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
  217. package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
  218. package/src/memory/migrations/270-schedule-description.ts +36 -0
  219. package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
  220. package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
  221. package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
  222. package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
  223. package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
  224. package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
  225. package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
  226. package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
  227. package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
  228. package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
  229. package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
  230. package/src/memory/migrations/index.ts +10 -0
  231. package/src/memory/migrations/registry.ts +8 -0
  232. package/src/memory/schema/conversations.ts +16 -0
  233. package/src/memory/schema/infrastructure.ts +26 -0
  234. package/src/memory/skill-loaded-events-store.test.ts +160 -0
  235. package/src/memory/skill-loaded-events-store.ts +95 -0
  236. package/src/memory/tool-executed-events-store.test.ts +219 -0
  237. package/src/memory/tool-executed-events-store.ts +102 -0
  238. package/src/memory/tool-usage-store.ts +15 -3
  239. package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
  240. package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
  241. package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
  242. package/src/memory/v2/__tests__/page-store.test.ts +33 -0
  243. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
  244. package/src/memory/v2/activation-store.ts +50 -1
  245. package/src/memory/v2/consolidation-job.ts +113 -29
  246. package/src/memory/v2/injected-block-slugs.ts +79 -0
  247. package/src/memory/v2/injection.ts +6 -1
  248. package/src/memory/v2/prompts/consolidation.ts +414 -13
  249. package/src/memory/v2/router.ts +2 -28
  250. package/src/memory/v2/static-context.ts +1 -1
  251. package/src/memory/v2/types.ts +16 -0
  252. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  253. package/src/notifications/access-request-copy.ts +298 -0
  254. package/src/notifications/adapters/slack.ts +3 -3
  255. package/src/notifications/adapters/telegram.ts +2 -1
  256. package/src/notifications/copy-composer.ts +49 -267
  257. package/src/notifications/decision-engine.ts +16 -35
  258. package/src/notifications/home-feed-side-effect.ts +1 -6
  259. package/src/oauth/oauth-store.ts +0 -9
  260. package/src/permissions/checker.test.ts +83 -1
  261. package/src/permissions/checker.ts +25 -2
  262. package/src/permissions/gateway-threshold-reader.test.ts +182 -0
  263. package/src/permissions/gateway-threshold-reader.ts +80 -0
  264. package/src/platform/client.ts +1 -3
  265. package/src/platform/feature-gate.ts +3 -12
  266. package/src/plugin-api/constants.ts +4 -2
  267. package/src/plugin-api/index.ts +63 -11
  268. package/src/plugin-api/types.ts +236 -71
  269. package/src/plugins/defaults/compaction/compact.ts +66 -2
  270. package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
  271. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  272. package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
  273. package/src/plugins/defaults/compaction/window-manager.ts +303 -1
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
  275. package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
  276. package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
  277. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
  278. package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
  279. package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
  280. package/src/plugins/defaults/history-repair/terminal.ts +39 -2
  281. package/src/plugins/defaults/image-recovery/detect.ts +25 -0
  282. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
  283. package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
  284. package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
  285. package/src/plugins/defaults/image-recovery/package.json +14 -0
  286. package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
  287. package/src/plugins/defaults/index.ts +71 -5
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
  289. package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
  291. package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
  304. package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
  305. package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
  306. package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
  307. package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
  308. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
  309. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
  310. package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
  311. package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
  312. package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
  313. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
  314. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
  315. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
  316. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
  317. package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
  318. package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
  319. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
  320. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
  321. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
  322. package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
  323. package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
  324. package/src/plugins/pipeline.ts +8 -5
  325. package/src/plugins/types.ts +5 -51
  326. package/src/providers/cache-control.ts +26 -0
  327. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
  328. package/src/providers/model-catalog.ts +13 -1
  329. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
  330. package/src/providers/openai/chat-completions-provider.ts +46 -0
  331. package/src/providers/openai/responses-provider.ts +45 -0
  332. package/src/providers/registry.ts +0 -8
  333. package/src/runtime/__tests__/agent-wake.test.ts +0 -1
  334. package/src/runtime/agent-wake.ts +13 -0
  335. package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
  336. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
  337. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
  338. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
  339. package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
  340. package/src/runtime/routes/acp-routes.test.ts +106 -0
  341. package/src/runtime/routes/acp-routes.ts +248 -2
  342. package/src/runtime/routes/browser-tabs-routes.ts +1 -1
  343. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  344. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
  345. package/src/runtime/routes/consolidation-routes.ts +6 -82
  346. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  347. package/src/runtime/routes/conversation-management-routes.ts +71 -0
  348. package/src/runtime/routes/identity-routes.ts +8 -0
  349. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  350. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
  351. package/src/runtime/routes/plugins-routes.ts +34 -45
  352. package/src/runtime/routes/schedule-routes.ts +43 -5
  353. package/src/runtime/routes/settings-routes.ts +140 -15
  354. package/src/runtime/routes/skills-routes.ts +18 -6
  355. package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
  356. package/src/runtime/services/conversation-serializer.ts +38 -1
  357. package/src/runtime/verification-outbound-actions.ts +147 -2
  358. package/src/runtime/verification-templates.ts +29 -3
  359. package/src/schedule/schedule-store.ts +19 -0
  360. package/src/skills/catalog-install.ts +77 -13
  361. package/src/tasks/task-scheduler.ts +1 -0
  362. package/src/telemetry/types.ts +66 -1
  363. package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
  364. package/src/telemetry/usage-telemetry-reporter.ts +213 -20
  365. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
  366. package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
  367. package/src/tools/browser/browser-execution.ts +27 -9
  368. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
  369. package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
  370. package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
  371. package/src/tools/browser/cdp-client/factory.ts +217 -17
  372. package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
  373. package/src/tools/browser/cdp-client/types.ts +22 -2
  374. package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
  375. package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
  376. package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
  377. package/src/tools/executor.ts +39 -7
  378. package/src/tools/registry.ts +387 -5
  379. package/src/tools/schedule/create.ts +16 -0
  380. package/src/tools/schedule/list.ts +12 -4
  381. package/src/tools/schedule/update.ts +12 -0
  382. package/src/tools/skills/load.ts +11 -6
  383. package/src/tools/terminal/safe-env.ts +2 -0
  384. package/src/tools/types.ts +65 -9
  385. package/src/tools/workspace-tools/loader.ts +673 -0
  386. package/src/usage/attribution.ts +28 -0
  387. package/src/util/device-id.ts +17 -3
  388. package/src/util/platform.ts +16 -0
  389. package/tsconfig.plugin-api.json +13 -0
  390. package/src/__tests__/plugin-external-api.test.ts +0 -68
  391. package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
  392. package/src/daemon/message-types/browser.ts +0 -10
  393. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  394. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
  395. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
  396. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
  397. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
  398. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
  399. package/src/plugins/external-api.ts +0 -114
  400. package/src/plugins/plugin-skill-contributions.ts +0 -292
@@ -5,6 +5,7 @@ import {
5
5
  type CdpResult,
6
6
  createCdpInspectBackend,
7
7
  createExtensionBackend,
8
+ createHostBridgeBackend,
8
9
  createLocalBackend,
9
10
  } from "../../../browser-session/index.js";
10
11
  import { getConfig } from "../../../config/loader.js";
@@ -15,13 +16,14 @@ import { getPinnedTab } from "../pinned-tabs.js";
15
16
  import { createCdpInspectClient } from "./cdp-inspect-client.js";
16
17
  import { CdpError } from "./errors.js";
17
18
  import { createExtensionCdpClient } from "./extension-cdp-client.js";
19
+ import { createHostBridgeCdpClient } from "./host-bridge-cdp-client.js";
18
20
  import { createLocalCdpClient } from "./local-cdp-client.js";
19
21
  import type {
20
22
  AttemptDiagnostic,
21
23
  BackendCandidate,
22
- BrowserMode,
23
24
  CdpClient,
24
25
  CdpClientKind,
26
+ InternalBrowserMode,
25
27
  ScopedCdpClient,
26
28
  TabInfo,
27
29
  } from "./types.js";
@@ -52,7 +54,7 @@ let _desktopAutoCooldownSince = 0;
52
54
 
53
55
  /**
54
56
  * Record a cooldown after a desktop-auto cdp-inspect transport failure.
55
- * Called by {@link maybeRecordDesktopAutoCooldown} in production; also
57
+ * Called by {@link maybeRecordCandidateCooldown} in production; also
56
58
  * exported directly for use in tests.
57
59
  */
58
60
  export function recordDesktopAutoCooldown(): void {
@@ -83,6 +85,85 @@ export function _getDesktopAutoCooldownSince(): number {
83
85
  return _desktopAutoCooldownSince;
84
86
  }
85
87
 
88
+ // ---------------------------------------------------------------------------
89
+ // Host-bridge cooldown tracker
90
+ // ---------------------------------------------------------------------------
91
+
92
+ /**
93
+ * Per-actor timestamps (epoch ms) of the last transport-level failure
94
+ * for a host-bridge attempt. Mirrors the desktop-auto cdp-inspect
95
+ * cooldown above — the failure root cause is the same (the user's
96
+ * Chrome is not exposing a remote-debugging port), just probed through
97
+ * the desktop SSE bridge instead of directly. While the configured
98
+ * `desktopAuto.cooldownMs` window has not elapsed, the factory skips
99
+ * the host-bridge candidate so every browser op doesn't pay an SSE
100
+ * round-trip for a guaranteed failure.
101
+ *
102
+ * **Per-actor scope**: unlike the desktop-auto cooldown (one local
103
+ * machine, so process-global is correct), the bridge reaches a
104
+ * different desktop machine per actor on a multi-actor cloud daemon —
105
+ * one actor's missing debug port must not suppress another actor's
106
+ * only route to their own Chrome. Callers without a resolved actor
107
+ * share the `__default__` slot.
108
+ */
109
+ const _hostBridgeCooldownSince = new Map<string, number>();
110
+
111
+ /** Entries older than this are swept on record to bound map growth. */
112
+ const HOST_BRIDGE_COOLDOWN_SWEEP_MS = 60 * 60 * 1000;
113
+
114
+ function hostBridgeCooldownKey(sourceActorPrincipalId?: string): string {
115
+ return sourceActorPrincipalId ?? "__default__";
116
+ }
117
+
118
+ /** Record a cooldown after a host-bridge transport failure for this actor. */
119
+ export function recordHostBridgeCooldown(
120
+ sourceActorPrincipalId?: string,
121
+ ): void {
122
+ const now = Date.now();
123
+ for (const [key, since] of _hostBridgeCooldownSince) {
124
+ if (now - since >= HOST_BRIDGE_COOLDOWN_SWEEP_MS) {
125
+ _hostBridgeCooldownSince.delete(key);
126
+ }
127
+ }
128
+ _hostBridgeCooldownSince.set(
129
+ hostBridgeCooldownKey(sourceActorPrincipalId),
130
+ now,
131
+ );
132
+ }
133
+
134
+ /**
135
+ * Whether the host-bridge cooldown is currently active for this actor.
136
+ * Returns `true` if a failure was recorded and the configured cooldown
137
+ * window has not yet elapsed.
138
+ */
139
+ export function isHostBridgeCooldownActive(
140
+ cooldownMs: number,
141
+ sourceActorPrincipalId?: string,
142
+ ): boolean {
143
+ if (cooldownMs <= 0) return false;
144
+ const since = _hostBridgeCooldownSince.get(
145
+ hostBridgeCooldownKey(sourceActorPrincipalId),
146
+ );
147
+ if (since === undefined) return false;
148
+ return Date.now() - since < cooldownMs;
149
+ }
150
+
151
+ /** Reset the host-bridge cooldown state. Exported for testing only. */
152
+ export function _resetHostBridgeCooldown(): void {
153
+ _hostBridgeCooldownSince.clear();
154
+ }
155
+
156
+ /** Get the raw cooldown-since timestamp for an actor. Exported for testing only. */
157
+ export function _getHostBridgeCooldownSince(
158
+ sourceActorPrincipalId?: string,
159
+ ): number {
160
+ return (
161
+ _hostBridgeCooldownSince.get(
162
+ hostBridgeCooldownKey(sourceActorPrincipalId),
163
+ ) ?? 0
164
+ );
165
+ }
166
+
86
167
  // ---------------------------------------------------------------------------
87
168
  // Public API
88
169
  // ---------------------------------------------------------------------------
@@ -96,9 +177,10 @@ export interface GetCdpClientOptions {
96
177
  * Backend mode preference. When omitted or `"auto"`, the factory
97
178
  * uses the existing priority-ordered fallback chain. When set to a
98
179
  * specific backend kind, the factory pins to that single backend
99
- * and disables failover.
180
+ * and disables failover. `"host-bridge"` is internal-only (sticky
181
+ * conversation memo), never user-requestable.
100
182
  */
101
- mode?: BrowserMode;
183
+ mode?: InternalBrowserMode;
102
184
  /**
103
185
  * Explicit target client id. When provided, the extension backend routes
104
186
  * to this specific client instead of auto-resolving to the most-recently-
@@ -159,7 +241,7 @@ export function getCdpClient(
159
241
  context: ToolContext,
160
242
  options?: GetCdpClientOptions,
161
243
  ): ScopedCdpClient {
162
- const mode: BrowserMode = options?.mode ?? "auto";
244
+ const mode: InternalBrowserMode = options?.mode ?? "auto";
163
245
  const targetClientId = options?.targetClientId;
164
246
  const candidates =
165
247
  mode === "auto"
@@ -191,7 +273,7 @@ export function getCdpClient(
191
273
  */
192
274
  export function buildPinnedCandidateList(
193
275
  context: ToolContext,
194
- mode: Exclude<BrowserMode, "auto">,
276
+ mode: Exclude<InternalBrowserMode, "auto">,
195
277
  targetClientId?: string,
196
278
  ): BackendCandidate[] {
197
279
  const { conversationId, sourceActorPrincipalId } = context;
@@ -199,7 +281,7 @@ export function buildPinnedCandidateList(
199
281
  switch (mode) {
200
282
  case "extension": {
201
283
  const hostBrowserProxy = HostBrowserProxy.instance;
202
- if (!hostBrowserProxy.hasExtensionClient()) {
284
+ if (!hostBrowserProxy.hasExtensionClient(sourceActorPrincipalId)) {
203
285
  throw new CdpError(
204
286
  "transport_error",
205
287
  `Pinned mode "extension" unavailable: no Chrome Extension connected`,
@@ -267,6 +349,48 @@ export function buildPinnedCandidateList(
267
349
  },
268
350
  ];
269
351
  }
352
+ case "host-bridge": {
353
+ // Reached only via the conversation's sticky-backend memo after a
354
+ // successful bridge send — never via --browser-mode. Throwing when
355
+ // an extension has since connected (not just when the bridge is
356
+ // gone) deliberately trips the caller's sticky-fallback retry,
357
+ // which clears the memo and re-runs auto mode so the conversation
358
+ // upgrades to the extension candidate with correct kind labeling.
359
+ const hostBrowserProxy = HostBrowserProxy.instance;
360
+ if (
361
+ !hostBrowserProxy.isAvailable(sourceActorPrincipalId) ||
362
+ hostBrowserProxy.hasExtensionClient(sourceActorPrincipalId)
363
+ ) {
364
+ const errorMessage = hostBrowserProxy.hasExtensionClient(
365
+ sourceActorPrincipalId,
366
+ )
367
+ ? "a Chrome Extension is now connected"
368
+ : "no host_browser bridge client connected";
369
+ throw new CdpError(
370
+ "transport_error",
371
+ `Pinned mode "host-bridge" unavailable: ${errorMessage}`,
372
+ {
373
+ attemptDiagnostics: [
374
+ {
375
+ candidateKind: "host-bridge",
376
+ inclusionReason: "pinned mode: host-bridge",
377
+ stage: "candidate_selection",
378
+ errorCode: "transport_error",
379
+ errorMessage,
380
+ },
381
+ ],
382
+ },
383
+ );
384
+ }
385
+ return [
386
+ makeHostBridgeCandidate(
387
+ hostBrowserProxy,
388
+ conversationId,
389
+ sourceActorPrincipalId,
390
+ "pinned mode: host-bridge (conversation sticky memo)",
391
+ ),
392
+ ];
393
+ }
270
394
  case "local": {
271
395
  return [
272
396
  {
@@ -315,7 +439,7 @@ export function buildCandidateList(context: ToolContext, targetClientId?: string
315
439
  // unavailable, fail loudly rather than silently routing to a different
316
440
  // browser.
317
441
  if (targetClientId != null) {
318
- if (!hostBrowserProxy.hasExtensionClient()) {
442
+ if (!hostBrowserProxy.hasExtensionClient(sourceActorPrincipalId)) {
319
443
  throw new CdpError(
320
444
  "transport_error",
321
445
  `Cannot reach target_client_id "${targetClientId}": no Chrome Extension connected`,
@@ -360,7 +484,11 @@ export function buildCandidateList(context: ToolContext, targetClientId?: string
360
484
  }
361
485
 
362
486
  // 1. Extension -- preferred when a Chrome Extension client is connected.
363
- if (hostBrowserProxy.hasExtensionClient()) {
487
+ // Availability checks are actor-scoped: on a multi-actor cloud daemon,
488
+ // another actor's extension must not select extension-labelled
489
+ // transports for this conversation (the proxy would refuse to
490
+ // dispatch to it anyway).
491
+ if (hostBrowserProxy.hasExtensionClient(sourceActorPrincipalId)) {
364
492
  candidates.push({
365
493
  kind: "extension",
366
494
  reason: "Chrome Extension connected via registry singleton",
@@ -385,6 +513,33 @@ export function buildCandidateList(context: ToolContext, targetClientId?: string
385
513
  return { client, backend };
386
514
  },
387
515
  });
516
+ } else if (hostBrowserProxy.isAvailable(sourceActorPrincipalId)) {
517
+ // 1b. Host bridge -- a host_browser-capable client (the desktop SSE
518
+ // bridge) is connected but no Chrome Extension. Raw CDP commands are
519
+ // proxied to the user's machine and executed against Chrome's local
520
+ // remote-debugging port. This is the only route to the user's own
521
+ // Chrome when the daemon runs remotely (cloud), where cdp-inspect
522
+ // would dial the daemon's localhost instead.
523
+ const { cooldownMs } = getConfig().hostBrowser.cdpInspect.desktopAuto;
524
+ if (isHostBridgeCooldownActive(cooldownMs, sourceActorPrincipalId)) {
525
+ log.debug(
526
+ {
527
+ conversationId,
528
+ cooldownMs,
529
+ cooldownSince: _getHostBridgeCooldownSince(sourceActorPrincipalId),
530
+ },
531
+ "CDP factory: host-bridge skipped (cooldown active)",
532
+ );
533
+ } else {
534
+ candidates.push(
535
+ makeHostBridgeCandidate(
536
+ hostBrowserProxy,
537
+ conversationId,
538
+ sourceActorPrincipalId,
539
+ "host_browser bridge connected (no Chrome Extension); raw CDP via desktop SSE bridge",
540
+ ),
541
+ );
542
+ }
388
543
  } else {
389
544
  log.debug(
390
545
  { conversationId },
@@ -473,6 +628,42 @@ export function buildCandidateList(context: ToolContext, targetClientId?: string
473
628
  return candidates;
474
629
  }
475
630
 
631
+ /**
632
+ * Build the host-bridge backend candidate. Shared between auto mode
633
+ * (no extension connected, bridge available) and the pinned path that
634
+ * serves the conversation's sticky-backend memo.
635
+ *
636
+ * Unlike the extension candidate, no pinned tab is passed: extension
637
+ * pins store Chrome tab ids, which never match the CDP target ids the
638
+ * bridge resolves from /json/list.
639
+ */
640
+ function makeHostBridgeCandidate(
641
+ hostBrowserProxy: HostBrowserProxy,
642
+ conversationId: string,
643
+ sourceActorPrincipalId: string | undefined,
644
+ reason: string,
645
+ ): BackendCandidate {
646
+ return {
647
+ kind: "host-bridge",
648
+ reason,
649
+ sourceActorPrincipalId,
650
+ create() {
651
+ const client = createHostBridgeCdpClient(
652
+ hostBrowserProxy,
653
+ conversationId,
654
+ sourceActorPrincipalId,
655
+ );
656
+ const backend = createHostBridgeBackend({
657
+ isAvailable: () => true,
658
+ sendCdp: (command, signal) =>
659
+ dispatchThroughClient(client, command, signal),
660
+ dispose: () => client.dispose(),
661
+ });
662
+ return { client, backend };
663
+ },
664
+ };
665
+ }
666
+
476
667
  // ---------------------------------------------------------------------------
477
668
  // Chained client with per-invocation failover
478
669
  // ---------------------------------------------------------------------------
@@ -487,7 +678,7 @@ export function buildCandidateList(context: ToolContext, targetClientId?: string
487
678
  export function buildChainedClient(
488
679
  conversationId: string,
489
680
  candidates: BackendCandidate[],
490
- mode: BrowserMode = "auto",
681
+ mode: InternalBrowserMode = "auto",
491
682
  ): ScopedCdpClient {
492
683
  if (candidates.length === 0) {
493
684
  throw new Error("CDP factory: no backend candidates available");
@@ -749,7 +940,7 @@ async function sendWithFailover<T>(
749
940
  }) => void,
750
941
  isDisposed: () => boolean,
751
942
  conversationId: string,
752
- mode: BrowserMode,
943
+ mode: InternalBrowserMode,
753
944
  ): Promise<T> {
754
945
  let lastError: CdpError | undefined;
755
946
  const diagnostics: AttemptDiagnostic[] = [];
@@ -804,7 +995,7 @@ async function sendWithFailover<T>(
804
995
  errorCode: "transport_error",
805
996
  errorMessage,
806
997
  });
807
- maybeRecordDesktopAutoCooldown(candidate);
998
+ maybeRecordCandidateCooldown(candidate);
808
999
 
809
1000
  // Emit production-visible fallback log in auto mode
810
1001
  if (mode === "auto" && i < candidates.length - 1) {
@@ -856,7 +1047,7 @@ async function sendWithFailover<T>(
856
1047
  errorMessage,
857
1048
  discoveryCode: extractDiscoveryCode(err),
858
1049
  });
859
- maybeRecordDesktopAutoCooldown(candidate);
1050
+ maybeRecordCandidateCooldown(candidate);
860
1051
 
861
1052
  // Emit production-visible fallback log in auto mode
862
1053
  if (mode === "auto" && i < candidates.length - 1) {
@@ -903,7 +1094,7 @@ async function sendWithFailover<T>(
903
1094
  errorMessage: cdpError.message,
904
1095
  discoveryCode: extractDiscoveryCode(cdpError.underlying),
905
1096
  });
906
- maybeRecordDesktopAutoCooldown(candidate);
1097
+ maybeRecordCandidateCooldown(candidate);
907
1098
 
908
1099
  // Emit production-visible fallback log in auto mode
909
1100
  if (mode === "auto") {
@@ -998,10 +1189,19 @@ async function sendWithFailover<T>(
998
1189
  }
999
1190
 
1000
1191
  /**
1001
- * If the failed candidate is a desktop-auto cdp-inspect attempt,
1002
- * record the cooldown so subsequent calls skip the probe.
1192
+ * Record the appropriate cooldown for a failed candidate so subsequent
1193
+ * calls skip the probe: desktop-auto cdp-inspect attempts (matched by
1194
+ * reason prefix, exempting user-pinned cdp-inspect) and host-bridge
1195
+ * attempts (matched by kind alone — the bridge is never user-pinned).
1003
1196
  */
1004
- function maybeRecordDesktopAutoCooldown(candidate: BackendCandidate): void {
1197
+ function maybeRecordCandidateCooldown(candidate: BackendCandidate): void {
1198
+ if (candidate.kind === "host-bridge") {
1199
+ log.debug(
1200
+ "CDP factory: recording host-bridge cooldown after transport failure",
1201
+ );
1202
+ recordHostBridgeCooldown(candidate.sourceActorPrincipalId);
1203
+ return;
1204
+ }
1005
1205
  if (
1006
1206
  candidate.kind === "cdp-inspect" &&
1007
1207
  candidate.reason.startsWith("desktopAuto:")
@@ -0,0 +1,67 @@
1
+ import type { HostBrowserProxy } from "../../../daemon/host-browser-proxy.js";
2
+ import { CdpError } from "./errors.js";
3
+ import { ExtensionCdpClient } from "./extension-cdp-client.js";
4
+ import type { CdpClientKind } from "./types.js";
5
+
6
+ /**
7
+ * CdpClient for the desktop host-browser bridge: raw CDP commands are
8
+ * routed through HostBrowserProxy to the desktop client's SSE bridge,
9
+ * which executes them against the user's Chrome via its local
10
+ * remote-debugging port. Reuses ExtensionCdpClient's request/reply and
11
+ * error-classification plumbing with three deliberate differences:
12
+ *
13
+ * - `kind` is `"host-bridge"`, so transport-aware tool branches
14
+ * (e.g. `--new-tab`'s `Vellum.createTab` path) correctly treat the
15
+ * bridge as a non-extension backend.
16
+ * - No pinned-tab `cdpSessionId` is ever injected: extension pins
17
+ * store Chrome tab ids, which never match the CDP target ids the
18
+ * bridge resolves from `/json/list` and would guarantee
19
+ * `cdp_session_not_found` failures.
20
+ * - Tab methods throw locally instead of sending `Vellum.*`
21
+ * pseudo-methods the bridge cannot serve (matches the
22
+ * local/cdp-inspect client convention).
23
+ */
24
+ export class HostBridgeCdpClient extends ExtensionCdpClient {
25
+ override readonly kind: CdpClientKind = "host-bridge";
26
+
27
+ constructor(
28
+ proxy: HostBrowserProxy,
29
+ conversationId: string,
30
+ sourceActorPrincipalId?: string,
31
+ ) {
32
+ super(
33
+ proxy,
34
+ conversationId,
35
+ /* cdpSessionId */ undefined,
36
+ sourceActorPrincipalId,
37
+ /* targetClientId */ undefined,
38
+ );
39
+ }
40
+
41
+ override async listTabs(): Promise<never> {
42
+ throw unsupportedTabOperation("listTabs");
43
+ }
44
+
45
+ override async selectTab(_tabId: number): Promise<never> {
46
+ throw unsupportedTabOperation("selectTab");
47
+ }
48
+
49
+ override async closeTab(_tabId: number): Promise<never> {
50
+ throw unsupportedTabOperation("closeTab");
51
+ }
52
+ }
53
+
54
+ function unsupportedTabOperation(operation: string): CdpError {
55
+ return new CdpError(
56
+ "transport_error",
57
+ `${operation} is not supported by the host-bridge backend (extension backend required)`,
58
+ );
59
+ }
60
+
61
+ export function createHostBridgeCdpClient(
62
+ proxy: HostBrowserProxy,
63
+ conversationId: string,
64
+ sourceActorPrincipalId?: string,
65
+ ): HostBridgeCdpClient {
66
+ return new HostBridgeCdpClient(proxy, conversationId, sourceActorPrincipalId);
67
+ }
@@ -98,22 +98,36 @@ export interface CdpClient {
98
98
  * the sacrificial-profile screencast setup when running against the
99
99
  * user's own Chrome via the extension).
100
100
  */
101
- export type CdpClientKind = "local" | "extension" | "cdp-inspect";
101
+ export type CdpClientKind = "local" | "extension" | "cdp-inspect" | "host-bridge";
102
102
 
103
103
  /**
104
104
  * Backend mode preference for the CDP factory. Controls which
105
105
  * transport is selected:
106
106
  *
107
107
  * - `"auto"` — default, existing priority-ordered fallback
108
- * (extension → cdp-inspect → local).
108
+ * (extension → host-bridge → cdp-inspect → local).
109
109
  * - `"extension"` — pin to the chrome-extension backend. Fails
110
110
  * immediately if the host browser proxy is unavailable.
111
111
  * - `"cdp-inspect"` — pin to the cdp-inspect backend. Fails
112
112
  * immediately if cdp-inspect cannot connect.
113
113
  * - `"local"` — pin to the local Playwright backend. No fallback.
114
+ *
115
+ * The `host-bridge` backend (raw CDP to the user's Chrome via the
116
+ * desktop client's SSE bridge) is auto-mode only and not listed here —
117
+ * see {@link InternalBrowserMode}.
114
118
  */
115
119
  export type BrowserMode = "auto" | "extension" | "cdp-inspect" | "local";
116
120
 
121
+ /**
122
+ * {@link BrowserMode} plus internal-only kinds the factory can pin to.
123
+ * The conversation's sticky-backend memo records the last successful
124
+ * {@link CdpClientKind} and re-pins to it on the next tool call; after
125
+ * a successful host-bridge send that memo is `"host-bridge"`, which is
126
+ * never user-requestable via `--browser-mode` but must round-trip
127
+ * through the factory's pinned-candidate path.
128
+ */
129
+ export type InternalBrowserMode = BrowserMode | "host-bridge";
130
+
117
131
  /**
118
132
  * Stage at which a candidate attempt ended. Used in
119
133
  * {@link AttemptDiagnostic} to indicate how far the attempt progressed.
@@ -212,6 +226,12 @@ export interface BackendCandidate {
212
226
  readonly kind: CdpClientKind;
213
227
  /** Human-readable reason this candidate was included. */
214
228
  readonly reason: string;
229
+ /**
230
+ * Actor the candidate was built for. Set on host-bridge candidates so
231
+ * a transport failure records the cooldown for the owning actor only
232
+ * (the bridge reaches a different desktop machine per actor).
233
+ */
234
+ readonly sourceActorPrincipalId?: string;
215
235
  /**
216
236
  * Materialise the backend. Called at most once — the factory caches
217
237
  * the result after the first successful CDP command so subsequent
@@ -16,6 +16,7 @@ import {
16
16
  } from "@vellumai/service-contracts/credential-rpc";
17
17
 
18
18
  import { RiskLevel } from "../../permissions/types.js";
19
+ import { getCesClient } from "../../security/secure-keys.js";
19
20
  import { getLogger } from "../../util/logger.js";
20
21
  import type {
21
22
  ToolContext,
@@ -77,7 +78,7 @@ export const makeAuthenticatedRequestTool = {
77
78
  input: Record<string, unknown>,
78
79
  context: ToolContext,
79
80
  ): Promise<ToolExecutionResult> {
80
- const cesClient = context.cesClient;
81
+ const cesClient = getCesClient();
81
82
  if (!cesClient) {
82
83
  return {
83
84
  content: