@vellumai/assistant 0.12.2 → 0.12.3-staging.1

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 (298) hide show
  1. package/.env.example +2 -0
  2. package/AGENTS.md +1 -1
  3. package/docs/architecture/integrations.md +7 -0
  4. package/docs/architecture/memory.md +17 -2
  5. package/docs/credential-execution-service.md +1 -1
  6. package/docs/desktop-browser-cli.md +7 -3
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  9. package/node_modules/@vellumai/environments/src/shell.test.ts +21 -0
  10. package/node_modules/@vellumai/environments/src/shell.ts +24 -0
  11. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  13. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -2
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  15. package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  16. package/openapi.yaml +54 -5
  17. package/package.json +1 -1
  18. package/scripts/smoke-desktop-browser-cli.ts +1 -0
  19. package/scripts/sync-llm-catalog.ts +3 -0
  20. package/scripts/voice-ttft-spike.ts +2 -2
  21. package/src/__tests__/agent-loop.test.ts +259 -0
  22. package/src/__tests__/agent-wake-delegation-prompt.test.ts +64 -2
  23. package/src/__tests__/anthropic-provider.test.ts +55 -0
  24. package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
  25. package/src/__tests__/attachments-store.test.ts +22 -3
  26. package/src/__tests__/channel-approval.test.ts +9 -14
  27. package/src/__tests__/channel-reply-delivery.test.ts +56 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +23 -0
  29. package/src/__tests__/computer-use-screenshot-attachments.test.ts +411 -0
  30. package/src/__tests__/computer-use-screenshot-selection.test.ts +50 -0
  31. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  32. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  33. package/src/__tests__/conversation-agent-loop.test.ts +257 -6
  34. package/src/__tests__/conversation-attachments.test.ts +106 -0
  35. package/src/__tests__/conversation-fork-crud.test.ts +191 -0
  36. package/src/__tests__/conversation-rendered-delegation-state.test.ts +276 -0
  37. package/src/__tests__/credential-execution-client.test.ts +70 -53
  38. package/src/__tests__/db-conversation-tool-surface.test.ts +221 -0
  39. package/src/__tests__/events-tail-route.test.ts +33 -0
  40. package/src/__tests__/history-repair.test.ts +53 -0
  41. package/src/__tests__/inference-profile-session-handler.test.ts +26 -0
  42. package/src/__tests__/list-messages-tool-merge.test.ts +85 -1
  43. package/src/__tests__/llm-catalog-parity.test.ts +27 -4
  44. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  45. package/src/__tests__/oauth-commands-routes.test.ts +114 -101
  46. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  47. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  48. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  49. package/src/__tests__/persist-media-references.test.ts +50 -0
  50. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -1
  51. package/src/__tests__/run-conversation-turn-persistence.test.ts +138 -1
  52. package/src/__tests__/schedule-routes.test.ts +20 -0
  53. package/src/__tests__/scheduler-result-notification.test.ts +23 -4
  54. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  55. package/src/__tests__/secret-routes-platform-proxy.test.ts +86 -31
  56. package/src/__tests__/secret-routes-scrub.test.ts +22 -27
  57. package/src/__tests__/secret-scanner.test.ts +20 -0
  58. package/src/__tests__/secure-keys-managed-failover.test.ts +8 -0
  59. package/src/__tests__/secure-keys.test.ts +7 -3
  60. package/src/__tests__/server-tool-pairing.test.ts +107 -0
  61. package/src/__tests__/skills.test.ts +5 -4
  62. package/src/__tests__/subagent-tool-gate-mode.test.ts +226 -0
  63. package/src/__tests__/terminal-tools.test.ts +8 -0
  64. package/src/__tests__/tool-result-follow-up.test.ts +99 -0
  65. package/src/__tests__/tool-result-metadata-plumbing.test.ts +63 -0
  66. package/src/__tests__/unicode.test.ts +36 -0
  67. package/src/agent/loop.ts +37 -12
  68. package/src/agent/tool-result-follow-up.ts +70 -0
  69. package/src/api/attachment-provenance.test.ts +68 -0
  70. package/src/api/computer-use-tool.test.ts +55 -0
  71. package/src/api/computer-use-tool.ts +27 -0
  72. package/src/api/events/assistant-outbound-attachment.ts +3 -0
  73. package/src/api/events/desktop-activity-changed.ts +10 -0
  74. package/src/api/events/question-request.ts +1 -0
  75. package/src/api/index.ts +11 -0
  76. package/src/api/responses/conversation-message.ts +3 -0
  77. package/src/approvals/approval-primitive.ts +5 -2
  78. package/src/approvals/scoped-approval-grants.ts +6 -2
  79. package/src/browser/virtual-desktop-target.ts +1 -2
  80. package/src/cli/commands/__tests__/cli-test-harness.ts +21 -3
  81. package/src/cli/commands/__tests__/plugins.test.ts +20 -1
  82. package/src/cli/commands/__tests__/schedules.test.ts +14 -0
  83. package/src/cli/commands/bash.help.ts +4 -3
  84. package/src/cli/commands/browser.help.ts +5 -1
  85. package/src/cli/commands/credentials.help.ts +3 -3
  86. package/src/cli/commands/oauth/index.help.ts +10 -0
  87. package/src/cli/commands/oauth/providers.ts +12 -0
  88. package/src/cli/commands/oauth/request.test.ts +179 -2
  89. package/src/cli/commands/oauth/request.ts +41 -10
  90. package/src/cli/commands/plugins.ts +12 -5
  91. package/src/cli/commands/schedules.ts +2 -0
  92. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  93. package/src/cli/lib/__tests__/install-from-github.test.ts +41 -0
  94. package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +133 -51
  95. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +8 -2
  96. package/src/cli/lib/bundled-marketplace.json +855 -0
  97. package/src/cli/lib/bundled-plugin-packages.json +783 -1
  98. package/src/cli/lib/inspect-plugin.ts +11 -4
  99. package/src/cli/lib/upgrade-plugin.ts +19 -11
  100. package/src/cli/output.ts +18 -4
  101. package/src/config/__tests__/default-provider.test.ts +4 -0
  102. package/src/config/bundled-skills/schedule/SKILL.md +6 -6
  103. package/src/config/loader.ts +9 -17
  104. package/src/config/platform-identity.ts +8 -8
  105. package/src/config/profile-text-generation.test.ts +51 -0
  106. package/src/config/profile-text-generation.ts +51 -0
  107. package/src/config/schemas/__tests__/memory-v3.test.ts +12 -0
  108. package/src/config/schemas/llm.ts +7 -2
  109. package/src/config/schemas/mcp.ts +5 -1
  110. package/src/config/schemas/memory-v3.ts +12 -0
  111. package/src/credential-execution/ces-runtime.ts +2 -2
  112. package/src/credential-execution/executable-discovery.ts +36 -134
  113. package/src/credential-execution/process-manager.test.ts +20 -24
  114. package/src/credential-execution/process-manager.ts +24 -19
  115. package/src/daemon/__tests__/conversation-tool-setup.test.ts +43 -0
  116. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +36 -0
  117. package/src/daemon/assistant-attachments.ts +20 -12
  118. package/src/daemon/chat-credential-redaction.ts +16 -1
  119. package/src/daemon/conversation-agent-loop-handlers.ts +74 -3
  120. package/src/daemon/conversation-agent-loop.ts +12 -0
  121. package/src/daemon/conversation-attachments.ts +93 -12
  122. package/src/daemon/conversation-tool-setup.ts +71 -3
  123. package/src/daemon/conversation-turn-finalize.ts +26 -13
  124. package/src/daemon/conversation.ts +72 -17
  125. package/src/daemon/daemon-control.ts +3 -7
  126. package/src/daemon/lifecycle.ts +4 -4
  127. package/src/daemon/mcp-reload-service.ts +1 -1
  128. package/src/daemon/message-types/shared.ts +1 -0
  129. package/src/daemon/orphan-reaper.ts +4 -3
  130. package/src/daemon/persist-media-references.ts +20 -3
  131. package/src/daemon/tool-setup-types.ts +6 -0
  132. package/src/daemon/wake-conversation-ops.ts +50 -15
  133. package/src/desktop/desktop-automation-lease.test.ts +230 -0
  134. package/src/desktop/desktop-automation-lease.ts +90 -6
  135. package/src/desktop/desktop-help.ts +15 -0
  136. package/src/desktop/desktop-stream-bridge.test.ts +65 -10
  137. package/src/desktop/desktop-stream-bridge.ts +3 -3
  138. package/src/desktop/desktop-wallpaper-renderer.ts +132 -0
  139. package/src/desktop/desktop-wallpaper-worker.ts +17 -0
  140. package/src/desktop/desktop-wallpaper.test.ts +48 -7
  141. package/src/desktop/desktop-wallpaper.ts +34 -102
  142. package/src/desktop/virtual-desktop-feature.ts +1 -1
  143. package/src/mcp/__tests__/manager-tool-caps.test.ts +111 -0
  144. package/src/mcp/__tests__/startup.test.ts +30 -7
  145. package/src/mcp/__tests__/tool-caps.test.ts +107 -0
  146. package/src/mcp/manager.ts +168 -101
  147. package/src/mcp/startup.ts +33 -13
  148. package/src/mcp/tool-caps.ts +176 -0
  149. package/src/messaging/provider-message-metadata.ts +3 -3
  150. package/src/notifications/__tests__/copy-composer.test.ts +70 -0
  151. package/src/notifications/copy-composer.ts +11 -3
  152. package/src/notifications/schedule-result-producer.ts +4 -6
  153. package/src/oauth/AGENTS.md +2 -0
  154. package/src/oauth/__tests__/identity-verifier.test.ts +36 -1
  155. package/src/oauth/identity-verifier.ts +24 -0
  156. package/src/oauth/oauth-store.ts +11 -1
  157. package/src/oauth/provider-serializer.ts +1 -0
  158. package/src/oauth/seed-providers.ts +12 -0
  159. package/src/permissions/confirmation-guardian-request.test.ts +16 -0
  160. package/src/permissions/confirmation-guardian-request.ts +2 -3
  161. package/src/permissions/question-prompter.test.ts +31 -0
  162. package/src/permissions/question-prompter.ts +2 -0
  163. package/src/persistence/attachments-store.ts +84 -44
  164. package/src/persistence/conversation-crud.ts +111 -20
  165. package/src/persistence/conversation-plugin-facade.ts +30 -0
  166. package/src/persistence/conversation-tool-surface.ts +135 -0
  167. package/src/persistence/conversation-types.test.ts +32 -0
  168. package/src/persistence/conversation-types.ts +26 -4
  169. package/src/persistence/migrations/378-create-conversation-tool-surfaces.test.ts +78 -0
  170. package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +29 -0
  171. package/src/persistence/migrations/379-oauth-providers-response-ok-field.test.ts +92 -0
  172. package/src/persistence/migrations/379-oauth-providers-response-ok-field.ts +32 -0
  173. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.test.ts +90 -0
  174. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.ts +34 -0
  175. package/src/persistence/schema/conversation-tool-surfaces.ts +32 -0
  176. package/src/persistence/schema/index.ts +1 -0
  177. package/src/persistence/schema/oauth.ts +1 -0
  178. package/src/persistence/steps.ts +24 -3
  179. package/src/plugin-api/conversation-turn.ts +31 -7
  180. package/src/plugin-api/index.ts +11 -1
  181. package/src/plugin-api/plugin-channel-turn-trust.test.ts +133 -0
  182. package/src/plugin-api/plugin-channel-turn-trust.ts +71 -0
  183. package/src/plugins/defaults/memory/AGENTS.md +39 -5
  184. package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +365 -0
  185. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +43 -0
  186. package/src/plugins/defaults/memory/__tests__/conversation-memory-purge.test.ts +1 -0
  187. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +2 -0
  188. package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +17 -0
  189. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +74 -0
  190. package/src/plugins/defaults/memory/__tests__/memory-run-evidence.test.ts +161 -0
  191. package/src/plugins/defaults/memory/__tests__/relocated-memory-test-rows.ts +10 -0
  192. package/src/plugins/defaults/memory/buffer-file.ts +354 -0
  193. package/src/plugins/defaults/memory/buffer-format.ts +40 -0
  194. package/src/plugins/defaults/memory/context-search/agent-runner.ts +1 -2
  195. package/src/plugins/defaults/memory/context-search/format.ts +2 -1
  196. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -1
  197. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +2 -1
  198. package/src/plugins/defaults/memory/conversation-memory-purge.ts +4 -0
  199. package/src/plugins/defaults/memory/graph/capability-seed.ts +1 -2
  200. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -42
  201. package/src/plugins/defaults/memory/host-utils.ts +0 -10
  202. package/src/plugins/defaults/memory/injectors.ts +4 -3
  203. package/src/plugins/defaults/memory/memory-retrospective-job.ts +72 -183
  204. package/src/plugins/defaults/memory/memory-run-evidence.ts +213 -0
  205. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +1 -1
  206. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +407 -99
  207. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +10 -0
  208. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +107 -7
  209. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +307 -86
  210. package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +34 -0
  211. package/src/plugins/defaults/memory/substrate/page-index.ts +2 -1
  212. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +89 -49
  213. package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
  214. package/src/plugins/defaults/memory/tools.ts +1 -1
  215. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +2 -2
  216. package/src/plugins/defaults/memory/v1/graph/extraction.ts +2 -1
  217. package/src/plugins/defaults/memory/v1/graph/retriever.ts +1 -1
  218. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +5 -0
  219. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +5 -2
  220. package/src/plugins/defaults/memory/v2/reranker.ts +2 -1
  221. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +81 -1
  222. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +87 -0
  223. package/src/plugins/defaults/memory/v3/__tests__/plugin-schema.test.ts +12 -0
  224. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +183 -4
  225. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +103 -2
  226. package/src/plugins/defaults/memory/v3/card.ts +2 -1
  227. package/src/plugins/defaults/memory/v3/injector.ts +212 -178
  228. package/src/plugins/defaults/memory/v3/orchestrate.ts +112 -25
  229. package/src/plugins/defaults/memory/v3/plugin-schema.ts +54 -4
  230. package/src/plugins/defaults/memory/v3/pool-log-store.ts +253 -1
  231. package/src/plugins/defaults/memory/v3/pool-select.test.ts +35 -2
  232. package/src/plugins/defaults/memory/v3/pool-select.ts +31 -19
  233. package/src/plugins/defaults/memory/v3/sections.ts +2 -1
  234. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +41 -14
  235. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +4 -1
  236. package/src/plugins/defaults/tool-result-truncate/terminal.ts +1 -46
  237. package/src/prompts/__tests__/parallel-tasks-section.test.ts +25 -0
  238. package/src/prompts/delegation-gate.ts +57 -0
  239. package/src/prompts/system-prompt.ts +14 -32
  240. package/src/providers/inference/adapter-factory.ts +6 -0
  241. package/src/providers/jev/client.test.ts +260 -0
  242. package/src/providers/jev/client.ts +518 -0
  243. package/src/providers/model-catalog.ts +58 -3
  244. package/src/providers/server-tool-pairing.ts +16 -7
  245. package/src/runtime/AGENTS.md +2 -2
  246. package/src/runtime/__tests__/agent-wake.test.ts +94 -1
  247. package/src/runtime/agent-wake.ts +31 -4
  248. package/src/runtime/guardian-action-service.ts +2 -17
  249. package/src/runtime/guardian-reply-router.ts +1 -8
  250. package/src/runtime/http-server.ts +2 -2
  251. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +89 -0
  252. package/src/runtime/migrations/vbundle-import-policy.ts +33 -11
  253. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +24 -2
  254. package/src/runtime/routes/channel-route-shared.ts +1 -9
  255. package/src/runtime/routes/conversation-routes.ts +29 -4
  256. package/src/runtime/routes/desktop-setup-routes.test.ts +2 -2
  257. package/src/runtime/routes/desktop-setup-routes.ts +8 -5
  258. package/src/runtime/routes/guardian-approval-interception.ts +24 -0
  259. package/src/runtime/routes/inbound-message-handler.ts +2 -3
  260. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +1 -1
  261. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -4
  262. package/src/runtime/routes/inference-profile-session-handler.ts +11 -1
  263. package/src/runtime/routes/inference-profiles-routes.ts +9 -0
  264. package/src/runtime/routes/oauth-commands-routes.ts +57 -54
  265. package/src/runtime/routes/oauth-providers.ts +4 -0
  266. package/src/runtime/routes/oauth-request-hints.test.ts +246 -0
  267. package/src/runtime/routes/oauth-request-hints.ts +149 -0
  268. package/src/runtime/routes/schedule-routes.ts +2 -0
  269. package/src/runtime/routes/secret-routes.ts +99 -119
  270. package/src/schedule/__tests__/worker-mcp-bootstrap.test.ts +107 -0
  271. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +173 -0
  272. package/src/schedule/__tests__/worker-mcp-tools.test.ts +13 -47
  273. package/src/schedule/run-script.ts +2 -2
  274. package/src/schedule/scheduler.ts +47 -11
  275. package/src/schedule/tool-surface-readiness.ts +65 -0
  276. package/src/schedule/worker-mcp.ts +85 -0
  277. package/src/schedule/worker.ts +9 -31
  278. package/src/security/secure-keys.ts +60 -48
  279. package/src/tools/ask-question/ask-question-tool.test.ts +167 -5
  280. package/src/tools/ask-question/ask-question-tool.ts +119 -23
  281. package/src/tools/browser/browser-execution.ts +7 -1
  282. package/src/tools/host-terminal/host-shell.ts +12 -6
  283. package/src/tools/shared/filesystem/file-ops-service.ts +1 -31
  284. package/src/tools/shared/shell-output.test.ts +10 -0
  285. package/src/tools/shared/shell-output.ts +14 -2
  286. package/src/tools/skills/sandbox-runner.ts +13 -2
  287. package/src/tools/skills/scaffold-managed.ts +2 -1
  288. package/src/tools/terminal/__tests__/safe-env.test.ts +33 -4
  289. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -14
  290. package/src/tools/terminal/safe-env.ts +35 -24
  291. package/src/tools/terminal/sanitized-bash.ts +15 -2
  292. package/src/tools/terminal/shell-launch.test.ts +162 -0
  293. package/src/tools/terminal/shell.test.ts +29 -0
  294. package/src/tools/terminal/shell.ts +13 -7
  295. package/src/util/browser-human-verification.ts +14 -0
  296. package/src/util/host-process.test.ts +17 -1
  297. package/src/util/host-process.ts +24 -0
  298. package/src/util/unicode.ts +29 -0
