@vellumai/assistant 0.4.56 → 0.5.0

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 (457) 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 +204 -185
  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 +249 -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__/file-read-tool.test.ts +40 -0
  67. package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
  68. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  69. package/src/__tests__/gemini-image-service.test.ts +4 -4
  70. package/src/__tests__/gemini-provider.test.ts +6 -9
  71. package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
  72. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  73. package/src/__tests__/host-file-read-tool.test.ts +87 -0
  74. package/src/__tests__/host-shell-tool.test.ts +6 -6
  75. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  76. package/src/__tests__/identity-intro-cache.test.ts +209 -0
  77. package/src/__tests__/intent-routing.test.ts +51 -99
  78. package/src/__tests__/invite-routes-http.test.ts +5 -0
  79. package/src/__tests__/list-messages-attachments.test.ts +1 -1
  80. package/src/__tests__/managed-proxy-context.test.ts +2 -5
  81. package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
  82. package/src/__tests__/media-generate-image.test.ts +32 -15
  83. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  84. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
  86. package/src/__tests__/memory-recall-quality.test.ts +4 -3
  87. package/src/__tests__/memory-regressions.test.ts +86 -90
  88. package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
  89. package/src/__tests__/migration-export-http.test.ts +26 -27
  90. package/src/__tests__/migration-import-commit-http.test.ts +165 -37
  91. package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
  92. package/src/__tests__/migration-validate-http.test.ts +16 -16
  93. package/src/__tests__/model-intents.test.ts +2 -2
  94. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
  95. package/src/__tests__/non-member-access-request.test.ts +3 -3
  96. package/src/__tests__/notification-broadcaster.test.ts +1 -1
  97. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  98. package/src/__tests__/notification-decision-identity.test.ts +8 -9
  99. package/src/__tests__/notification-decision-strategy.test.ts +1 -1
  100. package/src/__tests__/notification-deep-link.test.ts +1 -1
  101. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  102. package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
  103. package/src/__tests__/oauth-store.test.ts +1 -3
  104. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
  105. package/src/__tests__/onboarding-template-contract.test.ts +23 -59
  106. package/src/__tests__/provider-error-scenarios.test.ts +154 -0
  107. package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
  108. package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
  109. package/src/__tests__/provider-registry-ollama.test.ts +5 -2
  110. package/src/__tests__/qdrant-manager.test.ts +7 -7
  111. package/src/__tests__/ratelimit.test.ts +0 -74
  112. package/src/__tests__/recording-handler.test.ts +0 -1
  113. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  114. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  115. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  116. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  117. package/src/__tests__/scheduler-recurrence.test.ts +46 -2
  118. package/src/__tests__/schema-transforms.test.ts +114 -54
  119. package/src/__tests__/secret-onetime-send.test.ts +20 -0
  120. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
  121. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  122. package/src/__tests__/send-endpoint-busy.test.ts +63 -4
  123. package/src/__tests__/send-notification-tool.test.ts +2 -2
  124. package/src/__tests__/shell-credential-ref.test.ts +0 -1
  125. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
  126. package/src/__tests__/skill-memory.test.ts +549 -0
  127. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
  128. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
  129. package/src/__tests__/slack-channel-config.test.ts +109 -94
  130. package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
  131. package/src/__tests__/swarm-recursion.test.ts +2 -2
  132. package/src/__tests__/swarm-tool.test.ts +2 -2
  133. package/src/__tests__/system-prompt.test.ts +19 -66
  134. package/src/__tests__/telegram-config.test.ts +121 -0
  135. package/src/__tests__/terminal-tools.test.ts +1 -1
  136. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  138. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  139. package/src/__tests__/tool-executor.test.ts +1 -1
  140. package/src/__tests__/trace-emitter.test.ts +8 -1
  141. package/src/__tests__/trust-store.test.ts +7 -8
  142. package/src/__tests__/twilio-routes.test.ts +1 -18
  143. package/src/__tests__/user-reference.test.ts +82 -2
  144. package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
  145. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  146. package/src/approvals/guardian-request-resolvers.ts +3 -3
  147. package/src/avatar/ascii-renderer.ts +2 -2
  148. package/src/avatar/png-renderer.ts +2 -2
  149. package/src/avatar/resvg-lazy.ts +21 -0
  150. package/src/calls/guardian-dispatch.ts +1 -1
  151. package/src/calls/relay-access-wait.ts +2 -2
  152. package/src/calls/twilio-rest.ts +0 -248
  153. package/src/cli/AGENTS.md +5 -8
  154. package/src/cli/__tests__/notifications.test.ts +5 -5
  155. package/src/cli/commands/avatar.ts +64 -2
  156. package/src/cli/commands/conversations.ts +131 -1
  157. package/src/cli/commands/credentials.ts +2 -0
  158. package/src/cli/commands/notifications.ts +3 -3
  159. package/src/cli.ts +10 -0
  160. package/src/config/bundled-skills/acp/SKILL.md +5 -5
  161. package/src/config/bundled-skills/acp/TOOLS.json +6 -6
  162. package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
  163. package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
  164. package/src/config/bundled-skills/browser/SKILL.md +15 -15
  165. package/src/config/bundled-skills/browser/TOOLS.json +14 -14
  166. package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
  167. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
  168. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  169. package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
  170. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  171. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
  172. package/src/config/bundled-skills/contacts/SKILL.md +3 -3
  173. package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
  174. package/src/config/bundled-skills/document/SKILL.md +4 -4
  175. package/src/config/bundled-skills/document/TOOLS.json +2 -2
  176. package/src/config/bundled-skills/followups/TOOLS.json +3 -3
  177. package/src/config/bundled-skills/gmail/SKILL.md +32 -32
  178. package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
  179. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
  180. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  181. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  182. package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
  183. package/src/config/bundled-skills/google-calendar/types.ts +1 -1
  184. package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
  185. package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
  186. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
  187. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
  188. package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
  189. package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
  190. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
  191. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
  192. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
  193. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
  194. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
  195. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
  196. package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
  197. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  198. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
  199. package/src/config/bundled-skills/messaging/SKILL.md +29 -25
  200. package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
  201. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  202. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  203. package/src/config/bundled-skills/notifications/SKILL.md +3 -3
  204. package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
  205. package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
  206. package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
  207. package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
  208. package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
  209. package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
  210. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  211. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
  212. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
  213. package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
  214. package/src/config/bundled-skills/schedule/SKILL.md +26 -26
  215. package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
  216. package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
  217. package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
  218. package/src/config/bundled-skills/sequences/SKILL.md +2 -2
  219. package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
  220. package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
  221. package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
  222. package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
  223. package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
  224. package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
  225. package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
  226. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
  227. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  228. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  229. package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
  230. package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
  231. package/src/config/bundled-skills/slack/SKILL.md +2 -2
  232. package/src/config/bundled-skills/slack/TOOLS.json +8 -8
  233. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
  234. package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
  235. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  236. package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
  237. package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
  238. package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
  240. package/src/config/bundled-skills/watcher/SKILL.md +4 -4
  241. package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
  242. package/src/config/feature-flag-registry.json +33 -17
  243. package/src/config/schemas/sandbox.ts +1 -1
  244. package/src/config/schemas/services.ts +13 -3
  245. package/src/config/schemas/timeouts.ts +0 -10
  246. package/src/contacts/contact-store.ts +63 -0
  247. package/src/contacts/contacts-write.ts +1 -1
  248. package/src/daemon/assistant-attachments.ts +2 -2
  249. package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
  250. package/src/daemon/conversation-agent-loop.ts +7 -30
  251. package/src/daemon/conversation-error.ts +24 -0
  252. package/src/daemon/conversation-memory.ts +8 -7
  253. package/src/daemon/conversation-runtime-assembly.ts +141 -275
  254. package/src/daemon/conversation-slash.ts +7 -26
  255. package/src/daemon/conversation-surfaces.ts +14 -0
  256. package/src/daemon/conversation-tool-setup.ts +9 -8
  257. package/src/daemon/conversation.ts +2 -0
  258. package/src/daemon/daemon-control.ts +1 -1
  259. package/src/daemon/date-context.ts +10 -83
  260. package/src/daemon/handlers/config-channels.ts +12 -2
  261. package/src/daemon/handlers/config-slack-channel.ts +7 -1
  262. package/src/daemon/handlers/config-telegram.ts +6 -1
  263. package/src/daemon/handlers/conversations.ts +2 -2
  264. package/src/daemon/handlers/skills.ts +4 -0
  265. package/src/daemon/lifecycle.ts +28 -4
  266. package/src/daemon/providers-setup.ts +1 -1
  267. package/src/daemon/server.ts +1 -5
  268. package/src/daemon/shutdown-handlers.ts +9 -3
  269. package/src/daemon/tool-side-effects.ts +40 -0
  270. package/src/daemon/trace-emitter.ts +26 -2
  271. package/src/events/domain-events.ts +1 -1
  272. package/src/events/tool-permission-telemetry-listener.ts +46 -0
  273. package/src/inbound/platform-callback-registration.ts +0 -18
  274. package/src/media/app-icon-generator.ts +15 -8
  275. package/src/media/avatar-router.ts +15 -8
  276. package/src/media/gemini-image-service.ts +125 -21
  277. package/src/memory/attachments-store.ts +3 -3
  278. package/src/memory/channel-verification-sessions.ts +6 -6
  279. package/src/memory/conversation-crud.ts +196 -1
  280. package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
  281. package/src/memory/conversation-title-service.ts +2 -3
  282. package/src/memory/db-init.ts +25 -1
  283. package/src/memory/invite-store.ts +4 -4
  284. package/src/memory/items-extractor.ts +4 -4
  285. package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
  286. package/src/memory/jobs-store.ts +3 -2
  287. package/src/memory/jobs-worker.ts +7 -5
  288. package/src/memory/lifecycle-events-store.ts +63 -0
  289. package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
  290. package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
  291. package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
  292. package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
  293. package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
  294. package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
  295. package/src/memory/migrations/index.ts +6 -0
  296. package/src/memory/migrations/registry.ts +13 -0
  297. package/src/memory/retriever.test.ts +223 -96
  298. package/src/memory/retriever.ts +115 -138
  299. package/src/memory/schema/calls.ts +1 -1
  300. package/src/memory/schema/contacts.ts +1 -1
  301. package/src/memory/schema/infrastructure.ts +29 -0
  302. package/src/memory/schema/memory-core.ts +7 -17
  303. package/src/memory/schema/notifications.ts +1 -1
  304. package/src/memory/search/formatting.ts +23 -6
  305. package/src/memory/search/lexical.ts +2 -0
  306. package/src/memory/search/semantic.ts +2 -0
  307. package/src/memory/search/staleness.ts +5 -1
  308. package/src/memory/search/types.ts +4 -0
  309. package/src/memory/task-memory-cleanup.ts +96 -6
  310. package/src/memory/trace-event-store.ts +148 -0
  311. package/src/notifications/README.md +1 -1
  312. package/src/notifications/decision-engine.ts +45 -4
  313. package/src/notifications/emit-signal.ts +5 -4
  314. package/src/notifications/events-store.ts +4 -4
  315. package/src/notifications/signal.ts +1 -1
  316. package/src/oauth/manual-token-connection.ts +49 -25
  317. package/src/permissions/checker.ts +6 -5
  318. package/src/permissions/defaults.ts +4 -4
  319. package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
  320. package/src/prompts/cache-boundary.ts +8 -0
  321. package/src/prompts/system-prompt.ts +105 -634
  322. package/src/prompts/templates/BOOTSTRAP.md +172 -33
  323. package/src/prompts/templates/IDENTITY.md +8 -24
  324. package/src/prompts/templates/SOUL.md +20 -41
  325. package/src/prompts/templates/USER.md +3 -19
  326. package/src/prompts/user-reference.ts +14 -16
  327. package/src/providers/anthropic/client.ts +51 -19
  328. package/src/providers/gemini/client.ts +6 -9
  329. package/src/providers/managed-proxy/constants.ts +1 -7
  330. package/src/providers/managed-proxy/context.ts +0 -1
  331. package/src/providers/model-intents.ts +5 -5
  332. package/src/providers/openai/client.ts +10 -1
  333. package/src/providers/openrouter/client.ts +1 -0
  334. package/src/providers/ratelimit.ts +0 -35
  335. package/src/providers/registry.ts +3 -5
  336. package/src/providers/retry.ts +18 -1
  337. package/src/runtime/access-request-helper.ts +16 -2
  338. package/src/runtime/auth/route-policy.ts +7 -0
  339. package/src/runtime/channel-verification-service.ts +1 -1
  340. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  341. package/src/runtime/guardian-vellum-migration.ts +61 -1
  342. package/src/runtime/http-server.ts +8 -4
  343. package/src/runtime/migrations/vbundle-builder.ts +212 -32
  344. package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
  345. package/src/runtime/migrations/vbundle-importer.ts +66 -1
  346. package/src/runtime/migrations/vbundle-validator.ts +17 -3
  347. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
  348. package/src/runtime/routes/attachment-routes.ts +2 -2
  349. package/src/runtime/routes/btw-routes.ts +93 -0
  350. package/src/runtime/routes/channel-verification-routes.ts +19 -2
  351. package/src/runtime/routes/conversation-management-routes.ts +55 -1
  352. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-routes.ts +49 -5
  354. package/src/runtime/routes/conversation-starter-routes.ts +207 -0
  355. package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
  356. package/src/runtime/routes/identity-intro-cache.ts +105 -0
  357. package/src/runtime/routes/identity-routes.ts +51 -0
  358. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
  359. package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
  360. package/src/runtime/routes/migration-routes.ts +25 -13
  361. package/src/runtime/routes/secret-routes.ts +18 -0
  362. package/src/runtime/routes/settings-routes.ts +9 -9
  363. package/src/runtime/routes/telemetry-routes.ts +53 -0
  364. package/src/runtime/routes/trace-event-routes.ts +62 -0
  365. package/src/runtime/tool-grant-request-helper.ts +1 -1
  366. package/src/runtime/verification-outbound-actions.ts +47 -31
  367. package/src/security/encrypted-store.ts +262 -78
  368. package/src/skills/catalog-install.ts +10 -0
  369. package/src/skills/managed-store.ts +2 -0
  370. package/src/skills/skill-memory.ts +222 -0
  371. package/src/subagent/manager.ts +1 -4
  372. package/src/telemetry/types.ts +10 -1
  373. package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
  374. package/src/telemetry/usage-telemetry-reporter.ts +53 -4
  375. package/src/tools/AGENTS.md +11 -11
  376. package/src/tools/acp/spawn.ts +1 -1
  377. package/src/tools/apps/executors.ts +8 -8
  378. package/src/tools/apps/registry.ts +1 -1
  379. package/src/tools/assets/materialize.ts +6 -6
  380. package/src/tools/assets/search.ts +10 -10
  381. package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
  382. package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
  383. package/src/tools/browser/auth-detector.ts +6 -6
  384. package/src/tools/browser/browser-execution.ts +13 -13
  385. package/src/tools/browser/browser-manager.ts +3 -3
  386. package/src/tools/browser/chrome-cdp.ts +5 -5
  387. package/src/tools/browser/jit-auth.ts +2 -2
  388. package/src/tools/browser/network-recorder.test.ts +2 -2
  389. package/src/tools/browser/network-recorder.ts +3 -3
  390. package/src/tools/browser/runtime-check.ts +3 -3
  391. package/src/tools/claude-code/claude-code.ts +2 -2
  392. package/src/tools/computer-use/definitions.ts +18 -18
  393. package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
  394. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
  395. package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
  396. package/src/tools/credentials/broker-types.ts +5 -5
  397. package/src/tools/credentials/broker.ts +15 -15
  398. package/src/tools/credentials/metadata-store.ts +2 -2
  399. package/src/tools/credentials/resolve.ts +1 -1
  400. package/src/tools/credentials/selection.ts +1 -1
  401. package/src/tools/credentials/tool-policy.ts +1 -1
  402. package/src/tools/credentials/vault.ts +115 -25
  403. package/src/tools/execution-target.ts +2 -2
  404. package/src/tools/executor.ts +7 -7
  405. package/src/tools/filesystem/edit.ts +2 -2
  406. package/src/tools/filesystem/read.ts +15 -4
  407. package/src/tools/filesystem/write.ts +1 -1
  408. package/src/tools/host-filesystem/edit.ts +2 -1
  409. package/src/tools/host-filesystem/read.ts +18 -1
  410. package/src/tools/host-filesystem/write.ts +1 -1
  411. package/src/tools/host-terminal/host-shell.ts +9 -8
  412. package/src/tools/mcp/mcp-tool-factory.ts +7 -6
  413. package/src/tools/memory/definitions.ts +6 -5
  414. package/src/tools/memory/handlers.test.ts +1 -1
  415. package/src/tools/network/__tests__/web-search.test.ts +3 -3
  416. package/src/tools/network/domain-normalize.ts +2 -2
  417. package/src/tools/network/script-proxy/session-manager.ts +10 -10
  418. package/src/tools/network/web-fetch.ts +1 -1
  419. package/src/tools/network/web-search.ts +3 -3
  420. package/src/tools/permission-checker.ts +8 -8
  421. package/src/tools/registry.ts +7 -7
  422. package/src/tools/schedule/list.ts +2 -2
  423. package/src/tools/schema-transforms.ts +31 -21
  424. package/src/tools/secret-detection-handler.ts +1 -1
  425. package/src/tools/sensitive-output-placeholders.ts +1 -1
  426. package/src/tools/shared/filesystem/edit-engine.ts +1 -1
  427. package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
  428. package/src/tools/shared/filesystem/image-read.ts +25 -5
  429. package/src/tools/shared/filesystem/path-policy.ts +2 -2
  430. package/src/tools/shared/shell-output.ts +1 -1
  431. package/src/tools/side-effects.ts +1 -1
  432. package/src/tools/skills/execute.ts +1 -1
  433. package/src/tools/skills/load.ts +3 -3
  434. package/src/tools/skills/sandbox-runner.ts +3 -3
  435. package/src/tools/subagent/read.ts +1 -1
  436. package/src/tools/subagent/spawn.ts +2 -2
  437. package/src/tools/swarm/delegate.ts +3 -3
  438. package/src/tools/system/request-permission.ts +5 -4
  439. package/src/tools/terminal/backends/native.ts +4 -4
  440. package/src/tools/terminal/parser.ts +6 -6
  441. package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
  442. package/src/tools/terminal/shell.ts +16 -16
  443. package/src/tools/tool-approval-handler.ts +21 -12
  444. package/src/tools/tool-manifest.ts +4 -4
  445. package/src/tools/types.ts +3 -3
  446. package/src/tools/ui-surface/definitions.ts +9 -37
  447. package/src/tools/watcher/list.ts +1 -1
  448. package/src/util/logger.ts +7 -2
  449. package/src/util/pricing.ts +4 -0
  450. package/src/util/retry.ts +29 -1
  451. package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
  452. package/src/workspace/migrations/registry.ts +2 -0
  453. package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
  454. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
  455. package/src/cli/reference.ts +0 -38
  456. package/src/memory/job-handlers/capability-cards.ts +0 -420
  457. package/src/runtime/routes/thread-starter-routes.ts +0 -294
