@vellumai/assistant 0.11.2 → 0.11.3-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- 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__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "../persistence/conversation-crud.js";
|
|
24
24
|
import { getDb } from "../persistence/db-connection.js";
|
|
25
25
|
import { initializeDb } from "../persistence/db-init.js";
|
|
26
|
+
import { resolveDefaultScheduleInferenceProfile } from "../schedule/inference-profile.js";
|
|
26
27
|
import {
|
|
27
28
|
createOwnerDeferredWake,
|
|
28
29
|
createSchedule,
|
|
@@ -82,6 +83,9 @@ describe("scheduler wake mode", () => {
|
|
|
82
83
|
source: "defer",
|
|
83
84
|
persistTriggerAsEvent: true,
|
|
84
85
|
trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
|
|
86
|
+
// Every schedule is pinned at creation, so the row always carries a
|
|
87
|
+
// profile for the woken turn to run under.
|
|
88
|
+
forceOverrideProfile: resolveDefaultScheduleInferenceProfile()!,
|
|
85
89
|
});
|
|
86
90
|
|
|
87
91
|
// AND processMessage is never called (wake mode doesn't use it)
|
|
@@ -113,6 +117,7 @@ describe("scheduler wake mode", () => {
|
|
|
113
117
|
hint: "Follow up",
|
|
114
118
|
source: "defer",
|
|
115
119
|
persistTriggerAsEvent: true,
|
|
120
|
+
forceOverrideProfile: resolveDefaultScheduleInferenceProfile()!,
|
|
116
121
|
});
|
|
117
122
|
});
|
|
118
123
|
|
|
@@ -108,7 +108,10 @@ import {
|
|
|
108
108
|
initializeProviders,
|
|
109
109
|
listProviders,
|
|
110
110
|
} from "../providers/registry.js";
|
|
111
|
-
import {
|
|
111
|
+
import {
|
|
112
|
+
notifyCesOfAssistantApiKeyUpdate,
|
|
113
|
+
ROUTES,
|
|
114
|
+
} from "../runtime/routes/secret-routes.js";
|
|
112
115
|
|
|
113
116
|
const addRoute = ROUTES.find(
|
|
114
117
|
(r) => r.method === "POST" && r.endpoint === "secrets",
|
|
@@ -176,6 +179,31 @@ describe("secret routes managed proxy registry sync", () => {
|
|
|
176
179
|
expect(lastGeminiConstructorOpts).toBeDefined();
|
|
177
180
|
});
|
|
178
181
|
|
|
182
|
+
test("the CES key-update notification with no CES client logs a warning", async () => {
|
|
183
|
+
// When getCesClient() is undefined the push is skipped, but the skip must
|
|
184
|
+
// be logged, not dropped silently. Exercise the extracted helper directly
|
|
185
|
+
// with an injected logger.
|
|
186
|
+
const warns: string[] = [];
|
|
187
|
+
const fakeLogger = {
|
|
188
|
+
info: () => {},
|
|
189
|
+
warn: (a: unknown, b?: unknown) => {
|
|
190
|
+
warns.push(typeof a === "string" ? a : String(b));
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
await notifyCesOfAssistantApiKeyUpdate(
|
|
195
|
+
"ast-managed-key",
|
|
196
|
+
undefined,
|
|
197
|
+
fakeLogger as unknown as Parameters<
|
|
198
|
+
typeof notifyCesOfAssistantApiKeyUpdate
|
|
199
|
+
>[2],
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
expect(warns.some((m) => m.includes("no CES client is connected"))).toBe(
|
|
203
|
+
true,
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
179
207
|
test("provider API key writes notify live-conversation refresh listeners", async () => {
|
|
180
208
|
await addApiKey("fireworks", "fw-key");
|
|
181
209
|
|
|
@@ -19,6 +19,7 @@ import * as encryptedStore from "../security/encrypted-store.js";
|
|
|
19
19
|
import {
|
|
20
20
|
_resetBackend,
|
|
21
21
|
deleteSecureKeyAsync,
|
|
22
|
+
getActiveBackendName,
|
|
22
23
|
getSecureKeyAsync,
|
|
23
24
|
getSecureKeyResultAsync,
|
|
24
25
|
listSecureKeysAsync,
|
|
@@ -58,6 +59,9 @@ describe("secure-keys", () => {
|
|
|
58
59
|
_resetBackend();
|
|
59
60
|
delete process.env.VELLUM_DEV;
|
|
60
61
|
delete process.env.VELLUM_DESKTOP_APP;
|
|
62
|
+
delete process.env.IS_CONTAINERIZED;
|
|
63
|
+
delete process.env.CES_CREDENTIAL_URL;
|
|
64
|
+
delete process.env.CES_LOCAL_SOCKET;
|
|
61
65
|
});
|
|
62
66
|
|
|
63
67
|
afterAll(() => {
|
|
@@ -507,4 +511,91 @@ describe("secure-keys", () => {
|
|
|
507
511
|
).toBeUndefined();
|
|
508
512
|
});
|
|
509
513
|
});
|
|
514
|
+
|
|
515
|
+
// -----------------------------------------------------------------------
|
|
516
|
+
// Encrypted-store → CES upgrade without a reconnect callback
|
|
517
|
+
// -----------------------------------------------------------------------
|
|
518
|
+
//
|
|
519
|
+
// Worker/CLI processes inject a CES client whose handshake is still in
|
|
520
|
+
// flight, so the first credential read falls back to the encrypted store.
|
|
521
|
+
// These processes never register `_cesReconnect`. Once the handshake
|
|
522
|
+
// completes and the client reports ready, the next read must upgrade to
|
|
523
|
+
// CES RPC, since the live client already exists and no reconnect is needed.
|
|
524
|
+
describe("encrypted-store → CES upgrade without a reconnect callback", () => {
|
|
525
|
+
test("upgrades to CES RPC once the injected client becomes ready", async () => {
|
|
526
|
+
const client = makeMockCesClient(false);
|
|
527
|
+
setCesClient(client);
|
|
528
|
+
|
|
529
|
+
// Not ready yet → falls back to the encrypted store (local mode).
|
|
530
|
+
encryptedStore.setKey("openai", "local-value");
|
|
531
|
+
expect(await getSecureKeyAsync("openai")).toBe("local-value");
|
|
532
|
+
expect(getActiveBackendName()).toBe("encrypted-store");
|
|
533
|
+
|
|
534
|
+
// Handshake completes; no `_cesReconnect` was ever registered.
|
|
535
|
+
client.setReady(true);
|
|
536
|
+
|
|
537
|
+
await getSecureKeyResultAsync("openai");
|
|
538
|
+
expect(getActiveBackendName()).toBe("ces-rpc");
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// -----------------------------------------------------------------------
|
|
543
|
+
// Containerized pod with no ready CES backend
|
|
544
|
+
// -----------------------------------------------------------------------
|
|
545
|
+
//
|
|
546
|
+
// On a pod the local encrypted store does not exist. When no CES path is
|
|
547
|
+
// ready, reads must report the store as unreachable (presence
|
|
548
|
+
// indeterminate) rather than absent. A provisioned credential is never
|
|
549
|
+
// reported as "no key". The unreachable result must not be pinned: once a
|
|
550
|
+
// CES client arrives, the next read routes through it.
|
|
551
|
+
describe("containerized pod with no ready CES backend", () => {
|
|
552
|
+
test("reads report unreachable (indeterminate), never absent", async () => {
|
|
553
|
+
process.env.IS_CONTAINERIZED = "1";
|
|
554
|
+
delete process.env.CES_CREDENTIAL_URL;
|
|
555
|
+
delete process.env.CES_LOCAL_SOCKET;
|
|
556
|
+
_resetBackend();
|
|
557
|
+
|
|
558
|
+
const result = await getSecureKeyResultAsync("openai");
|
|
559
|
+
expect(result.value).toBeUndefined();
|
|
560
|
+
// unreachable → presence indeterminate, NEVER absent.
|
|
561
|
+
expect(result.unreachable).toBe(true);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
test("does not pin the unreachable result: a ready CES client wins the next read", async () => {
|
|
565
|
+
process.env.IS_CONTAINERIZED = "1";
|
|
566
|
+
delete process.env.CES_CREDENTIAL_URL;
|
|
567
|
+
delete process.env.CES_LOCAL_SOCKET;
|
|
568
|
+
_resetBackend();
|
|
569
|
+
|
|
570
|
+
expect((await getSecureKeyResultAsync("openai")).unreachable).toBe(true);
|
|
571
|
+
|
|
572
|
+
setCesClient(makeMockCesClient(true));
|
|
573
|
+
|
|
574
|
+
const afterAttach = await getSecureKeyResultAsync("openai");
|
|
575
|
+
expect(afterAttach.unreachable).toBe(false);
|
|
576
|
+
expect(getActiveBackendName()).toBe("ces-rpc");
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// -----------------------------------------------------------------------
|
|
581
|
+
// Encrypted-store read error vs genuinely-absent (local mode)
|
|
582
|
+
// -----------------------------------------------------------------------
|
|
583
|
+
describe("encrypted-store read error vs absent", () => {
|
|
584
|
+
test("genuinely-missing store → absent (unreachable false)", async () => {
|
|
585
|
+
const result = await getSecureKeyResultAsync("nope");
|
|
586
|
+
expect(result.value).toBeUndefined();
|
|
587
|
+
expect(result.unreachable).toBe(false);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("store exists but key material is missing → unreachable", async () => {
|
|
591
|
+
encryptedStore.setKey("openai", "sk-1");
|
|
592
|
+
// Remove the store key so the existing entry can no longer be decrypted.
|
|
593
|
+
rmSync(join(TEST_DIR, "store.key"));
|
|
594
|
+
_resetBackend();
|
|
595
|
+
|
|
596
|
+
const result = await getSecureKeyResultAsync("openai");
|
|
597
|
+
expect(result.value).toBeUndefined();
|
|
598
|
+
expect(result.unreachable).toBe(true);
|
|
599
|
+
});
|
|
600
|
+
});
|
|
510
601
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import type { AnsweredQuestion } from "../api/events/question-answered.js";
|
|
3
4
|
import { renderHistoryContent } from "../daemon/handlers/shared.js";
|
|
4
5
|
import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
|
|
5
6
|
import {
|
|
@@ -298,9 +299,9 @@ describe("renderHistoryContent", () => {
|
|
|
298
299
|
expect(output.toolCalls[0].errorCode).toBe("acp_claude_oauth_missing");
|
|
299
300
|
});
|
|
300
301
|
|
|
301
|
-
test("emits the attachment id for a workspace_ref tool-result image", () => {
|
|
302
|
+
test("emits the attachment id for a workspace_ref tool-result image", async () => {
|
|
302
303
|
// "aGVsbG8=" = "hello" — a stand-in for screenshot bytes.
|
|
303
|
-
const stored = uploadAttachment("shot.png", "image/png", "aGVsbG8=");
|
|
304
|
+
const stored = await uploadAttachment("shot.png", "image/png", "aGVsbG8=");
|
|
304
305
|
const output = renderHistoryContent([
|
|
305
306
|
{ type: "tool_use", id: "tu_1", name: "browser_screenshot", input: {} },
|
|
306
307
|
{
|
|
@@ -549,6 +550,95 @@ describe("renderHistoryContent", () => {
|
|
|
549
550
|
expect(output.toolCalls[0].activityMetadata).toEqual(activityMetadata);
|
|
550
551
|
});
|
|
551
552
|
|
|
553
|
+
// ── Persisted answered ask_question ────────────────────────────────────────
|
|
554
|
+
|
|
555
|
+
test("hydrates a persisted _answeredQuestion onto its tool_use block", () => {
|
|
556
|
+
// Mirrors what the daemon stamps when an `ask_question` prompt settles, so
|
|
557
|
+
// the answered card rehydrates on a reopened conversation instead of the
|
|
558
|
+
// user's decision disappearing with the interactive prompt.
|
|
559
|
+
const answeredQuestion: AnsweredQuestion = {
|
|
560
|
+
requestId: "req-1",
|
|
561
|
+
questions: [
|
|
562
|
+
{
|
|
563
|
+
id: "q1",
|
|
564
|
+
question: "Which Alice?",
|
|
565
|
+
options: [
|
|
566
|
+
{ id: "alice_work", label: "Alice (work)" },
|
|
567
|
+
{ id: "alice_personal", label: "Alice (personal)" },
|
|
568
|
+
],
|
|
569
|
+
},
|
|
570
|
+
],
|
|
571
|
+
responses: [
|
|
572
|
+
{ questionId: "q1", decision: "option", optionId: "alice_work" },
|
|
573
|
+
],
|
|
574
|
+
overall: "completed",
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const output = renderHistoryContent([
|
|
578
|
+
{
|
|
579
|
+
type: "tool_use",
|
|
580
|
+
id: "tu_q",
|
|
581
|
+
name: "ask_question",
|
|
582
|
+
input: { questions: [{ question: "Which Alice?" }] },
|
|
583
|
+
_answeredQuestion: answeredQuestion,
|
|
584
|
+
},
|
|
585
|
+
]);
|
|
586
|
+
|
|
587
|
+
expect(output.toolCalls[0].answeredQuestion).toEqual(answeredQuestion);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("hydrates a free-text answer verbatim", () => {
|
|
591
|
+
const output = renderHistoryContent([
|
|
592
|
+
{
|
|
593
|
+
type: "tool_use",
|
|
594
|
+
id: "tu_q",
|
|
595
|
+
name: "ask_question",
|
|
596
|
+
input: {},
|
|
597
|
+
_answeredQuestion: {
|
|
598
|
+
requestId: "req-2",
|
|
599
|
+
questions: [{ id: "q1", question: "Which fruit?", options: [] }],
|
|
600
|
+
responses: [
|
|
601
|
+
{ questionId: "q1", decision: "free_text", text: "Cherry" },
|
|
602
|
+
],
|
|
603
|
+
overall: "completed",
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
]);
|
|
607
|
+
|
|
608
|
+
expect(output.toolCalls[0].answeredQuestion?.responses).toEqual([
|
|
609
|
+
{ questionId: "q1", decision: "free_text", text: "Cherry" },
|
|
610
|
+
]);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test("leaves answeredQuestion absent on rows that predate the annotation", () => {
|
|
614
|
+
const output = renderHistoryContent([
|
|
615
|
+
{
|
|
616
|
+
type: "tool_use",
|
|
617
|
+
id: "tu_q",
|
|
618
|
+
name: "ask_question",
|
|
619
|
+
input: { questions: [{ question: "Which Alice?" }] },
|
|
620
|
+
},
|
|
621
|
+
]);
|
|
622
|
+
|
|
623
|
+
expect(output.toolCalls[0].answeredQuestion).toBeUndefined();
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test("ignores a malformed _answeredQuestion annotation", () => {
|
|
627
|
+
// The card renders these contents directly, so a rider that does not match
|
|
628
|
+
// the schema must degrade to no card rather than a crashing render.
|
|
629
|
+
const output = renderHistoryContent([
|
|
630
|
+
{
|
|
631
|
+
type: "tool_use",
|
|
632
|
+
id: "tu_q",
|
|
633
|
+
name: "ask_question",
|
|
634
|
+
input: {},
|
|
635
|
+
_answeredQuestion: { requestId: "req-3", overall: "sideways" },
|
|
636
|
+
},
|
|
637
|
+
]);
|
|
638
|
+
|
|
639
|
+
expect(output.toolCalls[0].answeredQuestion).toBeUndefined();
|
|
640
|
+
});
|
|
641
|
+
|
|
552
642
|
test("ignores non-object _activityMetadata annotations", () => {
|
|
553
643
|
const output = renderHistoryContent([
|
|
554
644
|
{
|
|
@@ -1084,7 +1174,7 @@ describe("getAttachmentsForMessage", () => {
|
|
|
1084
1174
|
|
|
1085
1175
|
test("returns attachments linked to a message", async () => {
|
|
1086
1176
|
const msgId = await createMessage("assistant", "Here is a chart");
|
|
1087
|
-
const stored = uploadAttachment("chart.png", "image/png", "iVBORw==");
|
|
1177
|
+
const stored = await uploadAttachment("chart.png", "image/png", "iVBORw==");
|
|
1088
1178
|
linkAttachmentToMessage(msgId, stored.id, 0);
|
|
1089
1179
|
|
|
1090
1180
|
const result = getAttachmentsForMessage(msgId);
|
|
@@ -1101,8 +1191,8 @@ describe("getAttachmentsForMessage", () => {
|
|
|
1101
1191
|
|
|
1102
1192
|
test("returns multiple attachments in position order", async () => {
|
|
1103
1193
|
const msgId = await createMessage("assistant", "Two files");
|
|
1104
|
-
const a1 = uploadAttachment("first.txt", "text/plain", "AAAA");
|
|
1105
|
-
const a2 = uploadAttachment("second.txt", "text/plain", "BBBB");
|
|
1194
|
+
const a1 = await uploadAttachment("first.txt", "text/plain", "AAAA");
|
|
1195
|
+
const a2 = await uploadAttachment("second.txt", "text/plain", "BBBB");
|
|
1106
1196
|
|
|
1107
1197
|
linkAttachmentToMessage(msgId, a2.id, 1);
|
|
1108
1198
|
linkAttachmentToMessage(msgId, a1.id, 0);
|
|
@@ -1115,8 +1205,8 @@ describe("getAttachmentsForMessage", () => {
|
|
|
1115
1205
|
|
|
1116
1206
|
test("returns all attachments linked to a message", async () => {
|
|
1117
1207
|
const msgId = await createMessage("assistant", "Mixed");
|
|
1118
|
-
const a1 = uploadAttachment("a.png", "image/png", "AAAA");
|
|
1119
|
-
const a2 = uploadAttachment("b.png", "image/png", "BBBB");
|
|
1208
|
+
const a1 = await uploadAttachment("a.png", "image/png", "AAAA");
|
|
1209
|
+
const a2 = await uploadAttachment("b.png", "image/png", "BBBB");
|
|
1120
1210
|
|
|
1121
1211
|
linkAttachmentToMessage(msgId, a1.id, 0);
|
|
1122
1212
|
linkAttachmentToMessage(msgId, a2.id, 1);
|
|
@@ -38,41 +38,41 @@ describe("MAX_FILE_SIZE_BYTES", () => {
|
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
|
|
40
40
|
describe("checkFileSizeOnDisk", () => {
|
|
41
|
-
test("returns undefined for a file within the default limit", () => {
|
|
41
|
+
test("returns undefined for a file within the default limit", async () => {
|
|
42
42
|
const dir = makeTempDir();
|
|
43
43
|
const filePath = join(dir, "small.txt");
|
|
44
44
|
writeFileSync(filePath, "hello");
|
|
45
45
|
|
|
46
|
-
expect(checkFileSizeOnDisk(filePath)).toBeUndefined();
|
|
46
|
+
expect(await checkFileSizeOnDisk(filePath)).toBeUndefined();
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
test("returns error string for a file exceeding a custom limit", () => {
|
|
49
|
+
test("returns error string for a file exceeding a custom limit", async () => {
|
|
50
50
|
const dir = makeTempDir();
|
|
51
51
|
const filePath = join(dir, "big.txt");
|
|
52
52
|
writeFileSync(filePath, "x".repeat(200));
|
|
53
53
|
|
|
54
|
-
const result = checkFileSizeOnDisk(filePath, 100);
|
|
54
|
+
const result = await checkFileSizeOnDisk(filePath, 100);
|
|
55
55
|
expect(result).toBeDefined();
|
|
56
56
|
expect(result).toContain("exceeds");
|
|
57
57
|
expect(result).toContain("limit");
|
|
58
58
|
expect(result).toContain(filePath);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
test("returns undefined for a file exactly at the custom limit", () => {
|
|
61
|
+
test("returns undefined for a file exactly at the custom limit", async () => {
|
|
62
62
|
const dir = makeTempDir();
|
|
63
63
|
const filePath = join(dir, "exact.txt");
|
|
64
64
|
writeFileSync(filePath, "x".repeat(100));
|
|
65
65
|
|
|
66
|
-
expect(checkFileSizeOnDisk(filePath, 100)).toBeUndefined();
|
|
66
|
+
expect(await checkFileSizeOnDisk(filePath, 100)).toBeUndefined();
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
test("uses default limit when none is provided", () => {
|
|
69
|
+
test("uses default limit when none is provided", async () => {
|
|
70
70
|
const dir = makeTempDir();
|
|
71
71
|
const filePath = join(dir, "tiny.txt");
|
|
72
72
|
writeFileSync(filePath, "a");
|
|
73
73
|
|
|
74
74
|
// Should not error since 1 byte is well under 100 MB
|
|
75
|
-
expect(checkFileSizeOnDisk(filePath)).toBeUndefined();
|
|
75
|
+
expect(await checkFileSizeOnDisk(filePath)).toBeUndefined();
|
|
76
76
|
});
|
|
77
77
|
});
|
|
78
78
|
|
|
@@ -5,8 +5,9 @@ import { describe, expect, test } from "bun:test";
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Guard for skill ↔ public-docs drift. A bundled skill's SKILL.md is the
|
|
8
|
-
* behavioral source of truth; its public reference page lives in
|
|
9
|
-
*
|
|
8
|
+
* behavioral source of truth; its public reference page lives in this repo at
|
|
9
|
+
* clients/docs/src/app/docs/_components/skills-reference-<slug>-content.tsx
|
|
10
|
+
* (served at https://www.vellum.ai/docs/skills-reference/<slug>).
|
|
10
11
|
* `scripts/check-skill-docs-sync.ts` fingerprints each documented SKILL.md so a
|
|
11
12
|
* behavior change can't land without the author reconciling the docs page and
|
|
12
13
|
* re-recording the fingerprint.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
3
|
import { tmpdir } from "node:os";
|
|
3
4
|
import { join } from "node:path";
|
|
@@ -6,6 +7,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
|
6
7
|
import type { SkillToolEntry } from "../config/skills.js";
|
|
7
8
|
import { RiskLevel } from "../permissions/types.js";
|
|
8
9
|
import { computeSkillVersionHash } from "../skills/version-hash.js";
|
|
10
|
+
import { resolveSubagentRole } from "../subagent/role-resolution.js";
|
|
11
|
+
import type { SubagentRole } from "../subagent/types.js";
|
|
12
|
+
import { bundledToolInputMisuseKeys } from "../tools/shared/input-misuse.js";
|
|
9
13
|
import {
|
|
10
14
|
createSkillTool,
|
|
11
15
|
createSkillToolsFromManifest,
|
|
@@ -490,6 +494,249 @@ describe("createSkillTool — required/type/enum validation", () => {
|
|
|
490
494
|
});
|
|
491
495
|
});
|
|
492
496
|
|
|
497
|
+
// ---------------------------------------------------------------------------
|
|
498
|
+
// createSkillTool: parameter misuse redirects
|
|
499
|
+
// ---------------------------------------------------------------------------
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* The subagent skill's real `subagent_read` manifest entry. Its redirect keys
|
|
503
|
+
* are absent from the advertised `properties`, so manifest validation is the
|
|
504
|
+
* layer that has to surface the redirect: nothing downstream of it runs.
|
|
505
|
+
*/
|
|
506
|
+
function subagentReadEntry(): SkillToolEntry {
|
|
507
|
+
const manifest = JSON.parse(
|
|
508
|
+
readFileSync(
|
|
509
|
+
join(import.meta.dir, "../config/bundled-skills/subagent/TOOLS.json"),
|
|
510
|
+
"utf-8",
|
|
511
|
+
),
|
|
512
|
+
) as { tools: SkillToolEntry[] };
|
|
513
|
+
const entry = manifest.tools.find((t) => t.name === "subagent_read");
|
|
514
|
+
if (!entry) {
|
|
515
|
+
throw new Error("subagent_read is missing from the subagent TOOLS.json");
|
|
516
|
+
}
|
|
517
|
+
return entry;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function subagentReadTool() {
|
|
521
|
+
return createSkillTool(
|
|
522
|
+
subagentReadEntry(),
|
|
523
|
+
"/skills/subagent",
|
|
524
|
+
"v1:test",
|
|
525
|
+
BUNDLED,
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
describe("createSkillTool: parameter misuse redirects", () => {
|
|
530
|
+
for (const key of ["path", "file", "filename"]) {
|
|
531
|
+
test(`subagent_read sends "${key}" to file_read`, async () => {
|
|
532
|
+
const result = await subagentReadTool().execute(
|
|
533
|
+
{ [key]: "/tmp/notes.md" },
|
|
534
|
+
makeContext(),
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
expect(result.isError).toBe(true);
|
|
538
|
+
expect(result.content).toBe(
|
|
539
|
+
"subagent_read returns a subagent's output, it does not read files. Use file_read for files. Pass subagent_id or label here.",
|
|
540
|
+
);
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
for (const key of ["subagentId", "agent_id"]) {
|
|
545
|
+
test(`subagent_read names "${key}" as a misspelling of subagent_id`, async () => {
|
|
546
|
+
const result = await subagentReadTool().execute(
|
|
547
|
+
{ [key]: "sa-1" },
|
|
548
|
+
makeContext(),
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
expect(result.isError).toBe(true);
|
|
552
|
+
expect(result.content).toBe(
|
|
553
|
+
"Unknown parameter. Use subagent_id (snake_case) or label.",
|
|
554
|
+
);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
test("a key with no redirect keeps the generic validation error", async () => {
|
|
559
|
+
const result = await subagentReadTool().execute(
|
|
560
|
+
{ subagent_id: "sa-1", nonsense: 1 },
|
|
561
|
+
makeContext(),
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
expect(result.isError).toBe(true);
|
|
565
|
+
expect(result.content).toContain('Invalid input for tool "subagent_read"');
|
|
566
|
+
expect(result.content).toContain('Unknown parameter "nonsense"');
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test("redirect keys stay out of the advertised schema", () => {
|
|
570
|
+
const properties = (
|
|
571
|
+
subagentReadEntry().input_schema as {
|
|
572
|
+
properties: Record<string, unknown>;
|
|
573
|
+
}
|
|
574
|
+
).properties;
|
|
575
|
+
|
|
576
|
+
for (const key of bundledToolInputMisuseKeys("subagent_read")) {
|
|
577
|
+
expect(properties[key]).toBeUndefined();
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test("a non-bundled skill reusing the name keeps the generic validation error", async () => {
|
|
582
|
+
// Tool names are not reserved, so a workspace skill can define its own
|
|
583
|
+
// `subagent_read` where `path` is a declared parameter. Its validation
|
|
584
|
+
// errors must describe its own manifest, not Vellum's file-reader redirect.
|
|
585
|
+
const tool = createSkillTool(
|
|
586
|
+
makeEntry({
|
|
587
|
+
name: "subagent_read",
|
|
588
|
+
input_schema: {
|
|
589
|
+
type: "object",
|
|
590
|
+
properties: { path: { type: "string" } },
|
|
591
|
+
required: ["path"],
|
|
592
|
+
},
|
|
593
|
+
}),
|
|
594
|
+
"/workspace/skills/notes",
|
|
595
|
+
"v1:test",
|
|
596
|
+
false,
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
const result = await tool.execute(
|
|
600
|
+
{ path: "/tmp/notes.md", nonsense: 1 },
|
|
601
|
+
makeContext(),
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
expect(result.isError).toBe(true);
|
|
605
|
+
expect(result.content).toContain('Invalid input for tool "subagent_read"');
|
|
606
|
+
expect(result.content).toContain('Unknown parameter "nonsense"');
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
test("an unspecified owner keeps the generic validation error", async () => {
|
|
610
|
+
const tool = createSkillTool(
|
|
611
|
+
subagentReadEntry(),
|
|
612
|
+
"/skills/subagent",
|
|
613
|
+
"v1:test",
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
const result = await tool.execute({ path: "/tmp/notes.md" }, makeContext());
|
|
617
|
+
|
|
618
|
+
expect(result.isError).toBe(true);
|
|
619
|
+
expect(result.content).toContain('Invalid input for tool "subagent_read"');
|
|
620
|
+
expect(result.content).toContain('Unknown parameter "path"');
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
test("a tool with no rules keeps the generic validation error", async () => {
|
|
624
|
+
const hash = computeSkillVersionHash(tempDir);
|
|
625
|
+
const tool = createSkillTool(
|
|
626
|
+
makeEntry({ executor: "echo.ts" }),
|
|
627
|
+
tempDir,
|
|
628
|
+
hash,
|
|
629
|
+
BUNDLED,
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
const result = await tool.execute(
|
|
633
|
+
{ query: "hello", path: "/tmp/notes.md" },
|
|
634
|
+
makeContext(),
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
expect(result.isError).toBe(true);
|
|
638
|
+
expect(result.content).toContain('Invalid input for tool "test_tool"');
|
|
639
|
+
expect(result.content).toContain('Unknown parameter "path"');
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// ---------------------------------------------------------------------------
|
|
644
|
+
// createSkillTool: subagent_spawn roles survive manifest validation
|
|
645
|
+
// ---------------------------------------------------------------------------
|
|
646
|
+
|
|
647
|
+
/** The subagent skill's real `subagent_spawn` manifest entry. */
|
|
648
|
+
function subagentSpawnEntry(): SkillToolEntry {
|
|
649
|
+
const manifest = JSON.parse(
|
|
650
|
+
readFileSync(
|
|
651
|
+
join(import.meta.dir, "../config/bundled-skills/subagent/TOOLS.json"),
|
|
652
|
+
"utf-8",
|
|
653
|
+
),
|
|
654
|
+
) as { tools: SkillToolEntry[] };
|
|
655
|
+
const entry = manifest.tools.find((t) => t.name === "subagent_spawn");
|
|
656
|
+
if (!entry) {
|
|
657
|
+
throw new Error("subagent_spawn is missing from the subagent TOOLS.json");
|
|
658
|
+
}
|
|
659
|
+
return entry;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* The real `subagent_spawn` schema wired to a stub executor. Only the schema
|
|
664
|
+
* is under test here, so the manifest's own executor (which would really spawn)
|
|
665
|
+
* is swapped for the echo script.
|
|
666
|
+
*/
|
|
667
|
+
function subagentSpawnTool() {
|
|
668
|
+
return createSkillTool(
|
|
669
|
+
{ ...subagentSpawnEntry(), executor: "echo.ts" },
|
|
670
|
+
tempDir,
|
|
671
|
+
computeSkillVersionHash(tempDir),
|
|
672
|
+
BUNDLED,
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Every `role` string {@link resolveSubagentRole} defines an answer for has to
|
|
678
|
+
* clear manifest validation first: `createSkillTool` validates against the
|
|
679
|
+
* manifest BEFORE the executor runs, so a constraint there that is narrower
|
|
680
|
+
* than the resolver silently deletes the resolver's behavior, and every test
|
|
681
|
+
* that calls `executeSubagentSpawn` directly still passes.
|
|
682
|
+
*/
|
|
683
|
+
describe("createSkillTool: subagent_spawn role validation", () => {
|
|
684
|
+
const ROLE_CASES: ReadonlyArray<readonly [string, SubagentRole]> = [
|
|
685
|
+
["researcher", "researcher"],
|
|
686
|
+
["builder", "builder"],
|
|
687
|
+
["advisor", "advisor"],
|
|
688
|
+
["planner", "researcher"],
|
|
689
|
+
["investigator", "researcher"],
|
|
690
|
+
["coder", "builder"],
|
|
691
|
+
["general", "builder"],
|
|
692
|
+
["a skeptical security reviewer", "researcher"],
|
|
693
|
+
["Researcher", "researcher"],
|
|
694
|
+
];
|
|
695
|
+
|
|
696
|
+
test.each(ROLE_CASES)(
|
|
697
|
+
'role "%s" passes manifest validation and resolves to %s',
|
|
698
|
+
async (role, expected) => {
|
|
699
|
+
const result = await subagentSpawnTool().execute(
|
|
700
|
+
{ label: "Check", objective: "Do the thing", role },
|
|
701
|
+
makeContext(),
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
// Reached the executor at all, so validation let the role through.
|
|
705
|
+
expect(result.isError).toBe(false);
|
|
706
|
+
expect(JSON.parse(result.content).input.role).toBe(role);
|
|
707
|
+
// And the value the executor receives lands where the tool description
|
|
708
|
+
// and the docs say it does.
|
|
709
|
+
expect(resolveSubagentRole(role).role).toBe(expected);
|
|
710
|
+
},
|
|
711
|
+
);
|
|
712
|
+
|
|
713
|
+
test("role declares no enum, so the resolver stays reachable", () => {
|
|
714
|
+
const role = (
|
|
715
|
+
subagentSpawnEntry().input_schema as {
|
|
716
|
+
properties: Record<string, { type?: string; enum?: unknown }>;
|
|
717
|
+
}
|
|
718
|
+
).properties.role;
|
|
719
|
+
|
|
720
|
+
expect(role.type).toBe("string");
|
|
721
|
+
// An enum here is enforced by `validateInputAgainstSchema` ahead of the
|
|
722
|
+
// executor, so any list short of "every string" rejects the legacy names
|
|
723
|
+
// and free-text personas the resolver exists to handle.
|
|
724
|
+
expect(role.enum).toBeUndefined();
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
test("output_contract keeps its enum, which the resolver has no fallback for", () => {
|
|
728
|
+
// The counterpart constraint: unlike `role`, an unrecognized contract has
|
|
729
|
+
// no defined behavior, so rejecting it at the boundary is correct.
|
|
730
|
+
const contract = (
|
|
731
|
+
subagentSpawnEntry().input_schema as {
|
|
732
|
+
properties: Record<string, { enum?: string[] }>;
|
|
733
|
+
}
|
|
734
|
+
).properties.output_contract;
|
|
735
|
+
|
|
736
|
+
expect(contract.enum).toEqual(["report", "verdict", "artifact"]);
|
|
737
|
+
});
|
|
738
|
+
});
|
|
739
|
+
|
|
493
740
|
// ---------------------------------------------------------------------------
|
|
494
741
|
// createSkillTool — expectedSkillVersionHash plumbing
|
|
495
742
|
// ---------------------------------------------------------------------------
|