@vellumai/assistant 0.9.0 → 0.10.0-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 +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +976 -63
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +51 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +49 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +197 -4
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +66 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { OwnerConsent } from "./client.js";
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Mutable mock state
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
let mockClient: {
|
|
10
|
+
platformAssistantId: string;
|
|
11
|
+
getOwnerConsent: () => Promise<OwnerConsent | null>;
|
|
12
|
+
} | null = null;
|
|
13
|
+
let createCallCount = 0;
|
|
14
|
+
// Legacy fail-closed opt-out marker surfaced via getConfigReadOnly(). Default
|
|
15
|
+
// off/absent so existing behavior is unchanged.
|
|
16
|
+
let mockLegacyTelemetryOptOut: boolean | undefined = false;
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Module mocks (must precede the import under test)
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
mock.module("./client.js", () => ({
|
|
23
|
+
VellumPlatformClient: {
|
|
24
|
+
create: async () => {
|
|
25
|
+
createCallCount += 1;
|
|
26
|
+
return mockClient;
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
mock.module("../config/loader.js", () => ({
|
|
32
|
+
getConfigReadOnly: () => ({
|
|
33
|
+
legacyTelemetryOptOut: mockLegacyTelemetryOptOut,
|
|
34
|
+
}),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
mock.module("../util/logger.js", () => ({
|
|
38
|
+
getLogger: () =>
|
|
39
|
+
new Proxy({} as Record<string, unknown>, {
|
|
40
|
+
get: () => () => {},
|
|
41
|
+
}),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Import under test (after mocks)
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
import {
|
|
49
|
+
__setCachedShareAnalyticsForTest,
|
|
50
|
+
__setCachedShareDiagnosticsForTest,
|
|
51
|
+
__setCachedShareDiagnosticsVersionForTest,
|
|
52
|
+
getCachedShareAnalytics,
|
|
53
|
+
getCachedShareDiagnostics,
|
|
54
|
+
getCachedShareDiagnosticsVersion,
|
|
55
|
+
refreshConsentCache,
|
|
56
|
+
startConsentRefresh,
|
|
57
|
+
stopConsentRefresh,
|
|
58
|
+
} from "./consent-cache.js";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Build a mock owner consent. Fields default to `false` so existing
|
|
62
|
+
* share_analytics-focused cases don't have to spell them out.
|
|
63
|
+
*/
|
|
64
|
+
function makeConsent(overrides: Partial<OwnerConsent> = {}): OwnerConsent {
|
|
65
|
+
return {
|
|
66
|
+
shareAnalytics: false,
|
|
67
|
+
shareDiagnostics: false,
|
|
68
|
+
shareDiagnosticsAcceptedVersion: "",
|
|
69
|
+
...overrides,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function makeClient(consent: OwnerConsent | null, assistantId = "asst_1") {
|
|
74
|
+
return {
|
|
75
|
+
platformAssistantId: assistantId,
|
|
76
|
+
getOwnerConsent: async () => consent,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
describe("consent-cache", () => {
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
mockClient = null;
|
|
83
|
+
createCallCount = 0;
|
|
84
|
+
mockLegacyTelemetryOptOut = false;
|
|
85
|
+
__setCachedShareAnalyticsForTest(false);
|
|
86
|
+
__setCachedShareDiagnosticsForTest(false);
|
|
87
|
+
__setCachedShareDiagnosticsVersionForTest("");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
afterEach(async () => {
|
|
91
|
+
await stopConsentRefresh();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("defaults to false before any refresh", () => {
|
|
95
|
+
expect(getCachedShareAnalytics()).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("stays false when no platform client is available", async () => {
|
|
99
|
+
mockClient = null;
|
|
100
|
+
await refreshConsentCache();
|
|
101
|
+
expect(getCachedShareAnalytics()).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("becomes true after a successful fetch reporting shareAnalytics: true", async () => {
|
|
105
|
+
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
106
|
+
await refreshConsentCache();
|
|
107
|
+
expect(getCachedShareAnalytics()).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("a null fetch keeps the last known value", async () => {
|
|
111
|
+
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
112
|
+
await refreshConsentCache();
|
|
113
|
+
expect(getCachedShareAnalytics()).toBe(true);
|
|
114
|
+
|
|
115
|
+
// Transient failure: consent endpoint returns null.
|
|
116
|
+
mockClient = makeClient(null);
|
|
117
|
+
await refreshConsentCache();
|
|
118
|
+
expect(getCachedShareAnalytics()).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("a missing client flips a prior opt-in back to false", async () => {
|
|
122
|
+
__setCachedShareAnalyticsForTest(true);
|
|
123
|
+
mockClient = null;
|
|
124
|
+
await refreshConsentCache();
|
|
125
|
+
expect(getCachedShareAnalytics()).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("a client without a resolvable assistant identity fails closed", async () => {
|
|
129
|
+
__setCachedShareAnalyticsForTest(true);
|
|
130
|
+
// Identity cleared mid-session (e.g. assistantId emptied while base URL +
|
|
131
|
+
// API key persist). getOwnerConsent must not be relied upon to preserve the
|
|
132
|
+
// prior opt-in here — fail closed instead.
|
|
133
|
+
mockClient = makeClient(makeConsent({ shareAnalytics: true }), "");
|
|
134
|
+
await refreshConsentCache();
|
|
135
|
+
expect(getCachedShareAnalytics()).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("legacy opt-out marker keeps analytics off despite platform opt-in", async () => {
|
|
139
|
+
mockLegacyTelemetryOptOut = true;
|
|
140
|
+
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
141
|
+
await refreshConsentCache();
|
|
142
|
+
// Platform reports opt-in, but the fail-closed marker forces off.
|
|
143
|
+
expect(getCachedShareAnalytics()).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("a fetch reporting shareAnalytics: false turns the cache off", async () => {
|
|
147
|
+
__setCachedShareAnalyticsForTest(true);
|
|
148
|
+
mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
|
|
149
|
+
await refreshConsentCache();
|
|
150
|
+
expect(getCachedShareAnalytics()).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("startConsentRefresh is idempotent and runs an immediate refresh", async () => {
|
|
154
|
+
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
155
|
+
|
|
156
|
+
startConsentRefresh();
|
|
157
|
+
startConsentRefresh(); // no-op: timer already running
|
|
158
|
+
// Let the fire-and-forget immediate refresh settle.
|
|
159
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
160
|
+
|
|
161
|
+
expect(getCachedShareAnalytics()).toBe(true);
|
|
162
|
+
// One immediate refresh from the first call; the second is a no-op.
|
|
163
|
+
expect(createCallCount).toBe(1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("stopConsentRefresh clears the timer (start can run again)", async () => {
|
|
167
|
+
startConsentRefresh();
|
|
168
|
+
await stopConsentRefresh();
|
|
169
|
+
// A fresh start after stop performs another immediate refresh.
|
|
170
|
+
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
171
|
+
startConsentRefresh();
|
|
172
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
173
|
+
expect(getCachedShareAnalytics()).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// share_diagnostics tracks the same refresh as share_analytics; Sentry's
|
|
177
|
+
// beforeSend re-reads it per event so a revocation is honored within a cycle.
|
|
178
|
+
test("diagnostics defaults to false before any refresh", () => {
|
|
179
|
+
expect(getCachedShareDiagnostics()).toBe(false);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("diagnostics becomes true after a fetch reporting shareDiagnostics: true", async () => {
|
|
183
|
+
mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
|
|
184
|
+
await refreshConsentCache();
|
|
185
|
+
expect(getCachedShareDiagnostics()).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("a later fetch reporting shareDiagnostics: false honors the revocation", async () => {
|
|
189
|
+
mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
|
|
190
|
+
await refreshConsentCache();
|
|
191
|
+
expect(getCachedShareDiagnostics()).toBe(true);
|
|
192
|
+
|
|
193
|
+
mockClient = makeClient(makeConsent({ shareDiagnostics: false }));
|
|
194
|
+
await refreshConsentCache();
|
|
195
|
+
expect(getCachedShareDiagnostics()).toBe(false);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("a null diagnostics fetch keeps the last known value", async () => {
|
|
199
|
+
mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
|
|
200
|
+
await refreshConsentCache();
|
|
201
|
+
expect(getCachedShareDiagnostics()).toBe(true);
|
|
202
|
+
|
|
203
|
+
mockClient = makeClient(null);
|
|
204
|
+
await refreshConsentCache();
|
|
205
|
+
expect(getCachedShareDiagnostics()).toBe(true);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("a missing client flips a prior diagnostics opt-in back to false", async () => {
|
|
209
|
+
__setCachedShareDiagnosticsForTest(true);
|
|
210
|
+
mockClient = null;
|
|
211
|
+
await refreshConsentCache();
|
|
212
|
+
expect(getCachedShareDiagnostics()).toBe(false);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("a client without a resolvable assistant identity fails diagnostics closed", async () => {
|
|
216
|
+
__setCachedShareDiagnosticsForTest(true);
|
|
217
|
+
mockClient = makeClient(makeConsent({ shareDiagnostics: true }), "");
|
|
218
|
+
await refreshConsentCache();
|
|
219
|
+
expect(getCachedShareDiagnostics()).toBe(false);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("caches the accepted diagnostics-consent version from a successful fetch", async () => {
|
|
223
|
+
mockClient = makeClient(
|
|
224
|
+
makeConsent({
|
|
225
|
+
shareDiagnostics: true,
|
|
226
|
+
shareDiagnosticsAcceptedVersion: "2026-06-18",
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
229
|
+
await refreshConsentCache();
|
|
230
|
+
expect(getCachedShareDiagnosticsVersion()).toBe("2026-06-18");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("a missing client clears a prior cached diagnostics version", async () => {
|
|
234
|
+
__setCachedShareDiagnosticsVersionForTest("2026-06-18");
|
|
235
|
+
mockClient = null;
|
|
236
|
+
await refreshConsentCache();
|
|
237
|
+
expect(getCachedShareDiagnosticsVersion()).toBe("");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("a client without a resolvable assistant identity clears the diagnostics version", async () => {
|
|
241
|
+
__setCachedShareDiagnosticsVersionForTest("2026-06-18");
|
|
242
|
+
mockClient = makeClient(
|
|
243
|
+
makeConsent({
|
|
244
|
+
shareDiagnostics: true,
|
|
245
|
+
shareDiagnosticsAcceptedVersion: "2026-06-18",
|
|
246
|
+
}),
|
|
247
|
+
"",
|
|
248
|
+
);
|
|
249
|
+
await refreshConsentCache();
|
|
250
|
+
expect(getCachedShareDiagnosticsVersion()).toBe("");
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("a null fetch keeps the last known diagnostics version", async () => {
|
|
254
|
+
mockClient = makeClient(
|
|
255
|
+
makeConsent({
|
|
256
|
+
shareDiagnostics: true,
|
|
257
|
+
shareDiagnosticsAcceptedVersion: "2026-06-18",
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
await refreshConsentCache();
|
|
261
|
+
expect(getCachedShareDiagnosticsVersion()).toBe("2026-06-18");
|
|
262
|
+
|
|
263
|
+
mockClient = makeClient(null);
|
|
264
|
+
await refreshConsentCache();
|
|
265
|
+
expect(getCachedShareDiagnosticsVersion()).toBe("2026-06-18");
|
|
266
|
+
});
|
|
267
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory cache of the platform owner's telemetry consent.
|
|
3
|
+
*
|
|
4
|
+
* Three values are cached, all refreshed from the same owner-consent fetch:
|
|
5
|
+
* - `share_analytics`: gates usage telemetry collection.
|
|
6
|
+
* - `share_diagnostics`: gates crash diagnostics (read by Sentry `beforeSend`),
|
|
7
|
+
* and composes the per-turn trace-collection gate alongside the
|
|
8
|
+
* `trace-collection` feature flag.
|
|
9
|
+
* - `share_diagnostics_accepted_version`: the consent version the owner
|
|
10
|
+
* accepted; the disclosing-version half of the trace-collection gate (see
|
|
11
|
+
* telemetry/trace-collection-policy.ts).
|
|
12
|
+
*
|
|
13
|
+
* Hot-path gates (record-time telemetry writes, Sentry `beforeSend`) need a
|
|
14
|
+
* synchronous, I/O-free read, so this module owns the values and refreshes them
|
|
15
|
+
* periodically in the background. Default-off until the first successful fetch:
|
|
16
|
+
* an absent session, a disabled platform, or a transient fetch failure all leave
|
|
17
|
+
* the values untouched (initial `false`), so we never report analytics or send
|
|
18
|
+
* crash diagnostics without a confirmed opt-in.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { getConfigReadOnly } from "../config/loader.js";
|
|
22
|
+
import { getLogger } from "../util/logger.js";
|
|
23
|
+
import { VellumPlatformClient } from "./client.js";
|
|
24
|
+
|
|
25
|
+
const log = getLogger("consent-cache");
|
|
26
|
+
|
|
27
|
+
const REFRESH_INTERVAL_MS = 5 * 60_000; // refresh consent every 5 min
|
|
28
|
+
|
|
29
|
+
let cachedShareAnalytics = false; // default-off until first success
|
|
30
|
+
let cachedShareDiagnostics = false; // default-off until first success
|
|
31
|
+
let cachedShareDiagnosticsVersion = ""; // "" fails the trace disclosure gate
|
|
32
|
+
// Fail-closed marker for a workspace that locally opted out of usage data
|
|
33
|
+
// before telemetry moved to platform `share_analytics` consent (migration 106).
|
|
34
|
+
// While set, telemetry stays off regardless of platform consent. Cleared by a
|
|
35
|
+
// future cross-repo reconciliation once the platform exposes an explicit
|
|
36
|
+
// re-consent signal; not auto-cleared here.
|
|
37
|
+
let legacyOptOut = false;
|
|
38
|
+
let refreshTimer: ReturnType<typeof setInterval> | null = null;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Synchronous hot-path accessor for the effective `share_analytics` consent.
|
|
42
|
+
* Never does I/O; returns `false` until a successful refresh proves otherwise,
|
|
43
|
+
* and stays `false` while the legacy fail-closed opt-out marker is set.
|
|
44
|
+
*/
|
|
45
|
+
export function getCachedShareAnalytics(): boolean {
|
|
46
|
+
return cachedShareAnalytics && !legacyOptOut;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Synchronous hot-path accessor for the `share_diagnostics` consent (read by
|
|
51
|
+
* Sentry `beforeSend`). Never does I/O; returns `false` until a successful
|
|
52
|
+
* refresh proves otherwise. Because every Sentry event re-reads this, a
|
|
53
|
+
* mid-session opt-out is honored within one refresh cycle.
|
|
54
|
+
*/
|
|
55
|
+
export function getCachedShareDiagnostics(): boolean {
|
|
56
|
+
return cachedShareDiagnostics;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Synchronous hot-path accessor for the owner's accepted diagnostics-consent
|
|
61
|
+
* version ("YYYY-MM-DD", or "" until a successful refresh / if never accepted).
|
|
62
|
+
* The disclosing-version half of the per-turn trace-collection gate.
|
|
63
|
+
*/
|
|
64
|
+
export function getCachedShareDiagnosticsVersion(): string {
|
|
65
|
+
return cachedShareDiagnosticsVersion;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Refresh the cached consent from the platform.
|
|
70
|
+
*
|
|
71
|
+
* No platform session / features disabled (`create()` is null) → default-off.
|
|
72
|
+
* No resolvable assistant identity (no owner whose consent we can attest to) →
|
|
73
|
+
* fail closed. A successful fetch adopts the reported values. A `null` fetch
|
|
74
|
+
* (transient failure / undeployed endpoint) leaves the previous values
|
|
75
|
+
* unchanged so a known opt-in is not flipped off mid-session.
|
|
76
|
+
*/
|
|
77
|
+
export async function refreshConsentCache(): Promise<void> {
|
|
78
|
+
legacyOptOut = getConfigReadOnly().legacyTelemetryOptOut === true;
|
|
79
|
+
|
|
80
|
+
const client = await VellumPlatformClient.create();
|
|
81
|
+
if (!client) {
|
|
82
|
+
setCachedShareAnalytics(false);
|
|
83
|
+
setCachedShareDiagnostics(false);
|
|
84
|
+
setCachedShareDiagnosticsVersion("");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// No resolvable owner identity → fail closed (don't ride a stale opt-in).
|
|
89
|
+
if (!client.platformAssistantId) {
|
|
90
|
+
setCachedShareAnalytics(false);
|
|
91
|
+
setCachedShareDiagnostics(false);
|
|
92
|
+
setCachedShareDiagnosticsVersion("");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const consent = await client.getOwnerConsent();
|
|
97
|
+
if (consent) {
|
|
98
|
+
setCachedShareAnalytics(consent.shareAnalytics);
|
|
99
|
+
setCachedShareDiagnostics(consent.shareDiagnostics);
|
|
100
|
+
setCachedShareDiagnosticsVersion(consent.shareDiagnosticsAcceptedVersion);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function setCachedShareAnalytics(value: boolean): void {
|
|
105
|
+
if (value !== cachedShareAnalytics) {
|
|
106
|
+
log.debug(
|
|
107
|
+
{ from: cachedShareAnalytics, to: value },
|
|
108
|
+
"share_analytics consent changed",
|
|
109
|
+
);
|
|
110
|
+
cachedShareAnalytics = value;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function setCachedShareDiagnostics(value: boolean): void {
|
|
115
|
+
if (value !== cachedShareDiagnostics) {
|
|
116
|
+
log.debug(
|
|
117
|
+
{ from: cachedShareDiagnostics, to: value },
|
|
118
|
+
"share_diagnostics consent changed",
|
|
119
|
+
);
|
|
120
|
+
cachedShareDiagnostics = value;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function setCachedShareDiagnosticsVersion(value: string): void {
|
|
125
|
+
if (value !== cachedShareDiagnosticsVersion) {
|
|
126
|
+
log.debug(
|
|
127
|
+
{ from: cachedShareDiagnosticsVersion, to: value },
|
|
128
|
+
"share_diagnostics_accepted_version changed",
|
|
129
|
+
);
|
|
130
|
+
cachedShareDiagnosticsVersion = value;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Begin periodic consent refresh. Idempotent — a second call is a no-op while a
|
|
136
|
+
* timer is already running. Runs one immediate refresh, then every 5 minutes.
|
|
137
|
+
*/
|
|
138
|
+
export function startConsentRefresh(): void {
|
|
139
|
+
if (refreshTimer) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
refreshConsentCache().catch((err) => {
|
|
144
|
+
log.debug({ err }, "initial consent refresh failed");
|
|
145
|
+
});
|
|
146
|
+
refreshTimer = setInterval(() => {
|
|
147
|
+
refreshConsentCache().catch((err) => {
|
|
148
|
+
log.debug({ err }, "consent refresh failed");
|
|
149
|
+
});
|
|
150
|
+
}, REFRESH_INTERVAL_MS);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Stop periodic consent refresh and clear the timer. */
|
|
154
|
+
export async function stopConsentRefresh(): Promise<void> {
|
|
155
|
+
if (refreshTimer) {
|
|
156
|
+
clearInterval(refreshTimer);
|
|
157
|
+
refreshTimer = null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Test-only: override the cached analytics value without going through a refresh. */
|
|
162
|
+
export function __setCachedShareAnalyticsForTest(value: boolean): void {
|
|
163
|
+
cachedShareAnalytics = value;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Test-only: override the cached diagnostics value without going through a refresh. */
|
|
167
|
+
export function __setCachedShareDiagnosticsForTest(value: boolean): void {
|
|
168
|
+
cachedShareDiagnostics = value;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Test-only: override the cached diagnostics-consent version directly. */
|
|
172
|
+
export function __setCachedShareDiagnosticsVersionForTest(value: string): void {
|
|
173
|
+
cachedShareDiagnosticsVersion = value;
|
|
174
|
+
}
|
|
@@ -20,7 +20,7 @@ export const HOOKS = {
|
|
|
20
20
|
SHUTDOWN: "shutdown",
|
|
21
21
|
/** Fires once per user turn, immediately before the agent loop receives `runMessages`. */
|
|
22
22
|
USER_PROMPT_SUBMIT: "user-prompt-submit",
|
|
23
|
-
/** Fires immediately before each provider call. A hook may edit the outbound request (e.g. the system prompt) and opt the turn into deferred output streaming. */
|
|
23
|
+
/** Fires immediately before each provider call. A hook may edit the outbound request (e.g. the system prompt), route the call to a different inference profile, and opt the turn into deferred output streaming. */
|
|
24
24
|
PRE_MODEL_CALL: "pre-model-call",
|
|
25
25
|
/** Fires once per tool result, after the tool returns and before the result is sent to the provider. */
|
|
26
26
|
POST_TOOL_USE: "post-tool-use",
|
package/src/plugin-api/index.ts
CHANGED
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
*
|
|
33
33
|
* - {@link assistantEventHub} — the assistant's pub/sub hub for runtime events
|
|
34
34
|
* - {@link getSecureKeyAsync} — read a secret from secure storage
|
|
35
|
+
* - {@link getModelProfiles} — list the workspace inference profiles a plugin
|
|
36
|
+
* can route to (e.g. a model router building its category → profile map)
|
|
37
|
+
* - {@link getConfiguredProvider} — resolve a {@link Provider} for a call site
|
|
38
|
+
* (optionally overriding the profile) and run inference through the
|
|
39
|
+
* workspace's configured profiles and credentials — no plugin-supplied API key
|
|
35
40
|
*
|
|
36
41
|
* - {@link PluginInitContext} — passed to `init` hook at bootstrap
|
|
37
42
|
* - {@link PluginShutdownContext} — passed to `shutdown` hook at teardown
|
|
@@ -40,7 +45,8 @@
|
|
|
40
45
|
* - {@link PostCompactContext} — passed to `post-compact` hook, fired after
|
|
41
46
|
* the agent loop compacts a conversation mid-turn to re-apply injections
|
|
42
47
|
* - {@link PreModelCallContext} — passed to `pre-model-call` hook, fired
|
|
43
|
-
* before each provider call to edit the request
|
|
48
|
+
* before each provider call to edit the request, route it to a different
|
|
49
|
+
* inference profile, or defer output streaming
|
|
44
50
|
* - {@link PostToolUseContext} — passed to `post-tool-use` hook, fired once
|
|
45
51
|
* per tool result before it joins the provider-bound history
|
|
46
52
|
* - {@link StopContext} — passed to `stop` hook, the definitive terminal hook
|
|
@@ -77,8 +83,23 @@ export type {
|
|
|
77
83
|
ToolUseContent,
|
|
78
84
|
WebSearchToolResultContent,
|
|
79
85
|
} from "../providers/types.js";
|
|
86
|
+
// Provider + inference types. A plugin that runs its own inference through
|
|
87
|
+
// `getConfiguredProvider` names these to type the provider handle it gets back,
|
|
88
|
+
// the request options it passes to `sendMessage`, and the response.
|
|
89
|
+
export type {
|
|
90
|
+
Provider,
|
|
91
|
+
ProviderEvent,
|
|
92
|
+
ProviderResponse,
|
|
93
|
+
SendMessageConfig,
|
|
94
|
+
SendMessageOptions,
|
|
95
|
+
} from "../providers/types.js";
|
|
96
|
+
// Call-site identifier accepted by `getConfiguredProvider`. Plugins typically
|
|
97
|
+
// pass `"inference"` (the general-purpose call site) and pick the model via the
|
|
98
|
+
// `overrideProfile` option.
|
|
99
|
+
export type { LLMCallSite } from "../config/schemas/llm.js";
|
|
80
100
|
export type {
|
|
81
101
|
AgentLoopExitReason,
|
|
102
|
+
ModelProfileInfo,
|
|
82
103
|
PluginHookFn,
|
|
83
104
|
PluginInitContext,
|
|
84
105
|
PluginLogger,
|
|
@@ -110,3 +131,14 @@ export type {
|
|
|
110
131
|
} from "../runtime/assistant-event-hub.js";
|
|
111
132
|
export { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
112
133
|
export { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
134
|
+
export { getModelProfiles } from "./model-profiles.js";
|
|
135
|
+
// Resolve a provider for a call site (optionally overriding the profile) so a
|
|
136
|
+
// plugin can run inference through the workspace's configured profiles and
|
|
137
|
+
// credentials — managed-proxy or BYOK — without supplying its own API key.
|
|
138
|
+
// Pair with `getModelProfiles` to pick a profile. Returns `null` when no
|
|
139
|
+
// provider is configured. By default `overrideProfile` layers below any
|
|
140
|
+
// per-call-site config the workspace has pinned (e.g. a cheap `inference`
|
|
141
|
+
// profile), so it loses to that pin; pass `forceOverrideProfile: true` to
|
|
142
|
+
// float the chosen profile above the call-site layers when the plugin must
|
|
143
|
+
// run on a specific profile regardless of workspace tuning.
|
|
144
|
+
export { getConfiguredProvider } from "../providers/provider-send-message.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getConfig } from "../config/loader.js";
|
|
2
|
+
import { orderProfileKeys } from "../config/profile-order.js";
|
|
3
|
+
import type { ModelProfileInfo } from "./types.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* List the workspace inference profiles a plugin can route to, in the order the
|
|
7
|
+
* `/model` picker presents them (`llm.profileOrder` first, then the rest
|
|
8
|
+
* alphabetically). Disabled profiles are included and flagged via
|
|
9
|
+
* {@link ModelProfileInfo.isDisabled}; weighted "mix" profiles are included and
|
|
10
|
+
* flagged via {@link ModelProfileInfo.isMix}, since a mix is itself a valid
|
|
11
|
+
* routing target (it resolves to one constituent per conversation).
|
|
12
|
+
*
|
|
13
|
+
* Reads the live in-memory config, so the result reflects the current profile
|
|
14
|
+
* set each time it is called.
|
|
15
|
+
*/
|
|
16
|
+
export function getModelProfiles(): ModelProfileInfo[] {
|
|
17
|
+
const { llm } = getConfig();
|
|
18
|
+
const { profiles, activeProfile } = llm;
|
|
19
|
+
const result: ModelProfileInfo[] = [];
|
|
20
|
+
for (const key of orderProfileKeys(profiles, llm.profileOrder)) {
|
|
21
|
+
const entry = profiles[key];
|
|
22
|
+
if (entry == null) continue;
|
|
23
|
+
result.push({
|
|
24
|
+
key,
|
|
25
|
+
label: entry.label ?? key,
|
|
26
|
+
description: entry.description ?? null,
|
|
27
|
+
isActive: key === activeProfile,
|
|
28
|
+
isDisabled: entry.status === "disabled",
|
|
29
|
+
isMix: entry.mix != null,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
package/src/plugin-api/types.ts
CHANGED
|
@@ -94,6 +94,35 @@ export interface PluginInitContext {
|
|
|
94
94
|
assistantVersion: string;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// ─── Model profiles ──────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A workspace inference profile a plugin can route to. Returned by
|
|
101
|
+
* {@link getModelProfiles}; {@link key} is the value a `pre-model-call` hook
|
|
102
|
+
* assigns to `PreModelCallContext.modelProfile` to route a call. A model router
|
|
103
|
+
* reads this list (typically at `init`) to learn which profiles exist before
|
|
104
|
+
* mapping a classified message onto one.
|
|
105
|
+
*/
|
|
106
|
+
export interface ModelProfileInfo {
|
|
107
|
+
/** Profile key in `llm.profiles`; assignable to `PreModelCallContext.modelProfile`. */
|
|
108
|
+
readonly key: string;
|
|
109
|
+
/** Human-readable label, falling back to {@link key} when none is set. */
|
|
110
|
+
readonly label: string;
|
|
111
|
+
/** Author-supplied description, or `null` when none is set. */
|
|
112
|
+
readonly description: string | null;
|
|
113
|
+
/** Whether this is the workspace's active profile. */
|
|
114
|
+
readonly isActive: boolean;
|
|
115
|
+
/** Whether the profile is disabled; routing to it is rejected by the resolver. */
|
|
116
|
+
readonly isDisabled: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Whether this is a weighted "mix" profile — an A/B blend that resolves to one
|
|
119
|
+
* of its constituent profiles per conversation via a seeded weighted pick.
|
|
120
|
+
* Routing to its {@link key} is valid; it directs the call into the blend
|
|
121
|
+
* rather than at a single fixed model.
|
|
122
|
+
*/
|
|
123
|
+
readonly isMix: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
97
126
|
// ─── Shutdown context ────────────────────────────────────────────────────────
|
|
98
127
|
|
|
99
128
|
/**
|
|
@@ -438,8 +467,9 @@ export interface StopContext {
|
|
|
438
467
|
* and compaction work can share a conversation), hooks MUST self-gate on
|
|
439
468
|
* {@link callSite} / {@link conversationId} before acting.
|
|
440
469
|
*
|
|
441
|
-
* A hook may edit the outbound request by replacing {@link systemPrompt},
|
|
442
|
-
*
|
|
470
|
+
* A hook may edit the outbound request by replacing {@link systemPrompt}, route
|
|
471
|
+
* the call to a different inference profile via {@link modelProfile}, and opt
|
|
472
|
+
* this turn into deferred output streaming via {@link deferAssistantOutput}.
|
|
443
473
|
* Mutate the context in place or return a new one; throwing is contained by the
|
|
444
474
|
* loop (the call proceeds with the original request).
|
|
445
475
|
*/
|
|
@@ -456,6 +486,24 @@ export interface PreModelCallContext {
|
|
|
456
486
|
* append a section); the loop sends the resulting value.
|
|
457
487
|
*/
|
|
458
488
|
systemPrompt: string | null;
|
|
489
|
+
/**
|
|
490
|
+
* Inference profile to route THIS provider call to, named by its key in the
|
|
491
|
+
* workspace `llm.profiles`. Seeded with the call's already-resolved override
|
|
492
|
+
* profile, or `null` when none applies. A hook may replace it to select a
|
|
493
|
+
* different profile per call — the lever a model router uses to map a
|
|
494
|
+
* classified message onto a profile (model + provider connection + sampling
|
|
495
|
+
* settings). For the user-facing `mainAgent` call the resolver layers the
|
|
496
|
+
* named profile at the top of precedence (above the workspace active
|
|
497
|
+
* profile), so the hook's choice wins; a key with no matching profile falls
|
|
498
|
+
* through unchanged (no throw). Honored only when {@link callSite} is set.
|
|
499
|
+
* Set to `null` to apply no override.
|
|
500
|
+
*
|
|
501
|
+
* Context-window sizing and overflow recovery for this call are computed from
|
|
502
|
+
* the profile resolved before the hook runs. Routing a near-budget
|
|
503
|
+
* conversation to a profile with a smaller context window relies on the loop's
|
|
504
|
+
* overflow recovery (compact and retry) rather than proactive compaction.
|
|
505
|
+
*/
|
|
506
|
+
modelProfile: string | null;
|
|
459
507
|
/**
|
|
460
508
|
* Seeded `false`. When a hook sets it `true`, the loop suppresses this turn's
|
|
461
509
|
* live assistant `text_delta` stream; a `post-model-call` hook is then
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// Drive the gate off a controllable llm config + a stubbed default-profile
|
|
4
|
+
// resolver, so we can assert the default-on semantics precisely.
|
|
5
|
+
let mockLlm: {
|
|
6
|
+
profiles: Record<string, { advisorEnabled?: boolean | null }>;
|
|
7
|
+
activeProfile?: string;
|
|
8
|
+
} = { profiles: {} };
|
|
9
|
+
|
|
10
|
+
mock.module("../../../../config/loader.js", () => ({
|
|
11
|
+
getConfig: () => ({ llm: mockLlm }),
|
|
12
|
+
}));
|
|
13
|
+
mock.module("../../../../config/llm-resolver.js", () => ({
|
|
14
|
+
resolveDefaultProfileKey: () => "balanced",
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const { advisorEnabledForProfile } = await import("../advisor-gate.js");
|
|
18
|
+
|
|
19
|
+
describe("advisorEnabledForProfile", () => {
|
|
20
|
+
test("default-on when the profile omits the flag", () => {
|
|
21
|
+
mockLlm = { profiles: { p: {} }, activeProfile: "p" };
|
|
22
|
+
expect(advisorEnabledForProfile("p")).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("default-on when the flag is null", () => {
|
|
26
|
+
mockLlm = { profiles: { p: { advisorEnabled: null } }, activeProfile: "p" };
|
|
27
|
+
expect(advisorEnabledForProfile("p")).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("disabled only on an explicit false", () => {
|
|
31
|
+
mockLlm = {
|
|
32
|
+
profiles: { p: { advisorEnabled: false } },
|
|
33
|
+
activeProfile: "p",
|
|
34
|
+
};
|
|
35
|
+
expect(advisorEnabledForProfile("p")).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("enabled on an explicit true", () => {
|
|
39
|
+
mockLlm = { profiles: { p: { advisorEnabled: true } }, activeProfile: "p" };
|
|
40
|
+
expect(advisorEnabledForProfile("p")).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("falls back to the active profile when modelProfile is null", () => {
|
|
44
|
+
mockLlm = {
|
|
45
|
+
profiles: { a: { advisorEnabled: false } },
|
|
46
|
+
activeProfile: "a",
|
|
47
|
+
};
|
|
48
|
+
expect(advisorEnabledForProfile(null)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("falls back to the call-site default profile when neither is set", () => {
|
|
52
|
+
// resolveDefaultProfileKey is stubbed to "balanced".
|
|
53
|
+
mockLlm = { profiles: { balanced: { advisorEnabled: false } } };
|
|
54
|
+
expect(advisorEnabledForProfile(null)).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|