@vellumai/assistant 0.12.4 → 0.12.5-dev.202609251017.e467a6c

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 (281) hide show
  1. package/docs/architecture/memory.md +9 -1
  2. package/docs/architecture/scheduling.md +13 -5
  3. package/docs/desktop-browser-cli.md +3 -2
  4. package/docs/guardian-request-flow.md +1 -1
  5. package/node_modules/@vellumai/avatar-manifest/src/__tests__/notification-avatar.test.ts +48 -4
  6. package/node_modules/@vellumai/avatar-manifest/src/notification-avatar.ts +12 -8
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/guardian-requests.test.ts +26 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +77 -3
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/guardian-requests.test.ts +26 -0
  10. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +77 -3
  11. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +2 -4
  12. package/node_modules/@vellumai/service-contracts/src/__tests__/guardian-requests.test.ts +26 -0
  13. package/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +77 -3
  14. package/openapi.yaml +152 -3
  15. package/package.json +1 -1
  16. package/src/__tests__/agent-loop.test.ts +76 -0
  17. package/src/__tests__/anthropic-error-formatting.test.ts +9 -0
  18. package/src/__tests__/background-shell-bash.test.ts +34 -0
  19. package/src/__tests__/background-shell-host-bash.test.ts +98 -1
  20. package/src/__tests__/background-tool-registry.test.ts +39 -0
  21. package/src/__tests__/channel-readiness-discord.test.ts +8 -0
  22. package/src/__tests__/channel-readiness-slack-remote.test.ts +8 -0
  23. package/src/__tests__/client-os-metadata-persistence.test.ts +4 -20
  24. package/src/__tests__/conversation-agent-loop.test.ts +529 -1
  25. package/src/__tests__/conversation-error.test.ts +18 -0
  26. package/src/__tests__/conversation-pairing.test.ts +69 -0
  27. package/src/__tests__/conversation-queue.test.ts +257 -4
  28. package/src/__tests__/conversation-runtime-assembly.test.ts +27 -0
  29. package/src/__tests__/emit-signal-routing-intent.test.ts +62 -0
  30. package/src/__tests__/evict-conversations-for-reload.test.ts +21 -0
  31. package/src/__tests__/gemini-provider.test.ts +9 -0
  32. package/src/__tests__/host-cu-proxy.test.ts +79 -0
  33. package/src/__tests__/host-cu-routes-targeted.test.ts +14 -5
  34. package/src/__tests__/introduction-card-resolver.test.ts +53 -8
  35. package/src/__tests__/job-handler-registration-guard.test.ts +1 -0
  36. package/src/__tests__/non-member-access-request.test.ts +48 -0
  37. package/src/__tests__/notification-events-source-context-probe.test.ts +48 -1
  38. package/src/__tests__/notification-platform-adapter.test.ts +97 -0
  39. package/src/__tests__/notification-vellum-adapter.test.ts +103 -3
  40. package/src/__tests__/platform-callback-registration.test.ts +46 -1
  41. package/src/__tests__/plugin-import-boundary-guard.test.ts +3 -0
  42. package/src/__tests__/scaffold-managed-skill-tool.test.ts +116 -66
  43. package/src/__tests__/scheduler-disk-pressure.test.ts +1 -0
  44. package/src/__tests__/scheduler-result-notification.test.ts +521 -7
  45. package/src/__tests__/scheduler-wake.test.ts +1 -0
  46. package/src/__tests__/skill-load-inline-command.test.ts +35 -0
  47. package/src/__tests__/subagent-cron-run-attribution.test.ts +41 -2
  48. package/src/__tests__/subagent-manager-notify.test.ts +48 -0
  49. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +6 -6
  50. package/src/__tests__/usage-attribution.test.ts +31 -0
  51. package/src/__tests__/wake-schedule-escalation.test.ts +1 -0
  52. package/src/agent/loop.ts +23 -0
  53. package/src/api/ask-question-tool.ts +50 -0
  54. package/src/api/events/generation-handoff.ts +4 -0
  55. package/src/api/events/message-complete.ts +4 -0
  56. package/src/api/events/tool-result.ts +11 -0
  57. package/src/api/index.ts +11 -0
  58. package/src/api/requests/guardian-actions.test.ts +45 -0
  59. package/src/api/requests/guardian-actions.ts +20 -0
  60. package/src/api/responses/conversation-message.ts +4 -0
  61. package/src/api/responses/home.ts +27 -0
  62. package/src/approvals/guardian-decision-primitive.ts +6 -4
  63. package/src/approvals/guardian-request-resolvers.ts +35 -30
  64. package/src/avatar/notification-avatar.test.ts +42 -34
  65. package/src/avatar/notification-avatar.ts +1 -0
  66. package/src/calls/__tests__/call-controller.test.ts +18 -0
  67. package/src/calls/__tests__/voice-control-protocol.test.ts +6 -1
  68. package/src/calls/__tests__/voice-leg-coordinator.test.ts +70 -0
  69. package/src/calls/__tests__/voice-session-bridge.test.ts +116 -4
  70. package/src/calls/__tests__/voice-triage-escalate.test.ts +77 -11
  71. package/src/calls/call-controller.ts +3 -1
  72. package/src/calls/voice-control-protocol.ts +12 -4
  73. package/src/calls/voice-escalation-judge.ts +2 -2
  74. package/src/calls/voice-leg-coordinator.ts +23 -5
  75. package/src/calls/voice-screen-annotation.ts +44 -0
  76. package/src/calls/voice-session-bridge.ts +63 -20
  77. package/src/calls/voice-triage-escalate.ts +82 -27
  78. package/src/cli/commands/__tests__/conversations-clear.test.ts +156 -0
  79. package/src/cli/commands/conversations.help.ts +7 -2
  80. package/src/cli/commands/conversations.ts +36 -30
  81. package/src/cli/commands/oauth/index.help.ts +8 -2
  82. package/src/cli/commands/oauth/request.test.ts +32 -0
  83. package/src/cli/commands/platform/index.help.ts +2 -1
  84. package/src/cli/commands/platform/index.ts +8 -2
  85. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +60 -0
  86. package/src/cli/lib/__tests__/plugin-catalog-platform.test.ts +66 -2
  87. package/src/cli/lib/bundled-marketplace.json +35 -0
  88. package/src/cli/lib/bundled-plugin-packages.json +21 -0
  89. package/src/cli/lib/plugin-catalog-cache.ts +17 -2
  90. package/src/cli/lib/plugin-catalog-platform.ts +48 -9
  91. package/src/config/__tests__/default-profile-catalog.test.ts +28 -0
  92. package/src/config/__tests__/platform-identity.test.ts +59 -0
  93. package/src/config/__tests__/profile-order.test.ts +19 -0
  94. package/src/config/__tests__/sync-gated-profiles.test.ts +71 -0
  95. package/src/config/bundled-skills/computer-use/SKILL.md +20 -0
  96. package/src/config/bundled-skills/computer-use/TOOLS.json +11 -11
  97. package/src/config/bundled-skills/screen-annotation/SKILL.md +41 -29
  98. package/src/config/bundled-skills/screen-annotation/TOOLS.json +5 -5
  99. package/src/config/call-site-defaults.ts +6 -0
  100. package/src/config/default-profile-catalog.ts +30 -1
  101. package/src/config/default-profile-names.ts +36 -2
  102. package/src/config/feature-flag-registry.json +9 -1
  103. package/src/config/platform-identity.ts +22 -5
  104. package/src/config/profile-order.ts +7 -1
  105. package/src/config/schemas/call-site-catalog.ts +7 -0
  106. package/src/config/schemas/llm.ts +21 -5
  107. package/src/config/sync-gated-profiles.ts +96 -44
  108. package/src/daemon/__tests__/abort-null-controller.test.ts +135 -1
  109. package/src/daemon/__tests__/auto-profile-router.test.ts +380 -0
  110. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +16 -0
  111. package/src/daemon/activity-trail.test.ts +206 -0
  112. package/src/daemon/activity-trail.ts +190 -0
  113. package/src/daemon/auto-profile-router.ts +431 -0
  114. package/src/daemon/computer-use-observation.test.ts +81 -0
  115. package/src/daemon/computer-use-observation.ts +23 -0
  116. package/src/daemon/conversation-agent-loop-handlers.ts +11 -0
  117. package/src/daemon/conversation-agent-loop.ts +157 -32
  118. package/src/daemon/conversation-error.ts +19 -0
  119. package/src/daemon/conversation-lifecycle.ts +70 -7
  120. package/src/daemon/conversation-media-retry.ts +1 -2
  121. package/src/daemon/conversation-messaging.ts +31 -2
  122. package/src/daemon/conversation-process.ts +107 -16
  123. package/src/daemon/conversation-queue-manager.ts +7 -0
  124. package/src/daemon/conversation-runtime-assembly.ts +1 -0
  125. package/src/daemon/conversation-turn-finalize.ts +14 -0
  126. package/src/daemon/conversation.ts +25 -6
  127. package/src/daemon/event-loop-watchdog.test.ts +201 -2
  128. package/src/daemon/event-loop-watchdog.ts +182 -6
  129. package/src/daemon/host-cu-proxy.ts +34 -3
  130. package/src/daemon/pkb-reminder-builder.test.ts +10 -3
  131. package/src/daemon/pkb-reminder-builder.ts +1 -1
  132. package/src/daemon/wake-conversation-ops.ts +4 -2
  133. package/src/desktop/desktop-computer-use.test.ts +14 -4
  134. package/src/home/feed-types.ts +2 -0
  135. package/src/inbound/platform-callback-registration.ts +19 -6
  136. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +27 -0
  137. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +83 -0
  138. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +48 -1
  139. package/src/live-voice/__tests__/live-voice-vad.test.ts +34 -2
  140. package/src/live-voice/__tests__/protocol.test.ts +22 -0
  141. package/src/live-voice/continuation-judge.ts +1 -1
  142. package/src/live-voice/live-voice-session.ts +25 -19
  143. package/src/live-voice/protocol.ts +16 -5
  144. package/src/messaging/providers/discord/withdraw.ts +4 -5
  145. package/src/messaging/providers/gmail/client.ts +4 -0
  146. package/src/messaging/providers/slack/withdraw.ts +5 -5
  147. package/src/messaging/providers/telegram-bot/withdraw.ts +4 -5
  148. package/src/monitoring/__tests__/proc-fixtures.ts +28 -0
  149. package/src/monitoring/__tests__/proc-wait-state.test.ts +232 -0
  150. package/src/monitoring/__tests__/process-usage.test.ts +243 -0
  151. package/src/monitoring/proc-wait-state.ts +366 -0
  152. package/src/monitoring/process-memory.ts +4 -3
  153. package/src/monitoring/process-usage.ts +215 -0
  154. package/src/monitoring/resource-sample-types.ts +8 -0
  155. package/src/monitoring/resource-sampler.ts +17 -2
  156. package/src/monitoring/stall-capture.ts +12 -0
  157. package/src/notifications/README.md +88 -9
  158. package/src/notifications/__tests__/assistant-reply-producer.test.ts +390 -119
  159. package/src/notifications/__tests__/background-result-producer.test.ts +1272 -0
  160. package/src/notifications/__tests__/broadcaster.test.ts +292 -1
  161. package/src/notifications/__tests__/completion-policy.test.ts +243 -0
  162. package/src/notifications/__tests__/decision-engine.test.ts +51 -4
  163. package/src/notifications/__tests__/has-pending-background-work.test.ts +281 -0
  164. package/src/notifications/__tests__/home-feed-side-effect.test.ts +161 -3
  165. package/src/notifications/__tests__/resolve-visible-in-source.test.ts +110 -1
  166. package/src/notifications/__tests__/schedule-result-producer.test.ts +11 -4
  167. package/src/notifications/adapters/macos.ts +24 -12
  168. package/src/notifications/adapters/platform.ts +16 -5
  169. package/src/notifications/assistant-reply-producer.ts +96 -71
  170. package/src/notifications/background-result-producer.ts +369 -0
  171. package/src/notifications/broadcaster.ts +28 -8
  172. package/src/notifications/completion-policy.ts +115 -0
  173. package/src/notifications/completion-work.ts +46 -0
  174. package/src/notifications/conversation-pairing.ts +32 -13
  175. package/src/notifications/decision-engine.ts +18 -24
  176. package/src/notifications/emit-signal.ts +16 -1
  177. package/src/notifications/events-store.ts +4 -0
  178. package/src/notifications/has-pending-background-work.ts +91 -0
  179. package/src/notifications/home-feed-mirror.ts +44 -0
  180. package/src/notifications/home-feed-side-effect.ts +54 -28
  181. package/src/notifications/resolve-visible-in-source.ts +56 -2
  182. package/src/notifications/result-output.ts +154 -0
  183. package/src/notifications/schedule-result-producer.ts +6 -187
  184. package/src/notifications/types.ts +2 -0
  185. package/src/oauth/AGENTS.md +9 -1
  186. package/src/oauth/byo-connection.test.ts +28 -0
  187. package/src/persistence/__tests__/recent-conversation-messages.test.ts +41 -0
  188. package/src/persistence/conversation-crud.ts +26 -0
  189. package/src/persistence/conversation-types.ts +15 -37
  190. package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +85 -14
  191. package/src/persistence/jobs-store.ts +229 -0
  192. package/src/persistence/migrations/383-normalize-opencode-host-connections.ts +44 -0
  193. package/src/persistence/migrations/__tests__/383-normalize-opencode-host-connections.test.ts +102 -0
  194. package/src/persistence/steps.ts +12 -0
  195. package/src/platform/sync-avatar.test.ts +29 -0
  196. package/src/plugins/defaults/memory/AGENTS.md +5 -3
  197. package/src/plugins/defaults/memory/__tests__/skill-update-receipt-boundary-guard.test.ts +45 -0
  198. package/src/plugins/defaults/memory/__tests__/skill-update-receipt-job.test.ts +580 -0
  199. package/src/plugins/defaults/memory/__tests__/turn-retrieval.test.ts +21 -0
  200. package/src/plugins/defaults/memory/graph/tools.ts +9 -5
  201. package/src/plugins/defaults/memory/hooks/conversation-deleted.ts +4 -0
  202. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +7 -3
  203. package/src/plugins/defaults/memory/job-handlers.ts +8 -2
  204. package/src/plugins/defaults/memory/skill-update-receipt-job.ts +334 -0
  205. package/src/plugins/defaults/memory/tools.test.ts +17 -0
  206. package/src/plugins/defaults/memory/turn-retrieval.ts +8 -0
  207. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +24 -0
  208. package/src/plugins/defaults/memory/v3/injector.ts +4 -4
  209. package/src/plugins/types.ts +2 -0
  210. package/src/providers/__tests__/search-provider-catalog.test.ts +8 -0
  211. package/src/providers/anthropic/client.ts +4 -0
  212. package/src/providers/gemini/client.ts +7 -1
  213. package/src/providers/inference/auth.ts +25 -0
  214. package/src/providers/inference/profile-probe.ts +1 -0
  215. package/src/{calls/__tests__/typesafe-noul.test.ts → providers/jev/ask.test.ts} +4 -4
  216. package/src/{calls/typesafe-noul.ts → providers/jev/ask.ts} +67 -34
  217. package/src/providers/openai/__tests__/api-error-normalization.test.ts +12 -0
  218. package/src/providers/openai/api-error-normalization.ts +4 -0
  219. package/src/providers/retry.ts +2 -0
  220. package/src/providers/search-provider-catalog.ts +10 -0
  221. package/src/providers/types.ts +7 -0
  222. package/src/providers/usage-tracking.ts +5 -3
  223. package/src/runtime/__tests__/agent-wake.test.ts +308 -0
  224. package/src/runtime/access-request-helper.ts +16 -9
  225. package/src/runtime/agent-wake-queue.ts +52 -0
  226. package/src/runtime/agent-wake.ts +111 -18
  227. package/src/runtime/channel-approval-types.ts +6 -38
  228. package/src/runtime/channel-approvals.ts +4 -5
  229. package/src/runtime/http-server.ts +4 -1
  230. package/src/runtime/introduction-policy.test.ts +17 -1
  231. package/src/runtime/introduction-policy.ts +25 -7
  232. package/src/runtime/migrations/vbundle-builder.ts +9 -0
  233. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +21 -1
  234. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +60 -0
  235. package/src/runtime/routes/__tests__/oauth-proxy-routes.test.ts +61 -3
  236. package/src/runtime/routes/auto-profile-routes.ts +83 -0
  237. package/src/runtime/routes/conversation-management-routes.ts +1 -3
  238. package/src/runtime/routes/conversation-routes.ts +9 -0
  239. package/src/runtime/routes/guardian-action-routes.ts +8 -23
  240. package/src/runtime/routes/host-cu-routes.ts +3 -1
  241. package/src/runtime/routes/index.ts +2 -0
  242. package/src/runtime/routes/inference-profiles-routes.ts +5 -2
  243. package/src/runtime/routes/inference-provider-connection-routes.ts +25 -8
  244. package/src/runtime/routes/migration-routes.ts +8 -2
  245. package/src/runtime/routes/oauth-proxy-passthrough.test.ts +32 -0
  246. package/src/runtime/routes/platform-routes.ts +6 -1
  247. package/src/runtime/routes/user-route-dispatcher.ts +1 -2
  248. package/src/runtime/web-presence.ts +3 -4
  249. package/src/schedule/schedule-store.ts +10 -0
  250. package/src/schedule/scheduler.ts +92 -24
  251. package/src/subagent/manager.ts +32 -12
  252. package/src/telegram/__tests__/webhook-health.test.ts +8 -0
  253. package/src/telemetry/telemetry-wire-source.json +1 -1
  254. package/src/telemetry/telemetry-wire.generated.ts +1 -0
  255. package/src/tools/background-tool-registry.ts +30 -3
  256. package/src/tools/browser/cdp-client/local-cdp-client.ts +1 -2
  257. package/src/tools/host-terminal/host-shell.ts +7 -0
  258. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +28 -1
  259. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +34 -0
  260. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +35 -1
  261. package/src/tools/network/__tests__/web-search.test.ts +96 -0
  262. package/src/tools/network/web-fetch.ts +30 -9
  263. package/src/tools/network/web-search.ts +235 -4
  264. package/src/tools/skills/load.ts +13 -17
  265. package/src/tools/skills/scaffold-managed.ts +86 -71
  266. package/src/tools/terminal/shell-launch.test.ts +27 -1
  267. package/src/tools/terminal/shell.ts +4 -0
  268. package/src/tools/workspace-tools/loader.ts +1 -2
  269. package/src/usage/attribution.ts +18 -1
  270. package/src/util/abort-reasons.ts +2 -0
  271. package/src/util/errors.ts +1 -0
  272. package/src/util/oauth-request-body.test.ts +5 -1
  273. package/src/util/provider-error-patterns.ts +11 -0
  274. package/src/util/single-flight.test.ts +6 -0
  275. package/src/util/single-flight.ts +2 -0
  276. package/src/util/worker-pipe.test.ts +56 -10
  277. package/src/util/worker-pipe.ts +9 -4
  278. package/src/workspace/git-service.ts +6 -1
  279. package/src/workspace/migrations/158-repoint-opencode-host-provider-fragments.ts +163 -0
  280. package/src/workspace/migrations/__tests__/158-repoint-opencode-host-provider-fragments.test.ts +188 -0
  281. package/src/workspace/migrations/registry.ts +2 -0
