@vellumai/assistant 0.8.5 → 0.8.6

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 (544) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/bunfig.toml +6 -1
  4. package/docs/credential-execution-service.md +6 -6
  5. package/docs/plugins.md +4 -3
  6. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
  7. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
  8. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  9. package/openapi.yaml +1900 -166
  10. package/package.json +1 -1
  11. package/src/__tests__/actor-token-service.test.ts +3 -2
  12. package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
  13. package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
  14. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  15. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  16. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  17. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/approval-routes-http.test.ts +9 -13
  20. package/src/__tests__/assert-not-live-db.ts +79 -0
  21. package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
  22. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  23. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  24. package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
  25. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  26. package/src/__tests__/btw-routes.test.ts +3 -2
  27. package/src/__tests__/call-controller.test.ts +3 -2
  28. package/src/__tests__/channel-approval-routes.test.ts +3 -2
  29. package/src/__tests__/channel-guardian.test.ts +3 -2
  30. package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
  31. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  32. package/src/__tests__/channel-retry-sweep.test.ts +320 -3
  33. package/src/__tests__/checker.test.ts +12 -12
  34. package/src/__tests__/compaction-events.test.ts +1 -0
  35. package/src/__tests__/compaction-trail-store.test.ts +264 -0
  36. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  37. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  38. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  39. package/src/__tests__/computer-use-tools.test.ts +12 -14
  40. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  41. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  42. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  43. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  44. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  45. package/src/__tests__/config-schema.test.ts +10 -10
  46. package/src/__tests__/connection-model-compat.test.ts +83 -0
  47. package/src/__tests__/contacts-tools.test.ts +3 -2
  48. package/src/__tests__/context-token-estimator.test.ts +22 -0
  49. package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
  50. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  52. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
  54. package/src/__tests__/conversation-agent-loop.test.ts +488 -2
  55. package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
  56. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  57. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
  58. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  61. package/src/__tests__/conversation-error.test.ts +30 -0
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -8
  63. package/src/__tests__/conversation-fork-route.test.ts +3 -2
  64. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  65. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  66. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  67. package/src/__tests__/conversation-lifecycle.test.ts +1 -0
  68. package/src/__tests__/conversation-list-source.test.ts +3 -2
  69. package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
  70. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
  71. package/src/__tests__/conversation-pairing.test.ts +53 -0
  72. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
  73. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  74. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
  75. package/src/__tests__/conversation-queue.test.ts +333 -291
  76. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
  77. package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
  78. package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
  79. package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
  80. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  81. package/src/__tests__/conversation-slash-queue.test.ts +84 -32
  82. package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
  83. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
  85. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
  90. package/src/__tests__/conversation-sync-tags.test.ts +128 -12
  91. package/src/__tests__/conversation-title-service.test.ts +1 -0
  92. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
  93. package/src/__tests__/conversation-usage.test.ts +1 -0
  94. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  95. package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
  96. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
  97. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  98. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  99. package/src/__tests__/credential-execution-client.test.ts +72 -1
  100. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  101. package/src/__tests__/credential-health-service.test.ts +252 -3
  102. package/src/__tests__/credential-security-invariants.test.ts +5 -5
  103. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  104. package/src/__tests__/credential-vault.test.ts +5 -5
  105. package/src/__tests__/cross-provider-web-search.test.ts +56 -2
  106. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  107. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  108. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  109. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  110. package/src/__tests__/db-test-helpers.ts +58 -0
  111. package/src/__tests__/disk-pressure-guard.test.ts +58 -41
  112. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  113. package/src/__tests__/disk-pressure-routes.test.ts +0 -33
  114. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  115. package/src/__tests__/dm-persistence.test.ts +26 -40
  116. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  117. package/src/__tests__/document-find-replace.test.ts +3 -2
  118. package/src/__tests__/document-tool-security.test.ts +81 -2
  119. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  120. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  121. package/src/__tests__/encrypted-store.test.ts +11 -9
  122. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  123. package/src/__tests__/filing-service.test.ts +1 -0
  124. package/src/__tests__/first-greeting.test.ts +62 -12
  125. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  126. package/src/__tests__/gemini-provider.test.ts +26 -0
  127. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  128. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  129. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  130. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
  131. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  132. package/src/__tests__/heartbeat-service.test.ts +1 -0
  133. package/src/__tests__/helpers/mock-logger.ts +26 -0
  134. package/src/__tests__/host-bash-routes.test.ts +1 -0
  135. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  136. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  137. package/src/__tests__/host-shell-tool.test.ts +5 -4
  138. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  139. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  140. package/src/__tests__/http-user-message-parity.test.ts +29 -7
  141. package/src/__tests__/identity-intro-cache.test.ts +133 -22
  142. package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
  143. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  144. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  145. package/src/__tests__/injector-disk-pressure.test.ts +3 -17
  146. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  147. package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
  148. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  149. package/src/__tests__/llm-resolver.test.ts +331 -0
  150. package/src/__tests__/llm-schema.test.ts +1 -1
  151. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  152. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  153. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  154. package/src/__tests__/messaging-send-tool.test.ts +1 -0
  155. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  156. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  157. package/src/__tests__/model-intents.test.ts +3 -3
  158. package/src/__tests__/native-web-search.test.ts +30 -2
  159. package/src/__tests__/notification-deep-link.test.ts +62 -0
  160. package/src/__tests__/oauth-commands-routes.test.ts +37 -0
  161. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  162. package/src/__tests__/oauth-store.test.ts +3 -2
  163. package/src/__tests__/onboarding-template-contract.test.ts +3 -2
  164. package/src/__tests__/openai-provider.test.ts +8 -9
  165. package/src/__tests__/openai-responses-provider.test.ts +70 -10
  166. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  167. package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
  168. package/src/__tests__/persistence-pipeline.test.ts +139 -1
  169. package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
  170. package/src/__tests__/plugin-bootstrap.test.ts +9 -11
  171. package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
  172. package/src/__tests__/process-message-background-slack.test.ts +21 -16
  173. package/src/__tests__/process-message-display-content.test.ts +19 -22
  174. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  175. package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
  176. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  177. package/src/__tests__/recording-handler.test.ts +1 -0
  178. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  179. package/src/__tests__/registry.test.ts +82 -76
  180. package/src/__tests__/relay-server.test.ts +10 -10
  181. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  182. package/src/__tests__/schedule-store.test.ts +16 -1
  183. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  184. package/src/__tests__/secret-ingress-http.test.ts +5 -1
  185. package/src/__tests__/secure-keys.test.ts +3 -3
  186. package/src/__tests__/send-endpoint-busy.test.ts +81 -42
  187. package/src/__tests__/server-history-render.test.ts +4 -1
  188. package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
  189. package/src/__tests__/skill-feature-flags.test.ts +14 -16
  190. package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
  191. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  192. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  193. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  194. package/src/__tests__/slack-channel-config.test.ts +3 -3
  195. package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
  196. package/src/__tests__/subagent-disposal.test.ts +27 -8
  197. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  198. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  199. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  200. package/src/__tests__/subagent-notify-parent.test.ts +5 -4
  201. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  202. package/src/__tests__/subagent-tools.test.ts +2 -1
  203. package/src/__tests__/suggestion-routes.test.ts +1 -0
  204. package/src/__tests__/system-prompt.test.ts +38 -0
  205. package/src/__tests__/test-preload-verifier.ts +68 -0
  206. package/src/__tests__/test-preload.ts +32 -39
  207. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  208. package/src/__tests__/tool-executor.test.ts +55 -10
  209. package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
  210. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  211. package/src/__tests__/twilio-routes.test.ts +3 -2
  212. package/src/__tests__/validate-input.test.ts +381 -0
  213. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  214. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
  215. package/src/__tests__/voice-session-bridge.test.ts +37 -28
  216. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  217. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  218. package/src/acp/session-manager.ts +5 -6
  219. package/src/agent/loop.ts +80 -0
  220. package/src/api/README.md +124 -2
  221. package/src/api/constants/call-sites.ts +27 -0
  222. package/src/api/events/assistant-outbound-attachment.ts +51 -0
  223. package/src/api/events/assistant-text-delta.ts +32 -0
  224. package/src/api/events/assistant-turn-start.ts +33 -0
  225. package/src/api/events/document-comment-created.ts +48 -0
  226. package/src/api/events/document-comment-deleted.ts +24 -0
  227. package/src/api/events/document-comment-reopened.ts +25 -0
  228. package/src/api/events/document-comment-resolved.ts +27 -0
  229. package/src/api/events/generation-cancelled.ts +24 -0
  230. package/src/api/events/generation-handoff.ts +41 -0
  231. package/src/api/events/message-complete.ts +42 -0
  232. package/src/api/events/open-url.ts +30 -0
  233. package/src/{events → api/events}/relationship-state-updated.ts +3 -3
  234. package/src/api/events/tool-use-start.ts +32 -0
  235. package/src/api/index.ts +128 -3
  236. package/src/api/responses/llm-context-response.ts +39 -0
  237. package/src/api/responses/llm-request-log-entry.ts +93 -0
  238. package/src/api/responses/memory-recall-log.ts +65 -0
  239. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  240. package/src/background-wake/background-wake-routes.test.ts +687 -52
  241. package/src/background-wake/platform-client.test.ts +308 -0
  242. package/src/background-wake/platform-client.ts +167 -0
  243. package/src/background-wake/publisher.ts +91 -0
  244. package/src/background-wake/runtime-registry.ts +2 -2
  245. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  246. package/src/calls/guardian-dispatch.ts +1 -0
  247. package/src/calls/voice-session-bridge.ts +4 -4
  248. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  249. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  250. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  251. package/src/cli/commands/channels/index.ts +229 -0
  252. package/src/cli/commands/memory-v3-render.ts +147 -0
  253. package/src/cli/commands/memory-v3.ts +255 -4
  254. package/src/cli/commands/notifications.ts +365 -55
  255. package/src/cli/lib/open-browser.ts +7 -2
  256. package/src/cli/program.ts +2 -0
  257. package/src/config/assistant-feature-flags.ts +23 -42
  258. package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
  259. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  260. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  261. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  262. package/src/config/call-site-defaults.ts +1 -1
  263. package/src/config/feature-flag-cache.ts +86 -0
  264. package/src/config/feature-flag-registry.json +17 -17
  265. package/src/config/llm-context-resolution.ts +10 -1
  266. package/src/config/llm-resolver.ts +121 -15
  267. package/src/config/loader.ts +4 -5
  268. package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
  269. package/src/config/schemas/heartbeat.ts +1 -1
  270. package/src/config/schemas/llm.ts +90 -1
  271. package/src/config/schemas/memory-v2.ts +26 -0
  272. package/src/config/schemas/services.ts +6 -2
  273. package/src/config/seed-inference-profiles.ts +36 -16
  274. package/src/context/token-estimator.ts +10 -5
  275. package/src/credential-execution/executable-discovery.ts +40 -0
  276. package/src/credential-execution/process-manager.ts +6 -2
  277. package/src/credential-health/credential-health-service.ts +125 -40
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  279. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
  280. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  281. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  282. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  283. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  284. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  285. package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
  286. package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
  287. package/src/daemon/conversation-agent-loop.ts +132 -28
  288. package/src/daemon/conversation-error.ts +33 -5
  289. package/src/daemon/conversation-messaging.ts +84 -43
  290. package/src/daemon/conversation-process.ts +74 -37
  291. package/src/daemon/conversation-runtime-assembly.ts +29 -9
  292. package/src/daemon/conversation-skill-tools.ts +14 -30
  293. package/src/daemon/conversation-surfaces.ts +69 -34
  294. package/src/daemon/conversation-tool-setup.ts +33 -48
  295. package/src/daemon/conversation.ts +26 -46
  296. package/src/daemon/daemon-control.ts +1 -1
  297. package/src/daemon/daemon-skill-host.ts +9 -2
  298. package/src/daemon/disk-pressure-guard.ts +27 -29
  299. package/src/daemon/first-greeting.ts +31 -13
  300. package/src/daemon/handlers/shared.ts +6 -1
  301. package/src/daemon/lifecycle.ts +12 -12
  302. package/src/daemon/mcp-reload-service.ts +1 -1
  303. package/src/daemon/meet-manifest-loader.ts +10 -17
  304. package/src/daemon/message-types/conversations.ts +20 -22
  305. package/src/daemon/message-types/document-comments.ts +8 -44
  306. package/src/daemon/message-types/home.ts +2 -2
  307. package/src/daemon/message-types/integrations.ts +2 -7
  308. package/src/daemon/message-types/messages.ts +23 -38
  309. package/src/daemon/message-types/subagents.ts +6 -0
  310. package/src/daemon/process-message.ts +9 -9
  311. package/src/daemon/providers-setup.ts +1 -1
  312. package/src/daemon/server.ts +16 -0
  313. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  314. package/src/daemon/tool-setup-types.ts +0 -6
  315. package/src/daemon/wake-target-adapter.ts +10 -0
  316. package/src/documents/document-store.ts +38 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  318. package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
  319. package/src/heartbeat/heartbeat-service.ts +63 -0
  320. package/src/home/__tests__/feed-writer.test.ts +161 -0
  321. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  322. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  323. package/src/home/feed-writer.ts +146 -7
  324. package/src/home/suggested-prompts.ts +27 -145
  325. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  326. package/src/ipc/gateway-client.test.ts +4 -1
  327. package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
  328. package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
  329. package/src/ipc/skill-routes/memory.ts +4 -3
  330. package/src/ipc/skill-routes/registries.ts +28 -29
  331. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  332. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  333. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  334. package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
  335. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  336. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  337. package/src/memory/conversation-attention-store.ts +17 -3
  338. package/src/memory/conversation-crud.ts +352 -112
  339. package/src/memory/db-connection.ts +29 -19
  340. package/src/memory/db-init.ts +4 -0
  341. package/src/memory/db-singleton.ts +77 -0
  342. package/src/memory/delivery-channels.ts +82 -0
  343. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  344. package/src/memory/graph/retriever.test.ts +3 -3
  345. package/src/memory/job-handlers/embedding.test.ts +3 -2
  346. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  347. package/src/memory/jobs-worker.ts +12 -1
  348. package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
  349. package/src/memory/llm-request-log-source-local.ts +24 -0
  350. package/src/memory/llm-request-log-source.ts +31 -0
  351. package/src/memory/llm-request-log-store.ts +188 -3
  352. package/src/memory/memory-v2-activation-log-store.ts +95 -1
  353. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  354. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  355. package/src/memory/migrations/index.ts +2 -0
  356. package/src/memory/schema/conversations.ts +9 -1
  357. package/src/memory/schema/inference.ts +0 -1
  358. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  359. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  362. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
  363. package/src/memory/v2/harness/metrics.ts +5 -1
  364. package/src/memory/v2/harness/replay-input.ts +19 -3
  365. package/src/memory/v2/harness/runner.ts +6 -0
  366. package/src/memory/v2/harness/trace.ts +6 -0
  367. package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
  368. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
  369. package/src/memory/v3/__tests__/edges.test.ts +144 -1
  370. package/src/memory/v3/__tests__/filter.test.ts +48 -0
  371. package/src/memory/v3/__tests__/gate.test.ts +96 -33
  372. package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
  373. package/src/memory/v3/__tests__/loop.test.ts +250 -5
  374. package/src/memory/v3/__tests__/scouts.test.ts +49 -0
  375. package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
  376. package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
  377. package/src/memory/v3/__tests__/traversal.test.ts +39 -0
  378. package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
  379. package/src/memory/v3/__tests__/validate.test.ts +32 -0
  380. package/src/memory/v3/coretrieval-seed.ts +240 -0
  381. package/src/memory/v3/edges.ts +58 -21
  382. package/src/memory/v3/filter.ts +27 -22
  383. package/src/memory/v3/gate.ts +51 -36
  384. package/src/memory/v3/index-composition.ts +18 -5
  385. package/src/memory/v3/loop.ts +65 -17
  386. package/src/memory/v3/scouts.ts +15 -4
  387. package/src/memory/v3/shadow-diff.ts +287 -0
  388. package/src/memory/v3/shadow-middleware.ts +44 -2
  389. package/src/memory/v3/traversal.ts +6 -1
  390. package/src/memory/v3/tree-walk.ts +6 -1
  391. package/src/memory/v3/validate.ts +56 -33
  392. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  393. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  394. package/src/notifications/adapters/slack.ts +45 -11
  395. package/src/notifications/broadcaster.ts +114 -63
  396. package/src/notifications/conversation-pairing.ts +23 -3
  397. package/src/notifications/decisions-store.ts +32 -1
  398. package/src/notifications/deliveries-store.ts +45 -0
  399. package/src/notifications/edit-notification.ts +201 -0
  400. package/src/notifications/emit-signal.ts +11 -1
  401. package/src/notifications/signal.ts +10 -0
  402. package/src/notifications/types.ts +37 -0
  403. package/src/oauth/byo-connection.test.ts +67 -3
  404. package/src/oauth/byo-connection.ts +32 -5
  405. package/src/oauth/connect-orchestrator.ts +9 -0
  406. package/src/oauth/connection-resolver.test.ts +76 -0
  407. package/src/oauth/connection-resolver.ts +49 -10
  408. package/src/oauth/manual-token-connection.ts +51 -3
  409. package/src/oauth/seed-providers.ts +3 -0
  410. package/src/permissions/approval-policy.test.ts +19 -5
  411. package/src/permissions/approval-policy.ts +14 -3
  412. package/src/permissions/checker.ts +21 -8
  413. package/src/platform/client.test.ts +24 -1
  414. package/src/platform/client.ts +8 -0
  415. package/src/platform/feature-gate.ts +15 -0
  416. package/src/plugins/defaults/injectors.ts +2 -8
  417. package/src/plugins/defaults/persistence.ts +25 -6
  418. package/src/plugins/types.ts +57 -13
  419. package/src/proactive-artifact/job.test.ts +1 -0
  420. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  421. package/src/prompts/system-prompt.ts +38 -40
  422. package/src/prompts/template-detection.ts +10 -4
  423. package/src/prompts/templates/BOOTSTRAP.md +7 -11
  424. package/src/prompts/templates/IDENTITY.md +0 -2
  425. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  426. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  427. package/src/providers/call-site-routing.ts +33 -9
  428. package/src/providers/connection-model-compat.ts +23 -0
  429. package/src/providers/connection-resolution.ts +39 -20
  430. package/src/providers/fireworks/client.ts +1 -0
  431. package/src/providers/gemini/client.ts +24 -3
  432. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  433. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  434. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  435. package/src/providers/inference/auth.ts +0 -8
  436. package/src/providers/inference/connections.ts +3 -66
  437. package/src/providers/inference/resolve-auth.ts +2 -3
  438. package/src/providers/model-catalog.ts +35 -1
  439. package/src/providers/model-intents.ts +3 -3
  440. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  441. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
  442. package/src/providers/openai/chat-completions-provider.ts +110 -12
  443. package/src/providers/openai/codex-models.ts +2 -0
  444. package/src/providers/openai/responses-provider.ts +53 -53
  445. package/src/providers/openrouter/client.ts +13 -8
  446. package/src/providers/provider-send-message.ts +18 -9
  447. package/src/providers/registry.ts +48 -8
  448. package/src/providers/retry.ts +16 -4
  449. package/src/providers/search-provider-catalog.ts +17 -9
  450. package/src/providers/types.ts +9 -0
  451. package/src/runtime/__tests__/agent-wake.test.ts +1 -0
  452. package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
  453. package/src/runtime/access-request-helper.ts +1 -0
  454. package/src/runtime/auth/route-policy.ts +10 -0
  455. package/src/runtime/channel-readiness-service.ts +68 -0
  456. package/src/runtime/channel-reply-delivery.ts +23 -0
  457. package/src/runtime/channel-retry-sweep.ts +47 -14
  458. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  459. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  460. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  461. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
  462. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  463. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  464. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  465. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
  466. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
  467. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
  468. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  469. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  470. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  471. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  472. package/src/runtime/routes/app-management-routes.ts +111 -4
  473. package/src/runtime/routes/background-wake-routes.ts +188 -20
  474. package/src/runtime/routes/btw-routes.ts +4 -4
  475. package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
  476. package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
  477. package/src/runtime/routes/conversation-list-routes.ts +147 -0
  478. package/src/runtime/routes/conversation-management-routes.ts +39 -14
  479. package/src/runtime/routes/conversation-query-routes.ts +60 -10
  480. package/src/runtime/routes/conversation-routes.ts +186 -140
  481. package/src/runtime/routes/conversations-import-routes.ts +19 -6
  482. package/src/runtime/routes/documents-routes.ts +10 -1
  483. package/src/runtime/routes/group-routes.ts +11 -0
  484. package/src/runtime/routes/home-feed-routes.ts +129 -0
  485. package/src/runtime/routes/identity-intro-cache.ts +61 -16
  486. package/src/runtime/routes/identity-routes.ts +30 -9
  487. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
  488. package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
  489. package/src/runtime/routes/index.ts +2 -0
  490. package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
  491. package/src/runtime/routes/integrations/vercel.ts +15 -0
  492. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  493. package/src/runtime/routes/memory-v3-routes.ts +160 -2
  494. package/src/runtime/routes/migration-routes.ts +20 -13
  495. package/src/runtime/routes/notification-routes.ts +63 -1
  496. package/src/runtime/routes/oauth-commands-routes.ts +6 -1
  497. package/src/runtime/routes/surface-action-routes.ts +1 -38
  498. package/src/runtime/routes/surface-content-routes.ts +12 -5
  499. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  500. package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
  501. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
  502. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  503. package/src/runtime/sync/resource-sync-events.ts +1 -1
  504. package/src/runtime/tool-grant-request-helper.ts +1 -0
  505. package/src/schedule/schedule-store.ts +8 -1
  506. package/src/schedule/scheduler.ts +111 -15
  507. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  508. package/src/security/encrypted-store.ts +7 -16
  509. package/src/security/store-path-override.ts +61 -0
  510. package/src/signals/user-message.ts +5 -8
  511. package/src/skills/validate-input.ts +177 -0
  512. package/src/subagent/manager.ts +13 -13
  513. package/src/subagent/types.ts +6 -0
  514. package/src/tasks/tool-sanitizer.ts +2 -2
  515. package/src/tools/apps/definitions.ts +35 -21
  516. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  517. package/src/tools/computer-use/definitions.ts +268 -266
  518. package/src/tools/document/document-tool.ts +131 -8
  519. package/src/tools/execution-target.ts +2 -5
  520. package/src/tools/executor.ts +18 -55
  521. package/src/tools/host-filesystem/edit.test.ts +1 -0
  522. package/src/tools/host-filesystem/read.test.ts +1 -0
  523. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  524. package/src/tools/host-filesystem/write.test.ts +1 -0
  525. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  526. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  527. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  528. package/src/tools/network/managed-search-proxy.ts +183 -0
  529. package/src/tools/network/web-search.ts +199 -44
  530. package/src/tools/policy-context.ts +3 -1
  531. package/src/tools/registry.ts +146 -103
  532. package/src/tools/schedule/create.ts +1 -1
  533. package/src/tools/skills/skill-tool-factory.ts +17 -36
  534. package/src/tools/subagent/spawn.ts +3 -0
  535. package/src/tools/tool-approval-handler.ts +10 -4
  536. package/src/tools/tool-name-aliases.ts +72 -14
  537. package/src/tools/types.ts +17 -15
  538. package/src/tools/ui-surface/definitions.ts +98 -86
  539. package/src/types/onboarding-context.ts +6 -0
  540. package/src/usage/attribution.ts +32 -1
  541. package/src/util/browser.ts +7 -2
  542. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  543. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  544. package/src/workspace/migrations/registry.ts +4 -0
