@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.
- package/AGENTS.md +33 -1
- package/ARCHITECTURE.md +1 -1
- package/bunfig.toml +6 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +1900 -166
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +3 -2
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +12 -14
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
- package/src/__tests__/conversation-agent-loop.test.ts +488 -2
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +69 -8
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -5
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +26 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +5 -4
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-resolver.test.ts +331 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +3 -2
- package/src/__tests__/openai-provider.test.ts +8 -9
- package/src/__tests__/openai-responses-provider.test.ts +70 -10
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/plugin-bootstrap.test.ts +9 -11
- package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
- package/src/__tests__/process-message-background-slack.test.ts +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +14 -16
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +5 -4
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +38 -0
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/loop.ts +80 -0
- package/src/api/README.md +124 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/{events → api/events}/relationship-state-updated.ts +3 -3
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +128 -3
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/memory-v3-render.ts +147 -0
- package/src/cli/commands/memory-v3.ts +255 -4
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +23 -42
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +1 -1
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +90 -1
- package/src/config/schemas/memory-v2.ts +26 -0
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/token-estimator.ts +10 -5
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
- package/src/daemon/conversation-agent-loop.ts +132 -28
- package/src/daemon/conversation-error.ts +33 -5
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +29 -9
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +33 -48
- package/src/daemon/conversation.ts +26 -46
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +27 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/lifecycle.ts +12 -12
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-types/conversations.ts +20 -22
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -2
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +23 -38
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +28 -29
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +352 -112
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +4 -0
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-worker.ts +12 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
- package/src/memory/llm-request-log-source-local.ts +24 -0
- package/src/memory/llm-request-log-source.ts +31 -0
- package/src/memory/llm-request-log-store.ts +188 -3
- package/src/memory/memory-v2-activation-log-store.ts +95 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edges.test.ts +144 -1
- package/src/memory/v3/__tests__/filter.test.ts +48 -0
- package/src/memory/v3/__tests__/gate.test.ts +96 -33
- package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
- package/src/memory/v3/__tests__/loop.test.ts +250 -5
- package/src/memory/v3/__tests__/scouts.test.ts +49 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
- package/src/memory/v3/__tests__/traversal.test.ts +39 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
- package/src/memory/v3/__tests__/validate.test.ts +32 -0
- package/src/memory/v3/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edges.ts +58 -21
- package/src/memory/v3/filter.ts +27 -22
- package/src/memory/v3/gate.ts +51 -36
- package/src/memory/v3/index-composition.ts +18 -5
- package/src/memory/v3/loop.ts +65 -17
- package/src/memory/v3/scouts.ts +15 -4
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +44 -2
- package/src/memory/v3/traversal.ts +6 -1
- package/src/memory/v3/tree-walk.ts +6 -1
- package/src/memory/v3/validate.ts +56 -33
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugins/defaults/injectors.ts +2 -8
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/types.ts +57 -13
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +7 -11
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/call-site-routing.ts +33 -9
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +24 -3
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +110 -12
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +53 -53
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +18 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -4
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +9 -0
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/auth/route-policy.ts +10 -0
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/background-wake-routes.ts +188 -20
- package/src/runtime/routes/btw-routes.ts +4 -4
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +147 -0
- package/src/runtime/routes/conversation-management-routes.ts +39 -14
- package/src/runtime/routes/conversation-query-routes.ts +60 -10
- package/src/runtime/routes/conversation-routes.ts +186 -140
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/documents-routes.ts +10 -1
- package/src/runtime/routes/group-routes.ts +11 -0
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-v3-routes.ts +160 -2
- package/src/runtime/routes/migration-routes.ts +20 -13
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/tools/apps/definitions.ts +35 -21
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +268 -266
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +2 -5
- package/src/tools/executor.ts +18 -55
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-search.ts +199 -44
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +146 -103
- package/src/tools/schedule/create.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +17 -15
- package/src/tools/ui-surface/definitions.ts +98 -86
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/util/browser.ts +7 -2
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +4 -0
package/src/tools/registry.ts
CHANGED
|
@@ -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<
|
|
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) =>
|
|
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
|
-
*
|
|
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
|
|
115
|
-
* which supports hot-reloading a skill without tearing
|
|
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 =
|
|
183
|
+
const existingIsCore = !ownersByName.has(tool.name);
|
|
124
184
|
if (existingIsCore) {
|
|
125
185
|
log.warn(
|
|
126
|
-
{ toolName: tool.name,
|
|
127
|
-
`Skill "${
|
|
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
|
|
191
|
+
// Existing is from a different owner (plugin/mcp) or a different
|
|
132
192
|
// skill — skill tools can only replace themselves (hot-reload).
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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 ${
|
|
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
|
-
|
|
207
|
+
ownersByName.set(tool.name, { kind: "skill", id: skillId });
|
|
158
208
|
log.info(
|
|
159
|
-
{ name: tool.name, ownerSkillId:
|
|
209
|
+
{ name: tool.name, ownerSkillId: skillId },
|
|
160
210
|
"Skill tool registered",
|
|
161
211
|
);
|
|
162
212
|
}
|
|
163
213
|
|
|
164
|
-
|
|
165
|
-
skillRefCount.set(
|
|
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
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
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 =
|
|
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
|
-
|
|
219
|
-
|
|
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 "${
|
|
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
|
|
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:
|
|
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,
|
|
268
|
-
if (
|
|
311
|
+
for (const [name, owner] of ownersByName) {
|
|
312
|
+
if (owner.kind === "plugin" && owner.id === pluginName) {
|
|
269
313
|
tools.delete(name);
|
|
270
|
-
|
|
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,
|
|
300
|
-
if (
|
|
344
|
+
for (const [name, owner] of ownersByName) {
|
|
345
|
+
if (owner.kind === "skill" && owner.id === skillId) {
|
|
301
346
|
tools.delete(name);
|
|
302
|
-
|
|
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
|
|
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 =
|
|
367
|
+
const existingIsCore = !ownersByName.has(tool.name);
|
|
318
368
|
if (existingIsCore) {
|
|
319
369
|
log.warn(
|
|
320
|
-
{ toolName: tool.name,
|
|
321
|
-
`MCP server "${
|
|
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
|
-
|
|
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
|
-
|
|
330
|
-
|
|
380
|
+
ownerMcpServerId: serverId,
|
|
381
|
+
existingOwner,
|
|
331
382
|
},
|
|
332
|
-
`MCP server "${
|
|
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 (
|
|
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 "${
|
|
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:
|
|
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,
|
|
375
|
-
if (
|
|
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(
|
|
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.
|
|
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) =>
|
|
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
|
-
|
|
447
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
497
|
-
if (
|
|
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 =
|
|
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
|
|
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 (
|
|
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,
|
|
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
|
|
372
|
-
|
|
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;
|