@vellumai/assistant 0.10.2-dev.202606250318.5e7cfb0 → 0.10.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/bun.lock +0 -20
- package/docs/workspace-tools.md +33 -42
- package/eslint-rules/cli-no-daemon-internals.js +0 -6
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -31
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -44
- package/node_modules/@vellumai/gateway-client/src/index.ts +0 -14
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -17
- package/node_modules/@vellumai/service-contracts/package.json +0 -1
- package/node_modules/@vellumai/service-contracts/src/index.ts +0 -1
- package/openapi.yaml +0 -155
- package/package.json +1 -4
- package/scripts/test.sh +15 -36
- package/src/__tests__/actor-token-service.test.ts +14 -36
- package/src/__tests__/agent-loop-override-profile.test.ts +0 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +0 -2
- package/src/__tests__/annotate-activity-metadata.test.ts +0 -2
- package/src/__tests__/annotate-risk-options.test.ts +0 -2
- package/src/__tests__/approval-cascade.test.ts +0 -2
- package/src/__tests__/assistant-attachments.test.ts +0 -42
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -2
- package/src/__tests__/build-persisted-content.test.ts +0 -2
- package/src/__tests__/call-controller.test.ts +0 -19
- package/src/__tests__/channel-guardian.test.ts +58 -94
- package/src/__tests__/channel-reply-delivery.test.ts +0 -2
- package/src/__tests__/compaction-events.test.ts +0 -2
- package/src/__tests__/compaction.benchmark.test.ts +0 -2
- package/src/__tests__/compactor-call-site-logging.test.ts +0 -2
- package/src/__tests__/compactor-low-watermark-cut.test.ts +0 -2
- package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -2
- package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -2
- package/src/__tests__/compactor-web-search-strip.test.ts +0 -2
- package/src/__tests__/config-loader-backfill.test.ts +10 -123
- package/src/__tests__/config-schema.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -31
- package/src/__tests__/contacts-relay-reads.test.ts +15 -13
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop.test.ts +0 -134
- package/src/__tests__/conversation-analysis-routes.test.ts +0 -2
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -2
- package/src/__tests__/conversation-history-web-search.test.ts +0 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +0 -2
- package/src/__tests__/conversation-pairing.test.ts +0 -2
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -2
- package/src/__tests__/conversation-process-callsite.test.ts +0 -2
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -2
- package/src/__tests__/conversation-queue.test.ts +0 -91
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -14
- package/src/__tests__/conversation-slash-queue.test.ts +0 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -2
- package/src/__tests__/conversation-speed-override.test.ts +0 -2
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -29
- package/src/__tests__/conversation-title-service.test.ts +0 -2
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -47
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -2
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-migration-rollback.test.ts +171 -205
- package/src/__tests__/db-test-helpers.ts +4 -5
- package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -4
- package/src/__tests__/disk-pressure-guard.test.ts +0 -41
- package/src/__tests__/dm-persistence.test.ts +0 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +5 -10
- package/src/__tests__/events-dev-bypass-actor.test.ts +1 -7
- package/src/__tests__/exploration-drift-hook.test.ts +2 -3
- package/src/__tests__/filing-service.test.ts +0 -2
- package/src/__tests__/guardian-binding-drift-heal.test.ts +10 -75
- package/src/__tests__/guardian-dispatch.test.ts +1 -95
- package/src/__tests__/guardian-outbound-http.test.ts +0 -13
- package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -2
- package/src/__tests__/heartbeat-service.test.ts +0 -2
- package/src/__tests__/helpers/channel-test-adapter.ts +7 -1
- package/src/__tests__/host-app-control-routes.test.ts +30 -24
- package/src/__tests__/host-bash-routes.test.ts +41 -31
- package/src/__tests__/host-browser-routes.test.ts +32 -26
- package/src/__tests__/host-cu-routes-targeted.test.ts +33 -25
- package/src/__tests__/host-file-routes-targeted.test.ts +52 -40
- package/src/__tests__/host-transfer-routes-targeted.test.ts +43 -31
- package/src/__tests__/http-user-message-parity.test.ts +8 -290
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -28
- package/src/__tests__/inbound-slack-persistence.test.ts +0 -2
- package/src/__tests__/invite-redemption-service.test.ts +0 -198
- package/src/__tests__/llm-context-normalization.test.ts +0 -105
- package/src/__tests__/llm-request-log-error-payload.test.ts +9 -71
- package/src/__tests__/llm-usage-store.test.ts +0 -25
- package/src/__tests__/mcp-health-check.test.ts +1 -2
- package/src/__tests__/media-stream-server-integration.test.ts +0 -127
- package/src/__tests__/memory-retrieval-hook.test.ts +0 -2
- package/src/__tests__/messaging-send-tool.test.ts +0 -2
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +5 -146
- package/src/__tests__/native-web-search.test.ts +0 -2
- package/src/__tests__/non-member-access-request.test.ts +17 -189
- package/src/__tests__/notification-broadcaster.test.ts +0 -4
- package/src/__tests__/notification-decision-recipient-context.test.ts +32 -33
- package/src/__tests__/notification-deep-link.test.ts +0 -6
- package/src/__tests__/notification-guardian-path.test.ts +0 -19
- package/src/__tests__/openai-provider.test.ts +12 -22
- package/src/__tests__/openai-responses-provider.test.ts +2 -12
- package/src/__tests__/outbound-slack-persistence.test.ts +0 -2
- package/src/__tests__/pending-interactions-resolved-event.test.ts +4 -7
- package/src/__tests__/persistence-secret-redaction.test.ts +0 -2
- package/src/__tests__/plugin-bootstrap.test.ts +73 -3
- package/src/__tests__/plugin-route-contribution.test.ts +17 -4
- package/src/__tests__/plugin-tool-contribution.test.ts +18 -3
- package/src/__tests__/plugin-types.test.ts +2 -0
- package/src/__tests__/process-message-background-slack.test.ts +0 -2
- package/src/__tests__/process-message-display-content.test.ts +0 -2
- package/src/__tests__/provider-error-scenarios.test.ts +4 -5
- package/src/__tests__/provider-usage-tracking.test.ts +0 -39
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -2
- package/src/__tests__/registry.test.ts +1 -4
- package/src/__tests__/relay-server.test.ts +25 -694
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -14
- package/src/__tests__/send-endpoint-busy.test.ts +8 -30
- package/src/__tests__/skills.test.ts +0 -44
- package/src/__tests__/slack-inbound-verification.test.ts +2 -47
- package/src/__tests__/stt-hints.test.ts +13 -44
- package/src/__tests__/subagent-detail.test.ts +0 -27
- package/src/__tests__/subagent-disposal.test.ts +0 -65
- package/src/__tests__/subagent-notify-parent.test.ts +0 -2
- package/src/__tests__/subagent-role-registry.test.ts +2 -7
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +0 -2
- package/src/__tests__/subagent-tools.test.ts +0 -2
- package/src/__tests__/suggestion-routes.test.ts +0 -2
- package/src/__tests__/title-generate-hook.test.ts +0 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -2
- package/src/__tests__/tool-executor.test.ts +11 -16
- package/src/__tests__/tool-preview-lifecycle.test.ts +0 -2
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -2
- package/src/__tests__/tool-start-timestamp.test.ts +0 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
- package/src/__tests__/twilio-routes.test.ts +0 -96
- package/src/__tests__/ui-file-upload-surface.test.ts +0 -86
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +0 -33
- package/src/__tests__/web-search-backend-failure.test.ts +0 -2
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +35 -14
- package/src/__tests__/workspace-tool-loader.test.ts +2 -195
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +0 -56
- package/src/api/index.ts +1 -19
- package/src/api/responses/llm-request-log-entry.ts +0 -29
- package/src/api/responses/subagent-detail.ts +0 -17
- package/src/api/surfaces.ts +3 -39
- package/src/approvals/guardian-request-resolvers.ts +11 -1
- package/src/calls/__tests__/relay-setup-router.test.ts +4 -262
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +8 -10
- package/src/calls/inbound-trust-reader.ts +1 -17
- package/src/calls/media-stream-server.ts +0 -21
- package/src/calls/relay-server.ts +50 -167
- package/src/calls/relay-setup-router.ts +7 -37
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +12 -9
- package/src/calls/twilio-routes.ts +4 -14
- package/src/channels/types.ts +20 -10
- package/src/cli/commands/__tests__/cache.test.ts +1 -8
- package/src/cli/commands/cache.ts +181 -194
- package/src/cli/commands/db/__tests__/repair.test.ts +5 -6
- package/src/cli/commands/db/status.ts +1 -37
- package/src/cli/commands/mcp.ts +218 -252
- package/src/cli/commands/memory/index.ts +0 -2
- package/src/cli/commands/plugins.ts +3 -75
- package/src/cli/lib/__tests__/install-from-github.test.ts +0 -102
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +1 -160
- package/src/cli/lib/list-installed-plugins.ts +1 -179
- package/src/config/__tests__/sync-gated-profiles.test.ts +3 -11
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +17 -27
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +3 -13
- package/src/config/bundled-skills/subagent/SKILL.md +1 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +5 -38
- package/src/config/schemas/__tests__/memory-v3.test.ts +0 -1
- package/src/config/schemas/memory-lifecycle.ts +0 -12
- package/src/config/schemas/memory-v3.ts +0 -7
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/timeouts.ts +0 -8
- package/src/config/seed-inference-profiles.ts +11 -21
- package/src/config/skills.ts +5 -27
- package/src/config/sync-gated-profiles.ts +13 -12
- package/src/contacts/contacts-write.ts +0 -3
- package/src/daemon/assistant-attachments.ts +4 -27
- package/src/daemon/conversation-agent-loop.ts +0 -28
- package/src/daemon/conversation-process.ts +16 -35
- package/src/daemon/conversation-surfaces.ts +38 -111
- package/src/daemon/conversation-tool-setup.ts +16 -50
- package/src/daemon/conversation.ts +1 -13
- package/src/daemon/disk-pressure-guard.ts +2 -12
- package/src/daemon/event-loop-watchdog.ts +1 -28
- package/src/daemon/external-plugins-bootstrap.ts +34 -4
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -25
- package/src/daemon/handlers/config-a2a.ts +14 -6
- package/src/daemon/handlers/config-channels.ts +22 -78
- package/src/daemon/handlers/conversations.ts +0 -77
- package/src/daemon/lifecycle.ts +0 -4
- package/src/daemon/mcp-reload-service.ts +0 -10
- package/src/daemon/memory-v2-startup.test.ts +0 -72
- package/src/daemon/memory-v2-startup.ts +19 -87
- package/src/daemon/message-types/conversations.ts +0 -2
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +4 -0
- package/src/daemon/shutdown-handlers.ts +0 -20
- package/src/daemon/tool-setup-types.ts +0 -9
- package/src/daemon/workspace-tools-watcher.ts +328 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/client.ts +1 -15
- package/src/mcp/mcp-auth-orchestrator.ts +1 -6
- package/src/mcp/mcp-oauth-provider.ts +8 -19
- package/src/memory/__tests__/memory-retrospective-job.test.ts +0 -8
- package/src/memory/conversation-crud.ts +0 -38
- package/src/memory/db-connection.ts +3 -22
- package/src/memory/db-init.ts +502 -36
- package/src/memory/db-singleton.ts +4 -6
- package/src/memory/jobs-worker.ts +0 -58
- package/src/memory/llm-request-log-store.ts +1 -26
- package/src/memory/llm-usage-store.ts +20 -48
- package/src/memory/memory-retrospective-job.ts +8 -9
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +56 -130
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/registry.ts +573 -0
- package/src/memory/migrations/run-migrations.ts +6 -90
- package/src/memory/migrations/validate-migration-state.ts +66 -101
- package/src/memory/schema/conversations.ts +0 -9
- package/src/memory/schema/infrastructure.ts +0 -20
- package/src/memory/v2/__tests__/cli-command-store.test.ts +0 -25
- package/src/memory/v2/__tests__/skill-store.test.ts +0 -80
- package/src/memory/v2/cli-command-store.ts +38 -75
- package/src/memory/v2/prompts/consolidation.ts +82 -13
- package/src/memory/v2/prompts/router.ts +93 -21
- package/src/memory/v2/skill-store.ts +31 -68
- package/src/notifications/__tests__/broadcaster.test.ts +8 -16
- package/src/notifications/__tests__/decision-engine.test.ts +9 -78
- package/src/notifications/broadcaster.ts +1 -8
- package/src/notifications/decision-engine.ts +7 -15
- package/src/notifications/destination-resolver.ts +24 -68
- package/src/notifications/emit-signal.ts +14 -39
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +4 -7
- package/src/plugin-api/index.ts +6 -6
- package/src/plugin-api/types.ts +5 -3
- package/src/plugin-api/vision-support.test.ts +4 -28
- package/src/plugin-api/vision-support.ts +31 -66
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -161
- package/src/plugins/defaults/advisor/consult.ts +6 -110
- package/src/plugins/defaults/advisor/steering.ts +2 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +5 -32
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +1 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +7 -47
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +11 -10
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +20 -12
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +11 -42
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +3 -33
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +4 -48
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +8 -4
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +15 -43
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +2 -11
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -77
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +11 -12
- package/src/plugins/mtime-cache.ts +291 -76
- package/src/plugins/pipeline.ts +13 -111
- package/src/plugins/types.ts +2 -0
- package/src/providers/anthropic/client.ts +0 -5
- package/src/providers/call-site-routing.ts +0 -4
- package/src/providers/model-catalog.ts +0 -16
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/chat-completions-provider.ts +83 -37
- package/src/providers/openai/responses-provider.ts +46 -50
- package/src/providers/openrouter/client.ts +0 -5
- package/src/providers/provider-send-message.ts +0 -4
- package/src/providers/ratelimit.ts +0 -4
- package/src/providers/retry.ts +0 -4
- package/src/providers/types.ts +0 -9
- package/src/providers/usage-tracking.ts +0 -4
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +3 -335
- package/src/runtime/access-request-helper.ts +39 -19
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +2 -9
- package/src/runtime/auth/require-bound-guardian.ts +11 -21
- package/src/runtime/channel-verification-service.ts +31 -56
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +7 -66
- package/src/runtime/invite-redemption-service.ts +187 -198
- package/src/runtime/local-actor-identity.ts +11 -76
- package/src/runtime/pending-interactions.ts +1 -11
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +5 -56
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +0 -187
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -13
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +32 -8
- package/src/runtime/routes/conversation-cli-routes.ts +5 -4
- package/src/runtime/routes/conversation-list-routes.ts +7 -4
- package/src/runtime/routes/conversation-query-routes.ts +0 -72
- package/src/runtime/routes/conversation-routes.ts +85 -84
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +1 -3
- package/src/runtime/routes/guardian-action-routes.ts +5 -4
- package/src/runtime/routes/host-app-control-routes.ts +4 -5
- package/src/runtime/routes/host-bash-routes.ts +4 -5
- package/src/runtime/routes/host-browser-routes.ts +11 -9
- package/src/runtime/routes/host-cu-routes.ts +4 -5
- package/src/runtime/routes/host-file-routes.ts +4 -5
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +2 -3
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +5 -97
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +49 -61
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -16
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +8 -21
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +3 -14
- package/src/runtime/routes/index.ts +0 -2
- package/src/runtime/routes/llm-context-normalization.ts +0 -83
- package/src/runtime/routes/mcp-auth-routes.ts +19 -171
- package/src/runtime/routes/migration-rollback-routes.ts +3 -4
- package/src/runtime/routes/migration-routes.ts +1 -4
- package/src/runtime/routes/subagents-routes.ts +0 -5
- package/src/runtime/routes/surface-action-routes.ts +56 -42
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
- package/src/runtime/services/conversation-serializer.ts +9 -7
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +9 -85
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +0 -16
- package/src/subagent/manager.ts +0 -9
- package/src/subagent/types.ts +3 -3
- package/src/telemetry/types.ts +1 -34
- package/src/telemetry/usage-telemetry-reporter.test.ts +2 -3
- package/src/telemetry/usage-telemetry-reporter.ts +3 -87
- package/src/tools/ask-question/ask-question-tool.test.ts +0 -29
- package/src/tools/ask-question/ask-question-tool.ts +0 -13
- package/src/tools/executor.ts +4 -4
- package/src/tools/registry.ts +0 -18
- package/src/tools/shared/filesystem/path-policy.ts +5 -12
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +2 -9
- package/src/tools/tool-manifest.ts +0 -3
- package/src/tools/types.ts +2 -17
- package/src/tools/workspace-tools/loader.ts +244 -348
- package/src/util/errors.ts +1 -26
- package/src/util/platform.ts +0 -5
- package/src/workflows/library.test.ts +0 -140
- package/src/workflows/library.ts +28 -82
- package/src/workspace/migrations/017-seed-persona-dirs.ts +34 -3
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +24 -3
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +66 -14
- package/src/workspace/migrations/registry.ts +0 -2
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +0 -91
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +0 -48
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +0 -28
- package/node_modules/@vellumai/service-contracts/src/channels.ts +0 -41
- package/src/__tests__/code-search-tool.test.ts +0 -585
- package/src/__tests__/guardian-expiry-notifier.test.ts +0 -282
- package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -390
- package/src/__tests__/plugin-pipeline.test.ts +0 -96
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +0 -102
- package/src/__tests__/steer-on-enqueue-question.test.ts +0 -181
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +0 -208
- package/src/agent/loop-exclusive-tool.test.ts +0 -150
- package/src/api/constants/sse-replay.ts +0 -41
- package/src/api/events/conversation-notice.ts +0 -26
- package/src/approvals/guardian-channel-delivery.ts +0 -30
- package/src/approvals/guardian-expiry-notifier.ts +0 -148
- package/src/cli/commands/memory/__tests__/worker.test.ts +0 -302
- package/src/cli/commands/memory/worker.ts +0 -175
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -143
- package/src/config/prune-seeded-callsite-defaults.ts +0 -110
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -129
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +0 -312
- package/src/contacts/__tests__/member-write-relay.test.ts +0 -202
- package/src/contacts/guardian-delivery-reader.ts +0 -223
- package/src/contacts/member-write-relay.ts +0 -189
- package/src/daemon/conversation-notices.ts +0 -60
- package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -225
- package/src/hooks/hook-loader.ts +0 -341
- package/src/mcp/mcp-header-store.ts +0 -134
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +0 -110
- package/src/memory/__tests__/prompt-override.test.ts +0 -192
- package/src/memory/__tests__/watchdog-events-store.test.ts +0 -161
- package/src/memory/migrations/300-add-processing-started-at.ts +0 -30
- package/src/memory/migrations/301-create-watchdog-events.ts +0 -45
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +0 -224
- package/src/memory/prompt-override.ts +0 -129
- package/src/memory/steps.ts +0 -573
- package/src/memory/watchdog-events-store.ts +0 -87
- package/src/memory/worker-control.ts +0 -118
- package/src/memory/worker-process.ts +0 -72
- package/src/notifications/__tests__/connected-channels.test.ts +0 -114
- package/src/notifications/__tests__/destination-resolver.test.ts +0 -256
- package/src/onboarding/checkin-event.test.ts +0 -222
- package/src/onboarding/checkin-event.ts +0 -321
- package/src/onboarding/schedule-checkin.ts +0 -190
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
- package/src/plugins/defaults/advisor/context-pack.ts +0 -288
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +0 -146
- package/src/plugins/surface-import.ts +0 -121
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -321
- package/src/providers/openai/api-error-normalization.ts +0 -270
- package/src/runtime/__tests__/channel-verification-service.test.ts +0 -133
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +0 -181
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +0 -66
- package/src/runtime/__tests__/local-principal-trust.test.ts +0 -164
- package/src/runtime/anchored-guardian.test.ts +0 -156
- package/src/runtime/anchored-guardian.ts +0 -135
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +0 -99
- package/src/runtime/local-principal-trust.ts +0 -52
- package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -212
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -93
- package/src/runtime/routes/onboarding-checkin-routes.ts +0 -86
- package/src/tools/filesystem/search.ts +0 -543
- package/src/util/telemetry-db-path.ts +0 -24
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +0 -134
|
@@ -3,17 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A plugin that gates image processing on vision capability (e.g. an
|
|
5
5
|
* image-to-text fallback for text-only models) calls {@link doesSupportVision}
|
|
6
|
-
* instead of hardcoding model names.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* resolved through `llm.profiles` to an effective `(provider, model)`.
|
|
11
|
-
*
|
|
12
|
-
* A bare string is tried as a model id first and then as a profile key, so the
|
|
13
|
-
* two callers share one function. Resolution returns `false` when nothing
|
|
14
|
-
* resolves (rather than failing open): a consumer gating an image→text
|
|
15
|
-
* fallback wants an unknown model treated as "can't show images" — caption it —
|
|
16
|
-
* over silently shipping a raw image to a provider that may reject it.
|
|
6
|
+
* instead of hardcoding model names. The function resolves the effective
|
|
7
|
+
* (provider, model) for a profile — merging with `llm.default` to fill gaps,
|
|
8
|
+
* inferring the provider for model-only profiles via the catalog — and then
|
|
9
|
+
* looks up `supportsVision` in the model catalog.
|
|
17
10
|
*/
|
|
18
11
|
|
|
19
12
|
import { getConfig } from "../config/loader.js";
|
|
@@ -24,74 +17,46 @@ import {
|
|
|
24
17
|
import type { ModelProfileInfo } from "./types.js";
|
|
25
18
|
|
|
26
19
|
/**
|
|
27
|
-
* Whether
|
|
20
|
+
* Whether a profile's resolved model can process image input.
|
|
28
21
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
return profileVision(modelOrProfile.key) ?? false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Catalog vision flag for a concrete model id, or `undefined` when the catalog
|
|
47
|
-
* doesn't know the model. The same model id carries the same capability under
|
|
48
|
-
* every provider that offers it, so the first catalog match wins.
|
|
22
|
+
* Resolution mirrors the host's call-site resolver:
|
|
23
|
+
* - The profile's `(provider, model)` fields are merged over `llm.default` so
|
|
24
|
+
* a profile that only sets `model` (or only `provider`) inherits the other
|
|
25
|
+
* from the workspace default.
|
|
26
|
+
* - When `provider` is still missing but `model` is a known catalog model,
|
|
27
|
+
* the provider is inferred via `getCatalogProviderForModel` (same logic as
|
|
28
|
+
* the resolver's `withImpliedProviderForKnownModel`).
|
|
29
|
+
* - For a mix profile, returns `true` if any constituent arm supports vision
|
|
30
|
+
* (the mix can route to it) and `false` only if every arm is text-only.
|
|
31
|
+
* - Unknown `(provider, model)` pairs default to `true` (fail-open), matching
|
|
32
|
+
* the config GET route's `enrichProfilesWithVisionFlag`.
|
|
49
33
|
*/
|
|
50
|
-
function
|
|
51
|
-
for (const provider of PROVIDER_CATALOG) {
|
|
52
|
-
const catalogModel = provider.models.find((m) => m.id === model);
|
|
53
|
-
if (catalogModel != null) return catalogModel.supportsVision ?? false;
|
|
54
|
-
}
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Resolve a profile key through `llm.profiles` to its vision capability, or
|
|
60
|
-
* `undefined` when the key is unknown or resolves to a model the catalog
|
|
61
|
-
* doesn't know. A mix profile resolves to `true` if any arm supports vision
|
|
62
|
-
* (the mix can route to it) and `false` only once every arm is a known
|
|
63
|
-
* text-only model.
|
|
64
|
-
*/
|
|
65
|
-
function profileVision(profileKey: string): boolean | undefined {
|
|
34
|
+
export function doesSupportVision(profile: ModelProfileInfo): boolean {
|
|
66
35
|
const { llm } = getConfig();
|
|
67
|
-
const entry = llm.profiles[
|
|
68
|
-
if (entry == null) return
|
|
36
|
+
const entry = llm.profiles[profile.key];
|
|
37
|
+
if (entry == null) return true;
|
|
69
38
|
|
|
39
|
+
// Mix: fail-open if any arm supports vision.
|
|
70
40
|
if (entry.mix != null) {
|
|
71
|
-
|
|
72
|
-
for (const arm of entry.mix) {
|
|
41
|
+
return entry.mix.some((arm) => {
|
|
73
42
|
const armEntry = llm.profiles[arm.profile];
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (armVision == null) sawUnknown = true;
|
|
78
|
-
}
|
|
79
|
-
return sawUnknown ? undefined : false;
|
|
43
|
+
if (armEntry == null) return true;
|
|
44
|
+
return resolveEntrySupportsVision(armEntry, llm);
|
|
45
|
+
});
|
|
80
46
|
}
|
|
81
47
|
|
|
82
|
-
return
|
|
48
|
+
return resolveEntrySupportsVision(entry, llm);
|
|
83
49
|
}
|
|
84
50
|
|
|
85
51
|
/**
|
|
86
52
|
* Resolve whether a concrete (non-mix) profile entry supports vision by
|
|
87
|
-
* merging its fields over `llm.default` and inferring the provider when
|
|
88
|
-
* the model is set.
|
|
89
|
-
* can't be determined or isn't in the catalog.
|
|
53
|
+
* merging its fields over `llm.default` and inferring the provider when
|
|
54
|
+
* only the model is set.
|
|
90
55
|
*/
|
|
91
|
-
function
|
|
56
|
+
function resolveEntrySupportsVision(
|
|
92
57
|
entry: { provider?: string; model?: string },
|
|
93
58
|
llm: { default?: { provider?: string; model?: string } },
|
|
94
|
-
): boolean
|
|
59
|
+
): boolean {
|
|
95
60
|
const provider = entry.provider ?? llm.default?.provider;
|
|
96
61
|
const model = entry.model ?? llm.default?.model;
|
|
97
62
|
|
|
@@ -102,12 +67,12 @@ function resolveEntryVision(
|
|
|
102
67
|
(typeof model === "string" ? getCatalogProviderForModel(model) : undefined);
|
|
103
68
|
|
|
104
69
|
if (typeof effectiveProvider !== "string" || typeof model !== "string") {
|
|
105
|
-
return
|
|
70
|
+
return true; // fail-open
|
|
106
71
|
}
|
|
107
72
|
|
|
108
73
|
const catalogProvider = PROVIDER_CATALOG.find(
|
|
109
74
|
(p) => p.id === effectiveProvider,
|
|
110
75
|
);
|
|
111
76
|
const catalogModel = catalogProvider?.models.find((m) => m.id === model);
|
|
112
|
-
return catalogModel?.supportsVision;
|
|
77
|
+
return catalogModel?.supportsVision ?? true;
|
|
113
78
|
}
|
|
@@ -8,26 +8,12 @@ let sendMessageArgs: Record<string, unknown> | null = null;
|
|
|
8
8
|
let responseText = "Use a channel-based worker pool; drain on shutdown.";
|
|
9
9
|
let sendMessageError: Error | null = null;
|
|
10
10
|
let providerResolves = true;
|
|
11
|
-
let providerSupportsWeb = false;
|
|
12
|
-
let streamDeltas: string[] = [];
|
|
13
|
-
let streamEvents: Array<Record<string, unknown>> = [];
|
|
14
11
|
|
|
15
12
|
const fakeProvider = {
|
|
16
13
|
name: "mock-advisor-provider",
|
|
17
|
-
get supportsNativeWebSearch() {
|
|
18
|
-
return providerSupportsWeb;
|
|
19
|
-
},
|
|
20
14
|
async sendMessage(messages: unknown, options: unknown) {
|
|
21
15
|
sendMessageArgs = { messages, options } as Record<string, unknown>;
|
|
22
16
|
if (sendMessageError) throw sendMessageError;
|
|
23
|
-
const onEvent = (
|
|
24
|
-
options as { onEvent?: (e: Record<string, unknown>) => void }
|
|
25
|
-
).onEvent;
|
|
26
|
-
if (onEvent) {
|
|
27
|
-
// Activity (search/thinking) streams before the final advice text.
|
|
28
|
-
for (const ev of streamEvents) onEvent(ev);
|
|
29
|
-
for (const text of streamDeltas) onEvent({ type: "text_delta", text });
|
|
30
|
-
}
|
|
31
17
|
return {
|
|
32
18
|
content: [{ type: "text", text: responseText }],
|
|
33
19
|
model: "mock-model",
|
|
@@ -43,14 +29,6 @@ mock.module("../../../../providers/provider-send-message.js", () => ({
|
|
|
43
29
|
getConfiguredProvider: async () => (providerResolves ? fakeProvider : null),
|
|
44
30
|
}));
|
|
45
31
|
|
|
46
|
-
// Keep the tool tests focused on the consult wiring: stub the context pack so
|
|
47
|
-
// they don't reach into the registry / workspace / memory sources (those have
|
|
48
|
-
// their own coverage). The consult itself never imports this module.
|
|
49
|
-
mock.module("../context-pack.js", () => ({
|
|
50
|
-
buildAdvisorContext: async () => null,
|
|
51
|
-
deriveRecallQuery: () => null,
|
|
52
|
-
}));
|
|
53
|
-
|
|
54
32
|
const { consultAdvisor } = await import("../consult.js");
|
|
55
33
|
const advisorTool = (await import("../tools/advisor.js")).default;
|
|
56
34
|
const { recordSystemPrompt, recordMessages, resetAdvisorStateForTests } =
|
|
@@ -71,9 +49,6 @@ beforeEach(() => {
|
|
|
71
49
|
responseText = "Use a channel-based worker pool; drain on shutdown.";
|
|
72
50
|
sendMessageError = null;
|
|
73
51
|
providerResolves = true;
|
|
74
|
-
providerSupportsWeb = false;
|
|
75
|
-
streamDeltas = [];
|
|
76
|
-
streamEvents = [];
|
|
77
52
|
resetAdvisorStateForTests();
|
|
78
53
|
});
|
|
79
54
|
|
|
@@ -125,104 +100,6 @@ describe("consultAdvisor", () => {
|
|
|
125
100
|
expect(options.systemPrompt).toContain("You are a coding agent.");
|
|
126
101
|
});
|
|
127
102
|
|
|
128
|
-
test("stays tool-less when the provider has no native web search", async () => {
|
|
129
|
-
providerSupportsWeb = false;
|
|
130
|
-
await consultAdvisor({ systemPrompt: null, messages: [userMsg("hi")] });
|
|
131
|
-
const options = sendMessageArgs?.options as { tools?: unknown };
|
|
132
|
-
expect(options.tools).toBeUndefined();
|
|
133
|
-
expect(optionConfig().tool_choice).toEqual({ type: "none" });
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("enables native web search when the provider supports it", async () => {
|
|
137
|
-
providerSupportsWeb = true;
|
|
138
|
-
await consultAdvisor({ systemPrompt: null, messages: [userMsg("hi")] });
|
|
139
|
-
|
|
140
|
-
const options = sendMessageArgs?.options as {
|
|
141
|
-
tools?: Array<{ name: string }>;
|
|
142
|
-
};
|
|
143
|
-
expect(options.tools?.map((t) => t.name)).toEqual(["web_search"]);
|
|
144
|
-
// tool_choice must not be `none`, or the provider suppresses its server tool.
|
|
145
|
-
expect(optionConfig().tool_choice).toEqual({ type: "auto" });
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
test("streams web-search activity to onText, not just the final advice", async () => {
|
|
149
|
-
providerSupportsWeb = true;
|
|
150
|
-
streamEvents = [
|
|
151
|
-
{ type: "server_tool_start", name: "web_search", toolUseId: "s1", input: {} },
|
|
152
|
-
{
|
|
153
|
-
type: "server_tool_complete",
|
|
154
|
-
toolUseId: "s1",
|
|
155
|
-
isError: false,
|
|
156
|
-
resolvedInput: { query: "vellum streaming" },
|
|
157
|
-
},
|
|
158
|
-
];
|
|
159
|
-
streamDeltas = ["Here is ", "the advice."];
|
|
160
|
-
const chunks: string[] = [];
|
|
161
|
-
|
|
162
|
-
await consultAdvisor({
|
|
163
|
-
systemPrompt: null,
|
|
164
|
-
messages: [userMsg("hi")],
|
|
165
|
-
onText: (c) => chunks.push(c),
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
const joined = chunks.join("");
|
|
169
|
-
// The drawer isn't silent during the search prefix...
|
|
170
|
-
expect(joined).toContain("Searching the web");
|
|
171
|
-
expect(joined).toContain("Searched: vellum streaming");
|
|
172
|
-
// ...and the advice text still streams.
|
|
173
|
-
expect(joined).toContain("Here is the advice.");
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("surfaces a failure note (not 'Searched') when a web search errors", async () => {
|
|
177
|
-
providerSupportsWeb = true;
|
|
178
|
-
streamEvents = [
|
|
179
|
-
{ type: "server_tool_start", name: "web_search", toolUseId: "s1", input: {} },
|
|
180
|
-
{
|
|
181
|
-
type: "server_tool_complete",
|
|
182
|
-
toolUseId: "s1",
|
|
183
|
-
isError: true,
|
|
184
|
-
errorCode: "query_too_long",
|
|
185
|
-
resolvedInput: { query: "an overly long query" },
|
|
186
|
-
},
|
|
187
|
-
];
|
|
188
|
-
streamDeltas = ["Proceeding without search."];
|
|
189
|
-
const chunks: string[] = [];
|
|
190
|
-
|
|
191
|
-
await consultAdvisor({
|
|
192
|
-
systemPrompt: null,
|
|
193
|
-
messages: [userMsg("hi")],
|
|
194
|
-
onText: (c) => chunks.push(c),
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
const joined = chunks.join("");
|
|
198
|
-
expect(joined).toContain("Web search failed");
|
|
199
|
-
expect(joined).not.toContain("🔎 Searched:");
|
|
200
|
-
// The consult still continues and streams its guidance.
|
|
201
|
-
expect(joined).toContain("Proceeding without search.");
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test("streams the model's reasoning summary to onText", async () => {
|
|
205
|
-
streamEvents = [{ type: "thinking_delta", thinking: "weighing tradeoffs" }];
|
|
206
|
-
const chunks: string[] = [];
|
|
207
|
-
await consultAdvisor({
|
|
208
|
-
systemPrompt: null,
|
|
209
|
-
messages: [userMsg("hi")],
|
|
210
|
-
onText: (c) => chunks.push(c),
|
|
211
|
-
});
|
|
212
|
-
expect(chunks.join("")).toContain("weighing tradeoffs");
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test("embeds the runtime context in the advisor system prompt", async () => {
|
|
216
|
-
await consultAdvisor({
|
|
217
|
-
systemPrompt: "You are a coding agent.",
|
|
218
|
-
messages: [userMsg("hi")],
|
|
219
|
-
runtimeContext: "## Available tools\n- bash — run commands",
|
|
220
|
-
});
|
|
221
|
-
const options = sendMessageArgs?.options as { systemPrompt: string };
|
|
222
|
-
expect(options.systemPrompt).toContain("<agent_runtime_context>");
|
|
223
|
-
expect(options.systemPrompt).toContain("- bash — run commands");
|
|
224
|
-
});
|
|
225
|
-
|
|
226
103
|
test("soft-fails when no provider is configured", async () => {
|
|
227
104
|
providerResolves = false;
|
|
228
105
|
const advice = await consultAdvisor({
|
|
@@ -246,29 +123,6 @@ describe("consultAdvisor", () => {
|
|
|
246
123
|
});
|
|
247
124
|
expect(advice).toContain("no guidance");
|
|
248
125
|
});
|
|
249
|
-
|
|
250
|
-
test("streams the model's text deltas to `onText` as it generates", async () => {
|
|
251
|
-
streamDeltas = ["Use a ", "channel-based ", "worker pool."];
|
|
252
|
-
const chunks: string[] = [];
|
|
253
|
-
|
|
254
|
-
const advice = await consultAdvisor({
|
|
255
|
-
systemPrompt: null,
|
|
256
|
-
messages: [userMsg("hi")],
|
|
257
|
-
onText: (c) => chunks.push(c),
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// Each visible delta is forwarded live...
|
|
261
|
-
expect(chunks).toEqual(["Use a ", "channel-based ", "worker pool."]);
|
|
262
|
-
// ...and the complete guidance is still returned.
|
|
263
|
-
expect(advice).toBe(responseText);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
test("registers no `onEvent` sink when `onText` is absent", async () => {
|
|
267
|
-
streamDeltas = ["x"];
|
|
268
|
-
await consultAdvisor({ systemPrompt: null, messages: [userMsg("hi")] });
|
|
269
|
-
const options = sendMessageArgs?.options as { onEvent?: unknown };
|
|
270
|
-
expect(options.onEvent).toBeUndefined();
|
|
271
|
-
});
|
|
272
126
|
});
|
|
273
127
|
|
|
274
128
|
describe("advisor tool.execute", () => {
|
|
@@ -296,19 +150,4 @@ describe("advisor tool.execute", () => {
|
|
|
296
150
|
expect(result?.content).toContain("advisor unavailable");
|
|
297
151
|
expect(result?.content).toContain("kaboom");
|
|
298
152
|
});
|
|
299
|
-
|
|
300
|
-
test("streams the consult live via `ctx.onOutput`", async () => {
|
|
301
|
-
recordMessages("c3", [userMsg("hi")]);
|
|
302
|
-
streamDeltas = ["plan: ", "do X"];
|
|
303
|
-
const out: string[] = [];
|
|
304
|
-
|
|
305
|
-
const result = await advisorTool.execute?.({}, {
|
|
306
|
-
conversationId: "c3",
|
|
307
|
-
onOutput: (c: string) => out.push(c),
|
|
308
|
-
} as never);
|
|
309
|
-
|
|
310
|
-
expect(out).toEqual(["plan: ", "do X"]);
|
|
311
|
-
expect(result?.isError).toBe(false);
|
|
312
|
-
expect(result?.content).toBe(responseText);
|
|
313
|
-
});
|
|
314
153
|
});
|
|
@@ -16,11 +16,7 @@ import {
|
|
|
16
16
|
getConfiguredProvider,
|
|
17
17
|
userMessage,
|
|
18
18
|
} from "../../../providers/provider-send-message.js";
|
|
19
|
-
import type {
|
|
20
|
-
Message,
|
|
21
|
-
ProviderEvent,
|
|
22
|
-
ToolDefinition,
|
|
23
|
-
} from "../../../providers/types.js";
|
|
19
|
+
import type { Message } from "../../../providers/types.js";
|
|
24
20
|
import { ADVISOR_CONFIG } from "./config.js";
|
|
25
21
|
import { advisorRequestText, buildAdvisorSystem } from "./steering.js";
|
|
26
22
|
import { toAdvisorMessages } from "./transcript.js";
|
|
@@ -30,26 +26,6 @@ import { toAdvisorMessages } from "./transcript.js";
|
|
|
30
26
|
// on via `llm.advisorProfile`, which we float above the call-site layers.
|
|
31
27
|
const ADVISOR_CALL_SITE: LLMCallSite = "advisor";
|
|
32
28
|
|
|
33
|
-
/**
|
|
34
|
-
* The single tool the consult may attach: a `web_search`-named tool that
|
|
35
|
-
* provider-native search (Anthropic/OpenAI) substitutes for its server-side
|
|
36
|
-
* web tool. Only passed when `provider.supportsNativeWebSearch` is true, so the
|
|
37
|
-
* provider runs the search itself and returns results inline — no agent loop,
|
|
38
|
-
* which keeps the consult a one-shot completion.
|
|
39
|
-
*/
|
|
40
|
-
const ADVISOR_WEB_SEARCH_TOOL: ToolDefinition = {
|
|
41
|
-
name: "web_search",
|
|
42
|
-
description:
|
|
43
|
-
"Search the web for current information to ground your guidance.",
|
|
44
|
-
input_schema: {
|
|
45
|
-
type: "object",
|
|
46
|
-
properties: {
|
|
47
|
-
query: { type: "string", description: "The search query." },
|
|
48
|
-
},
|
|
49
|
-
required: ["query"],
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
29
|
/**
|
|
54
30
|
* Resolve the routing override for the advisor consult. When the workspace has
|
|
55
31
|
* set `llm.advisorProfile`, force it above the call-site layers so it is
|
|
@@ -69,22 +45,7 @@ function advisorOverride(): {
|
|
|
69
45
|
export interface ConsultParams {
|
|
70
46
|
systemPrompt: string | null;
|
|
71
47
|
messages: ReadonlyArray<Message>;
|
|
72
|
-
/**
|
|
73
|
-
* The agent's runtime context (available tools and skills, workspace/project
|
|
74
|
-
* context, recalled memory), gathered by the tool from its `ToolContext`.
|
|
75
|
-
* Embedded in the advisor's system prompt so its advice is grounded in what
|
|
76
|
-
* the agent can actually do. Omitted/null when nothing could be gathered.
|
|
77
|
-
*/
|
|
78
|
-
runtimeContext?: string | null;
|
|
79
48
|
signal?: AbortSignal;
|
|
80
|
-
/**
|
|
81
|
-
* Optional sink for the advisor's live activity as it generates: incremental
|
|
82
|
-
* advice text, the reasoning summary (when surfaced), and a note per web
|
|
83
|
-
* search. Wiring this to the tool's `onOutput` surfaces the consult live as
|
|
84
|
-
* `tool_output_chunk` while the advisor is still working; the complete
|
|
85
|
-
* guidance is still returned as the resolved string. See `advisorActivitySink`.
|
|
86
|
-
*/
|
|
87
|
-
onText?: (chunk: string) => void;
|
|
88
49
|
}
|
|
89
50
|
|
|
90
51
|
/** Combine the caller's signal with a consult timeout. */
|
|
@@ -93,55 +54,6 @@ function withTimeout(signal: AbortSignal | undefined, ms: number): AbortSignal {
|
|
|
93
54
|
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
94
55
|
}
|
|
95
56
|
|
|
96
|
-
/**
|
|
97
|
-
* Build the streaming sink for a consult: forward the advisor's live activity
|
|
98
|
-
* to `onText` so the tool-output drawer streams throughout the consult instead
|
|
99
|
-
* of sitting silent until the final advice lands.
|
|
100
|
-
*
|
|
101
|
-
* The consult searches the web (up to 5×) and reasons over full context before
|
|
102
|
-
* writing its guidance. Forwarding the visible advice text alone would leave
|
|
103
|
-
* the drawer blank for that whole prefix, so the sink also surfaces the
|
|
104
|
-
* reasoning summary (when the model emits one) and a one-line note per web
|
|
105
|
-
* search — a success note with the query, or a failure note when the search
|
|
106
|
-
* errors. The complete guidance is still returned by `consultAdvisor`; the
|
|
107
|
-
* renderer swaps it in once the tool result arrives.
|
|
108
|
-
*/
|
|
109
|
-
function advisorActivitySink(
|
|
110
|
-
onText: (chunk: string) => void,
|
|
111
|
-
): (event: ProviderEvent) => void {
|
|
112
|
-
return (event) => {
|
|
113
|
-
switch (event.type) {
|
|
114
|
-
case "text_delta":
|
|
115
|
-
if (event.text) onText(event.text);
|
|
116
|
-
break;
|
|
117
|
-
case "thinking_delta":
|
|
118
|
-
if (event.thinking) onText(event.thinking);
|
|
119
|
-
break;
|
|
120
|
-
case "server_tool_start":
|
|
121
|
-
if (event.name === "web_search") onText("\n🔎 Searching the web…\n");
|
|
122
|
-
break;
|
|
123
|
-
case "server_tool_complete": {
|
|
124
|
-
const rawQuery = event.resolvedInput?.["query"];
|
|
125
|
-
const query = typeof rawQuery === "string" ? rawQuery.trim() : "";
|
|
126
|
-
if (event.isError) {
|
|
127
|
-
// A failed search (e.g. `query_too_long`, `max_uses_exceeded`) must
|
|
128
|
-
// not be announced as a success — the advisor proceeds without it.
|
|
129
|
-
onText(
|
|
130
|
-
query
|
|
131
|
-
? `\n⚠️ Web search failed: ${query}\n`
|
|
132
|
-
: "\n⚠️ Web search failed.\n",
|
|
133
|
-
);
|
|
134
|
-
} else if (query) {
|
|
135
|
-
onText(`\n🔎 Searched: ${query}\n`);
|
|
136
|
-
}
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
default:
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
57
|
/**
|
|
146
58
|
* Returns the advisor's guidance text, or a short benign notice when the
|
|
147
59
|
* advisor can't run. Callers should surface the string as a non-error tool
|
|
@@ -161,33 +73,17 @@ export async function consultAdvisor(params: ConsultParams): Promise<string> {
|
|
|
161
73
|
}
|
|
162
74
|
|
|
163
75
|
// Append the consult instruction as the final user turn, then run a
|
|
164
|
-
// completion through the resolved provider. No `max_tokens` is
|
|
165
|
-
// resolver applies the profile's normal output budget rather
|
|
166
|
-
// advisor-specific cap.
|
|
76
|
+
// tool-less completion through the resolved provider. No `max_tokens` is
|
|
77
|
+
// set, so the resolver applies the profile's normal output budget rather
|
|
78
|
+
// than an advisor-specific cap.
|
|
167
79
|
const messages: Message[] = [...history, userMessage(advisorRequestText())];
|
|
168
80
|
|
|
169
|
-
// Give the advisor live web access when — and only when — the resolved
|
|
170
|
-
// provider runs search server-side (provider-native). Passing a `web_search`
|
|
171
|
-
// tool to a non-native provider would surface a client tool call this
|
|
172
|
-
// one-shot consult cannot execute, so we gate strictly on the capability and
|
|
173
|
-
// otherwise keep the consult tool-less.
|
|
174
|
-
const webEnabled = provider.supportsNativeWebSearch === true;
|
|
175
|
-
|
|
176
|
-
const { onText } = params;
|
|
177
81
|
const response = await provider.sendMessage(messages, {
|
|
178
|
-
systemPrompt: buildAdvisorSystem(
|
|
179
|
-
params.systemPrompt,
|
|
180
|
-
params.runtimeContext,
|
|
181
|
-
),
|
|
182
|
-
...(webEnabled ? { tools: [ADVISOR_WEB_SEARCH_TOOL] } : {}),
|
|
183
|
-
// Stream the consult's activity live (advice text, reasoning summary, and a
|
|
184
|
-
// note per web search) so the drawer isn't blank while the advisor searches
|
|
185
|
-
// and reasons before writing its guidance. See `advisorActivitySink`.
|
|
186
|
-
onEvent: onText ? advisorActivitySink(onText) : undefined,
|
|
82
|
+
systemPrompt: buildAdvisorSystem(params.systemPrompt),
|
|
187
83
|
config: {
|
|
188
84
|
callSite: ADVISOR_CALL_SITE,
|
|
189
85
|
...override,
|
|
190
|
-
tool_choice:
|
|
86
|
+
tool_choice: { type: "none" },
|
|
191
87
|
},
|
|
192
88
|
signal: withTimeout(params.signal, ADVISOR_CONFIG.timeoutMs),
|
|
193
89
|
});
|
|
@@ -33,15 +33,9 @@ export function stripSteering(systemPrompt: string | null): string | null {
|
|
|
33
33
|
* System prompt for the advisor sub-call. Frames the advisor's role and, for
|
|
34
34
|
* context, quotes the executor's own system prompt (as the advisor tool does —
|
|
35
35
|
* the advisor sees the system prompt as context about the executor's task).
|
|
36
|
-
*
|
|
37
|
-
* `runtimeContext`, when present, carries the agent's situational context that
|
|
38
|
-
* lives outside its system prompt — available tools and skills, workspace /
|
|
39
|
-
* project context, and recalled memory (see `buildAdvisorContext`) — so the
|
|
40
|
-
* advisor can ground its recommendations in what the agent can actually do.
|
|
41
36
|
*/
|
|
42
37
|
export function buildAdvisorSystem(
|
|
43
38
|
originalSystemPrompt: string | null,
|
|
44
|
-
runtimeContext?: string | null,
|
|
45
39
|
): string {
|
|
46
40
|
const base = `You are a senior advisor consulted by another AI agent working on a task — most often at the planning stage, before it starts building, but sometimes partway through. The entire conversation above is the agent's working context: its task or goal, every tool call it has made, and every result it has seen. The agent has paused to consult you because you bring a second, independent perspective it cannot get from inside its own reasoning loop. Your job is to maximize its odds of completing the task correctly and efficiently.
|
|
47
41
|
|
|
@@ -60,14 +54,8 @@ How to advise:
|
|
|
60
54
|
- Stay in your lane. Advise the agent; do not role-play as it, write its final deliverable, or take its next action for it. If the agent is already on the right track, confirm it and sharpen the plan rather than manufacturing objections.
|
|
61
55
|
|
|
62
56
|
Write as much as the guidance genuinely needs, and no more.`;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
prompt += `\n\nFor context, the agent is operating under this system prompt:\n<agent_system_prompt>\n${originalSystemPrompt}\n</agent_system_prompt>`;
|
|
66
|
-
}
|
|
67
|
-
if (runtimeContext) {
|
|
68
|
-
prompt += `\n\nThe agent's runtime context — the tools and skills available to it, the loaded workspace/project context, and relevant memory — follows. Ground your recommendations in what the agent can actually do and what is around it; reference specific tools, skills, files, or memory where relevant.\n<agent_runtime_context>\n${runtimeContext}\n</agent_runtime_context>`;
|
|
69
|
-
}
|
|
70
|
-
return prompt;
|
|
57
|
+
if (!originalSystemPrompt) return base;
|
|
58
|
+
return `${base}\n\nFor context, the agent is operating under this system prompt:\n<agent_system_prompt>\n${originalSystemPrompt}\n</agent_system_prompt>`;
|
|
71
59
|
}
|
|
72
60
|
|
|
73
61
|
/**
|
|
@@ -19,26 +19,19 @@ import { RiskLevel } from "@vellumai/plugin-api";
|
|
|
19
19
|
import { advisorEnabledForProfile } from "../advisor-gate.js";
|
|
20
20
|
import { getCapture } from "../advisor-state-store.js";
|
|
21
21
|
import { consultAdvisor } from "../consult.js";
|
|
22
|
-
import { buildAdvisorContext } from "../context-pack.js";
|
|
23
22
|
|
|
24
23
|
const advisorTool: ToolDefinition = {
|
|
25
24
|
name: "advisor",
|
|
26
25
|
description:
|
|
27
26
|
"Consult a stronger advisor model to shape your plan and get strategic guidance. " +
|
|
28
27
|
"Takes NO parameters — your full conversation (the task, every tool call, and every " +
|
|
29
|
-
"result) is forwarded automatically
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"once before declaring a task complete. It runs on its own — if you call it alongside " +
|
|
34
|
-
"other tools, those are held back until you've seen its guidance. Give its guidance " +
|
|
35
|
-
"serious weight.",
|
|
28
|
+
"result) is forwarded automatically. Call it BEFORE you start building: it can lay out " +
|
|
29
|
+
"a plan when you don't have one yet, or review and sharpen the plan you've already " +
|
|
30
|
+
"drafted. Also call it when you're stuck, when weighing a change in approach, and once " +
|
|
31
|
+
"before declaring a task complete. Give its guidance serious weight.",
|
|
36
32
|
input_schema: { type: "object", properties: {}, additionalProperties: false },
|
|
37
33
|
// Read-only advice; low risk so the consult isn't gated behind a prompt.
|
|
38
34
|
defaultRiskLevel: RiskLevel.Low,
|
|
39
|
-
// Runs alone in its turn: the loop defers any sibling tool calls so the model
|
|
40
|
-
// incorporates the advisor's guidance before acting on anything else.
|
|
41
|
-
exclusive: true,
|
|
42
35
|
async execute(
|
|
43
36
|
_input: Record<string, unknown>,
|
|
44
37
|
ctx: ToolContext,
|
|
@@ -55,30 +48,10 @@ const advisorTool: ToolDefinition = {
|
|
|
55
48
|
}
|
|
56
49
|
try {
|
|
57
50
|
const capture = getCapture(ctx.conversationId);
|
|
58
|
-
const messages = capture?.messages ?? [];
|
|
59
|
-
// Gather the agent's situational context (tools, skills, workspace,
|
|
60
|
-
// memory) so the advisor reasons with the same awareness the agent has.
|
|
61
|
-
// Best-effort: a failure here must not block the consult.
|
|
62
|
-
const runtimeContext = await buildAdvisorContext({
|
|
63
|
-
conversationId: ctx.conversationId,
|
|
64
|
-
workingDir: ctx.workingDir,
|
|
65
|
-
allowedToolNames: ctx.allowedToolNames,
|
|
66
|
-
// Per-turn trust snapshot — gates personal-memory surfaces off the same
|
|
67
|
-
// values the executor captured for this invocation, not live state.
|
|
68
|
-
trustClass: ctx.trustClass,
|
|
69
|
-
sourceChannel: ctx.executionChannel,
|
|
70
|
-
transcript: messages,
|
|
71
|
-
signal: ctx.signal,
|
|
72
|
-
}).catch(() => null);
|
|
73
51
|
const advice = await consultAdvisor({
|
|
74
52
|
systemPrompt: capture?.systemPrompt ?? null,
|
|
75
|
-
messages,
|
|
76
|
-
runtimeContext,
|
|
53
|
+
messages: capture?.messages ?? [],
|
|
77
54
|
signal: ctx.signal,
|
|
78
|
-
// Stream the advisor's guidance live as it generates: each delta is
|
|
79
|
-
// surfaced as a `tool_output_chunk` for this tool call and rendered in
|
|
80
|
-
// the tool-detail drawer. The complete text is still returned below.
|
|
81
|
-
onText: (chunk) => ctx.onOutput?.(chunk),
|
|
82
55
|
});
|
|
83
56
|
return { content: advice, isError: false };
|
|
84
57
|
} catch (err) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Default `post-tool-use` hook: when a turn's exploration tool calls (bash,
|
|
3
|
-
*
|
|
3
|
+
* file_read, file_list) show drift — a long unbroken run with no text sent to
|
|
4
4
|
* the user, or the model re-issuing the exact same call — surface a notice
|
|
5
5
|
* via `additionalContext` that coaches it to (a) give the user a brief
|
|
6
6
|
* progress summary and (b) delegate the rest of the investigation to an
|
|
@@ -105,7 +105,6 @@ const LOOP_PRONE_MODEL_PATTERN = /kimi-k2[p.]6|minimax-m3/i;
|
|
|
105
105
|
/** Read-only exploration tools whose unbroken runs indicate inline drift. */
|
|
106
106
|
const EXPLORATION_TOOL_NAMES: ReadonlySet<string> = new Set([
|
|
107
107
|
"bash",
|
|
108
|
-
"code_search",
|
|
109
108
|
"file_read",
|
|
110
109
|
"file_list",
|
|
111
110
|
]);
|