@@ -2,7 +2,6 @@ import type { ToolDefinition } from "../providers/types.js";
2
2
  import { getLogger } from "../util/logger.js";
3
3
  import { coreAppProxyTools } from "./apps/definitions.js";
4
4
  import { registerAppTools } from "./apps/registry.js";
5
- import { allComputerUseTools } from "./computer-use/definitions.js";
6
5
  import { hostFileEditTool } from "./host-filesystem/edit.js";
7
6
  import { hostFileReadTool } from "./host-filesystem/read.js";
8
7
  import { hostFileTransferTool } from "./host-filesystem/transfer.js";
@@ -10,7 +9,7 @@ import { hostFileWriteTool } from "./host-filesystem/write.js";
10
9
  import { hostShellTool } from "./host-terminal/host-shell.js";
11
10
  import { toProviderSafeToolName } from "./provider-tool-name.js";
12
11
  import { registerSystemTools } from "./system/register.js";
13
- import type { LoadedTool, Tool } from "./types.js";
12
+ import type { LoadedTool, OwnerInfo, Tool } from "./types.js";
14
13
  import { allUiSurfaceTools } from "./ui-surface/definitions.js";
15
14
  import { registerUiSurfaceTools } from "./ui-surface/registry.js";
16
15
 
@@ -18,6 +17,15 @@ const log = getLogger("tool-registry");
18
17
 
