@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
@@ -201,18 +201,18 @@ describe("Permission Checker", () => {
201
201
  });
202
202
  });
203
203
 
204
- // file_write is always medium
204
+ // file_write is always low (sandboxed)
205
205
  describe("file_write", () => {
206
- test("file_write is always medium risk", async () => {
206
+ test("file_write is always low risk", async () => {
207
207
  const risk = await classifyRisk("file_write", {
208
208
  path: "/tmp/file.txt",
209
209
  });
210
- expect(risk).toBe(RiskLevel.Medium);
210
+ expect(risk).toBe(RiskLevel.Low);
211
211
  });
212
212
 
213
- test("file_write with any path is medium risk", async () => {
213
+ test("file_write with any path is low risk", async () => {
214
214
  const risk = await classifyRisk("file_write", { path: "/etc/passwd" });
215
- expect(risk).toBe(RiskLevel.Medium);
215
+ expect(risk).toBe(RiskLevel.Low);
216
216
  });
217
217
  });
218
218
 
@@ -673,23 +673,22 @@ describe("Permission Checker", () => {
673
673
  expect(result.decision).toBe("allow");
674
674
  });
675
675
 
676
- test("file_write within workspace with no rule prompt (medium risk bypasses workspace auto-allow)", async () => {
676
+ test("file_write → auto-allow (workspace-scoped)", async () => {
677
677
  const result = await check(
678
678
  "file_write",
679
679
  { path: "/tmp/file.txt" },
680
680
  "/tmp",
681
681
  );
682
- expect(result.decision).toBe("prompt");
683
- expect(result.reason).toContain("medium risk");
682
+ expect(result.decision).toBe("allow");
684
683
  });
685
684
 
686
- test("file_write outside workspace with no rule → prompt", async () => {
685
+ test("file_write outside workspace auto-allow (Low risk)", async () => {
687
686
  const result = await check(
688
687
  "file_write",
689
688
  { path: "/etc/some-file.txt" },
690
689
  "/tmp",
691
690
  );
692
- expect(result.decision).toBe("prompt");
691
+ expect(result.decision).toBe("allow");
693
692
  });
694
693
 
695
694
  test("file_write with matching rule → allow", async () => {
@@ -1486,12 +1485,12 @@ describe("Permission Checker", () => {
1486
1485
  expect(result.matchedRule!.id).toBe("default:allow-file_edit-updates");
1487
1486
  });
1488
1487
 
1489
- test("file_write of non-workspace file is not auto-allowed", async () => {
1488
+ test("file_write of non-workspace file is auto-allowed (Low risk)", async () => {
1490
1489
  const otherPath = join(checkerTestDir, "workspace", "OTHER.md");
1491
1490
  // Use a workingDir that doesn't contain the path so it's not workspace-scoped
1492
1491
  const result = await check("file_write", { path: otherPath }, "/home");
1493
- // Medium risk with no matching allow rule → prompt
1494
- expect(result.decision).toBe("prompt");
1492
+ // Low risk auto-allowed even outside workspace
1493
+ expect(result.decision).toBe("allow");
1495
1494
  });
1496
1495
  });
1497
1496
 
@@ -2096,16 +2095,16 @@ describe("Permission Checker", () => {
2096
2095
  expect(risk).toBe(RiskLevel.High);
2097
2096
  });
2098
2097
 
2099
- test("file_write to non-skill path remains Medium risk", async () => {
2098
+ test("file_write to non-skill path is Low risk", async () => {
2100
2099
  const normalPath = "/tmp/some-file.txt";
2101
2100
  const risk = await classifyRisk("file_write", { path: normalPath });
2102
- expect(risk).toBe(RiskLevel.Medium);
2101
+ expect(risk).toBe(RiskLevel.Low);
2103
2102
  });
2104
2103
 
2105
- test("file_edit of non-skill path remains Medium risk", async () => {
2104
+ test("file_edit of non-skill path is Low risk", async () => {
2106
2105
  const normalPath = "/tmp/some-file.txt";
2107
2106
  const risk = await classifyRisk("file_edit", { path: normalPath });
2108
- expect(risk).toBe(RiskLevel.Medium);
2107
+ expect(risk).toBe(RiskLevel.Low);
2109
2108
  });
2110
2109
 
2111
2110
  test("host_file_write to non-skill path remains Medium risk (via registry)", async () => {
@@ -2557,12 +2556,12 @@ describe("Permission Checker", () => {
2557
2556
  expect(result.reason).toContain("requires approval");
2558
2557
  });
2559
2558
 
2560
- test("strict mode: file_write to non-skill path prompts as Strict mode (not High risk)", async () => {
2559
+ test("strict mode: file_write to non-skill path prompts as Strict mode", async () => {
2561
2560
  testConfig.permissions.mode = "strict";
2562
2561
  const normalPath = "/tmp/some-file.txt";
2563
2562
  const result = await check("file_write", { path: normalPath }, "/tmp");
2564
2563
  expect(result.decision).toBe("prompt");
2565
- // Medium-risk file_write in strict mode with no rule → Strict mode reason
2564
+ // Low-risk file_write in strict mode with no rule → Strict mode reason
2566
2565
  expect(result.reason).toContain("Strict mode");
2567
2566
  });
2568
2567
 
@@ -2865,7 +2864,7 @@ describe("Permission Checker", () => {
2865
2864
  const symlinkedFile = join(symDir, "config.json");
2866
2865
  const realFileResolved = join(realDirResolved, "config.json");
2867
2866
 
2868
- // file_write is Medium risk — needs a matching rule to allow.
2867
+ // file_write is Low risk — but add a rule to verify symlink path matching.
2869
2868
  addRule("file_write", `file_write:${realFileResolved}`, "everywhere");
2870
2869
  const result = await check(
2871
2870
  "file_write",
@@ -3597,7 +3596,7 @@ describe("Permission Checker", () => {
3597
3596
  expect(result.matchedRule!.pattern).toBe("skill_load:*");
3598
3597
  });
3599
3598
 
3600
- test("medium-risk file_write with no rule prompts in strict mode", async () => {
3599
+ test("low-risk file_write with no rule prompts in strict mode", async () => {
3601
3600
  testConfig.permissions.mode = "strict";
3602
3601
  const result = await check(
3603
3602
  "file_write",
@@ -3996,14 +3995,14 @@ describe("Permission Checker", () => {
3996
3995
  );
3997
3996
 
3998
3997
  test(
3999
- "file_write to non-extra dir remains Medium when extra dirs are configured",
3998
+ "file_write to non-extra dir remains Low when extra dirs are configured",
4000
3999
  withExtraDirs(async () => {
4001
4000
  const risk = await classifyRisk(
4002
4001
  "file_write",
4003
4002
  { path: "/tmp/unrelated.txt" },
4004
4003
  "/tmp",
4005
4004
  );
4006
- expect(risk).toBe(RiskLevel.Medium);
4005
+ expect(risk).toBe(RiskLevel.Low);
4007
4006
  }),
4008
4007
  );
4009
4008
 
@@ -4583,24 +4582,24 @@ describe("workspace mode — auto-allow workspace-scoped operations", () => {
4583
4582
  expect(result.reason).toContain("Workspace mode");
4584
4583
  });
4585
4584
 
4586
- test("file_write within workspace → prompt (Medium risk bypasses workspace auto-allow)", async () => {
4585
+ test("file_write within workspace → allow (workspace-scoped)", async () => {
4587
4586
  const result = await check(
4588
4587
  "file_write",
4589
4588
  { file_path: "/home/user/my-project/src/index.ts" },
4590
4589
  workspaceDir,
4591
4590
  );
4592
- expect(result.decision).toBe("prompt");
4593
- expect(result.reason).toContain("medium risk");
4591
+ expect(result.decision).toBe("allow");
4592
+ expect(result.reason).toContain("Workspace mode");
4594
4593
  });
4595
4594
 
4596
- test("file_edit within workspace → prompt (Medium risk bypasses workspace auto-allow)", async () => {
4595
+ test("file_edit within workspace → allow (workspace-scoped)", async () => {
4597
4596
  const result = await check(
4598
4597
  "file_edit",
4599
4598
  { file_path: "/home/user/my-project/src/index.ts" },
4600
4599
  workspaceDir,
4601
4600
  );
4602
- expect(result.decision).toBe("prompt");
4603
- expect(result.reason).toContain("medium risk");
4601
+ expect(result.decision).toBe("allow");
4602
+ expect(result.reason).toContain("Workspace mode");
4604
4603
  });
4605
4604
 
4606
4605
  // ── file operations outside workspace follow risk-based fallback ──
@@ -4615,14 +4614,14 @@ describe("workspace mode — auto-allow workspace-scoped operations", () => {
4615
4614
  expect(result.reason).toContain("Low risk");
4616
4615
  });
4617
4616
 
4618
- test("file_write outside workspace → prompt (Medium risk fallback)", async () => {
4617
+ test("file_write outside workspace → allow (Low risk fallback)", async () => {
4619
4618
  const result = await check(
4620
4619
  "file_write",
4621
4620
  { file_path: "/tmp/outside.txt" },
4622
4621
  workspaceDir,
4623
4622
  );
4624
- expect(result.decision).toBe("prompt");
4625
- expect(result.reason).toContain("risk");
4623
+ expect(result.decision).toBe("allow");
4624
+ expect(result.reason).toContain("Low risk");
4626
4625
  });
4627
4626
 
4628
4627
  // ── bash (sandbox) — default rule matches, workspace mode not reached ──
@@ -145,21 +145,37 @@ describe("ensureChromeWithCdp", () => {
145
145
  throw new Error("Connection refused");
146
146
  };
147
147
 
148
- const session = await ensureChromeWithCdp({
149
- startUrl: "https://example.com/",
150
- });
151
- expect(session.launchedByUs).toBe(true);
152
- expect(session.baseUrl).toBe("http://localhost:9222");
153
- expect(spawnMock).toHaveBeenCalledTimes(1);
154
-
155
- // Verify spawn was called with the right Chrome path and args
156
- const spawnArgs = spawnMock.mock.calls[0] as unknown as [string, string[]];
157
- expect(spawnArgs[0]).toContain("Google Chrome");
158
- const flags = spawnArgs[1];
159
- expect(flags).toContain("--remote-debugging-port=9222");
160
- expect(flags).toContain("--force-renderer-accessibility");
161
- expect(flags.some((f: string) => f.includes("Chrome-CDP"))).toBe(true);
162
- expect(flags).toContain("https://example.com/");
148
+ // Replace setTimeout with a zero-delay version to skip retry waits
149
+ const origSetTimeout = globalThis.setTimeout;
150
+ globalThis.setTimeout = ((
151
+ fn: TimerHandler,
152
+ _ms?: number,
153
+ ...args: unknown[]
154
+ ) => {
155
+ return origSetTimeout(fn, 0, ...args);
156
+ }) as typeof setTimeout;
157
+ try {
158
+ const session = await ensureChromeWithCdp({
159
+ startUrl: "https://example.com/",
160
+ });
161
+ expect(session.launchedByUs).toBe(true);
162
+ expect(session.baseUrl).toBe("http://localhost:9222");
163
+ expect(spawnMock).toHaveBeenCalledTimes(1);
164
+
165
+ // Verify spawn was called with the right Chrome path and args
166
+ const spawnArgs = spawnMock.mock.calls[0] as unknown as [
167
+ string,
168
+ string[],
169
+ ];
170
+ expect(spawnArgs[0]).toContain("Google Chrome");
171
+ const flags = spawnArgs[1];
172
+ expect(flags).toContain("--remote-debugging-port=9222");
173
+ expect(flags).toContain("--force-renderer-accessibility");
174
+ expect(flags.some((f: string) => f.includes("Chrome-CDP"))).toBe(true);
175
+ expect(flags).toContain("https://example.com/");
176
+ } finally {
177
+ globalThis.setTimeout = origSetTimeout;
178
+ }
163
179
  });
164
180
 
165
181
  test("uses custom port when specified", async () => {
@@ -182,9 +198,22 @@ describe("ensureChromeWithCdp", () => {
182
198
  throw new Error("Connection refused");
183
199
  };
184
200
 
185
- const promise = ensureChromeWithCdp();
186
- await expect(promise).rejects.toThrow("CDP endpoint not responding");
187
- }, 20_000);
201
+ // Replace setTimeout with a zero-delay version to skip retry waits
202
+ const origSetTimeout = globalThis.setTimeout;
203
+ globalThis.setTimeout = ((
204
+ fn: TimerHandler,
205
+ _ms?: number,
206
+ ...args: unknown[]
207
+ ) => {
208
+ return origSetTimeout(fn, 0, ...args);
209
+ }) as typeof setTimeout;
210
+ try {
211
+ const promise = ensureChromeWithCdp();
212
+ await expect(promise).rejects.toThrow("CDP endpoint not responding");
213
+ } finally {
214
+ globalThis.setTimeout = origSetTimeout;
215
+ }
216
+ });
188
217
  });
189
218
 
190
219
  // ---------------------------------------------------------------------------
@@ -38,9 +38,9 @@ mock.module("../config/loader.js", () => ({
38
38
  "image-generation": {
39
39
  mode: "your-own",
40
40
  provider: "gemini",
41
- model: "gemini-2.5-flash-image",
41
+ model: "gemini-3.1-flash-image-preview",
42
42
  },
43
- "web-search": { mode: "your-own", provider: "anthropic-native" },
43
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
44
44
  },
45
45
  }),
46
46
  loadConfig: () => ({}),
@@ -76,9 +76,9 @@ mock.module("../config/loader.js", () => ({
76
76
  "image-generation": {
77
77
  mode: "your-own",
78
78
  provider: "gemini",
79
- model: "gemini-2.5-flash-image",
79
+ model: "gemini-3.1-flash-image-preview",
80
80
  },
81
- "web-search": { mode: "your-own", provider: "anthropic-native" },
81
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
82
82
  },
83
83
  }),
84
84
  loadConfig: () => ({}),
@@ -93,10 +93,12 @@ describe("AssistantConfigSchema", () => {
93
93
  expect(result.services.inference.mode).toBe("your-own");
94
94
  expect(result.services["image-generation"].provider).toBe("gemini");
95
95
  expect(result.services["image-generation"].model).toBe(
96
- "gemini-2.5-flash-image",
96
+ "gemini-3.1-flash-image-preview",
97
97
  );
98
98
  expect(result.services["image-generation"].mode).toBe("your-own");
99
- expect(result.services["web-search"].provider).toBe("anthropic-native");
99
+ expect(result.services["web-search"].provider).toBe(
100
+ "inference-provider-native",
101
+ );
100
102
  expect(result.services["web-search"].mode).toBe("your-own");
101
103
  expect(result.maxTokens).toBe(16000);
102
104
  expect(result.thinking).toEqual({
@@ -125,11 +127,10 @@ describe("AssistantConfigSchema", () => {
125
127
  providerStreamTimeoutSec: 300,
126
128
  });
127
129
  expect(result.sandbox).toEqual({
128
- enabled: true,
130
+ enabled: false,
129
131
  });
130
132
  expect(result.rateLimit).toEqual({
131
133
  maxRequestsPerMinute: 0,
132
- maxTokensPerSession: 0,
133
134
  });
134
135
  expect(result.secretDetection).toEqual({
135
136
  enabled: true,
@@ -153,8 +154,7 @@ describe("AssistantConfigSchema", () => {
153
154
  shellMaxTimeoutSec: 300,
154
155
  permissionTimeoutSec: 60,
155
156
  },
156
- sandbox: { enabled: true },
157
- rateLimit: { maxRequestsPerMinute: 10, maxTokensPerSession: 100000 },
157
+ rateLimit: { maxRequestsPerMinute: 10 },
158
158
  secretDetection: {
159
159
  enabled: false,
160
160
  action: "block" as const,
@@ -351,13 +351,6 @@ describe("AssistantConfigSchema", () => {
351
351
  expect(result.success).toBe(false);
352
352
  });
353
353
 
354
- test("rejects non-integer rateLimit values", () => {
355
- const result = AssistantConfigSchema.safeParse({
356
- rateLimit: { maxTokensPerSession: 3.5 },
357
- });
358
- expect(result.success).toBe(false);
359
- });
360
-
361
354
  test("rejects negative auditLog.retentionDays", () => {
362
355
  const result = AssistantConfigSchema.safeParse({
363
356
  auditLog: { retentionDays: -7 },
@@ -376,11 +369,10 @@ describe("AssistantConfigSchema", () => {
376
369
 
377
370
  test("accepts zero for non-negative fields", () => {
378
371
  const result = AssistantConfigSchema.parse({
379
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
372
+ rateLimit: { maxRequestsPerMinute: 0 },
380
373
  auditLog: { retentionDays: 0 },
381
374
  });
382
375
  expect(result.rateLimit.maxRequestsPerMinute).toBe(0);
383
- expect(result.rateLimit.maxTokensPerSession).toBe(0);
384
376
  expect(result.auditLog.retentionDays).toBe(0);
385
377
  });
386
378
 
@@ -1105,7 +1097,7 @@ describe("loadConfig with schema validation", () => {
1105
1097
  test("falls back for invalid sandbox.enabled", () => {
1106
1098
  writeConfig({ sandbox: { enabled: "yes" } });
1107
1099
  const config = loadConfig();
1108
- expect(config.sandbox.enabled).toBe(true);
1100
+ expect(config.sandbox.enabled).toBe(false);
1109
1101
  });
1110
1102
 
1111
1103
  test("loads sandbox with only enabled field", () => {
@@ -1132,11 +1124,10 @@ describe("loadConfig with schema validation", () => {
1132
1124
 
1133
1125
  test("falls back for invalid rateLimit values", () => {
1134
1126
  writeConfig({
1135
- rateLimit: { maxRequestsPerMinute: -1, maxTokensPerSession: 3.5 },
1127
+ rateLimit: { maxRequestsPerMinute: -1 },
1136
1128
  });
1137
1129
  const config = loadConfig();
1138
1130
  expect(config.rateLimit.maxRequestsPerMinute).toBe(0);
1139
- expect(config.rateLimit.maxTokensPerSession).toBe(0);
1140
1131
  });
1141
1132
 
1142
1133
  test("falls back for invalid auditLog.retentionDays", () => {
@@ -175,7 +175,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
175
175
  expect(emittedSignals).toHaveLength(1);
176
176
  expect(emittedSignals[0].sourceEventName).toBe("guardian.question");
177
177
  expect(emittedSignals[0].sourceChannel).toBe("telegram");
178
- expect(emittedSignals[0].sourceSessionId).toBe("conv-1");
178
+ expect(emittedSignals[0].sourceContextId).toBe("conv-1");
179
179
 
180
180
  const payload = emittedSignals[0].contextPayload as Record<string, unknown>;
181
181
  expect(payload.requestId).toBe(canonicalRequest.id);
@@ -47,7 +47,7 @@ mock.module("../config/loader.js", () => ({
47
47
  nonInteractiveLatestTurnCompression: "truncate",
48
48
  },
49
49
  },
50
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
50
+ rateLimit: { maxRequestsPerMinute: 0 },
51
51
  daemon: {
52
52
  startupSocketWaitMs: 5000,
53
53
  stopTimeoutMs: 5000,
@@ -64,9 +64,9 @@ mock.module("../config/loader.js", () => ({
64
64
  "image-generation": {
65
65
  mode: "your-own",
66
66
  provider: "gemini",
67
- model: "gemini-2.5-flash-image",
67
+ model: "gemini-3.1-flash-image-preview",
68
68
  },
69
- "web-search": { mode: "your-own", provider: "anthropic-native" },
69
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
70
70
  },
71
71
  }),
72
72
  loadRawConfig: () => ({}),
@@ -133,7 +133,7 @@ mock.module("../memory/retriever.js", () => ({
133
133
  injectedTokens: 0,
134
134
  latencyMs: 0,
135
135
  }),
136
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
136
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
137
137
  }));
138
138
 
139
139
  mock.module("../context/window-manager.js", () => ({
@@ -50,7 +50,7 @@ mock.module("../config/loader.js", () => ({
50
50
  nonInteractiveLatestTurnCompression: "truncate",
51
51
  },
52
52
  },
53
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
53
+ rateLimit: { maxRequestsPerMinute: 0 },
54
54
  workspaceGit: { turnCommitMaxWaitMs: 10 },
55
55
  ui: {},
56
56
  }),
@@ -171,7 +171,7 @@ mock.module("../memory/retriever.js", () => ({
171
171
  injectedTokens: 0,
172
172
  latencyMs: 0,
173
173
  }),
174
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
174
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
175
175
  }));
176
176
 
177
177
  mock.module("../memory/app-store.js", () => ({
@@ -38,7 +38,7 @@ mock.module("../config/loader.js", () => ({
38
38
  nonInteractiveLatestTurnCompression: "truncate",
39
39
  },
40
40
  },
41
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
41
+ rateLimit: { maxRequestsPerMinute: 0 },
42
42
  workspaceGit: { turnCommitMaxWaitMs: 10 },
43
43
  ui: {},
44
44
  }),
@@ -154,7 +154,7 @@ mock.module("../memory/retriever.js", () => ({
154
154
  injectedTokens: 0,
155
155
  latencyMs: 0,
156
156
  }),
157
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
157
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
158
158
  }));
159
159
 
160
160
  mock.module("../memory/app-store.js", () => ({
@@ -1834,11 +1834,18 @@ describe("session-agent-loop", () => {
1834
1834
  const ctx = makeCtx({ agentLoopRun });
1835
1835
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
1836
1836
 
1837
- // The providerErrorUserMessage should trigger a synthesized assistant_text_delta
1837
+ // The error should be sent as a conversation_error (not as an
1838
+ // assistant_text_delta, which would cause duplicate text rendering
1839
+ // alongside the InlineChatErrorAlert card).
1838
1840
  const textDeltas = events.filter(
1839
1841
  (e) => e.type === "assistant_text_delta",
1840
1842
  );
1841
- expect(textDeltas.length).toBeGreaterThanOrEqual(1);
1843
+ expect(textDeltas).toHaveLength(0);
1844
+
1845
+ const conversationErrors = events.filter(
1846
+ (e) => e.type === "conversation_error",
1847
+ );
1848
+ expect(conversationErrors.length).toBeGreaterThanOrEqual(1);
1842
1849
  });
1843
1850
  });
1844
1851
  });
@@ -32,7 +32,7 @@ mock.module("../config/loader.js", () => ({
32
32
  model: "test",
33
33
  provider: "test",
34
34
  memory: { enabled: false },
35
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
35
+ rateLimit: { maxRequestsPerMinute: 0 },
36
36
  }),
37
37
  }));
38
38
 
@@ -78,7 +78,7 @@ mock.module("../config/loader.js", () => ({
78
78
  summaryModel: "mock-model",
79
79
  maxSummaryTokens: 512,
80
80
  },
81
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
81
+ rateLimit: { maxRequestsPerMinute: 0 },
82
82
  timeouts: { permissionTimeoutSec: 1 },
83
83
  skills: { entries: {}, allowBundled: true },
84
84
  permissions: { mode: "workspace" },
@@ -158,7 +158,7 @@ mock.module("../memory/retriever.js", () => ({
158
158
  injectedTokens: 0,
159
159
  latencyMs: 0,
160
160
  }),
161
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
161
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
162
162
  }));
163
163
 
164
164
  mock.module("../context/window-manager.js", () => ({
@@ -66,6 +66,8 @@ describe("classifyConversationError", () => {
66
66
  "ETIMEDOUT",
67
67
  "ENOTFOUND",
68
68
  "socket hang up",
69
+ "The socket connection was closed unexpectedly",
70
+ "Anthropic request failed: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()",
69
71
  "fetch failed",
70
72
  "Connection refused by server",
71
73
  "connection reset",
@@ -276,6 +278,37 @@ describe("classifyConversationError", () => {
276
278
  });
277
279
  });
278
280
 
281
+ describe("streaming corruption errors", () => {
282
+ const cases = [
283
+ "Unexpected event order, got message_start before receiving message_stop",
284
+ "Anthropic request failed: Unexpected event order, got message_start before receiving \"message_stop\"",
285
+ "stream ended without producing a Message",
286
+ "request ended without sending any chunks",
287
+ "stream has ended, this shouldn't happen",
288
+ ];
289
+
290
+ for (const msg of cases) {
291
+ it(`classifies "${msg}" as PROVIDER_API (retryable)`, () => {
292
+ const result = classifyConversationError(new Error(msg), baseCtx);
293
+ expect(result.code).toBe("PROVIDER_API");
294
+ expect(result.retryable).toBe(true);
295
+ expect(result.userMessage).toContain("interrupted");
296
+ expect(result.errorCategory).toBe("stream_corruption");
297
+ });
298
+ }
299
+
300
+ it("classifies ProviderError without statusCode with streaming message as PROVIDER_API", () => {
301
+ const err = new ProviderError(
302
+ "Unexpected event order, got message_start before receiving message_stop",
303
+ "anthropic",
304
+ );
305
+ const result = classifyConversationError(err, baseCtx);
306
+ expect(result.code).toBe("PROVIDER_API");
307
+ expect(result.retryable).toBe(true);
308
+ expect(result.errorCategory).toBe("stream_corruption");
309
+ });
310
+ });
311
+
279
312
  describe("abort/cancel errors (non-user-initiated)", () => {
280
313
  it('classifies "aborted" as CONVERSATION_ABORTED', () => {
281
314
  const result = classifyConversationError(
@@ -136,7 +136,6 @@ mock.module("../util/logger.js", () => ({
136
136
  const mockConfig = {
137
137
  model: "mock-model",
138
138
  provider: "mock",
139
- sandbox: { enabled: false, backend: "native" },
140
139
  contextWindow: {
141
140
  enabled: true,
142
141
  maxInputTokens: 180000,
@@ -31,7 +31,7 @@ mock.module("../config/loader.js", () => ({
31
31
  summaryModel: "mock-model",
32
32
  maxSummaryTokens: 512,
33
33
  },
34
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
34
+ rateLimit: { maxRequestsPerMinute: 0 },
35
35
  }),
36
36
  loadRawConfig: () => ({}),
37
37
  saveRawConfig: () => {},
@@ -114,7 +114,7 @@ function makeSignal(
114
114
  signalId: "sig-test",
115
115
  createdAt: Date.now(),
116
116
  sourceChannel: "scheduler",
117
- sourceSessionId: "sess-1",
117
+ sourceContextId: "sess-1",
118
118
  sourceEventName: "test.event",
119
119
  contextPayload: {},
120
120
  attentionHints: {
@@ -45,7 +45,7 @@ mock.module("../config/loader.js", () => ({
45
45
  nonInteractiveLatestTurnCompression: "truncate",
46
46
  },
47
47
  },
48
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
48
+ rateLimit: { maxRequestsPerMinute: 0 },
49
49
  daemon: {
50
50
  startupSocketWaitMs: 5000,
51
51
  stopTimeoutMs: 5000,
@@ -62,9 +62,9 @@ mock.module("../config/loader.js", () => ({
62
62
  "image-generation": {
63
63
  mode: "your-own",
64
64
  provider: "gemini",
65
- model: "gemini-2.5-flash-image",
65
+ model: "gemini-3.1-flash-image-preview",
66
66
  },
67
- "web-search": { mode: "your-own", provider: "anthropic-native" },
67
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
68
68
  },
69
69
  }),
70
70
  loadRawConfig: () => ({}),
@@ -123,7 +123,7 @@ mock.module("../memory/retriever.js", () => ({
123
123
  injectedTokens: 0,
124
124
  latencyMs: 0,
125
125
  }),
126
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
126
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
127
127
  }));
128
128
 
129
129
  // Mock AgentLoop to capture the messages it receives
@@ -45,7 +45,7 @@ mock.module("../config/loader.js", () => ({
45
45
  nonInteractiveLatestTurnCompression: "truncate",
46
46
  },
47
47
  },
48
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
48
+ rateLimit: { maxRequestsPerMinute: 0 },
49
49
  services: {
50
50
  inference: {
51
51
  mode: "your-own",
@@ -55,9 +55,9 @@ mock.module("../config/loader.js", () => ({
55
55
  "image-generation": {
56
56
  mode: "your-own",
57
57
  provider: "gemini",
58
- model: "gemini-2.5-flash-image",
58
+ model: "gemini-3.1-flash-image-preview",
59
59
  },
60
- "web-search": { mode: "your-own", provider: "anthropic-native" },
60
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
61
61
  },
62
62
  }),
63
63
  loadRawConfig: () => ({}),
@@ -185,7 +185,7 @@ mock.module("../memory/retriever.js", () => ({
185
185
  injectedTokens: 0,
186
186
  latencyMs: 0,
187
187
  }),
188
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
188
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
189
189
  }));
190
190
 
191
191
  let maybeCompactCalls: Array<{ force: boolean }> = [];