package/openapi.yaml CHANGED
@@ -3,7 +3,7 @@
3
3
  openapi: 3.1.0
4
4
  info:
5
5
  title: Vellum Assistant API
6
- version: 0.12.4
6
+ version: 0.12.5
7
7
  description: Auto-generated OpenAPI specification for the Vellum Assistant runtime HTTP server.
8
8
  servers:
9
9
  - url: http://127.0.0.1:7821
@@ -2597,6 +2597,58 @@ paths:
2597
2597
  - userId
2598
2598
  - authenticated
2599
2599
  additionalProperties: false
2600
+ /v1/auto-profile/preview:
2601
+ post:
2602
+ operationId: autoprofile_preview_post
2603
+ summary: Preview the Auto profile's pick for a draft
2604
+ description:
2605
+ Ask the Auto profile which default profile a draft message would run on, using the conversation's recent
2606
+ history when one is given. The pick is kept briefly so the turn that sends the same text reuses it. Spends one
2607
+ Jev call per request.
2608
+ tags:
2609
+ - conversations
2610
+ requestBody:
2611
+ required: true
2612
+ content:
2613
+ application/json:
2614
+ schema:
2615
+ type: object
2616
+ properties:
2617
+ text:
2618
+ type: string
2619
+ minLength: 1
2620
+ maxLength: 20000
2621
+ conversationId:
2622
+ type: string
2623
+ description: Conversation the draft belongs to, for recent history. Omit for a new chat.
2624
+ required:
2625
+ - text
2626
+ responses:
2627
+ "200":
2628
+ description: Successful response
2629
+ content:
2630
+ application/json:
2631
+ schema:
2632
+ type: object
2633
+ properties:
2634
+ profile:
2635
+ anyOf:
2636
+ - type: string
2637
+ - type: "null"
2638
+ outcome:
2639
+ type: string
2640
+ enum:
2641
+ - routed
2642
+ - unavailable
2643
+ - timeout
2644
+ - error
2645
+ - fallback
2646
+ confidence:
2647
+ type: number
2648
+ required:
2649
+ - profile
2650
+ - outcome
2651
+ additionalProperties: false
2600
2652
  /v1/avatar/accent:
