@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
@@ -0,0 +1,68 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { AssistantOutboundAttachmentSchema } from "./events/assistant-outbound-attachment.js";
4
+ import { ConversationMessageAttachmentSchema } from "./responses/conversation-message.js";
5
+
6
+ const outboundFixture = {
7
+ id: "attachment-1",
8
+ filename: "screenshot.png",
9
+ mimeType: "image/png",
10
+ data: "c2NyZWVuc2hvdA==",
11
+ sourceType: "tool_block" as const,
12
+ };
13
+
14
+ const historyFixture = {
15
+ id: "attachment-1",
16
+ filename: "screenshot.png",
17
+ mimeType: "image/png",
18
+ sizeBytes: 10,
19
+ kind: "image",
20
+ };
21
+
22
+ describe("computer-use screenshot attachment provenance", () => {
23
+ test("new schemas accept the marker and its absence", () => {
24
+ expect(
25
+ AssistantOutboundAttachmentSchema.parse({
26
+ ...outboundFixture,
27
+ computerUseScreenshot: true,
28
+ }).computerUseScreenshot,
29
+ ).toBe(true);
30
+ expect(
31
+ AssistantOutboundAttachmentSchema.parse(outboundFixture)
32
+ .computerUseScreenshot,
33
+ ).toBeUndefined();
34
+
35
+ expect(
36
+ ConversationMessageAttachmentSchema.parse({
37
+ ...historyFixture,
38
+ computerUseScreenshot: true,
39
+ }).computerUseScreenshot,
40
+ ).toBe(true);
41
+ expect(
42
+ ConversationMessageAttachmentSchema.parse(historyFixture)
43
+ .computerUseScreenshot,
44
+ ).toBeUndefined();
45
+ });
46
+
47
+ test("legacy object parsers tolerate and strip the additive marker", () => {
48
+ const legacyOutbound = AssistantOutboundAttachmentSchema.omit({
49
+ computerUseScreenshot: true,
50
+ });
51
+ const legacyHistory = ConversationMessageAttachmentSchema.omit({
52
+ computerUseScreenshot: true,
53
+ });
54
+
55
+ expect(
56
+ legacyOutbound.parse({
57
+ ...outboundFixture,
58
+ computerUseScreenshot: true,
59
+ }),
60
+ ).toEqual(outboundFixture);
61
+ expect(
62
+ legacyHistory.parse({
63
+ ...historyFixture,
64
+ computerUseScreenshot: true,
65
+ }),
66
+ ).toEqual(historyFixture);
67
+ });
68
+ });
@@ -0,0 +1,55 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ isComputerUseToolCall,
5
+ resolveComputerUseToolName,
6
+ } from "./computer-use-tool.js";
7
+
8
+ describe("isComputerUseToolCall", () => {
9
+ test("recognizes direct and skill-wrapped computer-use calls", () => {
10
+ expect(isComputerUseToolCall("computer_use_click", {})).toBe(true);
11
+ expect(
12
+ isComputerUseToolCall("skill_execute", { tool: "computer_use_type" }),
13
+ ).toBe(true);
14
+ });
15
+
16
+ test("resolves the direct or wrapped computer-use tool name", () => {
17
+ expect(resolveComputerUseToolName("computer_use_click", {})).toBe(
18
+ "computer_use_click",
19
+ );
20
+ expect(
21
+ resolveComputerUseToolName("skill_execute", {
22
+ tool: "computer_use_scroll",
23
+ }),
24
+ ).toBe("computer_use_scroll");
25
+ });
26
+
27
+ test("rejects unrelated media tools and malformed skill input", () => {
28
+ expect(isComputerUseToolCall("browser_screenshot", {})).toBe(false);
29
+ expect(isComputerUseToolCall("file_read", {})).toBe(false);
30
+ expect(isComputerUseToolCall("image_generate", {})).toBe(false);
31
+ expect(isComputerUseToolCall("skill_execute", null)).toBe(false);
32
+ expect(isComputerUseToolCall("skill_execute", "computer_use_click")).toBe(
33
+ false,
34
+ );
35
+ expect(isComputerUseToolCall("skill_execute", { tool: 42 })).toBe(false);
36
+ expect(
37
+ isComputerUseToolCall("skill_execute", {
38
+ tool: "browser_screenshot",
39
+ }),
40
+ ).toBe(false);
41
+ });
42
+
43
+ test("does not classify raw-only recovered skill input", () => {
44
+ expect(
45
+ isComputerUseToolCall("skill_execute", {
46
+ _raw: '{"tool":"computer_use_click"}',
47
+ }),
48
+ ).toBe(false);
49
+ expect(
50
+ resolveComputerUseToolName("skill_execute", {
51
+ _raw: '{"tool":"computer_use_click"}',
52
+ }),
53
+ ).toBeUndefined();
54
+ });
55
+ });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Resolve the host computer-use tool behind a direct or skill-wrapped call.
3
+ *
4
+ * Skills expose their selected tool in structured input. Calls recovered only
5
+ * from `_raw` retain their legacy behavior because this helper deliberately
6
+ * does not parse a second input representation.
7
+ */
8
+ export function resolveComputerUseToolName(
9
+ name: string,
10
+ input: unknown,
11
+ ): string | undefined {
12
+ if (name.startsWith("computer_use_")) {
13
+ return name;
14
+ }
15
+ if (name !== "skill_execute" || input === null || typeof input !== "object") {
16
+ return undefined;
17
+ }
18
+ const tool = (input as Record<string, unknown>).tool;
19
+ return typeof tool === "string" && tool.startsWith("computer_use_")
20
+ ? tool
21
+ : undefined;
22
+ }
23
+
24
+ /** True when a tool call is backed by the host computer-use capability. */
25
+ export function isComputerUseToolCall(name: string, input: unknown): boolean {
26
+ return resolveComputerUseToolName(name, input) !== undefined;
27
+ }
@@ -28,6 +28,9 @@ export const AssistantOutboundAttachmentSchema = z.object({
28
28
  * true and the client should hydrate via the /content endpoint. */
29
29
  data: z.string(),
30
30
  sourceType: z.enum(["sandbox_file", "host_file", "tool_block"]).optional(),
31
+ /** True when this existing computer-use screenshot was linked to the reply
32
+ * automatically. Missing or false identifies legacy or explicit placement. */
33
+ computerUseScreenshot: z.boolean().optional(),
31
34
  /** Original file size in bytes. Present when `data` was omitted to
32
35
  * keep payloads small. */
33
36
  sizeBytes: z.number().optional(),
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+
3
+ /** Transient browser activity changed; refetch desktop status. */
4
+ export const DesktopActivityChangedEventSchema = z.object({
5
+ type: z.literal("desktop_activity_changed"),
6
+ });
7
+
8
+ export type DesktopActivityChangedEvent = z.infer<
9
+ typeof DesktopActivityChangedEventSchema
10
+ >;
@@ -48,6 +48,7 @@ export const QuestionEntrySchema = z.object({
48
48
  description: z.string().optional(),
49
49
  options: z.array(QuestionOptionSchema),
50
50
  freeTextPlaceholder: z.string().optional(),
51
+ presentation: z.literal("virtual_desktop").optional(),
51
52
  });
52
53
 
53
54
  export type QuestionEntry = z.infer<typeof QuestionEntrySchema>;
package/src/api/index.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import { z } from "zod";
2
2
 
3
+ export {
4
+ isComputerUseToolCall,
5
+ resolveComputerUseToolName,
6
+ } from "./computer-use-tool.js";
7
+
3
8
  import { AcpAuthRequiredEventSchema } from "./events/acp-auth-required.js";
4
9
  import { AcpSessionCompletedEventSchema } from "./events/acp-session-completed.js";
5
10
  import { AcpSessionErrorEventSchema } from "./events/acp-session-error.js";
@@ -34,6 +39,7 @@ import { ConversationErrorEventSchema } from "./events/conversation-error.js";
34
39
  import { ConversationInferenceProfileUpdatedEventSchema } from "./events/conversation-inference-profile-updated.js";
35
40
  import { ConversationNoticeEventSchema } from "./events/conversation-notice.js";
36
41
  import { ConversationTitleUpdatedEventSchema } from "./events/conversation-title-updated.js";
42
+ import { DesktopActivityChangedEventSchema } from "./events/desktop-activity-changed.js";
37
43
  import { DiskPressureStatusChangedEventSchema } from "./events/disk-pressure-status-changed.js";
38
44
  import { DocumentCommentCreatedEventSchema } from "./events/document-comment-created.js";
39
45
  import { DocumentCommentDeletedEventSchema } from "./events/document-comment-deleted.js";
@@ -342,6 +348,10 @@ export {
342
348
  type ConversationTitleUpdatedEvent,
343
349
  ConversationTitleUpdatedEventSchema,
344
350
  } from "./events/conversation-title-updated.js";
351
+ export {
352
+ type DesktopActivityChangedEvent,
353
+ DesktopActivityChangedEventSchema,
354
+ } from "./events/desktop-activity-changed.js";
345
355
  export {
346
356
  type DiskPressureBlockedCapability,
347
357
  DiskPressureBlockedCapabilitySchema,
@@ -1000,6 +1010,7 @@ export const AssistantEventSchema = z.discriminatedUnion("type", [
1000
1010
  ConversationInferenceProfileUpdatedEventSchema,
1001
1011
  ConversationNoticeEventSchema,
1002
1012
  ConversationTitleUpdatedEventSchema,
1013
+ DesktopActivityChangedEventSchema,
1003
1014
  DiskPressureStatusChangedEventSchema,
1004
1015
  DocumentCommentCreatedEventSchema,
1005
1016
  DocumentCommentDeletedEventSchema,
@@ -51,6 +51,9 @@ export const ConversationMessageAttachmentSchema = z.object({
51
51
  thumbnailData: z.string().optional(),
52
52
  /** True when the attachment bytes are backed by a file on disk. */
53
53
  fileBacked: z.boolean().optional(),
54
+ /** True when this computer-use screenshot was placed on the reply
55
+ * automatically. Missing or false identifies legacy or explicit placement. */
56
+ computerUseScreenshot: z.boolean().optional(),
54
57
  });
55
58
  export type ConversationMessageAttachment = z.infer<
56
59
  typeof ConversationMessageAttachmentSchema
@@ -213,7 +213,10 @@ function consumeGrantSync(params: ConsumeGrantParams): ConsumeGrantResult {
213
213
  return { ok: true, grant: reqResult.grant };
214
214
  }
215
215
 
216
- log.info(
216
+ // Debug, not info: `consumeGrantForInvocation` and the inline grant wait
217
+ // poll this function, so a miss is the expected steady state while a
218
+ // guardian decides. Hits and the wait's outcome are logged at info.
219
+ log.debug(
217
220
  {
218
221
  event: "approval_primitive_consume_miss",
219
222
  mode: "request_id",
@@ -255,7 +258,7 @@ function consumeGrantSync(params: ConsumeGrantParams): ConsumeGrantResult {
255
258
  return { ok: true, grant: sigResult.grant };
256
259
  }
257
260
 
258
- log.info(
261
+ log.debug(
259
262
  {
260
263
  event: "approval_primitive_consume_miss",
261
264
  mode: "tool_signature",
@@ -193,7 +193,10 @@ function consumeScopedApprovalGrantByRequestId(
193
193
  .get();
194
194
 
195
195
  if (!candidate) {
196
- log.info(
196
+ // Debug, not info: the inline grant wait and the voice retry loop call
197
+ // this every poll, so a miss is the expected steady state while a
198
+ // guardian decides. The caller logs the outcome of the wait.
199
+ log.debug(
197
200
  {
198
201
  event: "scoped_grant_consume_miss",
199
202
  requestId,
@@ -364,7 +367,8 @@ function consumeScopedApprovalGrantByToolSignature(
364
367
  .get();
365
368
 
366
369
  if (!candidate) {
367
- log.info(
370
+ // Debug for the same reason as the request-id miss above: polled.
371
+ log.debug(
368
372
  {
369
373
  event: "scoped_grant_consume_miss",
370
374
  toolName: params.toolName,
@@ -1,4 +1,3 @@
1
- import { getConfig } from "../config/loader.js";
2
1
  import { isVirtualDesktopEnabled } from "../desktop/virtual-desktop-feature.js";
3
2
  import { browserManager } from "../tools/browser/browser-manager.js";
4
3
  import { normalizeBrowserMode } from "../tools/browser/browser-mode.js";
@@ -30,5 +29,5 @@ export function shouldUseVirtualDesktopBrowser(
30
29
  ) {
31
30
  return false;
32
31
  }
33
- return isVirtualDesktopEnabled(getConfig());
32
+ return isVirtualDesktopEnabled();
34
33
  }
@@ -6,7 +6,10 @@
6
6
  * `process.stdout.write`, `process.stderr.write`, `console.log`, and
7
7
  * `console.error` are captured for the duration of the run, so command output
8
8
  * lands in the result regardless of which sink the command writes to;
9
- * `process.exitCode` is reset afterwards.
9
+ * `process.exitCode` is reset afterwards. A write callback is honoured the
10
+ * way the real stream honours it, and `process.exit` records its code as the
11
+ * run's exit code instead of ending the test process, so a command that exits
12
+ * itself once its output has drained is observed rather than fatal.
10
13
  * The caller passes its own (possibly mock-backed) registration function, so
11
14
  * this helper imports nothing from `src/` beyond what any test file may
12
15
  * import itself (see the test-machinery isolation rules in assistant/CLAUDE.md).
@@ -34,18 +37,32 @@ export async function runCliCommand(
34
37
  const stderrChunks: string[] = [];
35
38
  const events: string[] = [];
36
39
 
37
- process.stdout.write = ((chunk: unknown) => {
40
+ const originalExit = process.exit;
41
+ const invokeCallback = (rest: unknown[]): void => {
42
+ const callback = rest.find((arg) => typeof arg === "function");
43
+ if (callback) {
44
+ (callback as () => void)();
45
+ }
46
+ };
47
+ process.stdout.write = ((chunk: unknown, ...rest: unknown[]) => {
38
48
  const text = typeof chunk === "string" ? chunk : String(chunk);
39
49
  stdoutChunks.push(text);
40
50
  events.push(text);
51
+ invokeCallback(rest);
41
52
  return true;
42
53
  }) as typeof process.stdout.write;
43
- process.stderr.write = ((chunk: unknown) => {
54
+ process.stderr.write = ((chunk: unknown, ...rest: unknown[]) => {
44
55
  const text = typeof chunk === "string" ? chunk : String(chunk);
45
56
  stderrChunks.push(text);
46
57
  events.push(text);
58
+ invokeCallback(rest);
47
59
  return true;
48
60
  }) as typeof process.stderr.write;
61
+ process.exit = ((code?: number) => {
62
+ if (code !== undefined) {
63
+ process.exitCode = code;
64
+ }
65
+ }) as typeof process.exit;
49
66
  console.log = (...logArgs: unknown[]) => {
50
67
  const text = logArgs.map(String).join(" ") + "\n";
51
68
  stdoutChunks.push(text);
@@ -75,6 +92,7 @@ export async function runCliCommand(
75
92
  } finally {
76
93
  process.stdout.write = originalStdoutWrite;
77
94
  process.stderr.write = originalStderrWrite;
95
+ process.exit = originalExit;
78
96
  console.log = originalConsoleLog;
79
97
  console.error = originalConsoleError;
80
98
  }
@@ -65,6 +65,7 @@ let installPluginCalls: InstallPluginOptions[] = [];
65
65
  let platformInstallCalls: Array<{ name: string; force?: boolean }> = [];
66
66
  let upgradePluginCalls: UpgradePluginOptions[] = [];
67
67
  let catalogMatches: PluginSearchMatch[] = [];
68
+ let catalogError: Error | null = null;
68
69
 
69
70
  /**
70
71
  * Queued daemon IPC responses. The default (empty queue) is a transport
@@ -162,7 +163,12 @@ mock.module("../../lib/install-from-platform.js", () => ({
162
163
  }));
163
164
 
164
165
  mock.module("../../lib/plugin-catalog-cache.js", () => ({
165
- getPluginCatalog: async () => ({ ref: "main", matches: catalogMatches }),
166
+ getPluginCatalog: async () => {
167
+ if (catalogError) {
168
+ throw catalogError;
169
+ }
170
+ return { ref: "main", matches: catalogMatches };
171
+ },
166
172
  }));
167
173
 
168
174
  mock.module("../../lib/inspect-plugin.js", () => ({
@@ -264,6 +270,7 @@ beforeEach(() => {
264
270
  platformInstallCalls = [];
265
271
  upgradePluginCalls = [];
266
272
  catalogMatches = [];
273
+ catalogError = null;
267
274
  ipcResults = [];
268
275
  inspectResult = null;
269
276
  installTarget = null;
@@ -466,6 +473,18 @@ describe("plugins install - declared-schedules consent", () => {
466
473
  });
467
474
  expect(r.exitCode).toBe(0);
468
475
  });
476
+
477
+ test("falls back to the platform installer when catalog discovery fails", async () => {
478
+ catalogError = new Error("catalog unavailable");
479
+
480
+ const r = await runCommand(["plugins", "install", "example"]);
481
+
482
+ expect(installPluginCalls).toHaveLength(0);
483
+ expect(platformInstallCalls).toEqual([
484
+ { name: "example", force: undefined },
485
+ ]);
486
+ expect(r.exitCode).toBe(0);
487
+ });
469
488
  });
470
489
 
471
490
  describe("plugins upgrade - declared-schedules consent (local fallback)", () => {
@@ -380,6 +380,7 @@ describe("schedules get", () => {
380
380
  mode: "execute",
381
381
  status: "active",
382
382
  routingIntent: "all_channels",
383
+ quiet: false,
383
384
  reuseConversation: false,
384
385
  wakeConversationId: null,
385
386
  isOneShot: false,
@@ -436,9 +437,22 @@ describe("schedules get", () => {
436
437
  expect(output).toContain("Every 30 minutes (UTC)");
437
438
  expect(output).toContain("run heartbeat");
438
439
  expect(output).toContain("all_channels");
440
+ expect(output).toMatch(/Quiet:\s+no/);
439
441
  expect(output).toContain("conv-abc");
440
442
  });
441
443
 
444
+ test("prints Quiet: yes when the schedule suppresses completion notifications", async () => {
445
+ mockIpcResult = {
446
+ ok: true,
447
+ result: { schedule: { ...scheduleFixture, quiet: true } },
448
+ };
449
+
450
+ const { exitCode } = await runCommand(["schedules", "get", "schedule-1"]);
451
+
452
+ expect(exitCode).toBe(0);
453
+ expect(logLines.join("\n")).toMatch(/Quiet:\s+yes/);
454
+ });
455
+
442
456
  test("emits JSON result when --json is set", async () => {
443
457
  mockIpcResult = { ok: true, result: { schedule: scheduleFixture } };
444
458
 
@@ -16,9 +16,10 @@ export const bashHelp: CliCommandHelp = {
16
16
  ],
17
17
  helpText: `
18
18
  Spawns the command with the same sanitized environment, working directory,
19
- and platform shell the bash tool uses for tool-call subprocesses. Vault
20
- bearers such as CES_SERVICE_TOKEN are stripped; CES_LOCAL_SOCKET and workspace
21
- paths are forwarded. The command does not go through the assistant HTTP or IPC
19
+ and platform shell the bash tool uses for tool-call subprocesses. CES
20
+ bootstrap and workspace paths are forwarded so children can reach CES over
21
+ IPC. CES HTTP credentials are also forwarded. The command does not go through
22
+ the assistant HTTP or IPC
22
23
  API, and does not require a running assistant process.
23
24
 
24
25
  Arguments:
@@ -17,6 +17,7 @@ import type {
17
17
  BrowserOperationMeta,
18
18
  OperationField,
19
19
  } from "../../browser/types.js";
20
+ import { DESKTOP_HELP_GUIDANCE } from "../../util/browser-human-verification.js";
20
21
  import type {
21
22
  CliCommandHelp,
22
23
  CliOptionHelp,
@@ -158,7 +159,10 @@ or --use-active-tab. Download waiting is unavailable on this target.
158
159
  Users can interact directly with the expanded desktop. If they ask you to pause,
159
160
  stop and wait. Take a fresh snapshot before resuming. Closing the viewer does not
160
161
  end automation. Run assistant browser --virtual-desktop detach when finished or
161
- blocked, including before asking a question. Chrome stays running.
162
+ blocked. Chrome stays running.
163
+ For logins, use saved credentials first. Securely collect missing credentials
164
+ with assistant credentials prompt, then fill the login form yourself.
165
+ ${DESKTOP_HELP_GUIDANCE}
162
166
  Each subcommand maps to a browser operation and communicates
163
167
  with the assistant process.
164
168
 
@@ -74,9 +74,9 @@ path or connection details. Run this to diagnose credential lookup mismatches
74
74
  for example, when the CLI and the daemon are reading from different stores.
75
75
 
76
76
  Backend types:
77
- encrypted-store Direct file read from keys.enc (standalone CLI, no daemon)
78
- ces-rpc Delegates to the running CES process via stdio RPC (daemon)
79
- ces-http Delegates to CES sidecar over HTTP (containerized/Docker mode)
77
+ encrypted-store Direct file read from keys.enc (standalone CLI, no assistant)
78
+ ces-rpc Delegates to the running CES process via socket RPC
79
+ ces-http Managed failover when the CES socket is unavailable
80
80
 
81
81
  Also shows the CREDENTIAL_SECURITY_DIR, GATEWAY_SECURITY_DIR, and
82
82
  VELLUM_WORKSPACE_DIR env vars so you can confirm which instance directory this
@@ -258,6 +258,11 @@ Examples:
258
258
  description:
259
259
  "Dot-notation path to a boolean field that must be truthy for the response to be valid",
260
260
  },
261
+ {
262
+ flags: "--response-ok-field <field>",
263
+ description:
264
+ "Dot-notation path to the boolean every response body carries to report success (Slack: ok); an explicit false under a 2xx fails the request",
265
+ },
261
266
  {
262
267
  flags: "--setup-notes <json>",
263
268
  description:
@@ -430,6 +435,11 @@ Examples:
430
435
  flags: "--identity-ok-field <field>",
431
436
  description: "Dot-notation path to a boolean ok field",
432
437
  },
438
+ {
439
+ flags: "--response-ok-field <field>",
440
+ description:
441
+ "Dot-notation path to the boolean every response body carries to report success",
442
+ },
433
443
  {
434
444
  flags: "--setup-notes <json>",
435
445
  description: "JSON array of setup instruction notes",
@@ -49,6 +49,7 @@ interface SerializedProvider {
49
49
  identityResponsePaths?: string[] | null;
50
50
  identityFormat?: string | null;
51
51
  identityOkField?: string | null;
52
+ responseOkField?: string | null;
52
53
  availableScopes?: unknown;
53
54
  setupNotes?: string[] | unknown;
54
55
  featureFlag?: string | null;
@@ -217,6 +218,9 @@ function formatProviderDetail(p: SerializedProvider): string {
217
218
  if (p.identityOkField) {
218
219
  lines.push(` Identity ok field: ${p.identityOkField}`);
219
220
  }
221
+ if (p.responseOkField) {
222
+ lines.push(` Response ok field: ${p.responseOkField}`);
223
+ }
220
224
  if (p.setupNotes) {
221
225
  if (Array.isArray(p.setupNotes)) {
222
226
  lines.push(
@@ -403,6 +407,7 @@ export function registerProviderCommands(oauth: Command): void {
403
407
  identityResponsePaths?: string;
404
408
  identityFormat?: string;
405
409
  identityOkField?: string;
410
+ responseOkField?: string;
406
411
  setupNotes?: string;
407
412
  availableScopes?: string;
408
413
  },
@@ -505,6 +510,9 @@ export function registerProviderCommands(oauth: Command): void {
505
510
  if (opts.identityOkField !== undefined) {
506
511
  body.identity_ok_field = opts.identityOkField;
507
512
  }
513
+ if (opts.responseOkField !== undefined) {
514
+ body.response_ok_field = opts.responseOkField;
515
+ }
508
516
  if (opts.setupNotes !== undefined) {
509
517
  body.setup_notes = JSON.parse(opts.setupNotes);
510
518
  }
@@ -578,6 +586,7 @@ export function registerProviderCommands(oauth: Command): void {
578
586
  identityResponsePaths?: string;
579
587
  identityFormat?: string;
580
588
  identityOkField?: string;
589
+ responseOkField?: string;
581
590
  setupNotes?: string;
582
591
  availableScopes?: string;
583
592
  },
@@ -686,6 +695,9 @@ export function registerProviderCommands(oauth: Command): void {
686
695
  if (opts.identityOkField !== undefined) {
687
696
  body.identity_ok_field = opts.identityOkField;
688
697
  }
698
+ if (opts.responseOkField !== undefined) {
699
+ body.response_ok_field = opts.responseOkField;
700
+ }
689
701
  if (opts.setupNotes !== undefined) {
690
702
  body.setup_notes = JSON.parse(opts.setupNotes);
691
703
  }