@vellumai/assistant 0.4.46 → 0.4.49

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 (382) hide show
  1. package/ARCHITECTURE.md +7 -7
  2. package/README.md +2 -23
  3. package/docs/architecture/integrations.md +45 -41
  4. package/docs/architecture/keychain-broker.md +3 -3
  5. package/docs/architecture/security.md +5 -5
  6. package/docs/runbook-trusted-contacts.md +3 -8
  7. package/hook-templates/debug-prompt-logger/hook.json +1 -1
  8. package/hook-templates/debug-prompt-logger/run.sh +1 -3
  9. package/package.json +1 -1
  10. package/src/__tests__/actor-token-service.test.ts +0 -1
  11. package/src/__tests__/anthropic-provider.test.ts +156 -0
  12. package/src/__tests__/approval-cascade.test.ts +810 -0
  13. package/src/__tests__/approval-primitive.test.ts +0 -1
  14. package/src/__tests__/approval-routes-http.test.ts +2 -0
  15. package/src/__tests__/assistant-attachments.test.ts +12 -34
  16. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
  17. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
  18. package/src/__tests__/browser-fill-credential.test.ts +5 -2
  19. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  20. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
  21. package/src/__tests__/channel-guardian.test.ts +0 -2
  22. package/src/__tests__/channel-readiness-routes.test.ts +35 -25
  23. package/src/__tests__/channel-readiness-service.test.ts +10 -9
  24. package/src/__tests__/checker.test.ts +9 -29
  25. package/src/__tests__/cli.test.ts +23 -0
  26. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  27. package/src/__tests__/computer-use-tools.test.ts +2 -19
  28. package/src/__tests__/config-watcher.test.ts +0 -1
  29. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  30. package/src/__tests__/context-image-dimensions.test.ts +332 -0
  31. package/src/__tests__/context-token-estimator.test.ts +196 -13
  32. package/src/__tests__/conversation-attention-store.test.ts +0 -1
  33. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  34. package/src/__tests__/conversation-routes-guardian-reply.test.ts +144 -0
  35. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  36. package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
  37. package/src/__tests__/credential-broker-server-use.test.ts +22 -21
  38. package/src/__tests__/credential-broker.test.ts +2 -1
  39. package/src/__tests__/credential-metadata-store.test.ts +239 -26
  40. package/src/__tests__/credential-resolve.test.ts +5 -4
  41. package/src/__tests__/credential-security-e2e.test.ts +8 -8
  42. package/src/__tests__/credential-security-invariants.test.ts +111 -7
  43. package/src/__tests__/credential-vault-unit.test.ts +287 -54
  44. package/src/__tests__/credential-vault.test.ts +406 -12
  45. package/src/__tests__/credentials-cli.test.ts +82 -6
  46. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  47. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  48. package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
  49. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  50. package/src/__tests__/gemini-image-service.test.ts +75 -45
  51. package/src/__tests__/gemini-provider.test.ts +9 -6
  52. package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
  53. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
  54. package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
  55. package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
  56. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
  57. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
  58. package/src/__tests__/guardian-grant-minting.test.ts +35 -0
  59. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  60. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  61. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
  62. package/src/__tests__/heartbeat-service.test.ts +0 -1
  63. package/src/__tests__/host-cu-proxy.test.ts +629 -0
  64. package/src/__tests__/host-shell-tool.test.ts +27 -15
  65. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  66. package/src/__tests__/ingress-url-consistency.test.ts +14 -21
  67. package/src/__tests__/integration-status.test.ts +38 -25
  68. package/src/__tests__/intent-routing.test.ts +0 -1
  69. package/src/__tests__/invite-routes-http.test.ts +10 -9
  70. package/src/__tests__/keychain-broker-client.test.ts +11 -43
  71. package/src/__tests__/managed-proxy-context.test.ts +5 -3
  72. package/src/__tests__/media-generate-image.test.ts +63 -2
  73. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
  74. package/src/__tests__/messaging-send-tool.test.ts +4 -6
  75. package/src/__tests__/notification-routing-intent.test.ts +0 -1
  76. package/src/__tests__/oauth-cli.test.ts +373 -14
  77. package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
  78. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  79. package/src/__tests__/oauth-store.test.ts +756 -0
  80. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
  81. package/src/__tests__/provider-error-scenarios.test.ts +0 -1
  82. package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
  83. package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
  84. package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
  85. package/src/__tests__/public-ingress-urls.test.ts +15 -21
  86. package/src/__tests__/recording-handler.test.ts +3 -4
  87. package/src/__tests__/registry.test.ts +2 -2
  88. package/src/__tests__/runtime-events-sse.test.ts +55 -7
  89. package/src/__tests__/schedule-store.test.ts +0 -1
  90. package/src/__tests__/scheduler-recurrence.test.ts +0 -1
  91. package/src/__tests__/schema-transforms.test.ts +226 -0
  92. package/src/__tests__/scoped-approval-grants.test.ts +0 -1
  93. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
  94. package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
  95. package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
  96. package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
  97. package/src/__tests__/secret-ingress-handler.test.ts +0 -1
  98. package/src/__tests__/secret-onetime-send.test.ts +5 -3
  99. package/src/__tests__/send-endpoint-busy.test.ts +21 -6
  100. package/src/__tests__/sequence-store.test.ts +0 -1
  101. package/src/__tests__/session-init.benchmark.test.ts +4 -5
  102. package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
  103. package/src/__tests__/skill-include-graph.test.ts +66 -0
  104. package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
  105. package/src/__tests__/skill-load-tool.test.ts +149 -1
  106. package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
  107. package/src/__tests__/skills-uninstall.test.ts +3 -3
  108. package/src/__tests__/skills.test.ts +3 -12
  109. package/src/__tests__/slack-channel-config.test.ts +76 -11
  110. package/src/__tests__/slack-share-routes.test.ts +17 -14
  111. package/src/__tests__/system-prompt.test.ts +0 -1
  112. package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
  113. package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
  114. package/src/__tests__/terminal-tools.test.ts +4 -3
  115. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  116. package/src/__tests__/tool-approval-handler.test.ts +0 -1
  117. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
  118. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  119. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
  120. package/src/__tests__/tool-executor.test.ts +0 -1
  121. package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
  122. package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
  123. package/src/__tests__/trust-store.test.ts +1 -22
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  126. package/src/__tests__/twilio-config.test.ts +2 -1
  127. package/src/__tests__/twilio-provider.test.ts +4 -2
  128. package/src/__tests__/twilio-routes.test.ts +5 -20
  129. package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
  130. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
  131. package/src/agent/ax-tree-compaction.test.ts +235 -0
  132. package/src/agent/loop.ts +76 -130
  133. package/src/calls/call-domain.ts +8 -10
  134. package/src/calls/relay-server.ts +9 -13
  135. package/src/calls/twilio-config.ts +4 -8
  136. package/src/calls/twilio-provider.ts +2 -1
  137. package/src/calls/twilio-rest.ts +2 -1
  138. package/src/calls/twilio-routes.ts +1 -2
  139. package/src/calls/voice-ingress-preflight.ts +1 -1
  140. package/src/cli/commands/browser-relay.ts +46 -15
  141. package/src/cli/commands/completions.ts +0 -3
  142. package/src/cli/commands/credentials.ts +110 -23
  143. package/src/cli/commands/oauth/apps.ts +255 -0
  144. package/src/cli/commands/oauth/connections.ts +299 -0
  145. package/src/cli/commands/oauth/index.ts +52 -0
  146. package/src/cli/commands/oauth/providers.ts +242 -0
  147. package/src/cli/commands/skills.ts +4 -338
  148. package/src/cli/program.ts +1 -5
  149. package/src/cli/reference.ts +1 -3
  150. package/src/cli.ts +3 -2
  151. package/src/config/assistant-feature-flags.ts +0 -3
  152. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  153. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
  154. package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
  155. package/src/config/bundled-skills/computer-use/TOOLS.json +22 -4
  156. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
  157. package/src/config/bundled-skills/gmail/SKILL.md +4 -4
  158. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
  159. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
  160. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
  161. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
  162. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
  163. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
  164. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
  165. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
  166. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
  167. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
  168. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
  169. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
  170. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
  171. package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
  172. package/src/config/bundled-skills/google-calendar/calendar-client.ts +90 -44
  173. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
  174. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
  175. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
  176. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
  177. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
  178. package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
  179. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
  180. package/src/config/bundled-skills/messaging/SKILL.md +6 -6
  181. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
  182. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
  183. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
  184. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
  185. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
  186. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
  187. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
  188. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
  189. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
  190. package/src/config/bundled-skills/messaging/tools/shared.ts +12 -15
  191. package/src/config/bundled-skills/settings/SKILL.md +1 -1
  192. package/src/config/bundled-skills/settings/TOOLS.json +2 -8
  193. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
  194. package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
  195. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
  196. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
  197. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
  198. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
  199. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
  200. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
  201. package/src/config/env-registry.ts +14 -83
  202. package/src/config/env.ts +11 -50
  203. package/src/config/feature-flag-registry.json +16 -16
  204. package/src/config/schema.ts +3 -1
  205. package/src/config/skills.ts +21 -2
  206. package/src/context/image-dimensions.ts +229 -0
  207. package/src/context/token-estimator.ts +75 -12
  208. package/src/context/window-manager.ts +49 -10
  209. package/src/daemon/assistant-attachments.ts +1 -13
  210. package/src/daemon/guardian-action-generators.ts +4 -5
  211. package/src/daemon/handlers/config-ingress.ts +8 -33
  212. package/src/daemon/handlers/config-slack-channel.ts +76 -56
  213. package/src/daemon/handlers/config-telegram.ts +53 -24
  214. package/src/daemon/handlers/sessions.ts +10 -24
  215. package/src/daemon/handlers/shared.ts +0 -130
  216. package/src/daemon/host-cu-proxy.ts +401 -0
  217. package/src/daemon/lifecycle.ts +39 -63
  218. package/src/daemon/message-protocol.ts +3 -0
  219. package/src/daemon/message-types/computer-use.ts +2 -119
  220. package/src/daemon/message-types/host-cu.ts +19 -0
  221. package/src/daemon/message-types/integrations.ts +1 -0
  222. package/src/daemon/message-types/messages.ts +3 -0
  223. package/src/daemon/server.ts +14 -21
  224. package/src/daemon/session-agent-loop-handlers.ts +2 -0
  225. package/src/daemon/session-attachments.ts +1 -2
  226. package/src/daemon/session-messaging.ts +3 -1
  227. package/src/daemon/session-slash.ts +1 -1
  228. package/src/daemon/session-surfaces.ts +40 -28
  229. package/src/daemon/session-tool-setup.ts +20 -11
  230. package/src/daemon/session.ts +139 -16
  231. package/src/daemon/tool-side-effects.ts +2 -8
  232. package/src/daemon/watch-handler.ts +2 -2
  233. package/src/email/providers/index.ts +2 -1
  234. package/src/events/tool-metrics-listener.ts +2 -2
  235. package/src/hooks/manager.ts +1 -4
  236. package/src/inbound/public-ingress-urls.ts +7 -7
  237. package/src/instrument.ts +15 -1
  238. package/src/logfire.ts +16 -5
  239. package/src/media/app-icon-generator.ts +30 -4
  240. package/src/media/avatar-router.ts +26 -3
  241. package/src/media/gemini-image-service.ts +28 -2
  242. package/src/memory/conversation-key-store.ts +21 -0
  243. package/src/memory/db-init.ts +4 -0
  244. package/src/memory/guardian-action-store.ts +1 -1
  245. package/src/memory/migrations/149-oauth-tables.ts +60 -0
  246. package/src/memory/migrations/index.ts +1 -0
  247. package/src/memory/schema/guardian.ts +1 -1
  248. package/src/memory/schema/index.ts +1 -0
  249. package/src/memory/schema/oauth.ts +65 -0
  250. package/src/messaging/provider.ts +19 -13
  251. package/src/messaging/providers/gmail/adapter.ts +40 -23
  252. package/src/messaging/providers/gmail/client.ts +283 -122
  253. package/src/messaging/providers/gmail/people-client.ts +32 -24
  254. package/src/messaging/providers/slack/adapter.ts +29 -19
  255. package/src/messaging/providers/slack/client.ts +265 -78
  256. package/src/messaging/providers/telegram-bot/adapter.ts +19 -18
  257. package/src/messaging/providers/whatsapp/adapter.ts +17 -11
  258. package/src/messaging/registry.ts +2 -31
  259. package/src/notifications/copy-composer.ts +0 -5
  260. package/src/notifications/signal.ts +4 -5
  261. package/src/oauth/byo-connection.test.ts +537 -0
  262. package/src/oauth/byo-connection.ts +128 -0
  263. package/src/oauth/connect-orchestrator.ts +139 -56
  264. package/src/oauth/connect-types.ts +17 -23
  265. package/src/oauth/connection-resolver.ts +58 -0
  266. package/src/oauth/connection.ts +38 -0
  267. package/src/oauth/manual-token-connection.ts +104 -0
  268. package/src/oauth/oauth-store.ts +496 -0
  269. package/src/oauth/platform-connection.test.ts +192 -0
  270. package/src/oauth/platform-connection.ts +111 -0
  271. package/src/oauth/provider-behaviors.ts +124 -0
  272. package/src/oauth/scope-policy.ts +9 -2
  273. package/src/oauth/seed-providers.ts +161 -0
  274. package/src/oauth/token-persistence.ts +74 -78
  275. package/src/permissions/checker.ts +8 -4
  276. package/src/permissions/defaults.ts +0 -1
  277. package/src/permissions/prompter.ts +10 -1
  278. package/src/permissions/trust-store.ts +13 -0
  279. package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
  280. package/src/prompts/system-prompt.ts +70 -45
  281. package/src/providers/anthropic/client.ts +133 -24
  282. package/src/providers/gemini/client.ts +15 -6
  283. package/src/providers/managed-proxy/constants.ts +2 -2
  284. package/src/providers/managed-proxy/context.ts +5 -1
  285. package/src/providers/ratelimit.ts +17 -0
  286. package/src/providers/registry.ts +2 -2
  287. package/src/providers/retry.ts +1 -27
  288. package/src/runtime/AGENTS.md +17 -0
  289. package/src/runtime/auth/route-policy.ts +0 -3
  290. package/src/runtime/channel-invite-transports/telegram.ts +2 -1
  291. package/src/runtime/channel-readiness-service.ts +168 -195
  292. package/src/runtime/channel-readiness-types.ts +4 -0
  293. package/src/runtime/channel-reply-delivery.ts +0 -40
  294. package/src/runtime/gateway-client.ts +0 -7
  295. package/src/runtime/guardian-action-conversation-turn.ts +1 -3
  296. package/src/runtime/guardian-action-followup-executor.ts +1 -1
  297. package/src/runtime/guardian-action-message-composer.ts +3 -23
  298. package/src/runtime/http-server.ts +17 -10
  299. package/src/runtime/http-types.ts +2 -3
  300. package/src/runtime/middleware/rate-limiter.ts +74 -20
  301. package/src/runtime/middleware/twilio-validation.ts +1 -11
  302. package/src/runtime/pending-interactions.ts +14 -12
  303. package/src/runtime/routes/channel-delivery-routes.ts +0 -1
  304. package/src/runtime/routes/channel-readiness-routes.ts +2 -0
  305. package/src/runtime/routes/conversation-routes.ts +73 -19
  306. package/src/runtime/routes/diagnostics-routes.ts +11 -9
  307. package/src/runtime/routes/events-routes.ts +21 -11
  308. package/src/runtime/routes/guardian-approval-interception.ts +20 -5
  309. package/src/runtime/routes/host-cu-routes.ts +97 -0
  310. package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
  311. package/src/runtime/routes/integrations/slack/share.ts +6 -6
  312. package/src/runtime/routes/integrations/twilio.ts +6 -5
  313. package/src/runtime/routes/log-export-routes.ts +126 -8
  314. package/src/runtime/routes/secret-routes.ts +3 -2
  315. package/src/runtime/routes/settings-routes.ts +113 -48
  316. package/src/runtime/routes/surface-action-routes.ts +1 -1
  317. package/src/runtime/routes/watch-routes.ts +128 -0
  318. package/src/schedule/integration-status.ts +10 -8
  319. package/src/security/credential-key.ts +14 -0
  320. package/src/security/keychain-broker-client.ts +5 -6
  321. package/src/security/oauth2.ts +1 -1
  322. package/src/security/token-manager.ts +145 -43
  323. package/src/skills/catalog-install.ts +358 -0
  324. package/src/skills/include-graph.ts +32 -0
  325. package/src/telegram/bot-username.ts +2 -3
  326. package/src/tools/apps/definitions.ts +0 -5
  327. package/src/tools/assets/materialize.ts +0 -5
  328. package/src/tools/assets/search.ts +0 -5
  329. package/src/tools/browser/headless-browser.ts +1 -67
  330. package/src/tools/browser/network-recorder.ts +1 -1
  331. package/src/tools/browser/network-recording-types.ts +1 -1
  332. package/src/tools/claude-code/claude-code.ts +0 -5
  333. package/src/tools/computer-use/definitions.ts +46 -11
  334. package/src/tools/computer-use/registry.ts +4 -5
  335. package/src/tools/credentials/broker.ts +5 -4
  336. package/src/tools/credentials/metadata-store.ts +22 -74
  337. package/src/tools/credentials/resolve.ts +2 -1
  338. package/src/tools/credentials/vault.ts +139 -151
  339. package/src/tools/filesystem/edit.ts +1 -6
  340. package/src/tools/filesystem/read.ts +0 -5
  341. package/src/tools/filesystem/write.ts +1 -6
  342. package/src/tools/host-filesystem/edit.ts +1 -6
  343. package/src/tools/host-filesystem/read.ts +1 -6
  344. package/src/tools/host-filesystem/write.ts +1 -6
  345. package/src/tools/mcp/mcp-tool-factory.ts +18 -1
  346. package/src/tools/memory/definitions.ts +0 -5
  347. package/src/tools/network/web-fetch.ts +0 -5
  348. package/src/tools/network/web-search.ts +0 -5
  349. package/src/tools/registry.ts +2 -7
  350. package/src/tools/schema-transforms.ts +99 -0
  351. package/src/tools/skills/load.ts +62 -8
  352. package/src/tools/swarm/delegate.ts +0 -5
  353. package/src/tools/system/avatar-generator.ts +0 -5
  354. package/src/tools/ui-surface/definitions.ts +0 -15
  355. package/src/tools/watch/screen-watch.ts +0 -5
  356. package/src/tools/watch/watch-state.ts +0 -12
  357. package/src/util/logger.ts +7 -41
  358. package/src/util/platform.ts +9 -28
  359. package/src/version.ts +10 -0
  360. package/src/watcher/providers/github.ts +51 -52
  361. package/src/watcher/providers/gmail.ts +88 -80
  362. package/src/watcher/providers/google-calendar.ts +94 -86
  363. package/src/watcher/providers/linear.ts +87 -93
  364. package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
  365. package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
  366. package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
  367. package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
  368. package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
  369. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
  370. package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
  371. package/src/cli/commands/dev.ts +0 -129
  372. package/src/cli/commands/map.ts +0 -391
  373. package/src/cli/commands/oauth.ts +0 -77
  374. package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +0 -16
  375. package/src/daemon/computer-use-session.ts +0 -1020
  376. package/src/daemon/ride-shotgun-handler.ts +0 -567
  377. package/src/oauth/provider-profiles.ts +0 -192
  378. package/src/prompts/computer-use-prompt.ts +0 -98
  379. package/src/runtime/routes/computer-use-routes.ts +0 -641
  380. package/src/runtime/telegram-streaming-delivery.test.ts +0 -597
  381. package/src/runtime/telegram-streaming-delivery.ts +0 -383
  382. package/src/tools/computer-use/request-computer-control.ts +0 -61