2601
2653
  post:
2602
2654
  operationId: avatar_accent_post
@@ -14054,7 +14106,7 @@ paths:
14054
14106
  post:
14055
14107
  operationId: guardianactions_decision_post
14056
14108
  summary: Submit guardian decision
14057
- description: Submit a guardian action decision (approve/reject).
14109
+ description: Submit a guardian decision on a pending request.
14058
14110
  tags:
14059
14111
  - guardian
14060
14112
  requestBody:
@@ -14066,9 +14118,17 @@ paths:
14066
14118
  properties:
14067
14119
  requestId:
14068
14120
  type: string
14121
+ minLength: 1
14069
14122
  description: Guardian request ID
14070
14123
  action:
14071
14124
  type: string
14125
+ enum:
14126
+ - approve_once
14127
+ - reject
14128
+ - trust
14129
+ - verify_code
14130
+ - leave_unverified
14131
+ - block
14072
14132
  description: Decision action
14073
14133
  conversationId:
14074
14134
  type: string
@@ -15079,6 +15139,24 @@ paths:
15079
15139
  - intent
15080
15140
  - status
15081
15141
  additionalProperties: false
15142
+ updates:
15143
+ type: array
15144
+ items:
15145
+ type: object
15146
+ properties:
15147
+ skillId:
15148
+ type: string
15149
+ name:
15150
+ type: string
15151
+ summary:
15152
+ type: string
15153
+ conversationId:
15154
+ type: string
15155
+ required:
15156
+ - skillId
15157
+ - name
15158
+ - summary
15159
+ additionalProperties: false
15082
15160
  category:
