@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
@@ -22,33 +22,50 @@ Subagent orchestration -- spawn background agents to work on tasks in parallel.
22
22
 
23
23
  Subagents follow this status flow: `pending` -> `running` -> `completed` / `failed` / `aborted`
24
24
 
25
- - **Spawn**: Use `subagent_spawn` with a label, objective, and role. The subagent runs autonomously.
25
+ - **Spawn**: Use `subagent_spawn` with a label, objective, and type. The subagent runs autonomously.
26
26
  - **Mid-run communication**: Subagents can send notifications to the parent via `notify_parent` while still running -- useful for sharing interim findings or signaling that they are blocked.
27
27
  - **Auto-notification**: The parent conversation is automatically notified when a subagent reaches a terminal status (completed/failed/aborted). Do NOT poll `subagent_status`.
28
28
  - **Read output**: Use `subagent_read` after the subagent reaches a terminal status to retrieve its full output.
29
29
 
30
- ## Roles
30
+ ## Types
31
31
 
32
- Each subagent is spawned with a role that determines its tool access. Choose the most restrictive role that can accomplish the task.
32
+ There are three subagent types. Pick one with two questions: **does it need to change anything**, and **do you need its answer before you can continue?**
33
33
 
34
34
  `recall` is local information search across memory, the personal knowledge base, past conversations, and workspace files. Use it when a subagent needs prior context that is not already in the prompt.
35
35
 
36
- | Role | Tools | When to use |
37
- |---|---|---|
38
- | `general` | Full tool access | Task genuinely needs unrestricted capabilities (rare -- prefer a specialized role) |
39
- | `researcher` | `web_search`, `web_fetch`, `file_read`, `file_list`, `recall`, `notify_parent` | Information gathering, web research, codebase exploration, reading documentation |
40
- | `coder` | `bash`, `file_read`, `file_write`, `file_edit`, `web_search`, `recall`, `notify_parent` | Code changes, file editing, running commands, build/test tasks |
41
- | `planner` | `file_read`, `file_list`, `web_search`, `web_fetch`, `recall`, `notify_parent` | Analysis, planning, synthesizing information, reviewing approaches |
42
- | `investigator` | `code_search`, `file_read`, `file_list`, `web_search`, `web_fetch`, `recall`, `notify_parent` | Root-cause analysis: debugging, log forensics, tracing behavior across many files. Read-only search/read tools only (no shell): use `code_search` to grep file contents across directories, `file_list` to enumerate paths, `file_read` to read whole files and logs; returns a compact root-cause report |
43
- | `advisor` | None (tool-less) | Read-only senior-advisor consult. Runs on a stronger model, inherits full parent context, and BLOCKS until it returns guidance |
36
+ | Type | Changes things? | You wait? | Tools | When to use |
37
+ |---|---|---|---|---|
38
+ | `researcher` | No | No | `web_search`, `web_fetch`, `file_read`, `file_list`, `code_search`, `recall`, `skill_execute`, `notify_parent` | Web research, codebase exploration, reading documentation, root-cause investigation, reviewing an approach against the code |
39
+ | `builder` | Yes | No | Your whole tool surface, unrestricted: shell, file writes and edits, and every connector, MCP, and browser tool you can reach | Code changes, file output, build/test runs, anything that must run a command or act on an outside system |
40
+ | `advisor` | No | Yes | Read-only fact checking in the workspace: `file_read`, `file_list`, `code_search` | Read-only senior-advisor consult. Runs on a stronger model, inherits full parent context, and BLOCKS until it returns guidance |
44
41
 
45
- All specialized roles (`researcher`, `coder`, `planner`) include `notify_parent` for mid-run communication with the parent.
42
+ Both background types can call `notify_parent` for mid-run communication with the parent.
46
43
 
47
- Read-only roles (`researcher`, `planner`, `investigator`, `advisor`) cannot write or edit files, run commands, or otherwise persist output. If the task must **produce a file, save results, or run a command**, use `coder` (or `general`) a read-only role finishes without producing anything, and the delegated write silently no-ops.
44
+ A `researcher` is scoped to the fixed read-only list above: it cannot write or edit files, run commands, reach a connector, or otherwise persist output. If the task must **produce a file, save results, run a command, or act on an outside system**, spawn a `builder`: a researcher finishes without producing anything, and the delegated write silently no-ops.
45
+
46
+ **Model tier is a separate knob.** Use `inference_profile` to run any type on a stronger or cheaper model. **A persona is not a type**: see the fallback below.
47
+
48
+ ### Legacy names and unknown roles
49
+
50
+ The older role names still work: `planner` and `investigator` run as a `researcher`, `coder` and `general` run as a `builder`. The spawn result names the type that actually ran.
51
+
52
+ Any other `role` text is treated as a persona, not a type. The subagent runs as a **`researcher`** (read-only) with that text framing how it approaches the task, and the spawn result says so. That is deliberate least privilege: an invented or misspelled role must never silently hand out write access. If the task genuinely needed to write, the subagent reports that it cannot, and you re-spawn it with `role: "builder"`.
53
+
54
+ Omitting `role` entirely runs a `builder`, so a spawn that names no type keeps your full tool surface.
55
+
56
+ ### Verification
57
+
58
+ Checking that something is actually done is not a fourth type. It is a `researcher` with `output_contract: "verdict"`.
59
+
60
+ A verdict subagent returns, for each criterion in the objective, `PASS` or `FAIL` plus the exact evidence (file path, line, value, or quote), `CANNOT VERIFY` where the evidence is missing, and nothing else. Give it the criteria explicitly in the objective; a vague "check the work" gets you a vague list.
61
+
62
+ It runs on a cheaper model by default, because checking a claim against evidence that already exists is mechanical work, not investigation. An explicit `inference_profile` still wins if a check genuinely needs a stronger model, and so does a profile pinned on the `subagentSpawn` call site in config (see Inference Profile below).
63
+
64
+ The other contracts: `output_contract: "artifact"` tells a `builder` that the deliverable is the thing produced and to end by listing the exact files it created or modified. `"report"` is the default and asks for nothing extra. A contract that does not match the type is rejected rather than quietly changed, and the `advisor` takes no contract (it has its own framing).
48
65
 
