@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
|
@@ -16,10 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
import { z } from "zod";
|
|
18
18
|
|
|
19
|
+
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
20
|
+
import { getConfig } from "../../config/loader.js";
|
|
19
21
|
import {
|
|
20
22
|
fetchManagedCatalog,
|
|
21
23
|
type ManagedCredentialDescriptor,
|
|
22
24
|
} from "../../credential-execution/managed-catalog.js";
|
|
25
|
+
import { buildForChatSentinel } from "../../daemon/chat-credential-redaction.js";
|
|
23
26
|
import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
|
|
24
27
|
import {
|
|
25
28
|
disconnectOAuthProvider,
|
|
@@ -48,6 +51,8 @@ import {
|
|
|
48
51
|
} from "../../tools/credentials/metadata-store.js";
|
|
49
52
|
import type { CredentialInjectionTemplate } from "../../tools/credentials/policy-types.js";
|
|
50
53
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
54
|
+
import { recordForChatMint } from "../for-chat-mint-registry.js";
|
|
55
|
+
import { recordRevealSuccess } from "../reveal-success-registry.js";
|
|
51
56
|
import { InjectionTemplateSchema } from "./credential-prompt-routes.js";
|
|
52
57
|
import { BadRequestError, InternalError } from "./errors.js";
|
|
53
58
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
@@ -296,11 +301,30 @@ async function handleCredentialsInspect({ body }: RouteHandlerArgs) {
|
|
|
296
301
|
return output;
|
|
297
302
|
}
|
|
298
303
|
|
|
299
|
-
async function handleCredentialsReveal({ body }: RouteHandlerArgs) {
|
|
304
|
+
async function handleCredentialsReveal({ body, headers }: RouteHandlerArgs) {
|
|
300
305
|
if (!body || typeof body !== "object") {
|
|
301
306
|
throw new BadRequestError("Request body is required");
|
|
302
307
|
}
|
|
303
308
|
|
|
309
|
+
const forChat = (body as { forChat?: unknown }).forChat === true;
|
|
310
|
+
if (
|
|
311
|
+
forChat &&
|
|
312
|
+
!isAssistantFeatureFlagEnabled("chat-credential-reveal", getConfig())
|
|
313
|
+
) {
|
|
314
|
+
throw new BadRequestError(
|
|
315
|
+
"--for-chat requires the chat-credential-reveal feature flag",
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Conversation-bound reveal nonce (see reveal-nonce.ts), forwarded by
|
|
320
|
+
// the CLI from the tool-shell env. Required for RECORDING any
|
|
321
|
+
// reveal-derived chat authority below; the reveal itself works without
|
|
322
|
+
// it (Settings rows, direct terminal use) — those calls just create no
|
|
323
|
+
// authority for any conversation's transcript.
|
|
324
|
+
const rawNonce = (body as { revealNonce?: unknown }).revealNonce;
|
|
325
|
+
const revealNonce =
|
|
326
|
+
typeof rawNonce === "string" && rawNonce.length > 0 ? rawNonce : undefined;
|
|
327
|
+
|
|
304
328
|
const lookup = resolveCredentialLookup(body);
|
|
305
329
|
const { value: secret, unreachable } = await getSecureKeyResultAsync(
|
|
306
330
|
lookup.storageKey,
|
|
@@ -315,6 +339,79 @@ async function handleCredentialsReveal({ body }: RouteHandlerArgs) {
|
|
|
315
339
|
throw new BadRequestError("Credential not found");
|
|
316
340
|
}
|
|
317
341
|
|
|
342
|
+
// Recording gate shared by both registries below: only a DIRECT tool-shell
|
|
343
|
+
// CLI invocation may create authority. The `local` principal is the
|
|
344
|
+
// identity a CLI invocation arrives with over the unix-socket IPC
|
|
345
|
+
// (verified by the adapters, never caller-supplied), but the principal
|
|
346
|
+
// check alone is not enough: in local mode the gateway derives `local`
|
|
347
|
+
// from the verified JWT sub and forwards it for web calls too, stamping
|
|
348
|
+
// `x-vellum-proxy-server: ipc` — a header a direct CLI never sends — so
|
|
349
|
+
// proxied Settings-row/chat-chip reveals are excluded.
|
|
350
|
+
const directLocalInvocation =
|
|
351
|
+
headers?.["x-vellum-principal-type"] === "local" &&
|
|
352
|
+
headers?.["x-vellum-proxy-server"] !== "ipc";
|
|
353
|
+
|
|
354
|
+
if (forChat) {
|
|
355
|
+
// Both lookup forms resolve service+field (or throw); the narrow check
|
|
356
|
+
// keeps the sentinel's vault coordinates provably non-empty.
|
|
357
|
+
if (!lookup.service || !lookup.field) {
|
|
358
|
+
throw new BadRequestError(
|
|
359
|
+
"Credential identity could not be resolved for --for-chat",
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
// Chat-safe reveal: return the enriched redaction sentinel instead of
|
|
363
|
+
// the plaintext. The caller (the model, echoing a tool result) can
|
|
364
|
+
// paste this into its reply to render a click-to-reveal chip without
|
|
365
|
+
// the secret ever entering model context or the conversation stream.
|
|
366
|
+
// The persist-path forgery guard re-mints it on identity match — see
|
|
367
|
+
// guardForChatSentinels in chat-credential-redaction.ts. No plaintext
|
|
368
|
+
// reaches the tool, so no reveal-success proof is recorded: that
|
|
369
|
+
// registry authorizes plaintext-echo swaps, and retaining the secret
|
|
370
|
+
// for a path that never emits it would widen retention for nothing.
|
|
371
|
+
const sentinel = buildForChatSentinel({
|
|
372
|
+
service: lookup.service,
|
|
373
|
+
field: lookup.field,
|
|
374
|
+
value: secret,
|
|
375
|
+
});
|
|
376
|
+
// Record the mint AFTER the reveal provably succeeded. This registry —
|
|
377
|
+
// not any parse of the requested shell command — is what authorizes the
|
|
378
|
+
// persist guard to re-mint a sentinel with this identity: a command
|
|
379
|
+
// that merely quotes or comments out a reveal invocation never reaches
|
|
380
|
+
// this route, so it never allowlists anything. Like the plaintext proof
|
|
381
|
+
// below, the mint records only for a direct tool-shell invocation, and
|
|
382
|
+
// only with the conversation-bound nonce that scopes it to the
|
|
383
|
+
// conversation whose tool shell made this call (see the registry
|
|
384
|
+
// module doc — no predictable identifier can do this job).
|
|
385
|
+
if (directLocalInvocation && revealNonce !== undefined) {
|
|
386
|
+
recordForChatMint({
|
|
387
|
+
service: lookup.service,
|
|
388
|
+
field: lookup.field,
|
|
389
|
+
sentinel,
|
|
390
|
+
nonce: revealNonce,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return { value: sentinel };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Ground truth for the chat-credential-reveal persist seams: this route
|
|
397
|
+
// is the only place a reveal legitimately reads plaintext, so a success
|
|
398
|
+
// recorded here is the proof the agent loop requires before promoting
|
|
399
|
+
// staged reveal candidates (a shell command can "succeed" without ever
|
|
400
|
+
// reaching this handler — `… || true`, or an echo of the command text).
|
|
401
|
+
// Recording a proxied UI reveal would let a click promote a staged ref
|
|
402
|
+
// in a concurrent turn whose command merely echoed (or was denied) the
|
|
403
|
+
// invocation. An unproven ref degrades to a plain sentinel — the safe
|
|
404
|
+
// direction. Both lookup branches populate service/field; the guard only
|
|
405
|
+
// satisfies the loose `CredentialLookup` type.
|
|
406
|
+
if (
|
|
407
|
+
directLocalInvocation &&
|
|
408
|
+
revealNonce !== undefined &&
|
|
409
|
+
lookup.service !== undefined &&
|
|
410
|
+
lookup.field !== undefined
|
|
411
|
+
) {
|
|
412
|
+
recordRevealSuccess(lookup.service, lookup.field, secret, revealNonce);
|
|
413
|
+
}
|
|
414
|
+
|
|
318
415
|
return { value: secret };
|
|
319
416
|
}
|
|
320
417
|
|
|
@@ -523,15 +620,31 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
523
620
|
},
|
|
524
621
|
summary: "Reveal a credential's plaintext value",
|
|
525
622
|
description:
|
|
526
|
-
"Return the raw plaintext value of a stored credential. Blocked in untrusted shell mode.",
|
|
623
|
+
"Return the raw plaintext value of a stored credential. Blocked in untrusted shell mode. With forChat, returns a chat-safe redaction sentinel instead of the plaintext.",
|
|
527
624
|
tags: ["credentials"],
|
|
528
625
|
requestBody: z.object({
|
|
529
626
|
service: z.string().optional().describe("Service namespace"),
|
|
530
627
|
field: z.string().optional().describe("Field name"),
|
|
531
628
|
id: z.string().optional().describe("Credential UUID for lookup by ID"),
|
|
629
|
+
forChat: z
|
|
630
|
+
.boolean()
|
|
631
|
+
.optional()
|
|
632
|
+
.describe(
|
|
633
|
+
"Return the credential's redaction sentinel (renders as a click-to-reveal chip in chat) instead of the plaintext. Requires the chat-credential-reveal feature flag.",
|
|
634
|
+
),
|
|
635
|
+
revealNonce: z
|
|
636
|
+
.string()
|
|
637
|
+
.optional()
|
|
638
|
+
.describe(
|
|
639
|
+
"Conversation-bound reveal nonce from the tool-shell environment. Scopes any recorded chat-redaction authority (plaintext proof or forChat mint) to the conversation whose tool executed this reveal; without it the reveal succeeds but records no authority.",
|
|
640
|
+
),
|
|
532
641
|
}),
|
|
533
642
|
responseBody: z.object({
|
|
534
|
-
value: z
|
|
643
|
+
value: z
|
|
644
|
+
.string()
|
|
645
|
+
.describe(
|
|
646
|
+
"The plaintext credential value (or its redaction sentinel when forChat is set)",
|
|
647
|
+
),
|
|
535
648
|
}),
|
|
536
649
|
handler: handleCredentialsReveal,
|
|
537
650
|
},
|
|
@@ -240,13 +240,18 @@ async function handleEmailDownload({ queryParams = {} }: RouteHandlerArgs) {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
async function handleEmailSend({ body = {} }: RouteHandlerArgs) {
|
|
243
|
-
const { to, text, subject, html, cc, bcc, reply_to } = body as {
|
|
243
|
+
const { to, text, subject, html, cc, bcc, attachments, reply_to } = body as {
|
|
244
244
|
to: string[];
|
|
245
245
|
text: string;
|
|
246
246
|
subject?: string;
|
|
247
247
|
html?: string;
|
|
248
248
|
cc?: string[];
|
|
249
249
|
bcc?: string[];
|
|
250
|
+
attachments?: {
|
|
251
|
+
filename: string;
|
|
252
|
+
content_type: string;
|
|
253
|
+
content: string;
|
|
254
|
+
}[];
|
|
250
255
|
reply_to?: string;
|
|
251
256
|
};
|
|
252
257
|
|
|
@@ -293,6 +298,7 @@ async function handleEmailSend({ body = {} }: RouteHandlerArgs) {
|
|
|
293
298
|
if (resolvedHtml) payload.html = resolvedHtml;
|
|
294
299
|
if (cc && cc.length > 0) payload.cc = cc;
|
|
295
300
|
if (bcc && bcc.length > 0) payload.bcc = bcc;
|
|
301
|
+
if (attachments && attachments.length > 0) payload.attachments = attachments;
|
|
296
302
|
if (reply_to) payload.reply_to = reply_to;
|
|
297
303
|
|
|
298
304
|
const response = await client.fetch("/v1/runtime-proxy/email/send/", {
|
|
@@ -567,6 +573,16 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
567
573
|
.describe("HTML body (auto-generated from text if omitted)"),
|
|
568
574
|
cc: z.array(z.string()).optional().describe("CC recipients"),
|
|
569
575
|
bcc: z.array(z.string()).optional().describe("BCC recipients"),
|
|
576
|
+
attachments: z
|
|
577
|
+
.array(
|
|
578
|
+
z.object({
|
|
579
|
+
filename: z.string(),
|
|
580
|
+
content_type: z.string(),
|
|
581
|
+
content: z.string().describe("Base64-encoded file content"),
|
|
582
|
+
}),
|
|
583
|
+
)
|
|
584
|
+
.optional()
|
|
585
|
+
.describe("File attachments"),
|
|
570
586
|
reply_to: z.string().optional().describe("Reply-to email ID"),
|
|
571
587
|
}),
|
|
572
588
|
responseBody: z.object({
|
|
@@ -151,7 +151,7 @@ describe("tryTranscribeAudioAttachments", () => {
|
|
|
151
151
|
expect(reason).not.toContain("OpenAI");
|
|
152
152
|
});
|
|
153
153
|
|
|
154
|
-
test("API failure returns
|
|
154
|
+
test("API failure returns a friendly, non-raw reason naming the provider", async () => {
|
|
155
155
|
const audio = makeAudioAttachment("a1");
|
|
156
156
|
mockAttachments = [audio];
|
|
157
157
|
mockTranscriber = {
|
|
@@ -165,9 +165,37 @@ describe("tryTranscribeAudioAttachments", () => {
|
|
|
165
165
|
const result = await tryTranscribeAudioAttachments(["a1"]);
|
|
166
166
|
|
|
167
167
|
expect(result.status).toBe("error");
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
);
|
|
168
|
+
const reason = (result as { reason: string }).reason;
|
|
169
|
+
// Friendly copy replaces the raw provider string.
|
|
170
|
+
expect(reason).not.toContain("API rate limit exceeded");
|
|
171
|
+
expect(reason).toContain("OpenAI Whisper");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("auth failure returns a friendly, JSON-free reason that names the API key", async () => {
|
|
175
|
+
const audio = makeAudioAttachment("a1");
|
|
176
|
+
mockAttachments = [audio];
|
|
177
|
+
mockTranscriber = {
|
|
178
|
+
providerId: "deepgram",
|
|
179
|
+
boundaryId: "daemon-batch",
|
|
180
|
+
transcribe: async () => {
|
|
181
|
+
// Mocked normalizeSttError returns an SttError as-is, so throw the
|
|
182
|
+
// categorized error directly (a plain 401 string would classify as
|
|
183
|
+
// provider-error under the simplified mock).
|
|
184
|
+
throw new SttError(
|
|
185
|
+
"auth",
|
|
186
|
+
'Deepgram API error (401): {"err_code":"INVALID_AUTH"}',
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const result = await tryTranscribeAudioAttachments(["a1"]);
|
|
192
|
+
|
|
193
|
+
expect(result.status).toBe("error");
|
|
194
|
+
const reason = (result as { reason: string }).reason;
|
|
195
|
+
expect(reason).not.toContain("{");
|
|
196
|
+
expect(reason).not.toContain("INVALID_AUTH");
|
|
197
|
+
expect(reason).toContain("API key");
|
|
198
|
+
expect(reason).toContain("Deepgram");
|
|
171
199
|
});
|
|
172
200
|
|
|
173
201
|
test("30-second timeout fires and returns error without blocking", async () => {
|
|
@@ -184,7 +212,7 @@ describe("tryTranscribeAudioAttachments", () => {
|
|
|
184
212
|
const result = await tryTranscribeAudioAttachments(["a1"]);
|
|
185
213
|
|
|
186
214
|
expect(result.status).toBe("error");
|
|
187
|
-
expect((result as { reason: string }).reason).
|
|
215
|
+
expect((result as { reason: string }).reason).toContain(
|
|
188
216
|
"Transcription timed out",
|
|
189
217
|
);
|
|
190
218
|
});
|
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
getAttachmentsByIds,
|
|
13
13
|
} from "../../../persistence/attachments-store.js";
|
|
14
14
|
import { resolveBatchTranscriber } from "../../../providers/speech-to-text/resolve.js";
|
|
15
|
+
import { describeSttFailure } from "../../../providers/voice-error-copy.js";
|
|
15
16
|
import { normalizeSttError } from "../../../stt/daemon-batch-transcriber.js";
|
|
17
|
+
import type { SttProviderId } from "../../../stt/types.js";
|
|
16
18
|
import { getLogger } from "../../../util/logger.js";
|
|
17
19
|
|
|
18
20
|
const log = getLogger("transcribe-audio");
|
|
@@ -37,6 +39,8 @@ export type TranscribeResult =
|
|
|
37
39
|
export async function tryTranscribeAudioAttachments(
|
|
38
40
|
attachmentIds: string[],
|
|
39
41
|
): Promise<TranscribeResult> {
|
|
42
|
+
// Hoisted so the catch can name the provider in a friendly failure message.
|
|
43
|
+
let providerId: SttProviderId | undefined;
|
|
40
44
|
try {
|
|
41
45
|
// Look up attachments and filter to audio MIME types
|
|
42
46
|
const resolved = getAttachmentsByIds(attachmentIds);
|
|
@@ -57,6 +61,7 @@ export async function tryTranscribeAudioAttachments(
|
|
|
57
61
|
"No speech-to-text provider configured. Add an API key for your STT service to enable voice message transcription.",
|
|
58
62
|
};
|
|
59
63
|
}
|
|
64
|
+
providerId = transcriber.providerId;
|
|
60
65
|
|
|
61
66
|
// Transcribe each audio attachment with a shared timeout
|
|
62
67
|
const abortController = new AbortController();
|
|
@@ -103,11 +108,7 @@ export async function tryTranscribeAudioAttachments(
|
|
|
103
108
|
}
|
|
104
109
|
} catch (err: unknown) {
|
|
105
110
|
const sttErr = normalizeSttError(err);
|
|
106
|
-
const reason =
|
|
107
|
-
sttErr.category === "timeout"
|
|
108
|
-
? "Transcription timed out"
|
|
109
|
-
: sttErr.message;
|
|
110
111
|
log.warn({ err }, "Audio transcription failed");
|
|
111
|
-
return { status: "error", reason };
|
|
112
|
+
return { status: "error", reason: describeSttFailure(sttErr, providerId) };
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -19,10 +19,12 @@ import { getIsPlatform } from "../../config/env-registry.js";
|
|
|
19
19
|
import { getConfigReadOnly } from "../../config/loader.js";
|
|
20
20
|
import { getDb } from "../../persistence/db-connection.js";
|
|
21
21
|
import {
|
|
22
|
+
type Auth,
|
|
22
23
|
AuthSchema,
|
|
23
24
|
type ConnectionModel,
|
|
24
25
|
ConnectionModelSchema,
|
|
25
26
|
ConnectionProviderSchema,
|
|
27
|
+
deriveAuthForProvider,
|
|
26
28
|
ProviderConnectionSchema,
|
|
27
29
|
PROVIDERS_REQUIRING_BASE_URL_AND_MODELS,
|
|
28
30
|
VALID_CONNECTION_PROVIDERS,
|
|
@@ -104,7 +106,9 @@ async function parseCustomProviderFields(
|
|
|
104
106
|
throw new BadRequestError(`Invalid base_url: must be an http(s) URL`);
|
|
105
107
|
}
|
|
106
108
|
} catch (err) {
|
|
107
|
-
if (err instanceof BadRequestError)
|
|
109
|
+
if (err instanceof BadRequestError) {
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
108
112
|
throw new BadRequestError(
|
|
109
113
|
`Invalid base_url: must be a valid http(s) URL`,
|
|
110
114
|
);
|
|
@@ -159,6 +163,28 @@ async function parseCustomProviderFields(
|
|
|
159
163
|
return out;
|
|
160
164
|
}
|
|
161
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Derive the auth object for a body that omits `auth`, from the provider and
|
|
168
|
+
* the optional top-level `credential` field. Throws the 400s for the cases
|
|
169
|
+
* the derivation can't express: a malformed credential, or a provider that
|
|
170
|
+
* needs an API key when none was supplied.
|
|
171
|
+
*/
|
|
172
|
+
function deriveConnectionAuth(provider: string, credential: unknown): Auth {
|
|
173
|
+
if (
|
|
174
|
+
credential !== undefined &&
|
|
175
|
+
(typeof credential !== "string" || credential.length === 0)
|
|
176
|
+
) {
|
|
177
|
+
throw new BadRequestError("credential must be a non-empty string");
|
|
178
|
+
}
|
|
179
|
+
const derived = deriveAuthForProvider(provider, credential);
|
|
180
|
+
if (!derived) {
|
|
181
|
+
throw new BadRequestError(
|
|
182
|
+
`Provider "${provider}" requires an API key. Pass "credential" (a vault credential key) or an explicit "auth" object.`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return derived;
|
|
186
|
+
}
|
|
187
|
+
|
|
162
188
|
// ---------------------------------------------------------------------------
|
|
163
189
|
// Handlers
|
|
164
190
|
// ---------------------------------------------------------------------------
|
|
@@ -174,10 +200,14 @@ function handleListConnections({ queryParams = {} }: RouteHandlerArgs) {
|
|
|
174
200
|
|
|
175
201
|
function handleGetConnection({ pathParams = {} }: RouteHandlerArgs) {
|
|
176
202
|
const { name } = pathParams;
|
|
177
|
-
if (!name)
|
|
203
|
+
if (!name) {
|
|
204
|
+
throw new BadRequestError("name is required");
|
|
205
|
+
}
|
|
178
206
|
|
|
179
207
|
const conn = getConnection(getDb(), name);
|
|
180
|
-
if (!conn)
|
|
208
|
+
if (!conn) {
|
|
209
|
+
throw new NotFoundError(`Connection "${name}" not found.`);
|
|
210
|
+
}
|
|
181
211
|
|
|
182
212
|
return conn;
|
|
183
213
|
}
|
|
@@ -197,7 +227,9 @@ async function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
|
|
|
197
227
|
`Invalid provider "${String(provider)}". Valid: ${VALID_CONNECTION_PROVIDERS.join(", ")}`,
|
|
198
228
|
);
|
|
199
229
|
}
|
|
200
|
-
const authResult = AuthSchema.safeParse(
|
|
230
|
+
const authResult = AuthSchema.safeParse(
|
|
231
|
+
auth ?? deriveConnectionAuth(providerResult.data, body.credential),
|
|
232
|
+
);
|
|
201
233
|
if (!authResult.success) {
|
|
202
234
|
throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
|
|
203
235
|
}
|
|
@@ -239,12 +271,12 @@ async function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
|
|
|
239
271
|
}
|
|
240
272
|
if (result.error.code === "base_url_required") {
|
|
241
273
|
throw new BadRequestError(
|
|
242
|
-
"base_url is required for openai-compatible
|
|
274
|
+
"base_url is required for openai-compatible providers.",
|
|
243
275
|
);
|
|
244
276
|
}
|
|
245
277
|
if (result.error.code === "models_required") {
|
|
246
278
|
throw new BadRequestError(
|
|
247
|
-
"At least one model is required for openai-compatible
|
|
279
|
+
"At least one model is required for openai-compatible providers.",
|
|
248
280
|
);
|
|
249
281
|
}
|
|
250
282
|
throw new BadRequestError("Invalid auth configuration.");
|
|
@@ -258,12 +290,36 @@ async function handleUpdateConnection({
|
|
|
258
290
|
body = {},
|
|
259
291
|
}: RouteHandlerArgs) {
|
|
260
292
|
const { name } = pathParams;
|
|
261
|
-
if (!name)
|
|
293
|
+
if (!name) {
|
|
294
|
+
throw new BadRequestError("name is required");
|
|
295
|
+
}
|
|
262
296
|
|
|
263
297
|
const existing = getConnection(getDb(), name);
|
|
264
|
-
if (!existing)
|
|
298
|
+
if (!existing) {
|
|
299
|
+
throw new NotFoundError(`Connection "${name}" not found.`);
|
|
300
|
+
}
|
|
265
301
|
|
|
266
|
-
|
|
302
|
+
// `auth` is optional: an explicit object wins; a bare `credential` rotates
|
|
303
|
+
// the key by re-deriving from the provider; omitting both leaves the stored
|
|
304
|
+
// auth untouched (so label-only edits never disturb e.g. an
|
|
305
|
+
// oauth_subscription connection).
|
|
306
|
+
if (
|
|
307
|
+
body.auth === undefined &&
|
|
308
|
+
body.credential !== undefined &&
|
|
309
|
+
existing.auth.type === "oauth_subscription"
|
|
310
|
+
) {
|
|
311
|
+
// Derivation would silently flip the auth type to api_key. Rotating a
|
|
312
|
+
// subscription token goes through the ChatGPT sign-in routes; switching
|
|
313
|
+
// to key auth requires an explicit `auth` object.
|
|
314
|
+
throw new BadRequestError(
|
|
315
|
+
`Connection "${name}" uses subscription auth, which "credential" cannot rotate. Re-run the ChatGPT sign-in flow, or pass an explicit "auth" object to switch auth types.`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
const auth =
|
|
319
|
+
body.auth ??
|
|
320
|
+
(body.credential !== undefined
|
|
321
|
+
? deriveConnectionAuth(existing.provider, body.credential)
|
|
322
|
+
: existing.auth);
|
|
267
323
|
const authResult = AuthSchema.safeParse(auth);
|
|
268
324
|
if (!authResult.success) {
|
|
269
325
|
throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
|
|
@@ -307,12 +363,12 @@ async function handleUpdateConnection({
|
|
|
307
363
|
}
|
|
308
364
|
if (result.error.code === "base_url_required") {
|
|
309
365
|
throw new BadRequestError(
|
|
310
|
-
"base_url is required for openai-compatible
|
|
366
|
+
"base_url is required for openai-compatible providers.",
|
|
311
367
|
);
|
|
312
368
|
}
|
|
313
369
|
if (result.error.code === "models_required") {
|
|
314
370
|
throw new BadRequestError(
|
|
315
|
-
"At least one model is required for openai-compatible
|
|
371
|
+
"At least one model is required for openai-compatible providers.",
|
|
316
372
|
);
|
|
317
373
|
}
|
|
318
374
|
throw new BadRequestError("Invalid auth configuration.");
|
|
@@ -323,9 +379,11 @@ async function handleUpdateConnection({
|
|
|
323
379
|
|
|
324
380
|
function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
|
|
325
381
|
const { name } = pathParams;
|
|
326
|
-
if (!name)
|
|
382
|
+
if (!name) {
|
|
383
|
+
throw new BadRequestError("name is required");
|
|
384
|
+
}
|
|
327
385
|
|
|
328
|
-
// Existence check first so a stale `
|
|
386
|
+
// Existence check first so a stale profile `provider_connection`
|
|
329
387
|
// reference to a missing connection returns 404 (not 409).
|
|
330
388
|
const existing = getConnection(getDb(), name);
|
|
331
389
|
if (!existing) {
|
|
@@ -345,17 +403,6 @@ function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
|
|
|
345
403
|
|
|
346
404
|
const config = getConfigReadOnly();
|
|
347
405
|
|
|
348
|
-
// llm.default carries provider_connection (LLMConfigBase).
|
|
349
|
-
if (
|
|
350
|
-
(config.llm?.default as Record<string, unknown> | undefined)
|
|
351
|
-
?.provider_connection === name
|
|
352
|
-
) {
|
|
353
|
-
throw new ConflictError(
|
|
354
|
-
`Connection "${name}" is referenced by llm.default. Update llm.default.provider_connection before deleting.`,
|
|
355
|
-
{ referencedBy: ["llm.default"] },
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
406
|
// llm.defaultProvider: guards both the resolved connection name (explicit
|
|
360
407
|
// `connectionName` or the `<provider>-personal` convention) and the case
|
|
361
408
|
// where the convention name is dangling but this is the last remaining
|
|
@@ -471,12 +518,13 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
471
518
|
},
|
|
472
519
|
summary: "Create a provider connection",
|
|
473
520
|
description:
|
|
474
|
-
"Create a new named provider connection. Fails with 409 if a connection with this name already exists.",
|
|
521
|
+
"Create a new named provider connection. When auth is omitted it is derived from the provider (keyless providers get none, vellum gets platform, everything else needs credential for api_key auth). Fails with 409 if a connection with this name already exists.",
|
|
475
522
|
tags: ["inference"],
|
|
476
523
|
requestBody: z.object({
|
|
477
524
|
name: z.string().min(1),
|
|
478
525
|
provider: ConnectionProviderSchema,
|
|
479
|
-
auth: AuthSchema,
|
|
526
|
+
auth: AuthSchema.optional(),
|
|
527
|
+
credential: z.string().min(1).optional(),
|
|
480
528
|
label: z.string().min(1).optional(),
|
|
481
529
|
base_url: z.string().url().nullable().optional(),
|
|
482
530
|
models: z.array(ConnectionModelSchema).nullable().optional(),
|
|
@@ -499,11 +547,12 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
499
547
|
},
|
|
500
548
|
summary: "Update a provider connection",
|
|
501
549
|
description:
|
|
502
|
-
"Update an existing connection. Cannot rename or change the provider. For the Vellum-managed connection (vellum) the auth is locked to platform; label remains editable.",
|
|
550
|
+
"Update an existing connection. Cannot rename or change the provider. Omitting auth keeps the stored auth; passing credential alone rotates the key via provider-derived api_key auth. For the Vellum-managed connection (vellum) the auth is locked to platform; label remains editable.",
|
|
503
551
|
tags: ["inference"],
|
|
504
552
|
pathParams: [{ name: "name", description: "Connection name" }],
|
|
505
553
|
requestBody: z.object({
|
|
506
|
-
auth: AuthSchema,
|
|
554
|
+
auth: AuthSchema.optional(),
|
|
555
|
+
credential: z.string().min(1).optional(),
|
|
507
556
|
label: z.string().min(1).nullable().optional(),
|
|
508
557
|
base_url: z.string().url().nullable().optional(),
|
|
509
558
|
models: z.array(ConnectionModelSchema).nullable().optional(),
|
|
@@ -53,7 +53,7 @@ async function handleInferenceSend({ body = {} }: RouteHandlerArgs) {
|
|
|
53
53
|
});
|
|
54
54
|
if (!provider) {
|
|
55
55
|
throw new BadRequestError(
|
|
56
|
-
"No LLM provider is configured.
|
|
56
|
+
"No LLM provider is configured. Connect a provider (assistant credentials) or set llm.defaultProvider to choose one.",
|
|
57
57
|
);
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { z } from "zod";
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { getRawShareAnalytics } from "../../platform/consent-cache.js";
|
|
20
20
|
import {
|
|
21
21
|
type AuthFallbackCount,
|
|
22
22
|
recordAuthFallbackCounts,
|
|
@@ -61,15 +61,16 @@ function handleRecordAuthFallback({ body }: RouteHandlerArgs) {
|
|
|
61
61
|
|
|
62
62
|
const recorded = recordAuthFallbackCounts(window_start, window_end, mapped);
|
|
63
63
|
if (recorded === 0) {
|
|
64
|
-
if (
|
|
65
|
-
// Consent is
|
|
66
|
-
// so the telemetry DB is unavailable. Fail so
|
|
67
|
-
// merges the batch back and retries next flush
|
|
64
|
+
if (getRawShareAnalytics() !== false) {
|
|
65
|
+
// Consent is not a confirmed opt-out but nothing was recorded (the
|
|
66
|
+
// body guarantees counts), so the telemetry DB is unavailable. Fail so
|
|
67
|
+
// the gateway's reporter merges the batch back and retries next flush
|
|
68
|
+
// instead of losing it.
|
|
68
69
|
throw new ServiceUnavailableError(
|
|
69
70
|
"Telemetry database unavailable; retry the auth-fallback batch",
|
|
70
71
|
);
|
|
71
72
|
}
|
|
72
|
-
// share_analytics
|
|
73
|
+
// Confirmed share_analytics opt-out: drop the counts to honor it.
|
|
73
74
|
return { skipped: true };
|
|
74
75
|
}
|
|
75
76
|
log.debug({ recorded }, "Recorded auth-fallback counts");
|
|
@@ -91,6 +91,7 @@ import {
|
|
|
91
91
|
import { getPlatformBaseUrl } from "../../config/env.js";
|
|
92
92
|
import { isPluginDisabled } from "../../plugins/disabled-state.js";
|
|
93
93
|
import { ensurePluginApiShim } from "../../plugins/ensure-plugin-api-shim.js";
|
|
94
|
+
import { reconcilePluginSourcesNow } from "../../plugins/mtime-cache.js";
|
|
94
95
|
import { getLocalCategorySlugs } from "../../skills/categories-cache.js";
|
|
95
96
|
import { getLogger } from "../../util/logger.js";
|
|
96
97
|
import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
@@ -1092,7 +1093,15 @@ async function handleUninstallPlugin({
|
|
|
1092
1093
|
await ensurePluginApiShim().catch(() => {});
|
|
1093
1094
|
|
|
1094
1095
|
try {
|
|
1096
|
+
// `uninstallPlugin` runs the plugin's `shutdown` hook (while its files are
|
|
1097
|
+
// still present) and then removes the directory — both in this daemon
|
|
1098
|
+
// process. Following it with a reconcile drops the plugin's now-absent
|
|
1099
|
+
// tools and hooks from the in-memory caches immediately (the `shutdown`
|
|
1100
|
+
// already ran, so the reconcile's deactivation does not run it again),
|
|
1101
|
+
// rather than leaving that to the background source watcher. Symmetric to
|
|
1102
|
+
// the install route, which reconciles to bring a new plugin up.
|
|
1095
1103
|
const result = await uninstallPlugin({ name: rawName });
|
|
1104
|
+
await reconcilePluginSourcesNow();
|
|
1096
1105
|
publishPluginsChanged(getOriginClientId(headers));
|
|
1097
1106
|
return { name: result.name, target: result.target };
|
|
1098
1107
|
} catch (err) {
|
|
@@ -1228,6 +1237,12 @@ async function handleInstallPlugin({ body = {}, headers }: RouteHandlerArgs) {
|
|
|
1228
1237
|
{ fetch: globalThis.fetch.bind(globalThis) },
|
|
1229
1238
|
);
|
|
1230
1239
|
}
|
|
1240
|
+
// Bring the freshly materialized plugin up in-process right now — register
|
|
1241
|
+
// its tools and run its `init` hook — instead of waiting for the resource
|
|
1242
|
+
// monitor to republish the sentinel and a later turn to apply it. This is
|
|
1243
|
+
// what makes `init` fire as part of the install rather than at the next
|
|
1244
|
+
// daemon boot. Never throws; a failure is contained and logged.
|
|
1245
|
+
await reconcilePluginSourcesNow();
|
|
1231
1246
|
publishPluginsChanged(getOriginClientId(headers));
|
|
1232
1247
|
return {
|
|
1233
1248
|
ok: true as const,
|
|
@@ -1403,6 +1418,21 @@ async function handleUpgradePlugin({
|
|
|
1403
1418
|
{ name: rawName, dryRun, strategy },
|
|
1404
1419
|
{ fetch: globalThis.fetch.bind(globalThis) },
|
|
1405
1420
|
);
|
|
1421
|
+
// An upgrade that actually moved files (outcome `upgraded`) rewrites the
|
|
1422
|
+
// plugin's on-disk source; bring the change up in-process right now — run
|
|
1423
|
+
// the old version's `shutdown` and the new version's `init` via the
|
|
1424
|
+
// reconcile — instead of waiting for the resource monitor to republish the
|
|
1425
|
+
// sentinel and a later turn to apply it. This is what makes the lifecycle
|
|
1426
|
+
// fire as part of the upgrade rather than at the next daemon boot,
|
|
1427
|
+
// symmetric to the install and uninstall routes. A no-op or dry run leaves
|
|
1428
|
+
// the tree unchanged, so there is nothing to reconcile. Ensure the
|
|
1429
|
+
// workspace `@vellumai/plugin-api` shim first (as uninstall does) so a hook
|
|
1430
|
+
// that imports the package resolves even inside the daemon boot window.
|
|
1431
|
+
// Never throws; a failure is contained and logged.
|
|
1432
|
+
if (result.outcome === "upgraded") {
|
|
1433
|
+
await ensurePluginApiShim().catch(() => {});
|
|
1434
|
+
await reconcilePluginSourcesNow();
|
|
1435
|
+
}
|
|
1406
1436
|
publishPluginsChanged(getOriginClientId(headers));
|
|
1407
1437
|
return {
|
|
1408
1438
|
name: result.name,
|
|
@@ -1435,9 +1465,10 @@ async function handleUpgradePlugin({
|
|
|
1435
1465
|
if (err instanceof PluginMergeBaselineError) {
|
|
1436
1466
|
throw new ConflictError(err.message);
|
|
1437
1467
|
}
|
|
1438
|
-
// The install
|
|
1439
|
-
// permanent state the caller cannot resolve by retrying.
|
|
1440
|
-
// request as well-formed but not actionable in the current
|
|
1468
|
+
// The install has neither a marketplace entry nor a recorded GitHub source
|
|
1469
|
+
// to advance to — a permanent state the caller cannot resolve by retrying.
|
|
1470
|
+
// 409 marks the request as well-formed but not actionable in the current
|
|
1471
|
+
// state.
|
|
1441
1472
|
if (err instanceof PluginNotUpgradableError) {
|
|
1442
1473
|
throw new ConflictError(err.message);
|
|
1443
1474
|
}
|
|
@@ -1866,9 +1897,9 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
1866
1897
|
requiredScopes: ["settings.write"],
|
|
1867
1898
|
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
1868
1899
|
},
|
|
1869
|
-
summary: "Upgrade a plugin to
|
|
1900
|
+
summary: "Upgrade a plugin to its source's current revision",
|
|
1870
1901
|
description:
|
|
1871
|
-
'Move an installed plugin to
|
|
1902
|
+
'Move an installed plugin to its source\'s current revision, re-materializing it under `<workspaceDir>/plugins/<name>/`. A marketplace plugin advances to the curated pin; a plugin installed directly from a GitHub URL (untrusted, not in the marketplace) advances to whatever its recorded ref now resolves to — a pinned SHA is immutable (a no-op), a branch/tag/HEAD moves as upstream does — re-materialized verbatim with no curated adapter overlay, exactly as the original untrusted install was. The target ref is never taken from the request (no caller-supplied ref), mirroring `plugins install`\'s curation boundary. A no-op (`outcome: "already-up-to-date"`) when the installed commit already equals the target; pass `dryRun` to preview the move (`outcome: "would-upgrade"`) without touching the install. Installs lacking provenance are re-pinned to the current SHA. The upgraded code is picked up live on the next read (no restart required). `strategy` controls how local edits are reconciled: `overwrite` (default) discards them and re-installs the target wholesale; `ours`/`theirs`/`assistant` perform a three-way merge against the re-materialized install commit, carrying non-conflicting edits from both sides forward and resolving conflicting hunks toward the local edit (`ours`) or the target (`theirs`), or writing git conflict markers into the file and reporting them in `conflicts`/`binaryConflicts` for the assistant to resolve (`assistant`). A merge strategy whose install-time baseline cannot be reconstructed returns 409. Mirrors the CLI\'s `assistant plugins upgrade <name> [--strategy <s>]`.',
|
|
1872
1903
|
tags: ["plugins"],
|
|
1873
1904
|
pathParams: [
|
|
1874
1905
|
{
|
|
@@ -1891,7 +1922,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
1891
1922
|
},
|
|
1892
1923
|
"409": {
|
|
1893
1924
|
description:
|
|
1894
|
-
"The install
|
|
1925
|
+
"The install has neither a marketplace entry nor a recorded GitHub source to advance to, or a merge strategy was requested whose install-time baseline cannot be reconstructed.",
|
|
1895
1926
|
},
|
|
1896
1927
|
"503": {
|
|
1897
1928
|
description:
|