@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5

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 (546) hide show
  1. package/bun.lock +20 -0
  2. package/docs/workspace-tools.md +42 -33
  3. package/eslint-rules/cli-no-daemon-internals.js +6 -0
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
  5. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
  6. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
  7. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
  8. package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
  9. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
  12. package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
  13. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/openapi.yaml +196 -56
  15. package/package.json +4 -1
  16. package/scripts/test.sh +36 -15
  17. package/src/__tests__/actor-token-service.test.ts +36 -14
  18. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
  19. package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  21. package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
  22. package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
  23. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  24. package/src/__tests__/approval-cascade.test.ts +2 -0
  25. package/src/__tests__/assistant-attachments.test.ts +42 -0
  26. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
  27. package/src/__tests__/btw-routes.test.ts +2 -0
  28. package/src/__tests__/build-persisted-content.test.ts +2 -0
  29. package/src/__tests__/call-controller.test.ts +19 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +21 -26
  31. package/src/__tests__/channel-delivery-store.test.ts +28 -0
  32. package/src/__tests__/channel-guardian.test.ts +164 -78
  33. package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
  34. package/src/__tests__/channel-reply-delivery.test.ts +2 -0
  35. package/src/__tests__/code-search-tool.test.ts +585 -0
  36. package/src/__tests__/compaction-events.test.ts +2 -0
  37. package/src/__tests__/compaction-ledger-store.test.ts +128 -0
  38. package/src/__tests__/compaction.benchmark.test.ts +2 -0
  39. package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
  40. package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
  41. package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
  43. package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
  44. package/src/__tests__/config-loader-backfill.test.ts +123 -10
  45. package/src/__tests__/config-schema.test.ts +1 -0
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
  47. package/src/__tests__/consult-deadline.test.ts +60 -0
  48. package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
  49. package/src/__tests__/contact-store-user-file.test.ts +7 -10
  50. package/src/__tests__/contacts-relay-reads.test.ts +19 -24
  51. package/src/__tests__/contacts-write.test.ts +0 -2
  52. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  53. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +134 -0
  57. package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
  58. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
  61. package/src/__tests__/conversation-fork-crud.test.ts +354 -24
  62. package/src/__tests__/conversation-history-web-search.test.ts +2 -0
  63. package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
  64. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
  65. package/src/__tests__/conversation-pairing.test.ts +2 -0
  66. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  67. package/src/__tests__/conversation-process-callsite.test.ts +2 -0
  68. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  69. package/src/__tests__/conversation-queue.test.ts +91 -0
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
  72. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  73. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  74. package/src/__tests__/conversation-speed-override.test.ts +2 -0
  75. package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
  76. package/src/__tests__/conversation-title-service.test.ts +2 -0
  77. package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
  78. package/src/__tests__/conversation-usage.test.ts +2 -0
  79. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
  80. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  81. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  82. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  83. package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
  84. package/src/__tests__/db-migration-rollback.test.ts +205 -171
  85. package/src/__tests__/db-test-helpers.ts +5 -4
  86. package/src/__tests__/delete-propagation.test.ts +5 -3
  87. package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
  88. package/src/__tests__/disk-pressure-guard.test.ts +41 -0
  89. package/src/__tests__/dm-backfill.test.ts +6 -4
  90. package/src/__tests__/dm-persistence.test.ts +2 -0
  91. package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
  92. package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
  93. package/src/__tests__/exploration-drift-hook.test.ts +3 -2
  94. package/src/__tests__/filing-service.test.ts +2 -0
  95. package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
  96. package/src/__tests__/guardian-dispatch.test.ts +140 -1
  97. package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
  98. package/src/__tests__/guardian-outbound-http.test.ts +13 -0
  99. package/src/__tests__/guardian-routing-state.test.ts +6 -10
  100. package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
  101. package/src/__tests__/heartbeat-service.test.ts +2 -0
  102. package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
  103. package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
  104. package/src/__tests__/helpers/mock-logger.ts +1 -0
  105. package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
  106. package/src/__tests__/host-app-control-routes.test.ts +24 -30
  107. package/src/__tests__/host-bash-routes.test.ts +31 -41
  108. package/src/__tests__/host-browser-routes.test.ts +26 -32
  109. package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
  110. package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
  111. package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
  112. package/src/__tests__/http-user-message-parity.test.ts +290 -8
  113. package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
  114. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  115. package/src/__tests__/invite-redemption-service.test.ts +471 -53
  116. package/src/__tests__/invite-routes-http.test.ts +34 -0
  117. package/src/__tests__/invite-service-ipc.test.ts +65 -2
  118. package/src/__tests__/llm-context-normalization.test.ts +105 -0
  119. package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
  120. package/src/__tests__/llm-usage-store.test.ts +25 -0
  121. package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
  122. package/src/__tests__/mcp-health-check.test.ts +2 -1
  123. package/src/__tests__/media-stream-server-integration.test.ts +127 -0
  124. package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
  125. package/src/__tests__/messaging-send-tool.test.ts +2 -0
  126. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  127. package/src/__tests__/mtime-cache.test.ts +275 -5
  128. package/src/__tests__/native-web-search.test.ts +2 -0
  129. package/src/__tests__/non-member-access-request.test.ts +191 -17
  130. package/src/__tests__/notification-broadcaster.test.ts +4 -0
  131. package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
  132. package/src/__tests__/notification-deep-link.test.ts +6 -0
  133. package/src/__tests__/notification-guardian-path.test.ts +19 -0
  134. package/src/__tests__/onboarding-persona-write.test.ts +52 -22
  135. package/src/__tests__/openai-provider.test.ts +22 -12
  136. package/src/__tests__/openai-responses-provider.test.ts +12 -2
  137. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  138. package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
  139. package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
  140. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  141. package/src/__tests__/persona-resolver.test.ts +75 -45
  142. package/src/__tests__/plugin-bootstrap.test.ts +21 -83
  143. package/src/__tests__/plugin-disabled-state.test.ts +190 -0
  144. package/src/__tests__/plugin-pipeline.test.ts +96 -0
  145. package/src/__tests__/plugin-route-contribution.test.ts +6 -19
  146. package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
  147. package/src/__tests__/plugin-types.test.ts +2 -4
  148. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  149. package/src/__tests__/process-message-display-content.test.ts +2 -0
  150. package/src/__tests__/provider-error-scenarios.test.ts +5 -4
  151. package/src/__tests__/provider-usage-tracking.test.ts +39 -0
  152. package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
  153. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
  154. package/src/__tests__/reaction-persistence.test.ts +51 -4
  155. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
  156. package/src/__tests__/registry.test.ts +4 -1
  157. package/src/__tests__/relay-server.test.ts +758 -32
  158. package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
  159. package/src/__tests__/secret-ingress-http.test.ts +14 -0
  160. package/src/__tests__/send-endpoint-busy.test.ts +30 -8
  161. package/src/__tests__/settings-routes.test.ts +32 -0
  162. package/src/__tests__/skills.test.ts +44 -0
  163. package/src/__tests__/slack-inbound-verification.test.ts +47 -2
  164. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
  165. package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
  166. package/src/__tests__/stt-hints.test.ts +49 -15
  167. package/src/__tests__/subagent-detail.test.ts +27 -0
  168. package/src/__tests__/subagent-disposal.test.ts +65 -0
  169. package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
  170. package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
  171. package/src/__tests__/subagent-notify-parent.test.ts +2 -0
  172. package/src/__tests__/subagent-role-registry.test.ts +24 -6
  173. package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
  174. package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
  175. package/src/__tests__/subagent-tools.test.ts +398 -1
  176. package/src/__tests__/suggestion-routes.test.ts +2 -0
  177. package/src/__tests__/thread-backfill.test.ts +3 -3
  178. package/src/__tests__/title-generate-hook.test.ts +2 -0
  179. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
  180. package/src/__tests__/tool-executor.test.ts +16 -11
  181. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  182. package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
  183. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  184. package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
  185. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
  186. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
  187. package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
  188. package/src/__tests__/trusted-contact-verification.test.ts +79 -54
  189. package/src/__tests__/twilio-routes.test.ts +96 -0
  190. package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
  191. package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
  192. package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
  193. package/src/__tests__/voice-invite-redemption.test.ts +216 -20
  194. package/src/__tests__/web-search-backend-failure.test.ts +2 -0
  195. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
  196. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
  197. package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
  198. package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
  199. package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
  200. package/src/__tests__/workspace-tool-loader.test.ts +195 -2
  201. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
  202. package/src/agent/loop-exclusive-tool.test.ts +154 -0
  203. package/src/agent/loop-native-web-search.test.ts +200 -0
  204. package/src/agent/loop.ts +164 -1
  205. package/src/api/constants/sse-replay.ts +41 -0
  206. package/src/api/events/conversation-notice.ts +26 -0
  207. package/src/api/index.ts +19 -1
  208. package/src/api/responses/llm-request-log-entry.ts +29 -0
  209. package/src/api/responses/subagent-detail.ts +17 -0
  210. package/src/api/surfaces.ts +39 -3
  211. package/src/approvals/guardian-channel-delivery.ts +30 -0
  212. package/src/approvals/guardian-expiry-notifier.ts +148 -0
  213. package/src/approvals/guardian-request-resolvers.ts +17 -15
  214. package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
  215. package/src/calls/call-domain.ts +3 -3
  216. package/src/calls/guardian-dispatch.ts +14 -9
  217. package/src/calls/inbound-trust-reader.ts +24 -2
  218. package/src/calls/media-stream-server.ts +21 -0
  219. package/src/calls/relay-access-wait.ts +6 -6
  220. package/src/calls/relay-server.ts +188 -51
  221. package/src/calls/relay-setup-router.ts +47 -17
  222. package/src/calls/relay-verification.ts +4 -4
  223. package/src/calls/stt-hints.ts +9 -12
  224. package/src/calls/twilio-routes.ts +14 -4
  225. package/src/channels/types.ts +10 -20
  226. package/src/cli/commands/__tests__/cache.test.ts +8 -1
  227. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
  228. package/src/cli/commands/cache.ts +194 -181
  229. package/src/cli/commands/contacts.ts +10 -7
  230. package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
  231. package/src/cli/commands/db/status.ts +37 -1
  232. package/src/cli/commands/mcp.ts +252 -218
  233. package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
  234. package/src/cli/commands/memory/index.ts +2 -0
  235. package/src/cli/commands/memory/worker.ts +242 -0
  236. package/src/cli/commands/plugins.ts +125 -13
  237. package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
  238. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
  239. package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
  240. package/src/cli/lib/list-installed-plugins.ts +179 -1
  241. package/src/cli/lib/publish-plugin.ts +633 -0
  242. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
  243. package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
  244. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
  245. package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
  246. package/src/config/bundled-skills/subagent/SKILL.md +17 -2
  247. package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
  248. package/src/config/call-site-defaults.ts +0 -6
  249. package/src/config/feature-flag-registry.json +5 -13
  250. package/src/config/llm-resolver.ts +0 -3
  251. package/src/config/loader.ts +38 -5
  252. package/src/config/prune-seeded-callsite-defaults.ts +110 -0
  253. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  254. package/src/config/schemas/call-site-catalog.ts +0 -7
  255. package/src/config/schemas/heartbeat.ts +2 -5
  256. package/src/config/schemas/llm.ts +3 -12
  257. package/src/config/schemas/memory-lifecycle.ts +12 -0
  258. package/src/config/schemas/memory-v3.ts +7 -0
  259. package/src/config/schemas/memory.ts +4 -0
  260. package/src/config/schemas/timeouts.ts +8 -0
  261. package/src/config/seed-inference-profiles.ts +30 -34
  262. package/src/config/skills.ts +27 -5
  263. package/src/config/sync-gated-profiles.ts +12 -16
  264. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
  265. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
  266. package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
  267. package/src/contacts/contact-store.ts +27 -237
  268. package/src/contacts/contacts-write.ts +18 -55
  269. package/src/contacts/gateway-channel-read.ts +51 -0
  270. package/src/contacts/guardian-delivery-reader.ts +223 -0
  271. package/src/contacts/member-write-relay.ts +183 -0
  272. package/src/contacts/types.ts +3 -15
  273. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
  274. package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
  275. package/src/daemon/assistant-attachments.ts +27 -4
  276. package/src/daemon/conversation-agent-loop.ts +28 -0
  277. package/src/daemon/conversation-notices.ts +60 -0
  278. package/src/daemon/conversation-process.ts +35 -16
  279. package/src/daemon/conversation-surfaces.ts +111 -38
  280. package/src/daemon/conversation-tool-setup.ts +71 -30
  281. package/src/daemon/conversation.ts +23 -1
  282. package/src/daemon/disk-pressure-guard.ts +12 -2
  283. package/src/daemon/event-loop-watchdog.ts +28 -1
  284. package/src/daemon/external-plugins-bootstrap.ts +17 -41
  285. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
  287. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
  288. package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
  289. package/src/daemon/handlers/config-a2a.ts +6 -14
  290. package/src/daemon/handlers/config-channels.ts +67 -26
  291. package/src/daemon/handlers/conversations.ts +77 -0
  292. package/src/daemon/lifecycle.ts +16 -0
  293. package/src/daemon/mcp-reload-service.ts +10 -0
  294. package/src/daemon/memory-v2-startup.test.ts +72 -0
  295. package/src/daemon/memory-v2-startup.ts +87 -19
  296. package/src/daemon/message-types/conversations.ts +2 -0
  297. package/src/daemon/message-types/surfaces.ts +12 -12
  298. package/src/daemon/server.ts +0 -4
  299. package/src/daemon/shutdown-handlers.ts +20 -0
  300. package/src/daemon/tool-setup-types.ts +9 -0
  301. package/src/heartbeat/heartbeat-service.ts +5 -0
  302. package/src/home/relationship-state-writer.ts +5 -0
  303. package/src/hooks/hook-loader.ts +341 -0
  304. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  305. package/src/ipc/assistant-server.ts +2 -2
  306. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
  307. package/src/mcp/client.ts +15 -1
  308. package/src/mcp/mcp-auth-orchestrator.ts +6 -1
  309. package/src/mcp/mcp-header-store.ts +134 -0
  310. package/src/mcp/mcp-oauth-provider.ts +19 -8
  311. package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
  312. package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
  313. package/src/memory/__tests__/prompt-override.test.ts +192 -0
  314. package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
  315. package/src/memory/compaction-ledger-store.ts +107 -0
  316. package/src/memory/conversation-crud.ts +129 -61
  317. package/src/memory/db-connection.ts +22 -3
  318. package/src/memory/db-init.ts +37 -503
  319. package/src/memory/db-singleton.ts +6 -4
  320. package/src/memory/jobs-worker.ts +104 -0
  321. package/src/memory/llm-request-log-store.ts +26 -1
  322. package/src/memory/llm-usage-store.ts +48 -20
  323. package/src/memory/memory-retrospective-job.ts +14 -8
  324. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
  325. package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
  326. package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
  327. package/src/memory/migrations/302-create-compaction-events.ts +107 -0
  328. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
  329. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  330. package/src/memory/migrations/run-migrations.ts +106 -10
  331. package/src/memory/migrations/validate-migration-state.ts +105 -67
  332. package/src/memory/prompt-override.ts +129 -0
  333. package/src/memory/schema/contacts.ts +6 -2
  334. package/src/memory/schema/conversations.ts +37 -0
  335. package/src/memory/schema/infrastructure.ts +20 -0
  336. package/src/memory/steps.ts +1294 -0
  337. package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
  338. package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
  339. package/src/memory/v2/cli-command-store.ts +75 -38
  340. package/src/memory/v2/prompts/consolidation.ts +13 -82
  341. package/src/memory/v2/prompts/router.ts +21 -93
  342. package/src/memory/v2/skill-store.ts +68 -31
  343. package/src/memory/watchdog-events-store.ts +87 -0
  344. package/src/memory/worker-control.ts +204 -0
  345. package/src/memory/worker-process.ts +89 -0
  346. package/src/messaging/channel-binding-metadata.ts +31 -0
  347. package/src/messaging/provider-types.ts +8 -0
  348. package/src/messaging/providers/slack/binding-metadata.ts +60 -0
  349. package/src/notifications/__tests__/broadcaster.test.ts +8 -8
  350. package/src/notifications/__tests__/connected-channels.test.ts +86 -0
  351. package/src/notifications/__tests__/decision-engine.test.ts +78 -9
  352. package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
  353. package/src/notifications/broadcaster.ts +8 -1
  354. package/src/notifications/decision-engine.ts +15 -7
  355. package/src/notifications/destination-resolver.ts +53 -25
  356. package/src/notifications/emit-signal.ts +34 -15
  357. package/src/onboarding/checkin-event.test.ts +222 -0
  358. package/src/onboarding/checkin-event.ts +321 -0
  359. package/src/onboarding/schedule-checkin.ts +190 -0
  360. package/src/permissions/question-prompter.test.ts +1 -1
  361. package/src/permissions/question-prompter.ts +7 -4
  362. package/src/plugin-api/index.ts +12 -12
  363. package/src/plugin-api/types.ts +12 -14
  364. package/src/plugin-api/vision-support.test.ts +28 -4
  365. package/src/plugin-api/vision-support.ts +66 -31
  366. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
  367. package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
  368. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
  369. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
  370. package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
  371. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
  372. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
  373. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
  374. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
  375. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
  376. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
  377. package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
  378. package/src/plugins/defaults/index.ts +0 -35
  379. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
  380. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
  381. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
  382. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
  384. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
  385. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
  386. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
  387. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
  388. package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
  389. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
  390. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
  391. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
  392. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
  393. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
  394. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
  395. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
  396. package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
  397. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
  398. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
  399. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
  400. package/src/plugins/disabled-state.ts +31 -0
  401. package/src/plugins/external-plugin-loader.ts +2 -2
  402. package/src/plugins/mtime-cache.ts +186 -330
  403. package/src/plugins/pipeline.ts +111 -13
  404. package/src/plugins/registry.ts +59 -16
  405. package/src/plugins/surface-import.ts +121 -0
  406. package/src/plugins/types.ts +7 -9
  407. package/src/prompts/persona-resolver.ts +43 -11
  408. package/src/providers/anthropic/client.ts +5 -0
  409. package/src/providers/call-site-routing.ts +45 -0
  410. package/src/providers/model-catalog.ts +16 -0
  411. package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
  412. package/src/providers/openai/api-error-normalization.ts +270 -0
  413. package/src/providers/openai/chat-completions-provider.ts +37 -83
  414. package/src/providers/openai/responses-provider.ts +50 -46
  415. package/src/providers/openrouter/client.ts +5 -0
  416. package/src/providers/provider-send-message.ts +10 -0
  417. package/src/providers/ratelimit.ts +10 -0
  418. package/src/providers/registry.ts +1 -1
  419. package/src/providers/retry.ts +10 -0
  420. package/src/providers/types.ts +22 -0
  421. package/src/providers/usage-tracking.ts +10 -0
  422. package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
  423. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
  424. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
  425. package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
  426. package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
  427. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
  428. package/src/runtime/access-request-helper.ts +18 -39
  429. package/src/runtime/actor-trust-resolver.ts +46 -19
  430. package/src/runtime/anchored-guardian.test.ts +109 -0
  431. package/src/runtime/anchored-guardian.ts +86 -0
  432. package/src/runtime/assistant-event-hub.ts +1 -1
  433. package/src/runtime/assistant-stream-state.ts +9 -2
  434. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
  435. package/src/runtime/auth/require-bound-guardian.ts +21 -11
  436. package/src/runtime/channel-verification-service.ts +56 -31
  437. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  438. package/src/runtime/guardian-vellum-migration.ts +69 -8
  439. package/src/runtime/invite-redemption-service.ts +213 -187
  440. package/src/runtime/local-actor-identity.test.ts +108 -0
  441. package/src/runtime/local-actor-identity.ts +85 -13
  442. package/src/runtime/local-principal-trust.ts +52 -0
  443. package/src/runtime/member-verdict-cache.ts +0 -0
  444. package/src/runtime/pending-interactions.ts +11 -1
  445. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
  446. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  447. package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
  448. package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
  449. package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
  450. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
  451. package/src/runtime/routes/browser-routes.ts +1 -1
  452. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
  453. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  454. package/src/runtime/routes/contact-routes.ts +48 -57
  455. package/src/runtime/routes/conversation-cli-routes.ts +4 -5
  456. package/src/runtime/routes/conversation-list-routes.ts +4 -7
  457. package/src/runtime/routes/conversation-query-routes.ts +72 -0
  458. package/src/runtime/routes/conversation-routes.ts +84 -85
  459. package/src/runtime/routes/events-routes.ts +2 -2
  460. package/src/runtime/routes/global-search-routes.ts +3 -1
  461. package/src/runtime/routes/guardian-action-routes.ts +4 -5
  462. package/src/runtime/routes/host-app-control-routes.ts +5 -4
  463. package/src/runtime/routes/host-bash-routes.ts +5 -4
  464. package/src/runtime/routes/host-browser-routes.ts +9 -11
  465. package/src/runtime/routes/host-cu-routes.ts +5 -4
  466. package/src/runtime/routes/host-file-routes.ts +5 -4
  467. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  468. package/src/runtime/routes/http-adapter.ts +16 -1
  469. package/src/runtime/routes/identity-routes.ts +3 -2
  470. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  471. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
  472. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
  473. package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
  474. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  475. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
  476. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
  477. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
  478. package/src/runtime/routes/index.ts +2 -0
  479. package/src/runtime/routes/llm-context-normalization.ts +83 -0
  480. package/src/runtime/routes/mcp-auth-routes.ts +171 -19
  481. package/src/runtime/routes/migration-rollback-routes.ts +4 -3
  482. package/src/runtime/routes/migration-routes.ts +4 -1
  483. package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
  484. package/src/runtime/routes/settings-routes.ts +8 -3
  485. package/src/runtime/routes/subagents-routes.ts +5 -0
  486. package/src/runtime/routes/surface-action-routes.ts +42 -56
  487. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  488. package/src/runtime/services/conversation-serializer.ts +13 -58
  489. package/src/runtime/tool-grant-request-helper.ts +3 -3
  490. package/src/runtime/trust-verdict-consumer.ts +111 -40
  491. package/src/runtime/verification-outbound-actions.ts +18 -18
  492. package/src/signals/user-message.ts +16 -0
  493. package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
  494. package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
  495. package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
  496. package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
  497. package/src/subagent/index.ts +1 -1
  498. package/src/subagent/manager.ts +254 -33
  499. package/src/subagent/types.ts +11 -4
  500. package/src/telemetry/types.ts +34 -1
  501. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  502. package/src/telemetry/usage-telemetry-reporter.ts +87 -3
  503. package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
  504. package/src/tools/ask-question/ask-question-tool.ts +13 -0
  505. package/src/tools/executor.ts +4 -4
  506. package/src/tools/filesystem/search.ts +543 -0
  507. package/src/tools/registry.ts +38 -0
  508. package/src/tools/shared/filesystem/path-policy.ts +12 -5
  509. package/src/tools/subagent/consult-deadline.ts +49 -0
  510. package/src/tools/subagent/spawn.ts +234 -5
  511. package/src/tools/tool-approval-handler.ts +1 -1
  512. package/src/tools/tool-defaults.ts +9 -2
  513. package/src/tools/tool-manifest.ts +3 -0
  514. package/src/tools/types.ts +131 -25
  515. package/src/tools/workspace-tools/loader.ts +348 -244
  516. package/src/util/errors.ts +26 -1
  517. package/src/util/logger.ts +9 -0
  518. package/src/util/platform.ts +19 -0
  519. package/src/util/telemetry-db-path.ts +24 -0
  520. package/src/workflows/library.test.ts +140 -0
  521. package/src/workflows/library.ts +82 -28
  522. package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
  523. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
  524. package/src/workspace/migrations/031-drop-user-md.ts +232 -148
  525. package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
  526. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
  527. package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
  528. package/src/workspace/migrations/registry.ts +4 -0
  529. package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
  530. package/src/daemon/workspace-tools-watcher.ts +0 -328
  531. package/src/memory/migrations/registry.ts +0 -573
  532. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
  533. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
  534. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
  535. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
  536. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
  537. package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
  538. package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
  539. package/src/plugins/defaults/advisor/config.ts +0 -21
  540. package/src/plugins/defaults/advisor/consult.ts +0 -93
  541. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
  542. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
  543. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
  544. package/src/plugins/defaults/advisor/package.json +0 -14
  545. package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
  546. package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