15083
15161
  type: string
15084
15162
  enum:
@@ -15314,6 +15392,24 @@ paths:
15314
15392
  - intent
15315
15393
  - status
15316
15394
  additionalProperties: false
15395
+ updates:
15396
+ type: array
15397
+ items:
15398
+ type: object
15399
+ properties:
15400
+ skillId:
15401
+ type: string
15402
+ name:
15403
+ type: string
15404
+ summary:
15405
+ type: string
15406
+ conversationId:
15407
+ type: string
15408
+ required:
15409
+ - skillId
15410
+ - name
15411
+ - summary
15412
+ additionalProperties: false
15317
15413
  category:
15318
15414
  type: string
15319
15415
  enum:
@@ -15643,6 +15739,24 @@ paths:
15643
15739
  - intent
15644
15740
  - status
15645
15741
  additionalProperties: false
15742
+ updates:
15743
+ type: array
15744
+ items:
15745
+ type: object
15746
+ properties:
15747
+ skillId:
15748
+ type: string
15749
+ name:
15750
+ type: string
15751
+ summary:
15752
+ type: string
15753
+ conversationId:
15754
+ type: string
15755
+ required:
15756
+ - skillId
15757
+ - name
15758
+ - summary
15759
+ additionalProperties: false
15646
15760
  category:
