@vellumai/assistant 0.4.56 → 0.4.57

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 (450) hide show
  1. package/ARCHITECTURE.md +10 -10
  2. package/Dockerfile +3 -0
  3. package/README.md +11 -11
  4. package/docs/architecture/integrations.md +2 -2
  5. package/docs/architecture/memory.md +3 -4
  6. package/docs/credential-execution-service.md +13 -20
  7. package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
  8. package/package.json +1 -1
  9. package/src/__tests__/actor-token-service.test.ts +7 -7
  10. package/src/__tests__/anthropic-provider.test.ts +172 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
  12. package/src/__tests__/approval-cascade.test.ts +2 -2
  13. package/src/__tests__/approval-routes-http.test.ts +3 -4
  14. package/src/__tests__/asset-materialize-tool.test.ts +5 -5
  15. package/src/__tests__/asset-search-tool.test.ts +1 -1
  16. package/src/__tests__/assistant-attachments.test.ts +5 -5
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
  19. package/src/__tests__/attachments-store.test.ts +2 -2
  20. package/src/__tests__/avatar-e2e.test.ts +5 -3
  21. package/src/__tests__/browser-skill-endstate.test.ts +0 -1
  22. package/src/__tests__/call-routes-http.test.ts +2 -2
  23. package/src/__tests__/callback-handoff-copy.test.ts +1 -1
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
  25. package/src/__tests__/channel-readiness-routes.test.ts +0 -1
  26. package/src/__tests__/channel-readiness-service.test.ts +0 -1
  27. package/src/__tests__/checker.test.ts +31 -32
  28. package/src/__tests__/chrome-cdp.test.ts +47 -18
  29. package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
  30. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  31. package/src/__tests__/config-schema.test.ts +9 -18
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
  34. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
  35. package/src/__tests__/conversation-agent-loop.test.ts +11 -4
  36. package/src/__tests__/conversation-attachments.test.ts +1 -1
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
  38. package/src/__tests__/conversation-error.test.ts +33 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
  40. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  41. package/src/__tests__/conversation-pairing.test.ts +1 -1
  42. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
  44. package/src/__tests__/conversation-queue.test.ts +23 -14
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +185 -173
  47. package/src/__tests__/conversation-seed-composer.test.ts +1 -1
  48. package/src/__tests__/conversation-slash-queue.test.ts +4 -4
  49. package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
  50. package/src/__tests__/conversation-starter-routes.test.ts +291 -0
  51. package/src/__tests__/conversation-wipe.test.ts +438 -0
  52. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
  53. package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
  54. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
  55. package/src/__tests__/credential-security-e2e.test.ts +20 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault-unit.test.ts +227 -0
  58. package/src/__tests__/credentials-cli.test.ts +3 -0
  59. package/src/__tests__/date-context.test.ts +59 -377
  60. package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
  61. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
  62. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
  63. package/src/__tests__/encrypted-store.test.ts +237 -15
  64. package/src/__tests__/ephemeral-permissions.test.ts +4 -5
  65. package/src/__tests__/event-bus.test.ts +3 -3
  66. package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
  67. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  68. package/src/__tests__/gemini-image-service.test.ts +4 -4
  69. package/src/__tests__/gemini-provider.test.ts +6 -9
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
  71. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  72. package/src/__tests__/host-shell-tool.test.ts +6 -6
  73. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  74. package/src/__tests__/intent-routing.test.ts +51 -99
  75. package/src/__tests__/invite-routes-http.test.ts +5 -0
  76. package/src/__tests__/list-messages-attachments.test.ts +1 -1
  77. package/src/__tests__/managed-proxy-context.test.ts +2 -5
  78. package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
  79. package/src/__tests__/media-generate-image.test.ts +32 -15
  80. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  81. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
  82. package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
  83. package/src/__tests__/memory-recall-quality.test.ts +4 -3
  84. package/src/__tests__/memory-regressions.test.ts +86 -90
  85. package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
  86. package/src/__tests__/migration-export-http.test.ts +26 -27
  87. package/src/__tests__/migration-import-commit-http.test.ts +165 -37
  88. package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
  89. package/src/__tests__/migration-validate-http.test.ts +16 -16
  90. package/src/__tests__/model-intents.test.ts +1 -1
  91. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
  92. package/src/__tests__/notification-broadcaster.test.ts +1 -1
  93. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  94. package/src/__tests__/notification-decision-identity.test.ts +8 -9
  95. package/src/__tests__/notification-decision-strategy.test.ts +1 -1
  96. package/src/__tests__/notification-deep-link.test.ts +1 -1
  97. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  98. package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
  99. package/src/__tests__/oauth-store.test.ts +1 -3
  100. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
  101. package/src/__tests__/onboarding-template-contract.test.ts +23 -59
  102. package/src/__tests__/provider-error-scenarios.test.ts +154 -0
  103. package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
  104. package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
  105. package/src/__tests__/provider-registry-ollama.test.ts +5 -2
  106. package/src/__tests__/qdrant-manager.test.ts +7 -7
  107. package/src/__tests__/ratelimit.test.ts +0 -74
  108. package/src/__tests__/recording-handler.test.ts +0 -1
  109. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  110. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  111. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  112. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  113. package/src/__tests__/scheduler-recurrence.test.ts +46 -2
  114. package/src/__tests__/schema-transforms.test.ts +114 -54
  115. package/src/__tests__/secret-onetime-send.test.ts +20 -0
  116. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
  117. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  118. package/src/__tests__/send-endpoint-busy.test.ts +63 -4
  119. package/src/__tests__/send-notification-tool.test.ts +2 -2
  120. package/src/__tests__/shell-credential-ref.test.ts +0 -1
  121. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
  122. package/src/__tests__/skill-memory.test.ts +547 -0
  123. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
  124. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
  125. package/src/__tests__/slack-channel-config.test.ts +109 -94
  126. package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
  127. package/src/__tests__/swarm-recursion.test.ts +2 -2
  128. package/src/__tests__/swarm-tool.test.ts +2 -2
  129. package/src/__tests__/system-prompt.test.ts +19 -66
  130. package/src/__tests__/telegram-config.test.ts +121 -0
  131. package/src/__tests__/terminal-tools.test.ts +1 -1
  132. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
  133. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  134. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  135. package/src/__tests__/tool-executor.test.ts +1 -1
  136. package/src/__tests__/trace-emitter.test.ts +8 -1
  137. package/src/__tests__/trust-store.test.ts +7 -8
  138. package/src/__tests__/twilio-routes.test.ts +1 -18
  139. package/src/__tests__/user-reference.test.ts +82 -2
  140. package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
  141. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  142. package/src/approvals/guardian-request-resolvers.ts +3 -3
  143. package/src/avatar/ascii-renderer.ts +2 -2
  144. package/src/avatar/png-renderer.ts +2 -2
  145. package/src/avatar/resvg-lazy.ts +21 -0
  146. package/src/calls/guardian-dispatch.ts +1 -1
  147. package/src/calls/relay-access-wait.ts +2 -2
  148. package/src/calls/twilio-rest.ts +0 -248
  149. package/src/cli/AGENTS.md +5 -8
  150. package/src/cli/__tests__/notifications.test.ts +5 -5
  151. package/src/cli/commands/avatar.ts +64 -2
  152. package/src/cli/commands/conversations.ts +131 -1
  153. package/src/cli/commands/credentials.ts +2 -0
  154. package/src/cli/commands/notifications.ts +3 -3
  155. package/src/cli.ts +10 -0
  156. package/src/config/bundled-skills/acp/SKILL.md +5 -5
  157. package/src/config/bundled-skills/acp/TOOLS.json +6 -6
  158. package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
  159. package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
  160. package/src/config/bundled-skills/browser/SKILL.md +15 -15
  161. package/src/config/bundled-skills/browser/TOOLS.json +14 -14
  162. package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
  163. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
  164. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  165. package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
  166. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  167. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
  168. package/src/config/bundled-skills/contacts/SKILL.md +3 -3
  169. package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
  170. package/src/config/bundled-skills/document/SKILL.md +4 -4
  171. package/src/config/bundled-skills/document/TOOLS.json +2 -2
  172. package/src/config/bundled-skills/followups/TOOLS.json +3 -3
  173. package/src/config/bundled-skills/gmail/SKILL.md +32 -32
  174. package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
  175. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
  176. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  177. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  178. package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
  179. package/src/config/bundled-skills/google-calendar/types.ts +1 -1
  180. package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
  181. package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
  182. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
  183. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
  184. package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
  185. package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
  186. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
  187. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
  188. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
  189. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
  190. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
  191. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
  192. package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
  193. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  194. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
  195. package/src/config/bundled-skills/messaging/SKILL.md +29 -25
  196. package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
  197. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  198. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  199. package/src/config/bundled-skills/notifications/SKILL.md +3 -3
  200. package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
  201. package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
  202. package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
  203. package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
  204. package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
  205. package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
  206. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  207. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
  208. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
  209. package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
  210. package/src/config/bundled-skills/schedule/SKILL.md +26 -26
  211. package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
  212. package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
  213. package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
  214. package/src/config/bundled-skills/sequences/SKILL.md +2 -2
  215. package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
  216. package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
  217. package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
  218. package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
  219. package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
  220. package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
  221. package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
  222. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
  223. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  224. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  225. package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
  226. package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
  227. package/src/config/bundled-skills/slack/SKILL.md +2 -2
  228. package/src/config/bundled-skills/slack/TOOLS.json +8 -8
  229. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
  230. package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
  231. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  232. package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
  233. package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
  234. package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
  235. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
  236. package/src/config/bundled-skills/watcher/SKILL.md +4 -4
  237. package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
  238. package/src/config/feature-flag-registry.json +33 -17
  239. package/src/config/schemas/sandbox.ts +1 -1
  240. package/src/config/schemas/services.ts +13 -3
  241. package/src/config/schemas/timeouts.ts +0 -10
  242. package/src/contacts/contact-store.ts +63 -0
  243. package/src/contacts/contacts-write.ts +1 -1
  244. package/src/daemon/assistant-attachments.ts +2 -2
  245. package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
  246. package/src/daemon/conversation-agent-loop.ts +7 -30
  247. package/src/daemon/conversation-error.ts +24 -0
  248. package/src/daemon/conversation-memory.ts +8 -7
  249. package/src/daemon/conversation-runtime-assembly.ts +139 -274
  250. package/src/daemon/conversation-slash.ts +7 -26
  251. package/src/daemon/conversation-surfaces.ts +14 -0
  252. package/src/daemon/conversation-tool-setup.ts +9 -8
  253. package/src/daemon/conversation.ts +2 -0
  254. package/src/daemon/daemon-control.ts +1 -1
  255. package/src/daemon/date-context.ts +10 -83
  256. package/src/daemon/handlers/config-channels.ts +12 -2
  257. package/src/daemon/handlers/config-slack-channel.ts +7 -1
  258. package/src/daemon/handlers/config-telegram.ts +6 -1
  259. package/src/daemon/handlers/conversations.ts +2 -2
  260. package/src/daemon/handlers/skills.ts +4 -0
  261. package/src/daemon/lifecycle.ts +28 -4
  262. package/src/daemon/providers-setup.ts +1 -1
  263. package/src/daemon/server.ts +1 -5
  264. package/src/daemon/shutdown-handlers.ts +9 -3
  265. package/src/daemon/tool-side-effects.ts +40 -0
  266. package/src/daemon/trace-emitter.ts +25 -2
  267. package/src/events/domain-events.ts +1 -1
  268. package/src/events/tool-permission-telemetry-listener.ts +46 -0
  269. package/src/inbound/platform-callback-registration.ts +0 -18
  270. package/src/media/app-icon-generator.ts +15 -8
  271. package/src/media/avatar-router.ts +15 -8
  272. package/src/media/gemini-image-service.ts +125 -21
  273. package/src/memory/attachments-store.ts +3 -3
  274. package/src/memory/channel-verification-sessions.ts +6 -6
  275. package/src/memory/conversation-crud.ts +196 -1
  276. package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
  277. package/src/memory/conversation-title-service.ts +2 -3
  278. package/src/memory/db-init.ts +25 -1
  279. package/src/memory/invite-store.ts +4 -4
  280. package/src/memory/items-extractor.ts +4 -4
  281. package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
  282. package/src/memory/jobs-store.ts +3 -2
  283. package/src/memory/jobs-worker.ts +7 -5
  284. package/src/memory/lifecycle-events-store.ts +63 -0
  285. package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
  286. package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
  287. package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
  288. package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
  289. package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
  290. package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
  291. package/src/memory/migrations/index.ts +6 -0
  292. package/src/memory/migrations/registry.ts +13 -0
  293. package/src/memory/retriever.test.ts +223 -96
  294. package/src/memory/retriever.ts +115 -138
  295. package/src/memory/schema/calls.ts +1 -1
  296. package/src/memory/schema/contacts.ts +1 -1
  297. package/src/memory/schema/infrastructure.ts +29 -0
  298. package/src/memory/schema/memory-core.ts +7 -17
  299. package/src/memory/schema/notifications.ts +1 -1
  300. package/src/memory/search/formatting.ts +23 -6
  301. package/src/memory/search/lexical.ts +2 -0
  302. package/src/memory/search/semantic.ts +2 -0
  303. package/src/memory/search/staleness.ts +1 -0
  304. package/src/memory/search/types.ts +4 -0
  305. package/src/memory/task-memory-cleanup.ts +96 -6
  306. package/src/memory/trace-event-store.ts +148 -0
  307. package/src/notifications/README.md +1 -1
  308. package/src/notifications/decision-engine.ts +2 -2
  309. package/src/notifications/emit-signal.ts +4 -4
  310. package/src/notifications/events-store.ts +4 -4
  311. package/src/notifications/signal.ts +1 -1
  312. package/src/oauth/manual-token-connection.ts +49 -25
  313. package/src/permissions/checker.ts +6 -5
  314. package/src/permissions/defaults.ts +4 -4
  315. package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
  316. package/src/prompts/cache-boundary.ts +8 -0
  317. package/src/prompts/system-prompt.ts +105 -634
  318. package/src/prompts/templates/BOOTSTRAP.md +166 -33
  319. package/src/prompts/templates/IDENTITY.md +8 -23
  320. package/src/prompts/templates/SOUL.md +20 -41
  321. package/src/prompts/templates/USER.md +3 -19
  322. package/src/prompts/user-reference.ts +14 -16
  323. package/src/providers/anthropic/client.ts +46 -2
  324. package/src/providers/gemini/client.ts +6 -9
  325. package/src/providers/managed-proxy/constants.ts +1 -7
  326. package/src/providers/managed-proxy/context.ts +0 -1
  327. package/src/providers/model-intents.ts +5 -5
  328. package/src/providers/openai/client.ts +10 -1
  329. package/src/providers/openrouter/client.ts +1 -0
  330. package/src/providers/ratelimit.ts +0 -35
  331. package/src/providers/registry.ts +3 -5
  332. package/src/providers/retry.ts +18 -1
  333. package/src/runtime/access-request-helper.ts +1 -1
  334. package/src/runtime/auth/route-policy.ts +7 -0
  335. package/src/runtime/channel-verification-service.ts +1 -1
  336. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  337. package/src/runtime/guardian-vellum-migration.ts +63 -1
  338. package/src/runtime/http-server.ts +8 -4
  339. package/src/runtime/migrations/vbundle-builder.ts +212 -32
  340. package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
  341. package/src/runtime/migrations/vbundle-importer.ts +66 -1
  342. package/src/runtime/migrations/vbundle-validator.ts +17 -3
  343. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
  344. package/src/runtime/routes/attachment-routes.ts +2 -2
  345. package/src/runtime/routes/btw-routes.ts +9 -0
  346. package/src/runtime/routes/channel-verification-routes.ts +19 -2
  347. package/src/runtime/routes/conversation-management-routes.ts +55 -1
  348. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  349. package/src/runtime/routes/conversation-routes.ts +49 -5
  350. package/src/runtime/routes/conversation-starter-routes.ts +207 -0
  351. package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
  352. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
  353. package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
  354. package/src/runtime/routes/migration-routes.ts +25 -13
  355. package/src/runtime/routes/secret-routes.ts +18 -0
  356. package/src/runtime/routes/settings-routes.ts +8 -8
  357. package/src/runtime/routes/telemetry-routes.ts +53 -0
  358. package/src/runtime/routes/trace-event-routes.ts +62 -0
  359. package/src/runtime/tool-grant-request-helper.ts +1 -1
  360. package/src/runtime/verification-outbound-actions.ts +47 -31
  361. package/src/security/encrypted-store.ts +263 -78
  362. package/src/skills/catalog-install.ts +10 -0
  363. package/src/skills/managed-store.ts +2 -0
  364. package/src/skills/skill-memory.ts +220 -0
  365. package/src/subagent/manager.ts +1 -4
  366. package/src/telemetry/types.ts +10 -1
  367. package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
  368. package/src/telemetry/usage-telemetry-reporter.ts +51 -4
  369. package/src/tools/AGENTS.md +11 -11
  370. package/src/tools/acp/spawn.ts +1 -1
  371. package/src/tools/apps/executors.ts +8 -8
  372. package/src/tools/apps/registry.ts +1 -1
  373. package/src/tools/assets/materialize.ts +6 -6
  374. package/src/tools/assets/search.ts +10 -10
  375. package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
  376. package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
  377. package/src/tools/browser/auth-detector.ts +6 -6
  378. package/src/tools/browser/browser-execution.ts +13 -13
  379. package/src/tools/browser/browser-manager.ts +3 -3
  380. package/src/tools/browser/chrome-cdp.ts +5 -5
  381. package/src/tools/browser/jit-auth.ts +2 -2
  382. package/src/tools/browser/network-recorder.test.ts +2 -2
  383. package/src/tools/browser/network-recorder.ts +3 -3
  384. package/src/tools/browser/runtime-check.ts +3 -3
  385. package/src/tools/claude-code/claude-code.ts +2 -2
  386. package/src/tools/computer-use/definitions.ts +18 -18
  387. package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
  388. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
  389. package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
  390. package/src/tools/credentials/broker-types.ts +5 -5
  391. package/src/tools/credentials/broker.ts +15 -15
  392. package/src/tools/credentials/metadata-store.ts +2 -2
  393. package/src/tools/credentials/resolve.ts +1 -1
  394. package/src/tools/credentials/selection.ts +1 -1
  395. package/src/tools/credentials/tool-policy.ts +1 -1
  396. package/src/tools/credentials/vault.ts +115 -25
  397. package/src/tools/execution-target.ts +2 -2
  398. package/src/tools/executor.ts +7 -7
  399. package/src/tools/filesystem/edit.ts +2 -2
  400. package/src/tools/filesystem/read.ts +1 -1
  401. package/src/tools/filesystem/write.ts +1 -1
  402. package/src/tools/host-filesystem/edit.ts +2 -1
  403. package/src/tools/host-filesystem/read.ts +2 -1
  404. package/src/tools/host-filesystem/write.ts +1 -1
  405. package/src/tools/host-terminal/host-shell.ts +9 -8
  406. package/src/tools/mcp/mcp-tool-factory.ts +7 -6
  407. package/src/tools/memory/definitions.ts +6 -5
  408. package/src/tools/memory/handlers.test.ts +1 -1
  409. package/src/tools/network/__tests__/web-search.test.ts +3 -3
  410. package/src/tools/network/domain-normalize.ts +2 -2
  411. package/src/tools/network/script-proxy/session-manager.ts +10 -10
  412. package/src/tools/network/web-fetch.ts +1 -1
  413. package/src/tools/network/web-search.ts +3 -3
  414. package/src/tools/permission-checker.ts +8 -8
  415. package/src/tools/registry.ts +7 -7
  416. package/src/tools/schedule/list.ts +2 -2
  417. package/src/tools/schema-transforms.ts +31 -21
  418. package/src/tools/secret-detection-handler.ts +1 -1
  419. package/src/tools/sensitive-output-placeholders.ts +1 -1
  420. package/src/tools/shared/filesystem/edit-engine.ts +1 -1
  421. package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
  422. package/src/tools/shared/filesystem/image-read.ts +25 -5
  423. package/src/tools/shared/filesystem/path-policy.ts +2 -2
  424. package/src/tools/shared/shell-output.ts +1 -1
  425. package/src/tools/side-effects.ts +1 -1
  426. package/src/tools/skills/execute.ts +1 -1
  427. package/src/tools/skills/load.ts +3 -3
  428. package/src/tools/skills/sandbox-runner.ts +3 -3
  429. package/src/tools/subagent/read.ts +1 -1
  430. package/src/tools/subagent/spawn.ts +2 -2
  431. package/src/tools/swarm/delegate.ts +3 -3
  432. package/src/tools/system/request-permission.ts +5 -4
  433. package/src/tools/terminal/backends/native.ts +4 -4
  434. package/src/tools/terminal/parser.ts +6 -6
  435. package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
  436. package/src/tools/terminal/shell.ts +16 -16
  437. package/src/tools/tool-approval-handler.ts +21 -12
  438. package/src/tools/tool-manifest.ts +4 -4
  439. package/src/tools/types.ts +3 -3
  440. package/src/tools/ui-surface/definitions.ts +9 -37
  441. package/src/tools/watcher/list.ts +1 -1
  442. package/src/util/logger.ts +7 -2
  443. package/src/util/retry.ts +29 -1
  444. package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
  445. package/src/workspace/migrations/registry.ts +2 -0
  446. package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
  447. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
  448. package/src/cli/reference.ts +0 -38
  449. package/src/memory/job-handlers/capability-cards.ts +0 -420
  450. package/src/runtime/routes/thread-starter-routes.ts +0 -294