@@ -0,0 +1,49 @@
1
+ /**
2
+ * A progress-aware deadline for the synchronous advisor consult.
3
+ *
4
+ * A reasoning advisor profile spends most of its window *thinking*, streaming
5
+ * reasoning tokens the whole time. A fixed wall-clock ceiling would cut it off
6
+ * mid-thought, so this aborts only after the consult goes `idleMs` without any
7
+ * streamed token (thinking or text) — i.e. genuine silence — with an absolute
8
+ * `maxMs` backstop so a runaway or looping stream can't block the parent
9
+ * forever.
10
+ *
11
+ * Usage: combine `signal` with the caller's own signal, call `recordProgress()`
12
+ * on every streamed chunk, and `dispose()` once the consult settles.
13
+ */
14
+ export interface ConsultDeadline {
15
+ /** Aborts when the idle window lapses or the absolute max elapses. */
16
+ readonly signal: AbortSignal;
17
+ /** Reset the idle window — call on every streamed chunk (thinking or text). */
18
+ recordProgress(): void;
19
+ /** Clear both timers; call once the consult settles (success or failure). */
20
+ dispose(): void;
21
+ }
22
+
23
+ export function createConsultDeadline(opts: {
24
+ idleMs: number;
25
+ maxMs: number;
26
+ }): ConsultDeadline {
27
+ const controller = new AbortController();
28
+ let idleTimer: ReturnType<typeof setTimeout> | undefined;
29
+
30
+ const recordProgress = (): void => {
31
+ // Once aborted, don't re-arm — the consult is already being torn down.
32
+ if (controller.signal.aborted) return;
33
+ if (idleTimer) clearTimeout(idleTimer);
34
+ idleTimer = setTimeout(() => controller.abort(), opts.idleMs);
35
+ };
36
+
37
+ // Absolute backstop, independent of streaming progress.
38
+ const maxTimer = setTimeout(() => controller.abort(), opts.maxMs);
39
+
40
+ // Arm the idle window immediately so time-to-first-token is bounded too.
41
+ recordProgress();
42
+
43
+ const dispose = (): void => {
44
+ if (idleTimer) clearTimeout(idleTimer);
45
+ clearTimeout(maxTimer);
46
+ };
47
+
48
+ return { signal: controller.signal, recordProgress, dispose };
49
+ }
@@ -2,11 +2,45 @@ import { validateInferenceProfileKey } from "../../config/inference-profile-vali
2
2
  import { resolveDefaultProfileKey } from "../../config/llm-resolver.js";