15647
15761
  type: string
15648
15762
  enum:
@@ -21280,6 +21394,7 @@ paths:
21280
21394
  - fastcrw
21281
21395
  - searxng
21282
21396
  - tinyfish
21397
+ - exa
21283
21398
  resultCount:
21284
21399
  type: number
21285
21400
  durationMs:
@@ -21358,6 +21473,10 @@ paths:
21358
21473
  type: string
21359
21474
  mayRequireJavaScript:
21360
21475
  type: boolean
21476
+ startIndexPastEnd:
21477
+ type: boolean
21478
+ providerWarning:
21479
+ type: string
21361
21480
  required:
21362
21481
  - url
21363
21482
  - finalUrl
@@ -21894,6 +22013,7 @@ paths:
21894
22013
  - fastcrw
21895
22014
  - searxng
21896
22015
  - tinyfish
22016
+ - exa
21897
22017
  resultCount:
21898
22018
  type: number
21899
22019
  durationMs:
@@ -21972,6 +22092,10 @@ paths:
21972
22092
  type: string
21973
22093
  mayRequireJavaScript:
21974
22094
  type: boolean
22095
+ startIndexPastEnd:
22096
+ type: boolean
22097
+ providerWarning:
22098
+ type: string
21975
22099
  required:
21976
22100
  - url
21977
22101
  - finalUrl
@@ -22454,6 +22578,8 @@ paths:
22454
22578
  type: boolean
22455
22579
  noResponse:
22456
22580
  type: boolean
22581
+ autoRoutedProfile:
22582
+ type: string
22457
22583
  cameraFrame:
22458
22584
  type: boolean
22459
22585
  const: true
@@ -24794,6 +24920,24 @@ paths:
24794
24920
  - intent
24795
24921
  - status
24796
24922
  additionalProperties: false
24923
+ updates:
24924
+ type: array
24925
+ items:
24926
+ type: object
24927
+ properties:
24928
+ skillId:
24929
+ type: string
24930
+ name:
24931
+ type: string
24932
+ summary:
24933
+ type: string
24934
+ conversationId:
24935
+ type: string
24936
+ required:
24937
+ - skillId
24938
+ - name
24939
+ - summary
24940
+ additionalProperties: false
24797
24941
  category:
24798
24942
  type: string
24799
24943
  enum:
@@ -26891,7 +27035,12 @@ paths:
26891
27035
  assistantId:
26892
27036
  type: string
26893
27037
  hasAssistantApiKey:
26894
- type: boolean
27038
+ anyOf:
27039
+ - type: boolean
27040
+ - type: "null"
27041
+ description:
27042
+ Whether a managed assistant API key is stored. Null when the credential store could not be read, which is
27043
+ not evidence that no key is stored.
26895
27044
  hasWebhookSecret:
26896
27045
  type: boolean
26897
27046
  clientInstallationId:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.12.4",
3
+ "version": "0.12.5-dev.202609251017.e467a6c",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -181,6 +181,82 @@ describe("AgentLoop", () => {
181
181
  expect(calls[1].options?.config?.overrideProfile).toBe("quality-optimized");
182
182
  });
183
183
 
