@vellumai/assistant 0.11.2 → 0.11.3-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 (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -1,11 +1,22 @@
1
1
  /**
2
- * @vellumai/environments the single source of truth for Vellum's known
3
- * deployment environments (names, platform/web URLs, per-service port
4
- * blocks). Consumed by the CLI, the assistant daemon, and the local-mode
5
- * host library so the environment list is defined exactly once on the TS
6
- * side. The Swift client mirrors the same set in `VellumEnvironment.swift`.
2
+ * @vellumai/environments: the single source of truth for how a Vellum install
3
+ * is laid out and which deployment environment it belongs to.
7
4
  *
8
- * Intentionally free of runtime dependencies: pure types and constants only.
5
+ * Two things live here:
6
+ *
7
+ * - The known deployment environments (names, platform/web URLs, per-service
8
+ * port blocks). The Swift client mirrors the same set in
9
+ * `VellumEnvironment.swift`.
10
+ * - Install layout: whether a runtime is a repo checkout or an installed
11
+ * package, and where the `assistant` command it ships lives.
12
+ *
13
+ * Consumed by the CLI, the assistant daemon, and the local-mode host library
14
+ * so each answer is defined exactly once on the TS side.
15
+ *
16
+ * This is the lowest layer of the shared-packages hierarchy and stays a leaf:
17
+ * types, constants, and small helpers over node builtins, with no runtime
18
+ * dependencies of its own. See `__tests__/package-boundary.test.ts`.
9
19
  */
10
20
  export * from "./types.js";
11
21
  export * from "./seeds.js";
22
+ export * from "./install-layout.js";
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Reading the layout a Vellum runtime is installed in.
3
+ *
4
+ * The daemon is started either from a developer's repo checkout or from an
5
+ * installed npm package (`<install>/node_modules/@vellumai/assistant/src/
6
+ * index.ts`, which is what the desktop app runs). The CLI needs the layout to
7
+ * decide whether a launch is a dev run and what to put on the daemon's PATH;
8
+ * the daemon needs it to decide what to install as the `assistant` command.
9
+ * Both ask the same two questions, so they ask them here.
10
+ */
11
+
12
+ import { existsSync } from "node:fs";
13
+ import { join, sep } from "node:path";
14
+
15
+ const NODE_MODULES_SEGMENT = `${sep}node_modules${sep}`;
16
+
17
+ /**
18
+ * Whether `path` sits in a developer's repo checkout rather than inside an
19
+ * installed package.
20
+ */
21
+ export function isRepoCheckoutPath(path: string): boolean {
22
+ return !path.includes(NODE_MODULES_SEGMENT);
23
+ }
24
+
25
+ /**
26
+ * The `assistant` command shipped with the install that contains `path`, or
27
+ * null when there is none (a repo checkout, or an install whose dependency
28
+ * graph omits the package declaring the bin).
29
+ *
30
+ * Searches innermost `node_modules` outward, since a nested install shadows a
31
+ * hoisted one.
32
+ */
33
+ export function findAssistantCommand(path: string): string | null {
34
+ let index = path.lastIndexOf(NODE_MODULES_SEGMENT);
35
+
36
+ while (index !== -1) {
37
+ const nodeModulesDir = path.slice(
38
+ 0,
39
+ index + NODE_MODULES_SEGMENT.length - 1,
40
+ );
41
+ const command = join(nodeModulesDir, ".bin", "assistant");
42
+ if (existsSync(command)) {
43
+ return command;
44
+ }
45
+ index = path.lastIndexOf(NODE_MODULES_SEGMENT, index - 1);
46
+ }
47
+
48
+ return null;
49
+ }
@@ -24,6 +24,35 @@ function portBlock(base: number): PortMap {
24
24
  };
25
25
  }
26
26
 
