@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
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Resolution of the `role` a spawn asks for into one of the three subagent
3
+ * types.
4
+ *
5
+ * The model writes this field freely: it names a type, an older type name, a
6
+ * job title, or a whole sentence of framing. Every one of those has to land on
7
+ * a defined capability envelope, so resolution is total and never rejects.
8
+ */
9
+
10
+ import { truncate } from "../util/truncate.js";
11
+ import {
12
+ DEFAULT_SUBAGENT_ROLE,
13
+ SUBAGENT_ROLE_REGISTRY,
14
+ type SubagentRole,
15
+ } from "./types.js";
16
+
17
+ export interface ResolvedSubagentRole {
18
+ /** The type the child actually runs as. */
19
+ role: SubagentRole;
20
+ /**
21
+ * The requested text, when it named no type and is carried into the child's
22
+ * framing as a persona instead.
23
+ */
24
+ personaText?: string;
25
+ /** The legacy name the caller used, when it resolved through an alias. */
26
+ alias?: string;
27
+ }
28
+
29
+ /**
30
+ * The role names that predate the three types. They stay accepted because
31
+ * every prompt, skill, and habit that names one is still in circulation, and a
32
+ * spawn is a poor place to learn a taxonomy changed.
33
+ */
34
+ const ROLE_ALIASES: Record<string, SubagentRole> = {
35
+ planner: "researcher",
36
+ investigator: "researcher",
37
+ coder: "builder",
38
+ general: "builder",
39
+ };
40
+
41
+ /**
42
+ * How much of a free-text role survives into the persona line. A role value
43
+ * can arrive as an entire sentence of instructions; the persona line is a
44
+ * framing hint, and the objective is where the task belongs.
45
+ */
46
+ const MAX_PERSONA_LENGTH = 120;
47
+
48
+ /** Single-line, length-bounded form of a free-text role. */
49
+ function toPersonaText(raw: string): string {
50
+ return truncate(raw.replace(/\s+/g, " ").trim(), MAX_PERSONA_LENGTH);
51
+ }
52
+
53
+ /**
54
+ * Resolve a requested role into the type the child runs as.
55
+ *
56
+ * - Absent (or blank) resolves to `builder`, which imposes no tool allowlist.
57
+ * A spawn that names no role has always run on the parent's full surface,
58
+ * and a task delegated without a stated shape is as likely to need a file
59
+ * written as read.
60
+ * - The three type names pass through, case-insensitively.
61
+ * - A legacy name resolves to its successor type and is reported in `alias`.
62
+ * - Anything else resolves to `researcher` with the text kept as
63
+ * `personaText`. This is least privilege for a value nobody defined: a typo
64
+ * or an invented role must not silently hand out write access. When the task
65
+ * really did need to write, the child reports that it cannot and the parent
66
+ * re-spawns as `builder`, which is a visible and cheap correction.
67
+ */
68
+ export function resolveSubagentRole(
69
+ raw: string | undefined,
70
+ ): ResolvedSubagentRole {
71
+ const trimmed = raw?.trim() ?? "";
72
+ if (trimmed.length === 0) {
73
+ return { role: DEFAULT_SUBAGENT_ROLE };
74
+ }
75
+
76
+ // `Object.hasOwn` rather than `in`: a role of "constructor" or "toString"
77
+ // reaches inherited members and would resolve to something that is not a
78
+ // role at all.
79
+ const key = trimmed.toLowerCase();
80
+ if (Object.hasOwn(SUBAGENT_ROLE_REGISTRY, key)) {
81
+ return { role: key as SubagentRole };
82
+ }
83
+ if (Object.hasOwn(ROLE_ALIASES, key)) {
84
+ return { role: ROLE_ALIASES[key], alias: key };
85
+ }
86
+
87
+ return { role: "researcher", personaText: toPersonaText(trimmed) };
88
+ }
@@ -92,15 +92,47 @@ export interface SubagentConfig {
92
92
  preactivatedSkillIds?: string[];
93
93
  /** Whether the parent should present the result to the user. Defaults to true. */
94
94
  sendResultToUser?: boolean;
95
- /** Optional role for the subagent. Defaults handled by consumers. */
95
+ /** The subagent's type. Omitted spawns run as {@link DEFAULT_SUBAGENT_ROLE}. */
96
96
  role?: SubagentRole;
97
97
  /**
98
- * When true, side-effecting tools (send/write/delete/purchase, host commands)
99
- * are refused for this subagent regardless of trust class the executor
100
- * rejects any such dispatch and the tool is kept off the model's tool surface.
101
- * For unattended passes that must never take an unapproved action while the
102
- * user isn't watching; the subagent surfaces the intended action for the
103
- * user to approve instead.
98
+ * Free-text framing rendered as a persona line under the role preamble, for
99
+ * a spawn whose requested role names a character rather than a type ("staff
100
+ * security engineer", "financial journalist"). The role still decides what
101
+ * the child may do; the persona only decides how it reads the task.
102
+ */
103
+ persona?: string;
104
+ /**
105
+ * The shape the child's final message has to take. Omitted spawns run as
106
+ * `report`, which asks for nothing beyond the role preamble's own reporting
107
+ * guidance.
108
+ */
109
+ outputContract?: SubagentOutputContract;
110
+ /**
111
+ * How this subagent was spawned: the call site and its context/lifecycle
112
+ * shape. Stamped onto the child conversation row and emitted as
113
+ * `subagent_spawn_mode` on `llm_usage` telemetry so delegated spend is
114
+ * separable per variety.
115
+ *
116
+ * Set by the spawning call site, which is the only layer that knows: the
117
+ * manager cannot tell an advisor consult from a plain fork, nor a live-voice
118
+ * continuation from a tool-initiated one. Omitting it falls back to the
119
+ * mechanical `fork ? "fork" : "regular"`, so a future call site that forgets
120
+ * still lands on an honest value rather than NULL.
121
+ */
122
+ spawnMode?: SubagentSpawnMode;
123
+ /**
124
+ * When true, this subagent may run only the runtime's own read-only built-ins:
125
+ * anything else, including a workspace/plugin/skill/MCP tool registered UNDER
126
+ * a built-in's name, is kept off the model's tool surface and refused by the
127
+ * executor regardless of trust class (see `isRefusedInReadOnlyPass`). Because
128
+ * the check is on the tool's registered OWNER and not its name, it is the only
129
+ * durable read-only guarantee available; a role allowlist alone is a set of
130
+ * names any extension can claim.
131
+ *
132
+ * Set by callers whose contract is that nothing changes: an unattended pass
133
+ * that must not take an unapproved action while the user isn't watching (the
134
+ * subagent surfaces the intended action for the user to approve instead), and
135
+ * the advisor consult, whose whole product is judgment rather than action.
104
136
  */
105
137
  denySideEffectTools?: boolean;
106
138
  /**
@@ -143,6 +175,13 @@ export interface SubagentConfig {
143
175
  * leave this unset so existing call-site precedence stays intact.
144
176
  */
145
177
  forceOverrideProfile?: boolean;
178
+ /**
179
+ * The `cron_runs.id` of the schedule firing that spawned this subagent.
180
+ * Passed into the subagent's `runAgentLoop` so its usage rows carry the same
181
+ * stamp as the parent turn's and schedule cost reporting sees the delegated
182
+ * spend. Unset for spawns that no schedule triggered.
183
+ */
184
+ cronRunId?: string | null;
146
185
  /**
147
186
  * Tool-use id of the `skill_execute` call that spawned this subagent.
148
187
  * Forwarded into the `subagent_spawned` event so the client can anchor the
@@ -168,8 +207,90 @@ export interface SubagentState {
168
207
  completedAt?: number;
169
208
  /** Cumulative token usage. */
170
209
  usage: UsageStats;
210
+ /**
211
+ * What the subagent actually ran, harvested from the child conversation when
212
+ * the run ends and re-read while that conversation is still retained (a
213
+ * follow-up turn queued during the run drains after the run returns). In
214
+ * memory only, so a state rebuilt from the durable row never has it: the
215
+ * manager answers that case with {@link SubagentToolStatsReading}.
216
+ */
217
+ stats?: SubagentToolStatsSummary;
218
+ }
219
+
220
+ /**
221
+ * Harvested form of the conversation's live `SubagentToolStats`: the
222
+ * `filesWritten` Set is collapsed to its size, so the state carries counts
223
+ * rather than a reference into a conversation that is about to be released.
224
+ */
225
+ export interface SubagentToolStatsSummary {
226
+ calls: number;
227
+ succeeded: number;
228
+ /**
229
+ * Distinct paths the child passed to `file_write` / `file_edit`. Those two
230
+ * tools are the only writes the executor can attribute to a path, so a
231
+ * builder that writes through the shell, a document tool, or an MCP tool
232
+ * adds nothing here. {@link formatSubagentToolStats} names the two tools for
233
+ * that reason.
234
+ */
235
+ filesWritten: number;
171
236
  }
