@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
@@ -52,9 +52,16 @@ interface FakeManagedSubagent {
52
52
  estimatedCost: number;
53
53
  };
54
54
  subagentDeniedToolNames: Set<string>;
55
+ subagentToolStats: {
56
+ calls: number;
57
+ succeeded: number;
58
+ filesWritten: Set<string>;
59
+ };
55
60
  } | null;
56
61
  state: SubagentState;
57
62
  parentSendToClient: (msg: AssistantEvent) => void;
63
+ /** Sticky marker that a follow-up turn was queued during the run. */
64
+ hadEnqueuedMessages?: boolean;
58
65
  }
59
66
 
60
67
  /** Type-safe accessor for SubagentManager's private internals via bracket notation. */
@@ -62,6 +69,7 @@ interface ManagerInternals {
62
69
  subagents: Map<string, FakeManagedSubagent>;
63
70
  parentToChildren: Map<string, Set<string>>;
64
71
  runSubagent: (subagentId: string, objective: string) => Promise<void>;
72
+ releaseConversation: (managed: FakeManagedSubagent) => void;
65
73
  stopSweep: () => void;
66
74
  }
67
75
 
@@ -86,6 +94,11 @@ function injectFakeSubagent(
86
94
  sendToClient: () => {},
87
95
  usageStats: { inputTokens: 100, outputTokens: 50, estimatedCost: 0.005 },
88
96
  subagentDeniedToolNames: new Set<string>(),
97
+ subagentToolStats: {
98
+ calls: 0,
99
+ succeeded: 0,
100
+ filesWritten: new Set<string>(),
101
+ },
89
102
  };
90
103
 
91
104
  const internals = asInternals(manager);
@@ -359,6 +372,141 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
359
372
  asInternals(manager).stopSweep();
360
373
  });
361
374
 