@@ -49,6 +49,11 @@ mock.module("../util/retry.js", () => {
49
49
  return status === 429 || status >= 500;
50
50
  }
51
51
 
52
+ const RETRYABLE_NETWORK_MESSAGE_PATTERNS = [
53
+ /socket.*closed unexpectedly/i,
54
+ /socket hang up/i,
55
+ ];
56
+
52
57
  function isRetryableNetworkError(error: unknown): boolean {
53
58
  if (!(error instanceof Error)) return false;
54
59
  const retryableCodes = new Set([
@@ -63,6 +68,18 @@ mock.module("../util/retry.js", () => {
63
68
  const causeCode = (error.cause as NodeJS.ErrnoException).code;
64
69
  if (causeCode && retryableCodes.has(causeCode)) return true;
65
70
  }
71
+ if (
72
+ RETRYABLE_NETWORK_MESSAGE_PATTERNS.some((p) => p.test(error.message))
73
+ ) {
74
+ return true;
75
+ }
76
+ const cause = error.cause;
77
+ if (
78
+ cause instanceof Error &&
79
+ RETRYABLE_NETWORK_MESSAGE_PATTERNS.some((p) => p.test(cause.message))
80
+ ) {
81
+ return true;
82
+ }
66
83
  return false;
67
84
  }
68
85
 
@@ -396,6 +413,34 @@ describe("RetryProvider — network error retries", () => {
396
413
  expect(result.content[0]).toMatchObject({ type: "text", text: "ok" });
397
414
  });
398
415
 
416
+ test("retries on Bun 'socket connection was closed unexpectedly' (ProviderError wrapping)", async () => {
417
+ const inner = makeFlaky(
418
+ 1,
419
+ new ProviderError(
420
+ "Anthropic request failed: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()",
421
+ "anthropic",
422
+ ),
423
+ );
424
+ const provider = new RetryProvider(inner);
425
+
426
+ const result = await provider.sendMessage(MESSAGES);
427
+ expect(inner.calls).toBe(2);
428
+ expect(result.stopReason).toBe("end_turn");
429
+ });
430
+
431
+ test("retries on 'socket connection was closed unexpectedly' in error cause", async () => {
432
+ const cause = new Error(
433
+ "The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()",
434
+ );
435
+ const outer = new Error("fetch failed", { cause });
436
+ const inner = makeFlaky(1, outer);
437
+ const provider = new RetryProvider(inner);
438
+
439
+ const result = await provider.sendMessage(MESSAGES);
440
+ expect(inner.calls).toBe(2);
441
+ expect(result.content[0]).toMatchObject({ type: "text", text: "ok" });
442
+ });
443
+
399
444
  test("does not retry on non-retryable errors", async () => {
400
445
  const inner = makeFailing(new Error("unexpected error"));
401
446
  const provider = new RetryProvider(inner);
@@ -419,6 +464,115 @@ describe("RetryProvider — network error retries", () => {
419
464
  });
420
465
  });
421
466
 
467
+ // ---------------------------------------------------------------------------
468
+ // RetryProvider — streaming corruption retries
469
+ // ---------------------------------------------------------------------------
470
+
471
+ describe("RetryProvider — streaming corruption retries", () => {
472
+ test("retries on 'Unexpected event order' (message_start before message_stop)", async () => {
473
+ const inner = makeFlaky(
474
+ 1,
475
+ new ProviderError(
476
+ 'Anthropic request failed: Unexpected event order, got message_start before receiving "message_stop"',
477
+ "anthropic",
478
+ ),
479
+ );
480
+ const provider = new RetryProvider(inner);
481
+
482
+ const result = await provider.sendMessage(MESSAGES);
483
+ expect(result.stopReason).toBe("end_turn");
484
+ expect(inner.calls).toBe(2);
485
+ });
486
+
487
+ test("retries on 'Unexpected event order' (event before message_start)", async () => {
488
+ const inner = makeFlaky(
489
+ 1,
490
+ new ProviderError(
491
+ 'Anthropic request failed: Unexpected event order, got content_block_start before "message_start"',
492
+ "anthropic",
493
+ ),
494
+ );
495
+ const provider = new RetryProvider(inner);
496
+
497
+ const result = await provider.sendMessage(MESSAGES);
498
+ expect(inner.calls).toBe(2);
499
+ expect(result.model).toBe("test-model");
500
+ });
501
+
502
+ test("retries on 'stream ended without producing'", async () => {
503
+ const inner = makeFlaky(
504
+ 1,
505
+ new ProviderError(
506
+ "Anthropic request failed: stream ended without producing a Message with role=assistant",
507
+ "anthropic",
508
+ ),
509
+ );
510
+ const provider = new RetryProvider(inner);
511
+
512
+ const result = await provider.sendMessage(MESSAGES);
513
+ expect(inner.calls).toBe(2);
514
+ expect(result.content).toHaveLength(1);
515
+ });
516
+
517
+ test("retries on 'request ended without sending any chunks'", async () => {
518
+ const inner = makeFlaky(
519
+ 1,
520
+ new ProviderError(
521
+ "Anthropic request failed: request ended without sending any chunks",
522
+ "anthropic",
523
+ ),
524
+ );
525
+ const provider = new RetryProvider(inner);
526
+
527
+ await provider.sendMessage(MESSAGES);
528
+ expect(inner.calls).toBe(2);
529
+ });
530
+
531
+ test("throws after exhausting retries on persistent stream corruption", async () => {
532
+ const inner = makeFailing(
533
+ new ProviderError(
534
+ 'Anthropic request failed: Unexpected event order, got message_start before receiving "message_stop"',
535
+ "anthropic",
536
+ ),
537
+ );
538
+ const provider = new RetryProvider(inner);
539
+
540
+ await expect(provider.sendMessage(MESSAGES)).rejects.toThrow(
541
+ "Unexpected event order",
542
+ );
543
+ expect(inner.calls).toBe(DEFAULT_MAX_RETRIES + 1);
544
+ });
545
+
546
+ test("does not retry non-stream ProviderError without status code", async () => {
547
+ const inner = makeFailing(
548
+ new ProviderError("model not found", "anthropic"),
549
+ );
550
+ const provider = new RetryProvider(inner);
551
+
552
+ await expect(provider.sendMessage(MESSAGES)).rejects.toThrow(
553
+ "model not found",
554
+ );
555
+ expect(inner.calls).toBe(1);
556
+ });
557
+
558
+ test("does not treat stream pattern as retryable when ProviderError has a status code", async () => {
559
+ // A 400 error that happens to contain "Unexpected event order" should NOT be retried
560
+ const inner = makeFailing(
561
+ new ProviderError(
562
+ "Unexpected event order in request payload",
563
+ "anthropic",
564
+ 400,
565
+ ),
566
+ );
567
+ const provider = new RetryProvider(inner);
568
+
569
+ await expect(provider.sendMessage(MESSAGES)).rejects.toThrow(
570
+ "Unexpected event order",
571
+ );
572
+ expect(inner.calls).toBe(1);
573
+ });
574
+ });
575
+
422
576
  // ---------------------------------------------------------------------------
423
577
  // RetryProvider — streaming + options passthrough
424
578
  // ---------------------------------------------------------------------------
@@ -52,9 +52,9 @@ function makeProvidersConfig(provider: string, model: string): ProvidersConfig {
52
52
  "image-generation": {
53
53
  mode: "your-own",
54
54
  provider: "gemini",
55
- model: "gemini-2.5-flash-image",
55
+ model: "gemini-3.1-flash-image-preview",
56
56
  },
57
- "web-search": { mode: "your-own", provider: "anthropic-native" },
57
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
58
58
  },
59
59
  };
60
60
  }
@@ -69,9 +69,9 @@ function makeProvidersConfig(provider: string, model: string): ProvidersConfig {
69
69
  "image-generation": {
70
70
  mode: "your-own",
71
71
  provider: "gemini",
72
- model: "gemini-2.5-flash-image",
72
+ model: "gemini-3.1-flash-image-preview",
73
73
  },
74
- "web-search": { mode: "your-own", provider: "anthropic-native" },
74
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
75
75
  },
76
76
  };
77
77
  }