@@ -1,249 +0,0 @@
1
- import { beforeAll, describe, expect, mock, test } from "bun:test";
2
-
3
- // Mock config before importing modules that depend on it.
4
- mock.module("../config/loader.js", () => ({
5
- getConfig: () => ({
6
- ui: {},
7
-
8
- provider: "mock-provider",
9
- permissions: { mode: "workspace" },
10
- apiKeys: {},
11
- sandbox: { enabled: false },
12
- timeouts: { toolExecutionTimeoutSec: 30, permissionTimeoutSec: 5 },
13
- skills: { load: { extraDirs: [] } },
14
- secretDetection: { enabled: false },
15
- contextWindow: {
16
- enabled: true,
17
- maxInputTokens: 180000,
18
- targetBudgetRatio: 0.30,
19
- compactThreshold: 0.8, summaryBudgetRatio: 0.05,
20
- },
21
- }),
22
- invalidateConfigCache: () => {},
23
- }));
24
-
25
- import { ComputerUseSession } from "../daemon/computer-use-session.js";
26
- import type { CuObservation } from "../daemon/message-protocol.js";
27
- import type { Provider, ProviderResponse } from "../providers/types.js";
28
- import {
29
- __resetRegistryForTesting,
30
- getAllTools,
31
- getSkillRefCount,
32
- initializeTools,
33
- } from "../tools/registry.js";
34
-
35
- function createProvider(responses: ProviderResponse[]): Provider {
36
- let calls = 0;
37
- return {
38
- name: "mock",
39
- async sendMessage() {
40
- const response = responses[calls] ?? responses[responses.length - 1];
41
- calls++;
42
- return response;
43
- },
44
- };
45
- }
46
-
47
- const doneResponse: ProviderResponse = {
48
- content: [
49
- {
50
- type: "tool_use",
51
- id: "tu-cleanup",
52
- name: "computer_use_done",
53
- input: { summary: "Done" },
54
- },
55
- ],
56
- model: "mock-model",
57
- usage: { inputTokens: 10, outputTokens: 5 },
58
- stopReason: "tool_use",
59
- };
60
-
61
- const observation: CuObservation = {
62
- type: "cu_observation",
63
- sessionId: "cleanup-test",
64
- axTree: 'Window "Test" [1]',
65
- };
66
-
67
- describe("CU session skill tool lifecycle cleanup", () => {
68
- beforeAll(async () => {
69
- __resetRegistryForTesting();
70
- await initializeTools();
71
- });
72
-
73
- test("computer-use skill refcount is 0 after session completes via computer_use_done", async () => {
74
- const provider = createProvider([doneResponse]);
75
- const session = new ComputerUseSession(
76
- "cleanup-done",
77
- "test cleanup",
78
- 1440,
79
- 900,
80
- provider,
81
- () => {},
82
- "computer_use",
83
- );
84
-
85
- expect(getSkillRefCount("computer-use")).toBe(0);
86
-
87
- await session.handleObservation({
88
- ...observation,
89
- sessionId: "cleanup-done",
90
- });
91
-
92
- expect(session.getState()).toBe("complete");
93
- expect(getSkillRefCount("computer-use")).toBe(0);
94
- });
95
-
96
- test("computer-use skill refcount is 0 after session is aborted", async () => {
97
- // Use a provider that hangs until the abort signal fires, keeping the
98
- // session active long enough to abort after skill projection has occurred.
99
- const hangingProvider: Provider = {
100
- name: "mock",
101
- sendMessage: (_msgs, _tools, _sys, opts) =>
102
- new Promise<ProviderResponse>((_, reject) => {
103
- if (opts?.signal?.aborted) {
104
- reject(new DOMException("Aborted", "AbortError"));
105
- return;
106
- }
107
- opts?.signal?.addEventListener(
108
- "abort",
109
- () => reject(new DOMException("Aborted", "AbortError")),
110
- { once: true },
111
- );
112
- }),
113
- };
114
-
115
- const session = new ComputerUseSession(
116
- "cleanup-abort",
117
- "test abort cleanup",
118
- 1440,
119
- 900,
120
- hangingProvider,
121
- () => {},
122
- "computer_use",
123
- );
124
-
125
- expect(getSkillRefCount("computer-use")).toBe(0);
126
-
127
- // Start the session (don't await — it will hang on the provider call).
128
- // Skill projection happens synchronously at the start of runAgentLoop,
129
- // so by the time sendMessage is called the refcount has been incremented.
130
- const sessionPromise = session.handleObservation({
131
- ...observation,
132
- sessionId: "cleanup-abort",
133
- });
134
-
135
- // Yield to let runAgentLoop start and reach the provider call
136
- await new Promise((r) => setTimeout(r, 50));
137
-
138
- session.abort();
139
-
140
- // Let the session finish its cleanup
141
- await sessionPromise;
142
-
143
- expect(session.getState()).toBe("error");
144
- expect(getSkillRefCount("computer-use")).toBe(0);
145
- });
146
-
147
- test("computer-use skill refcount is 0 after session completes via computer_use_respond", async () => {
148
- const provider = createProvider([
149
- {
150
- content: [
151
- {
152
- type: "tool_use",
153
- id: "tu-respond-cleanup",
154
- name: "computer_use_respond",
155
- input: { answer: "Test answer", reasoning: "Test reasoning" },
156
- },
157
- ],
158
- model: "mock-model",
159
- usage: { inputTokens: 10, outputTokens: 5 },
160
- stopReason: "tool_use",
161
- },
162
- ]);
163
-
164
- const session = new ComputerUseSession(
165
- "cleanup-respond",
166
- "test respond cleanup",
167
- 1440,
168
- 900,
169
- provider,
170
- () => {},
171
- "computer_use",
172
- );
173
-
174
- await session.handleObservation({
175
- ...observation,
176
- sessionId: "cleanup-respond",
177
- });
178
-
179
- expect(session.getState()).toBe("complete");
180
- expect(getSkillRefCount("computer-use")).toBe(0);
181
- });
182
-
183
- test("only escalation tool remains in registry after session cleanup", async () => {
184
- const provider = createProvider([doneResponse]);
185
- const session = new ComputerUseSession(
186
- "cleanup-registry-check",
187
- "test registry cleanup",
188
- 1440,
189
- 900,
190
- provider,
191
- () => {},
192
- "computer_use",
193
- );
194
-
195
- await session.handleObservation({
196
- ...observation,
197
- sessionId: "cleanup-registry-check",
198
- });
199
-
200
- expect(session.getState()).toBe("complete");
201
-
202
- const allTools = getAllTools();
203
- const cuTools = allTools.filter((t) => t.name.startsWith("computer_use_"));
204
- expect(cuTools).toHaveLength(1);
205
- expect(cuTools[0].name).toBe("computer_use_request_control");
206
- });
207
-
208
- test("multiple sequential CU sessions do not leak refcounts", async () => {
209
- for (let i = 0; i < 3; i++) {
210
- const provider = createProvider([doneResponse]);
211
- const session = new ComputerUseSession(
212
- `cleanup-sequential-${i}`,
213
- "test sequential cleanup",
214
- 1440,
215
- 900,
216
- provider,
217
- () => {},
218
- "computer_use",
219
- );
220
-
221
- await session.handleObservation({
222
- ...observation,
223
- sessionId: `cleanup-sequential-${i}`,
224
- });
225
- expect(session.getState()).toBe("complete");
226
- }
227
-
228
- expect(getSkillRefCount("computer-use")).toBe(0);
229
-
230
- const allTools = getAllTools();
231
- const cuTools = allTools.filter((t) => t.name.startsWith("computer_use_"));
232
- expect(cuTools).toHaveLength(1);
233
- expect(cuTools[0].name).toBe("computer_use_request_control");
234
- });
235
-
236
- // Cross-suite regression: after CU sessions complete, core registry invariants hold
237
- test("core registry has 1 computer_use_* tool after CU session lifecycle (escalation only)", () => {
238
- const allTools = getAllTools();
239
- const cuTools = allTools.filter((t) => t.name.startsWith("computer_use_"));
240
- expect(cuTools).toHaveLength(1);
241
- expect(cuTools[0].name).toBe("computer_use_request_control");
242
- });
243
-
244
- test("computer_use_request_control is in core registry after CU session lifecycle", async () => {
245
- const { getTool } = await import("../tools/registry.js");
246
- const tool = getTool("computer_use_request_control");
247
- expect(tool).toBeDefined();
248
- });
249
- });
@@ -1,452 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- import type { HandlerContext } from "../daemon/handlers/shared.js";
4
-
5
- // ── Mocks ──────────────────────────────────────────────────────────
6
-
7
- let mockEnsureChromeResult = {
8
- baseUrl: "http://localhost:9222",
9
- launchedByUs: false,
10
- userDataDir: "/tmp/cdp-test",
11
- };
12
- let mockEnsureChromeShouldThrow = false;
13
- let mockMinimizeCalled = false;
14
- let mockMinimizeBaseUrl: string | undefined;
15
-
16
- mock.module("../tools/browser/chrome-cdp.js", () => ({
17
- ensureChromeWithCdp: async () => {
18
- if (mockEnsureChromeShouldThrow) {
19
- throw new Error("Chrome launch failed");
20
- }
21
- return { ...mockEnsureChromeResult };
22
- },
23
- minimizeChromeWindow: async (baseUrl: string) => {
24
- mockMinimizeCalled = true;
25
- mockMinimizeBaseUrl = baseUrl;
26
- },
27
- isCdpReady: async () => true,
28
- restoreChromeWindow: async () => {},
29
- }));
30
-
31
- let mockRecorderStartCalls = 0;
32
- let mockRecorderStartShouldThrow = false;
33
- let mockRecorderStartThrowCount = 0;
34
- let mockRecorderConstructorCdpBaseUrl: string | undefined;
35
-
36
- mock.module("../tools/browser/network-recorder.js", () => ({
37
- NetworkRecorder: class MockNetworkRecorder {
38
- loginSignals: string[] = [];
39
- onLoginDetected?: () => void;
40
- get entryCount() {
41
- return 0;
42
- }
43
-
44
- constructor(_targetDomain?: string, cdpBaseUrl?: string) {
45
- mockRecorderConstructorCdpBaseUrl = cdpBaseUrl;
46
- }
47
-
48
- async startDirect() {
49
- mockRecorderStartCalls++;
50
- if (
51
- mockRecorderStartShouldThrow &&
52
- mockRecorderStartCalls <= mockRecorderStartThrowCount
53
- ) {
54
- throw new Error("CDP not ready");
55
- }
56
- }
57
-
58
- async stop() {
59
- return [];
60
- }
61
-
62
- async extractCookies() {
63
- return [];
64
- }
65
- },
66
- }));
67
-
68
- mock.module("../tools/browser/recording-store.js", () => ({
69
- saveRecording: () => "/tmp/test-recording.json",
70
- }));
71
-
72
- mock.module("../tools/browser/auto-navigate.js", () => ({
73
- autoNavigate: async () => [],
74
- }));
75
-
76
- mock.module("../util/logger.js", () => ({
77
- getLogger: () => ({
78
- info: () => {},
79
- debug: () => {},
80
- warn: () => {},
81
- error: () => {},
82
- }),
83
- }));
84
-
85
- const mockLastSummaryBySession = new Map<string, string>();
86
- mock.module("../daemon/watch-handler.js", () => ({
87
- generateSummary: async () => {},
88
- lastSummaryBySession: mockLastSummaryBySession,
89
- }));
90
-
91
- // ── Import under test (after mocks) ───────────────────────────────
92
-
93
- const { handleRideShotgunStart, handleRideShotgunStop } =
94
- await import("../daemon/ride-shotgun-handler.js");
95
- const { watchSessions } = await import("../tools/watch/watch-state.js");
96
-
97
- // ── Helpers ────────────────────────────────────────────────────────
98
-
99
- function makeMockCtx() {
100
- const sent: unknown[] = [];
101
- return {
102
- send: (msg: unknown) => sent.push(msg),
103
- sent,
104
- } as unknown as HandlerContext & { sent: unknown[] };
105
- }
106
-
107
- function waitForRecorderStart(timeoutMs = 3000): Promise<void> {
108
- return new Promise((resolve, reject) => {
109
- const start = Date.now();
110
- const poll = setInterval(() => {
111
- if (mockRecorderStartCalls > 0) {
112
- clearInterval(poll);
113
- resolve();
114
- } else if (Date.now() - start > timeoutMs) {
115
- clearInterval(poll);
116
- reject(new Error("Timed out waiting for recorder start"));
117
- }
118
- }, 50);
119
- });
120
- }
121
-
122
- // ── Tests ──────────────────────────────────────────────────────────
123
-
124
- describe("ride-shotgun-handler", () => {
125
- beforeEach(() => {
126
- mockRecorderStartCalls = 0;
127
- mockRecorderStartShouldThrow = false;
128
- mockRecorderStartThrowCount = 0;
129
- mockRecorderConstructorCdpBaseUrl = undefined;
130
- mockMinimizeCalled = false;
131
- mockMinimizeBaseUrl = undefined;
132
- mockEnsureChromeShouldThrow = false;
133
- mockEnsureChromeResult = {
134
- baseUrl: "http://localhost:9222",
135
- launchedByUs: false,
136
- userDataDir: "/tmp/cdp-test",
137
- };
138
- mockLastSummaryBySession.clear();
139
- watchSessions.clear();
140
- });
141
-
142
- afterEach(() => {
143
- // Clean up any dangling sessions
144
- for (const [, session] of watchSessions) {
145
- if (session.timeoutHandle) clearTimeout(session.timeoutHandle);
146
- }
147
- watchSessions.clear();
148
- });
149
-
150
- test("learn mode calls ensureChromeWithCdp before starting recorder", async () => {
151
- const ctx = makeMockCtx();
152
-
153
- await handleRideShotgunStart(
154
- {
155
- type: "ride_shotgun_start",
156
- durationSeconds: 60,
157
- intervalSeconds: 5,
158
- mode: "learn",
159
- targetDomain: "example.com",
160
- autoNavigate: false,
161
- },
162
- ctx,
163
- );
164
-
165
- // Background recording start — wait for it
166
- await waitForRecorderStart();
167
-
168
- expect(mockRecorderStartCalls).toBe(1);
169
- // The recorder should receive the CDP base URL from the session
170
- expect(mockRecorderConstructorCdpBaseUrl).toBe("http://localhost:9222");
171
- });
172
-
173
- test("learn mode passes CDP base URL to NetworkRecorder constructor", async () => {
174
- mockEnsureChromeResult = {
175
- baseUrl: "http://localhost:9333",
176
- launchedByUs: true,
177
- userDataDir: "/tmp/cdp-custom",
178
- };
179
-
180
- const ctx = makeMockCtx();
181
-
182
- await handleRideShotgunStart(
183
- {
184
- type: "ride_shotgun_start",
185
- durationSeconds: 60,
186
- intervalSeconds: 5,
187
- mode: "learn",
188
- targetDomain: "example.com",
189
- autoNavigate: false,
190
- },
191
- ctx,
192
- );
193
-
194
- await waitForRecorderStart();
195
-
196
- expect(mockRecorderConstructorCdpBaseUrl).toBe("http://localhost:9333");
197
- });
198
-
199
- test("learn mode does not start recorder when ensureChromeWithCdp fails", async () => {
200
- mockEnsureChromeShouldThrow = true;
201
-
202
- const ctx = makeMockCtx();
203
-
204
- await handleRideShotgunStart(
205
- {
206
- type: "ride_shotgun_start",
207
- durationSeconds: 60,
208
- intervalSeconds: 5,
209
- mode: "learn",
210
- targetDomain: "example.com",
211
- autoNavigate: false,
212
- },
213
- ctx,
214
- );
215
-
216
- // Give background task time to execute
217
- await new Promise((r) => setTimeout(r, 200));
218
-
219
- expect(mockRecorderStartCalls).toBe(0);
220
- });
221
-
222
- test("learn mode minimizes Chrome on completion when assistant launched it", async () => {
223
- mockEnsureChromeResult = {
224
- baseUrl: "http://localhost:9222",
225
- launchedByUs: true,
226
- userDataDir: "/tmp/cdp-test",
227
- };
228
-
229
- const ctx = makeMockCtx();
230
-
231
- await handleRideShotgunStart(
232
- {
233
- type: "ride_shotgun_start",
234
- durationSeconds: 60,
235
- intervalSeconds: 5,
236
- mode: "learn",
237
- targetDomain: "example.com",
238
- autoNavigate: false,
239
- },
240
- ctx,
241
- );
242
-
243
- await waitForRecorderStart();
244
-
245
- // Find the session and stop it
246
- const watchId = [...watchSessions.keys()][0]!;
247
- await handleRideShotgunStop({ type: "ride_shotgun_stop", watchId }, ctx);
248
-
249
- expect(mockMinimizeCalled).toBe(true);
250
- expect(mockMinimizeBaseUrl).toBe("http://localhost:9222");
251
- });
252
-
253
- test("learn mode does not minimize Chrome on completion when user launched it", async () => {
254
- mockEnsureChromeResult = {
255
- baseUrl: "http://localhost:9222",
256
- launchedByUs: false,
257
- userDataDir: "/tmp/cdp-test",
258
- };
259
-
260
- const ctx = makeMockCtx();
261
-
262
- await handleRideShotgunStart(
263
- {
264
- type: "ride_shotgun_start",
265
- durationSeconds: 60,
266
- intervalSeconds: 5,
267
- mode: "learn",
268
- targetDomain: "example.com",
269
- autoNavigate: false,
270
- },
271
- ctx,
272
- );
273
-
274
- await waitForRecorderStart();
275
-
276
- // Find the session and stop it
277
- const watchId = [...watchSessions.keys()][0]!;
278
- await handleRideShotgunStop({ type: "ride_shotgun_stop", watchId }, ctx);
279
-
280
- expect(mockMinimizeCalled).toBe(false);
281
- });
282
-
283
- test("observe mode does not call ensureChromeWithCdp", async () => {
284
- const ctx = makeMockCtx();
285
-
286
- await handleRideShotgunStart(
287
- {
288
- type: "ride_shotgun_start",
289
- durationSeconds: 60,
290
- intervalSeconds: 5,
291
- mode: "observe",
292
- },
293
- ctx,
294
- );
295
-
296
- // Give time for any background tasks
297
- await new Promise((r) => setTimeout(r, 200));
298
-
299
- // In observe mode, no recorder should be started
300
- expect(mockRecorderStartCalls).toBe(0);
301
-
302
- // Clean up
303
- const watchId = [...watchSessions.keys()][0]!;
304
- await handleRideShotgunStop({ type: "ride_shotgun_stop", watchId }, ctx);
305
- });
306
-
307
- test("sends watch_started message with session IDs", async () => {
308
- const ctx = makeMockCtx();
309
-
310
- await handleRideShotgunStart(
311
- {
312
- type: "ride_shotgun_start",
313
- durationSeconds: 30,
314
- intervalSeconds: 5,
315
- mode: "learn",
316
- targetDomain: "example.com",
317
- autoNavigate: false,
318
- },
319
- ctx,
320
- );
321
-
322
- const startMsg = ctx.sent.find(
323
- (m: any) => m.type === "watch_started",
324
- ) as any;
325
- expect(startMsg).toBeDefined();
326
- expect(startMsg.sessionId).toBeDefined();
327
- expect(startMsg.watchId).toBeDefined();
328
- expect(startMsg.durationSeconds).toBe(30);
329
-
330
- // Clean up
331
- const watchId = startMsg.watchId;
332
- await handleRideShotgunStop({ type: "ride_shotgun_stop", watchId }, ctx);
333
- });
334
-
335
- test("sends ride_shotgun_error when ensureChromeWithCdp fails", async () => {
336
- mockEnsureChromeShouldThrow = true;
337
-
338
- const ctx = makeMockCtx();
339
-
340
- await handleRideShotgunStart(
341
- {
342
- type: "ride_shotgun_start",
343
- durationSeconds: 60,
344
- intervalSeconds: 5,
345
- mode: "learn",
346
- targetDomain: "example.com",
347
- autoNavigate: false,
348
- },
349
- ctx,
350
- );
351
-
352
- // Give background task time to execute and complete session
353
- await new Promise((r) => setTimeout(r, 500));
354
-
355
- const errorMsg = ctx.sent.find(
356
- (m: any) => m.type === "ride_shotgun_error",
357
- ) as any;
358
- expect(errorMsg).toBeDefined();
359
- expect(errorMsg.watchId).toBeDefined();
360
- expect(errorMsg.sessionId).toBeDefined();
361
- expect(errorMsg.message).toContain("Chrome CDP");
362
- });
363
-
364
- test("cleans up session when ensureChromeWithCdp fails", async () => {
365
- mockEnsureChromeShouldThrow = true;
366
-
367
- const ctx = makeMockCtx();
368
-
369
- await handleRideShotgunStart(
370
- {
371
- type: "ride_shotgun_start",
372
- durationSeconds: 60,
373
- intervalSeconds: 5,
374
- mode: "learn",
375
- targetDomain: "example.com",
376
- autoNavigate: false,
377
- },
378
- ctx,
379
- );
380
-
381
- // Give background task time to execute
382
- await new Promise((r) => setTimeout(r, 500));
383
-
384
- // Session should be completed (not left hanging for the full duration)
385
- const session = [...watchSessions.values()][0];
386
- expect(session?.status).toBe("completed");
387
- });
388
-
389
- test("reports failure summary when no recorder ever started", async () => {
390
- mockEnsureChromeShouldThrow = true;
391
-
392
- const ctx = makeMockCtx();
393
-
394
- await handleRideShotgunStart(
395
- {
396
- type: "ride_shotgun_start",
397
- durationSeconds: 60,
398
- intervalSeconds: 5,
399
- mode: "learn",
400
- targetDomain: "example.com",
401
- autoNavigate: false,
402
- },
403
- ctx,
404
- );
405
-
406
- // Give background task time to execute
407
- await new Promise((r) => setTimeout(r, 500));
408
-
409
- // The result message should indicate the specific CDP failure
410
- const resultMsg = ctx.sent.find(
411
- (m: any) => m.type === "ride_shotgun_result",
412
- ) as any;
413
- expect(resultMsg).toBeDefined();
414
- expect(resultMsg.summary).toContain("failed");
415
- expect(resultMsg.summary).toContain("browser could not be started");
416
- expect(resultMsg.summary).not.toContain("recording saved");
417
- });
418
-
419
- test("sends ride_shotgun_error when all 10 recorder retries fail", async () => {
420
- mockRecorderStartShouldThrow = true;
421
- mockRecorderStartThrowCount = 10;
422
-
423
- const ctx = makeMockCtx();
424
-
425
- await handleRideShotgunStart(
426
- {
427
- type: "ride_shotgun_start",
428
- durationSeconds: 60,
429
- intervalSeconds: 5,
430
- mode: "learn",
431
- targetDomain: "example.com",
432
- autoNavigate: false,
433
- },
434
- ctx,
435
- );
436
-
437
- // Wait for all 10 retry attempts (each has a 2s delay except the last)
438
- // 9 retries * 2s = 18s, but mock doesn't actually wait — it should complete quickly
439
- // The mock delays are real setTimeout calls, so we need enough time
440
- await new Promise((r) => setTimeout(r, 25000));
441
-
442
- const errorMsg = ctx.sent.find(
443
- (m: any) => m.type === "ride_shotgun_error",
444
- ) as any;
445
- expect(errorMsg).toBeDefined();
446
- expect(errorMsg.message).toContain("10 attempts");
447
-
448
- // Session should be completed
449
- const session = [...watchSessions.values()][0];
450
- expect(session?.status).toBe("completed");
451
- }, 30000); // Extended timeout for retry delays
452
- });