27
+ /**
28
+ * Base URL of the cloud-hosted assistant SPA (the "hub") for a deployment
29
+ * environment. The Capacitor mobile shell bakes this into native builds as
30
+ * `server.url`, and the CLI's remote-web edge stamps it into the served
31
+ * config as `hubUrl`, so the environment-to-hub mapping lives here exactly
32
+ * once.
33
+ *
34
+ * Only the cloud deployments (production, staging) serve the hosted SPA at
35
+ * their own web origin; every other environment (dev, test, local, unknown)
36
+ * falls back to the dev SPA, whose host serves remote clients that cannot
37
+ * reach a local or per-developer web URL.
38
+ *
39
+ * The `/assistant` suffix is deliberate: booting on the bare host lands on
40
+ * the marketing page, whose CTA redirects to `www.vellum.ai/assistant` and
41
+ * bounces non-prod shells off their own host.
42
+ *
43
+ * NOTE: this module is loaded by Capacitor's single-file TS config loader
44
+ * (via the `@vellumai/environments/seeds` subpath), which cannot follow the
45
+ * package index's `.js`-suffixed runtime re-exports. Keep this file's
46
+ * imports type-only so it stays loadable on its own.
47
+ */
48
+ export function cloudAssistantHubUrl(envName: string | undefined): string {
49
+ const seed =
50
+ envName === "production" || envName === "staging"
51
+ ? SEEDS[envName]
52
+ : SEEDS.dev;
53
+ return `${seed.webUrl}/assistant`;
54
+ }
55
+
27
56
  /**
28
57
  * Built-in environment definitions and the source of truth for the
29
58
  * set of known environment names.
@@ -40,3 +40,42 @@ export function isChannelId(value: unknown): value is ChannelId {
40
40
  (CHANNEL_IDS as readonly string[]).includes(value)
41
41
  );
42
42
  }
43
+
44
+ /**
45
+ * The provider key holding each channel's bot credential.
46
+ *
47
+ * Two senses of "connected" share the provider registry: a provider the user
48
+ * authorized so the assistant can act **as them** (`slack`, `discord`,
49
+ * `google`), and a bot credential letting people reach the assistant **as
50
+ * itself**. Nothing in a provider key says which, and the naming actively
51
+ * misleads: `slack` and `discord` name the user integration while their bots
52
+ * take a `_channel` suffix, yet `telegram` *is* the bot, because Telegram has
53
+ * no user-identity integration.
54
+ *
55
+ * That irregularity is why this is stated rather than derived from the key,
56
+ * and it is stated here because this file already owns what a channel is.
57
+ *
58
+ * Deliberately only the key. What fields each credential requires is declared
59
+ * once already, per service, in the gateway's credential specs; restating it
60
+ * here would be a second copy of a different fact.
61
+ *
62
+ * Channels absent from this map reach the assistant without a bot credential
63
+ * of their own: `phone` through the voice provider, `vellum` and `platform`
64
+ * internally.
65
+ */
66
+ export const CHANNEL_BOT_PROVIDER = {
67
+ slack: "slack_channel",
68
+ discord: "discord_channel",
69
+ telegram: "telegram",
70
+ } as const satisfies Partial<Record<ChannelId, string>>;
71
+
72
+ /**
73
+ * Whether a provider key names a bot the assistant is reached through, rather
74
+ * than a grant letting it act as the user. This is the "which sense of
75
+ * connected" question, for any provider key.
76
+ */
77
+ export function isChannelBotProvider(providerKey: string): boolean {
78
+ return (Object.values(CHANNEL_BOT_PROVIDER) as readonly string[]).includes(
79
+ providerKey,
80
+ );
81
+ }
@@ -20,6 +20,16 @@ export function normalizePublicBaseUrl(value: unknown): string | undefined {
20
20
  return normalized.length > 0 ? normalized : undefined;
21
21
  }
22
22
 