19
18
  const tools = new Map<string, Tool>();
20
19
 
20
+ // Authoritative map of tool ownership, keyed by tool name. Populated by the
21
+ // `register*` functions and read by `getToolOwner()`. Lives on the registry
22
+ // (not on the `Tool` object) so callers cannot spoof ownership by writing a
23
+ // field on the manifest — the only way to claim a tool is to go through a
24
+ // `register*` function, which stamps the owner from its arguments. Core
25
+ // tools intentionally have no entry here; `getToolOwner` returns `undefined`
26
+ // for them.
27
+ const ownersByName = new Map<string, OwnerInfo>();
28
+
21
29
  // ── External tool registry ───────────────────────────────────────────
22
30
  // Skills register their tools here at initialization time so the tool
23
31
  // manifest can include them without importing from `../skills/`.
@@ -28,7 +36,10 @@ const tools = new Map<string, Tool>();
28
36
  // feature-flag check until after the daemon has run
29
37
  // `mergeDefaultWorkspaceConfig()`, so skills see the merged config
30
38
  // instead of forcing an early `loadConfig()` against unmerged defaults.
31
- const externalToolProviders: Array<() => Tool[]> = [];
39
+ const externalToolProviders: Array<{
40
+ owner: OwnerInfo;
41
+ provider: () => Tool[];
42
+ }> = [];
32
43
 