184
+ test("the auto origin follows the routed override and drops once a switch replaces it", async () => {
185
+ const toolCallId = "tool-1";
186
+ const { provider, calls } = createMockProvider([
187
+ toolUseResponse(toolCallId, "read_file", { path: "/tmp/test.txt" }),
188
+ textResponse("done"),
189
+ ]);
190
+ let overrideProfile = "quality-optimized";
191
+ const loop = new AgentLoop({
192
+ provider: provider,
193
+ systemPrompt: "system",
194
+ conversationId: "test-conversation",
195
+ tools: dummyTools,
196
+ toolExecutor: async () => {
197
+ // A confirmed profile session mid-turn: the user's pick, not Auto's.
198
+ overrideProfile = "cost-optimized";
199
+ return { content: "ok", isError: false };
200
+ },
201
+ });
202
+
203
+ await loop.run({
204
+ messages: [userMessage],
205
+ onEvent: collectEvents([]),
206
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
207
+ requestId: "req-1",
208
+ callSite: "mainAgent",
209
+ overrideProfile: "quality-optimized",
210
+ overrideProfileOrigin: "auto",
211
+ resolveOverrideProfile: () => overrideProfile,
212
+ });
213
+
214
+ expect(calls).toHaveLength(2);
215
+ expect(calls[0].options?.config?.overrideProfile).toBe("quality-optimized");
216
+ expect(calls[0].options?.config?.overrideProfileOrigin).toBe("auto");
217
+ expect(calls[1].options?.config?.overrideProfile).toBe("cost-optimized");
218
+ expect(calls[1].options?.config?.overrideProfileOrigin).toBeUndefined();
219
+ });
220
+
221
+ test("a pre-model-call hook keeps the auto origin unless it changes the profile", async () => {
222
+ let hookProfile: string | null | undefined;
223
+ registerPlugin({
224
+ manifest: { name: "profile-hook", version: "0.0.1" },
225
+ hooks: {
226
+ "pre-model-call": async (ctx) =>
227
+ hookProfile === undefined
228
+ ? ctx
229
+ : { ...ctx, modelProfile: hookProfile },
230
+ },
231
+ });
232
+ const run = async () => {
233
+ const { provider, calls } = createMockProvider([textResponse("ok")]);
234
+ const loop = new AgentLoop({
235
+ provider: provider,
236
+ systemPrompt: "system",
237
+ conversationId: "test-conversation",
238
+ tools: dummyTools,
239
+ });
240
+ await loop.run({
241
+ messages: [userMessage],
242
+ onEvent: collectEvents([]),
243
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
244
+ requestId: "req-1",
245
+ callSite: "mainAgent",
246
+ overrideProfile: "quality-optimized",
247
+ overrideProfileOrigin: "auto",
248
+ });
249
+ return calls[0].options?.config;
250
+ };
251
+
252
+ hookProfile = undefined;
253
+ expect((await run())?.overrideProfileOrigin).toBe("auto");
254
+ hookProfile = "cost-optimized";
255
+ expect((await run())?.overrideProfileOrigin).toBeUndefined();
256
+ hookProfile = null;
257
+ expect((await run())?.overrideProfileOrigin).toBeUndefined();
258
+ });
259
+
184
260
  test("re-resolves max input tokens before truncating tool results", async () => {
185
261
  const toolCallId = "tool-1";
186
262
  const toolOutput = "x".repeat(2_500);
@@ -207,6 +207,15 @@ describe("AnthropicProvider — semantic reason stamping", () => {
207
207
  ),
208
208
  reason: "insufficient_credits",
209
209
  },
210
+ {
211
+ name: "free-tier daily-limit body code → free_tier_daily_limit_reached (before insufficient_credits)",
212
+ error: new FakeAPIError(
213
+ 402,
214
+ '{"code":"free_tier_daily_limit_reached","detail":"free usage"}',
215
+ anthropicBody("invalid_request_error"),
216
+ ),
217
+ reason: "free_tier_daily_limit_reached",
218
+ },
210
219
  {
211
220
  name: "daily-limit body code → daily_limit_reached (before insufficient_credits)",
212
221
  error: new FakeAPIError(
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import type { WakeOptions } from "../runtime/agent-wake.js";
4
4
  import type { BackgroundTool } from "../tools/background-tool-registry.js";
5
+ import type { ToolContext } from "../tools/types.js";
5
6
 
6
7
  // ── Mock modules ────────────────────────────────────────────────────────────
7
8
 
@@ -239,4 +240,37 @@ describe("bash tool background mode", () => {
239
240
  expect(mockRegisterBackgroundTool).not.toHaveBeenCalled();
240
241
  expect(mockWakeAgentForOpportunity).not.toHaveBeenCalled();
241
242
  });
243
+
244
+ for (const cronRunId of [undefined, null, "cron-run-123"]) {
245
+ for (const outcome of ["completed", "failed", "cancelled"] as const) {
246
+ test(`preserves originating schedule ${String(cronRunId)} on ${outcome}`, async () => {
247
+ const context: ToolContext = {
248
+ ...baseContext,
249
+ cronRunId,
250
+ };
251
+ await shellTool.execute(
252
+ {
253
+ command:
254
+ outcome === "cancelled"
255
+ ? "sleep 30"
256
+ : `sleep 0.1; exit ${outcome === "failed" ? "1" : "0"}`,
257
+ activity: "test",
258
+ background: true,
259
+ },
260
+ context,
261
+ );
262
+ context.cronRunId = "cron-run-later";
263
+ expect(registeredTools[0]!.cronRunId).toBe(cronRunId ?? undefined);
264
+ if (outcome === "cancelled") {
265
+ registeredTools[0]!.cancel();
266
+ }
267
+ await waitForWake(mockWakeAgentForOpportunity);
268
+
269
+ expect(mockWakeAgentForOpportunity).toHaveBeenCalledTimes(1);
270
+ const wake = mockWakeAgentForOpportunity.mock.calls[0]![0];
271
+ expect(wake.cronRunId).toBe(cronRunId ?? undefined);
272
+ expect(wake.backgroundToolCompletion?.status).toBe(outcome);
273
+ });
274
+ }
275
+ }
242
276
  });
@@ -84,7 +84,11 @@ mock.module("../daemon/host-bash-proxy.js", () => ({
84
84
  // Import under test — MUST come after mock.module calls.
85
85
  // ---------------------------------------------------------------------------
86
86
 
87
- import type { CompletedBackgroundTool } from "../tools/background-tool-registry.js";
87
+ import type { WakeOptions } from "../runtime/agent-wake.js";
88
+ import type {
89
+ BackgroundTool,
90
+ CompletedBackgroundTool,
91
+ } from "../tools/background-tool-registry.js";
88
92
  import { hostShellTool } from "../tools/host-terminal/host-shell.js";
89
93
  import type { ToolContext, ToolExecutionResult } from "../tools/types.js";
90
94
 
@@ -148,6 +152,56 @@ afterEach(() => {
148
152
  // ---------------------------------------------------------------------------
149
153
 
150
154
  describe("host_bash background mode — proxy path", () => {
155
+ for (const cronRunId of [undefined, null, "cron-run-123"]) {
156
+ for (const outcome of [
157
+ "completed",
158
+ "failed",
159
+ "rejected",
160
+ "cancelled",
161
+ "cancelled-rejection",
162
+ ] as const) {
163
+ test(`preserves originating schedule ${String(cronRunId)} on ${outcome}`, async () => {
164
+ mockProxyAvailable = true;
165
+ const result = Promise.withResolvers<ToolExecutionResult>();
166
+ mockProxyRequestImpl = () => result.promise;
167
+ const context = makeContext({ cronRunId });
168
+
169
+ await hostShellTool.execute(
170
+ { command: "echo bg-proxy", background: true },
171
+ context,
172
+ );
173
+ context.cronRunId = "cron-run-later";
174
+ const ownedCommand = (
175
+ mockRegisterBackgroundTool.mock.calls as unknown[][]
176
+ )[0]![0] as BackgroundTool;
177
+ expect(ownedCommand.cronRunId).toBe(cronRunId ?? undefined);
178
+ const cancelled =
179
+ outcome === "cancelled" || outcome === "cancelled-rejection";
180
+ if (cancelled) {
181
+ const registered = (
182
+ mockRegisterBackgroundTool.mock.calls as unknown[][]
183
+ )[0]![0] as BackgroundTool;
184
+ registered.cancel();
185
+ }
186
+ if (outcome === "rejected" || outcome === "cancelled-rejection") {
187
+ result.reject(new Error("proxy transport error"));
188
+ } else {
189
+ result.resolve({ content: "done", isError: outcome === "failed" });
190
+ }
191
+ await new Promise((resolve) => setTimeout(resolve, 10));
192
+
193
+ expect(mockWakeAgentForOpportunity).toHaveBeenCalledTimes(1);
194
+ const wake = (
195
+ mockWakeAgentForOpportunity.mock.calls as unknown[][]
196
+ )[0]![0] as WakeOptions;
197
+ expect(wake.cronRunId).toBe(cronRunId ?? undefined);
198
+ expect(wake.backgroundToolCompletion?.status).toBe(
199
+ cancelled ? "cancelled" : outcome === "rejected" ? "failed" : outcome,
200
+ );
201
+ });
202
+ }
203
+ }
204
+
151
205
  test("returns immediately with backgrounded response", async () => {
152
206
  const proxyResult: ToolExecutionResult = {
153
207
  content: "proxy output",
@@ -311,6 +365,49 @@ describe("host_bash background mode — proxy path", () => {
311
365
  // ---------------------------------------------------------------------------
312
366
 
313
367
  describe("host_bash background mode — direct execution path", () => {
368
+ for (const cronRunId of [undefined, null, "cron-run-123"]) {
369
+ for (const outcome of [
370
+ "completed",
371
+ "failed",
372
+ "spawn-error",
373
+ "cancelled",
374
+ ] as const) {
375
+ test(`preserves originating schedule ${String(cronRunId)} on ${outcome}`, async () => {
376
+ const context = makeContext({ cronRunId });
377
+ await hostShellTool.execute(
378
+ { command: "echo bg-local", background: true },
379
+ context,
380
+ );
381
+ context.cronRunId = "cron-run-later";
382
+ const ownedCommand = (
383
+ mockRegisterBackgroundTool.mock.calls as unknown[][]
384
+ )[0]![0] as BackgroundTool;
385
+ expect(ownedCommand.cronRunId).toBe(cronRunId ?? undefined);
386
+ if (outcome === "cancelled") {
387
+ const registered = (
388
+ mockRegisterBackgroundTool.mock.calls as unknown[][]
389
+ )[0]![0] as BackgroundTool;
390
+ registered.cancel();
391
+ }
392
+ if (outcome === "spawn-error") {
393
+ latestChild!.emit("error", new Error("spawn ENOENT"));
394
+ latestChild!.emit("close", null);
395
+ } else {
396
+ latestChild!.emit("close", outcome === "failed" ? 1 : 0);
397
+ }
398
+
399
+ expect(mockWakeAgentForOpportunity).toHaveBeenCalledTimes(1);
400
+ const wake = (
401
+ mockWakeAgentForOpportunity.mock.calls as unknown[][]
402
+ )[0]![0] as WakeOptions;
403
+ expect(wake.cronRunId).toBe(cronRunId ?? undefined);
404
+ expect(wake.backgroundToolCompletion?.status).toBe(
405
+ outcome === "spawn-error" ? "failed" : outcome,
406
+ );
407
+ });
408
+ }
409
+ }
410
+
314
411
  test("returns immediately with backgrounded response", async () => {
315
412
  const ctx = makeContext();
316
413
 
@@ -6,6 +6,7 @@ import {
6
6
  cancelBackgroundTool,
7
7
  type CompletedBackgroundTool,
8
8
  generateBackgroundToolId,
9
+ hasBackgroundToolWork,
9
10
  listBackgroundTools,
10
11
  listCompletedBackgroundTools,
11
12
  MAX_BACKGROUND_TOOLS,
@@ -21,6 +22,7 @@ function makeTool(overrides: Partial<BackgroundTool> = {}): BackgroundTool {
21
22
  toolName: overrides.toolName ?? "bash",
22
23
  conversationId: overrides.conversationId ?? "conv-xyz",
23
24
  command: overrides.command ?? "echo hello",
25
+ cronRunId: overrides.cronRunId,
24
26
  startedAt: overrides.startedAt ?? Date.now(),
25
27
  cancel: overrides.cancel ?? mock(() => {}),
26
28
  };
@@ -58,6 +60,27 @@ describe("background-tool-registry", () => {
58
60
  });
59
61
 
60
62
  describe("cancelBackgroundTool", () => {
63
+ test("filters both running and cancelling commands by schedule owner", () => {
64
+ const owned = makeTool({ id: "bg-owned", cronRunId: "run-owned" });
65
+ registerBackgroundTool(owned);
66
+ registerBackgroundTool(makeTool({ id: "bg-user" }));
67
+ registerBackgroundTool(
68
+ makeTool({ id: "bg-other", cronRunId: "run-other" }),
69
+ );
70
+ expect(
71
+ hasBackgroundToolWork(owned.conversationId, { cronRunId: "run-owned" }),
72
+ ).toBe(true);
73
+ cancelBackgroundTool(owned.id);
74
+ expect(
75
+ hasBackgroundToolWork(owned.conversationId, { cronRunId: "run-owned" }),
76
+ ).toBe(true);
77
+ removeBackgroundTool(owned.id);
78
+ expect(
79
+ hasBackgroundToolWork(owned.conversationId, { cronRunId: "run-owned" }),
80
+ ).toBe(false);
81
+ expect(hasBackgroundToolWork(owned.conversationId)).toBe(true);
82
+ });
83
+
61
84
  test("calls cancel(), removes the entry, and returns true", () => {
62
85
  const cancelFn = mock(() => {});
63
86
  const tool = makeTool({ id: "bg-cancel-1", cancel: cancelFn });
@@ -68,6 +91,20 @@ describe("background-tool-registry", () => {
68
91
  expect(result).toBe(true);
69
92
  expect(cancelFn).toHaveBeenCalledTimes(1);
70
93
  expect(listBackgroundTools()).toHaveLength(0);
94
+ expect(hasBackgroundToolWork(tool.conversationId)).toBe(true);
95
+ removeBackgroundTool(tool.id);
96
+ expect(hasBackgroundToolWork(tool.conversationId)).toBe(false);
97
+ });
98
+
99
+ test("a synchronous terminal callback settles cancellation work", () => {
100
+ registerBackgroundTool(
101
+ makeTool({
102
+ id: "bg-sync-cancel",
103
+ cancel: () => removeBackgroundTool("bg-sync-cancel"),
104
+ }),
105
+ );
106
+ cancelBackgroundTool("bg-sync-cancel");
107
+ expect(hasBackgroundToolWork("conv-xyz")).toBe(false);
71
108
  });
72
109
 
73
110
  test("returns false for unknown IDs", () => {
@@ -105,6 +142,8 @@ describe("background-tool-registry", () => {
105
142
 
106
143
  expect(listBackgroundTools("conv-2")).toHaveLength(1);
107
144
  expect(listBackgroundTools("conv-nonexistent")).toHaveLength(0);
145
+ expect(hasBackgroundToolWork("conv-1")).toBe(true);
146
+ expect(hasBackgroundToolWork("conv-nonexistent")).toBe(false);
108
147
  });
109
148
  });
110
149
 
@@ -4,8 +4,16 @@ import { credentialKey } from "../security/credential-key.js";
4
4
 
5
5
  let mockSecureKeys: Record<string, string>;
6
6
 
7
+ // Spread the real module: mocks are shared across files in a combined run,
8
+ // and a wholesale replacement drops the exports peer modules import.
9
+ const actualSecureKeys = await import("../security/secure-keys.js");
7
10
  mock.module("../security/secure-keys.js", () => ({
11
+ ...actualSecureKeys,
8
12
  getSecureKeyAsync: async (key: string) => mockSecureKeys[key] ?? null,
13
+ getSecureKeyResultAsync: async (key: string) => ({
14
+ value: mockSecureKeys[key] ?? undefined,
15
+ unreachable: false,
16
+ }),
9
17
  setSecureKeyAsync: async (key: string, value: string) => {
10
18
  mockSecureKeys[key] = value;
11
19
  return true;
@@ -5,8 +5,16 @@ import { setConfig } from "./helpers/set-config.js";
5
5
 
6
6
  let mockSecureKeys: Record<string, string>;
7
7
 
8
+ // Spread the real module: mocks are shared across files in a combined run,
9
+ // and a wholesale replacement drops the exports peer modules import.
10
+ const actualSecureKeys = await import("../security/secure-keys.js");
8
11
  mock.module("../security/secure-keys.js", () => ({
12
+ ...actualSecureKeys,
9
13
  getSecureKeyAsync: async (key: string) => mockSecureKeys[key] ?? null,
14
+ getSecureKeyResultAsync: async (key: string) => ({
15
+ value: mockSecureKeys[key] ?? undefined,
16
+ unreachable: false,
17
+ }),
10
18
  setSecureKeyAsync: async (key: string, value: string) => {
11
19
  mockSecureKeys[key] = value;
12
20
  return true;