49
66
  ## Consulting the Advisor
50
67
 
51
- The `advisor` role is the one role you spawn on your own judgment, unprompted you do NOT wait for the user to ask for a subagent. The other roles (`researcher`, `coder`, `planner`, `investigator`, `general`) stay delegation-driven: reach for them to offload work, typically when the user's request calls for it. The advisor is different: proactively consult it whenever the conditions below are met.
68
+ The `advisor` is the one type you spawn on your own judgment, unprompted: you do NOT wait for the user to ask for a subagent. The background types (`researcher`, `builder`) stay delegation-driven: reach for them to offload work, typically when the user's request calls for it. The advisor is different: proactively consult it whenever the conditions below are met.
52
69
 
53
70
  Orient yourself first (read the relevant files, understand the task), then consult the advisor:
54
71
 
@@ -58,7 +75,7 @@ Orient yourself first (read the relevant files, understand the task), then consu
58
75
 
59
76
  The consult is synchronous and read-only: spawning an `advisor` subagent BLOCKS until it returns guidance. It runs on a stronger model and inherits your full context, so it sees the task, your tool calls, and their results without you re-explaining. It also receives a snapshot of your environment (the tools available to you this turn, the full skill catalog, and your workspace) so its guidance can point you at existing platform capabilities by name. Give its guidance serious weight; only override it when primary-source evidence contradicts a specific claim, and say so when you do.
60
77
 
61
- Beyond that snapshot, the advisor sees only what is in this conversation. It cannot read files, other conversations, or external systems on its own. If you want it to weigh specific evidence (a file's contents, a command's output, results gathered elsewhere), surface that evidence in the conversation or the spawn objective before consulting.
78
+ The advisor has read-only workspace tools (`file_read`, `file_list`, `code_search`) so it can open a file or search the code when a decisive fact would change its advice. It uses them sparingly, for verification rather than exploration, and it cannot change anything or persist output. It has no memory search and cannot see other conversations or external systems, and every lookup it has to make delays your answer, so surface the evidence you already have (a file's contents, a command's output, results gathered elsewhere) in the conversation or the spawn objective before consulting.
62
79
 
63
80
  Spawn the advisor **alone** — do NOT batch the consult in the same turn as other tool calls (especially file edits, shell commands, or anything destructive or expensive). Tool calls you issue in the same turn run concurrently with the consult, so they would execute before you see its guidance. Consult the advisor by itself, read its guidance, then act.
64
81
 