33
44
  /**
34
45
  * Register tools provided by an external skill. Called during skill
@@ -44,20 +55,30 @@ const externalToolProviders: Array<() => Tool[]> = [];
44
55
  * dependency: skills/load.ts → … → meet-join/register.ts → tool-manifest.ts
45
56
  * → skills/load.ts. Keeping it here lets external skill bootstraps import
46
57
  * from registry.ts, which is already a leaf in the dependency graph.
58
+ *
59
+ * `owner` records which extension produced these tools — typed
60
+ * {@link OwnerInfo} so ownership flows through `ownersByName` at
61
+ * `initializeTools()` time, the same way `register*` registers it for
62
+ * IPC-loaded tools. Eager (boot-time) skill bootstraps go through this
63
+ * path rather than `registerSkillTools`, so this is where their owner
64
+ * lookup gets established.
47
65
  */
48
66
  export function registerExternalTools(
67
+ owner: OwnerInfo,
49
68
  toolsOrProvider: Tool[] | (() => Tool[]),
50
69
  ): void {
51
70
  const provider =
52
71
  typeof toolsOrProvider === "function"
53
72
  ? toolsOrProvider
54
73
  : () => toolsOrProvider;
55
- externalToolProviders.push(provider);
74
+ externalToolProviders.push({ owner, provider });
56
75
  }
57
76
 
