@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e
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 +1 -1
- package/Dockerfile +8 -0
- package/docs/activation-funnel-telemetry.md +13 -7
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/openapi.yaml +530 -107
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +8 -0
- package/src/__tests__/activation-early-marking.test.ts +6 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
- package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
- package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
- package/src/__tests__/app-bundler.test.ts +4 -12
- package/src/__tests__/app-executors.test.ts +2 -49
- package/src/__tests__/app-routes-csp.test.ts +114 -146
- package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
- package/src/__tests__/build-persisted-content.test.ts +96 -0
- package/src/__tests__/bundle-scanner.test.ts +27 -1
- package/src/__tests__/call-controller.test.ts +291 -0
- package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
- package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
- package/src/__tests__/config-loader-backfill.test.ts +9 -4
- package/src/__tests__/config-schema-cmd.test.ts +10 -11
- package/src/__tests__/config-schema.test.ts +190 -257
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
- package/src/__tests__/conversation-agent-loop.test.ts +22 -9
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
- package/src/__tests__/conversation-process-callsite.test.ts +12 -19
- package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
- package/src/__tests__/conversation-summarize-route.test.ts +36 -44
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
- package/src/__tests__/conversation-usage.test.ts +4 -12
- package/src/__tests__/credential-routes.test.ts +250 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/db-migration-rollback.test.ts +22 -0
- package/src/__tests__/empty-response-hook.test.ts +186 -1
- package/src/__tests__/external-plugin-loader.test.ts +11 -5
- package/src/__tests__/heartbeat-service.test.ts +0 -28
- package/src/__tests__/host-shell-tool.test.ts +2 -0
- package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
- package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -16
- package/src/__tests__/list-all-apps.test.ts +0 -4
- package/src/__tests__/llm-context-resolution.test.ts +32 -56
- package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
- package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
- package/src/__tests__/llm-resolver.test.ts +342 -602
- package/src/__tests__/llm-schema.test.ts +79 -37
- package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
- package/src/__tests__/media-stream-output.test.ts +259 -3
- package/src/__tests__/media-stream-server-integration.test.ts +22 -1
- package/src/__tests__/media-stream-stt-session.test.ts +47 -0
- package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
- package/src/__tests__/memory-recall-log-store.test.ts +47 -13
- package/src/__tests__/mock-gateway-ipc.ts +46 -1
- package/src/__tests__/mtime-cache.test.ts +61 -0
- package/src/__tests__/navigate-settings-tab.test.ts +2 -0
- package/src/__tests__/normalize-onboarding.test.ts +33 -0
- package/src/__tests__/onboarding-persona-write.test.ts +26 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
- package/src/__tests__/provider-connections-backfill.test.ts +138 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
- package/src/__tests__/provider-registry-ollama.test.ts +8 -18
- package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
- package/src/__tests__/provider-usage-tracking.test.ts +9 -19
- package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
- package/src/__tests__/published-app-updater.test.ts +22 -16
- package/src/__tests__/registry.test.ts +5 -16
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
- package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
- package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
- package/src/__tests__/reveal-success-registry.test.ts +123 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
- package/src/__tests__/secret-fixtures.ts +9 -0
- package/src/__tests__/server-history-render.test.ts +28 -0
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/slack-share-routes.test.ts +0 -1
- package/src/__tests__/stt-stream-session.test.ts +6 -5
- package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
- package/src/__tests__/subagent-disposal.test.ts +2 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +37 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
- package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
- package/src/__tests__/telemetry-routes.test.ts +99 -19
- package/src/__tests__/tool-audit.test.ts +34 -4
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-profiler.test.ts +72 -1
- package/src/__tests__/tool-result-spool.test.ts +49 -4
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
- package/src/__tests__/ui-channel-variants.test.ts +108 -0
- package/src/__tests__/ui-shape-teaching.test.ts +255 -0
- package/src/__tests__/usage-attribution.test.ts +18 -41
- package/src/__tests__/user-plugin-loader.test.ts +4 -4
- package/src/__tests__/voice-config-update.test.ts +46 -0
- package/src/__tests__/voice-session-bridge.test.ts +216 -84
- package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
- package/src/agent/loop.ts +4 -3
- package/src/api/events/open-conversation.test.ts +64 -0
- package/src/api/events/open-conversation.ts +33 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +25 -35
- package/src/bundler/app-bundler.ts +34 -48
- package/src/bundler/app-compiler.ts +39 -4
- package/src/bundler/bundle-scanner.ts +13 -0
- package/src/bundler/manifest.ts +1 -1
- package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
- package/src/calls/call-constants.ts +5 -0
- package/src/calls/call-controller.ts +100 -32
- package/src/calls/call-transport.ts +9 -0
- package/src/calls/media-stream-output.ts +107 -4
- package/src/calls/media-stream-server.ts +29 -9
- package/src/calls/media-stream-stt-session.ts +7 -1
- package/src/calls/media-turn-detector.ts +11 -1
- package/src/calls/voice-session-bridge.ts +84 -63
- package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
- package/src/cli/commands/__tests__/notifications.test.ts +24 -3
- package/src/cli/commands/config.help.ts +6 -6
- package/src/cli/commands/credentials.help.ts +13 -0
- package/src/cli/commands/credentials.ts +6 -1
- package/src/cli/commands/email.help.ts +7 -0
- package/src/cli/commands/email.ts +35 -1
- package/src/cli/commands/inference-providers.ts +168 -107
- package/src/cli/commands/inference.help.ts +118 -46
- package/src/cli/commands/memory/index.help.ts +23 -0
- package/src/cli/commands/memory/nodes.ts +146 -0
- package/src/cli/commands/notifications.help.ts +10 -10
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
- package/src/cli/commands/oauth/connect.test.ts +126 -0
- package/src/cli/commands/oauth/connect.ts +29 -6
- package/src/cli/commands/oauth/index.help.ts +7 -1
- package/src/cli/commands/oauth/status.test.ts +69 -3
- package/src/cli/commands/oauth/status.ts +50 -12
- package/src/cli/commands/plugins.help.ts +13 -2
- package/src/cli/commands/plugins.ts +61 -7
- package/src/cli/commands/telemetry.help.ts +13 -0
- package/src/cli/commands/telemetry.ts +45 -5
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
- package/src/cli/lib/bundled-marketplace.json +93 -36
- package/src/cli/lib/inspect-plugin.ts +5 -1
- package/src/cli/lib/install-from-github.ts +68 -4
- package/src/cli/lib/plugin-catalog-local.ts +6 -2
- package/src/cli/lib/plugin-fingerprint.ts +3 -3
- package/src/cli/lib/upgrade-plugin.ts +236 -35
- package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
- package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
- package/src/config/__tests__/profile-materialization.test.ts +1 -88
- package/src/config/bundled-skills/AGENTS.md +3 -30
- package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
- package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
- package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
- package/src/config/bundled-skills/subagent/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/call-site-defaults.ts +3 -3
- package/src/config/feature-flag-registry.json +24 -39
- package/src/config/llm-resolver.ts +88 -475
- package/src/config/profile-materialization.ts +11 -11
- package/src/config/schema.ts +93 -127
- package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
- package/src/config/schemas/__tests__/stt.test.ts +31 -3
- package/src/config/schemas/live-voice.ts +10 -4
- package/src/config/schemas/llm.ts +52 -15
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +8 -0
- package/src/config/schemas/memory-v2.ts +2 -2
- package/src/config/schemas/memory-v3.ts +1 -1
- package/src/config/schemas/services.ts +6 -3
- package/src/config/schemas/stt.ts +38 -21
- package/src/config/schemas/tts.ts +13 -18
- package/src/config/skills.ts +31 -21
- package/src/context/compactor.ts +44 -7
- package/src/context/post-turn-tool-result-truncation.ts +4 -2
- package/src/context/tool-result-spool.ts +26 -5
- package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
- package/src/conversations/message-consolidation.ts +22 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
- package/src/daemon/app-source-watcher.ts +17 -23
- package/src/daemon/chat-credential-redaction.ts +1365 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
- package/src/daemon/conversation-agent-loop.ts +71 -31
- package/src/daemon/conversation-error.ts +36 -14
- package/src/daemon/conversation-process.ts +22 -0
- package/src/daemon/conversation-store.ts +35 -0
- package/src/daemon/conversation-surfaces.ts +25 -25
- package/src/daemon/conversation-tool-setup.ts +33 -7
- package/src/daemon/conversation.ts +55 -5
- package/src/daemon/handlers/shared.ts +33 -2
- package/src/daemon/lifecycle.ts +4 -4
- package/src/daemon/message-types/conversations.ts +6 -17
- package/src/daemon/providers-setup.ts +8 -0
- package/src/daemon/tool-setup-types.ts +7 -1
- package/src/daemon/wake-conversation-ops.ts +10 -1
- package/src/hooks/types.ts +9 -0
- package/src/ipc/__tests__/email-ipc.test.ts +90 -0
- package/src/ipc/gateway-client.test.ts +59 -0
- package/src/ipc/gateway-client.ts +70 -27
- package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
- package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
- package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
- package/src/live-voice/__tests__/protocol.test.ts +122 -0
- package/src/live-voice/live-voice-session.ts +443 -40
- package/src/live-voice/protocol.ts +143 -1
- package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
- package/src/monitoring/plugin-source-watch.ts +3 -62
- package/src/notifications/README.md +1 -1
- package/src/permissions/checker.ts +8 -4
- package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
- package/src/persistence/conversation-crud.ts +104 -2
- package/src/persistence/db-init.ts +14 -3
- package/src/persistence/job-handlers/cleanup.ts +15 -7
- package/src/persistence/llm-request-log-store.ts +88 -52
- package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
- package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
- package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
- package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
- package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
- package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
- package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
- package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
- package/src/persistence/migrations/helpers/relocation.ts +44 -1
- package/src/persistence/migrations/run-migrations.ts +25 -1
- package/src/persistence/schema/infrastructure.ts +9 -0
- package/src/persistence/schema/memory-core.ts +3 -0
- package/src/persistence/schema/memory-injection.ts +2 -0
- package/src/persistence/steps.ts +36 -0
- package/src/platform/client.test.ts +1 -44
- package/src/platform/client.ts +10 -20
- package/src/platform/consent-cache.test.ts +89 -23
- package/src/platform/consent-cache.ts +71 -33
- package/src/plugin-api/constants.ts +12 -0
- package/src/plugin-api/conversation-turn.ts +37 -14
- package/src/plugin-api/index.ts +11 -1
- package/src/plugin-api/resolve-credential.ts +75 -0
- package/src/plugin-api/vision-support.test.ts +8 -19
- package/src/plugin-api/vision-support.ts +26 -17
- package/src/plugins/collect-source-versions.ts +77 -0
- package/src/plugins/defaults/compaction/compact.ts +6 -0
- package/src/plugins/defaults/compaction/window-manager.ts +9 -0
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
- package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
- package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
- package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
- package/src/plugins/defaults/index.ts +8 -1
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
- package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
- package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
- package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
- package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
- package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
- package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
- package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
- package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
- package/src/plugins/defaults/memory/graph/store.ts +114 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
- package/src/plugins/defaults/memory/graph/tools.ts +47 -13
- package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
- package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
- package/src/plugins/defaults/memory/memory-db.ts +3 -2
- package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
- package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
- package/src/plugins/defaults/memory/path-containment.ts +21 -0
- package/src/plugins/defaults/memory/prompt-override.ts +51 -13
- package/src/plugins/defaults/memory/tools.test.ts +34 -0
- package/src/plugins/defaults/memory/tools.ts +12 -2
- package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
- package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
- package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
- package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
- package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
- package/src/plugins/defaults/memory/v2/migration.ts +46 -16
- package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
- package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
- package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
- package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
- package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
- package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
- package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
- package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
- package/src/plugins/defaults/memory/v3/prune.ts +17 -11
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
- package/src/plugins/external-plugin-loader.ts +15 -15
- package/src/plugins/mtime-cache.ts +47 -0
- package/src/plugins/pipeline.ts +9 -1
- package/src/plugins/plugin-execution-context.ts +44 -0
- package/src/plugins/plugin-tree-walk.ts +31 -24
- package/src/plugins/source-fingerprint.ts +3 -4
- package/src/prompts/normalize-onboarding.ts +12 -0
- package/src/prompts/persona-resolver.ts +8 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
- package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
- package/src/providers/__tests__/retry-callsite.test.ts +235 -215
- package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
- package/src/providers/atlascloud/client.ts +10 -49
- package/src/providers/baseten/client.ts +43 -0
- package/src/providers/call-site-routing.ts +26 -13
- package/src/providers/connection-resolution.ts +9 -11
- package/src/providers/fetch-provider-catalog.ts +4 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
- package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
- package/src/providers/inference/adapter-factory.ts +27 -6
- package/src/providers/inference/auth.ts +29 -1
- package/src/providers/inference/backfill.ts +61 -10
- package/src/providers/inference/connection-availability.ts +3 -2
- package/src/providers/inference/resolve-auth.ts +9 -1
- package/src/providers/model-catalog.ts +37 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
- package/src/providers/openai/api-key-validation.ts +70 -0
- package/src/providers/retry.ts +2 -0
- package/src/providers/types.ts +11 -4
- package/src/providers/vellum-model-routing.test.ts +26 -0
- package/src/providers/vellum-model-routing.ts +30 -0
- package/src/providers/voice-error-copy.ts +47 -0
- package/src/runtime/agent-wake.ts +40 -24
- package/src/runtime/for-chat-mint-registry.ts +118 -0
- package/src/runtime/reveal-nonce.ts +49 -0
- package/src/runtime/reveal-success-registry.ts +306 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
- package/src/runtime/routes/app-management-routes.ts +152 -45
- package/src/runtime/routes/app-routes.ts +16 -77
- package/src/runtime/routes/canned-message-complete.ts +16 -16
- package/src/runtime/routes/conversation-management-routes.ts +17 -14
- package/src/runtime/routes/conversation-query-routes.ts +54 -6
- package/src/runtime/routes/conversation-routes.ts +48 -2
- package/src/runtime/routes/credential-routes.ts +116 -3
- package/src/runtime/routes/email-routes.ts +17 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
- package/src/runtime/routes/inference-send-routes.ts +1 -1
- package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
- package/src/runtime/routes/plugins-routes.ts +37 -6
- package/src/runtime/routes/publish-routes.ts +15 -18
- package/src/runtime/routes/schedule-worker-routes.ts +9 -0
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/telemetry-routes.ts +149 -45
- package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
- package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
- package/src/schedule/schedule-store.ts +12 -1
- package/src/schedule/schedule-timezone.ts +63 -0
- package/src/schedule/scheduler.ts +100 -6
- package/src/schedule/worker-control.ts +19 -0
- package/src/security/auth-fallback-events-store.ts +7 -6
- package/src/security/secret-scanner.ts +26 -1
- package/src/services/published-app-updater.ts +6 -11
- package/src/stt/stt-stream-session.ts +36 -16
- package/src/subagent/manager.ts +34 -8
- package/src/telemetry/AGENTS.md +30 -1
- package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
- package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
- package/src/telemetry/config-setting-snapshot.ts +44 -10
- package/src/telemetry/telemetry-event-sources.test.ts +124 -30
- package/src/telemetry/telemetry-event-sources.ts +137 -83
- package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
- package/src/telemetry/telemetry-events-outbox.ts +60 -9
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire-validation.ts +39 -2
- package/src/telemetry/telemetry-wire.generated.ts +8 -0
- package/src/telemetry/tool-audit.ts +15 -9
- package/src/telemetry/tool-executed-events-store.test.ts +1 -1
- package/src/telemetry/turn-events-store.ts +20 -0
- package/src/telemetry/types.ts +45 -12
- package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
- package/src/telemetry/usage-telemetry-reporter.ts +117 -18
- package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
- package/src/telemetry/watchdog-direct-emit.ts +11 -6
- package/src/tools/apps/executors.ts +11 -36
- package/src/tools/executor.ts +14 -1
- package/src/tools/host-terminal/host-shell.ts +6 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
- package/src/tools/network/web-fetch.ts +6 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/subagent/spawn.ts +7 -11
- package/src/tools/terminal/shell.ts +5 -2
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/tool-profiler.ts +37 -6
- package/src/tools/ui-surface/channel-variants.ts +101 -0
- package/src/tools/ui-surface/definitions.ts +23 -67
- package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
- package/src/tts/__tests__/provider-adapters.test.ts +18 -0
- package/src/tts/provider-catalog.ts +2 -4
- package/src/tts/providers/deepgram-provider.ts +10 -1
- package/src/types/onboarding-context.ts +8 -0
- package/src/usage/attribution.ts +18 -112
- package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
- package/src/util/provider-error-patterns.ts +7 -1
- package/src/util/worker-process.ts +105 -0
- package/src/watcher/__tests__/telemetry.test.ts +17 -4
- package/src/watcher/telemetry.ts +5 -5
- package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/provider-commit-message-generator.ts +6 -4
- package/src/__tests__/app-open-proxy.test.ts +0 -67
- package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
- package/src/onboarding/onboarding-research-events-store.ts +0 -104
- package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
- package/src/tools/apps/definitions.ts +0 -73
- package/src/tools/apps/open-proxy.ts +0 -43
|
@@ -34,9 +34,9 @@ import {
|
|
|
34
34
|
__setCachedShareAnalyticsForTest,
|
|
35
35
|
__setCachedShareDiagnosticsForTest,
|
|
36
36
|
__setCachedShareDiagnosticsVersionForTest,
|
|
37
|
-
getCachedShareAnalytics,
|
|
38
37
|
getCachedShareDiagnostics,
|
|
39
38
|
getCachedShareDiagnosticsVersion,
|
|
39
|
+
getRawShareAnalytics,
|
|
40
40
|
refreshConsentCache,
|
|
41
41
|
startConsentRefresh,
|
|
42
42
|
stopConsentRefresh,
|
|
@@ -69,8 +69,9 @@ describe("consent-cache", () => {
|
|
|
69
69
|
// Legacy fail-closed opt-out marker read via getConfigReadOnly(). Default
|
|
70
70
|
// off so existing behavior is unchanged.
|
|
71
71
|
setConfig("legacyTelemetryOptOut", false);
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
// Boot state: no confirmed consent yet.
|
|
73
|
+
__setCachedShareAnalyticsForTest("unknown");
|
|
74
|
+
__setCachedShareDiagnosticsForTest("unknown");
|
|
74
75
|
__setCachedShareDiagnosticsVersionForTest("");
|
|
75
76
|
});
|
|
76
77
|
|
|
@@ -78,63 +79,107 @@ describe("consent-cache", () => {
|
|
|
78
79
|
await stopConsentRefresh();
|
|
79
80
|
});
|
|
80
81
|
|
|
81
|
-
test("
|
|
82
|
-
expect(
|
|
82
|
+
test("boot state is unknown", () => {
|
|
83
|
+
expect(getRawShareAnalytics()).toBe("unknown");
|
|
83
84
|
});
|
|
84
85
|
|
|
85
|
-
test("
|
|
86
|
+
test("no platform client available → unknown", async () => {
|
|
86
87
|
mockClient = null;
|
|
87
88
|
await refreshConsentCache();
|
|
88
|
-
expect(
|
|
89
|
+
expect(getRawShareAnalytics()).toBe("unknown");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("platform features disabled → confirmed false, not unknown", async () => {
|
|
93
|
+
// A config-level platform opt-out is permanent, not a cold cache: no
|
|
94
|
+
// flush will ever drain the outbox, so record-time gates must stay
|
|
95
|
+
// closed (confirmed false) instead of accumulating rows forever.
|
|
96
|
+
__setCachedShareAnalyticsForTest(true);
|
|
97
|
+
process.env.VELLUM_DISABLE_PLATFORM = "1";
|
|
98
|
+
try {
|
|
99
|
+
await refreshConsentCache();
|
|
100
|
+
} finally {
|
|
101
|
+
delete process.env.VELLUM_DISABLE_PLATFORM;
|
|
102
|
+
}
|
|
103
|
+
expect(getRawShareAnalytics()).toBe(false);
|
|
104
|
+
expect(getCachedShareDiagnostics()).toBe(false);
|
|
105
|
+
expect(getCachedShareDiagnosticsVersion()).toBe("");
|
|
89
106
|
});
|
|
90
107
|
|
|
91
108
|
test("becomes true after a successful fetch reporting shareAnalytics: true", async () => {
|
|
92
109
|
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
93
110
|
await refreshConsentCache();
|
|
94
|
-
expect(
|
|
111
|
+
expect(getRawShareAnalytics()).toBe(true);
|
|
95
112
|
});
|
|
96
113
|
|
|
97
114
|
test("a null fetch keeps the last known value", async () => {
|
|
98
115
|
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
99
116
|
await refreshConsentCache();
|
|
100
|
-
expect(
|
|
117
|
+
expect(getRawShareAnalytics()).toBe(true);
|
|
101
118
|
|
|
102
119
|
// Transient failure: consent endpoint returns null.
|
|
103
120
|
mockClient = makeClient(null);
|
|
104
121
|
await refreshConsentCache();
|
|
105
|
-
expect(
|
|
122
|
+
expect(getRawShareAnalytics()).toBe(true);
|
|
106
123
|
});
|
|
107
124
|
|
|
108
|
-
test("a
|
|
125
|
+
test("a null fetch keeps a confirmed opt-out", async () => {
|
|
126
|
+
mockClient = makeClient(makeConsent({ shareAnalytics: false }));
|
|
127
|
+
await refreshConsentCache();
|
|
128
|
+
expect(getRawShareAnalytics()).toBe(false);
|
|
129
|
+
|
|
130
|
+
mockClient = makeClient(null);
|
|
131
|
+
await refreshConsentCache();
|
|
132
|
+
expect(getRawShareAnalytics()).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("a null fetch keeps the boot unknown state", async () => {
|
|
136
|
+
mockClient = makeClient(null);
|
|
137
|
+
await refreshConsentCache();
|
|
138
|
+
expect(getRawShareAnalytics()).toBe("unknown");
|
|
139
|
+
expect(getCachedShareDiagnostics()).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("a missing client demotes a prior opt-in to unknown", async () => {
|
|
109
143
|
__setCachedShareAnalyticsForTest(true);
|
|
110
144
|
mockClient = null;
|
|
111
145
|
await refreshConsentCache();
|
|
112
|
-
expect(
|
|
146
|
+
expect(getRawShareAnalytics()).toBe("unknown");
|
|
113
147
|
});
|
|
114
148
|
|
|
115
|
-
test("a client without a resolvable assistant identity
|
|
149
|
+
test("a client without a resolvable assistant identity demotes to unknown", async () => {
|
|
116
150
|
__setCachedShareAnalyticsForTest(true);
|
|
117
151
|
// Identity cleared mid-session (e.g. assistantId emptied while base URL +
|
|
118
152
|
// API key persist). getOwnerConsent must not be relied upon to preserve the
|
|
119
|
-
// prior opt-in here —
|
|
153
|
+
// prior opt-in here — losing the owner mapping is not an opt-out, but the
|
|
154
|
+
// stale opt-in must not keep riding either.
|
|
120
155
|
mockClient = makeClient(makeConsent({ shareAnalytics: true }), "");
|
|
121
156
|
await refreshConsentCache();
|
|
122
|
-
expect(
|
|
157
|
+
expect(getRawShareAnalytics()).toBe("unknown");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("legacy opt-out marker is honored at boot, before any refresh", () => {
|
|
161
|
+
// The marker is read live from config: a workspace carrying
|
|
162
|
+
// legacyTelemetryOptOut=true must gate recording in the window between
|
|
163
|
+
// daemon start and the first consent refresh (unknown-consent recording
|
|
164
|
+
// would otherwise leak events in that window).
|
|
165
|
+
setConfig("legacyTelemetryOptOut", true);
|
|
166
|
+
expect(getRawShareAnalytics()).toBe(false);
|
|
123
167
|
});
|
|
124
168
|
|
|
125
169
|
test("legacy opt-out marker keeps analytics off despite platform opt-in", async () => {
|
|
126
170
|
setConfig("legacyTelemetryOptOut", true);
|
|
127
171
|
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
128
172
|
await refreshConsentCache();
|
|
129
|
-
// Platform reports opt-in, but the fail-closed marker forces off.
|
|
130
|
-
|
|
173
|
+
// Platform reports opt-in, but the fail-closed marker forces off. The raw
|
|
174
|
+
// accessor folds the marker into a confirmed false, not an unknown.
|
|
175
|
+
expect(getRawShareAnalytics()).toBe(false);
|
|
131
176
|
});
|
|
132
177
|
|
|
133
178
|
test("a fetch reporting shareAnalytics: false turns the cache off", async () => {
|
|
134
179
|
__setCachedShareAnalyticsForTest(true);
|
|
135
180
|
mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
|
|
136
181
|
await refreshConsentCache();
|
|
137
|
-
expect(
|
|
182
|
+
expect(getRawShareAnalytics()).toBe(false);
|
|
138
183
|
});
|
|
139
184
|
|
|
140
185
|
test("startConsentRefresh is idempotent and runs an immediate refresh", async () => {
|
|
@@ -145,7 +190,7 @@ describe("consent-cache", () => {
|
|
|
145
190
|
// Let the fire-and-forget immediate refresh settle.
|
|
146
191
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
147
192
|
|
|
148
|
-
expect(
|
|
193
|
+
expect(getRawShareAnalytics()).toBe(true);
|
|
149
194
|
// One immediate refresh from the first call; the second is a no-op.
|
|
150
195
|
expect(createCallCount).toBe(1);
|
|
151
196
|
});
|
|
@@ -157,12 +202,12 @@ describe("consent-cache", () => {
|
|
|
157
202
|
mockClient = makeClient(makeConsent({ shareAnalytics: true }));
|
|
158
203
|
startConsentRefresh();
|
|
159
204
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
160
|
-
expect(
|
|
205
|
+
expect(getRawShareAnalytics()).toBe(true);
|
|
161
206
|
});
|
|
162
207
|
|
|
163
208
|
// share_diagnostics tracks the same refresh as share_analytics; Sentry's
|
|
164
209
|
// beforeSend re-reads it per event so a revocation is honored within a cycle.
|
|
165
|
-
test("diagnostics
|
|
210
|
+
test("diagnostics reads false at boot (unknown collapses to false)", () => {
|
|
166
211
|
expect(getCachedShareDiagnostics()).toBe(false);
|
|
167
212
|
});
|
|
168
213
|
|
|
@@ -192,14 +237,14 @@ describe("consent-cache", () => {
|
|
|
192
237
|
expect(getCachedShareDiagnostics()).toBe(true);
|
|
193
238
|
});
|
|
194
239
|
|
|
195
|
-
test("a missing client
|
|
240
|
+
test("a missing client demotes a prior diagnostics opt-in", async () => {
|
|
196
241
|
__setCachedShareDiagnosticsForTest(true);
|
|
197
242
|
mockClient = null;
|
|
198
243
|
await refreshConsentCache();
|
|
199
244
|
expect(getCachedShareDiagnostics()).toBe(false);
|
|
200
245
|
});
|
|
201
246
|
|
|
202
|
-
test("a client without a resolvable assistant identity
|
|
247
|
+
test("a client without a resolvable assistant identity demotes diagnostics", async () => {
|
|
203
248
|
__setCachedShareDiagnosticsForTest(true);
|
|
204
249
|
mockClient = makeClient(makeConsent({ shareDiagnostics: true }), "");
|
|
205
250
|
await refreshConsentCache();
|
|
@@ -251,4 +296,25 @@ describe("consent-cache", () => {
|
|
|
251
296
|
await refreshConsentCache();
|
|
252
297
|
expect(getCachedShareDiagnosticsVersion()).toBe("2026-06-18");
|
|
253
298
|
});
|
|
299
|
+
|
|
300
|
+
test("diagnostics accessor collapses unknown to false; raw analytics exposes the tri-state", async () => {
|
|
301
|
+
// Refresh once so the module's legacy opt-out marker reflects the
|
|
302
|
+
// beforeEach config (the marker is only re-read during a refresh).
|
|
303
|
+
mockClient = null;
|
|
304
|
+
await refreshConsentCache();
|
|
305
|
+
|
|
306
|
+
for (const state of [true, false, "unknown"] as const) {
|
|
307
|
+
__setCachedShareAnalyticsForTest(state);
|
|
308
|
+
__setCachedShareDiagnosticsForTest(state);
|
|
309
|
+
expect(getRawShareAnalytics()).toBe(state);
|
|
310
|
+
expect(getCachedShareDiagnostics()).toBe(state === true);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("legacy opt-out marker folds the raw analytics state to false", async () => {
|
|
315
|
+
setConfig("legacyTelemetryOptOut", true);
|
|
316
|
+
mockClient = null;
|
|
317
|
+
await refreshConsentCache();
|
|
318
|
+
expect(getRawShareAnalytics()).toBe(false);
|
|
319
|
+
});
|
|
254
320
|
});
|
|
@@ -14,48 +14,73 @@
|
|
|
14
14
|
* synchronous, I/O-free read, so this module owns the values and refreshes them
|
|
15
15
|
* periodically in the background. Consent is opt-out — an owner who never made
|
|
16
16
|
* an explicit choice reports as consented (the client maps a never-chose wire
|
|
17
|
-
* value to `true`)
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* value to `true`).
|
|
18
|
+
*
|
|
19
|
+
* The consent values are tri-state (`ConsentState`): `true`/`false` are
|
|
20
|
+
* confirmed states from a successful platform fetch; `"unknown"` means no
|
|
21
|
+
* confirmed state exists — boot before the first refresh, no platform session,
|
|
22
|
+
* or no resolvable owner identity. A transient fetch failure keeps the
|
|
23
|
+
* previous value so a known opt-in is not flipped mid-session. Analytics
|
|
24
|
+
* exposes only the raw tri-state accessor (gates drop solely on a confirmed
|
|
25
|
+
* opt-out); the diagnostics boolean accessors collapse `"unknown"` to `false`
|
|
26
|
+
* — diagnostics gates are deliberately fail-closed, so no raw diagnostics
|
|
27
|
+
* accessor exists.
|
|
21
28
|
*/
|
|
22
29
|
|
|
23
30
|
import { getConfigReadOnly } from "../config/loader.js";
|
|
24
31
|
import { getLogger } from "../util/logger.js";
|
|
25
32
|
import { VellumPlatformClient } from "./client.js";
|
|
33
|
+
import { arePlatformFeaturesEnabled } from "./feature-gate.js";
|
|
26
34
|
|
|
27
35
|
const log = getLogger("consent-cache");
|
|
28
36
|
|
|
29
37
|
const REFRESH_INTERVAL_MS = 5 * 60_000; // refresh consent every 5 min
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
/** Cached consent value: a confirmed boolean, or `"unknown"` when no confirmed state exists. */
|
|
40
|
+
export type ConsentState = boolean | "unknown";
|
|
41
|
+
|
|
42
|
+
let cachedShareAnalytics: ConsentState = "unknown"; // no confirmed state until first success
|
|
43
|
+
let cachedShareDiagnostics: ConsentState = "unknown"; // no confirmed state until first success
|
|
33
44
|
let cachedShareDiagnosticsVersion = ""; // "" fails the trace disclosure gate
|
|
34
45
|
// Fail-closed marker for a workspace that locally opted out of usage data
|
|
35
46
|
// before telemetry moved to platform `share_analytics` consent (migration 106).
|
|
36
47
|
// While set, telemetry stays off regardless of platform consent. Cleared by a
|
|
37
48
|
// future cross-repo reconciliation once the platform exposes an explicit
|
|
38
|
-
// re-consent signal; not auto-cleared here.
|
|
39
|
-
|
|
49
|
+
// re-consent signal; not auto-cleared here. Read live from config (an
|
|
50
|
+
// in-memory cached read, safe on the hot path): a boot-time default would
|
|
51
|
+
// leave the record-time gates open between daemon start and the first
|
|
52
|
+
// consent refresh, exactly the window unknown-consent recording now keeps
|
|
53
|
+
// events flowing in.
|
|
54
|
+
function isLegacyOptedOut(): boolean {
|
|
55
|
+
try {
|
|
56
|
+
return getConfigReadOnly().legacyTelemetryOptOut === true;
|
|
57
|
+
} catch {
|
|
58
|
+
// Config not yet loaded — no marker to honor yet.
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
40
62
|
let refreshTimer: ReturnType<typeof setInterval> | null = null;
|
|
41
63
|
|
|
42
64
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
65
|
+
* Raw tri-state `share_analytics` consent — the ONLY analytics accessor:
|
|
66
|
+
* every gate distinguishes a confirmed opt-out (`false`, the only state that
|
|
67
|
+
* drops or purges) from a not-yet-known one. The legacy fail-closed opt-out
|
|
68
|
+
* marker folds into `false` — a workspace-level opt-out is a confirmed
|
|
69
|
+
* opt-out, not an unknown. Never does I/O; hot-path safe.
|
|
46
70
|
*/
|
|
47
|
-
export function
|
|
48
|
-
return
|
|
71
|
+
export function getRawShareAnalytics(): ConsentState {
|
|
72
|
+
return isLegacyOptedOut() ? false : cachedShareAnalytics;
|
|
49
73
|
}
|
|
50
74
|
|
|
51
75
|
/**
|
|
52
76
|
* Synchronous hot-path accessor for the `share_diagnostics` consent (read by
|
|
53
|
-
* Sentry `beforeSend`). Never does I/O; returns `false`
|
|
54
|
-
* refresh
|
|
55
|
-
* mid-session opt-out is honored within one
|
|
77
|
+
* Sentry `beforeSend`). Never does I/O; returns `false` unless a successful
|
|
78
|
+
* refresh confirmed an opt-in (`"unknown"` reads as `false`). Because every
|
|
79
|
+
* Sentry event re-reads this, a mid-session opt-out is honored within one
|
|
80
|
+
* refresh cycle.
|
|
56
81
|
*/
|
|
57
82
|
export function getCachedShareDiagnostics(): boolean {
|
|
58
|
-
return cachedShareDiagnostics;
|
|
83
|
+
return cachedShareDiagnostics === true;
|
|
59
84
|
}
|
|
60
85
|
|
|
61
86
|
/**
|
|
@@ -70,28 +95,35 @@ export function getCachedShareDiagnosticsVersion(): string {
|
|
|
70
95
|
/**
|
|
71
96
|
* Refresh the cached consent from the platform.
|
|
72
97
|
*
|
|
73
|
-
* No platform session / features disabled (`create()` is null) →
|
|
98
|
+
* No platform session / features disabled (`create()` is null) → `"unknown"`.
|
|
74
99
|
* No resolvable assistant identity (no owner whose consent we can attest to) →
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
100
|
+
* `"unknown"` — losing the owner mapping is not an opt-out, but it also isn't
|
|
101
|
+
* a confirmed opt-in, so we stop riding a stale one. A successful fetch adopts
|
|
102
|
+
* the reported values. A `null` fetch (transient failure / undeployed
|
|
103
|
+
* endpoint) leaves the previous values unchanged so a known opt-in is not
|
|
104
|
+
* flipped off mid-session.
|
|
78
105
|
*/
|
|
79
106
|
export async function refreshConsentCache(): Promise<void> {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
107
|
+
// Platform features disabled is a PERMANENT condition (config-level), not a
|
|
108
|
+
// cold-cache transient: telemetry can never ship, so confirmed-off keeps the
|
|
109
|
+
// record-time gates closed and the outbox from accumulating rows that no
|
|
110
|
+
// flush will ever drain.
|
|
111
|
+
if (!arePlatformFeaturesEnabled()) {
|
|
84
112
|
setCachedShareAnalytics(false);
|
|
85
113
|
setCachedShareDiagnostics(false);
|
|
86
114
|
setCachedShareDiagnosticsVersion("");
|
|
87
115
|
return;
|
|
88
116
|
}
|
|
89
117
|
|
|
90
|
-
|
|
118
|
+
const client = await VellumPlatformClient.create();
|
|
119
|
+
if (!client) {
|
|
120
|
+
resetConsentToUnknown();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// No resolvable owner identity → unknown (don't ride a stale opt-in).
|
|
91
125
|
if (!client.platformAssistantId) {
|
|
92
|
-
|
|
93
|
-
setCachedShareDiagnostics(false);
|
|
94
|
-
setCachedShareDiagnosticsVersion("");
|
|
126
|
+
resetConsentToUnknown();
|
|
95
127
|
return;
|
|
96
128
|
}
|
|
97
129
|
|
|
@@ -103,7 +135,13 @@ export async function refreshConsentCache(): Promise<void> {
|
|
|
103
135
|
}
|
|
104
136
|
}
|
|
105
137
|
|
|
106
|
-
function
|
|
138
|
+
function resetConsentToUnknown(): void {
|
|
139
|
+
setCachedShareAnalytics("unknown");
|
|
140
|
+
setCachedShareDiagnostics("unknown");
|
|
141
|
+
setCachedShareDiagnosticsVersion("");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function setCachedShareAnalytics(value: ConsentState): void {
|
|
107
145
|
if (value !== cachedShareAnalytics) {
|
|
108
146
|
log.debug(
|
|
109
147
|
{ from: cachedShareAnalytics, to: value },
|
|
@@ -113,7 +151,7 @@ function setCachedShareAnalytics(value: boolean): void {
|
|
|
113
151
|
}
|
|
114
152
|
}
|
|
115
153
|
|
|
116
|
-
function setCachedShareDiagnostics(value:
|
|
154
|
+
function setCachedShareDiagnostics(value: ConsentState): void {
|
|
117
155
|
if (value !== cachedShareDiagnostics) {
|
|
118
156
|
log.debug(
|
|
119
157
|
{ from: cachedShareDiagnostics, to: value },
|
|
@@ -161,12 +199,12 @@ export async function stopConsentRefresh(): Promise<void> {
|
|
|
161
199
|
}
|
|
162
200
|
|
|
163
201
|
/** Test-only: override the cached analytics value without going through a refresh. */
|
|
164
|
-
export function __setCachedShareAnalyticsForTest(value:
|
|
202
|
+
export function __setCachedShareAnalyticsForTest(value: ConsentState): void {
|
|
165
203
|
cachedShareAnalytics = value;
|
|
166
204
|
}
|
|
167
205
|
|
|
168
206
|
/** Test-only: override the cached diagnostics value without going through a refresh. */
|
|
169
|
-
export function __setCachedShareDiagnosticsForTest(value:
|
|
207
|
+
export function __setCachedShareDiagnosticsForTest(value: ConsentState): void {
|
|
170
208
|
cachedShareDiagnostics = value;
|
|
171
209
|
}
|
|
172
210
|
|
|
@@ -36,3 +36,15 @@ export const HOOKS = {
|
|
|
36
36
|
|
|
37
37
|
/** Union of every hook name declared in {@link HOOKS}. */
|
|
38
38
|
export type HookName = (typeof HOOKS)[keyof typeof HOOKS];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Appended (inside the `<system_notice>` wrapper) to the internal continuation /
|
|
42
|
+
* completion nudge strings that re-query the model. Those notices are injected
|
|
43
|
+
* as user-role turns; weaker models without a separate reasoning channel
|
|
44
|
+
* otherwise narrate or answer the notice as visible text, leaking agent-loop
|
|
45
|
+
* scaffolding into the user-facing reply. This clause forbids that narration
|
|
46
|
+
* without licensing an empty reply — each nudge's own instruction still drives
|
|
47
|
+
* the expected output (summary / final reply / continuation).
|
|
48
|
+
*/
|
|
49
|
+
export const INTERNAL_NUDGE_OUTPUT_SUPPRESSION =
|
|
50
|
+
" This notice is internal: do not repeat, quote, describe, or acknowledge it, and do not narrate whether you will continue or whether the turn is done. Reply with only the content the user should see.";
|
|
@@ -45,6 +45,15 @@ export interface RunConversationTurnOptions {
|
|
|
45
45
|
* controller fires, terminating the in-flight agent loop.
|
|
46
46
|
*/
|
|
47
47
|
signal?: AbortSignal;
|
|
48
|
+
/**
|
|
49
|
+
* Conversation type for newly created conversations. When omitted,
|
|
50
|
+
* defaults to `"standard"` (visible in the sidebar). Set to
|
|
51
|
+
* `"background"` for plugin-driven conversations that should not
|
|
52
|
+
* appear in the sidebar's Recents grouping.
|
|
53
|
+
*
|
|
54
|
+
* Ignored when `conversationId` references an existing conversation.
|
|
55
|
+
*/
|
|
56
|
+
conversationType?: "standard" | "background";
|
|
48
57
|
}
|
|
49
58
|
|
|
50
59
|
export interface RunConversationTurnResult {
|
|
@@ -71,7 +80,9 @@ export interface RunConversationTurnResult {
|
|
|
71
80
|
*/
|
|
72
81
|
function extractContentAndAttachments(
|
|
73
82
|
blocks: ContentBlock[],
|
|
74
|
-
resolveMedia: (
|
|
83
|
+
resolveMedia: (
|
|
84
|
+
source: MediaSource,
|
|
85
|
+
) => { data: string; media_type: string } | null,
|
|
75
86
|
): { text: string; attachments: UserMessageAttachment[] } {
|
|
76
87
|
const textParts: string[] = [];
|
|
77
88
|
const attachments: UserMessageAttachment[] = [];
|
|
@@ -126,21 +137,33 @@ export async function runConversationTurn(
|
|
|
126
137
|
options: RunConversationTurnOptions,
|
|
127
138
|
): Promise<RunConversationTurnResult> {
|
|
128
139
|
const { v7: uuidv7 } = await import("uuid");
|
|
129
|
-
const { getOrCreateConversation } =
|
|
130
|
-
"../daemon/conversation-store.js"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
"../persistence/conversation-crud.js"
|
|
140
|
-
);
|
|
140
|
+
const { getOrCreateConversation } =
|
|
141
|
+
await import("../daemon/conversation-store.js");
|
|
142
|
+
const { broadcastMessage } =
|
|
143
|
+
await import("../runtime/assistant-event-hub.js");
|
|
144
|
+
const { resolveMediaSourceData } =
|
|
145
|
+
await import("../providers/media-resolve.js");
|
|
146
|
+
const { getConversation, getMessageById, getMessages } =
|
|
147
|
+
await import("../persistence/conversation-crud.js");
|
|
148
|
+
const { publishConversationListAndMetadataChanged } =
|
|
149
|
+
await import("../runtime/sync/resource-sync-events.js");
|
|
141
150
|
|
|
142
151
|
const conversationId = options.conversationId ?? uuidv7();
|
|
143
|
-
const
|
|
152
|
+
const rowExisted = getConversation(conversationId) != null;
|
|
153
|
+
const conversation = await getOrCreateConversation(conversationId, {
|
|
154
|
+
...(options.conversationType
|
|
155
|
+
? { conversationType: options.conversationType }
|
|
156
|
+
: {}),
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// `getOrCreateConversation` creates the DB row (with conversationType if
|
|
160
|
+
// provided) before hydrating. The normal send-message route emits a
|
|
161
|
+
// "created" list invalidation so sibling clients/sidebars learn about new
|
|
162
|
+
// conversations — emit it here too, but only when the row is actually new
|
|
163
|
+
// so we don't spam invalidations for existing conversations.
|
|
164
|
+
if (!rowExisted) {
|
|
165
|
+
publishConversationListAndMetadataChanged("created", conversationId);
|
|
166
|
+
}
|
|
144
167
|
|
|
145
168
|
// Wire the external abort signal to the conversation's internal abort
|
|
146
169
|
// controller so aborting the signal terminates the in-flight agent loop.
|
package/src/plugin-api/index.ts
CHANGED
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
*/
|
|
68
68
|
|
|
69
69
|
export type { HookName } from "./constants.js";
|
|
70
|
-
export { HOOKS } from "./constants.js";
|
|
70
|
+
export { HOOKS, INTERNAL_NUDGE_OUTPUT_SUPPRESSION } from "./constants.js";
|
|
71
71
|
// Conversation message/content shapes. A hook receives the live message
|
|
72
72
|
// history (e.g. `PostToolUseContext.latestMessages: Message[]`), so plugins
|
|
73
73
|
// that inspect or narrow content blocks — reading a `tool_use` block's input,
|
|
@@ -150,6 +150,16 @@ export { getModelProfiles } from "./model-profiles.js";
|
|
|
150
150
|
// looks up the model catalog's `supportsVision` flag (mix profiles are
|
|
151
151
|
// vision-capable if any arm is). Returns false when nothing resolves.
|
|
152
152
|
export { doesSupportVision } from "./vision-support.js";
|
|
153
|
+
// Resolve a stored credential to its plaintext value — the same value
|
|
154
|
+
// `assistant credentials reveal` prints — from a UUID or a "service/field"
|
|
155
|
+
// reference. When a plugin is in context, resolution is scoped to credentials
|
|
156
|
+
// whose `field` matches the plugin's manifest name; outside any plugin it is
|
|
157
|
+
// unscoped. Throws CredentialResolutionError when the ref does not resolve, the
|
|
158
|
+
// store is unreachable, or the credential is out of the plugin's scope.
|
|
159
|
+
export {
|
|
160
|
+
CredentialResolutionError,
|
|
161
|
+
resolveCredential,
|
|
162
|
+
} from "./resolve-credential.js";
|
|
153
163
|
// Resolve a provider for a call site (optionally overriding the profile) so a
|
|
154
164
|
// plugin can run inference through the workspace's configured profiles and
|
|
155
165
|
// credentials — managed-proxy or BYOK — without supplying its own API key.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin-facing credential resolution.
|
|
3
|
+
*
|
|
4
|
+
* {@link resolveCredential} returns a stored credential's plaintext value — the
|
|
5
|
+
* same value `assistant credentials reveal` prints — resolving a reference that
|
|
6
|
+
* is either a credential UUID or a `"service/field"` string, exactly as the
|
|
7
|
+
* reveal path does (see {@link ../tools/credentials/resolve.resolveCredentialRef}
|
|
8
|
+
* and the `credentials/reveal` route).
|
|
9
|
+
*
|
|
10
|
+
* ## Plugin scoping
|
|
11
|
+
*
|
|
12
|
+
* When a plugin is in context — its hook or tool is executing, tracked by
|
|
13
|
+
* {@link ../plugins/plugin-execution-context.getCurrentPluginName} — resolution
|
|
14
|
+
* is restricted: the plugin may only resolve credentials whose `field` equals
|
|
15
|
+
* the plugin's own manifest name. A plugin named `acme` can therefore read
|
|
16
|
+
* `openai/acme` or `stripe/acme` but not `openai/api_key`. Outside any plugin
|
|
17
|
+
* context (host-internal callers, CLI, tests) the resolver is unscoped and
|
|
18
|
+
* behaves like a direct reveal.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { getCurrentPluginName } from "../plugins/plugin-execution-context.js";
|
|
22
|
+
import { getSecureKeyResultAsync } from "../security/secure-keys.js";
|
|
23
|
+
import { resolveCredentialRef } from "../tools/credentials/resolve.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Raised when a credential cannot be resolved: the reference does not match a
|
|
27
|
+
* stored credential, the store is unreachable, or the calling plugin is not
|
|
28
|
+
* permitted to resolve the requested credential.
|
|
29
|
+
*/
|
|
30
|
+
export class CredentialResolutionError extends Error {
|
|
31
|
+
constructor(message: string) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "CredentialResolutionError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resolve a credential reference to its plaintext value.
|
|
39
|
+
*
|
|
40
|
+
* @param ref A credential UUID or a `"service/field"` string.
|
|
41
|
+
* @returns The plaintext credential value.
|
|
42
|
+
* @throws {CredentialResolutionError} when the reference does not resolve, the
|
|
43
|
+
* store is unreachable, or a plugin in context is not scoped to the credential.
|
|
44
|
+
*/
|
|
45
|
+
export async function resolveCredential(ref: string): Promise<string> {
|
|
46
|
+
const resolved = resolveCredentialRef(ref);
|
|
47
|
+
if (!resolved) {
|
|
48
|
+
throw new CredentialResolutionError(`Credential not found: ${ref}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Scope the resolution to the plugin in context, if any. The field-name gate
|
|
52
|
+
// is enforced before the plaintext is read so an out-of-scope plugin never
|
|
53
|
+
// touches the secure backend.
|
|
54
|
+
const pluginName = getCurrentPluginName();
|
|
55
|
+
if (pluginName !== undefined && resolved.field !== pluginName) {
|
|
56
|
+
throw new CredentialResolutionError(
|
|
57
|
+
`Plugin "${pluginName}" may only resolve credentials whose field matches its name; ` +
|
|
58
|
+
`"${resolved.service}/${resolved.field}" is out of scope.`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { value, unreachable } = await getSecureKeyResultAsync(
|
|
63
|
+
resolved.storageKey,
|
|
64
|
+
);
|
|
65
|
+
if (value == null || value.length === 0) {
|
|
66
|
+
if (unreachable) {
|
|
67
|
+
throw new CredentialResolutionError(
|
|
68
|
+
"Credential store is unreachable — ensure the assistant is running",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
throw new CredentialResolutionError(`Credential not found: ${ref}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
@@ -14,10 +14,6 @@ interface MockProfileEntry {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
let mockProfiles: Record<string, MockProfileEntry> = {};
|
|
17
|
-
let mockDefault: { provider?: string; model?: string } = {
|
|
18
|
-
provider: "anthropic",
|
|
19
|
-
model: "claude-opus-4-6",
|
|
20
|
-
};
|
|
21
17
|
|
|
22
18
|
// Real model catalog — don't mock it, the test exercises real catalog lookups.
|
|
23
19
|
const { doesSupportVision } = await import("./vision-support.js");
|
|
@@ -44,21 +40,16 @@ function profile(key: string): ModelProfileInfo {
|
|
|
44
40
|
function applyConfig(): void {
|
|
45
41
|
setConfig("llm", { profiles: {} });
|
|
46
42
|
const config = getConfig() as { llm: unknown };
|
|
47
|
-
config.llm = { profiles: mockProfiles
|
|
43
|
+
config.llm = { profiles: mockProfiles };
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
function setMockConfig(
|
|
51
|
-
profiles: Record<string, MockProfileEntry>,
|
|
52
|
-
def: { provider?: string; model?: string } = {},
|
|
53
|
-
) {
|
|
46
|
+
function setMockConfig(profiles: Record<string, MockProfileEntry>) {
|
|
54
47
|
mockProfiles = profiles;
|
|
55
|
-
mockDefault = { provider: "anthropic", model: "claude-opus-4-6", ...def };
|
|
56
48
|
applyConfig();
|
|
57
49
|
}
|
|
58
50
|
|
|
59
51
|
beforeEach(() => {
|
|
60
52
|
mockProfiles = {};
|
|
61
|
-
mockDefault = { provider: "anthropic", model: "claude-opus-4-6" };
|
|
62
53
|
applyConfig();
|
|
63
54
|
});
|
|
64
55
|
|
|
@@ -94,18 +85,16 @@ describe("doesSupportVision", () => {
|
|
|
94
85
|
expect(doesSupportVision(profile("unknown-model"))).toBe(false);
|
|
95
86
|
});
|
|
96
87
|
|
|
97
|
-
test("
|
|
98
|
-
setMockConfig(
|
|
99
|
-
{ "model-only": { model: "claude-opus-4-6" } },
|
|
100
|
-
{ provider: "anthropic" },
|
|
101
|
-
);
|
|
88
|
+
test("implies the provider from the catalog when profile only sets model", () => {
|
|
89
|
+
setMockConfig({ "model-only": { model: "claude-opus-4-6" } });
|
|
102
90
|
expect(doesSupportVision(profile("model-only"))).toBe(true);
|
|
103
91
|
});
|
|
104
92
|
|
|
105
|
-
test("
|
|
106
|
-
//
|
|
93
|
+
test("fails safe to false for a profile without a model", () => {
|
|
94
|
+
// A model-less entry is not a usable resolution target, so vision
|
|
95
|
+
// resolution treats it as "can't show images" (caption instead).
|
|
107
96
|
setMockConfig({ "provider-only": { provider: "anthropic" } });
|
|
108
|
-
expect(doesSupportVision(profile("provider-only"))).toBe(
|
|
97
|
+
expect(doesSupportVision(profile("provider-only"))).toBe(false);
|
|
109
98
|
});
|
|
110
99
|
|
|
111
100
|
test("mix profile returns true when any arm supports vision", () => {
|