@@ -90,15 +107,29 @@ Only the parent conversation that spawned a subagent can interact with it (check
90
107
 
91
108
  Set `send_result_to_user: false` when spawning a subagent whose result is for internal processing only. The parent will still be notified on completion, but the notification will instruct it to read the result without presenting it to the user.
92
109
 
110
+ ## Repeat Spawns
111
+
112
+ Spawning an objective that several near-identical subagents have already completed in the last day can come back as a message about those earlier runs instead of a new subagent. Read what the earlier run produced with `subagent_read`, or narrow the objective to what is actually still missing.
113
+
114
+ A second message covers the other shape: near-identical copies that are still running and have returned nothing yet. There is nothing to read in that case, so wait for the running ones to report back, or narrow the objective to the part they are not covering.
115
+
116
+ Either way it is advisory, not a block: pass `confirm_repeat: true` to spawn anyway. An `advisor` consult is never held this way.
117
+
93
118
  ## Inference Profile
94
119
 
95
- Set `inference_profile` to an `llm.profiles` key when a subagent should run under a specific model profile. When omitted, the subagent inherits the parent turn's active profile if one exists; otherwise it uses the `subagentSpawn` call site's default model selection.
120
+ Set `inference_profile` to an `llm.profiles` key when a subagent should run under a specific model profile.
121
+
122
+ When it is omitted, the subagent takes the `subagentSpawn` call site's default model selection. It does not pick up the profile the spawning turn is running on: a profile pinned on a conversation is a choice about that conversation, and it does not follow the work that conversation delegates.
123
+
124
+ An `inference_profile` you name explicitly wins, unless the model catalog does not report it as tool-capable, in which case it is replaced by the `subagentSpawn` default with a note on the spawn result. An `advisor` consult applies that same fallback, with the note alongside its guidance.
125
+
126
+ `output_contract: "verdict"` takes a cheaper profile, so a verdict runs cheap unless you name an `inference_profile` yourself. A profile pinned on the `subagentSpawn` call site in config also beats the cheap preset, so an operator can decide what checks run on.
96
127
 
97
128
  ## Fork Mode
98
129
 
99
130
  Forks are sub-agents that inherit the parent's full context -- messages, system prompt, and memory -- sharing the KV cache for near-free context inheritance. Use forks when the task benefits from knowing what you've been discussing; use a regular sub-agent when the task is self-contained.
100
131
 
101
- **Key behaviors:** Forks default to `general` role (the `role` parameter is ignored for forks), except the special `advisor` role, which is honored even as a fork. `send_result_to_user` defaults to `false`. Read fork output with `last_n: 1` to get only the final synthesis.
132
+ **Key behaviors:** A fork honors the type you name, so `role: "researcher"` gives a read-only fork. A fork that names no type runs as a `builder` and so keeps your full tool surface, which is what the system prompt it inherits describes. A persona reaches a fork through its task framing rather than its prompt, since the prompt is yours verbatim. `send_result_to_user` defaults to `false`. Read fork output with `last_n: 1` to get only the final synthesis.
102
133
 
103
134
  **When to fork vs regular sub-agent:**
104
135
 
@@ -110,17 +141,17 @@ Forks are sub-agents that inherit the parent's full context -- messages, system
110
141
  | Comparing multiple sources against what was discussed | Parallel forks |
111
142
  | Self-contained task with a clear objective | Regular sub-agent |
112
143
 
113
- Rule of thumb: "Does this task need to know what we've been talking about?" If yes, fork. If the objective is fully self-describing, use a regular sub-agent with a scoped role.
144
+ Rule of thumb: "Does this task need to know what we've been talking about?" If yes, fork. If the objective is fully self-describing, use a regular sub-agent with a scoped type.
114
145
 
115
146
  ## Tips
116
147
 
117
148
  - Do NOT poll `subagent_status` in a loop. You will be notified automatically when a subagent completes.
118
- - Use roles to scope tool access and minimize blast radius. Default to the most restrictive role that works.
119
- - Spawn a `researcher` and `coder` in parallel for research-then-implement workflows -- the researcher gathers context while the coder starts on the known parts.
149
+ - Prefer `researcher` unless the task has to change something. Read-only is the smaller blast radius, and most delegated work is reading.
150
+ - Spawn a `researcher` and a `builder` in parallel for research-then-implement workflows -- the researcher gathers context while the builder starts on the known parts.
120
151
  - Use `notify_parent` for interim findings instead of waiting for completion. This lets the parent act on partial results early.
121
152
  - Use `subagent_message` to send follow-up instructions to a running subagent.
122
153
  - Use `subagent_abort` to cancel a subagent that is no longer needed.
123
154
  - Default to spawning subagents for any task that involves web research, multi-file exploration, or independent coding work. Serial execution should be the exception, not the rule.
124
- - Delegate root-cause investigations ("why is X happening?", debugging, log forensics) to an `investigator` instead of grepping inline. A long investigation done inline floods your own context with file slices and grep output, crowding out the conversation; the investigator does the digging in its own context and returns a compact root-cause report.
155
+ - Delegate root-cause investigations ("why is X happening?", debugging, log forensics) to a `researcher` instead of grepping inline. A long investigation done inline floods your own context with file slices and grep output, crowding out the conversation; the researcher does the digging in its own context and returns a compact root-cause report.
125
156
  - When a user request has both an information-gathering component and an action component, spawn a researcher immediately rather than doing the research inline yourself.
126
- - Prefer spawning 2-3 focused subagents over one large general-purpose subagent. Smaller scopes finish faster and fail more gracefully.
157
+ - Prefer spawning 2-3 focused subagents over one broad one. Smaller scopes finish faster and fail more gracefully.
@@ -3,7 +3,7 @@
3
3
  "tools": [
4
4
  {
5
5
  "name": "subagent_spawn",
6
- "description": "Spawn an independent subagent to work on a task in parallel. The subagent runs autonomously and its results are reported back when complete.\n\nTwo modes:\n- **Regular sub-agent** (fork: false or omitted): Gets only the objective + context fields. Use for self-contained tasks with clear objectives. Can use scoped roles (researcher, coder, planner).\n- **Fork** (fork: true): Inherits full parent context (messages, system prompt, memory). Shares KV cache for near-free context inheritance. Use when the task benefits from knowing what you've been discussing. Defaults to general role (the 'advisor' role is the exception: it is honored as a fork). Results are internal by default (send_result_to_user: false). Read with last_n: 1 to get only the final synthesis.\n\nDecision heuristic: Does the task need to know what we've been talking about? Fork. Is it self-contained? Regular sub-agent.\n\nThe 'advisor' role is a synchronous, read-only \"consult a stronger advisor\" mode: it inherits your full context plus a snapshot of your environment (available tools, skill catalog, workspace), has no tools of its own, and BLOCKS until it returns focused strategic guidance in a single response.",
6
+ "description": "Spawn an independent subagent to work on a task in parallel. The subagent runs autonomously and its results are reported back when complete.\n\nTwo modes:\n- **Regular sub-agent** (fork: false or omitted): Gets only the objective + context fields. Use for self-contained tasks with clear objectives. Pick its type with role: 'researcher' (read-only, fixed tool list) or 'builder' (write-capable, your whole tool surface).\n- **Fork** (fork: true): Inherits full parent context (messages, system prompt, memory). Shares KV cache for near-free context inheritance. Use when the task benefits from knowing what you've been discussing. A fork that names a role runs scoped to it; a fork that names none keeps your full tool surface. Results are internal by default (send_result_to_user: false). Read with last_n: 1 to get only the final synthesis.\n\nDecision heuristic: Does the task need to know what we've been talking about? Fork. Is it self-contained? Regular sub-agent.\n\nThe 'advisor' role is a synchronous, read-only \"consult a stronger advisor\" mode: it inherits your full context plus a snapshot of your environment (available tools, skill catalog, workspace), has read-only workspace tools for checking a decisive fact (file_read, file_list, code_search), and BLOCKS until it returns focused strategic guidance in a single response. It cannot change anything, persist output, or see other conversations.",
7
7
  "category": "orchestration",
8
8
  "risk": "low",
9
9
  "input_schema": {
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "fork": {
25
25
  "type": "boolean",
26
- "description": "When true, the subagent inherits the parent's full context (messages, system prompt, memory) instead of receiving only the objective + context fields. Fork mode defaults to 'general' role (the role parameter is ignored for forks), except the special 'advisor' role which is honored even as a fork, and defaults send_result_to_user to false. Use for tasks that benefit from the full conversational context."
26
+ "description": "When true, the subagent inherits the parent's full context (messages, system prompt, memory) instead of receiving only the objective + context fields. A fork honors the role you name; naming none keeps this conversation's full tool surface, since the inherited system prompt describes those tools. Forks default send_result_to_user to false. Use for tasks that benefit from the full conversational context."
27
27
  },
28
28
  "send_result_to_user": {
29
29
  "type": "boolean",
@@ -31,19 +31,20 @@
31
31
  },
32
32
  "role": {
33
33
  "type": "string",
34
- "enum": [
35
- "general",
36
- "researcher",
37
- "coder",
38
- "planner",
39
- "investigator",
40
- "advisor"
41
- ],
42
- "description": "Agent specialization that controls tool access. 'researcher': read-only (web, files, memory). 'coder': file and bash access. 'planner': read-only analysis. 'investigator': root-cause analysis: debugging, log forensics, tracing behavior across many files; uses read-only search/read tools (code_search, file_read, file_list) with no shell access and returns a compact root-cause report. 'advisor': synchronous, read-only \"consult a stronger advisor\": inherits full context plus a snapshot of your environment (available tools, skill catalog, workspace), has no tools of its own, and blocks until it returns focused strategic guidance. 'general': full access (default). The read-only roles (researcher, planner, investigator, advisor) cannot persist output; use 'coder' or 'general' for any objective that must write files or run commands, or a read-only subagent will finish without producing anything. Non-advisor roles are ignored when fork: true (forks default to general); the advisor role is honored even as a fork."
34
+ "description": "Which of the three subagent types to run, chosen by two questions: does it need to change anything, and do you need the answer before you can continue? 'researcher': changes nothing, runs in the background, scoped to a fixed read-only list (web_search, web_fetch, file_read, file_list, code_search, recall, skill_execute) for research, exploration, root-cause investigation, and review. 'builder': changes things, runs in the background on this conversation's whole tool surface (shell, file writes and edits, plus every connector, MCP, and browser tool you can reach) for code changes, file output, and anything that must run a command or act on an outside system. 'advisor': changes nothing and BLOCKS your turn until it answers; inherits your full context plus a snapshot of your environment (available tools, skill catalog, workspace), can read files and search code (file_read, file_list, code_search) to check a decisive fact, and returns focused strategic guidance. Default when omitted: 'builder', so a spawn that names no type keeps the full tool surface. The older names are still accepted as aliases ('planner' and 'investigator' run as researcher, 'coder' and 'general' run as builder). Any other text is not a type: the subagent runs as a researcher with that text as its persona, so if the task must write files or run commands, name 'builder' explicitly. A read-only subagent asked to produce a file finishes without producing anything. Roles apply to forks too; a fork that names no role runs as a builder and keeps this conversation's full tool surface, and a fork's persona reaches it through its task framing."
43
35
  },
44
36
  "inference_profile": {
45
37
  "type": "string",
46
- "description": "Optional llm.profiles key this subagent should run under. When omitted, the subagent inherits the parent turn's profile when one is active; otherwise it uses the subagentSpawn call site's default model selection."
38
+ "description": "Optional llm.profiles key this subagent should run under. When omitted, the subagent takes the subagentSpawn call site's default model selection, never the profile your own turn is running on: a profile pinned on this conversation is a choice about this conversation and does not follow the work you delegate. A profile you name here that the model catalog does not report as tool-capable is replaced by that same default, with a note on the result. An output_contract of 'verdict' takes a cheaper profile unless you name a profile here, or unless the subagentSpawn call site has its own profile pinned in config, which also beats the cheaper preset."
39
+ },
40
+ "confirm_repeat": {
41
+ "type": "boolean",
42
+ "description": "Set true to spawn anyway when the repeat-spawn guard reports that this objective already completed several times in the last day, or that near-identical copies of it are still running with nothing returned yet."
43
+ },
44
+ "output_contract": {
45
+ "type": "string",
46
+ "enum": ["report", "verdict", "artifact"],
47
+ "description": "What the subagent owes back. 'report' (default): prose answering the objective. 'verdict': per-criterion PASS or FAIL with the exact evidence (file path, line, value, or quote), CANNOT VERIFY where evidence is missing, and no prose beyond that list; researcher-only, and it defaults to a cheaper model because checking is mechanical. Use it for completion checks and audits instead of spawning a premium investigator. 'artifact': the deliverable is the thing produced, ending with the exact files created or modified; builder-only. A contract that does not match the role is rejected rather than coerced, and the advisor takes no contract at all."
47
48
  }
48
49
  },
49
50
  "required": ["label", "objective"]
@@ -123,7 +124,7 @@
123
124
  },
124
125
  {
125
126
  "name": "subagent_read",
126
- "description": "Read the full conversation output from a subagent. Use this after a subagent completes to retrieve its full work product.",
127
+ "description": "NOT a file reader: this returns a subagent's conversation output, addressed by subagent_id or label. To read a file from disk use file_read instead. Use this after a subagent completes to retrieve its full work product; while it is still running you will be notified automatically when it finishes, so there is no need to poll.",
127
128
  "category": "orchestration",
128
129
  "risk": "low",
129
130
  "input_schema": {
@@ -67,7 +67,7 @@ Semantic tokens flip automatically between light and dark. Use them for every su
67
67
  - Surfaces: --surface-base (page), --surface-lift (card), --surface-overlay, --surface-sunken (recessed tile), --surface-hover, --surface-active.
68
68
  - Text, strongest to faintest: --content-emphasised, --content-strong, --content-default (body), --content-secondary (labels and any text under 14px), --content-tertiary (14px and up only), --content-quiet, --content-faint, --content-disabled, --content-inset (on inverted fills). Text at 11 to 13px always takes --content-secondary or stronger.
69
69
  - Borders: --border-base (hairline), --border-subtle, --border-element (visible control edge), --border-hover, --border-active, --border-disabled.
70
- - Status pairs: --system-positive-strong/-weak, --system-negative-strong/-weak (and --system-negative-hover), --system-mid-strong/-weak, --system-info-strong/-weak.
70
+ - Status pairs: --system-positive-strong/-weak, --system-negative-strong/-weak (and --system-negative-hover), --system-mid-strong/-weak, --system-info-strong/-weak. A non-text glyph sitting on its own -weak fill takes --system-positive-on-weak or --system-negative-on-weak; those clear the 3:1 that non-text indicators need, which --system-negative-strong misses on --system-negative-weak in the dark theme. Text on a -weak fill needs 4.5:1 and takes --content-default or stronger; --content-tertiary misses that on both fills in the light and dark themes, and --content-secondary misses it on the negative fill in light.
71
71
  - Type: --font-sans (default), --font-mono (identifiers, code, tabular numbers only), --font-serif (editorial pull-quotes only).
72
72
  - Radius: --radius-xs 2, --radius-sm 4, --radius-md 8, --radius-lg 12, --radius-xl 16, --radius-xxl 20, --radius-pill 999.
73
73
 
@@ -135,7 +135,11 @@ const VELLUM_PROFILE_IMPLS: ProfileImpls = {
135
135
  label: "Speed",
136
136
  description: "Fastest responses, with reasoning turned off",
137
137
  maxTokens: 8192,
138
- effort: "low",
138
+ // Explicit reasoning opt-out, matching `cost-optimized` above: this
139
+ // profile advertises reasoning as off, and OpenAI-compat APIs default
140
+ // reasoning to "medium" when the field is omitted, so the opt-out has to
141
+ // be stated rather than implied.
142
+ effort: "none",
139
143
  thinking: { enabled: false, streamThinking: false },
140
144
  contextWindow: {
141
145
  maxInputTokens: DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS,
@@ -194,7 +198,7 @@ const BYOK_PROFILE_IMPLS: Record<
194
198
  label: "Speed",
195
199
  description: "Fastest responses, with reasoning turned off",
196
200
  maxTokens: 8192,
197
- effort: "low",
201
+ effort: "none",
198
202
  thinking: { enabled: false, streamThinking: false },
199
203
  contextWindow: { maxInputTokens: DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS },
200
204
  },
@@ -101,6 +101,14 @@
101
101
  "description": "Enable the Quick Input popover on right-click of the menu bar icon",
102
102
  "defaultEnabled": false
103
103
  },
104
+ {
105
+ "id": "companion-surface",
106
+ "scope": "client",
107
+ "key": "companion-surface",
108
+ "label": "Companion Surface",
109
+ "description": "Show the macOS companion surface: the floating avatar that carries Talk, Type and the running call, and its tray item. Read in the Electron main process, which decides whether the window opens at all",
110
+ "defaultEnabled": false
111
+ },
104
112
  {
105
113
  "id": "expand-completed-steps",
106
114
  "scope": "client",
@@ -197,14 +205,6 @@
197
205
  "description": "Show the Velvet theme option in the macOS appearance settings. Velvet is a dark-mode variant with red/pink accent colors.",
198
206
  "defaultEnabled": false
199
207
  },
200
- {
201
- "id": "queue-steering",
202
- "scope": "assistant",
203
- "key": "queue-steering",
204
- "label": "Queue Steering",
205
- "description": "Enable the 'Push to agent' button on queued messages, allowing users to steer the assistant to a specific queued message by aborting the current generation and promoting the message to the head of the queue.",
206
- "defaultEnabled": false
207
- },
208
208
  {
209
209
  "id": "chat-pull-to-refresh-enabled",
210
210
  "scope": "client",
@@ -213,14 +213,6 @@
213
213
  "description": "Enable pull-to-refresh gesture in the chat view.",
214
214
  "defaultEnabled": false
215
215
  },
216
- {
217
- "id": "platform-notifications",
218
- "scope": "client",
219
- "key": "platform-notifications",
220
- "label": "Platform Notifications",
221
- "description": "Enable the Notifications tab in settings.",
222
- "defaultEnabled": false
223
- },
224
216
  {
225
217
  "id": "centralized-assistant-status",
226
218
  "scope": "client",
@@ -346,7 +338,7 @@
346
338
  "scope": "client",
347
339
  "key": "assistant-switcher",
348
340
  "label": "Assistant Switcher",
349
- "description": "Show the Switch Assistant section in General settings (local mode only), linking to the assistant chooser.",
341
+ "description": "Show the Switch Assistant section in General settings, linking to the assistant chooser. Covers local clients, signed-in cloud-hub users, remote-gateway origins, and native mobile shells; remote-gateway desktop browsers hand off to the hub chooser with a self-registration param. Also makes the chooser's selection authoritative for the app's active assistant on every surface including the platform hub, a job that previously belonged to multi-platform-assistant alone.",
350
342
  "defaultEnabled": false
351
343
  },
352
344
  {
@@ -364,6 +356,22 @@
364
356
  "label": "Assistant Reply Push",
365
357
  "description": "Gates the `chat.assistant_reply` push notification the assistant sends at the end of a user-initiated turn whose reply the user has not seen. On by default; turn it off to silence the push without disabling the rest of the notification pipeline.",
366
358
  "defaultEnabled": true
359
+ },
360
+ {
361
+ "id": "desktop-presence-suppression",
362
+ "scope": "assistant",
363
+ "key": "desktop-presence-suppression",
364
+ "label": "Desktop Presence Suppression",
365
+ "description": "Gates whether a `chat.assistant_reply` push is suppressed while a Mac desktop client reports itself attended. On by default; turn it off to restore unconditional reply pushes.",
366
+ "defaultEnabled": true
367
+ },
368
+ {
369
+ "id": "plugin-schedules",
370
+ "scope": "assistant",
371
+ "key": "plugin-schedules",
372
+ "label": "Plugin Schedules",
373
+ "description": "Plugins declare recurring schedules as files that register and run automatically",
374
+ "defaultEnabled": false
367
375
  }
368
376
  ]
369
377
  }
@@ -40,7 +40,7 @@ import {
40
40
  * 3. `llm.callSites[callSite].profile` (the call site's named profile)
41
41
  * 4. `CALL_SITE_DEFAULTS[callSite].profile` intent resolved through
42
42
  * `llm.defaultProvider`
43
- * 5. balanced intent through `llm.defaultProvider` the code-owned anchor
43
+ * 5. balanced intent through `llm.defaultProvider`: the code-owned anchor
44
44
  * for profileless call sites, or when nothing above is usable
45
45
  *
46
46
  * A winner must carry its own `provider` AND `model`: the base layer's schema
@@ -157,9 +157,11 @@ export function resolveCallSiteConfig(
157
157
  //
158
158
  // The request config is the base + winner + site-tweak composition:
159
159
  // code-owned schema defaults, then the single winning profile, then the call
160
- // site's own tuning fragment. Selection is pure either/or — no profile ever
161
- // contributes a field to another profile; `deepMerge` here only makes nested
162
- // tweaks (`thinking.enabled`) combine leaf-wise instead of wiping siblings.
160
+ // site's own tuning fragment (its shipped `CALL_SITE_DEFAULTS` tuning with
161
+ // the workspace `llm.callSites` entry layered over it, field by field).
162
+ // Selection is pure either/or: no profile ever contributes a field to
163
+ // another profile, and `deepMerge` only makes nested tweaks
164
+ // (`thinking.enabled`) combine leaf-wise instead of wiping siblings.
163
165
  // `temperature`/`topP` come from the winner (or an explicit tweak);
164
166
  // `logitBias` only ever from the winner.
165
167
  // `forceOverrideProfile` is a no-op here (the override is already first).
@@ -221,8 +223,9 @@ export function selectWinningProfile(
221
223
  }
222
224
  // Anchor: profileless call sites (`vision`, `workflowLeaf`) and any
223
225
  // resolution whose every named rung was unusable land on balanced intent
224
- // through the default provider. `profileName` stays null the anchor is
225
- // not a selection.
226
+ // through the default provider, bottoming out on the code-owned catalog,
227
+ // so it always resolves. `profileName` stays null on the catalog path:
228
+ // the anchor itself is not a selection.
226
229
  return {
227
230
  profileName: null,
228
231
  source: "default",
@@ -380,15 +383,10 @@ function resolveOverrideOrDefault(
380
383
  const winnerFragment: Mergeable =
381
384
  selection.entry == null ? {} : winnerConfigFragment(selection.entry);
382
385
 
383
- // The call site's own tweak fragment: the workspace override replaces the
384
- // code default wholesale. `profile` is the selection discriminator and
385
- // `logitBias` is winner-owned, so neither enters the merge.
386
- const site = llm.callSites?.[callSite] ?? CALL_SITE_DEFAULTS[callSite];
387
- const {
388
- profile: _siteProfile,
389
- logitBias: _siteBias,
390
- ...tweak
391
- } = (site ?? {}) as Record<string, unknown>;
386
+ const tweak = composeCallSiteTweak(
387
+ CALL_SITE_DEFAULTS[callSite] as Mergeable | undefined,
388
+ llm.callSites?.[callSite] as Mergeable | undefined,
389
+ );
392
390
 
393
391
  // Direct call-site model overrides are fragments by design: when the tweak
394
392
  // pins a model the winner's provider does not serve, stamp the catalog
@@ -471,6 +469,38 @@ function winnerConfigFragment(entry: ProfileEntry): Mergeable {
471
469
 
472
470
  type Mergeable = Record<string, unknown>;
473
471
 
472
+ /**
473
+ * The call site's tuning fragment: its shipped `CALL_SITE_DEFAULTS` tuning
474
+ * with the workspace `llm.callSites` entry layered over it, field by field.
475
+ * An entry that only names a profile therefore repoints the selection while
476
+ * the tuning the call site ships with still applies, and a field the entry
477
+ * sets wins for that field alone.
478
+ *
479
+ * `profile` is the selection discriminator and `logitBias` is winner-owned,
480
+ * so neither survives into the fragment. A workspace entry also owns the
481
+ * selection for its own call site: when one exists the shipped layer
482
+ * contributes tuning only, so a shipped `provider`/`model` pin can never
483
+ * override the profile or the custom model the user chose there. With no
484
+ * workspace entry the shipped fragment applies whole, pin included.
485
+ */
486
+ export function composeCallSiteTweak(
487
+ shipped: Mergeable | undefined,
488
+ workspace: Mergeable | undefined,
489
+ ): Mergeable {
490
+ const {
491
+ provider: _shippedProvider,
492
+ model: _shippedModel,
493
+ ...shippedTuning
494
+ } = shipped ?? {};
495
+ const shippedLayer = workspace == null ? (shipped ?? {}) : shippedTuning;
496
+ const {
497
+ profile: _siteProfile,
498
+ logitBias: _siteBias,
499
+ ...tweak
500
+ } = deepMerge(shippedLayer, workspace ?? {});
501
+ return tweak;
502
+ }
503
+
474
504
  /**
475
505
  * FNV-1a 32-bit string hash → unit float in [0, 1). Deterministic and stable
476
506
  * across runtimes — the mix-pick contract depends on identical output for
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  existsSync,
3
3
  mkdirSync,
4
+ readdirSync,
4
5
  readFileSync,
5
6
  renameSync,
6
7
  rmSync,
7
8
  writeFileSync,
8
9
  } from "node:fs";
9
- import { dirname, join } from "node:path";
10
+ import { basename, dirname, join } from "node:path";
10
11
 
11
12
  import { safeStatSync } from "../util/fs.js";
12
13
  import { getLogger } from "../util/logger.js";
@@ -319,6 +320,53 @@ function writeQuarantineNotice(
319
320
  }
320
321
  }
321
322
 
323
+ /**
324
+ * Whether the newest quarantined config next to `configPath`
325
+ * (`config.json.corrupt-*.json`, written by {@link quarantineCorruptConfig})
326
+ * carried `memory.v3.live: true`. Quarantine filenames embed a
327
+ * filesystem-safe ISO timestamp, so a lexicographic sort is chronological.
328
+ *
329
+ * The first-launch seed asks this so a quarantine-reseed carries the memory
330
+ * tier forward: without it, reseeding an existing memory-v3 assistant
331
+ * silently demotes it to v2: the reseeded file records no `live` decision,
332
+ * migration 105 is already checkpointed on an existing workspace and never
333
+ * re-runs, and the schema default is `false`. Only `true` is ever salvaged:
334
+ * carrying `false` forward would re-pin a workspace to the schema default,
335
+ * the exact bug the seed's absent-leaf contract exists to prevent.
336
+ *
337
+ * A quarantined file usually fails `JSON.parse` (that is why it was
338
+ * quarantined); every config writer serializes via
339
+ * `JSON.stringify(config, null, 2)` and `memory.v3.live` is the only config
340
+ * key named `"live"`, so a literal `"live": true` match on the raw text is
341
+ * an unambiguous fallback for truncated files.
342
+ */
343
+ function quarantinedConfigSaidMemoryV3Live(configPath: string): boolean {
344
+ try {
345
+ const dir = dirname(configPath);
346
+ const prefix = `${basename(configPath)}.corrupt-`;
347
+ const quarantines = readdirSync(dir)
348
+ .filter((name) => name.startsWith(prefix) && name.endsWith(".json"))
349
+ .sort();
350
+ const newest = quarantines[quarantines.length - 1];
351
+ if (!newest) {
352
+ return false;
353
+ }
354
+ const raw = readFileSync(join(dir, newest), "utf-8");
355
+ try {
356
+ const parsed: unknown = JSON.parse(raw);
357
+ if (!isPlainObject(parsed) || !isPlainObject(parsed.memory)) {
358
+ return false;
359
+ }
360
+ const v3 = parsed.memory.v3;
361
+ return isPlainObject(v3) && v3.live === true;
362
+ } catch {
363
+ return /"live"\s*:\s*true/.test(raw);
364
+ }
365
+ } catch {
366
+ return false;
367
+ }
368
+ }
369
+
322
370
  /** Minimal structural view of the Zod issues the recovery ladder consumes. */
323
371
  type ValidationIssue = { path: PropertyKey[]; message: string };
324
372
 
@@ -1137,6 +1185,27 @@ export function loadConfig(): AssistantConfig {
1137
1185
  // `memory.v3.live=false` override still wins by merging after
1138
1186
  // migrations.
1139
1187
  seed.memory.v3 = {} as (typeof seed.memory)["v3"];
1188
+ // Exception: a quarantine-reseed carries the memory tier forward.
1189
+ // When the missing config.json is explained by a quarantined
1190
+ // predecessor that had `live: true`, this is an EXISTING memory-v3
1191
+ // assistant being reseeded, not a first launch: migration 105 is
1192
+ // already checkpointed here and never re-runs, so leaving the leaf
1193
+ // absent would silently demote the assistant to v2. Persist the
1194
+ // carried value and apply it to this load's effective config so the
1195
+ // current boot already runs v3.
1196
+ if (quarantinedConfigSaidMemoryV3Live(configPath)) {
1197
+ seed.memory.v3 = { live: true } as (typeof seed.memory)["v3"];
1198
+ config.memory.v3.live = true;
1199
+ // Refresh the last-known-good safety net (mirrors the
1200
+ // deployment-context refresh above): its snapshot was taken before
1201
+ // the carry, so a later in-process validation recovery would
1202
+ // otherwise restore live=false and demote the assistant in memory
1203
+ // despite the reseeded file on disk carrying true.
1204
+ lastKnownGoodConfig = structuredClone(config);
1205
+ log.info(
1206
+ "Carried memory.v3.live=true forward from the quarantined config",
1207
+ );
1208
+ }
1140
1209
  // Strip dataDir (runtime-derived) from the persisted config
1141
1210
  const { dataDir: _, ...persistable } = seed;
1142
1211
  writeFileSync(configPath, JSON.stringify(persistable, null, 2) + "\n");
@@ -0,0 +1,110 @@
1
+ import type { z } from "zod";
2
+
3
+ import { PROVIDER_CATALOG } from "../providers/model-catalog.js";
4
+ import { resolveDefaultProfileForProvider } from "./default-profile-catalog.js";
5
+ import { selectWinningProfile } from "./llm-resolver.js";
6
+ import type { AssistantConfig } from "./schema.js";
7
+ import type { LLMSchema } from "./schemas/llm.js";
8
+
9
+ /**
10
+ * Whether a named inference profile is KNOWN to support tool calling.
11
+ *
12
+ * Tri-state on purpose. The model catalog is not exhaustive: BYOK installs
13
+ * point profiles at models the catalog has never heard of, and a
14
+ * `supportsToolUse` flag it does not carry is not evidence of anything. So
15
+ * only `false` is a verdict:
16
+ * - `true` the catalog states the model supports tool use
17
+ * - `false` the catalog states the model does NOT support tool use
18
+ * - `undefined` the model is unknown to the catalog, or the catalog entry
19
+ * is silent on tool use
20
+ *
21
+ * A `mix` profile is answered across ALL of its arms rather than by expanding
22
+ * it, because the arm a mix lands on is a function of the running
23
+ * conversation's `selectionSeed` and no such conversation exists at probe
24
+ * time. A verdict is only returned when it holds for every arm: all-denied is
25
+ * `false`, all-capable is `true`, and a mix of the two is `undefined` (which
26
+ * arm runs is unknowable here, so there is no honest verdict).
27
+ *
28
+ * Callers gate on `=== false` so an unknown model is never punished (fail
29
+ * open).
30
+ */
31
+ export function profileSupportsTools(
32
+ profileKey: string,
33
+ config: AssistantConfig,
34
+ ): boolean | undefined {
35
+ const arms = mixArms(profileKey, config.llm);
36
+ if (arms == null) {
37
+ return concreteProfileSupportsTools(profileKey, config);
38
+ }
39
+ const verdicts = arms.map((arm) => concreteProfileSupportsTools(arm, config));
40
+ if (verdicts.length === 0) {
41
+ return undefined;
42
+ }
43
+ if (verdicts.every((verdict) => verdict === false)) {
44
+ return false;
45
+ }
46
+ if (verdicts.every((verdict) => verdict === true)) {
47
+ return true;
48
+ }
49
+ return undefined;
50
+ }
51
+
52
+ /** The arm profile names of a `mix` profile, or `undefined` if not a mix. */
53
+ function mixArms(
54
+ profileKey: string,
55
+ llm: z.infer<typeof LLMSchema>,
56
+ ): string[] | undefined {
57
+ const entry = resolveDefaultProfileForProvider(
58
+ llm.profiles,
59
+ profileKey,
60
+ llm.defaultProvider ?? null,
61
+ );
62
+ return entry?.mix?.map((arm) => arm.profile);
63
+ }
64
+
65
+ /**
66
+ * Verdict for a profile expected to name one concrete model. A key that is
67
+ * itself a mix yields no verdict: `LLMSchema.superRefine` forbids nesting, so
68
+ * this is only reachable from a hand-written config that never went through
69
+ * the schema, and expanding it would reintroduce the unseeded random pick.
70
+ */
71
+ function concreteProfileSupportsTools(
72
+ profileKey: string,
73
+ config: AssistantConfig,
74
+ ): boolean | undefined {
75
+ if (mixArms(profileKey, config.llm) != null) {
76
+ return undefined;
77
+ }
78
+ // Run the profile through the real resolver so a catalog default and a
79
+ // workspace shadow alike yield the model the child would actually run on.
80
+ // Feeding the key in as the override rung means a profile the resolver
81
+ // cannot use falls through to a different rung, and a non-`override` winner
82
+ // tells us the key never resolved: unknown, so no verdict.
83
+ const winner = selectWinningProfile("subagentSpawn", config.llm, {
84
+ overrideProfile: profileKey,
85
+ });
86
+ if (winner.source !== "override") {
87
+ return undefined;
88
+ }
89
+ const model = winner.entry?.model;
90
+ return model == null ? undefined : modelSupportsTools(model);
91
+ }
92
+
93
+ /**
94
+ * Look a model id up across every catalog provider. A model offered by more
95
+ * than one provider (e.g. the same id direct and through OpenRouter) is
96
+ * matched several times; any entry claiming tool support settles it, keeping
97
+ * the answer on the fail-open side.
98
+ */
99
+ function modelSupportsTools(modelId: string): boolean | undefined {
100
+ const matches = PROVIDER_CATALOG.flatMap((provider) =>
101
+ provider.models.filter((model) => model.id === modelId),
102
+ );
103
+ if (matches.some((model) => model.supportsToolUse === true)) {
104
+ return true;
105
+ }
106
+ if (matches.some((model) => model.supportsToolUse === false)) {
107
+ return false;
108
+ }
109
+ return undefined;
110
+ }