58
- /** Return all externally registered tools. */
59
- function getExternalTools(): Tool[] {
60
- return externalToolProviders.flatMap((provider) => provider());
77
+ /** Return all externally registered tools paired with their owners. */
78
+ function getExternalTools(): Array<{ owner: OwnerInfo; tool: Tool }> {
79
+ return externalToolProviders.flatMap(({ owner, provider }) =>
80
+ provider().map((tool) => ({ owner, tool })),
81
+ );
61
82
  }
62
83
 
63
84
  // Snapshot of core tools captured after initializeTools() completes.
@@ -76,6 +97,27 @@ const skillRefCount = new Map<string, number>();
76
97
  // separate and covers the case of two extensions choosing the same tool name.
77
98
  const pluginRefCount = new Map<string, number>();
78
99
 
100
+ /**
101
+ * Format an owner for log messages and error strings. Returns a stable
102
+ * human-readable description (e.g. `skill "deploy"`, `plugin "weather"`,
103
+ * `MCP server "github"`). When an owner is missing (core tool) or has an
104
+ * unrecognized kind, returns a fallback string so log/error sites never
105
+ * produce `undefined` interpolations.
106
+ */
107
+ function describeOwner(owner: OwnerInfo | undefined): string {
108
+ if (!owner) return "core tool";
109
+ switch (owner.kind) {
110
+ case "skill":
111
+ return `skill "${owner.id}"`;
112
+ case "plugin":
113
+ return `plugin "${owner.id}"`;
114
+ case "mcp":
115
+ return `MCP server "${owner.id}"`;
116
+ default:
117
+ return `${(owner as OwnerInfo).kind}-origin tool`;
118
+ }
119
+ }
120
+
79
121
  function withProviderSafeToolName(tool: Tool): Tool {
80
122
  const safeName = toProviderSafeToolName(tool.name);
81
123
  if (safeName === tool.name) {
@@ -107,83 +149,89 @@ export function getAllTools(): Tool[] {
107
149
  }
108
150
 
109
151
  /**
110
- * Register multiple skill-origin tools at once.
152
+ * Return the recorded owner for a tool, or `undefined` if the tool is
153
+ * core-origin (no owner) or unknown. Consumers that need to gate behavior on
154
+ * which extension contributed a tool (permissions checker, approval-handler
155
+ * load hints, conversation-skill-tools projection) call this rather than
156
+ * reading owner off the `Tool` object — the registry is the single source of
157
+ * truth for ownership.
158
+ */
159
+ export function getToolOwner(name: string): OwnerInfo | undefined {
160
+ return ownersByName.get(name);
161
+ }
162
+
163
+ /**
164
+ * Register multiple skill-origin tools owned by `skillId`.
165
+ *
111
166
  * Skips any tool whose name collides with a core tool (logs a warning instead
112
167
  * of throwing so the remaining tools in the batch still get registered).
113
168
  * Throws if a tool name collides with a skill tool owned by a different skill.
114
- * Allows replacement when the incoming tool has the same ownerSkillId as the existing one,
115
- * which supports hot-reloading a skill without tearing down first.
169
+ * Allows replacement when the incoming tool has the same skill owner id as
170
+ * the existing one, which supports hot-reloading a skill without tearing
171
+ * down first.
172
+ *
173
+ * Ownership is recorded in {@link ownersByName} keyed by tool name; the
174
+ * `Tool` object itself carries no owner metadata, so callers cannot spoof
175
+ * ownership by writing fields on the manifest.
116
176
  */
117
- export function registerSkillTools(newTools: Tool[]): Tool[] {
177
+ export function registerSkillTools(skillId: string, newTools: Tool[]): Tool[] {
118
178
  // Filter out tools that collide with core tools, and validate the rest.
119
179
  const accepted: Tool[] = [];
120
180
  for (const tool of newTools) {
121
181
  const existing = tools.get(tool.name);
122
182
  if (existing) {
123
- const existingIsCore = existing.origin === "core" || !existing.origin;
183
+ const existingIsCore = !ownersByName.has(tool.name);
124
184
  if (existingIsCore) {
125
185
  log.warn(
126
- { toolName: tool.name, skillId: tool.ownerSkillId },
127
- `Skill "${tool.ownerSkillId}" tried to register tool "${tool.name}" which conflicts with a core tool. Skipping.`,
186
+ { toolName: tool.name, ownerSkillId: skillId },
187
+ `Skill "${skillId}" tried to register tool "${tool.name}" which conflicts with a core tool. Skipping.`,
128
188
  );
129
189
  continue;
130
190
  }
131
- // Existing is from a different origin (plugin/mcp) or a different
191
+ // Existing is from a different owner (plugin/mcp) or a different
132
192
  // skill — skill tools can only replace themselves (hot-reload).
133
- if (
134
- existing.origin !== "skill" ||
135
- existing.ownerSkillId !== tool.ownerSkillId
136
- ) {
137
- const owner =
138
- existing.origin === "skill"
139
- ? `skill "${existing.ownerSkillId}"`
140
- : existing.origin === "plugin"
141
- ? `plugin "${existing.ownerPluginId}"`
142
- : existing.origin === "mcp"
143
- ? `MCP server "${existing.ownerMcpServerId}"`
144
- : `${existing.origin ?? "unknown"}-origin tool`;
193
+ const existingOwner = ownersByName.get(tool.name);
194
+ const existingSkillId =
195
+ existingOwner?.kind === "skill" ? existingOwner.id : undefined;
196
+ if (existingOwner?.kind !== "skill" || existingSkillId !== skillId) {
145
197
  throw new Error(
146
- `Skill tool "${tool.name}" is already registered by ${owner}`,
198
+ `Skill tool "${tool.name}" is already registered by ${describeOwner(existingOwner)}`,
147
199
  );
148
200
  }
149
201
  }
150
202
  accepted.push(tool);
151
203
  }
152
204
 
153
- // Collect unique skill IDs from the batch to bump ref counts
154
- const skillIds = new Set<string>();
155
205
  for (const tool of accepted) {
156
206
  tools.set(tool.name, tool);
157
- if (tool.ownerSkillId) skillIds.add(tool.ownerSkillId);
207
+ ownersByName.set(tool.name, { kind: "skill", id: skillId });
158
208
  log.info(
159
- { name: tool.name, ownerSkillId: tool.ownerSkillId },
209
+ { name: tool.name, ownerSkillId: skillId },
160
210
  "Skill tool registered",
161
211
  );
162
212
  }
163
213
 
164
- for (const id of skillIds) {
165
- skillRefCount.set(id, (skillRefCount.get(id) ?? 0) + 1);
214
+ if (accepted.length > 0) {
215
+ skillRefCount.set(skillId, (skillRefCount.get(skillId) ?? 0) + 1);
166
216
  }
167
217
 
168
218
  return accepted;
169
219
  }
170
220
 
171
221
  /**
172
- * Register tools contributed by a plugin. Stamps `origin: "plugin"` and
173
- * `ownerPluginId: pluginName` on every incoming tool so plugin ownership is
174
- * tracked in a namespace disjoint from skill tools if a plugin's
175
- * `manifest.name` happens to match a skill id, the two do not share refcount
176
- * state or conflict-detection paths.
222
+ * Register tools contributed by the plugin named `pluginName`. Records the
223
+ * plugin owner in {@link ownersByName} keyed by tool name ownership lives
224
+ * on the registry, never on the `Tool` object itself, so the bootstrap
225
+ * cannot be spoofed into claiming tools on behalf of an unrelated extension
226
+ * by forging fields on the manifest. Plugin ownership is tracked in a
227
+ * namespace disjoint from skill tools: if a plugin's `manifest.name`
228
+ * happens to match a skill id, the two do not share refcount state or
229
+ * conflict-detection paths.
177
230
  *
178
231
  * Conflict handling mirrors {@link registerSkillTools}: collisions with core
179
232
  * tools log a warning and skip; collisions with tools owned by a different
180
233
  * plugin, skill, or MCP server throw; re-registering the same plugin's own
181
234
  * tool (hot reload) is allowed.
182
- *
183
- * The stamp is authoritative: any pre-existing `origin` / `ownerPluginId` /
184
- * `ownerSkillId` / `ownerMcpServerId` fields on the incoming tools are
185
- * overwritten so the bootstrap cannot be spoofed into claiming tools on
186
- * behalf of an unrelated extension.
187
235
  */
188
236
  export function registerPluginTools(
189
237
  pluginName: string,
@@ -193,12 +241,6 @@ export function registerPluginTools(
193
241
  const tool: Tool = {
194
242
  ...pluginTool,
195
243
  category: "plugin",
196
- origin: "plugin" as const,
197
- ownerPluginId: pluginName,
198
- ownerSkillId: undefined,
199
- ownerMcpServerId: undefined,
200
- ownerSkillBundled: undefined,
201
- ownerSkillVersionHash: undefined,
202
244
  };
203
245
  return withProviderSafeToolName(tool);
204
246
  });
@@ -207,25 +249,26 @@ export function registerPluginTools(
207
249
  for (const tool of stamped) {
208
250
  const existing = tools.get(tool.name);
209
251
  if (existing) {
210
- const existingIsCore = existing.origin === "core" || !existing.origin;
252
+ const existingIsCore = !ownersByName.has(tool.name);
211
253
  if (existingIsCore) {
212
254
  log.warn(
213
- { toolName: tool.name, pluginName },
255
+ { toolName: tool.name, ownerPluginId: pluginName },
214
256
  `Plugin "${pluginName}" tried to register tool "${tool.name}" which conflicts with a core tool. Skipping.`,
215
257
  );
216
258
  continue;
217
259
  }
218
- if (existing.origin === "plugin") {
219
- if (existing.ownerPluginId !== pluginName) {
260
+ const existingOwner = ownersByName.get(tool.name);
261
+ if (existingOwner?.kind === "plugin") {
262
+ if (existingOwner.id !== pluginName) {
220
263
  throw new Error(
221
- `Plugin tool "${tool.name}" is already registered by plugin "${existing.ownerPluginId}"`,
264
+ `Plugin tool "${tool.name}" is already registered by plugin "${existingOwner.id}"`,
222
265
  );
223
266
  }
224
267
  // Same plugin re-registering its own tool (hot reload) — allow.
225
268
  } else {
226
269
  // Conflict with a skill or MCP-owned tool.
227
270
  throw new Error(
228
- `Plugin "${pluginName}" tried to register tool "${tool.name}" which conflicts with a ${existing.origin}-origin tool`,
271
+ `Plugin "${pluginName}" tried to register tool "${tool.name}" which conflicts with ${describeOwner(existingOwner)}`,
229
272
  );
230
273
  }
231
274
  }
@@ -234,8 +277,9 @@ export function registerPluginTools(
234
277
 
235
278
  for (const tool of accepted) {
236
279
  tools.set(tool.name, tool);
280
+ ownersByName.set(tool.name, { kind: "plugin", id: pluginName });
237
281
  log.info(
238
- { name: tool.name, ownerPluginId: tool.ownerPluginId },
282
+ { name: tool.name, ownerPluginId: pluginName },
239
283
  "Plugin tool registered",
240
284
  );
241
285
  }
@@ -264,10 +308,11 @@ export function unregisterPluginTools(pluginName: string): void {
264
308
  }
265
309
 
266
310
  pluginRefCount.delete(pluginName);
267
- for (const [name, tool] of tools) {
268
- if (tool.origin === "plugin" && tool.ownerPluginId === pluginName) {
311
+ for (const [name, owner] of ownersByName) {
312
+ if (owner.kind === "plugin" && owner.id === pluginName) {
269
313
  tools.delete(name);
270
- log.info({ name, pluginName }, "Plugin tool unregistered");
314
+ ownersByName.delete(name);
315
+ log.info({ name, ownerPluginId: pluginName }, "Plugin tool unregistered");
271
316
  }
272
317
  }
273
318
  }
@@ -296,60 +341,52 @@ export function unregisterSkillTools(skillId: string): void {
296
341
 
297
342
  // Last reference - actually remove the tools
298
343
  skillRefCount.delete(skillId);
299
- for (const [name, tool] of tools) {
300
- if (tool.origin === "skill" && tool.ownerSkillId === skillId) {
344
+ for (const [name, owner] of ownersByName) {
345
+ if (owner.kind === "skill" && owner.id === skillId) {
301
346
  tools.delete(name);
302
- log.info({ name, skillId }, "Skill tool unregistered");
347
+ ownersByName.delete(name);
348
+ log.info({ name, ownerSkillId: skillId }, "Skill tool unregistered");
303
349
  }
304
350
  }
305
351
  }
306
352
 
307
353
  /**
308
- * Register multiple MCP-origin tools at once.
354
+ * Register multiple MCP-origin tools owned by the MCP server `serverId`.
355
+ *
309
356
  * Skips any tool whose name collides with a core tool (logs a warning).
310
357
  * Throws if a tool name collides with a tool owned by a different MCP server.
358
+ *
359
+ * Ownership is recorded in {@link ownersByName} keyed by tool name; the
360
+ * `Tool` object itself carries no owner metadata.
311
361
  */
312
- export function registerMcpTools(newTools: Tool[]): Tool[] {
362
+ export function registerMcpTools(serverId: string, newTools: Tool[]): Tool[] {
313
363
  const accepted: Tool[] = [];
314
364
  for (const tool of newTools) {
315
365
  const existing = tools.get(tool.name);
316
366
  if (existing) {
317
- const existingIsCore = existing.origin === "core" || !existing.origin;
367
+ const existingIsCore = !ownersByName.has(tool.name);
318
368
  if (existingIsCore) {
319
369
  log.warn(
320
- { toolName: tool.name, serverId: tool.ownerMcpServerId },
321
- `MCP server "${tool.ownerMcpServerId}" tried to register tool "${tool.name}" which conflicts with a core tool. Skipping.`,
370
+ { toolName: tool.name, ownerMcpServerId: serverId },
371
+ `MCP server "${serverId}" tried to register tool "${tool.name}" which conflicts with a core tool. Skipping.`,
322
372
  );
323
373
  continue;
324
374
  }
325
- if (existing.origin === "skill") {
375
+ const existingOwner = ownersByName.get(tool.name);
376
+ if (existingOwner?.kind === "skill" || existingOwner?.kind === "plugin") {
326
377
  log.warn(
327
378
  {
328
379
  toolName: tool.name,
329
- serverId: tool.ownerMcpServerId,
330
- skillId: existing.ownerSkillId,
380
+ ownerMcpServerId: serverId,
381
+ existingOwner,
331
382
  },
332
- `MCP server "${tool.ownerMcpServerId}" tried to register tool "${tool.name}" which conflicts with skill tool from "${existing.ownerSkillId}". Skipping.`,
383
+ `MCP server "${serverId}" tried to register tool "${tool.name}" which conflicts with ${describeOwner(existingOwner)}. Skipping.`,
333
384
  );
334
385
  continue;
335
386
  }
336
- if (existing.origin === "plugin") {
337
- log.warn(
338
- {
339
- toolName: tool.name,
340
- serverId: tool.ownerMcpServerId,
341
- pluginName: existing.ownerPluginId,
342
- },
343
- `MCP server "${tool.ownerMcpServerId}" tried to register tool "${tool.name}" which conflicts with plugin tool from "${existing.ownerPluginId}". Skipping.`,
344
- );
345
- continue;
346
- }
347
- if (
348
- existing.origin === "mcp" &&
349
- existing.ownerMcpServerId !== tool.ownerMcpServerId
350
- ) {
387
+ if (existingOwner?.kind === "mcp" && existingOwner.id !== serverId) {
351
388
  throw new Error(
352
- `MCP tool "${tool.name}" is already registered by MCP server "${existing.ownerMcpServerId}"`,
389
+ `MCP tool "${tool.name}" is already registered by MCP server "${existingOwner.id}"`,
353
390
  );
354
391
  }
355
392
  }
@@ -358,8 +395,9 @@ export function registerMcpTools(newTools: Tool[]): Tool[] {
358
395
 
359
396
  for (const tool of accepted) {
360
397
  tools.set(tool.name, tool);
398
+ ownersByName.set(tool.name, { kind: "mcp", id: serverId });
361
399
  log.info(
362
- { name: tool.name, ownerMcpServerId: tool.ownerMcpServerId },
400
+ { name: tool.name, ownerMcpServerId: serverId },
363
401
  "MCP tool registered",
364
402
  );
365
403
  }
@@ -371,9 +409,10 @@ export function registerMcpTools(newTools: Tool[]): Tool[] {
371
409
  * Unregister all MCP-origin tools from the registry.
372
410
  */
373
411
  export function unregisterAllMcpTools(): void {
374
- for (const [name, tool] of tools) {
375
- if (tool.origin === "mcp") {
412
+ for (const [name, owner] of ownersByName) {
413
+ if (owner.kind === "mcp") {
376
414
  tools.delete(name);
415
+ ownersByName.delete(name);
377
416
  log.info({ name }, "MCP tool unregistered (reload)");
378
417
  }
379
418
  }
@@ -385,7 +424,9 @@ export function unregisterAllMcpTools(): void {
385
424
  * were registered after session creation (e.g. via `vellum mcp reload`).
386
425
  */
387
426
  export function getMcpToolDefinitions(): ToolDefinition[] {
388
- return Array.from(tools.values()).filter((t) => t.origin === "mcp");
427
+ return Array.from(tools.values()).filter(
428
+ (t) => ownersByName.get(t.name)?.kind === "mcp",
429
+ );
389
430
  }
390
431
 
391
432
  /**
@@ -393,7 +434,7 @@ export function getMcpToolDefinitions(): ToolDefinition[] {
393
434
  */
394
435
  export function getSkillToolNames(): string[] {
395
436
  return Array.from(tools.values())
396
- .filter((t) => t.origin === "skill")
437
+ .filter((t) => ownersByName.get(t.name)?.kind === "skill")
397
438
  .map((t) => t.name);
398
439
  }
399
440
 
@@ -405,15 +446,13 @@ export function getSkillRefCount(skillId: string): number {
405
446
  }
406
447
 
407
448
  export function getAllToolDefinitions(): ToolDefinition[] {
408
- // Exclude proxy tools (e.g. computer_use_* tools) - they are projected
409
- // into sessions by the skill system, not via the global tool list.
410
449
  // Exclude skill-origin tools - they are managed by the session-level
411
450
  // skill projection system (projectSkillTools) and must not leak into
412
451
  // the base tool list, which is shared across sessions via the global
413
452
  // registry. Including them here causes "Tool names must be unique"
414
453
  // errors when the projection appends the same tools a second time.
415
454
  return getAllTools().filter(
416
- (t) => t.executionMode !== "proxy" && t.origin !== "skill",
455
+ (t) => ownersByName.get(t.name)?.kind !== "skill",
417
456
  );
418
457
  }
419
458
 
@@ -442,10 +481,13 @@ export async function initializeTools(): Promise<void> {
442
481
  // External skill tools — registered by skill bootstrap modules via
443
482
  // `registerExternalTools()`. Called at init time (not spread into
444
483
  // `explicitTools`) so registrations that happen between module-load
445
- // and `initializeTools()` are picked up.
446
- const extTools = getExternalTools();
447
- for (const tool of extTools) {
484
+ // and `initializeTools()` are picked up. Each provider pairs its tools
485
+ // with an OwnerInfo so the registry can record ownership in
486
+ // {@link ownersByName} alongside the bare `registerTool()` install.
487
+ const extEntries = getExternalTools();
488
+ for (const { owner, tool } of extEntries) {
448
489
  registerTool(tool);
490
+ ownersByName.set(tool.name, owner);
449
491
  }
450
492
 
451
493
  // Host tools are registered explicitly so host access stays opt-in until
@@ -483,18 +525,17 @@ export async function initializeTools(): Promise<void> {
483
525
  const manifestToolNames = new Set<string>([
484
526
  ...eagerModuleToolNames,
485
527
  ...explicitTools.map((t: Tool) => t.name),
486
- ...extTools.map((t: Tool) => t.name),
528
+ ...extEntries.map(({ tool }) => tool.name),
487
529
  ...hostTools.map((t: Tool) => t.name),
488
530
  ...cesTools.map((t: Tool) => t.name),
489
- ...allComputerUseTools.map((t: Tool) => t.name),
490
531
  ...allUiSurfaceTools.map((t: Tool) => t.name),
491
532
  ...coreAppProxyTools.map((t: Tool) => t.name),
492
533
  ]);
493
534
 
494
535
  coreToolsSnapshot = new Map<string, Tool>();
495
536
  for (const [name, tool] of tools) {
496
- if (tool.origin === "skill") continue;
497
- if (tool.origin === "plugin") continue;
537
+ const ownerKind = ownersByName.get(name)?.kind;
538
+ if (ownerKind === "skill" || ownerKind === "plugin") continue;
498
539
  // Exclude pre-existing tools not declared in the manifest
499
540
  if (preExisting.has(name) && !manifestToolNames.has(name)) continue;
500
541
  coreToolsSnapshot.set(name, tool);
@@ -516,6 +557,7 @@ export async function initializeTools(): Promise<void> {
516
557
  */
517
558
  export function __resetRegistryForTesting(): void {
518
559
  tools.clear();
560
+ ownersByName.clear();
519
561
  skillRefCount.clear();
520
562
  pluginRefCount.clear();
521
563
 
@@ -533,6 +575,7 @@ export function __resetRegistryForTesting(): void {
533
575
  */
534
576
  export function __clearRegistryForTesting(): void {
535
577
  tools.clear();
578
+ ownersByName.clear();
536
579
  skillRefCount.clear();
537
580
  pluginRefCount.clear();
538
581
  }
@@ -43,7 +43,7 @@ export async function executeScheduleCreate(
43
43
  | Record<string, unknown>
44
44
  | undefined;
45
45
  const quiet = (input.quiet as boolean) ?? false;
46
- const reuseConversation = (input.reuse_conversation as boolean) ?? false;
46
+ const reuseConversation = input.reuse_conversation as boolean | undefined;
47
47
  const maxRetries = input.max_retries as number | undefined;
48
48
  const retryBackoffMs = input.retry_backoff_ms as number | undefined;
49
49
 
@@ -1,5 +1,6 @@
1
1
  import type { SkillToolEntry } from "../../config/skills.js";
2
2
  import { RiskLevel } from "../../permissions/types.js";
3
+ import { validateInputAgainstSchema } from "../../skills/validate-input.js";
3
4
  import type {
4
5
  ExecutionTarget,
5
6
  Tool,
@@ -14,39 +15,16 @@ const riskMap: Record<SkillToolEntry["risk"], RiskLevel> = {
14
15
  high: RiskLevel.High,
15
16
  };
16
17
 
17
- /**
18
- * Validate that all keys in `input` are declared in the tool's input_schema
19
- * properties. Returns an error result listing unknown parameters, or undefined
20
- * if validation passes.
21
- */
22
- function validateNoUnknownParams(
23
- toolName: string,
24
- input: Record<string, unknown>,
25
- schema: SkillToolEntry["input_schema"],
26
- ): ToolExecutionResult | undefined {
27
- const properties = schema?.properties;
28
- if (!properties) return undefined;
29
-
30
- const knownKeys = new Set(Object.keys(properties));
31
- const unknownKeys = Object.keys(input).filter((k) => !knownKeys.has(k));
32
- if (unknownKeys.length === 0) return undefined;
33
-
34
- const listed = unknownKeys.map((k) => `"${k}"`).join(", ");
35
- const supported = [...knownKeys].map((k) => `"${k}"`).join(", ");
36
- return {
37
- content: `Unknown parameter${unknownKeys.length > 1 ? "s" : ""} ${listed} for tool "${toolName}". Supported parameters: ${supported}. Remove unsupported parameters and retry.`,
38
- isError: true,
39
- };
40
- }
41
-
42
18
  /**
43
19
  * Create a runtime Tool object from a manifest entry.
44
20
  * Maps SkillToolEntry metadata to the Tool interface and routes execution
45
- * through the skill script runner.
21
+ * through the skill script runner. Ownership (the originating skill id) is
22
+ * recorded by the tool registry at `registerSkillTools(skillId, tools)`
23
+ * time, not stamped on the `Tool` object — see
24
+ * {@link ../../tools/registry.getToolOwner}.
46
25
  */
47
26
  export function createSkillTool(
48
27
  entry: SkillToolEntry,
49
- skillId: string,
50
28
  skillDir: string,
51
29
  versionHash: string,
52
30
  bundled?: boolean,
@@ -56,11 +34,7 @@ export function createSkillTool(
56
34
  description: entry.description,
57
35
  category: entry.category,
58
36
  defaultRiskLevel: riskMap[entry.risk],
59
- origin: "skill",
60
- ownerSkillId: skillId,
61
37
  executionTarget: entry.execution_target as ExecutionTarget,
62
- ownerSkillVersionHash: versionHash,
63
- ownerSkillBundled: bundled,
64
38
 
65
39
  input_schema: entry.input_schema as object,
66
40
 
@@ -68,12 +42,17 @@ export function createSkillTool(
68
42
  input: Record<string, unknown>,
69
43
  context: ToolContext,
70
44
  ): Promise<ToolExecutionResult> {
71
- const validationError = validateNoUnknownParams(
45
+ const validation = validateInputAgainstSchema(
72
46
  entry.name,
73
47
  input,
74
- entry.input_schema,
48
+ entry.input_schema as Record<string, unknown> | undefined,
75
49
  );
76
- if (validationError) return validationError;
50
+ if (!validation.ok) {
51
+ return {
52
+ content: `Invalid input for tool "${entry.name}": ${validation.errors.join("; ")}. Fix the arguments and retry.`,
53
+ isError: true,
54
+ };
55
+ }
77
56
 
78
57
  return runSkillToolScript(skillDir, entry.executor, input, context, {
79
58
  target: entry.execution_target,
@@ -86,15 +65,17 @@ export function createSkillTool(
86
65
 
87
66
  /**
88
67
  * Create runtime Tool objects from all entries in a manifest.
68
+ * The caller is responsible for passing the resulting array to
69
+ * `registerSkillTools(skillId, tools)`, which is where ownership is
70
+ * recorded.
89
71
  */
90
72
  export function createSkillToolsFromManifest(
91
73
  entries: SkillToolEntry[],
92
- skillId: string,
93
74
  skillDir: string,
94
75
  versionHash: string,
95
76
  bundled?: boolean,
96
77
  ): Tool[] {
97
78
  return entries.map((entry) =>
98
- createSkillTool(entry, skillId, skillDir, versionHash, bundled),
79
+ createSkillTool(entry, skillDir, versionHash, bundled),
99
80
  );
100
81
  }
@@ -102,6 +102,9 @@ export async function executeSubagentSpawn(
102
102
  ...(inheritedOverrideProfile
103
103
  ? { overrideProfile: inheritedOverrideProfile }
104
104
  : {}),
105
+ ...(context.toolUseId
106
+ ? { parentToolUseId: context.toolUseId }
107
+ : {}),
105
108
  ...forkFields,
106
109
  },
107
110
  sendToClient as (msg: unknown) => void,
@@ -10,7 +10,7 @@ import { createOrReuseToolGrantRequest } from "../runtime/tool-grant-request-hel
10
10
  import { redactSecrets } from "../security/secret-scanner.js";
11
11
  import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
12
12
  import { getLogger } from "../util/logger.js";
13
- import { getAllTools, getTool } from "./registry.js";
13
+ import { getAllTools, getTool, getToolOwner } from "./registry.js";
14
14
  import { isSideEffectTool } from "./side-effects.js";
15
15
  import { summarizeToolInput } from "./tool-input-summary.js";
16
16
  import {
@@ -340,8 +340,12 @@ export class ToolApprovalHandler {
340
340
  const tool = getTool(name);
341
341
  if (!tool) {
342
342
  const allowedToolNames = context.allowedToolNames;
343
+ // List every registered tool. Tools that need an external resolver
344
+ // (computer-use, ui-surface, etc.) now return a structured error
345
+ // from their `execute()` when no resolver is connected, rather than
346
+ // being filtered out here — listing them surfaces a clearer path
347
+ // than hiding their names entirely.
343
348
  const available = getAllTools()
344
- .filter((t) => t.executionMode !== "proxy" || context.proxyToolResolver)
345
349
  .map((t) => t.name)
346
350
  .filter((n) => !allowedToolNames || allowedToolNames.has(n))
347
351
  .sort()
@@ -368,8 +372,10 @@ export class ToolApprovalHandler {
368
372
 
369
373
  // Gate tools not active for the current turn
370
374
  if (context.allowedToolNames && !context.allowedToolNames.has(name)) {
371
- const loadHint = tool.ownerSkillId
372
- ? `Load the "${tool.ownerSkillId}" skill that provides this tool first.`
375
+ const owner = getToolOwner(name);
376
+ const ownerSkillId = owner?.kind === "skill" ? owner.id : undefined;
377
+ const loadHint = ownerSkillId
378
+ ? `Load the "${ownerSkillId}" skill that provides this tool first.`
373
379
  : `Load the skill that provides this tool first.`;
374
380
  const msg = `Tool "${name}" is not currently active. ${loadHint}`;
375
381
  const durationMs = Date.now() - startTime;