@@ -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
@@ -2,8 +2,8 @@ import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import type { ToolDefinition } from "../providers/types.js";
4
4
  import {
5
- injectReasonField,
6
- REASON_SKIP_SET,
5
+ ACTIVITY_SKIP_SET,
6
+ injectActivityField,
7
7
  schemaDefinesProperty,
8
8
  } from "../tools/schema-transforms.js";
9
9
 
@@ -14,26 +14,26 @@ function makeDef(
14
14
  return { name, description: `Tool ${name}`, input_schema: schema };
15
15
  }
16
16
 
17
- describe("REASON_SKIP_SET", () => {
18
- test("contains expected tool names", () => {
19
- expect(REASON_SKIP_SET.has("skill_execute")).toBe(true);
20
- expect(REASON_SKIP_SET.has("bash")).toBe(true);
21
- expect(REASON_SKIP_SET.has("host_bash")).toBe(true);
22
- expect(REASON_SKIP_SET.has("request_system_permission")).toBe(true);
23
- expect(REASON_SKIP_SET.size).toBe(4);
17
+ describe("ACTIVITY_SKIP_SET", () => {
18
+ test("is empty (all tools now define their own activity property)", () => {
19
+ expect(ACTIVITY_SKIP_SET.size).toBe(0);
24
20
  });
25
21
  });
26
22
 
27
- describe("injectReasonField", () => {
28
- test("injects reason on a tool without it", () => {
23
+ describe("injectActivityField", () => {
24
+ test("injects activity on a tool without it", () => {
29
25
  const defs = [makeDef("my_tool")];
30
- const result = injectReasonField(defs);
26
+ const result = injectActivityField(defs);
31
27
  const schema = result[0].input_schema as Record<string, unknown>;
32
28
  const props = schema.properties as Record<string, unknown>;
33
- expect(props.reason).toEqual({ type: "string" });
29
+ expect(props.activity).toEqual({
30
+ type: "string",
31
+ description:
32
+ "Brief, natural description of what you're doing, shown as a live status update (e.g. 'Checking your project settings')",
33
+ });
34
34
  });
35
35
 
36
- test("adds reason to required array", () => {
36
+ test("adds activity to required array", () => {
37
37
  const defs = [
38
38
  makeDef("my_tool", {
39
39
  type: "object",
@@ -41,9 +41,9 @@ describe("injectReasonField", () => {
41
41
  required: ["foo"],
42
42
  }),
43
43
  ];
44
- const result = injectReasonField(defs);
44
+ const result = injectActivityField(defs);
45
45
  const schema = result[0].input_schema as Record<string, unknown>;
46
- expect(schema.required).toEqual(["foo", "reason"]);
46
+ expect(schema.required).toEqual(["foo", "activity"]);
47
47
  });
48
48
 
49
49
  test("creates required array if missing", () => {
@@ -53,38 +53,38 @@ describe("injectReasonField", () => {
53
53
  properties: { foo: { type: "string" } },
54
54
  }),
55
55
  ];
56
- const result = injectReasonField(defs);
56
+ const result = injectActivityField(defs);
57
57
  const schema = result[0].input_schema as Record<string, unknown>;
58
- expect(schema.required).toEqual(["reason"]);
58
+ expect(schema.required).toEqual(["activity"]);
59
59
  });
60
60
 
61
61
  test("skips tools in skip set (returns unchanged)", () => {
62
62
  const defs = [makeDef("bash"), makeDef("host_bash")];
63
- const result = injectReasonField(defs);
63
+ const result = injectActivityField(defs, new Set(["bash", "host_bash"]));
64
64
  // Should be the exact same object references
65
65
  expect(Object.is(result[0], defs[0])).toBe(true);
66
66
  expect(Object.is(result[1], defs[1])).toBe(true);
67
- // No reason injected
67
+ // No activity injected
68
68
  const schema0 = result[0].input_schema as Record<string, unknown>;
69
69
  const props0 = schema0.properties as Record<string, unknown>;
70
- expect("reason" in props0).toBe(false);
70
+ expect("activity" in props0).toBe(false);
71
71
  });
72
72
 
73
- test("skips tools that already have reason in properties", () => {
73
+ test("skips tools that already have activity in properties", () => {
74
74
  const defs = [
75
75
  makeDef("my_tool", {
76
76
  type: "object",
77
- properties: { reason: { type: "number" } },
77
+ properties: { activity: { type: "number" } },
78
78
  required: [],
79
79
  }),
80
80
  ];
81
- const result = injectReasonField(defs);
81
+ const result = injectActivityField(defs);
82
82
  // Should be the exact same object reference (no clone needed)
83
83
  expect(Object.is(result[0], defs[0])).toBe(true);
84
84
  const schema = result[0].input_schema as Record<string, unknown>;
85
85
  const props = schema.properties as Record<string, unknown>;
86
- // Original reason type preserved
87
- expect(props.reason).toEqual({ type: "number" });
86
+ // Original activity type preserved
87
+ expect(props.activity).toEqual({ type: "number" });
88
88
  });
89
89
 
90
90
  test("does NOT mutate original definition objects", () => {
@@ -97,10 +97,10 @@ describe("injectReasonField", () => {
97
97
  };
98
98
  const defs = [makeDef("my_tool", originalSchema)];
99
99
 
100
- const result = injectReasonField(defs);
100
+ const result = injectActivityField(defs);
101
101
 
102
102
  // Original properties object is untouched
103
- expect("reason" in originalProps).toBe(false);
103
+ expect("activity" in originalProps).toBe(false);
104
104
  // Original required array is untouched
105
105
  expect(originalRequired).toEqual(["foo"]);
106
106
  // Original schema properties ref is the same object
@@ -115,40 +115,40 @@ describe("injectReasonField", () => {
115
115
 
116
116
  test("passes through non-object schemas unchanged", () => {
117
117
  const defs = [makeDef("my_tool", { type: "string" })];
118
- const result = injectReasonField(defs);
118
+ const result = injectActivityField(defs);
119
119
  expect(Object.is(result[0], defs[0])).toBe(true);
120
120
  });
121
121
 
122
122
  test("passes through schemas without properties unchanged", () => {
123
123
  const defs = [makeDef("my_tool", { type: "object" })];
124
- const result = injectReasonField(defs);
124
+ const result = injectActivityField(defs);
125
125
  expect(Object.is(result[0], defs[0])).toBe(true);
126
126
  });
127
127
 
128
- test("skips tools with reason defined inside allOf member (composite schema)", () => {
128
+ test("skips tools with activity defined inside allOf member (composite schema)", () => {
129
129
  const defs = [
130
130
  makeDef("my_tool", {
131
131
  type: "object",
132
132
  properties: { foo: { type: "string" } },
133
133
  allOf: [
134
134
  {
135
- properties: { reason: { type: "string" } },
135
+ properties: { activity: { type: "string" } },
136
136
  },
137
137
  ],
138
138
  required: [],
139
139
  }),
140
140
  ];
141
- const result = injectReasonField(defs);
141
+ const result = injectActivityField(defs);
142
142
  // Should be the exact same object reference (no injection)
143
143
  expect(Object.is(result[0], defs[0])).toBe(true);
144
144
  const schema = result[0].input_schema as Record<string, unknown>;
145
145
  const props = schema.properties as Record<string, unknown>;
146
- // Top-level properties should NOT have reason injected
147
- expect("reason" in props).toBe(false);
146
+ // Top-level properties should NOT have activity injected
147
+ expect("activity" in props).toBe(false);
148
148
  });
149
149
 
150
150
  test("handles empty definitions array", () => {
151
- const result = injectReasonField([]);
151
+ const result = injectActivityField([]);
152
152
  expect(result).toEqual([]);
153
153
  });
154
154
  });
@@ -157,44 +157,44 @@ describe("schemaDefinesProperty", () => {
157
157
  test("returns true for direct properties match", () => {
158
158
  const schema = {
159
159
  type: "object",
160
- properties: { reason: { type: "string" } },
160
+ properties: { activity: { type: "string" } },
161
161
  };
162
- expect(schemaDefinesProperty(schema, "reason")).toBe(true);
162
+ expect(schemaDefinesProperty(schema, "activity")).toBe(true);
163
163
  });
164
164
 
165
165
  test("returns true for property in allOf member", () => {
166
166
  const schema = {
167
- allOf: [{ properties: { reason: { type: "string" } } }],
167
+ allOf: [{ properties: { activity: { type: "string" } } }],
168
168
  };
169
- expect(schemaDefinesProperty(schema, "reason")).toBe(true);
169
+ expect(schemaDefinesProperty(schema, "activity")).toBe(true);
170
170
  });
171
171
 
172
172
  test("returns true for property in oneOf member", () => {
173
173
  const schema = {
174
174
  oneOf: [
175
175
  { properties: { foo: { type: "string" } } },
176
- { properties: { reason: { type: "string" } } },
176
+ { properties: { activity: { type: "string" } } },
177
177
  ],
178
178
  };
179
- expect(schemaDefinesProperty(schema, "reason")).toBe(true);
179
+ expect(schemaDefinesProperty(schema, "activity")).toBe(true);
180
180
  });
181
181
 
182
182
  test("returns true for property in anyOf member", () => {
183
183
  const schema = {
184
- anyOf: [{ properties: { reason: { type: "string" } } }],
184
+ anyOf: [{ properties: { activity: { type: "string" } } }],
185
185
  };
186
- expect(schemaDefinesProperty(schema, "reason")).toBe(true);
186
+ expect(schemaDefinesProperty(schema, "activity")).toBe(true);
187
187
  });
188
188
 
189
189
  test("returns true for nested allOf within oneOf", () => {
190
190
  const schema = {
191
191
  oneOf: [
192
192
  {
193
- allOf: [{ properties: { reason: { type: "string" } } }],
193
+ allOf: [{ properties: { activity: { type: "string" } } }],
194
194
  },
195
195
  ],
196
196
  };
197
- expect(schemaDefinesProperty(schema, "reason")).toBe(true);
197
+ expect(schemaDefinesProperty(schema, "activity")).toBe(true);
198
198
  });
199
199
 
200
200
  test("returns false when property not defined", () => {
@@ -202,25 +202,85 @@ describe("schemaDefinesProperty", () => {
202
202
  type: "object",
203
203
  properties: { foo: { type: "string" } },
204
204
  };
205
- expect(schemaDefinesProperty(schema, "reason")).toBe(false);
205
+ expect(schemaDefinesProperty(schema, "activity")).toBe(false);
206
206
  });
207
207
 
208
- test("returns false for $ref (fail-closed)", () => {
208
+ test("returns false for $ref with default behavior (fail-closed)", () => {
209
209
  const schema = { $ref: "#/definitions/Foo" };
210
- expect(schemaDefinesProperty(schema, "reason")).toBe(false);
210
+ expect(schemaDefinesProperty(schema, "activity")).toBe(false);
211
+ });
212
+
213
+ test("returns true for $ref with assume-defined behavior (fail-open)", () => {
214
+ const schema = { $ref: "#/definitions/Foo" };
215
+ expect(
216
+ schemaDefinesProperty(schema, "activity", {
217
+ refBehavior: "assume-defined",
218
+ }),
219
+ ).toBe(true);
220
+ });
221
+
222
+ test("returns true for $ref nested in allOf with assume-defined refBehavior", () => {
223
+ const schema = {
224
+ allOf: [{ $ref: "#/definitions/Foo" }],
225
+ };
226
+ expect(
227
+ schemaDefinesProperty(schema, "activity", {
228
+ refBehavior: "assume-defined",
229
+ }),
230
+ ).toBe(true);
231
+ });
232
+
233
+ test("returns false for $ref nested in allOf with assume-undefined refBehavior", () => {
234
+ const schema = {
235
+ allOf: [{ $ref: "#/definitions/Foo" }],
236
+ };
237
+ expect(
238
+ schemaDefinesProperty(schema, "activity", {
239
+ refBehavior: "assume-undefined",
240
+ }),
241
+ ).toBe(false);
242
+ });
243
+
244
+ test("returns true for $ref nested in oneOf with assume-defined refBehavior", () => {
245
+ const schema = {
246
+ oneOf: [{ $ref: "#/definitions/Foo" }],
247
+ };
248
+ expect(
249
+ schemaDefinesProperty(schema, "activity", {
250
+ refBehavior: "assume-defined",
251
+ }),
252
+ ).toBe(true);
253
+ });
254
+
255
+ test("returns true for $ref nested in anyOf with assume-defined refBehavior", () => {
256
+ const schema = {
257
+ anyOf: [{ $ref: "#/definitions/Foo" }],
258
+ };
259
+ expect(
260
+ schemaDefinesProperty(schema, "activity", {
261
+ refBehavior: "assume-defined",
262
+ }),
263
+ ).toBe(true);
264
+ });
265
+
266
+ test("returns false for $ref nested in allOf with default refBehavior (fail-closed)", () => {
267
+ const schema = {
268
+ allOf: [{ $ref: "#/definitions/Foo" }],
269
+ };
270
+ expect(schemaDefinesProperty(schema, "activity")).toBe(false);
211
271
  });
212
272
 
213
273
  test("returns false for null schema", () => {
214
- expect(schemaDefinesProperty(null, "reason")).toBe(false);
274
+ expect(schemaDefinesProperty(null, "activity")).toBe(false);
215
275
  });
216
276
 
217
277
  test("returns false for undefined schema", () => {
218
- expect(schemaDefinesProperty(undefined, "reason")).toBe(false);
278
+ expect(schemaDefinesProperty(undefined, "activity")).toBe(false);
219
279
  });
220
280
 
221
281
  test("returns false for non-object schema", () => {
222
- expect(schemaDefinesProperty("not-an-object", "reason")).toBe(false);
223
- expect(schemaDefinesProperty(42, "reason")).toBe(false);
224
- expect(schemaDefinesProperty(true, "reason")).toBe(false);
282
+ expect(schemaDefinesProperty("not-an-object", "activity")).toBe(false);
283
+ expect(schemaDefinesProperty(42, "activity")).toBe(false);
284
+ expect(schemaDefinesProperty(true, "activity")).toBe(false);
225
285
  });
226
286
  });
@@ -14,9 +14,29 @@ const mockConfig = {
14
14
  timeouts: { permissionTimeoutSec: 300 },
15
15
  };
16
16
 
17
+ function setMockNestedValue(
18
+ obj: Record<string, unknown>,
19
+ path: string,
20
+ value: unknown,
21
+ ): void {
22
+ const keys = path.split(".");
23
+ let current = obj;
24
+ for (let i = 0; i < keys.length - 1; i++) {
25
+ const key = keys[i];
26
+ if (current[key] == null || typeof current[key] !== "object") {
27
+ current[key] = {};
28
+ }
29
+ current = current[key] as Record<string, unknown>;
30
+ }
31
+ current[keys[keys.length - 1]] = value;
32
+ }
33
+
17
34
  mock.module("../config/loader.js", () => ({
18
35
  getConfig: () => mockConfig,
19
36
  loadConfig: () => mockConfig,
37
+ loadRawConfig: () => ({}),
38
+ saveRawConfig: () => {},
39
+ setNestedValue: setMockNestedValue,
20
40
  invalidateConfigCache: () => {},
21
41
  }));
22
42
 
@@ -24,9 +24,12 @@ const mockConfig = {
24
24
  "image-generation": {
25
25
  mode: "your-own" as const,
26
26
  provider: "gemini",
27
- model: "gemini-2.5-flash-image",
27
+ model: "gemini-3.1-flash-image-preview",
28
+ },
29
+ "web-search": {
30
+ mode: "your-own" as const,
31
+ provider: "inference-provider-native",
28
32
  },
29
- "web-search": { mode: "your-own" as const, provider: "anthropic-native" },
30
33
  },
31
34
  };
32
35
 
@@ -20,8 +20,7 @@ const mockConfig = {
20
20
  shellMaxTimeoutSec: 600,
21
21
  permissionTimeoutSec: 300,
22
22
  },
23
- sandbox: { enabled: false },
24
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
23
+ rateLimit: { maxRequestsPerMinute: 0 },
25
24
  secretDetection: {
26
25
  enabled: true,
27
26
  action: "warn" as "redact" | "warn" | "block",