@vellumai/assistant 0.11.6 → 0.11.7-staging.2

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 (478) hide show
  1. package/ARCHITECTURE.md +31 -19
  2. package/Dockerfile +1 -0
  3. package/docs/architecture/security.md +3 -3
  4. package/docs/flux-turn-detection-spike.md +8 -7
  5. package/docs/runbook-trusted-contacts.md +14 -0
  6. package/docs/stt-provider-onboarding.md +8 -4
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
  9. package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
  10. package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
  11. package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
  12. package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
  13. package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
  14. package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
  15. package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  19. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  20. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  21. package/node_modules/@vellumai/ces-client/src/http-credentials.ts +161 -0
  22. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  23. package/node_modules/@vellumai/environments/package.json +2 -1
  24. package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
  25. package/node_modules/@vellumai/environments/src/shell.ts +65 -0
  26. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  27. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  28. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  29. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  30. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  31. package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
  32. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
  33. package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
  34. package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
  35. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
  36. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
  37. package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
  38. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
  39. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
  40. package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
  41. package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
  42. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
  43. package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
  44. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
  45. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  46. package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  47. package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  48. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  49. package/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  50. package/openapi.yaml +419 -55
  51. package/package.json +4 -1
  52. package/scripts/sync-llm-catalog.ts +6 -0
  53. package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
  54. package/src/__tests__/approval-card-edit.test.ts +153 -0
  55. package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
  56. package/src/__tests__/available-skills.test.ts +54 -0
  57. package/src/__tests__/avatar-identity-sync.test.ts +5 -1
  58. package/src/__tests__/catalog-install-normalize.test.ts +9 -1
  59. package/src/__tests__/channel-policy.test.ts +1 -3
  60. package/src/__tests__/channel-readiness-discord.test.ts +9 -70
  61. package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
  62. package/src/__tests__/config-loader-backfill.test.ts +69 -0
  63. package/src/__tests__/config-schema.test.ts +2 -3
  64. package/src/__tests__/conversation-agent-loop.test.ts +238 -0
  65. package/src/__tests__/conversation-attachments.test.ts +0 -1
  66. package/src/__tests__/conversation-error.test.ts +44 -0
  67. package/src/__tests__/conversation-evictor.test.ts +19 -0
  68. package/src/__tests__/conversation-fork-referential.test.ts +73 -63
  69. package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
  70. package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
  71. package/src/__tests__/conversation-slash.test.ts +9 -0
  72. package/src/__tests__/conversation-title-service.test.ts +97 -0
  73. package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
  74. package/src/__tests__/credential-broker-server-use.test.ts +0 -218
  75. package/src/__tests__/credential-broker.test.ts +43 -420
  76. package/src/__tests__/credential-record-write-through.test.ts +78 -0
  77. package/src/__tests__/credential-routes.test.ts +17 -1
  78. package/src/__tests__/credential-security-invariants.test.ts +8 -9
  79. package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
  80. package/src/__tests__/delete-propagation.test.ts +92 -2
  81. package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
  82. package/src/__tests__/discord-channel-config.test.ts +237 -0
  83. package/src/__tests__/edit-propagation.test.ts +42 -2
  84. package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
  85. package/src/__tests__/fallback-breaker.test.ts +1335 -0
  86. package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
  87. package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
  88. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
  89. package/src/__tests__/image-conversion.test.ts +38 -4
  90. package/src/__tests__/install-skill-routing.test.ts +25 -0
  91. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  92. package/src/__tests__/llm-resolver.test.ts +74 -0
  93. package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
  94. package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
  95. package/src/__tests__/managed-profile-guard.test.ts +28 -0
  96. package/src/__tests__/managed-speech-defaults.test.ts +196 -1
  97. package/src/__tests__/mcp-cli.test.ts +23 -1
  98. package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
  99. package/src/__tests__/media-stream-stt-session.test.ts +2 -0
  100. package/src/__tests__/notification-decision-fallback.test.ts +106 -0
  101. package/src/__tests__/notification-discord-adapter.test.ts +162 -0
  102. package/src/__tests__/oauth-connect-orchestrator.test.ts +95 -0
  103. package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
  104. package/src/__tests__/persist-unsendable-image.test.ts +3 -3
  105. package/src/__tests__/platform.test.ts +19 -1
  106. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  107. package/src/__tests__/provider-catalog-visibility.test.ts +17 -0
  108. package/src/__tests__/provider-platform-proxy-integration.test.ts +8 -1
  109. package/src/__tests__/provider-usage-tracking.test.ts +56 -0
  110. package/src/__tests__/require-fresh-approval.test.ts +5 -2
  111. package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
  112. package/src/__tests__/schedule-retry.test.ts +183 -4
  113. package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
  114. package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
  115. package/src/__tests__/skill-load-tool.test.ts +34 -0
  116. package/src/__tests__/skill-tool-factory.test.ts +6 -1
  117. package/src/__tests__/skill-tool-manifest.test.ts +20 -0
  118. package/src/__tests__/skills.test.ts +25 -0
  119. package/src/__tests__/subagent-manager-notify.test.ts +38 -0
  120. package/src/__tests__/terminal-tools.test.ts +2 -0
  121. package/src/__tests__/tool-approval-handler.test.ts +146 -2
  122. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
  123. package/src/__tests__/tool-audit.test.ts +111 -40
  124. package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
  125. package/src/__tests__/ui-shape-teaching.test.ts +1 -1
  126. package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
  127. package/src/__tests__/voice-config-update.test.ts +43 -0
  128. package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
  129. package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
  130. package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
  131. package/src/agent/loop.ts +31 -2
  132. package/src/approvals/guardian-card-withdrawal.ts +22 -0
  133. package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
  134. package/src/avatar/avatar-manifest.ts +48 -100
  135. package/src/avatar/avatar-store.ts +7 -7
  136. package/src/avatar/ensure-raster.ts +146 -0
  137. package/src/avatar/resvg-lazy.test.ts +0 -1
  138. package/src/avatar/traits-png-sync.ts +9 -10
  139. package/src/background-wake/wake-intent-hooks.test.ts +0 -1
  140. package/src/backup/__tests__/paths.test.ts +6 -2
  141. package/src/backup/paths.ts +6 -4
  142. package/src/bundler/app-compiler.ts +2 -2
  143. package/src/bundler/compiler-tools.test.ts +36 -0
  144. package/src/bundler/compiler-tools.ts +54 -18
  145. package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
  146. package/src/calls/__tests__/voice-session-bridge.test.ts +57 -0
  147. package/src/calls/media-stream-stt-session.ts +2 -1
  148. package/src/calls/telephony-synthesis-language.ts +13 -5
  149. package/src/calls/voice-session-bridge.ts +19 -4
  150. package/src/channels/config.ts +23 -12
  151. package/src/cli/AGENTS.md +3 -1
  152. package/src/cli/commands/backup.help.ts +10 -13
  153. package/src/cli/commands/contacts.help.ts +16 -4
  154. package/src/cli/commands/contacts.ts +5 -1
  155. package/src/cli/commands/credentials.help.ts +1 -1
  156. package/src/cli/commands/inference-providers.ts +18 -53
  157. package/src/cli/commands/mcp.help.ts +6 -5
  158. package/src/cli/commands/mcp.ts +1 -1
  159. package/src/cli/commands/memory/memory-retrospective.ts +1 -0
  160. package/src/cli/commands/stt.help.ts +2 -2
  161. package/src/config/__tests__/backup-schema.test.ts +1 -1
  162. package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
  163. package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
  164. package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
  165. package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
  166. package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
  167. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
  168. package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
  169. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  170. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
  171. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
  172. package/src/config/bundled-skills/settings/TOOLS.json +11 -5
  173. package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
  174. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
  175. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
  176. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
  177. package/src/config/default-profile-catalog.ts +195 -10
  178. package/src/config/default-profile-names.ts +41 -0
  179. package/src/config/feature-flag-registry.json +34 -2
  180. package/src/config/inference-profile-validation.ts +11 -2
  181. package/src/config/llm-resolver.ts +25 -0
  182. package/src/config/loader.ts +3 -0
  183. package/src/config/managed-speech-defaults.ts +160 -10
  184. package/src/config/schema.ts +2 -0
  185. package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
  186. package/src/config/schemas/__tests__/stt.test.ts +94 -0
  187. package/src/config/schemas/backup.ts +1 -1
  188. package/src/config/schemas/channels.ts +32 -0
  189. package/src/config/schemas/live-voice.ts +13 -4
  190. package/src/config/schemas/llm.ts +263 -15
  191. package/src/config/schemas/mcp.ts +10 -2
  192. package/src/config/schemas/memory-retrospective.ts +1 -19
  193. package/src/config/schemas/services.ts +24 -0
  194. package/src/config/schemas/stt.ts +128 -5
  195. package/src/config/seed-inference-profiles.ts +32 -3
  196. package/src/config/skill-state.ts +5 -0
  197. package/src/config/skills.ts +21 -0
  198. package/src/contacts/notify-contacts-changed.ts +5 -2
  199. package/src/context/compactor.ts +74 -1
  200. package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
  201. package/src/daemon/config-watcher.ts +2 -1
  202. package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
  203. package/src/daemon/conversation-agent-loop.ts +86 -10
  204. package/src/daemon/conversation-error.ts +25 -0
  205. package/src/daemon/conversation-evictor.ts +2 -4
  206. package/src/daemon/conversation-runtime-assembly.ts +19 -7
  207. package/src/daemon/conversation-skill-tools.ts +5 -1
  208. package/src/daemon/conversation-slash.ts +2 -2
  209. package/src/daemon/conversation-store.ts +8 -1
  210. package/src/daemon/conversation-tool-setup.ts +37 -0
  211. package/src/daemon/conversation.ts +0 -4
  212. package/src/daemon/handlers/channel-config-result.ts +30 -0
  213. package/src/daemon/handlers/config-discord-channel.ts +301 -0
  214. package/src/daemon/handlers/config-ingress.ts +9 -0
  215. package/src/daemon/handlers/config-slack-channel.ts +13 -15
  216. package/src/daemon/handlers/config-telegram.ts +2 -5
  217. package/src/daemon/handlers/conversations.ts +0 -2
  218. package/src/daemon/handlers/skills.ts +25 -7
  219. package/src/daemon/lifecycle.ts +3 -1
  220. package/src/daemon/message-types/messages.ts +0 -2
  221. package/src/daemon/tool-setup-types.ts +5 -3
  222. package/src/daemon/trust-context-types.test.ts +33 -0
  223. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  224. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
  225. package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
  226. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
  227. package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
  228. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +64 -0
  229. package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
  230. package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
  231. package/src/live-voice/__tests__/protocol.test.ts +224 -0
  232. package/src/live-voice/live-voice-credential-preflight.ts +5 -1
  233. package/src/live-voice/live-voice-session-manager.ts +39 -2
  234. package/src/live-voice/live-voice-session.ts +224 -9
  235. package/src/live-voice/protocol.ts +187 -1
  236. package/src/messaging/providers/discord/api.ts +2 -1
  237. package/src/messaging/providers/discord/withdraw.ts +62 -0
  238. package/src/messaging/read-provider-metadata.ts +32 -0
  239. package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
  240. package/src/monitoring/__tests__/process-memory.test.ts +34 -1
  241. package/src/monitoring/db-integrity-sample.ts +1 -0
  242. package/src/monitoring/file-descriptors.ts +39 -6
  243. package/src/monitoring/process-memory.ts +37 -5
  244. package/src/monitoring/resource-sampler.ts +25 -8
  245. package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
  246. package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
  247. package/src/notifications/__tests__/connected-channels.test.ts +12 -0
  248. package/src/notifications/__tests__/copy-composer.test.ts +83 -0
  249. package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
  250. package/src/notifications/activity-failed-dedupe.ts +95 -0
  251. package/src/notifications/adapters/discord.ts +132 -0
  252. package/src/notifications/adapters/shared.ts +16 -0
  253. package/src/notifications/adapters/slack.ts +6 -1
  254. package/src/notifications/adapters/telegram.ts +5 -8
  255. package/src/notifications/approval-card-data.ts +94 -1
  256. package/src/notifications/background-failure-signal.ts +116 -0
  257. package/src/notifications/broadcaster.ts +3 -9
  258. package/src/notifications/copy-composer.ts +41 -8
  259. package/src/notifications/decision-engine.ts +55 -0
  260. package/src/notifications/destination-resolver.ts +45 -5
  261. package/src/notifications/emit-signal.ts +19 -5
  262. package/src/notifications/guardian-question-mode.ts +57 -0
  263. package/src/notifications/types.ts +2 -8
  264. package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
  265. package/src/oauth/connect-orchestrator.ts +16 -0
  266. package/src/oauth/seed-providers.ts +262 -0
  267. package/src/permissions/checker.test.ts +21 -0
  268. package/src/permissions/checker.ts +6 -0
  269. package/src/permissions/gateway-threshold-reader.test.ts +184 -0
  270. package/src/permissions/gateway-threshold-reader.ts +144 -10
  271. package/src/permissions/prompter.ts +0 -6
  272. package/src/permissions/types.ts +5 -4
  273. package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
  274. package/src/persistence/bulk-write-gate.ts +9 -10
  275. package/src/persistence/conversation-crud.ts +88 -134
  276. package/src/persistence/conversation-title-service.ts +43 -29
  277. package/src/persistence/embeddings/embedding-local.ts +14 -85
  278. package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
  279. package/src/persistence/lifecycle-events-store.test.ts +50 -0
  280. package/src/persistence/lifecycle-events-store.ts +75 -5
  281. package/src/platform/platform-patch-queue.test.ts +368 -0
  282. package/src/platform/platform-patch-queue.ts +190 -0
  283. package/src/platform/sync-avatar.test.ts +527 -0
  284. package/src/platform/sync-avatar.ts +209 -0
  285. package/src/platform/sync-identity.test.ts +75 -0
  286. package/src/platform/sync-identity.ts +17 -110
  287. package/src/plugin-api/model-profiles.test.ts +2 -2
  288. package/src/plugin-api/model-profiles.ts +7 -7
  289. package/src/plugin-api/transcription-session.ts +1 -1
  290. package/src/plugins/defaults/compaction/window-manager.ts +4 -0
  291. package/src/plugins/defaults/image-recovery/recover.ts +2 -2
  292. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
  293. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
  294. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
  295. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
  296. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
  297. package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
  298. package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
  299. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
  300. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
  301. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
  302. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
  303. package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
  304. package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
  305. package/src/plugins/defaults/memory/worker.ts +7 -0
  306. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  307. package/src/providers/__tests__/retry-callsite.test.ts +55 -0
  308. package/src/providers/__tests__/vellum-connection-routing.test.ts +21 -3
  309. package/src/providers/connection-resolution.ts +2 -17
  310. package/src/providers/fallback-breaker.ts +389 -0
  311. package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
  312. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
  313. package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
  314. package/src/providers/inference/adapter-factory.ts +246 -3
  315. package/src/providers/inference/auth.ts +15 -14
  316. package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
  317. package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
  318. package/src/providers/inference/resolve-auth.ts +1 -1
  319. package/src/providers/media-resolve.ts +2 -3
  320. package/src/providers/model-catalog.ts +100 -0
  321. package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
  322. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
  323. package/src/providers/openai/api-error-normalization.ts +13 -0
  324. package/src/providers/openai/chat-completions-provider.ts +84 -0
  325. package/src/providers/opencode/client.test.ts +150 -0
  326. package/src/providers/opencode/client.ts +61 -0
  327. package/src/providers/platform-proxy/constants.ts +5 -0
  328. package/src/providers/provider-resolvability.ts +20 -0
  329. package/src/providers/provider-secret-catalog.ts +3 -2
  330. package/src/providers/retry.ts +956 -68
  331. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
  332. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
  333. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
  334. package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
  335. package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
  336. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
  337. package/src/providers/speech-to-text/provider-catalog.ts +191 -3
  338. package/src/providers/speech-to-text/resolve.ts +132 -24
  339. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
  340. package/src/providers/types.ts +39 -0
  341. package/src/providers/usage-tracking.ts +12 -1
  342. package/src/providers/vellum/client.ts +29 -0
  343. package/src/providers/vellum-model-routing.test.ts +2 -0
  344. package/src/providers/vellum-model-routing.ts +3 -3
  345. package/src/runtime/AGENTS.md +1 -2
  346. package/src/runtime/__tests__/agent-wake.test.ts +105 -3
  347. package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
  348. package/src/runtime/agent-wake.ts +44 -5
  349. package/src/runtime/background-job-runner.ts +68 -50
  350. package/src/runtime/channel-readiness-service.ts +0 -39
  351. package/src/runtime/http-server.ts +12 -4
  352. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
  353. package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
  354. package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
  355. package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
  356. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
  357. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
  358. package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
  359. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
  360. package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
  361. package/src/runtime/routes/approval-routes.ts +0 -12
  362. package/src/runtime/routes/avatar-routes.ts +17 -30
  363. package/src/runtime/routes/backup-routes.ts +2 -2
  364. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
  365. package/src/runtime/routes/contact-prompt-routes.ts +65 -6
  366. package/src/runtime/routes/contact-routes.ts +20 -15
  367. package/src/runtime/routes/conversation-query-routes.ts +98 -10
  368. package/src/runtime/routes/conversation-routes.ts +2 -2
  369. package/src/runtime/routes/credential-routes.ts +2 -1
  370. package/src/runtime/routes/guardian-approval-interception.ts +12 -9
  371. package/src/runtime/routes/inbound-message-handler.ts +47 -48
  372. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
  373. package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
  374. package/src/runtime/routes/inbound-stages/edit-intercept.ts +20 -2
  375. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
  376. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
  377. package/src/runtime/routes/index.ts +2 -0
  378. package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
  379. package/src/runtime/routes/inference-profiles-routes.ts +12 -7
  380. package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
  381. package/src/runtime/routes/inference-send-routes.ts +2 -2
  382. package/src/runtime/routes/ingress-status-routes.ts +2 -2
  383. package/src/runtime/routes/integrations/discord.ts +119 -0
  384. package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
  385. package/src/runtime/routes/mcp-auth-routes.ts +12 -6
  386. package/src/runtime/routes/migration-routes.ts +3 -1
  387. package/src/runtime/routes/oauth-pending-flows.ts +136 -0
  388. package/src/runtime/routes/secret-routes.ts +7 -0
  389. package/src/runtime/routes/stt-routes.ts +38 -6
  390. package/src/runtime/routes/wake-conversation-routes.ts +8 -1
  391. package/src/runtime/routes/watch-routes.ts +9 -5
  392. package/src/runtime/sync/resource-sync-events.ts +2 -0
  393. package/src/runtime/trust-verdict-consumer.ts +3 -3
  394. package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
  395. package/src/schedule/retry-policy.ts +8 -8
  396. package/src/schedule/run-script.ts +14 -23
  397. package/src/schedule/schedule-recovery.ts +0 -1
  398. package/src/schedule/scheduler.ts +89 -51
  399. package/src/schedule/worker.ts +57 -0
  400. package/src/security/__tests__/openai-device-auth.test.ts +355 -0
  401. package/src/security/ces-rpc-record-backend.ts +123 -0
  402. package/src/security/oauth2.ts +7 -0
  403. package/src/security/openai-device-auth.ts +364 -0
  404. package/src/security/secure-keys.ts +15 -0
  405. package/src/skills/available-skills.ts +10 -1
  406. package/src/skills/catalog-install.ts +19 -1
  407. package/src/skills/inline-command-runner.ts +6 -2
  408. package/src/skills/platform-compatibility.test.ts +49 -0
  409. package/src/skills/platform-compatibility.ts +65 -0
  410. package/src/skills/tool-manifest.ts +20 -0
  411. package/src/stt/__tests__/roles.test.ts +152 -0
  412. package/src/stt/daemon-batch-transcriber.ts +4 -1
  413. package/src/stt/roles.ts +180 -0
  414. package/src/stt/stt-stream-session.ts +3 -2
  415. package/src/stt/types.ts +33 -3
  416. package/src/subagent/manager.ts +12 -0
  417. package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
  418. package/src/telemetry/live-voice-funnel.ts +12 -2
  419. package/src/telemetry/telemetry-wire.generated.ts +6 -0
  420. package/src/telemetry/tool-audit.ts +83 -30
  421. package/src/telemetry/tool-executed-events-store.test.ts +0 -1
  422. package/src/telemetry/turn-outcome.ts +75 -4
  423. package/src/tools/client-os.ts +115 -0
  424. package/src/tools/computer-use/definitions.ts +11 -7
  425. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
  426. package/src/tools/credentials/broker-types.ts +0 -71
  427. package/src/tools/credentials/broker.ts +6 -201
  428. package/src/tools/credentials/metadata-store.ts +65 -16
  429. package/src/tools/credentials/store.ts +2 -0
  430. package/src/tools/executor.ts +2 -8
  431. package/src/tools/host-shell.test.ts +75 -0
  432. package/src/tools/host-shell.ts +24 -0
  433. package/src/tools/host-terminal/host-shell.ts +15 -44
  434. package/src/tools/mcp/mcp-tool-factory.ts +44 -9
  435. package/src/tools/permission-checker.ts +73 -21
  436. package/src/tools/policy-context.ts +1 -0
  437. package/src/tools/shared/filesystem/image-read.ts +1 -1
  438. package/src/tools/skills/find-similar-skills.ts +10 -2
  439. package/src/tools/skills/load.ts +41 -6
  440. package/src/tools/skills/sandbox-runner.ts +8 -16
  441. package/src/tools/skills/skill-tool-factory.ts +1 -0
  442. package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
  443. package/src/tools/terminal/safe-env.ts +39 -9
  444. package/src/tools/terminal/shell.ts +12 -31
  445. package/src/tools/tool-approval-handler.ts +64 -10
  446. package/src/tools/tool-types.ts +10 -0
  447. package/src/tools/types.ts +19 -3
  448. package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
  449. package/src/tools/workflows/manage-workflows.ts +2 -2
  450. package/src/tools/workflows/run-workflow.test.ts +2 -1
  451. package/src/util/__tests__/file-use.test.ts +61 -0
  452. package/src/util/__tests__/process-table.test.ts +66 -0
  453. package/src/util/__tests__/process-tree.test.ts +46 -0
  454. package/src/util/errors.ts +1 -0
  455. package/src/util/file-use.ts +91 -0
  456. package/src/util/host-process.test.ts +17 -0
  457. package/src/util/host-process.ts +75 -0
  458. package/src/util/image-conversion.ts +39 -7
  459. package/src/util/platform.ts +32 -13
  460. package/src/util/process-table.ts +249 -0
  461. package/src/util/process-tree.ts +85 -129
  462. package/src/util/provider-error-patterns.ts +20 -0
  463. package/src/util/spawn.ts +9 -2
  464. package/src/watch/__tests__/watch-retro.test.ts +48 -1
  465. package/src/watch/watch-retro.ts +46 -4
  466. package/src/workspace/git-service.ts +4 -14
  467. package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
  468. package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
  469. package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
  470. package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
  471. package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
  472. package/src/workspace/migrations/registry.ts +8 -0
  473. package/src/__tests__/permission-types.test.ts +0 -17
  474. package/src/channels/gateway-discord-admission.ts +0 -32
  475. package/src/persistence/fork-message-copy.ts +0 -239
  476. package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
  477. package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
  478. package/src/security/oauth2-device-code.ts +0 -312