@@ -206,7 +206,7 @@ describe("managed proxy integration — credential precedence", () => {
206
206
  expect(baseURL).toContain("/v1/runtime-proxy/anthropic");
207
207
  });
208
208
 
209
- test("managed gemini uses vertex proxy path", async () => {
209
+ test("managed gemini uses gemini proxy path", async () => {
210
210
  enableManagedProxy();
211
211
  mockProviderKeys = {};
212
212
  await initializeProviders(makeProvidersConfig("anthropic", "test-model"));
@@ -216,8 +216,7 @@ describe("managed proxy integration — credential precedence", () => {
216
216
  | { baseUrl?: string }
217
217
  | undefined;
218
218
  expect(httpOptions).toBeDefined();
219
- expect(httpOptions!.baseUrl).toContain("/v1/runtime-proxy/vertex");
220
- expect(httpOptions!.baseUrl).not.toContain("/v1/runtime-proxy/gemini");
219
+ expect(httpOptions!.baseUrl).toContain("/v1/runtime-proxy/gemini");
221
220
  });
222
221
  });
223
222
 
@@ -309,8 +308,8 @@ describe("managed proxy integration — ollama exclusion", () => {
309
308
  });
310
309
 
311
310
  describe("managed proxy integration — constants integrity", () => {
312
- test("anthropic, gemini, and vertex have metadata with managed=true and a proxyPath", () => {
313
- for (const provider of ["anthropic", "gemini", "vertex"]) {
311
+ test("anthropic and gemini have metadata with managed=true and a proxyPath", () => {
312
+ for (const provider of ["anthropic", "gemini"]) {
314
313
  const meta = MANAGED_PROVIDER_META[provider];
315
314
  expect(meta).toBeDefined();
316
315
  expect(meta.managed).toBe(true);
@@ -325,9 +324,9 @@ describe("managed proxy integration — constants integrity", () => {
325
324
  );
326
325
  });
327
326
 
328
- test("gemini routes through vertex proxy path", () => {
327
+ test("gemini routes through gemini proxy path", () => {
329
328
  expect(MANAGED_PROVIDER_META.gemini.proxyPath).toBe(
330
- "/v1/runtime-proxy/vertex",
329
+ "/v1/runtime-proxy/gemini",
331
330
  );
332
331
  });
333
332
 
@@ -25,9 +25,12 @@ describe("provider registry (ollama)", () => {
25
25
  "image-generation": {
26
26
  mode: "your-own",
27
27
  provider: "gemini",
28
- model: "gemini-2.5-flash-image",
28
+ model: "gemini-3.1-flash-image-preview",
29
+ },
30
+ "web-search": {
31
+ mode: "your-own",
32
+ provider: "inference-provider-native",
29
33
  },
30
- "web-search": { mode: "your-own", provider: "anthropic-native" },
31
34
  },
32
35
  });
33
36
 
@@ -36,9 +36,9 @@ import { QdrantManager } from "../memory/qdrant-manager.js";
36
36
 
37
37
  /** Short timeouts so tests complete fast but with enough headroom for CI. */
38
38
  const FAST_TIMEOUTS = {
39
- readyzPollIntervalMs: 10,
40
- readyzTimeoutMs: 1_000,
41
- shutdownGraceMs: 200,
39
+ readyzPollIntervalMs: 5,
40
+ readyzTimeoutMs: 100,
41
+ shutdownGraceMs: 50,
42
42
  } as const;
43
43
 
44
44
  function placeFakeBinary(script: string): string {
@@ -248,7 +248,7 @@ describe("QdrantManager", () => {
248
248
  const startPromise = mgr.start();
249
249
 
250
250
  // Wait for spawn to happen
251
- await Bun.sleep(300);
251
+ await Bun.sleep(50);
252
252
 
253
253
  // PID file should be written
254
254
  expect(existsSync(pidPath)).toBe(true);
@@ -277,7 +277,7 @@ describe("QdrantManager", () => {
277
277
  });
278
278
 
279
279
  const startPromise = mgr.start();
280
- await Bun.sleep(300);
280
+ await Bun.sleep(50);
281
281
 
282
282
  expect(existsSync(pidPath)).toBe(true);
283
283
 
@@ -285,8 +285,8 @@ describe("QdrantManager", () => {
285
285
  await mgr.stop();
286
286
  const stopElapsed = Date.now() - stopStart;
287
287
 
288
- // Grace period is 200ms with FAST_TIMEOUTS — should wait at least that long
289
- expect(stopElapsed).toBeGreaterThanOrEqual(100);
288
+ // Grace period is 50ms with FAST_TIMEOUTS — should wait at least that long
289
+ expect(stopElapsed).toBeGreaterThanOrEqual(30);
290
290
  expect(existsSync(pidPath)).toBe(false);
291
291
 
292
292
  await expect(startPromise).rejects.toThrow("did not become ready");
@@ -38,7 +38,6 @@ describe("RateLimitProvider", () => {
38
38
  test("allows requests under the limit", async () => {
39
39
  const config: RateLimitConfig = {
40
40
  maxRequestsPerMinute: 5,
41
- maxTokensPerSession: 0,
42
41
  };
43
42
  const provider = new RateLimitProvider(makeProvider(), config);
44
43
 
@@ -50,7 +49,6 @@ describe("RateLimitProvider", () => {
50
49
  test("throws RateLimitError when exceeding request limit", async () => {
51
50
  const config: RateLimitConfig = {
52
51
  maxRequestsPerMinute: 2,
53
- maxTokensPerSession: 0,
54
52
  };
55
53
  const provider = new RateLimitProvider(makeProvider(), config);
56
54
 
@@ -63,7 +61,6 @@ describe("RateLimitProvider", () => {
63
61
  test("unlimited when maxRequestsPerMinute is 0", async () => {
64
62
  const config: RateLimitConfig = {
65
63
  maxRequestsPerMinute: 0,
66
- maxTokensPerSession: 0,
67
64
  };
68
65
  const provider = new RateLimitProvider(makeProvider(), config);
69
66
 
@@ -73,56 +70,10 @@ describe("RateLimitProvider", () => {
73
70
  });
74
71
  });
75
72
 
76
- describe("session token limiting", () => {
77
- test("allows requests under the token budget", async () => {
78
- const config: RateLimitConfig = {
79
- maxRequestsPerMinute: 0,
80
- maxTokensPerSession: 1000,
81
- };
82
- const provider = new RateLimitProvider(makeProvider(), config);
83
-
84
- // Each call uses 150 tokens (100 input + 50 output)
85
- for (let i = 0; i < 6; i++) {
86
- await provider.sendMessage(messages);
87
- }
88
- // 6 * 150 = 900, still under 1000
89
- });
90
-
91
- test("throws RateLimitError when token budget exhausted", async () => {
92
- const config: RateLimitConfig = {
93
- maxRequestsPerMinute: 0,
94
- maxTokensPerSession: 300,
95
- };
96
- const provider = new RateLimitProvider(makeProvider(), config);
97
-
98
- // 150 tokens per call
99
- await provider.sendMessage(messages); // 150
100
- await provider.sendMessage(messages); // 300
101
-
102
- expect(provider.sendMessage(messages)).rejects.toThrow(RateLimitError);
103
- });
104
-
105
- test("unlimited when maxTokensPerSession is 0", async () => {
106
- const config: RateLimitConfig = {
107
- maxRequestsPerMinute: 0,
108
- maxTokensPerSession: 0,
109
- };
110
- const provider = new RateLimitProvider(
111
- makeProvider({ usage: { inputTokens: 10000, outputTokens: 10000 } }),
112
- config,
113
- );
114
-
115
- for (let i = 0; i < 10; i++) {
116
- await provider.sendMessage(messages);
117
- }
118
- });
119
- });
120
-
121
73
  describe("passthrough behavior", () => {
122
74
  test("delegates to inner provider", async () => {
123
75
  const config: RateLimitConfig = {
124
76
  maxRequestsPerMinute: 0,
125
- maxTokensPerSession: 0,
126
77
  };
127
78
  const inner = makeProvider({ model: "custom-model" });
128
79
  const provider = new RateLimitProvider(inner, config);
@@ -134,7 +85,6 @@ describe("RateLimitProvider", () => {
134
85
  test("preserves provider name", () => {
135
86
  const config: RateLimitConfig = {
136
87
  maxRequestsPerMinute: 0,
137
- maxTokensPerSession: 0,
138
88
  };
139
89
  const provider = new RateLimitProvider(makeProvider(), config);
140
90
  expect(provider.name).toBe("mock");
@@ -143,7 +93,6 @@ describe("RateLimitProvider", () => {
143
93
  test("passes through all arguments to inner provider", async () => {
144
94
  const config: RateLimitConfig = {
145
95
  maxRequestsPerMinute: 0,
146
- maxTokensPerSession: 0,
147
96
  };
148
97
  let receivedArgs: unknown[] = [];
149
98
  const inner: Provider = {
@@ -173,27 +122,10 @@ describe("RateLimitProvider", () => {
173
122
  });
174
123
  });
175
124
 
176
- describe("combined limits", () => {
177
- test("enforces both limits simultaneously", async () => {
178
- const config: RateLimitConfig = {
179
- maxRequestsPerMinute: 10,
180
- maxTokensPerSession: 300,
181
- };
182
- const provider = new RateLimitProvider(makeProvider(), config);
183
-
184
- // Token limit should hit first (2 * 150 = 300)
185
- await provider.sendMessage(messages);
186
- await provider.sendMessage(messages);
187
-
188
- expect(provider.sendMessage(messages)).rejects.toThrow(RateLimitError);
189
- });
190
- });
191
-
192
125
  describe("shared request timestamps", () => {
193
126
  test("multiple providers sharing timestamps enforce a global rate limit", async () => {
194
127
  const config: RateLimitConfig = {
195
128
  maxRequestsPerMinute: 2,
196
- maxTokensPerSession: 0,
197
129
  };
198
130
  const shared: number[] = [];
199
131
  const provider1 = new RateLimitProvider(makeProvider(), config, shared);
@@ -215,7 +147,6 @@ describe("RateLimitProvider", () => {
215
147
  test("out-of-order timestamps are pruned correctly (clock skew)", async () => {
216
148
  const config: RateLimitConfig = {
217
149
  maxRequestsPerMinute: 3,
218
- maxTokensPerSession: 0,
219
150
  };
220
151
  const shared: number[] = [];
221
152
  const provider = new RateLimitProvider(makeProvider(), config, shared);
@@ -236,7 +167,6 @@ describe("RateLimitProvider", () => {
236
167
  test("waitSec uses actual oldest timestamp under clock skew", async () => {
237
168
  const config: RateLimitConfig = {
238
169
  maxRequestsPerMinute: 2,
239
- maxTokensPerSession: 0,
240
170
  };
241
171
  const shared: number[] = [];
242
172
  const provider = new RateLimitProvider(makeProvider(), config, shared);
@@ -266,7 +196,6 @@ describe("RateLimitProvider", () => {
266
196
  const highLimit = 200_000;
267
197
  const config: RateLimitConfig = {
268
198
  maxRequestsPerMinute: highLimit,
269
- maxTokensPerSession: 0,
270
199
  };
271
200
  const shared: number[] = [];
272
201
  const provider = new RateLimitProvider(makeProvider(), config, shared);
@@ -286,7 +215,6 @@ describe("RateLimitProvider", () => {
286
215
  test("shared array reference survives pruning", async () => {
287
216
  const config: RateLimitConfig = {
288
217
  maxRequestsPerMinute: 100,
289
- maxTokensPerSession: 0,
290
218
  };
291
219
  const shared: number[] = [];
292
220
  const provider1 = new RateLimitProvider(makeProvider(), config, shared);
@@ -311,7 +239,6 @@ describe("RateLimitProvider", () => {
311
239
  test("concurrent calls are rate-limited because timestamp is recorded before await", async () => {
312
240
  const config: RateLimitConfig = {
313
241
  maxRequestsPerMinute: 1,
314
- maxTokensPerSession: 0,
315
242
  };
316
243
  // Slow provider that yields to the event loop
317
244
  const inner: Provider = {
@@ -343,7 +270,6 @@ describe("RateLimitProvider", () => {
343
270
  test("failed inner calls still count toward request rate", async () => {
344
271
  const config: RateLimitConfig = {
345
272
  maxRequestsPerMinute: 1,
346
- maxTokensPerSession: 0,
347
273
  };
348
274
  const inner: Provider = {
349
275
  name: "failing",
@@ -24,7 +24,6 @@ mock.module("../config/loader.js", () => ({
24
24
  daemon: { standaloneRecording: true },
25
25
  provider: "mock-provider",
26
26
  permissions: { mode: "workspace" },
27
- sandbox: { enabled: false },
28
27
  timeouts: { toolExecutionTimeoutSec: 30, permissionTimeoutSec: 5 },
29
28
  skills: { load: { extraDirs: [] } },
30
29
  secretDetection: { enabled: false, allowOneTimeSend: false },
@@ -52,7 +52,7 @@ const mockConfig = {
52
52
  network: "none" as const,
53
53
  },
54
54
  },
55
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
55
+ rateLimit: { maxRequestsPerMinute: 0 },
56
56
  secretDetection: {
57
57
  enabled: false,
58
58
  action: "warn" as const,
@@ -43,7 +43,7 @@ mock.module("../config/loader.js", () => ({
43
43
  model: "test",
44
44
  provider: "test",
45
45
  memory: { enabled: false },
46
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
46
+ rateLimit: { maxRequestsPerMinute: 0 },
47
47
  }),
48
48
  }));
49
49
 
@@ -50,7 +50,7 @@ mock.module("../config/loader.js", () => ({
50
50
  model: "test",
51
51
  provider: "test",
52
52
  memory: { enabled: false },
53
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
53
+ rateLimit: { maxRequestsPerMinute: 0 },
54
54
  secretDetection: { enabled: false },
55
55
  }),
56
56
  }));
@@ -42,7 +42,7 @@ mock.module("../config/loader.js", () => ({
42
42
  model: "test",
43
43
  provider: "test",
44
44
  memory: { enabled: false },
45
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
45
+ rateLimit: { maxRequestsPerMinute: 0 },
46
46
  secretDetection: { enabled: false },
47
47
  }),
48
48
  }));
@@ -1,7 +1,15 @@
1
1
  import { mkdtempSync, rmSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
4
+ import {
5
+ afterAll,
6
+ beforeAll,
7
+ beforeEach,
8
+ describe,
9
+ expect,
10
+ mock,
11
+ test,
12
+ } from "bun:test";
5
13
 
6
14
  const testDir = mkdtempSync(join(tmpdir(), "scheduler-recurrence-test-"));
7
15
 
@@ -86,7 +94,30 @@ function buildEndedRrule(): string {
86
94
 
87
95
  // ── RRULE schedule fires through the scheduler ──────────────────────
88
96
 
97
+ // Replace setTimeout with a zero-delay version so the 500ms scheduler
98
+ // wait calls fire instantly instead of waiting real time.
99
+ let origSetTimeout: typeof globalThis.setTimeout;
100
+
89
101
  describe("scheduler RRULE execution", () => {
102
+ beforeAll(() => {
103
+ origSetTimeout = globalThis.setTimeout;
104
+ globalThis.setTimeout = ((
105
+ fn: TimerHandler,
106
+ _ms?: number,
107
+ ...args: unknown[]
108
+ ) => {
109
+ // Use a small real delay so fire-and-forget async ticks have time to
110
+ // settle, while still cutting the 500ms waits down dramatically.
111
+ // 200ms gives headroom for the run_task path which does a dynamic
112
+ // import of task-runner.js on first invocation.
113
+ return origSetTimeout(fn, 200, ...args);
114
+ }) as typeof setTimeout;
115
+ });
116
+
117
+ afterAll(() => {
118
+ globalThis.setTimeout = origSetTimeout;
119
+ });
120
+
90
121
  beforeEach(() => {
91
122
  const db = getDb();
92
123
  db.run("DELETE FROM cron_runs");
@@ -157,8 +188,11 @@ describe("scheduler RRULE execution", () => {
157
188
  forceScheduleDue(schedule.id);
158
189
 
159
190
  const directCalls: { conversationId: string; message: string }[] = [];
191
+ let onMessage: (() => void) | undefined;
192
+ const messageReceived = new Promise<void>((r) => (onMessage = r));
160
193
  const processMessage = async (conversationId: string, message: string) => {
161
194
  directCalls.push({ conversationId, message });
195
+ onMessage?.();
162
196
  };
163
197
 
164
198
  const scheduler = startScheduler(
@@ -166,7 +200,17 @@ describe("scheduler RRULE execution", () => {
166
200
  () => {},
167
201
  () => {},
168
202
  );
169
- await new Promise((resolve) => setTimeout(resolve, 500));
203
+ // The run_task path involves a dynamic import which can take >50ms in CI,
204
+ // exceeding the patched setTimeout delay. Await the actual callback instead
205
+ // of relying on a fixed timeout.
206
+ await Promise.race([
207
+ messageReceived,
208
+ new Promise((r) => origSetTimeout(r, 2000)),
209
+ ]);
210
+ // Yield to the macrotask queue so all pending microtasks settle —
211
+ // the scheduler tick still needs to create the schedule run after
212
+ // processMessage returns.
213
+ await new Promise((r) => origSetTimeout(r, 0));
170
214
  scheduler.stop();
171
215
 
172
216
  // runTask renders the template, so processMessage gets the template text