23
+ /**
24
+ * Application close code the gateway's velay bridge sends to proxied
25
+ * WebSockets when the tunnel itself is lost (velay disconnect, gateway
26
+ * shutdown). A dedicated code because the natural 1001 (going away) cannot
27
+ * be sent through the JS `close()` API (the bridge would remap it to a
28
+ * misleading 4001), and Bun's WebSocket client drops close reasons, so the
29
+ * code is the only signal that survives the relay to the daemon.
30
+ */
31
+ export const GATEWAY_TUNNEL_LOST_WS_CLOSE_CODE = 4801;
32
+
23
33
  export function normalizeHttpPublicBaseUrl(value: unknown): string | undefined {
24
34
  if (typeof value !== "string") return undefined;
25
35
  const trimmed = value.trim();
@@ -21,6 +21,12 @@
21
21
  * `Date#toISOString()`.
22
22
  */
23
23
 
24
+ /**
25
+ * Pairing-challenge TTL in milliseconds (10 minutes): the gateway's challenge
26
+ * store enforces it and the `vellum pair` CLI renders it in user-facing copy.
27
+ */
28
+ export const REMOTE_WEB_PAIRING_CODE_TTL_MS = 10 * 60 * 1000;
29
+
24
30
  /** `POST /v1/remote-web/pairing-challenge` request body. */
25
31
  export interface RemoteWebPairingChallengeRequest {
26
32
  /** Public https base URL the scanning device can reach the assistant at. */
@@ -157,6 +157,8 @@ export {
157
157
  hashVerificationSecret,
158
158
  IdentityBindingStatusSchema,
159
159
  ResolveBootstrapSessionIpcParamsSchema,
160
+ bindsSameIdentity,
161
+ boundIdentity,
160
162
  RevokePendingSessionsIpcParamsSchema,
161
163
  SessionLookupIpcResponseSchema,
162
164
  SessionMutationIpcResponseSchema,
@@ -172,6 +174,7 @@ export {
172
174
 
173
175
  export type {
174
176
  BindSessionIdentityIpcParams,
177
+ BoundIdentity,
175
178
  CountRecentSendsIpcParams,
176
179
  CountRecentSendsIpcResponse,
177
180
  CreateInboundSessionIpcParams,
@@ -183,6 +186,7 @@ export type {
183
186
  FindActiveSessionIpcParams,
184
187
  GetPendingSessionIpcParams,
185
188
  IdentityBindingStatus,
189
+ IdentityBoundSession,
186
190
  ResolveBootstrapSessionIpcParams,
187
191
  RevokePendingSessionsIpcParams,
188
192
  SessionLookupIpcResponse,
@@ -113,6 +113,78 @@ export const VerificationSessionSchema = z.object({
113
113
 
114
114
  export type VerificationSessionWire = z.infer<typeof VerificationSessionSchema>;
115
115
 
116
+ // ---------------------------------------------------------------------------
117
+ // Bound identity (which of the three identity columns decides who may redeem)
118
+ // ---------------------------------------------------------------------------
119
+
120
+ /** The identity fields the rules read. Callers may pass a full session row. */
121
+ export interface IdentityBoundSession {
122
+ expectedExternalUserId?: string | null;
123
+ expectedChatId?: string | null;
124
+ expectedPhoneE164?: string | null;
125
+ }
126
+
127
+ /**
128
+ * The identity a session redeems on.
129
+ *
130
+ * `chatId` is compared against the actor's conversation and everything else
131
+ * against the actor themselves, which is why the field travels with the value.
132
+ */
133
+ export interface BoundIdentity {
134
+ field: "phoneE164" | "externalUserId" | "chatId";
135
+ value: string;
136
+ }
137
+
138
+ /**
139
+ * The one identity a session is bound to, or null when it is bound to none
140
+ * (an inbound challenge, or a bootstrap session before its deep link is
141
+ * redeemed).
142
+ *
143
+ * This lives in the contract because more than one side has to agree on it:
144
+ * the gateway consume path decides who may redeem a code, and the supersede
145
+ * scope decides whose earlier codes a fresh mint kills. Those two disagreeing
146
+ * is a silent one-time-code bug, so the rule is stated once.
147
+ *
148
+ * Precedence, most to least specific:
149
+ *
150
+ * 1. `expectedPhoneE164` (a voice session's subject is the number itself).
151
+ * 2. `expectedExternalUserId` (the person).
152
+ * 3. `expectedChatId` (the conversation), and only when nothing identifies a
153
+ * person. A chat id can be shared (a Slack channel, a Telegram group), so
154
+ * on its own it would let any participant satisfy the binding. It counts
155
+ * only where there is no better answer.
156
+ */
157
+ export function boundIdentity(
158
+ session: IdentityBoundSession,
159
+ ): BoundIdentity | null {
160
+ // Presence, not truthiness. An empty string is a set-but-useless identity,
161
+ // and treating it as absent would report the session unbound, which the
162
+ // consume path reads as "no identity to check" and admits anyone holding
163
+ // the code. Kept as a value it can compare against and fail instead.
164
+ if (session.expectedPhoneE164 != null) {
165
+ return { field: "phoneE164", value: session.expectedPhoneE164 };
166
+ }
167
+ if (session.expectedExternalUserId != null) {
168
+ return { field: "externalUserId", value: session.expectedExternalUserId };
169
+ }
170
+ if (session.expectedChatId != null) {
171
+ return { field: "chatId", value: session.expectedChatId };
172
+ }
173
+ return null;
174
+ }
175
+
176
+ /**
177
+ * Whether two sessions are bound to the same identity. Two sessions bound to
178
+ * no identity are not the same: bootstrap sessions are claimed by id, so an
179
+ * unbound mint must not sweep up every other unbound row.
180
+ */
181
+ export function bindsSameIdentity(
182
+ a: BoundIdentity | null,
183
+ b: BoundIdentity | null,
184
+ ): boolean {
185
+ return a !== null && b !== null && a.field === b.field && a.value === b.value;
186
+ }
187
+
116
188
  // ---------------------------------------------------------------------------
117
189
  // IPC methods
118
190
  // ---------------------------------------------------------------------------
@@ -193,9 +265,12 @@ export type CreateInboundSessionIpcResponse = z.infer<
193
265
  /** Request for `verification_sessions_create_outbound`. */
194
266
  export const CreateOutboundSessionIpcParamsSchema = z.object({
195
267
  channel: z.string().min(1),
196
- expectedExternalUserId: z.string().optional(),
197
- expectedChatId: z.string().optional(),
198
- expectedPhoneE164: z.string().optional(),
268
+ // An identity field is either absent or a real value. An empty string is
269
+ // neither, and a session bound to one can never be redeemed by anybody,
270
+ // so reject it at the boundary rather than mint a dead code.
271
+ expectedExternalUserId: z.string().min(1).optional(),
272
+ expectedChatId: z.string().min(1).optional(),
273
+ expectedPhoneE164: z.string().min(1).optional(),
199
274
  identityBindingStatus: IdentityBindingStatusSchema.optional(),
200
275
  destinationAddress: z.string().optional(),
201
276
  codeDigits: z.number().int().positive().optional(),
@@ -211,7 +286,6 @@ export const CreateOutboundSessionIpcParamsSchema = z.object({
211
286
  requireSourceSessionPending: z.string().min(1).optional(),
212
287
  // Mint only if the channel has no active (pending_bootstrap /
213
288
  // awaiting_response, non-expired) session.
214
- ifNoneActive: z.boolean().optional(),
215
289
  // Sender-scoped variant: mint unless the channel's active session is bound
216
290
  // to this expectedExternalUserId (a different sender may supersede).
217
291
  ifNoneActiveForExternalUserId: z.string().min(1).optional(),
@@ -240,7 +314,8 @@ export type CreateOutboundSessionIpcResponse = z.infer<
240
314
 
241
315
  /**
242
316
  * Conflict marker returned by `verification_sessions_create_outbound` when a
243
- * claim guard (`requireSourceSessionPending` / `ifNoneActive`) fails. Only
317
+ * claim guard (`requireSourceSessionPending` /
318
+ * `ifNoneActiveForExternalUserId`) fails. Only
244
319
  * reachable when a guard was passed — legacy callers never see this shape.
245
320
  */
246
321
  export const CreateOutboundSessionConflictSchema = z.object({
@@ -277,7 +352,17 @@ export type GetPendingSessionIpcParams = z.infer<
277
352
  * Request for `verification_sessions_find_active` (statuses
278
353
  * `pending_bootstrap`/`awaiting_response`, newest first).
279
354
  */
280
- export const FindActiveSessionIpcParamsSchema = ChannelOnlyIpcParamsSchema;
355
+ export const FindActiveSessionIpcParamsSchema =
356
+ ChannelOnlyIpcParamsSchema.extend({
357
+ /**
358
+ * Narrows the lookup to one session. A channel can carry several live
359
+ * sessions at once, one per person verifying plus the guardian's own flow,
360
+ * so a caller that means a particular one has to say which. Omitted, the
361
+ * lookup returns whoever started most recently.
362
+ */
363
+ expectedExternalUserId: z.string().min(1).optional(),
364
+ verificationPurpose: VerificationPurposeSchema.optional(),
365
+ });
281
366
 
282
367
  export type FindActiveSessionIpcParams = z.infer<
283
368
  typeof FindActiveSessionIpcParamsSchema
@@ -0,0 +1,36 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { removeIpcEndpointFile, resolveIpcEndpoint } from "./endpoint.js";
6
+ const windowsEndpoint = (name: string, workspaceDir: string) => resolveIpcEndpoint(name, { workspaceDir, platform: "win32" });
7
+ describe("resolveIpcEndpoint", () => {
8
+ test("keeps long POSIX fallbacks deterministic and bounded", () => {
9
+ const options = {
10
+ workspaceDir: `/tmp/${"a".repeat(120)}`,
11
+ platform: "darwin" as const,
12
+ };
13
+ const endpoint = resolveIpcEndpoint("assistant", options);
14
+ expect(resolveIpcEndpoint("assistant", options)).toEqual(endpoint);
15
+ expect(Buffer.byteLength(endpoint.path)).toBeLessThanOrEqual(103);
16
+ });
17
+ test("returns normalized, isolated, bounded Windows pipes", () => {
18
+ const first = windowsEndpoint("assistant", "C:\\one");
19
+ expect(resolveIpcEndpoint("assistant", { workspaceDir: "c:\\ONE\\", platform: "win32", env: { ASSISTANT_IPC_SOCKET_DIR: "C:\\ignored" } })).toEqual(first);
20
+ expect(first.path.length).toBeLessThanOrEqual(256);
21
+ expect(windowsEndpoint("assistant", "C:\\two").path).not.toBe(first.path);
22
+ expect(windowsEndpoint("gateway", "C:\\one").path).not.toBe(first.path);
23
+ });
24
+ test("cleans socket files but skips named pipes", () => {
25
+ const dir = mkdtempSync(join(tmpdir(), "vellum-ipc-cleanup-"));
26
+ const socketPath = join(dir, "assistant.sock");
27
+ try {
28
+ writeFileSync(socketPath, "stale");
29
+ removeIpcEndpointFile(socketPath);
30
+ expect(existsSync(socketPath)).toBe(false);
31
+ removeIpcEndpointFile("\\\\.\\pipe\\vellum-assistant-test");
32
+ } finally {
33
+ rmSync(dir, { recursive: true, force: true });
34
+ }
35
+ });
36
+ });
@@ -0,0 +1,142 @@
1
+ import { createHash } from "node:crypto";
2
+ import { unlinkSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { posix, win32 } from "node:path";
5
+
6
+ const DARWIN_UNIX_SOCKET_MAX_PATH_BYTES = 103;
7
+ const DEFAULT_UNIX_SOCKET_MAX_PATH_BYTES = 107;
8
+ const IPC_TMP_DIR_NAME = "vellum-ipc";
9
+ const join = posix.join;
10
+
11
+ export type IpcSocketPathSource =
12
+ | "env-override"
13
+ | "workspace"
14
+ | "tmp-hash"
15
+ | "tmp-short-hash"
16
+ | "windows-named-pipe";
17
+
18
+ export interface IpcSocketPathResolution {
19
+ path: string;
20
+ source: IpcSocketPathSource;
21
+ }
22
+
23
+ export interface IpcEndpointOptions {
24
+ workspaceDir: string;
25
+ env?: Record<string, string | undefined>;
26
+ platform?: NodeJS.Platform;
27
+ }
28
+
29
+ function getUnixSocketMaxPathBytes(platform: NodeJS.Platform): number {
30
+ return platform === "darwin"
31
+ ? DARWIN_UNIX_SOCKET_MAX_PATH_BYTES
32
+ : DEFAULT_UNIX_SOCKET_MAX_PATH_BYTES;
33
+ }
34
+
35
+ function isPathWithinSocketLimit(path: string, maxPathBytes: number): boolean {
36
+ return Buffer.byteLength(path, "utf8") <= maxPathBytes;
37
+ }
38
+
39
+ /**
40
+ * Derive the env var name and socket filename from a socket name.
41
+ *
42
+ * Examples (hyphens in the name become underscores in the env var):
43
+ */
44
+ function deriveSocketNames(socketName: string): {
45
+ envVar: string;
46
+ fileName: string;
47
+ } {
48
+ if (!/^[a-z][a-z0-9-]{0,63}$/.test(socketName)) {
49
+ throw new Error(`Invalid IPC endpoint name: ${socketName}`);
50
+ }
51
+ const envVar = `${socketName.toUpperCase().replace(/-/g, "_")}_IPC_SOCKET_DIR`;
52
+ const fileName = `${socketName}.sock`;
53
+ return { envVar, fileName };
54
+ }
55
+
56
+ function resolveWindowsNamedPipe(
57
+ socketName: string,
58
+ workspaceDir: string,
59
+ ): IpcSocketPathResolution {
60
+ const workspaceIdentity = win32
61
+ .normalize(workspaceDir)
62
+ .replace(/\\+$/, "")
63
+ .toLowerCase();
64
+ const hash = createHash("sha256")
65
+ .update(`${workspaceIdentity}\0${socketName}`)
66
+ .digest("hex")
67
+ .slice(0, 24);
68
+ return {
69
+ path: `\\\\.\\pipe\\vellum-${socketName}-${hash}`,
70
+ source: "windows-named-pipe",
71
+ };
72
+ }
73
+
74
+ export function isNamedPipePath(endpointPath: string): boolean {
75
+ return /^\\\\[.?]\\pipe\\/.test(endpointPath);
76
+ }
77
+
78
+ export function removeIpcEndpointFile(endpointPath: string): void {
79
+ if (isNamedPipePath(endpointPath)) {
80
+ return;
81
+ }
82
+ try {
83
+ unlinkSync(endpointPath);
84
+ } catch {
85
+ // Already absent.
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Resolve the path to an IPC socket file.
91
+ *
92
+ * Resolution order:
93
+ * POSIX uses overrides, workspace paths, then bounded temporary paths.
94
+ * Windows uses deterministic named pipes.
95
+ */
96
+ export function resolveIpcEndpoint(
97
+ socketName: string,
98
+ options: IpcEndpointOptions,
99
+ ): IpcSocketPathResolution {
100
+ const { envVar, fileName } = deriveSocketNames(socketName);
101
+ const platform = options.platform ?? process.platform;
102
+ if (platform === "win32") {
103
+ return resolveWindowsNamedPipe(socketName, options.workspaceDir);
104
+ }
105
+
106
+ // Explicit override via env var.
107
+ const envSocketDir = (options.env ?? process.env)[envVar]?.trim();
108
+ if (envSocketDir) {
109
+ return {
110
+ path: join(envSocketDir, fileName),
111
+ source: "env-override",
112
+ };
113
+ }
114
+
115
+ const maxPathBytes = getUnixSocketMaxPathBytes(platform);
116
+ const workspacePath = join(options.workspaceDir, fileName);
117
+
118
+ if (isPathWithinSocketLimit(workspacePath, maxPathBytes)) {
119
+ return {
120
+ path: workspacePath,
121
+ source: "workspace",
122
+ };
123
+ }
124
+
125
+ // Workspace path exceeds AF_UNIX limit - fall back to tmpdir.
126
+ const hash = createHash("sha256")
127
+ .update(workspacePath)
128
+ .digest("hex")
129
+ .slice(0, 12);
130
+ const hashedPath = join(tmpdir(), IPC_TMP_DIR_NAME, `${hash}-${fileName}`);
131
+ if (isPathWithinSocketLimit(hashedPath, maxPathBytes)) {
132
+ return {
133
+ path: hashedPath,
134
+ source: "tmp-hash",
135
+ };
136
+ }
137
+
138
+ return {
139
+ path: join(tmpdir(), `v-${hash}.sock`),
140
+ source: "tmp-short-hash",
141
+ };
142
+ }
@@ -4,6 +4,8 @@ export {
4
4
  type SocketWatchdogOptions,
5
5
  type SocketWatchdogLogger,
6
6
  } from "./socket-watchdog.js";
7
+ export { isNamedPipePath, removeIpcEndpointFile, resolveIpcEndpoint } from "./endpoint.js";
8
+ export { ipcListenOptions } from "./listen-options.js";
7
9
  export {
8
10
  IpcFrameReader,
9
11
  writeLegacyMessage,
@@ -0,0 +1,3 @@
1
+ export function ipcListenOptions(path: string) {
2
+ return { path, readableAll: false, writableAll: false } as const;
3
+ }
@@ -46,6 +46,7 @@ interface TestHarness {
46
46
 
47
47
  interface BuildOptions {
48
48
  intervalMs?: number;
49
+ socketPathOverride?: string;
49
50
  createServerOverride?: () => Server;
50
51
  /** Override `getServer` to simulate races. */
51
52
  getServerOverride?: () => Server | null;
@@ -75,7 +76,7 @@ function buildHarness(opts: BuildOptions): TestHarness {
75
76
  };
76
77
 
77
78
  const watchdog = new SocketWatchdog({
78
- socketPath,
79
+ socketPath: opts.socketPathOverride ?? socketPath,
79
80
  intervalMs: opts.intervalMs ?? 0,
80
81
  getServer: opts.getServerOverride ?? (() => serverRef.current),
81
82
  createServer: opts.createServerOverride ?? defaultFactory,
@@ -190,6 +191,19 @@ describe("SocketWatchdog", () => {
190
191
  expect(harness.rebinds).toHaveLength(0);
191
192
  });
192
193
 
194
+ test("named pipes skip filesystem watchdog behavior", async () => {
195
+ const server = createServer();
196
+ harness = buildHarness({
197
+ socketPathOverride: "\\\\.\\pipe\\vellum-assistant-test",
198
+ getServerOverride: () => server,
199
+ createServerOverride: () => {
200
+ throw new Error("must not rebind");
201
+ },
202
+ });
203
+ harness.spawnedServers.push(server);
204
+ expect(await harness.watchdog.rebindIfMissing()).toBe(false);
205
+ });
206
+
193
207
  test("rebindIfMissing recreates the listener when the path is gone", async () => {
194
208
  harness = buildHarness({});
195
209
  const initial = await startInitialServer(harness);
@@ -16,6 +16,9 @@ import { existsSync, mkdirSync, unlinkSync } from "node:fs";
16
16
  import type { Server } from "node:net";
17
17
  import { dirname } from "node:path";
18
18
 
19
+ import { isNamedPipePath } from "./endpoint.js";
20
+ import { ipcListenOptions } from "./listen-options.js";
21
+
19
22
  /**
20
23
  * Minimal logger surface (pino-compatible). Each method receives a context
21
24
  * object plus an optional human-readable message.
@@ -66,6 +69,9 @@ const DEFAULT_INTERVAL_MS = 5000;
66
69
  * `mkdir` only applies the mode to directories it creates.
67
70
  */
68
71
  export function ensureSocketDir(socketPath: string): void {
72
+ if (isNamedPipePath(socketPath)) {
73
+ return;
74
+ }
69
75
  const socketDir = dirname(socketPath);
70
76
  if (!existsSync(socketDir)) {
71
77
  mkdirSync(socketDir, { recursive: true, mode: 0o700 });
@@ -109,7 +115,13 @@ export class SocketWatchdog {
109
115
  * watchdog is already running.
110
116
  */
111
117
  start(): void {
112
- if (this.intervalMs <= 0 || this.handle !== null) return;
118
+ if (
119
+ this.intervalMs <= 0 ||
120
+ this.handle !== null ||
121
+ isNamedPipePath(this.socketPath)
122
+ ) {
123
+ return;
124
+ }
113
125
  this.handle = setInterval(() => {
114
126
  // The async entry path of rebindIfMissing performs filesystem work
115
127
  // (ensureSocketDir, createServer) before its inner try/catch, so a
@@ -143,6 +155,9 @@ export class SocketWatchdog {
143
155
  * is not running, or a shutdown/restart raced the rebind.
144
156
  */
145
157
  async rebindIfMissing(): Promise<boolean> {
158
+ if (isNamedPipePath(this.socketPath)) {
159
+ return false;
160
+ }
146
161
  const initialServer = this.getServer();
147
162
  if (initialServer === null) return false;
148
163
  if (existsSync(this.socketPath)) return false;
@@ -167,7 +182,7 @@ export class SocketWatchdog {
167
182
  };
168
183
  newServer.once("error", onError);
169
184
  newServer.once("listening", onListening);
170
- newServer.listen(this.socketPath);
185
+ newServer.listen(ipcListenOptions(this.socketPath));
171
186
  });
172
187
  } catch (err) {
173
188
  this.log.error(
@@ -40,3 +40,42 @@ export function isChannelId(value: unknown): value is ChannelId {
40
40
  (CHANNEL_IDS as readonly string[]).includes(value)
41
41
  );
42
42
  }
43
+
44
+ /**
45
+ * The provider key holding each channel's bot credential.
46
+ *
47
+ * Two senses of "connected" share the provider registry: a provider the user
48
+ * authorized so the assistant can act **as them** (`slack`, `discord`,
49
+ * `google`), and a bot credential letting people reach the assistant **as
50
+ * itself**. Nothing in a provider key says which, and the naming actively
51
+ * misleads: `slack` and `discord` name the user integration while their bots
52
+ * take a `_channel` suffix, yet `telegram` *is* the bot, because Telegram has
53
+ * no user-identity integration.
54
+ *
55
+ * That irregularity is why this is stated rather than derived from the key,
56
+ * and it is stated here because this file already owns what a channel is.
57
+ *
58
+ * Deliberately only the key. What fields each credential requires is declared
59
+ * once already, per service, in the gateway's credential specs; restating it
60
+ * here would be a second copy of a different fact.
61
+ *
62
+ * Channels absent from this map reach the assistant without a bot credential
63
+ * of their own: `phone` through the voice provider, `vellum` and `platform`
64
+ * internally.
65
+ */
66
+ export const CHANNEL_BOT_PROVIDER = {
67
+ slack: "slack_channel",
68
+ discord: "discord_channel",
69
+ telegram: "telegram",
70
+ } as const satisfies Partial<Record<ChannelId, string>>;
71
+
72
+ /**
73
+ * Whether a provider key names a bot the assistant is reached through, rather
74
+ * than a grant letting it act as the user. This is the "which sense of
75
+ * connected" question, for any provider key.
76
+ */
77
+ export function isChannelBotProvider(providerKey: string): boolean {
78
+ return (Object.values(CHANNEL_BOT_PROVIDER) as readonly string[]).includes(
79
+ providerKey,
80
+ );
81
+ }