3
3
  import { getConfig } from "../../config/loader.js";
4
4
  import { findConversation } from "../../daemon/conversation-registry.js";
5
- import { getConversationOverrideProfile } from "../../memory/conversation-crud.js";
6
- import type { Message } from "../../providers/types.js";
7
- import { getSubagentManager } from "../../subagent/index.js";
5
+ import type { ServerMessage } from "../../daemon/message-protocol.js";
6
+ import {
7
+ getConversationOverrideProfile,
8
+ getMessages,
9
+ } from "../../memory/conversation-crud.js";
10
+ import type { ContentBlock, Message } from "../../providers/types.js";
11
+ import {
12
+ advisorRequestText,
13
+ buildAdvisorSystem,
14
+ } from "../../subagent/consult-prompt.js";
15
+ import { sanitizeConsultTranscript } from "../../subagent/consult-transcript.js";
16
+ import {
17
+ getSubagentManager,
18
+ SubagentAbortedError,
19
+ } from "../../subagent/index.js";
8
20
  import type { SubagentRole } from "../../subagent/types.js";
21
+ import { getLogger } from "../../util/logger.js";
9
22
  import type { ToolContext, ToolExecutionResult } from "../types.js";
23
+ import { createConsultDeadline } from "./consult-deadline.js";
24
+
25
+ const log = getLogger("subagent-spawn");
26
+
27
+ /**
28
+ * Idle ceiling on a single advisor consult: abort only after this much time
29
+ * passes with NO streamed token (thinking or text). A reasoning advisor profile
30
+ * streams its reasoning while it works, so a fixed wall-clock ceiling would kill
31
+ * it mid-thought; an idle window instead fires only when the consult is
32
+ * genuinely stalled (or never starts). Generous enough to also span
33
+ * time-to-first-token over a large inherited transcript.
34
+ */
35
+ const ADVISOR_IDLE_TIMEOUT_MS = 60_000;
36
+
37
+ /**
38
+ * Absolute backstop on a single advisor consult regardless of streaming
39
+ * progress, so a runaway or looping stream can't block the parent forever.
40
+ * Either ceiling still yields the partial guidance (recovered in the
41
+ * `SubagentAbortedError` branch below), not a discard.
42
+ */
43
+ const ADVISOR_MAX_TIMEOUT_MS = 300_000;
10
44
 