375
+ test("a settled run's notification carries what it actually ran", async () => {
376
+ clearCaptured();
377
+ const manager = new SubagentManager();
378
+ const subagentId = "sub-stats";
379
+ const state = makeState(subagentId);
380
+ injectFakeSubagent(manager, subagentId, state);
381
+
382
+ const managed = asInternals(manager).subagents.get(subagentId)!;
383
+ managed.conversation!.persistUserMessage = () => ({
384
+ id: "msg-1",
385
+ deduplicated: false,
386
+ });
387
+ managed.conversation!.runAgentLoop = async () => {
388
+ const counters = managed.conversation!.subagentToolStats;
389
+ counters.calls += 2;
390
+ counters.succeeded += 2;
391
+ counters.filesWritten.add("/report.md");
392
+ };
393
+
394
+ await asInternals(manager).runSubagent(subagentId, "Do something");
395
+
396
+ expect(capturedNotifications[0].message).toContain(
397
+ "[stats: 2 tool calls, 2 succeeded, files written via file_write/file_edit: 1]",
398
+ );
399
+
400
+ asInternals(manager).stopSweep();
401
+ });
402
+
403
+ test("a queued follow-up turn's tool calls reach the reported stats", async () => {
404
+ clearCaptured();
405
+ const manager = new SubagentManager();
406
+ const subagentId = "sub-queued-stats";
407
+ const state = makeState(subagentId);
408
+ injectFakeSubagent(manager, subagentId, state);
409
+
410
+ const managed = asInternals(manager).subagents.get(subagentId)!;
411
+ managed.conversation!.persistUserMessage = () => ({
412
+ id: "msg-1",
413
+ deduplicated: false,
414
+ });
415
+ managed.conversation!.runAgentLoop = async () => {
416
+ const counters = managed.conversation!.subagentToolStats;
417
+ counters.calls += 2;
418
+ counters.succeeded += 2;
419
+ };
420
+ // Guidance arrived while the run was processing: the conversation is
421
+ // retained past the run so it can drain that turn afterwards.
422
+ managed.hadEnqueuedMessages = true;
423
+
424
+ await asInternals(manager).runSubagent(subagentId, "Do something");
425
+
426
+ // The run's own harvest only ever sees its own calls.
427
+ expect(state.stats).toEqual({ calls: 2, succeeded: 2, filesWritten: 0 });
428
+ // Quoting it in a message that is never rewritten would under-report the
429
+ // queued turn permanently, so the deferred notification quotes nothing and
430
+ // sends the parent to subagent_read instead.
431
+ expect(capturedNotifications[0].message).toContain(
432
+ "Queued follow-up guidance is still being processed",
433
+ );
434
+ expect(capturedNotifications[0].message).not.toContain("[stats:");
435
+
436
+ // The queued turn now drains, into the same retained conversation.
437
+ const counters = managed.conversation!.subagentToolStats;
438
+ counters.calls += 3;
439
+ counters.succeeded += 2;
440
+ counters.filesWritten.add("/queued-turn.md");
441
+
442
+ expect(manager.currentToolStats(subagentId)).toEqual({
443
+ kind: "counted",
444
+ stats: { calls: 5, succeeded: 4, filesWritten: 1 },
445
+ });
446
+
447
+ asInternals(manager).stopSweep();
448
+ });
449
+
450
+ test("releasing the conversation freezes the settled counters", async () => {
451
+ clearCaptured();
452
+ const manager = new SubagentManager();
453
+ const subagentId = "sub-release-stats";
454
+ const state = makeState(subagentId);
455
+ injectFakeSubagent(manager, subagentId, state);
456
+
457
+ const managed = asInternals(manager).subagents.get(subagentId)!;
458
+ managed.conversation!.persistUserMessage = () => ({
459
+ id: "msg-1",
460
+ deduplicated: false,
461
+ });
462
+ managed.conversation!.runAgentLoop = async () => {
463
+ managed.conversation!.subagentToolStats.calls += 1;
464
+ managed.conversation!.subagentToolStats.succeeded += 1;
465
+ };
466
+ managed.hadEnqueuedMessages = true;
467
+
468
+ await asInternals(manager).runSubagent(subagentId, "Do something");
469
+
470
+ const counters = managed.conversation!.subagentToolStats;
471
+ counters.calls += 4;
472
+ counters.succeeded += 3;
473
+
474
+ // The sweep releases the retained conversation once the drain has had its
475
+ // window; the last reading is taken on the way out and stands afterwards.
476
+ asInternals(manager).releaseConversation(managed);
477
+
478
+ expect(managed.conversation).toBeNull();
479
+ expect(manager.currentToolStats(subagentId)).toEqual({
480
+ kind: "counted",
481
+ stats: { calls: 5, succeeded: 4, filesWritten: 0 },
482
+ });
483
+
484
+ asInternals(manager).stopSweep();
485
+ });
486
+
487
+ test("a run that never harvested reports nothing rather than a zero", () => {
488
+ const manager = new SubagentManager();
489
+ const subagentId = "sub-unharvested";
490
+ injectFakeSubagent(manager, subagentId, makeState(subagentId));
491
+
492
+ // Still running: its counters are mid-flight, and a zero read off them now
493
+ // would read as "this subagent used no tools".
494
+ expect(manager.currentToolStats(subagentId)).toEqual({
495
+ kind: "unmeasured",
496
+ });
497
+ });
498
+
499
+ test("an id the manager never held reports its counters unrecoverable", () => {
500
+ const manager = new SubagentManager();
501
+
502
+ // The counters exist nowhere but memory, so a caller holding a
503
+ // record-derived state for this id can never get them, and reporting zero
504
+ // calls would read as "this subagent did nothing".
505
+ expect(manager.currentToolStats("sub-not-in-manager")).toEqual({
506
+ kind: "unrecoverable",
507
+ });
508
+ });
509
+
362
510
  test("failed subagent does not notify if already aborted", async () => {
363
511
  clearCaptured();
364
512
  const manager = new SubagentManager();
@@ -113,7 +113,7 @@ function seedSubagent(
113
113
  conversationId,
114
114
  label: "Test",
115
115
  objective: "test",
116
- role: "general",
116
+ role: "builder",
117
117
  isFork: false,
118
118
  sendResultToUser: null,
119
119
  parentToolUseId: null,
@@ -5,10 +5,12 @@ import { migrateCreateSubagentsTable } from "../persistence/migrations/311-creat
5
5
  import { migrateAddSubagentParentToolUseId } from "../persistence/migrations/356-add-subagent-parent-tool-use-id.js";
6
6
  import { resetTestTables } from "../persistence/raw-query.js";
7
7
  import {
8
+ countRecentSimilarSpawns,
8
9
  deleteSubagentRecordsByParent,
9
10
  getSubagentRecordById,
10
11
  getSubagentRecordByLabel,
11
12
  loadAllSubagentRecords,
13
+ normalizeSpawnObjective,
12
14
  type SubagentRecord,
13
15
  upsertSubagentRecord,
14
16
  } from "../persistence/subagent-store.js";
@@ -134,6 +136,145 @@ describe("subagent-store", () => {
134
136
  });
135
137
  });
136
138
 
139
+ describe("recent similar spawn tallies", () => {
140
+ const OBJECTIVE = "Audit the billing pipeline for drift";
141
+
142
+ /** A completed row for `OBJECTIVE`, spelled however the caller wants it. */
143
+ function spawn(
144
+ id: string,
145
+ over: Partial<SubagentRecord> = {},
146
+ ): SubagentRecord {
147
+ return record({
148
+ id,
149
+ conversationId: `conv-${id}`,
150
+ objective: OBJECTIVE,
151
+ status: "completed",
152
+ createdAt: Date.now(),
153
+ completedAt: Date.now(),
154
+ estimatedCost: 0.5,
155
+ ...over,
156
+ });
157
+ }
158
+
159
+ function tally(parentConversationId = "parent-1") {
160
+ return countRecentSimilarSpawns({
161
+ parentConversationId,
162
+ normalizedObjective: normalizeSpawnObjective(OBJECTIVE),
163
+ sinceMs: Date.now() - 24 * 60 * 60 * 1000,
164
+ });
165
+ }
166
+
167
+ test("normalization folds case and whitespace over the whole objective", () => {
168
+ expect(normalizeSpawnObjective(" Audit the\nBILLING pipeline ")).toBe(
169
+ "audit the billing pipeline",
170
+ );
171
+ expect(normalizeSpawnObjective("x".repeat(200))).toHaveLength(200);
172
+ });
173
+
174
+ test("counts both scopes and sums their cost", () => {
175
+ upsertSubagentRecord(spawn("a"));
176
+ upsertSubagentRecord(
177
+ spawn("b", { objective: " AUDIT the Billing\tpipeline for drift " }),
178
+ );
179
+ upsertSubagentRecord(spawn("c", { parentConversationId: "parent-2" }));
180
+
181
+ expect(tally()).toEqual({
182
+ conversation: { count: 2, estimatedCost: 1, inFlight: 0 },
183
+ assistant: { count: 3, estimatedCost: 1.5, inFlight: 0 },
184
+ });
185
+ });
186
+
187
+ test("a genuinely different objective is not a match", () => {
188
+ upsertSubagentRecord(spawn("a"));
189
+ upsertSubagentRecord(
190
+ spawn("b", { objective: "Audit the payouts pipeline for drift" }),
191
+ );
192
+
193
+ expect(tally().conversation.count).toBe(1);
194
+ });
195
+
196
+ test("a shared boilerplate prefix does not fold distinct objectives together", () => {
197
+ const preamble =
198
+ "Follow the audit checklist to the letter and report ".repeat(3);
199
+ upsertSubagentRecord(
200
+ spawn("a", { objective: `${preamble} on the billing service` }),
201
+ );
202
+ upsertSubagentRecord(
203
+ spawn("b", { objective: `${preamble} on the payouts service` }),
204
+ );
205
+
206
+ const forBilling = countRecentSimilarSpawns({
207
+ parentConversationId: "parent-1",
208
+ normalizedObjective: normalizeSpawnObjective(
209
+ `${preamble} on the billing service`,
210
+ ),
211
+ sinceMs: Date.now() - 24 * 60 * 60 * 1000,
212
+ });
213
+
214
+ expect(forBilling.conversation.count).toBe(1);
215
+ });
216
+
217
+ test("the same objective still matches however long its preamble", () => {
218
+ const objective = `${"e".repeat(200)} tail`;
219
+ upsertSubagentRecord(spawn("a", { objective }));
220
+ upsertSubagentRecord(spawn("b", { objective: ` ${objective} ` }));
221
+
222
+ expect(
223
+ countRecentSimilarSpawns({
224
+ parentConversationId: "parent-1",
225
+ normalizedObjective: normalizeSpawnObjective(objective),
226
+ sinceMs: Date.now() - 24 * 60 * 60 * 1000,
227
+ }).conversation.count,
228
+ ).toBe(2);
229
+ });
230
+
231
+ test("spawns older than the cutoff and advisor consults are left out", () => {
232
+ upsertSubagentRecord(spawn("fresh"));
233
+ upsertSubagentRecord(
234
+ spawn("stale", { createdAt: Date.now() - 90_000_000 }),
235
+ );
236
+ upsertSubagentRecord(spawn("consult", { role: "advisor" }));
237
+
238
+ expect(tally().assistant.count).toBe(1);
239
+ });
240
+
241
+ test("runs that ended without an answer are left out of both scopes", () => {
242
+ upsertSubagentRecord(spawn("done"));
243
+ for (const status of ["failed", "aborted", "interrupted"]) {
244
+ upsertSubagentRecord(
245
+ spawn(`ended-${status}`, { status, completedAt: Date.now() }),
246
+ );
247
+ }
248
+
249
+ expect(tally()).toEqual({
250
+ conversation: { count: 1, estimatedCost: 0.5, inFlight: 0 },
251
+ assistant: { count: 1, estimatedCost: 0.5, inFlight: 0 },
252
+ });
253
+ });
254
+
255
+ test("runs still under way are tallied apart from completed ones", () => {
256
+ // The burst shape the loop guard exists for: copies fired faster than any
257
+ // of them can finish are invisible to a completed-only count.
258
+ upsertSubagentRecord(spawn("done"));
259
+ upsertSubagentRecord(spawn("running", { status: "running" }));
260
+ upsertSubagentRecord(spawn("queued", { status: "pending" }));
261
+ upsertSubagentRecord(spawn("asking", { status: "awaiting_input" }));
262
+ upsertSubagentRecord(
263
+ spawn("running-elsewhere", {
264
+ status: "running",
265
+ parentConversationId: "parent-2",
266
+ }),
267
+ );
268
+
269
+ expect(tally()).toEqual({
270
+ // An unfinished run has no cost recorded yet, so it adds nothing to the
271
+ // completed tally's spend.
272
+ conversation: { count: 1, estimatedCost: 0.5, inFlight: 3 },
273
+ assistant: { count: 1, estimatedCost: 0.5, inFlight: 4 },
274
+ });
275
+ });
276
+ });
277
+
137
278
  describe("subagentStateFromRecord", () => {
138
279
  test("maps the recorded status verbatim, leaving the settle to the caller", () => {
139
280
  upsertSubagentRecord(record({ id: "active", status: "running" }));
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
+ import { READ_ONLY_ALLOWED_TOOLS } from "../daemon/conversation-tool-setup.js";
3
4
  import {
4
5
  mergeSkillIds,
5
6
  SUBAGENT_ROLE_REGISTRY,
@@ -9,14 +10,7 @@ import { buildSubagentSystemPrompt } from "../subagent/manager.js";
9
10
  import type { SubagentConfig } from "../subagent/types.js";
10
11
 
11
12
  /** All roles defined in the SubagentRole union. */
12
- const ALL_ROLES: SubagentRole[] = [
13
- "general",
14
- "researcher",
15
- "coder",
16
- "planner",
17
- "investigator",
18
- "advisor",
19
- ];
13
+ const ALL_ROLES: SubagentRole[] = ["researcher", "builder", "advisor"];
20
14
 
21
15
  describe("SUBAGENT_ROLE_REGISTRY", () => {
22
16
  test("covers all values in the SubagentRole union", () => {
@@ -31,25 +25,84 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
31
25
  }
32
26
  });
33
27
 
34
- test("general has allowedTools: undefined", () => {
35
- expect(SUBAGENT_ROLE_REGISTRY.general.allowedTools).toBeUndefined();
28
+ test("the read-only roles declare an explicit allowlist", () => {
29
+ for (const role of ["researcher", "advisor"] as const) {
30
+ expect(Array.isArray(SUBAGENT_ROLE_REGISTRY[role].allowedTools)).toBe(
31
+ true,
32
+ );
33
+ }
34
+ expect(
35
+ SUBAGENT_ROLE_REGISTRY.researcher.allowedTools!.length,
36
+ ).toBeGreaterThan(0);
36
37
  });
37
38
 
38
- test("all scoped tool-using roles have allowedTools as a non-empty array", () => {
39
- for (const role of ALL_ROLES) {
40
- // 'general' has no filter (undefined); 'advisor' is tool-less (empty).
41
- if (role === "general" || role === "advisor") {
42
- continue;
43
- }
44
- const config = SUBAGENT_ROLE_REGISTRY[role];
45
- expect(Array.isArray(config.allowedTools)).toBe(true);
46
- expect(config.allowedTools!.length).toBeGreaterThan(0);
39
+ test("builder declares no allowlist, so it keeps the parent's whole tool surface", () => {
40
+ // The regression this guards: an explicit list is a ceiling. A builder
41
+ // scoped to file/shell/web tools silently loses connectors, MCP tools,
42
+ // browser and computer use, and everything else the parent can reach.
43
+ expect(SUBAGENT_ROLE_REGISTRY.builder.allowedTools).toBeUndefined();
44
+ });
45
+
46
+ test("advisor is scoped to exactly the three read-only fact-checking tools", () => {
47
+ expect(SUBAGENT_ROLE_REGISTRY.advisor.allowedTools).toEqual([
48
+ "file_read",
49
+ "file_list",
50
+ "code_search",
51
+ ]);
52
+ });
53
+
54
+ test("advisor cannot search memory or other conversations", () => {
55
+ // `recall` searches memory, the personal knowledge base, and prior
56
+ // conversations. The consult's documented contract is this conversation
57
+ // plus what the advisor reads in the workspace, so the one tool that
58
+ // reaches past it stays out.
59
+ expect(SUBAGENT_ROLE_REGISTRY.advisor.allowedTools).not.toContain("recall");
60
+ expect(SUBAGENT_ROLE_REGISTRY.advisor.systemPromptPreamble).toContain(
61
+ "cannot see other conversations",
62
+ );
63
+ });
64
+
65
+ test("every advisor tool is inside the runtime's owner-gated read-only set", () => {
66
+ // The advisor spawn sets `denySideEffectTools`, so a name outside
67
+ // READ_ONLY_ALLOWED_TOOLS is admitted by the role and then refused at
68
+ // dispatch: the model would be shown a tool it cannot run. Keeping the
69
+ // allowlist a subset is what makes the two gates agree.
70
+ for (const name of SUBAGENT_ROLE_REGISTRY.advisor.allowedTools!) {
71
+ expect(READ_ONLY_ALLOWED_TOOLS.has(name)).toBe(true);
47
72
  }
48
73
  });
49
74
 
50
- test("advisor is tool-less with an empty allowedTools array", () => {
51
- const config = SUBAGENT_ROLE_REGISTRY.advisor;
52
- expect(config.allowedTools).toEqual([]);
75
+ test("advisor is read-only: nothing write-capable, no shell, no skill execution", () => {
76
+ // The consult is a judgment call the parent blocks on, so the advisor must
77
+ // never be able to act on the workspace or persist output of its own.
78
+ const tools = SUBAGENT_ROLE_REGISTRY.advisor.allowedTools!;
79
+ for (const forbidden of [
80
+ "bash",
81
+ "host_bash",
82
+ "file_write",
83
+ "file_edit",
84
+ "skill_execute",
85
+ "web_fetch",
86
+ "recall",
87
+ ]) {
88
+ expect(tools).not.toContain(forbidden);
89
+ }
90
+ });
91
+
92
+ test("researcher preamble discourages slicing without instructing whole-file reads", () => {
93
+ const preamble = SUBAGENT_ROLE_REGISTRY.researcher.systemPromptPreamble;
94
+ // `file_read` takes no default line limit and its results skip result-time
95
+ // spooling, so a whole-file instruction puts full bodies on every LLM call
96
+ // for the rest of the turn. Keep the anti-slicing guidance without it.
97
+ expect(preamble.toLowerCase()).not.toContain("read whole files");
98
+ expect(preamble).toContain("one pass rather than many small slices");
99
+ });
100
+
101
+ test("advisor preamble states it can verify facts but cannot change anything", () => {
102
+ const preamble = SUBAGENT_ROLE_REGISTRY.advisor.systemPromptPreamble;
103
+ expect(preamble).toContain("read and search the files");
104
+ expect(preamble).toContain("cannot change anything");
105
+ expect(preamble).not.toContain("no tools");
53
106
  });
54
107
 
55
108
  test("SubagentRole type includes advisor", () => {
@@ -57,21 +110,25 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
57
110
  expect(SUBAGENT_ROLE_REGISTRY[advisor]).toBeDefined();
58
111
  });
59
112
 
60
- test('every role with a non-empty allowlist includes "notify_parent"', () => {
61
- for (const [_role, config] of Object.entries(SUBAGENT_ROLE_REGISTRY)) {
62
- // 'advisor' is tool-less (empty allowlist) and intentionally has none.
63
- if (config.allowedTools !== undefined && config.allowedTools.length > 0) {
64
- expect(config.allowedTools).toContain("notify_parent");
65
- }
66
- }
113
+ test('every allowlisted background role includes "notify_parent"', () => {
114
+ // Mid-run reporting only means something for a role the parent does not
115
+ // wait on. The advisor blocks the parent turn and returns its guidance as
116
+ // the tool result, so it intentionally has no notify_parent.
117
+ expect(SUBAGENT_ROLE_REGISTRY.researcher.allowedTools).toContain(
118
+ "notify_parent",
119
+ );
120
+ expect(SUBAGENT_ROLE_REGISTRY.advisor.allowedTools).not.toContain(
121
+ "notify_parent",
122
+ );
67
123
  });
68
124
 
69
- test('no role includes "skill_execute" (replaced by core tools)', () => {
70
- for (const [_role, config] of Object.entries(SUBAGENT_ROLE_REGISTRY)) {
71
- if (config.allowedTools !== undefined) {
72
- expect(config.allowedTools).not.toContain("skill_execute");
73
- }
74
- }
125
+ test('the scoped tool-using role includes "skill_execute" so preactivated skills work', () => {
126
+ // Without it, `preactivatedSkillIds` is a silent no-op for the role: the
127
+ // skill loads and none of its tools can be reached. Only an allowlisted
128
+ // role can lose it.
129
+ expect(SUBAGENT_ROLE_REGISTRY.researcher.allowedTools).toContain(
130
+ "skill_execute",
131
+ );
75
132
  });
76
133
 
77
134
  test('researcher includes "recall" for local information access', () => {
@@ -79,34 +136,42 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
79
136
  expect(tools).toContain("recall");
80
137
  });
81
138
 
82
- test('coder includes "recall" for local information access', () => {
83
- expect(SUBAGENT_ROLE_REGISTRY.coder.allowedTools!).toContain("recall");
84
- });
85
-
86
- test('planner includes "recall" for local information access', () => {
87
- expect(SUBAGENT_ROLE_REGISTRY.planner.allowedTools!).toContain("recall");
88
- });
89
-
90
- test("investigator is shell-free and uses code_search for code/log investigation", () => {
91
- const tools = SUBAGENT_ROLE_REGISTRY.investigator.allowedTools!;
139
+ test("researcher is read-only: no shell, no file writes", () => {
140
+ const tools = SUBAGENT_ROLE_REGISTRY.researcher.allowedTools!;
92
141
  expect(tools).not.toContain("bash");
93
142
  expect(tools).not.toContain("host_bash");
94
- expect(tools).toContain("code_search");
95
- });
96
-
97
- test("investigator excludes file write tools (read-only investigation)", () => {
98
- const tools = SUBAGENT_ROLE_REGISTRY.investigator.allowedTools!;
99
143
  expect(tools).not.toContain("file_write");
100
144
  expect(tools).not.toContain("file_edit");
101
145
  });
102
146
 
103
- test("investigator preamble defines the root-cause report contract", () => {
104
- const preamble = SUBAGENT_ROLE_REGISTRY.investigator.systemPromptPreamble;
147
+ test("researcher can search and enumerate as well as read", () => {
148
+ const tools = SUBAGENT_ROLE_REGISTRY.researcher.allowedTools!;
149
+ expect(tools).toContain("code_search");
150
+ expect(tools).toContain("file_list");
151
+ expect(tools).toContain("web_search");
152
+ expect(tools).toContain("web_fetch");
153
+ });
154
+
155
+ test("researcher preamble carries the read-only investigation contract", () => {
156
+ const preamble = SUBAGENT_ROLE_REGISTRY.researcher.systemPromptPreamble;
105
157
  expect(preamble).toContain("Root cause");
106
158
  expect(preamble).toContain("Evidence");
107
159
  expect(preamble).toContain("notify_parent");
108
- expect(preamble).not.toContain("shell access");
109
160
  expect(preamble).toContain("code_search");
161
+ expect(preamble).toContain("no shell");
162
+ });
163
+
164
+ test("builder preamble states it can write, run commands, and reach the parent's other tools", () => {
165
+ const preamble = SUBAGENT_ROLE_REGISTRY.builder.systemPromptPreamble;
166
+ expect(preamble).toContain("edit files");
167
+ expect(preamble).toContain("shell commands");
168
+ expect(preamble).toContain("any other tool the parent conversation can");
169
+ });
170
+
171
+ test("builder preamble requires self-verification and a change report", () => {
172
+ const preamble = SUBAGENT_ROLE_REGISTRY.builder.systemPromptPreamble;
173
+ expect(preamble).toContain("verify");
174
+ expect(preamble).toContain("files you touched");
110
175
  });
111
176
 
112
177
  test("no role references the old memory_recall tool name", () => {
@@ -122,21 +187,18 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
122
187
  expect(config.skillIds).toEqual([]);
123
188
  }
124
189
  });
125
-
126
- test('researcher and planner include "file_list"', () => {
127
- expect(SUBAGENT_ROLE_REGISTRY.researcher.allowedTools).toContain(
128
- "file_list",
129
- );
130
- expect(SUBAGENT_ROLE_REGISTRY.planner.allowedTools).toContain("file_list");
131
- });
132
190
  });
133
191
 
134
- describe("buildSubagentSystemPrompt blocked-signal constraint", () => {
135
- const cfg = (objective: string): SubagentConfig => ({
192
+ describe("buildSubagentSystemPrompt: framing and constraints", () => {
193
+ const cfg = (
194
+ objective: string,
195
+ extra: Partial<SubagentConfig> = {},
196
+ ): SubagentConfig => ({
136
197
  id: "sub-1",
137
198
  parentConversationId: "conv-1",
138
199
  label: "task",
139
200
  objective,
201
+ ...extra,
140
202
  });
141
203
 
142
204
  test("every role's constructed prompt tells it to signal blocked instead of fabricating a result", () => {
@@ -156,7 +218,7 @@ describe("buildSubagentSystemPrompt — blocked-signal constraint", () => {
156
218
  expect(
157
219
  buildSubagentSystemPrompt(cfg("save output"), "researcher"),
158
220
  ).toContain("do NOT fabricate");
159
- expect(buildSubagentSystemPrompt(cfg("save output"), "coder")).toContain(
221
+ expect(buildSubagentSystemPrompt(cfg("save output"), "builder")).toContain(
160
222
  "do NOT fabricate",
161
223
  );
162
224
  // It is NOT duplicated into the researcher role's own preamble.
@@ -164,6 +226,53 @@ describe("buildSubagentSystemPrompt — blocked-signal constraint", () => {
164
226
  SUBAGENT_ROLE_REGISTRY.researcher.systemPromptPreamble,
165
227
  ).not.toContain("do NOT fabricate");
166
228
  });
229
+
230
+ test("every role is also told not to invent output for a tool that failed or was missing", () => {
231
+ // The blocked-signal line above only covers a capability the ROLE lacks.
232
+ // This one covers the runtime cases: the call failed, or the tool was not
233
+ // there at all.
234
+ for (const role of ALL_ROLES) {
235
+ const prompt = buildSubagentSystemPrompt(cfg("run the build"), role);
236
+ expect(prompt).toContain(
237
+ "If a tool call fails, or a tool you expected is unavailable, report the failure verbatim and stop that line of work.",
238
+ );
239
+ expect(prompt).toContain(
240
+ "Never simulate, reconstruct, or invent tool output you did not actually receive.",
241
+ );
242
+ }
243
+ });
244
+
245
+ test("a persona renders as a line under the role preamble", () => {
246
+ const prompt = buildSubagentSystemPrompt(
247
+ cfg("assess the filing", { persona: "financial journalist" }),
248
+ "researcher",
249
+ );
250
+ expect(prompt).toContain(
251
+ "- Persona: act as financial journalist for this task.",
252
+ );
253
+ // Under the preamble, above the task.
254
+ expect(prompt.indexOf("- Persona:")).toBeGreaterThan(
255
+ prompt.indexOf(SUBAGENT_ROLE_REGISTRY.researcher.systemPromptPreamble),
256
+ );
257
+ expect(prompt.indexOf("- Persona:")).toBeLessThan(
258
+ prompt.indexOf("## Your Task"),
259
+ );
260
+ });
261
+
262
+ test("no persona line when the spawn carried none", () => {
263
+ expect(
264
+ buildSubagentSystemPrompt(cfg("do the work"), "builder"),
265
+ ).not.toContain("- Persona:");
266
+ });
267
+
268
+ test("the prompt names the type that ran", () => {
269
+ const prompt = buildSubagentSystemPrompt(
270
+ cfg("continue the work"),
271
+ "builder",
272
+ );
273
+ expect(prompt).toContain("## Your Task");
274
+ expect(prompt).toContain("- Role: builder");
275
+ });
167
276
  });
168
277
 
169
278
  describe("mergeSkillIds", () => {