@@ -28,6 +28,9 @@ const MANAGED_PROVIDERS = [
28
28
  ] as const;
29
29
 
30
30
  let platformBaseUrlOverride: string | undefined;
31
+ let platformAssistantIdOverride: string | undefined;
32
+ let platformOrganizationIdOverride: string | undefined;
33
+ let platformUserIdOverride: string | undefined;
31
34
 
32
35
  mock.module("@google/genai", () => ({
33
36
  GoogleGenAI: class MockGoogleGenAI {
@@ -54,9 +57,19 @@ mock.module("@google/genai", () => ({
54
57
 
55
58
  mock.module("../config/env.js", () => ({
56
59
  getPlatformBaseUrl: () => PLATFORM_BASE_URL,
60
+ getPlatformAssistantId: () => platformAssistantIdOverride,
57
61
  setPlatformBaseUrl: (value: string | undefined) => {
58
62
  platformBaseUrlOverride = value;
59
63
  },
64
+ setPlatformAssistantId: (value: string | undefined) => {
65
+ platformAssistantIdOverride = value;
66
+ },
67
+ setPlatformOrganizationId: (value: string | undefined) => {
68
+ platformOrganizationIdOverride = value;
69
+ },
70
+ setPlatformUserId: (value: string | undefined) => {
71
+ platformUserIdOverride = value;
72
+ },
60
73
  }));
61
74
 
62
75
  mock.module("../providers/provider-secret-catalog.js", () => ({
@@ -182,6 +195,9 @@ describe("secret routes managed proxy registry sync", () => {
182
195
  metadataDeletes.length = 0;
183
196
  lastGeminiConstructorOpts = null;
184
197
  platformBaseUrlOverride = undefined;
198
+ platformAssistantIdOverride = undefined;
199
+ platformOrganizationIdOverride = undefined;
200
+ platformUserIdOverride = undefined;
185
201
  providerRefreshCalls = 0;
186
202
  identitySyncCalls = 0;
187
203
  avatarSyncCalls = 0;
@@ -254,32 +270,38 @@ describe("secret routes managed proxy registry sync", () => {
254
270
  expect(providerRefreshCalls).toBe(2);
255
271
  });
256
272
 
257
- test("deleting vellum:assistant_api_key clears managed fallback providers immediately", async () => {
273
+ test("deleting a vellum credential is rejected without touching the vault", async () => {
258
274
  secureKeyStore[ASSISTANT_API_KEY_PATH] = "ast-managed-key";
275
+ secureKeyStore[PLATFORM_BASE_URL_PATH] = "https://managed.example.com";
276
+ platformBaseUrlOverride = "https://managed.example.com";
277
+ platformAssistantIdOverride = "asst-1";
259
278
  await initializeProviders(getConfig());
260
279
 
261
- for (const provider of MANAGED_PROVIDERS) {
262
- expect(listProviders()).toContain(provider);
263
- expect(getProviderRoutingSource(provider)).toBe("managed-proxy");
280
+ for (const name of [
281
+ "vellum:assistant_api_key",
282
+ "vellum:platform_base_url",
283
+ "vellum:webhook_secret",
284
+ "vellum:platform_assistant_id",
285
+ ]) {
286
+ await expect(deleteCredential(name)).rejects.toThrow(
287
+ "Vellum credentials cannot be deleted",
288
+ );
264
289
  }
265
290
 
266
- await deleteCredential("vellum:assistant_api_key");
267
-
268
- expect(secureKeyStore[ASSISTANT_API_KEY_PATH]).toBeUndefined();
269
- expect(metadataDeletes).toEqual([
270
- { service: "vellum", field: "assistant_api_key" },
271
- ]);
272
- expect(listProviders()).toEqual([]);
291
+ expect(secureKeyStore[ASSISTANT_API_KEY_PATH]).toBe("ast-managed-key");
292
+ expect(secureKeyStore[PLATFORM_BASE_URL_PATH]).toBe(
293
+ "https://managed.example.com",
294
+ );
295
+ expect(platformBaseUrlOverride).toBe("https://managed.example.com");
296
+ expect(platformAssistantIdOverride).toBe("asst-1");
297
+ expect(metadataDeletes).toEqual([]);
298
+ expect(providerRefreshCalls).toBe(0);
273
299
  });
274
300
 
275
301
  test("managed proxy credential writes notify live-conversation refresh listeners", async () => {
276
302
  await addCredential("vellum:assistant_api_key", "ast-managed-key");
277
303
 
278
304
  expect(providerRefreshCalls).toBe(1);
279
-
280
- await deleteCredential("vellum:assistant_api_key");
281
-
282
- expect(providerRefreshCalls).toBe(2);
283
305
  });
284
306
 
285
307
  /**
@@ -326,7 +348,6 @@ describe("secret routes managed proxy registry sync", () => {
326
348
  test("storing a platform registration credential enqueues name and avatar syncs", async () => {
327
349
  for (const name of [
328
350
  "vellum:assistant_api_key",
329
- "vellum:platform_assistant_id",
330
351
  "vellum:platform_base_url",
331
352
  ]) {
332
353
  identitySyncCalls = 0;
@@ -339,7 +360,6 @@ describe("secret routes managed proxy registry sync", () => {
339
360
 
340
361
  test("unrelated credentials do not enqueue platform syncs", async () => {
341
362
  await addCredential("openrouter:api_key", "openrouter-key");
342
- await addCredential("vellum:platform_user_id", "user-1");
343
363
 
344
364
  expect(identitySyncCalls).toBe(0);
345
365
  expect(avatarSyncCalls).toBe(0);
@@ -349,13 +369,61 @@ describe("secret routes managed proxy registry sync", () => {
349
369
  failSecureKeyWrites = true;
350
370
 
351
371
  await expect(
352
- addCredential("vellum:platform_assistant_id", "asst-1"),
372
+ addCredential("vellum:platform_base_url", "https://managed.example.com"),
353
373
  ).rejects.toThrow("Failed to store credential");
354
374
 
355
375
  expect(identitySyncCalls).toBe(0);
356
376
  expect(avatarSyncCalls).toBe(0);
357
377
  });
358
378
 
379
+ test("unknown vellum credentials are rejected without persisting or binding identity", async () => {
380
+ platformAssistantIdOverride = "existing-asst";
381
+ platformOrganizationIdOverride = "existing-org";
382
+ platformUserIdOverride = "existing-user";
383
+
384
+ await expect(
385
+ addCredential("vellum:platform_assistant_id", "asst-1"),
386
+ ).rejects.toThrow("Unknown vellum credential: platform_assistant_id");
387
+ await expect(
388
+ addCredential("vellum:platform_organization_id", "org-1"),
389
+ ).rejects.toThrow("Unknown vellum credential: platform_organization_id");
390
+ await expect(
391
+ addCredential("vellum:platform_user_id", "user-1"),
392
+ ).rejects.toThrow("Unknown vellum credential: platform_user_id");
393
+
394
+ expect(
395
+ secureKeyStore[credentialKey("vellum", "platform_assistant_id")],
396
+ ).toBeUndefined();
397
+ expect(
398
+ secureKeyStore[credentialKey("vellum", "platform_organization_id")],
399
+ ).toBeUndefined();
400
+ expect(
401
+ secureKeyStore[credentialKey("vellum", "platform_user_id")],
402
+ ).toBeUndefined();
403
+ expect(metadataUpserts).toEqual([]);
404
+ expect(platformAssistantIdOverride).toBe("existing-asst");
405
+ expect(platformOrganizationIdOverride).toBe("existing-org");
406
+ expect(platformUserIdOverride).toBe("existing-user");
407
+ expect(identitySyncCalls).toBe(0);
408
+ expect(avatarSyncCalls).toBe(0);
409
+ });
410
+
411
+ test("an unknown vellum credential does not delete leftover vault copies", async () => {
412
+ const key = credentialKey("vellum", "platform_assistant_id");
413
+ secureKeyStore[key] = "leftover-asst";
414
+ platformAssistantIdOverride = "asst-1";
415
+
416
+ await expect(
417
+ addCredential("vellum:platform_assistant_id", " "),
418
+ ).rejects.toThrow("Unknown vellum credential: platform_assistant_id");
419
+
420
+ expect(secureKeyStore[key]).toBe("leftover-asst");
421
+ expect(platformAssistantIdOverride).toBe("asst-1");
422
+ expect(metadataDeletes).toEqual([]);
423
+ expect(identitySyncCalls).toBe(0);
424
+ expect(avatarSyncCalls).toBe(0);
425
+ });
426
+
359
427
  test("storing vellum:platform_base_url sets override and triggers initializeProviders", async () => {
360
428
  await addCredential(
361
429
  "vellum:platform_base_url",
@@ -389,17 +457,4 @@ describe("secret routes managed proxy registry sync", () => {
389
457
  expect(getProviderRoutingSource(provider)).toBe("managed-proxy");
390
458
  }
391
459
  });
392
-
393
- test("deleting vellum:platform_base_url clears override and re-initializes providers", async () => {
394
- secureKeyStore[PLATFORM_BASE_URL_PATH] = "https://managed.example.com";
395
- platformBaseUrlOverride = "https://managed.example.com";
396
-
397
- await deleteCredential("vellum:platform_base_url");
398
-
399
- expect(secureKeyStore[PLATFORM_BASE_URL_PATH]).toBeUndefined();
400
- expect(platformBaseUrlOverride).toBeUndefined();
401
- expect(metadataDeletes).toEqual([
402
- { service: "vellum", field: "platform_base_url" },
403
- ]);
404
- });
405
460
  });
@@ -222,39 +222,34 @@ describe("secrets_add credential transcript scrub", () => {
222
222
  expect(scrubbedValues).toEqual([CREDENTIAL_VALUE]);
223
223
  });
224
224
 
225
- test("non-secret platform fields are stored but never scrubbed", async () => {
226
- for (const field of [
227
- "platform_assistant_id",
228
- "platform_organization_id",
229
- "platform_user_id",
230
- "platform_base_url",
231
- ]) {
232
- const result = await addRoute.handler({
233
- body: {
234
- type: "credential",
235
- name: `vellum:${field}`,
236
- value: "0198f4c2-1111-2222-3333-444455556666",
237
- },
238
- });
239
- expect(result).toEqual(expect.objectContaining({ success: true }));
240
- }
241
-
242
- // All four stored, none scrubbed — they are benign UUIDs/URLs that
243
- // legitimately appear in transcripts.
244
- expect(secureStore.size).toBe(4);
245
- expect(scrubbedValues).toEqual([]);
246
- });
247
-
248
- test("an identity-field delete stores nothing and never scrubs", async () => {
225
+ test("non-secret platform fields are not scrubbed", async () => {
249
226
  const result = await addRoute.handler({
250
227
  body: {
251
228
  type: "credential",
252
- name: "vellum:platform_assistant_id",
253
- value: " ",
229
+ name: "vellum:platform_base_url",
230
+ value: "0198f4c2-1111-2222-3333-444455556666",
254
231
  },
255
232
  });
256
-
257
233
  expect(result).toEqual(expect.objectContaining({ success: true }));
234
+
235
+ expect(secureStore.size).toBe(1);
236
+ expect(secureStore.get(credentialKey("vellum", "platform_base_url"))).toBe(
237
+ "0198f4c2-1111-2222-3333-444455556666",
238
+ );
239
+ expect(scrubbedValues).toEqual([]);
240
+ });
241
+
242
+ test("an unknown vellum credential is rejected and never triggers a scrub", async () => {
243
+ await expect(
244
+ addRoute.handler({
245
+ body: {
246
+ type: "credential",
247
+ name: "vellum:platform_assistant_id",
248
+ value: " ",
249
+ },
250
+ }),
251
+ ).rejects.toBeInstanceOf(BadRequestError);
252
+
258
253
  expect(secureStore.size).toBe(0);
259
254
  expect(scrubbedValues).toEqual([]);
260
255
  });
@@ -187,6 +187,26 @@ describe("Anthropic keys", () => {
187
187
  });
188
188
  });
189
189
 
190
+ // ---------------------------------------------------------------------------
191
+ // Resend
192
+ // ---------------------------------------------------------------------------
193
+ describe("Resend keys", () => {
194
+ const key = `re_${"a".repeat(8)}_${"b".repeat(24)}`;
195
+
196
+ test("detects re_ key", () => {
197
+ expectMatch(key, "Resend API Key");
198
+ });
199
+
200
+ test("redacts a key echoed from shell output", () => {
201
+ const out = redactSecrets(`$ cat .env\nRESEND=${key}\n`);
202
+ expect(out).not.toContain(key);
203
+ });
204
+
205
+ test("ignores re_ identifiers that are not key-shaped", () => {
206
+ expectNoMatch("re_0_of_1_is_deprecated_6387");
207
+ });
208
+ });
209
+
190
210
  // ---------------------------------------------------------------------------
191
211
  // OpenAI
192
212
  // ---------------------------------------------------------------------------
@@ -63,4 +63,12 @@ describe("secure-keys managed CES failover", () => {
63
63
  expect(rpcCall).toHaveBeenCalledTimes(1);
64
64
  expect(globalThis.fetch).toHaveBeenCalledTimes(1);
65
65
  });
66
+
67
+ test("uses CES HTTP only after lazy IPC finds no socket", async () => {
68
+ const start = Date.now();
69
+ expect(await getSecureKeyAsync("openai")).toBe("http-secret");
70
+ expect(Date.now() - start).toBeLessThan(1_000);
71
+ expect(getActiveBackendName()).toBe("ces-http");
72
+ expect(globalThis.fetch).toHaveBeenCalledTimes(1);
73
+ });
66
74
  });
@@ -61,7 +61,6 @@ describe("secure-keys", () => {
61
61
  delete process.env.VELLUM_DESKTOP_APP;
62
62
  delete process.env.IS_CONTAINERIZED;
63
63
  delete process.env.CES_CREDENTIAL_URL;
64
- delete process.env.CES_LOCAL_SOCKET;
65
64
  });
66
65
 
67
66
  afterAll(() => {
@@ -79,6 +78,13 @@ describe("secure-keys", () => {
79
78
  expect(await getSecureKeyAsync("openai")).toBe("sk-openai-789");
80
79
  });
81
80
 
81
+ test("does not poll for a CES socket when none is present", async () => {
82
+ const start = Date.now();
83
+ await getSecureKeyAsync("openai");
84
+ expect(Date.now() - start).toBeLessThan(1_000);
85
+ expect(getActiveBackendName()).toBe("encrypted-store");
86
+ });
87
+
82
88
  test("get returns undefined for nonexistent key", async () => {
83
89
  expect(await getSecureKeyAsync("nonexistent")).toBeUndefined();
84
90
  });
@@ -552,7 +558,6 @@ describe("secure-keys", () => {
552
558
  test("reads report unreachable (indeterminate), never absent", async () => {
553
559
  process.env.IS_CONTAINERIZED = "1";
554
560
  delete process.env.CES_CREDENTIAL_URL;
555
- delete process.env.CES_LOCAL_SOCKET;
556
561
  _resetBackend();
557
562
 
558
563
  const result = await getSecureKeyResultAsync("openai");
@@ -564,7 +569,6 @@ describe("secure-keys", () => {
564
569
  test("does not pin the unreachable result: a ready CES client wins the next read", async () => {
565
570
  process.env.IS_CONTAINERIZED = "1";
566
571
  delete process.env.CES_CREDENTIAL_URL;
567
- delete process.env.CES_LOCAL_SOCKET;
568
572
  _resetBackend();
569
573
 
570
574
  expect((await getSecureKeyResultAsync("openai")).unreachable).toBe(true);
@@ -0,0 +1,107 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { analyzeServerToolPairing } from "../providers/server-tool-pairing.js";
4
+ import type { ContentBlock, Message } from "../providers/types.js";
5
+
6
+ const DEFERRED_ID = "srvtoolu_deferred";
7
+
8
+ const opener: Message = {
9
+ role: "user",
10
+ content: [{ type: "text", text: "Check both" }],
11
+ };
12
+
13
+ /** Assistant turn calling a client tool and a native web search together. */
14
+ const mixedTail: Message = {
15
+ role: "assistant",
16
+ content: [
17
+ { type: "tool_use", id: "tu_a", name: "web_fetch", input: {} },
18
+ {
19
+ type: "server_tool_use",
20
+ id: DEFERRED_ID,
21
+ name: "web_search",
22
+ input: { query: "news" },
23
+ },
24
+ ],
25
+ };
26
+
27
+ const clientResult: ContentBlock = {
28
+ type: "tool_result",
29
+ tool_use_id: "tu_a",
30
+ content: "Error: HTTP 404",
31
+ is_error: true,
32
+ };
33
+
34
+ const guidance: ContentBlock = {
35
+ type: "text",
36
+ text: "<system_notice>retry</system_notice>",
37
+ };
38
+
39
+ describe("analyzeServerToolPairing", () => {
40
+ test("an unanswered use with nothing after it is deferred", () => {
41
+ const pairing = analyzeServerToolPairing([opener, mixedTail]);
42
+
43
+ expect(pairing.deferredUseIds).toEqual(new Set([DEFERRED_ID]));
44
+ expect(pairing.resolvedPairIds.size).toBe(0);
45
+ });
46
+
47
+ test("an unanswered use answered by tool_result blocks alone is deferred", () => {
48
+ const pairing = analyzeServerToolPairing([
49
+ opener,
50
+ mixedTail,
51
+ { role: "user", content: [clientResult] },
52
+ ]);
53
+
54
+ expect(pairing.deferredUseIds).toEqual(new Set([DEFERRED_ID]));
55
+ });
56
+
57
+ test("guidance text after the tool_result makes the use an orphan, not a deferral", () => {
58
+ // The trailing text closes the assistant turn on the provider side, which
59
+ // then rejects the unanswered use as unpaired; the repair passes stamp a
60
+ // synthetic result instead of preserving it.
61
+ const pairing = analyzeServerToolPairing([
62
+ opener,
63
+ mixedTail,
64
+ { role: "user", content: [clientResult, guidance] },
65
+ ]);
66
+
67
+ expect(pairing.deferredUseIds.size).toBe(0);
68
+ expect(pairing.resolvedPairIds.size).toBe(0);
69
+ });
70
+
71
+ test("a user text message after the tool_result message makes the use an orphan", () => {
72
+ const pairing = analyzeServerToolPairing([
73
+ opener,
74
+ mixedTail,
75
+ { role: "user", content: [clientResult] },
76
+ { role: "user", content: [{ type: "text", text: "also do this" }] },
77
+ ]);
78
+
79
+ expect(pairing.deferredUseIds.size).toBe(0);
80
+ });
81
+
82
+ test("a use answered in its own message is resolved and never deferred", () => {
83
+ const pairing = analyzeServerToolPairing([
84
+ opener,
85
+ {
86
+ role: "assistant",
87
+ content: [
88
+ {
89
+ type: "server_tool_use",
90
+ id: "srvtoolu_done",
91
+ name: "web_search",
92
+ input: { query: "news" },
93
+ },
94
+ {
95
+ type: "web_search_tool_result",
96
+ tool_use_id: "srvtoolu_done",
97
+ content: [],
98
+ },
99
+ ],
100
+ },
101
+ { role: "user", content: [guidance] },
102
+ ]);
103
+
104
+ expect(pairing.resolvedPairIds).toEqual(new Set(["srvtoolu_done"]));
105
+ expect(pairing.deferredUseIds.size).toBe(0);
106
+ });
107
+ });
@@ -1054,14 +1054,15 @@ describe("ingress-dependent setup skills declare public-ingress intentionally",
1054
1054
  expect(includes ?? []).not.toContain("public-ingress");
1055
1055
  });
1056
1056
 
1057
- test("telegram-setup documents the private-chat limitation", () => {
1057
+ test("telegram-setup documents which chats are supported and how a group is addressed", () => {
1058
1058
  const content = readFileSync(
1059
1059
  join(FIRST_PARTY_SKILLS_DIR, "telegram-setup", "SKILL.md"),
1060
1060
  "utf-8",
1061
1061
  );
1062
- expect(content).toMatch(/private-chat only/i);
1063
- expect(content).toMatch(/group, supergroup, and channel/i);
1064
- expect(content).toMatch(/button taps/i);
1062
+ expect(content).toMatch(/private chats and in groups and supergroups/i);
1063
+ expect(content).toMatch(/@mention of the bot's username/i);
1064
+ expect(content).toMatch(/reply to one of its posts/i);
1065
+ expect(content).toMatch(/Broadcast channels are not supported/i);
1065
1066
  });
1066
1067
 
1067
1068
  test("twilio-setup includes public-ingress", () => {