@@ -1,23 +1,14 @@
1
- import { v4 as uuid } from "uuid";
2
-
3
1
  import { credentialKey } from "../../security/credential-key.js";
4
2
  import { getSecureKeyAsync } from "../../security/secure-keys.js";
5
3
  import { getLogger } from "../../util/logger.js";
6
4
  import type {
7
- AuthorizeRequest,
8
- AuthorizeResult,
9
5
  BrowserFillRequest,
10
6
  BrowserFillResult,
11
- ConsumeResult,
12
- ServerUseByIdRequest,
13
- ServerUseByIdResult,
14
7
  ServerUseRequest,
15
8
  ServerUseResult,
16
- UsageToken,
17
9
  } from "./broker-types.js";
18
10
  import { isDomainAllowed } from "./domain-policy.js";
19
11
  import { getCredentialMetadata } from "./metadata-store.js";
20
- import { resolveById } from "./resolve.js";
21
12
  import {
22
13
  isToolAllowed,
23
14
  serverUseDenialReason,
@@ -26,21 +17,17 @@ import {
26
17
 
27
18
  const log = getLogger("credential-broker");
28
19
 
29
- /** Tokens expire after 5 minutes to limit the window for using stale/revoked credentials. */
30
- const TOKEN_TTL_MS = 5 * 60 * 1000;
31
-
32
20
  /**
33
- * Credential broker that issues single-use tokens for policy-checked credential access.
21
+ * Credential broker that mediates policy-checked credential access.
34
22
  *
35
- * The broker never exposes plaintext secret values. Instead, it:
23
+ * The broker never exposes plaintext secret values to callers. It:
36
24
  * 1. Checks that a credential exists and has metadata
37
- * 2. Issues a single-use token for the authorized usage
38
- * 3. On consumption, returns the storage key so the caller can read the secret internally
25
+ * 2. Enforces tool and domain policy
26
+ * 3. Reads the secret internally and passes it to an opaque callback
39
27
  *
40
- * Tool policy is enforced at authorize/fill time; domain policy is enforced at fill time.
28
+ * Tool policy is enforced at fill/use time; domain policy is enforced at fill time.
41
29
  */
42
30
  export class CredentialBroker {
43
- private tokens = new Map<string, UsageToken>();
44
31
  /** Transient values for one-time send: consumed on first read, never persisted.
45
32
  * Values are wrapped in objects so post-await guards use reference identity
46
33
  * (not string value equality) to detect concurrent replacements. */
@@ -48,7 +35,7 @@ export class CredentialBroker {
48
35
 
49
36
  /**
50
37
  * Inject a value for one-time use. The value is consumed on the next
51
- * browserFill or consume call for this service/field pair, then discarded.
38
+ * browserFill or serverUse call for this service/field pair, then discarded.
52
39
  */
53
40
  injectTransient(service: string, field: string, value: string): void {
54
41
  const key = credentialKey(service, field);
@@ -59,113 +46,6 @@ export class CredentialBroker {
59
46
  );
60
47
  }
61
48
 
62
- /**
63
- * Authorize the use of a credential for a specific tool and optional domain.
64
- * Returns a single-use token on success, or a denial reason on failure.
65
- */
66
- authorize(request: AuthorizeRequest): AuthorizeResult {
67
- const metadata = getCredentialMetadata(request.service, request.field);
68
- if (!metadata) {
69
- return {
70
- authorized: false,
71
- reason: `No credential found for ${request.service}/${request.field}`,
72
- };
73
- }
74
-
75
- // Tool policy enforcement - deny if tool is not in the credential's allowed list
76
- if (!isToolAllowed(request.toolName, metadata.allowedTools)) {
77
- return {
78
- authorized: false,
79
- reason: toolNotAllowedReason(
80
- request.toolName,
81
- request.service,
82
- request.field,
83
- metadata.allowedTools,
84
- ),
85
- };
86
- }
87
-
88
- const token: UsageToken = {
89
- tokenId: uuid(),
90
- credentialId: metadata.credentialId,
91
- service: request.service,
92
- field: request.field,
93
- toolName: request.toolName,
94
- createdAt: Date.now(),
95
- consumed: false,
96
- };
97
-
98
- this.tokens.set(token.tokenId, token);
99
- log.info(
100
- {
101
- tokenId: token.tokenId,
102
- service: request.service,
103
- field: request.field,
104
- tool: request.toolName,
105
- },
106
- "Usage token issued",
107
- );
108
-
109
- return { authorized: true, token: { ...token } };
110
- }
111
-
112
- /**
113
- * Consume a previously issued token. Returns the storage key on success.
114
- * Each token can only be consumed once.
115
- */
116
- consume(tokenId: string): ConsumeResult {
117
- const token = this.tokens.get(tokenId);
118
- if (!token) {
119
- return { success: false, reason: "Token not found or already revoked" };
120
- }
121
- if (token.consumed) {
122
- return { success: false, reason: "Token already consumed" };
123
- }
124
- if (Date.now() - token.createdAt > TOKEN_TTL_MS) {
125
- this.tokens.delete(tokenId);
126
- log.info({ tokenId }, "Token expired (TTL exceeded)");
127
- return { success: false, reason: "Token expired" };
128
- }
129
-
130
- token.consumed = true;
131
- const storageKey = credentialKey(token.service, token.field);
132
- // Check for transient value first (one-time send) - consume and return the value
133
- // directly since transient values are never persisted to secure storage.
134
- const transient = this.transientValues.get(storageKey);
135
- if (transient !== undefined) {
136
- this.transientValues.delete(storageKey);
137
- log.info(
138
- { tokenId, storageKey, transient: true },
139
- "Usage token consumed (transient)",
140
- );
141
- return { success: true, storageKey, value: transient.value };
142
- }
143
-
144
- log.info({ tokenId, storageKey }, "Usage token consumed");
145
- return { success: true, storageKey };
146
- }
147
-
148
- /**
149
- * Revoke a token, removing it from the active set.
150
- * Returns true if the token existed and was revoked.
151
- */
152
- revoke(tokenId: string): boolean {
153
- const existed = this.tokens.delete(tokenId);
154
- if (existed) {
155
- log.info({ tokenId }, "Usage token revoked");
156
- }
157
- return existed;
158
- }
159
-
160
- /** Revoke all tokens (e.g. on conversation teardown). */
161
- revokeAll(): void {
162
- const count = this.tokens.size;
163
- this.tokens.clear();
164
- if (count > 0) {
165
- log.info({ count }, "All usage tokens revoked");
166
- }
167
- }
168
-
169
49
  /**
170
50
  * Fill a browser field using a credential without exposing plaintext to the caller.
171
51
  *
@@ -317,81 +197,6 @@ export class CredentialBroker {
317
197
  return { success: false, reason: "Credential use failed" };
318
198
  }
319
199
  }
320
-
321
- /**
322
- * Look up a credential by its opaque ID for proxy injection.
323
- *
324
- * Returns metadata and injection templates so the proxy knows how to
325
- * inject the credential into outbound requests. The secret value is
326
- * never included in the result - the proxy reads it separately via
327
- * the secure key backend at injection time.
328
- */
329
- async serverUseById(
330
- request: ServerUseByIdRequest,
331
- ): Promise<ServerUseByIdResult> {
332
- const resolved = resolveById(request.credentialId);
333
- if (!resolved) {
334
- return {
335
- success: false,
336
- reason: `No credential found for id "${request.credentialId}"`,
337
- };
338
- }
339
-
340
- const { metadata } = resolved;
341
-
342
- // Shared server-use policy: the ID lookup above is the only by-ID-specific
343
- // gate, so tool and domain enforcement come from the same helper serverUse
344
- // uses.
345
- const denialReason = serverUseDenialReason(
346
- metadata,
347
- request.requestingTool,
348
- metadata.service,
349
- metadata.field,
350
- );
351
- if (denialReason) {
352
- return { success: false, reason: denialReason };
353
- }
354
-
355
- // Fail-closed: verify the secret value actually exists in secure storage.
356
- // Without this, downstream proxy code would attempt unauthenticated requests.
357
- const value = await getSecureKeyAsync(resolved.storageKey);
358
- if (!value) {
359
- return {
360
- success: false,
361
- reason: `Credential metadata exists but no stored value for ${metadata.service}/${metadata.field}`,
362
- };
363
- }
364
-
365
- log.info(
366
- {
367
- credentialId: request.credentialId,
368
- service: metadata.service,
369
- field: metadata.field,
370
- tool: request.requestingTool,
371
- },
372
- "Server-side credential lookup by ID completed",
373
- );
374
-
375
- return {
376
- success: true,
377
- credentialId: resolved.credentialId,
378
- service: resolved.service,
379
- field: resolved.field,
380
- injectionTemplates: resolved.injectionTemplates,
381
- };
382
- }
383
-
384
- /** Return the number of active (non-consumed, non-revoked, non-expired) tokens. */
385
- get activeTokenCount(): number {
386
- const now = Date.now();
387
- let count = 0;
388
- for (const token of this.tokens.values()) {
389
- if (!token.consumed && now - token.createdAt <= TOKEN_TTL_MS) {
390
- count++;
391
- }
392
- }
393
- return count;
394
- }
395
200
  }
396
201
 
397
202
  /** Shared singleton broker instance used by vault and browser tools. */
@@ -1,24 +1,29 @@
1
1
  /**
2
2
  * Credential metadata store.
3
3
  *
4
- * Thin wrapper around the portable StaticCredentialMetadataStore from
5
- * @vellumai/credential-storage. Wires in the platform-specific data
6
- * directory and preserves the existing module-level API so that call
7
- * sites throughout the assistant daemon do not need to change.
4
+ * Reads stay on the workspace `metadata.json` file (sync). When a CES
5
+ * record backend is injected, upserts and deletes also write-through to
6
+ * CES so the CES catalog stays current.
8
7
  *
9
- * OAuth-specific fields (expiresAt, grantedScopes, oauth2TokenUrl,
10
- * oauth2ClientId, oauth2TokenEndpointAuthMethod, hasRefreshToken) are now
11
- * exclusively managed by the SQLite oauth-store and have been removed
12
- * from this interface as of v5.
8
+ * Tests: `_setMetadataPath` keeps the file-backed store and skips CES
9
+ * write-through.
13
10
  */
14
11
 
15
12
  import { join } from "node:path";
16
13
 
17
- import { StaticCredentialMetadataStore } from "@vellumai/credential-storage";
14
+ import {
15
+ credentialKey,
16
+ StaticCredentialMetadataStore,
17
+ } from "@vellumai/credential-storage";
18
+ import type { CredentialRecord } from "@vellumai/service-contracts/credential-rpc";
18
19
 
20
+ import type { CredentialRecordBackend } from "../../security/ces-rpc-record-backend.js";
21
+ import { getLogger } from "../../util/logger.js";
19
22
  import { getDataDir } from "../../util/platform.js";
20
23
  import type { CredentialInjectionTemplate } from "./policy-types.js";
21
24
 
25
+ const log = getLogger("credential-metadata-store");
26
+
22
27
  /**
23
28
  * CredentialMetadata extends the shared StaticCredentialRecord with
24
29
  * assistant-specific injection template fields (composeWith, valueTransform).
@@ -43,12 +48,9 @@ export interface CredentialMetadata {
43
48
  // Singleton store instance
44
49
  // ---------------------------------------------------------------------------
45
50
 
46
- /**
47
- * Lazily initialised store instance. The path is determined on first access
48
- * (or overridden via `_setMetadataPath` for tests).
49
- */
50
51
  let _store: StaticCredentialMetadataStore | undefined;
51
52
  let _overridePath: string | null = null;
53
+ let _recordBackend: CredentialRecordBackend | undefined;
52
54
 
53
55
  function getStore(): StaticCredentialMetadataStore {
54
56
  if (!_store) {
@@ -59,6 +61,48 @@ function getStore(): StaticCredentialMetadataStore {
59
61
  return _store;
60
62
  }
61
63
 
64
+ function toRecord(metadata: CredentialMetadata): CredentialRecord {
65
+ return {
66
+ credentialId: metadata.credentialId,
67
+ service: metadata.service,
68
+ field: metadata.field,
69
+ allowedTools: metadata.allowedTools,
70
+ allowedDomains: metadata.allowedDomains,
71
+ usageDescription: metadata.usageDescription,
72
+ alias: metadata.alias,
73
+ injectionTemplates: metadata.injectionTemplates,
74
+ createdAt: metadata.createdAt,
75
+ updatedAt: metadata.updatedAt,
76
+ };
77
+ }
78
+
79
+ async function persistRecord(metadata: CredentialMetadata): Promise<void> {
80
+ if (!_recordBackend || _overridePath) {
81
+ return;
82
+ }
83
+ const account = credentialKey(metadata.service, metadata.field);
84
+ const ok = await _recordBackend.set(account, toRecord(metadata));
85
+ if (!ok) {
86
+ log.warn(
87
+ { service: metadata.service, field: metadata.field },
88
+ "Failed to persist credential record to CES",
89
+ );
90
+ }
91
+ }
92
+
93
+ /** Await CES write-through for a record already in the local file store. */
94
+ export async function persistCredentialMetadata(
95
+ metadata: CredentialMetadata,
96
+ ): Promise<void> {
97
+ await persistRecord(metadata);
98
+ }
99
+
100
+ export function setCredentialRecordBackend(
101
+ backend: CredentialRecordBackend | undefined,
102
+ ): void {
103
+ _recordBackend = backend;
104
+ }
105
+
62
106
  // ---------------------------------------------------------------------------
63
107
  // Public API - unchanged signatures, delegates to shared store
64
108
  // ---------------------------------------------------------------------------
@@ -89,7 +133,9 @@ export function upsertCredentialMetadata(
89
133
  injectionTemplates?: CredentialInjectionTemplate[] | null;
90
134
  },
91
135
  ): CredentialMetadata {
92
- return getStore().upsert(service, field, policy) as CredentialMetadata;
136
+ const record = getStore().upsert(service, field, policy) as CredentialMetadata;
137
+ void persistRecord(record);
138
+ return record;
93
139
  }
94
140
 
95
141
  /**
@@ -127,13 +173,16 @@ export function deleteCredentialMetadata(
127
173
  service: string,
128
174
  field: string,
129
175
  ): boolean {
130
- return getStore().delete(service, field);
176
+ const deleted = getStore().delete(service, field);
177
+ if (deleted && _recordBackend && !_overridePath) {
178
+ void _recordBackend.delete(credentialKey(service, field));
179
+ }
180
+ return deleted;
131
181
  }
132
182
 
133
183
  /** @internal Test-only: override the metadata file path. */
134
184
  export function _setMetadataPath(path: string | null): void {
135
185
  _overridePath = path;
136
- // Reset the store so it picks up the new path
137
186
  if (_store) {
138
187
  if (path) {
139
188
  _store.setPath(path);
@@ -37,6 +37,7 @@ import {
37
37
  import { getLogger } from "../../util/logger.js";
38
38
  import {
39
39
  assertMetadataWritable,
40
+ persistCredentialMetadata,
40
41
  upsertCredentialMetadata,
41
42
  } from "./metadata-store.js";
42
43
  import type { CredentialInjectionTemplate } from "./policy-types.js";
@@ -161,6 +162,7 @@ export async function storeCredentialValue(
161
162
  allowedDomains: input.allowedDomains,
162
163
  injectionTemplates: input.injectionTemplates,
163
164
  });
165
+ await persistCredentialMetadata(metadata);
164
166
  const { syncManualTokenConnection } =
165
167
  await import("../../oauth/manual-token-connection.js");
166
168
  await syncManualTokenConnection(service);
@@ -33,6 +33,7 @@ import { getWorkflowRunManager } from "../workflows/run-manager.js";
33
33
  import { executeWithTimeout, safeTimeoutMs } from "./execution-timeout.js";
34
34
  import { fileEditInputSchema } from "./filesystem/edit.js";
35
35
  import { fileWriteInputSchema } from "./filesystem/write.js";
36
+ import { getHostShell } from "./host-shell.js";
36
37
  import { PermissionChecker } from "./permission-checker.js";
37
38
  import { getToolOwner } from "./registry.js";
38
39
  import { extractAndSanitize } from "./sensitive-output-placeholders.js";
@@ -89,6 +90,7 @@ export class ToolExecutor {
89
90
  undefined,
90
91
  undefined,
91
92
  context.signal,
93
+ getHostShell(context, input),
92
94
  );
93
95
  } catch {
94
96
  // Stays undefined; the audit row records RISK_LEVEL_UNCLASSIFIED.
@@ -108,12 +110,6 @@ export class ToolExecutor {
108
110
  let permApprovalMode: string | undefined;
109
111
  let permApprovalReason: string | undefined;
110
112
  let permRiskThreshold: string | undefined;
111
- // Whether THIS invocation was interactively prompted. Distinct from the
112
- // turn-level `context.approvedViaPrompt` (seeded from
113
- // `approvedViaPromptThisTurn`, which stays true for later auto-approved
114
- // tools in the same turn): the `permission_decided` telemetry must reflect
115
- // a prompt for this specific call, not any prompt earlier in the turn.
116
- let wasPromptedThisInvocation = false;
117
113
  // Run pre-execution approval gates (abort, guardian policy,
118
114
  // allowed-tool-set, task-run preflight, tool registry lookup). The gate
119
115
  // resolves the tool's sandbox/host target internally.
@@ -253,7 +249,6 @@ export class ToolExecutor {
253
249
 
254
250
  if (permResult.wasPrompted) {
255
251
  context.approvedViaPrompt = true;
256
- wasPromptedThisInvocation = true;
257
252
  }
258
253
  } else {
259
254
  // Grant consumed — permission check was skipped. Set provenance explicitly
@@ -324,7 +319,6 @@ export class ToolExecutor {
324
319
  matchedTrustRuleId: permMatchedTrustRuleId,
325
320
  durationMs,
326
321
  attribution: context.attribution ?? null,
327
- wasPrompted: wasPromptedThisInvocation,
328
322
  });
329
323
  recordToolCompletion(
330
324
  context.conversationId,
@@ -0,0 +1,75 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+
3
+ import { assistantEventHub } from "../runtime/assistant-event-hub.js";
4
+ import { getHostShell } from "./host-shell.js";
5
+
6
+ const subscriptions: Array<{ dispose(): void }> = [];
7
+
8
+ afterEach(() => {
9
+ for (const subscription of subscriptions.splice(0)) {
10
+ subscription.dispose();
11
+ }
12
+ });
13
+
14
+ function connectHostBashClient(
15
+ clientId: string,
16
+ interfaceId: "macos" | "windows",
17
+ ): void {
18
+ subscriptions.push(
19
+ assistantEventHub.subscribe({
20
+ type: "client",
21
+ clientId,
22
+ interfaceId,
23
+ capabilities: ["host_bash"],
24
+ actorPrincipalId: "actor-1",
25
+ callback: () => {},
26
+ }),
27
+ );
28
+ }
29
+
30
+ describe("getHostShell", () => {
31
+ test("uses the explicitly targeted Windows client's shell", () => {
32
+ connectHostBashClient("windows-client", "windows");
33
+
34
+ expect(
35
+ getHostShell(
36
+ {
37
+ clientOs: "web",
38
+ transportInterface: "web",
39
+ sourceActorPrincipalId: "actor-1",
40
+ },
41
+ { target_client_id: "windows-client" },
42
+ ),
43
+ ).toBe("powershell");
44
+ });
45
+
46
+ test("uses the single same-user Windows client's shell", () => {
47
+ connectHostBashClient("windows-client", "windows");
48
+
49
+ expect(
50
+ getHostShell(
51
+ {
52
+ clientOs: "web",
53
+ transportInterface: "web",
54
+ sourceActorPrincipalId: "actor-1",
55
+ },
56
+ {},
57
+ ),
58
+ ).toBe("powershell");
59
+ });
60
+
61
+ test("keeps Bash classification for a targeted macOS client", () => {
62
+ connectHostBashClient("mac-client", "macos");
63
+
64
+ expect(
65
+ getHostShell(
66
+ {
67
+ clientOs: "web",
68
+ transportInterface: "web",
69
+ sourceActorPrincipalId: "actor-1",
70
+ },
71
+ { target_client_id: "mac-client" },
72
+ ),
73
+ ).toBeUndefined();
74
+ });
75
+ });
@@ -0,0 +1,24 @@
1
+ import type { ClassifyRiskIpcParams } from "@vellumai/gateway-client";
2
+
3
+ import { resolveInvocationHostClientOs } from "./client-os.js";
4
+ import type { ToolContext } from "./types.js";
5
+
6
+ export function getHostShell(
7
+ context: Pick<
8
+ ToolContext,
9
+ "clientOs" | "transportInterface" | "sourceActorPrincipalId"
10
+ >,
11
+ input: Record<string, unknown>,
12
+ ): ClassifyRiskIpcParams["shell"] {
13
+ const targetClientOs = resolveInvocationHostClientOs(
14
+ "host_bash",
15
+ input,
16
+ context,
17
+ );
18
+ return targetClientOs === "windows" ||
19
+ (targetClientOs === undefined &&
20
+ (context.clientOs === "windows" ||
21
+ context.transportInterface === "windows"))
22
+ ? "powershell"
23
+ : undefined;
24
+ }
@@ -9,7 +9,7 @@
9
9
  import { spawn } from "node:child_process";
10
10
  import { existsSync } from "node:fs";
11
11
  import { homedir } from "node:os";
12
- import { isAbsolute } from "node:path";
12
+ import { isAbsolute, join } from "node:path";
13
13
 
14
14
  import { z } from "zod";
15
15
 
@@ -24,6 +24,11 @@ import {
24
24
  } from "../../runtime/assistant-event-hub.js";
25
25
  import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
26
26
  import { redactSecrets } from "../../security/secret-scanner.js";
27
+ import {
28
+ buildShellInvocation,
29
+ prependUniquePathEntries,
30
+ terminateProcessTree,
31
+ } from "../../util/host-process.js";
27
32
  import { getLogger } from "../../util/logger.js";
28
33
  import type { CompletedBackgroundTool } from "../background-tool-registry.js";
29
34
  import {
@@ -67,15 +72,10 @@ const HOST_BASH_PROXY_ENV_KEYS = [
67
72
  function buildHostShellEnv(): Record<string, string> {
68
73
  const env = buildSanitizedEnv();
69
74
  // Ensure ~/.local/bin and ~/.bun/bin are in PATH so `vellum` and `bun` are
70
- // always reachable, even when the daemon is launched from a macOS app
71
- // bundle that inherits a minimal PATH.
75
+ // reachable when a desktop app launches the assistant with a minimal PATH.
72
76
  const home = homedir();
73
- const extraDirs = [`${home}/.local/bin`, `${home}/.bun/bin`];
74
- const currentPath = env.PATH ?? "";
75
- const missing = extraDirs.filter((d) => !currentPath.split(":").includes(d));
76
- if (missing.length > 0) {
77
- env.PATH = [...missing, currentPath].filter(Boolean).join(":");
78
- }
77
+ const extraDirs = [join(home, ".local", "bin"), join(home, ".bun", "bin")];
78
+ env.PATH = prependUniquePathEntries(env.PATH, extraDirs);
79
79
  return env;
80
80
  }
81
81
 
@@ -466,7 +466,8 @@ export const hostShellTool = {
466
466
  const bgId = generateBackgroundToolId();
467
467
  const startedAt = Date.now();
468
468
 
469
- const child = spawn("bash", ["-c", "--", command], {
469
+ const wrapped = buildShellInvocation(command);
470
+ const child = spawn(wrapped.command, wrapped.args, {
470
471
  cwd: workingDir,
471
472
  env: hostEnv,
472
473
  stdio: ["ignore", "pipe", "pipe"],
@@ -480,21 +481,7 @@ export const hostShellTool = {
480
481
  // event reports "cancelled" rather than "failed".
481
482
  let aborted = false;
482
483
 
483
- const killTree = () => {
484
- if (child.pid != null) {
485
- try {
486
- process.kill(-child.pid, "SIGKILL");
487
- return;
488
- } catch {
489
- // Process group may have already exited — fall through.
490
- }
491
- }
492
- try {
493
- child.kill("SIGKILL");
494
- } catch {
495
- // Child may have already exited.
496
- }
497
- };
484
+ const killTree = () => terminateProcessTree(child);
498
485
 
499
486
  const timer = setTimeout(() => {
500
487
  timedOut = true;
@@ -666,31 +653,15 @@ export const hostShellTool = {
666
653
  const stderrChunks: Buffer[] = [];
667
654
  let timedOut = false;
668
655
 
669
- const child = spawn("bash", ["-c", "--", command], {
656
+ const wrapped = buildShellInvocation(command);
657
+ const child = spawn(wrapped.command, wrapped.args, {
670
658
  cwd: workingDir,
671
659
  env: hostEnv,
672
660
  stdio: ["ignore", "pipe", "pipe"],
673
661
  detached: true,
674
662
  });
675
663
 
676
- // Kill the entire process tree. Tries the process group first
677
- // (negative PID), then falls back to killing the direct child if the
678
- // PID is unavailable or the group kill fails.
679
- const killTree = () => {
680
- if (child.pid != null) {
681
- try {
682
- process.kill(-child.pid, "SIGKILL");
683
- return;
684
- } catch {
685
- // Process group may have already exited — fall through.
686
- }
687
- }
688
- try {
689
- child.kill("SIGKILL");
690
- } catch {
691
- // Child may have already exited.
692
- }
693
- };
664
+ const killTree = () => terminateProcessTree(child);
694
665
 
695
666
  const timer = setTimeout(() => {
696
667
  timedOut = true;