172
237
 
238
+ /**
239
+ * What the manager can say about a subagent's tool-call counters, which live in
240
+ * memory only.
241
+ *
242
+ * - `counted`: the run was harvested, so the numbers are real.
243
+ * - `unmeasured`: a live run that never reached its harvest. Nothing was
244
+ * measured and nothing was lost, so a reader claims neither.
245
+ * - `unrecoverable`: the state was rebuilt from the durable row (the startup
246
+ * rehydration, or a subagent the manager's window no longer holds). The row
247
+ * carries no counters, so they can never be produced for this subagent, and
248
+ * reporting zero calls would read as "this subagent did nothing".
249
+ *
250
+ * A rehydrated entry sits in the manager exactly like a live one, so manager
251
+ * membership alone cannot separate the last two; the manager tracks which of
252
+ * its entries it rebuilt and answers with this instead.
253
+ */
254
+ export type SubagentToolStatsReading =
255
+ | { kind: "counted"; stats: SubagentToolStatsSummary }
256
+ | { kind: "unmeasured" }
257
+ | { kind: "unrecoverable" };
258
+
259
+ /**
260
+ * The machine half of a subagent's result, appended to the parent's completion
261
+ * notification and to `subagent_read`. The child's prose is its own account of
262
+ * what it did; this line is the measured one, so a report of executed work by a
263
+ * subagent that called nothing is visible instead of taken on trust.
264
+ *
265
+ * The file count names the two tools it comes from. A builder runs on the
266
+ * parent's whole tool surface and can write through the shell, a document tool,
267
+ * or an MCP tool, none of which the counter sees; an unqualified "files
268
+ * written: 0" would read as fabricated work for a subagent that really did
269
+ * write, which is the exact misreading this line exists to prevent.
270
+ */
271
+ export function formatSubagentToolStats(
272
+ stats: SubagentToolStatsSummary,
273
+ ): string {
274
+ if (stats.calls === 0) {
275
+ return "[stats: no tools were used by this subagent; treat any claims of executed work as unverified]";
276
+ }
277
+ const plural = stats.calls === 1 ? "" : "s";
278
+ return `[stats: ${stats.calls} tool call${plural}, ${stats.succeeded} succeeded, files written via file_write/file_edit: ${stats.filesWritten}]`;
279
+ }
280
+
281
+ /** Stats footer for a subagent whose in-memory counters no longer exist. */
282
+ export const SUBAGENT_STATS_UNAVAILABLE =
283
+ "[stats: unavailable (tool counters are not retained for this subagent)]";
284
+
285
+ /**
286
+ * Appended to a read taken while the subagent is still working through
287
+ * guidance queued during its run. Its own run is over by then, which is what
288
+ * makes it terminal, but the queued turn adds output and tool calls after
289
+ * that, so such a read is a progress report rather than the final one.
290
+ */
291
+ export const SUBAGENT_READ_STILL_PROCESSING =
292
+ "[note: this subagent is still processing queued follow-up guidance. The output above and the counts below stop at its last finished turn; read again for the rest.]";
293
+
173
294
  // ── Bounded listing ─────────────────────────────────────────────────────