11
45
  export async function executeSubagentSpawn(
12
46
  input: Record<string, unknown>,
@@ -63,6 +97,19 @@ export async function executeSubagentSpawn(
63
97
  };
64
98
  }
65
99
 
100
+ // ── Advisor role: synchronous, tool-less, stronger-model consult ──
101
+ // Branch before the fire-and-forget path: the advisor blocks on the run and
102
+ // returns its guidance as the tool result in the same turn.
103
+ if (role === "advisor") {
104
+ return runAdvisorConsult({
105
+ context,
106
+ label,
107
+ objective,
108
+ sendToClient: sendToClient as (msg: ServerMessage) => void,
109
+ requestedOverrideProfile,
110
+ });
111
+ }
112
+
66
113
  // ── Fork mode: resolve parent context ────────────────────────────
67
114
  let forkFields:
68
115
  | {
@@ -138,8 +185,8 @@ export async function executeSubagentSpawn(
138
185
  objective,
139
186
  context: extraContext,
140
187
  sendResultToUser,
141
- // For fork mode, role is ignored by the manager (forced to general),
142
- // but we still omit it from the config to signal intent.
188
+ // Regular forks omit the role so they default to general; the advisor
189
+ // role is special-cased earlier via runAdvisorConsult, not here.
143
190
  ...(!fork && role ? { role: role as SubagentRole } : {}),
144
191
  ...(inheritedOverrideProfile
145
192
  ? { overrideProfile: inheritedOverrideProfile }
@@ -168,3 +215,185 @@ export async function executeSubagentSpawn(
168
215
  return { content: `Failed to spawn subagent: ${msg}`, isError: true };
169
216
  }
170
217
  }
218
+
219
+ // ── Advisor consult ──────────────────────────────────────────────────
220
+
221
+ /**
222
+ * Run the `advisor` role as a synchronous, context-inheriting, stronger-model
223
+ * consult and return its guidance as the tool result.
224
+ *
225
+ * Inherits the parent transcript (sanitized), frames it as advice via
226
+ * `buildAdvisorSystem`, runs tool-less on `llm.advisorProfile` (unless the
227
+ * caller passed an explicit `inference_profile`), and is bounded by a
228
+ * progress-aware deadline: an idle window (`ADVISOR_IDLE_TIMEOUT_MS`) reset on
229
+ * every streamed token so a reasoning model isn't killed mid-thought, plus an
230
+ * absolute `ADVISOR_MAX_TIMEOUT_MS` backstop. If either ceiling is hit, the
231
+ * partial guidance produced so far is recovered and returned with a "may be cut
232
+ * off" note rather than discarded. Degrades to a benign non-error notice on any
233
+ * other failure (including the depth-limit rejection when a subagent itself
234
+ * calls the advisor).
235
+ */
236
+ async function runAdvisorConsult(args: {
237
+ context: ToolContext;
238
+ label: string;
239
+ /** The agent's own `objective` — its framing of what it wants advised on. */
240
+ objective: string;
241
+ sendToClient: (msg: ServerMessage) => void;
242
+ requestedOverrideProfile: string | undefined;
243
+ }): Promise<ToolExecutionResult> {
244
+ const { context, label, objective, sendToClient, requestedOverrideProfile } =
245
+ args;
246
+
247
+ try {
248
+ const parentConversation = findConversation(context.conversationId);
249
+ if (!parentConversation) {
250
+ return {
251
+ content:
252
+ "(advisor unavailable: parent conversation could not be resolved)",
253
+ isError: false,
254
+ };
255
+ }
256
+
257
+ // Snapshot the parent's in-memory transcript and system prompt, then append
258
+ // the in-flight assistant turn (the plan/text the model wrote THIS turn,
259
+ // before calling the advisor). The in-memory array does not yet hold that
260
+ // turn — the agent loop only writes it back to `conversation.messages` after
261
+ // the turn settles — but it is already persisted to the DB (the assistant
262
+ // row is finalized at `message_complete`, which fires before tool execution).
263
+ // `sanitizeConsultTranscript` then strips the dangling advisor `tool_use`
264
+ // off that final assistant turn so the inherited transcript is provider-safe.
265
+ const parentSystemPrompt = parentConversation.getCurrentSystemPrompt();
266
+ const withInFlight = appendInFlightAssistantTurn(
267
+ [...parentConversation.messages],
268
+ context.conversationId,
269
+ );
270
+ const sanitizedMessages = sanitizeConsultTranscript(withInFlight);
271
+
272
+ // Default to the stronger advisor profile when the caller did not pin one;
273
+ // an explicit `inference_profile` wins (already forced upstream).
274
+ const advisorProfile = getConfig().llm.advisorProfile;
275
+ const overrideProfile = requestedOverrideProfile ?? advisorProfile;
276
+ const forceOverrideProfile = overrideProfile !== undefined;
277
+
278
+ // Progress-aware deadline: reset on every streamed token so the consult
279
+ // isn't killed mid-thought, with an absolute backstop. Combine it with the
280
+ // caller's own signal.
281
+ const deadline = createConsultDeadline({
282
+ idleMs: ADVISOR_IDLE_TIMEOUT_MS,
283
+ maxMs: ADVISOR_MAX_TIMEOUT_MS,
284
+ });
285
+ const signal = context.signal
286
+ ? AbortSignal.any([context.signal, deadline.signal])
287
+ : deadline.signal;
288
+ // Every streamed chunk (thinking or text) counts as progress and resets the
289
+ // idle window, then forwards to the caller's stream sink if one is present.
290
+ const onText = (chunk: string): void => {
291
+ deadline.recordProgress();
292
+ context.onOutput?.(chunk);
293
+ };
294
+
295
+ try {
296
+ const advice = await getSubagentManager().spawnAndAwait(
297
+ {
298
+ parentConversationId: context.conversationId,
299
+ label,
300
+ // Carry the agent's own objective into the consult request — the
301
+ // agent states the task here, and the inherited transcript can be thin.
302
+ objective: advisorRequestText(objective),
303
+ sendResultToUser: false,
304
+ role: "advisor",
305
+ fork: true,
306
+ parentMessages: sanitizedMessages,
307
+ systemPromptOverride: buildAdvisorSystem(parentSystemPrompt),
308
+ ...(overrideProfile ? { overrideProfile } : {}),
309
+ ...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
310
+ ...(context.toolUseId ? { parentToolUseId: context.toolUseId } : {}),
311
+ },
312
+ sendToClient,
313
+ { signal, onText },
314
+ );
315
+
316
+ const trimmed = advice.trim();
317
+ return {
318
+ content:
319
+ trimmed.length > 0 ? trimmed : "(advisor returned no guidance)",
320
+ isError: false,
321
+ };
322
+ } finally {
323
+ deadline.dispose();
324
+ }
325
+ } catch (err) {
326
+ // Timed out mid-generation: salvage whatever guidance the advisor had
327
+ // written rather than throwing it away. Partial strategic advice is far
328
+ // more useful to the agent than an "unavailable" notice — especially on a
329
+ // slow reasoning profile that needs most of the window to think.
330
+ if (err instanceof SubagentAbortedError) {
331
+ const partial = err.partialText.trim();
332
+ if (partial.length > 0) {
333
+ log.warn(
334
+ { conversationId: context.conversationId },
335
+ "Advisor consult timed out; returning partial guidance",
336
+ );
337
+ return {
338
+ content: `${partial}\n\n_(The advisor reached its time limit while still writing — the guidance above may be cut off.)_`,
339
+ isError: false,
340
+ };
341
+ }
342
+ }
343
+ const reason = err instanceof Error ? err.message : String(err);
344
+ log.warn(
345
+ { err, conversationId: context.conversationId },
346
+ "Advisor consult failed",
347
+ );
348
+ // Never fail the turn — the advisor is advice, not a blocker.
349
+ return { content: `(advisor unavailable: ${reason})`, isError: false };
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Append the in-flight assistant turn (persisted this turn before the advisor
355
+ * tool ran) to an in-memory message snapshot, unless the snapshot already ends
356
+ * with it. The latest persisted assistant row carries the plan/text the model
357
+ * wrote immediately before calling the advisor plus the dangling advisor
358
+ * `tool_use`; `sanitizeConsultTranscript` strips the dangling call.
359
+ *
360
+ * Best-effort: a malformed or missing row leaves the snapshot unchanged so the
361
+ * consult still runs over the in-memory history.
362
+ */
363
+ function appendInFlightAssistantTurn(
364
+ messages: Message[],
365
+ conversationId: string,
366
+ ): Message[] {
367
+ // When the snapshot already ends on an assistant turn, the in-flight turn is
368
+ // present (or there is none to add) — appending the latest row would duplicate it.
369
+ if (messages[messages.length - 1]?.role === "assistant") return messages;
370
+
371
+ let rows;
372
+ try {
373
+ rows = getMessages(conversationId);
374
+ } catch {
375
+ return messages;
376
+ }
377
+ if (!rows || rows.length === 0) return messages;
378
+
379
+ const lastRow = rows[rows.length - 1];
380
+ if (lastRow.role !== "assistant") return messages;
381
+
382
+ let blocks: ContentBlock[];
383
+ try {
384
+ const parsed = JSON.parse(lastRow.content);
385
+ if (Array.isArray(parsed)) {
386
+ blocks = parsed as ContentBlock[];
387
+ } else if (typeof parsed === "string") {
388
+ blocks = [{ type: "text", text: parsed }];
389
+ } else {
390
+ return messages;
391
+ }
392
+ } catch {
393
+ // Plain-text content (no JSON envelope).
394
+ blocks = [{ type: "text", text: lastRow.content }];
395
+ }
396
+
397
+ if (blocks.length === 0) return messages;
398
+ return [...messages, { role: "assistant", content: blocks }];
399
+ }
@@ -553,7 +553,7 @@ export class ToolApprovalHandler {
553
553
  const inputDigest =
554
554
  deferredConsumeParams?.inputDigest ??
555
555
  computeToolApprovalDigest(name, input);
556
- const escalation = createOrReuseToolGrantRequest({
556
+ const escalation = await createOrReuseToolGrantRequest({
557
557
  assistantId: context.assistantId,
558
558
  sourceChannel: context.executionChannel as ChannelId,
559
559
  conversationId: context.conversationId,
@@ -10,6 +10,7 @@
10
10
  * is a `Tool` (`Required<ToolDefinition>`).
11
11
  */
12
12
 
13
+ import { resolveExecutionTarget } from "./execution-target.js";
13
14
  import type {
14
15
  RiskLevel,
15
16
  Tool,
@@ -30,7 +31,10 @@ import type {
30
31
  * are rejected at the JSON-schema layer.
31
32
  * - `executionTarget` defaults to `sandbox` — author-supplied tool code
32
33
  * runs in the assistant container by default; opt in to `host` when
33
- * the tool proxies work to the connected client.
34
+ * the tool proxies work to the connected client. The name-prefix
35
+ * heuristic (`host_*` / `computer_use_*` resolves to host) is applied
36
+ * by `resolveExecutionTarget` in `finalizeTool`, so a tool named
37
+ * `host_my_thing` defaults to host even without an explicit field.
34
38
  * - `category` defaults to empty — Slack channel `allowedToolCategories`
35
39
  * policy denies uncategorized tools when a category allow-list is set,
36
40
  * which is the correct deny-by-default for tools the author didn't
@@ -90,8 +94,10 @@ export function finalizeTool(tool: ToolDefinition, defaultName = ""): Tool {
90
94
  content: `tool ${name} has no execute implementation`,
91
95
  isError: true,
92
96
  });
93
- const executionTarget = tool.executionTarget ?? TOOL_DEFAULTS.executionTarget;
97
+ const executionTarget =
98
+ tool.executionTarget ?? resolveExecutionTarget({ name });
94
99
  const category = tool.category ?? TOOL_DEFAULTS.category;
100
+ const exclusive = tool.exclusive ?? false;
95
101
  return {
96
102
  ...tool,
97
103
  name,
@@ -101,5 +107,6 @@ export function finalizeTool(tool: ToolDefinition, defaultName = ""): Tool {
101
107
  executionTarget,
102
108
  execute,
103
109
  category,
110
+ exclusive,
104
111
  };
105
112
  }
@@ -18,6 +18,7 @@ import { runAuthenticatedCommandTool } from "./credential-execution/run-authenti
18
18
  import { fileEditTool } from "./filesystem/edit.js";
19
19
  import { fileListTool } from "./filesystem/list.js";
20
20
  import { fileReadTool } from "./filesystem/read.js";
21
+ import { codeSearchTool } from "./filesystem/search.js";
21
22
  import { fileWriteTool } from "./filesystem/write.js";
22
23
  import { recallTool, rememberTool } from "./memory/register.js";
23
24
  import { webFetchTool } from "./network/web-fetch.js";
@@ -59,6 +60,7 @@ export const eagerModuleToolNames: string[] = [
59
60
  "file_write",
60
61
  "file_edit",
61
62
  "file_list",
63
+ "code_search",
62
64
  "web_search",
63
65
  "web_fetch",
64
66
  "skill_execute",
@@ -81,6 +83,7 @@ export const explicitTools: ToolDefinition[] = [
81
83
  fileWriteTool,
82
84
  fileEditTool,
83
85
  fileListTool,
86
+ codeSearchTool,
84
87
  webFetchTool,
85
88
  webSearchTool,
86
89
  skillExecuteTool,
@@ -249,6 +249,26 @@ export function stringifyToolInput(input: Record<string, unknown>): string {
249
249
  }
250
250
  }
251
251
 
252
+ /**
253
+ * Runtime context passed as the second argument to every tool's `execute`.
254
+ *
255
+ * The fields fall into two groups:
256
+ *
257
+ * - A small, stable core that we are comfortable exposing to any tool —
258
+ * including workspace- and plugin-authored tools via `@vellumai/plugin-api`:
259
+ * `conversationId`, `workingDir`, `requestId`, `signal`, `onOutput`,
260
+ * `assistantId`, `isInteractive`.
261
+ * - Everything tagged `@legacy` below: host-internal routing, permission,
262
+ * trust, requester-identity, proxy, and telemetry metadata that historically
263
+ * accreted on this single context. These are NOT a surface we want third-party
264
+ * tools to depend on; we are triaging them post-launch with the goal of
265
+ * moving them off the public context (or removing them) over time. Grep for
266
+ * `@legacy` to enumerate the set. Do not add new fields here — extend the
267
+ * stable core only when a field is genuinely safe and stable to expose.
268
+ *
269
+ * The daemon constructs and passes the full object to every tool at runtime; a
270
+ * tool that only reads the stable core is unaffected by the eventual cleanup.
271
+ */
252
272
  export interface ToolContext {
253
273
  /** Identifier of the conversation this tool invocation belongs to. */
254
274
  conversationId: string;
@@ -262,23 +282,44 @@ export interface ToolContext {
262
282
  onOutput?: (chunk: string) => void;
263
283
  /** Logical assistant scope for multi-assistant routing. */
264
284
  assistantId?: string;
265
- /** When set, the tool execution is part of a task run. Used to retrieve ephemeral permission rules. */
285
+ /** True when an interactive client is connected (not just a no-op callback). */
286
+ isInteractive?: boolean;
287
+ /**
288
+ * When set, the tool execution is part of a task run. Used to retrieve ephemeral permission rules.
289
+ * @legacy
290
+ */
266
291
  taskRunId?: string;
267
292
  /**
268
293
  * Model attribution snapshot for the conversation at invocation time
269
294
  * (provider/model/profile that issued this tool call). Used by tool
270
295
  * telemetry; never sent to the tool itself.
296
+ * @legacy
271
297
  */
272
298
  attribution?: UsageAttributionSnapshot | null;
273
- /** Optional callback for tool lifecycle events (start/prompt/deny/execute/error). */
299
+ /**
300
+ * Optional callback for tool lifecycle events (start/prompt/deny/execute/error).
301
+ * @legacy
302
+ */
274
303
  onToolLifecycleEvent?: ToolLifecycleEventHandler;
275
- /** Optional resolver for proxy tools - delegates execution to an external client. */
304
+ /**
305
+ * Optional resolver for proxy tools - delegates execution to an external client.
306
+ * @legacy
307
+ */
276
308
  proxyToolResolver?: ProxyToolResolver;
277
- /** When set, only tools in this set may execute. Tools outside the set are blocked with an error. */
309
+ /**
310
+ * When set, only tools in this set may execute. Tools outside the set are blocked with an error.
311
+ * @legacy
312
+ */
278
313
  allowedToolNames?: Set<string>;
279
- /** True when this turn is restricted to storage cleanup-safe tools. */
314
+ /**
315
+ * True when this turn is restricted to storage cleanup-safe tools.
316
+ * @legacy
317
+ */
280
318
  diskPressureCleanupModeActive?: boolean;
281
- /** Prompt the user for a secret value via native SecureField UI. */
319
+ /**
320
+ * Prompt the user for a secret value via native SecureField UI.
321
+ * @legacy
322
+ */
282
323
  requestSecret?: (params: {
283
324
  service: string;
284
325
  field: string;
@@ -289,11 +330,15 @@ export interface ToolContext {
289
330
  allowedTools?: string[];
290
331
  allowedDomains?: string[];
291
332
  }) => Promise<SecretPromptResult>;
292
- /** Optional callback to send a message to the connected client (e.g. open_url). */
333
+ /**
334
+ * Optional callback to send a message to the connected client (e.g. open_url).
335
+ * @legacy
336
+ */
293
337
  sendToClient?: (msg: { type: string; [key: string]: unknown }) => void;
294
- /** True when an interactive client is connected (not just a no-op callback). */
295
- isInteractive?: boolean;
296
- /** When true, tools with side effects should always prompt for confirmation. */
338
+ /**
339
+ * When true, tools with side effects should always prompt for confirmation.
340
+ * @legacy
341
+ */
297
342
  forcePromptSideEffects?: boolean;
298
343
  /**
299
344
  * When true, the tool requires a fresh interactive approval for every
@@ -304,26 +349,46 @@ export interface ToolContext {
304
349
  * temporary override options in the prompt UI. Used by
305
350
  * `manage_secure_command_tool` to ensure a human reviews each secure
306
351
  * bundle installation.
352
+ * @legacy
307
353
  */
308
354
  requireFreshApproval?: boolean;
309
- /** Approval callback for proxy policy decisions that require user confirmation. */
355
+ /**
356
+ * Approval callback for proxy policy decisions that require user confirmation.
357
+ * @legacy
358
+ */
310
359
  proxyApprovalCallback?: ProxyApprovalCallback;
311
- /** Optional principal identifier propagated to sub-tool confirmation flows. */
360
+ /**
361
+ * Optional principal identifier propagated to sub-tool confirmation flows.
362
+ * @legacy
363
+ */
312
364
  principal?: string;
313
365
  /**
314
366
  * Trust classification of the actor who initiated this tool invocation.
315
367
  * Determines permission level: guardians self-approve, trusted contacts
316
368
  * may escalate to guardian for approval, unknown actors are fail-closed.
317
369
  * See {@link TrustClass} in actor-trust-resolver.ts for value semantics.
370
+ * @legacy
318
371
  */
319
372
  trustClass: TrustClass;
320
- /** Channel through which the tool invocation originates (e.g. 'telegram', 'phone'). Used for scoped grant consumption. */
373
+ /**
374
+ * Channel through which the tool invocation originates (e.g. 'telegram', 'phone'). Used for scoped grant consumption.
375
+ * @legacy
376
+ */
321
377
  executionChannel?: string;
322
- /** Voice/call session ID, if the invocation originates from a call. Used for scoped grant consumption. */
378
+ /**
379
+ * Voice/call session ID, if the invocation originates from a call. Used for scoped grant consumption.
380
+ * @legacy
381
+ */
323
382
  callSessionId?: string;
324
- /** True when the tool invocation was triggered by a user clicking a surface action button (not a regular message). */
383
+ /**
384
+ * True when the tool invocation was triggered by a user clicking a surface action button (not a regular message).
385
+ * @legacy
386
+ */
325
387
  triggeredBySurfaceAction?: boolean;
326
- /** True when the user explicitly approved this tool invocation via the interactive permission prompt (not auto-approved by trust rules or temporary overrides). */
388
+ /**
389
+ * True when the user explicitly approved this tool invocation via the interactive permission prompt (not auto-approved by trust rules or temporary overrides).
390
+ * @legacy
391
+ */
327
392
  approvedViaPrompt?: boolean;
328
393
  /**
329
394
  * True when the invocation is inside a scheduled task run whose
@@ -331,21 +396,43 @@ export interface ToolContext {
331
396
  * Tools that normally require a surface-action click (e.g. bulk archive,
332
397
  * unsubscribe) may treat this as equivalent consent, since the user
333
398
  * already reviewed the tool list when the task was saved.
399
+ * @legacy
334
400
  */
335
401
  batchAuthorizedByTask?: boolean;
336
- /** External user ID of the requester (non-guardian actor). Used for scoped grant consumption. */
402
+ /**
403
+ * External user ID of the requester (non-guardian actor). Used for scoped grant consumption.
404
+ * @legacy
405
+ */
337
406
  requesterExternalUserId?: string;
338
- /** Chat ID of the requester (non-guardian actor). Used for tool grant request escalation notifications. */
407
+ /**
408
+ * Chat ID of the requester (non-guardian actor). Used for tool grant request escalation notifications.
409
+ * @legacy
410
+ */
339
411
  requesterChatId?: string;
340
- /** Human-readable identifier for the requester (e.g., @username). */
412
+ /**
413
+ * Human-readable identifier for the requester (e.g., @username).
414
+ * @legacy
415
+ */
341
416
  requesterIdentifier?: string;
342
- /** Preferred display name for the requester. */
417
+ /**
418
+ * Preferred display name for the requester.
419
+ * @legacy
420
+ */
343
421
  requesterDisplayName?: string;
344
- /** Slack channel ID for channel-scoped permission enforcement. When set, tools are checked against the channel's permission profile. */
422
+ /**
423
+ * Slack channel ID for channel-scoped permission enforcement. When set, tools are checked against the channel's permission profile.
424
+ * @legacy
425
+ */
345
426
  channelPermissionChannelId?: string;
346
- /** The tool_use block ID from the LLM response, used to correlate confirmation prompts with specific tool invocations. */
427
+ /**
428
+ * The tool_use block ID from the LLM response, used to correlate confirmation prompts with specific tool invocations.
429
+ * @legacy
430
+ */
347
431
  toolUseId?: string;
348
- /** True when the assistant is running as a platform-managed remote instance. Used to auto-approve sandboxed bash tools. */
432
+ /**
433
+ * True when the assistant is running as a platform-managed remote instance. Used to auto-approve sandboxed bash tools.
434
+ * @legacy
435
+ */
349
436
  isPlatformHosted?: boolean;
350
437
  /**
351
438
  * The interface ID of the connected client driving the current turn (e.g.
@@ -353,6 +440,7 @@ export interface ToolContext {
353
440
  * transport preference — for example, macOS-originated turns prefer the
354
441
  * user's real Chrome session via the paired extension before falling back
355
442
  * to cdp-inspect or local Playwright.
443
+ * @legacy
356
444
  */
357
445
  transportInterface?: InterfaceId;
358
446
  /**
@@ -363,6 +451,7 @@ export interface ToolContext {
363
451
  * has `inferenceProfile` set — the override only flows through the
364
452
  * in-memory `SubagentConfig.overrideProfile` chain. See
365
453
  * `executeSubagentSpawn` in tools/subagent/spawn.ts.
454
+ * @legacy
366
455
  */
367
456
  overrideProfile?: string;
368
457
  /**
@@ -371,6 +460,7 @@ export interface ToolContext {
371
460
  * default a spawned subagent's inference profile to the profile the invoking
372
461
  * turn resolved to, so subagents match whatever agent invoked them rather
373
462
  * than always falling back to the static `subagentSpawn` call-site default.
463
+ * @legacy
374
464
  */
375
465
  invokingCallSite?: LLMCallSite;
376
466
  /**
@@ -379,6 +469,7 @@ export interface ToolContext {
379
469
  * Used by host proxies to bind cross-client targeted execution to the same
380
470
  * authenticated user identity. May be undefined for legacy/internal flows
381
471
  * with no resolved actor identity.
472
+ * @legacy
382
473
  */
383
474
  sourceActorPrincipalId?: string;
384
475
  }
@@ -449,6 +540,15 @@ export const ToolDefinitionSchema = z.object({
449
540
  ) => Promise<ToolExecutionResult>
450
541
  >()
451
542
  .optional(),
543
+ /**
544
+ * When true, this tool runs alone in its turn. If the model emits it
545
+ * alongside other tool calls, the agent loop executes only this one and
546
+ * defers the siblings — returning them un-run with a benign notice — so the
547
+ * model incorporates this tool's output before acting on anything else. The
548
+ * `advisor` tool sets this so its guidance lands before the agent commits to
549
+ * a path. Default false (the loop runs sibling calls concurrently as usual).
550
+ */
551
+ exclusive: z.boolean().optional(),
452
552
  });
453
553
 
454
554
  /**
@@ -460,8 +560,14 @@ export const ToolDefinitionSchema = z.object({
460
560
  */
461
561
  export type ToolDefinition = z.infer<typeof ToolDefinitionSchema>;
462
562
 
463
- /** Tool after the loader has derived its name and filled defaults. */
464
- export type Tool = Required<ToolDefinition>;
563
+ /**
564
+ * Tool after the loader has derived its name and filled defaults. Every field
565
+ * is required except `exclusive`, which stays optional — most tools never set
566
+ * it, and the agent loop reads it as `?.exclusive === true`, so forcing every
567
+ * hand-built `Tool` (MCP/meet/test fixtures) to carry it would be noise.
568
+ */
569
+ export type Tool = Required<Omit<ToolDefinition, "exclusive">> &
570
+ Pick<ToolDefinition, "exclusive">;
465
571
 
466
572
  /** The kind of extension that owns a tool. Core tools have no owner. */
467
573
  export type OwnerKind = "skill" | "mcp" | "plugin" | "workspace";