174
295
 
175
296
  /** Recency key matching the durable query's `COALESCE(completed_at, created_at)`. */
@@ -209,13 +330,119 @@ export const SUBAGENT_LIMITS = {
209
330
 
210
331
  // ── Roles ───────────────────────────────────────────────────────────────
211
332
 
212
- export type SubagentRole =
213
- | "general"
214
- | "researcher"
215
- | "coder"
216
- | "planner"
217
- | "investigator"
218
- | "advisor";
333
+ /**
334
+ * What a subagent is, derived from two questions: can it change things, and
335
+ * does the parent wait for it.
336
+ *
337
+ * - `researcher`: read-only, runs in the background, scoped to a fixed
338
+ * allowlist.
339
+ * - `builder`: write-capable, runs in the background, on the parent's whole
340
+ * tool surface.
341
+ * - `advisor`: read-only, the parent turn blocks on its guidance.
342
+ *
343
+ * Write-plus-blocking is deliberately absent: a parent that waits on a change
344
+ * has no reason to delegate it. Model tier is a separate knob
345
+ * (`inference_profile`), and a persona is free text
346
+ * (`SubagentConfig.persona`), so neither is a type.
347
+ *
348
+ * The legacy names (`general`, `coder`, `planner`, `investigator`) remain
349
+ * accepted at the tool boundary as aliases; see ./role-resolution.ts.
350
+ */
351
+ export type SubagentRole = "researcher" | "builder" | "advisor";
352
+
353
+ // ── Output contracts ─────────────────────────────────────────────────────
354
+
355
+ /**
356
+ * Every contract the tool boundary accepts, in advertised order. `report` is
357
+ * first because it is what a spawn that names none runs under.
358
+ */
359
+ export const SUBAGENT_OUTPUT_CONTRACTS = [
360
+ "report",
361
+ "verdict",
362
+ "artifact",
363
+ ] as const;
364
+
365
+ /**
366
+ * The shape a subagent's final message has to take. Orthogonal to
367
+ * {@link SubagentRole}, which decides what the child may do: the contract
368
+ * decides what it owes back, so the same read-only researcher can return an
369
+ * investigation report or an evidence-backed pass/fail list.
370
+ *
371
+ * - `report`: prose answering the objective, which is what every role preamble
372
+ * already asks for.
373
+ * - `verdict`: per-criterion PASS/FAIL with the evidence behind each call.
374
+ * Researcher-only, since a verdict is a claim about what is already there.
375
+ * Checking work this way is mechanical rather than exploratory, so the spawn
376
+ * also defaults to the cheap model tier.
377
+ * - `artifact`: the deliverable is the thing produced, not the write-up.
378
+ * Builder-only, since nothing else can produce one.
379
+ *
380
+ * "Verifier" is deliberately not a fourth role: verification is a researcher
381
+ * under the `verdict` contract, and a role would have implied a separate tool
382
+ * envelope it does not need.
383
+ */
384
+ export type SubagentOutputContract = (typeof SUBAGENT_OUTPUT_CONTRACTS)[number];
385
+
386
+ /**
387
+ * The instruction a contract adds to the child's framing, or `undefined` for
388
+ * `report`, whose expectations the role preamble already states.
389
+ *
390
+ * Rendered into the built system prompt for a regular spawn and into the fork
391
+ * task framing for a fork, which inherits the parent's prompt verbatim and so
392
+ * never sees a built one.
393
+ */
394
+ export function subagentOutputContractText(
395
+ contract: SubagentOutputContract | undefined,
396
+ ): string | undefined {
397
+ switch (contract) {
398
+ case "verdict":
399
+ return (
400
+ "For each criterion in the objective return PASS or FAIL plus the exact evidence (file path, line, value, or quote). " +
401
+ "If evidence is unavailable say CANNOT VERIFY and state what is missing. No prose beyond the verdict list."
402
+ );
403
+ case "artifact":
404
+ return "Your deliverable is the artifact itself. End by listing the exact files you created or modified.";
405
+ default:
406
+ return undefined;
407
+ }
408
+ }
409
+
410
+ // ── Spawn modes ──────────────────────────────────────────────────────────
411
+
412
+ /**
413
+ * How a subagent was spawned. Orthogonal to {@link SubagentRole}: the role
414
+ * selects the tool allowlist and system-prompt preamble (what the child may
415
+ * do), the spawn mode selects context inheritance and lifecycle (how many
416
+ * input tokens the child starts with, and whether the parent blocks on it).
417
+ * A fork's inherited parent transcript is the dominant input-token driver and
418
+ * is independent of which role ran, which is why neither field subsumes the
419
+ * other.
420
+ *
421
+ * - `regular`: fire-and-forget `subagent_spawn`, fresh objective-only context.
422
+ * - `fork`: `subagent_spawn` with `fork: true`, inherits the parent transcript.
423
+ * - `advisor_consult`: synchronous, read-only advisor consult on the advisor
424
+ * profile; the parent turn blocks on it and returns its guidance inline.
425
+ * - `voice_continuation`: live-voice background continuation of an interrupted
426
+ * turn, spawned as a fork with no role and therefore WRITE-CAPABLE: it runs
427
+ * as {@link DEFAULT_SUBAGENT_ROLE} on the parent's full tool surface, with
428
+ * side effects governed by the standard non-interactive permission path
429
+ * under the trust the foreground voice turn ran under. It is silent only in
430
+ * the sense that the run makes no sound of its own, a later session turn
431
+ * speaking its answer; that is a statement about sound, not blast radius.
432
+ *
433
+ * This block is the single description of the modes. The persistence and
434
+ * telemetry layers that carry the same values point here rather than restate
435
+ * them, so a correction lands once.
436
+ *
437
+ * Mirrored on the wire as `llm_usage.subagent_spawn_mode`, which is an OPEN
438
+ * string set on the platform side: adding a value here needs no coordinated
439
+ * platform release.
440
+ */
441
+ export type SubagentSpawnMode =
442
+ | "regular"
443
+ | "fork"
444
+ | "advisor_consult"
445
+ | "voice_continuation";
219
446
 
220
447
  export interface SubagentRoleConfig {
221
448
  /**
@@ -223,6 +450,17 @@ export interface SubagentRoleConfig {
223
450
  * `undefined` means no filter (all tools available).
224
451
  */
225
452
  allowedTools?: string[];
453
+ /**
454
+ * When true, the role refuses side-effecting tools regardless of trust
455
+ * class: a name on {@link allowedTools} still has to resolve to a
456
+ * first-party built-in, so a workspace or plugin tool that claims an
457
+ * allowed name is kept off the surface and refused at dispatch.
458
+ *
459
+ * This lives on the role rather than at a spawn site so every path that
460
+ * projects the role, the live spawn and the `tools list --agent` preview
461
+ * alike, applies the same gate.
462
+ */
463
+ denySideEffects?: boolean;
226
464
  /** Skill IDs to pre-activate on the subagent conversation. */
227
465
  skillIds: string[];
228
466
  /** Role-specific text prepended to the subagent system prompt. */
@@ -231,76 +469,73 @@ export interface SubagentRoleConfig {
231
469
 
232
470
  export const SUBAGENT_ROLE_REGISTRY: Record<SubagentRole, SubagentRoleConfig> =
233
471
  {
234
- general: {
235
- allowedTools: undefined,
236
- skillIds: [],
237
- systemPromptPreamble:
238
- "You are a general-purpose subagent. Complete the delegated task thoroughly and concisely.",
239
- },
240
472
  researcher: {
241
473
  allowedTools: [
242
474
  "web_search",
243
475
  "web_fetch",
244
476
  "file_read",
245
477
  "file_list",
246
- "recall",
247
- "notify_parent",
248
- ],
249
- skillIds: [],
250
- systemPromptPreamble:
251
- "You are a research-focused subagent with read-only access. Search the web, read files, and recall memories. You cannot write files or run shell commands.",
252
- },
253
- coder: {
254
- allowedTools: [
255
- "bash",
256
- "file_read",
257
- "file_write",
258
- "file_edit",
259
- "web_search",
260
- "recall",
261
- "notify_parent",
262
- ],
263
- skillIds: [],
264
- systemPromptPreamble:
265
- "You are a code-focused subagent with file and shell access. Read, write, and edit files, and run shell commands.",
266
- },
267
- planner: {
268
- allowedTools: [
269
- "file_read",
270
- "file_list",
271
- "web_search",
272
- "web_fetch",
273
- "recall",
274
- "notify_parent",
275
- ],
276
- skillIds: [],
277
- systemPromptPreamble:
278
- "You are an analysis-focused subagent with read-only access. Read files, search the web, and synthesize findings. You cannot write files or run shell commands.",
279
- },
280
- investigator: {
281
- allowedTools: [
282
478
  "code_search",
283
- "file_read",
284
- "file_list",
285
- "web_search",
286
- "web_fetch",
287
479
  "recall",
480
+ "skill_execute",
288
481
  "notify_parent",
289
482
  ],
290
483
  skillIds: [],
291
484
  systemPromptPreamble: [
292
- "You are an investigation-focused subagent for root-cause analysis: debugging, log forensics, and tracing behavior across code.",
293
- "You have read-only investigation tools only — there is no shell. Use code_search to search file contents across directories, file_list to enumerate paths, and file_read to read whole files and logs. You cannot modify files or system state.",
294
- "Working method: read whole files instead of many small line-range slices; prefer broad code_search queries across a directory over one-symbol-at-a-time queries.",
485
+ "You are a research subagent with read-only access: search the web, read and search files, and recall memories. There is no shell, and you cannot write or edit files.",
486
+ // `file_read` takes no default line limit and its results are exempt
487
+ // from result-time spooling, so a full body rides every LLM call until
488
+ // the turn ends. Telling the role to read whole files makes that the
489
+ // default shape of a research turn, and this role now covers the
490
+ // traffic three roles used to. The anti-slicing intent stays: one pass
491
+ // over the range that is needed rather than many small ones.
492
+ "Working method: use code_search to search file contents across directories, file_list to enumerate paths, and file_read to read files and logs. Prefer broad code_search queries across a directory over one-symbol-at-a-time queries, and read the range you need in one pass rather than many small slices.",
295
493
  "Send notify_parent (urgency 'important') as soon as each finding is confirmed, so progress survives interruption.",
296
- "Your final message must be a compact root-cause report with these sections: Symptom, Root cause, Evidence (file:line references), Suggested fix, Open questions.",
297
- "If you approach context limits, stop investigating and produce the report from what you have a partial report delivered is worth more than a complete investigation lost.",
494
+ "Your final message is the deliverable: a compact report that answers the objective, gives the evidence behind each claim (file:line references, URLs, or quotes), and names what you could not determine. For a root-cause investigation, use the sections Symptom, Root cause, Evidence, Suggested fix, Open questions.",
495
+ "If you approach context limits, stop investigating and write the report from what you have. A partial report delivered is worth more than a complete investigation lost.",
496
+ ].join(" "),
497
+ },
498
+ builder: {
499
+ // No allowlist: a builder projects the same tool surface its parent
500
+ // conversation does, connectors, MCP tools, browser and computer use
501
+ // included. A fixed list would be a ceiling on what "can change things"
502
+ // means, and the tools a build task needs are not enumerable in advance:
503
+ // the work that has to file the ticket, send the message, or drive the
504
+ // browser is exactly the work a parent delegates.
505
+ skillIds: [],
506
+ systemPromptPreamble: [
507
+ "You are a build subagent with the parent's full tool surface: read, write, and edit files, run shell commands, search code, search the web, and use any other tool the parent conversation can reach.",
508
+ "Carry the task through end to end, then verify it yourself with the command that proves it (a build, a test run, a re-read of what you wrote) before reporting.",
509
+ "Send notify_parent (urgency 'important') when a milestone lands or a decision only the parent can make blocks you.",
510
+ "Your final message must state what you changed, the exact files you touched, and the result of the verification you ran.",
298
511
  ].join(" "),
299
512
  },
300
513
  advisor: {
301
- allowedTools: [],
514
+ // Read-only fact checking, deliberately narrower than the researcher's
515
+ // list: no web fetch, no skill execution, no memory search, nothing that
516
+ // persists. The advisor answers from the inherited conversation and opens
517
+ // a file only when a specific fact would change the advice.
518
+ //
519
+ // Names alone are not the guarantee. The advisor spawn also sets
520
+ // `denySideEffectTools`, so each name must additionally resolve to the
521
+ // first-party built-in implementation (`READ_ONLY_ALLOWED_TOOLS` plus the
522
+ // `ownerKind === "default"` check in `isRefusedInReadOnlyPass`) before the
523
+ // tool reaches the wire or the executor. Every entry here must stay inside
524
+ // that read-only set, or it is admitted by the role and then refused at
525
+ // dispatch. `subagent-role-registry.test.ts` asserts the subset.
526
+ allowedTools: ["file_read", "file_list", "code_search"],
527
+ denySideEffects: true,
302
528
  skillIds: [],
303
529
  systemPromptPreamble:
304
- "You are a read-only senior advisor consulted for a one-shot strategic review. Read the inherited conversation, then return focused, high-leverage guidance in a single response. You have no tools you cannot search, read files, or run commands so reason from the context you were given.",
530
+ "You are a read-only senior advisor consulted for a one-shot strategic review. Read the inherited conversation, then return focused, high-leverage guidance in a single response. You may read and search the files in the workspace to verify a decisive fact, but you cannot change anything and you cannot see other conversations.",
305
531
  },
306
532
  };
533
+
534
+ /**
535
+ * The type a spawn that names no role runs as. `builder` imposes no tool
536
+ * allowlist, so a caller that omits the field keeps the full surface its
537
+ * conversation projects: the internal context-inheriting callers (the
538
+ * live-voice continuation) continue the parent's turn with the tools that turn
539
+ * had, and a delegated task whose shape was never stated can still write.
540
+ */
541
+ export const DEFAULT_SUBAGENT_ROLE: SubagentRole = "builder";
@@ -25,10 +25,9 @@ export interface UnknownAllowlistTool {
25
25
 
26
26
  /**
27
27
  * Pure core: given the set of registered tool names, return every allowlist
28
- * entry that has no matching registered tool. Roles with no allowlist
29
- * (`allowedTools: undefined`, e.g. `general`) impose no filter and contribute
30
- * nothing. Exported for direct unit testing without standing up the real tool
31
- * registry.
28
+ * entry that has no matching registered tool. A role with no allowlist
29
+ * (`allowedTools: undefined`) imposes no filter and contributes nothing.
30
+ * Exported for direct unit testing without standing up the real tool registry.
32
31
  */
33
32
  export function findUnknownAllowlistTools(
34
33
  registeredToolNames: ReadonlySet<string>,
@@ -45,12 +45,22 @@ mock.module("../bot-username.js", () => ({
45
45
 
46
46
  let apiBaseUrl = "https://api.telegram.org";
47
47
 
48
+ // What the gateway reconciler recorded on its last successful setWebhook.
49
+ // Defaults to the URL the fixtures report from getWebhookInfo, so the common
50
+ // case is a verified match; tests override it to exercise a stale registration
51
+ // and a deployment that never recorded one.
52
+ let registeredWebhookUrl: string | undefined =
53
+ "https://example.test/webhooks/telegram";
54
+
48
55
  // Spread the real modules below: these are broad barrels shared with peer test
49
56
  // files, and replacing one wholesale drops the exports those files import.
50
57
  const actualLoader = await import("../../config/loader.js");
51
58
  mock.module("../../config/loader.js", () => ({
52
59
  ...actualLoader,
53
- getConfig: () => ({ telegram: { apiBaseUrl }, ingress: ingressConfig }),
60
+ getConfig: () => ({
61
+ telegram: { apiBaseUrl, registeredWebhookUrl },
62
+ ingress: ingressConfig,
63
+ }),
54
64
  loadRawConfig: () => ({ ingress: ingressConfig }),
55
65
  }));
56
66
 
@@ -152,6 +162,7 @@ beforeEach(() => {
152
162
  fetchCallCount = 0;
153
163
  fetchedUrls.length = 0;
154
164
  apiBaseUrl = "https://api.telegram.org";
165
+ registeredWebhookUrl = "https://example.test/webhooks/telegram";
155
166
  emittedSignals.length = 0;
156
167
  emitFails = false;
157
168
  _resetTelegramWebhookHealthState();
@@ -317,6 +328,67 @@ describe("detection", () => {
317
328
  expect(result.status).toBe("unknown");
318
329
  });
319
330
 
331
+ test("reports url_mismatch when Telegram holds an address we did not register", async () => {
332
+ // The quiet-channel case the module header names as a known limitation:
333
+ // Telegram only produces delivery errors when it has something to deliver,
334
+ // so a stale tunnel address never errors and, before this comparison, read
335
+ // as healthy. No error is set here on purpose.
336
+ registeredWebhookUrl = "https://current.test/webhooks/telegram";
337
+ setWebhookInfo({
338
+ url: "https://stale.test/webhooks/telegram",
339
+ pending_update_count: 0,
340
+ });
341
+
342
+ const result = await checkTelegramWebhookHealth();
343
+
344
+ expect(result.status).toBe("url_mismatch");
345
+ expect(result.registeredUrl).toBe("https://stale.test/webhooks/telegram");
346
+ expect(result.expectedUrl).toBe("https://current.test/webhooks/telegram");
347
+ });
348
+
349
+ test("reports url_mismatch ahead of a recent delivery error", async () => {
350
+ // Both conditions hold. The mismatch is the cause and the error is its
351
+ // symptom, and they have different fixes, so the mismatch must win.
352
+ registeredWebhookUrl = "https://current.test/webhooks/telegram";
353
+ setWebhookInfo({
354
+ url: "https://stale.test/webhooks/telegram",
355
+ pending_update_count: 3,
356
+ last_error_date: Math.floor(Date.now() / 1000),
357
+ last_error_message: "Connection refused",
358
+ });
359
+
360
+ expect((await checkTelegramWebhookHealth()).status).toBe("url_mismatch");
361
+ });
362
+
363
+ test("reports unverified when nothing recorded which URL we registered", async () => {
364
+ // Reconciliation has not completed, or predates URL recording. Registered
365
+ // and quiet, but unproven: this must not report healthy.
366
+ registeredWebhookUrl = undefined;
367
+ setWebhookInfo({
368
+ url: "https://example.test/webhooks/telegram",
369
+ pending_update_count: 0,
370
+ });
371
+
372
+ const result = await checkTelegramWebhookHealth();
373
+
374
+ expect(result.status).toBe("unverified");
375
+ expect(result.expectedUrl).toBeUndefined();
376
+ });
377
+
378
+ test("an unverified webhook does not alert the guardian", async () => {
379
+ // Unproven is not broken. Alerting here would page every install whose
380
+ // last reconciliation predates recording.
381
+ registeredWebhookUrl = undefined;
382
+ setWebhookInfo({
383
+ url: "https://example.test/webhooks/telegram",
384
+ pending_update_count: 0,
385
+ });
386
+
387
+ await runTelegramWebhookHealthCheck();
388
+
389
+ expect(emittedSignals).toHaveLength(0);
390
+ });
391
+
320
392
  test("honors a configured telegram.apiBaseUrl", async () => {
321
393
  apiBaseUrl = "https://telegram-proxy.internal/";
322
394