@vellumai/assistant 0.11.6 → 0.11.7-staging.1
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 +31 -19
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +3 -3
- package/docs/flux-turn-detection-spike.md +8 -7
- package/docs/runbook-trusted-contacts.md +14 -0
- package/docs/stt-provider-onboarding.md +8 -4
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
- package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
- package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
- package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
- package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
- package/node_modules/@vellumai/environments/src/shell.ts +65 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
- package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
- package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/openapi.yaml +419 -55
- package/package.json +4 -1
- package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
- package/src/__tests__/approval-card-edit.test.ts +153 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
- package/src/__tests__/available-skills.test.ts +54 -0
- package/src/__tests__/avatar-identity-sync.test.ts +5 -1
- package/src/__tests__/catalog-install-normalize.test.ts +9 -1
- package/src/__tests__/channel-policy.test.ts +1 -3
- package/src/__tests__/channel-readiness-discord.test.ts +9 -70
- package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
- package/src/__tests__/config-loader-backfill.test.ts +69 -0
- package/src/__tests__/config-schema.test.ts +2 -3
- package/src/__tests__/conversation-agent-loop.test.ts +238 -0
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +44 -0
- package/src/__tests__/conversation-evictor.test.ts +19 -0
- package/src/__tests__/conversation-fork-referential.test.ts +73 -63
- package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
- package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
- package/src/__tests__/conversation-slash.test.ts +9 -0
- package/src/__tests__/conversation-title-service.test.ts +97 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
- package/src/__tests__/credential-broker-server-use.test.ts +0 -218
- package/src/__tests__/credential-broker.test.ts +43 -420
- package/src/__tests__/credential-routes.test.ts +17 -1
- package/src/__tests__/credential-security-invariants.test.ts +8 -9
- package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
- package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
- package/src/__tests__/discord-channel-config.test.ts +237 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
- package/src/__tests__/fallback-breaker.test.ts +1335 -0
- package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
- package/src/__tests__/image-conversion.test.ts +38 -4
- package/src/__tests__/install-skill-routing.test.ts +25 -0
- package/src/__tests__/llm-resolver.test.ts +74 -0
- package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
- package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
- package/src/__tests__/managed-profile-guard.test.ts +28 -0
- package/src/__tests__/managed-speech-defaults.test.ts +196 -1
- package/src/__tests__/mcp-cli.test.ts +23 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
- package/src/__tests__/media-stream-stt-session.test.ts +2 -0
- package/src/__tests__/notification-decision-fallback.test.ts +106 -0
- package/src/__tests__/notification-discord-adapter.test.ts +162 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
- package/src/__tests__/persist-unsendable-image.test.ts +3 -3
- package/src/__tests__/platform.test.ts +19 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/provider-usage-tracking.test.ts +56 -0
- package/src/__tests__/require-fresh-approval.test.ts +5 -2
- package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
- package/src/__tests__/schedule-retry.test.ts +183 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
- package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
- package/src/__tests__/skill-load-tool.test.ts +34 -0
- package/src/__tests__/skill-tool-factory.test.ts +6 -1
- package/src/__tests__/skill-tool-manifest.test.ts +20 -0
- package/src/__tests__/skills.test.ts +25 -0
- package/src/__tests__/subagent-manager-notify.test.ts +38 -0
- package/src/__tests__/terminal-tools.test.ts +2 -0
- package/src/__tests__/tool-approval-handler.test.ts +146 -2
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
- package/src/__tests__/tool-audit.test.ts +111 -40
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
- package/src/__tests__/ui-shape-teaching.test.ts +1 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
- package/src/__tests__/voice-config-update.test.ts +43 -0
- package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
- package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
- package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
- package/src/agent/loop.ts +31 -2
- package/src/approvals/guardian-card-withdrawal.ts +22 -0
- package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
- package/src/avatar/avatar-manifest.ts +48 -100
- package/src/avatar/avatar-store.ts +7 -7
- package/src/avatar/ensure-raster.ts +146 -0
- package/src/avatar/resvg-lazy.test.ts +0 -1
- package/src/avatar/traits-png-sync.ts +9 -10
- package/src/background-wake/wake-intent-hooks.test.ts +0 -1
- package/src/backup/__tests__/paths.test.ts +6 -2
- package/src/backup/paths.ts +6 -4
- package/src/bundler/app-compiler.ts +2 -2
- package/src/bundler/compiler-tools.test.ts +36 -0
- package/src/bundler/compiler-tools.ts +54 -18
- package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +20 -0
- package/src/calls/media-stream-stt-session.ts +2 -1
- package/src/calls/telephony-synthesis-language.ts +13 -5
- package/src/calls/voice-session-bridge.ts +6 -4
- package/src/channels/config.ts +23 -12
- package/src/cli/AGENTS.md +3 -1
- package/src/cli/commands/backup.help.ts +10 -13
- package/src/cli/commands/contacts.help.ts +16 -4
- package/src/cli/commands/contacts.ts +5 -1
- package/src/cli/commands/credentials.help.ts +1 -1
- package/src/cli/commands/inference-providers.ts +18 -53
- package/src/cli/commands/mcp.help.ts +6 -5
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/memory/memory-retrospective.ts +1 -0
- package/src/cli/commands/stt.help.ts +2 -2
- package/src/config/__tests__/backup-schema.test.ts +1 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
- package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
- package/src/config/bundled-skills/settings/TOOLS.json +11 -5
- package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
- package/src/config/default-profile-catalog.ts +195 -10
- package/src/config/default-profile-names.ts +41 -0
- package/src/config/feature-flag-registry.json +18 -2
- package/src/config/inference-profile-validation.ts +11 -2
- package/src/config/llm-resolver.ts +25 -0
- package/src/config/loader.ts +3 -0
- package/src/config/managed-speech-defaults.ts +160 -10
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
- package/src/config/schemas/__tests__/stt.test.ts +94 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/channels.ts +32 -0
- package/src/config/schemas/live-voice.ts +13 -4
- package/src/config/schemas/llm.ts +259 -11
- package/src/config/schemas/mcp.ts +10 -2
- package/src/config/schemas/memory-retrospective.ts +1 -19
- package/src/config/schemas/services.ts +24 -0
- package/src/config/schemas/stt.ts +128 -5
- package/src/config/seed-inference-profiles.ts +32 -3
- package/src/config/skill-state.ts +5 -0
- package/src/config/skills.ts +21 -0
- package/src/contacts/notify-contacts-changed.ts +5 -2
- package/src/context/compactor.ts +74 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
- package/src/daemon/config-watcher.ts +2 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
- package/src/daemon/conversation-agent-loop.ts +86 -10
- package/src/daemon/conversation-error.ts +25 -0
- package/src/daemon/conversation-evictor.ts +2 -4
- package/src/daemon/conversation-runtime-assembly.ts +19 -7
- package/src/daemon/conversation-skill-tools.ts +5 -1
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-store.ts +8 -1
- package/src/daemon/conversation-tool-setup.ts +37 -0
- package/src/daemon/conversation.ts +0 -4
- package/src/daemon/handlers/channel-config-result.ts +30 -0
- package/src/daemon/handlers/config-discord-channel.ts +301 -0
- package/src/daemon/handlers/config-ingress.ts +9 -0
- package/src/daemon/handlers/config-slack-channel.ts +13 -15
- package/src/daemon/handlers/config-telegram.ts +2 -5
- package/src/daemon/handlers/conversations.ts +0 -2
- package/src/daemon/handlers/skills.ts +25 -7
- package/src/daemon/lifecycle.ts +3 -1
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/tool-setup-types.ts +5 -3
- package/src/daemon/trust-context-types.test.ts +33 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
- package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
- package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
- package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
- package/src/live-voice/__tests__/protocol.test.ts +224 -0
- package/src/live-voice/live-voice-credential-preflight.ts +5 -1
- package/src/live-voice/live-voice-session-manager.ts +39 -2
- package/src/live-voice/live-voice-session.ts +213 -8
- package/src/live-voice/protocol.ts +187 -1
- package/src/messaging/providers/discord/api.ts +2 -1
- package/src/messaging/providers/discord/withdraw.ts +62 -0
- package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
- package/src/monitoring/__tests__/process-memory.test.ts +34 -1
- package/src/monitoring/db-integrity-sample.ts +1 -0
- package/src/monitoring/file-descriptors.ts +39 -6
- package/src/monitoring/process-memory.ts +37 -5
- package/src/monitoring/resource-sampler.ts +25 -8
- package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
- package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
- package/src/notifications/__tests__/connected-channels.test.ts +12 -0
- package/src/notifications/__tests__/copy-composer.test.ts +83 -0
- package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
- package/src/notifications/activity-failed-dedupe.ts +95 -0
- package/src/notifications/adapters/discord.ts +132 -0
- package/src/notifications/adapters/shared.ts +16 -0
- package/src/notifications/adapters/slack.ts +6 -1
- package/src/notifications/adapters/telegram.ts +5 -8
- package/src/notifications/approval-card-data.ts +94 -1
- package/src/notifications/background-failure-signal.ts +116 -0
- package/src/notifications/broadcaster.ts +3 -9
- package/src/notifications/copy-composer.ts +41 -8
- package/src/notifications/decision-engine.ts +55 -0
- package/src/notifications/destination-resolver.ts +45 -5
- package/src/notifications/emit-signal.ts +19 -5
- package/src/notifications/guardian-question-mode.ts +57 -0
- package/src/notifications/types.ts +2 -8
- package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
- package/src/oauth/seed-providers.ts +261 -0
- package/src/permissions/checker.test.ts +21 -0
- package/src/permissions/checker.ts +6 -0
- package/src/permissions/gateway-threshold-reader.test.ts +184 -0
- package/src/permissions/gateway-threshold-reader.ts +144 -10
- package/src/permissions/prompter.ts +0 -6
- package/src/permissions/types.ts +5 -4
- package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
- package/src/persistence/bulk-write-gate.ts +9 -10
- package/src/persistence/conversation-crud.ts +85 -133
- package/src/persistence/conversation-title-service.ts +43 -29
- package/src/persistence/embeddings/embedding-local.ts +14 -85
- package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
- package/src/persistence/lifecycle-events-store.test.ts +50 -0
- package/src/persistence/lifecycle-events-store.ts +75 -5
- package/src/platform/platform-patch-queue.test.ts +368 -0
- package/src/platform/platform-patch-queue.ts +190 -0
- package/src/platform/sync-avatar.test.ts +527 -0
- package/src/platform/sync-avatar.ts +209 -0
- package/src/platform/sync-identity.test.ts +75 -0
- package/src/platform/sync-identity.ts +17 -110
- package/src/plugin-api/model-profiles.test.ts +2 -2
- package/src/plugin-api/model-profiles.ts +7 -7
- package/src/plugin-api/transcription-session.ts +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +4 -0
- package/src/plugins/defaults/image-recovery/recover.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
- package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
- package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +7 -0
- package/src/providers/__tests__/retry-callsite.test.ts +55 -0
- package/src/providers/connection-resolution.ts +2 -17
- package/src/providers/fallback-breaker.ts +389 -0
- package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
- package/src/providers/inference/adapter-factory.ts +231 -3
- package/src/providers/inference/auth.ts +1 -0
- package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
- package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
- package/src/providers/inference/resolve-auth.ts +1 -1
- package/src/providers/media-resolve.ts +2 -3
- package/src/providers/model-catalog.ts +75 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
- package/src/providers/openai/api-error-normalization.ts +13 -0
- package/src/providers/openai/chat-completions-provider.ts +84 -0
- package/src/providers/opencode/client.test.ts +150 -0
- package/src/providers/opencode/client.ts +61 -0
- package/src/providers/provider-resolvability.ts +20 -0
- package/src/providers/retry.ts +956 -68
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
- package/src/providers/speech-to-text/provider-catalog.ts +191 -3
- package/src/providers/speech-to-text/resolve.ts +132 -24
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
- package/src/providers/types.ts +39 -0
- package/src/providers/usage-tracking.ts +12 -1
- package/src/runtime/AGENTS.md +1 -2
- package/src/runtime/__tests__/agent-wake.test.ts +105 -3
- package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
- package/src/runtime/agent-wake.ts +44 -5
- package/src/runtime/background-job-runner.ts +68 -50
- package/src/runtime/channel-readiness-service.ts +0 -39
- package/src/runtime/http-server.ts +12 -4
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
- package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
- package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
- package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
- package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
- package/src/runtime/routes/approval-routes.ts +0 -12
- package/src/runtime/routes/avatar-routes.ts +17 -30
- package/src/runtime/routes/backup-routes.ts +2 -2
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
- package/src/runtime/routes/contact-prompt-routes.ts +65 -6
- package/src/runtime/routes/contact-routes.ts +20 -15
- package/src/runtime/routes/conversation-query-routes.ts +98 -10
- package/src/runtime/routes/conversation-routes.ts +2 -2
- package/src/runtime/routes/credential-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +12 -9
- package/src/runtime/routes/inbound-message-handler.ts +21 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
- package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
- package/src/runtime/routes/inference-profiles-routes.ts +12 -7
- package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
- package/src/runtime/routes/inference-send-routes.ts +2 -2
- package/src/runtime/routes/ingress-status-routes.ts +2 -2
- package/src/runtime/routes/integrations/discord.ts +119 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
- package/src/runtime/routes/mcp-auth-routes.ts +12 -6
- package/src/runtime/routes/oauth-pending-flows.ts +136 -0
- package/src/runtime/routes/secret-routes.ts +7 -0
- package/src/runtime/routes/stt-routes.ts +38 -6
- package/src/runtime/routes/wake-conversation-routes.ts +8 -1
- package/src/runtime/routes/watch-routes.ts +9 -5
- package/src/runtime/sync/resource-sync-events.ts +2 -0
- package/src/runtime/trust-verdict-consumer.ts +3 -3
- package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
- package/src/schedule/retry-policy.ts +8 -8
- package/src/schedule/run-script.ts +14 -23
- package/src/schedule/schedule-recovery.ts +0 -1
- package/src/schedule/scheduler.ts +89 -51
- package/src/schedule/worker.ts +57 -0
- package/src/security/__tests__/openai-device-auth.test.ts +355 -0
- package/src/security/oauth2.ts +7 -0
- package/src/security/openai-device-auth.ts +364 -0
- package/src/skills/available-skills.ts +10 -1
- package/src/skills/catalog-install.ts +19 -1
- package/src/skills/inline-command-runner.ts +6 -2
- package/src/skills/platform-compatibility.test.ts +49 -0
- package/src/skills/platform-compatibility.ts +65 -0
- package/src/skills/tool-manifest.ts +20 -0
- package/src/stt/__tests__/roles.test.ts +152 -0
- package/src/stt/daemon-batch-transcriber.ts +4 -1
- package/src/stt/roles.ts +180 -0
- package/src/stt/stt-stream-session.ts +3 -2
- package/src/stt/types.ts +33 -3
- package/src/subagent/manager.ts +12 -0
- package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
- package/src/telemetry/live-voice-funnel.ts +12 -2
- package/src/telemetry/telemetry-wire.generated.ts +6 -0
- package/src/telemetry/tool-audit.ts +83 -30
- package/src/telemetry/tool-executed-events-store.test.ts +0 -1
- package/src/telemetry/turn-outcome.ts +75 -4
- package/src/tools/client-os.ts +115 -0
- package/src/tools/computer-use/definitions.ts +11 -7
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
- package/src/tools/credentials/broker-types.ts +0 -71
- package/src/tools/credentials/broker.ts +6 -201
- package/src/tools/executor.ts +2 -8
- package/src/tools/host-shell.test.ts +75 -0
- package/src/tools/host-shell.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +15 -44
- package/src/tools/mcp/mcp-tool-factory.ts +44 -9
- package/src/tools/permission-checker.ts +73 -21
- package/src/tools/policy-context.ts +1 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/find-similar-skills.ts +10 -2
- package/src/tools/skills/load.ts +41 -6
- package/src/tools/skills/sandbox-runner.ts +8 -16
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
- package/src/tools/terminal/safe-env.ts +39 -9
- package/src/tools/terminal/shell.ts +12 -31
- package/src/tools/tool-approval-handler.ts +64 -10
- package/src/tools/tool-types.ts +10 -0
- package/src/tools/types.ts +19 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
- package/src/tools/workflows/manage-workflows.ts +2 -2
- package/src/tools/workflows/run-workflow.test.ts +2 -1
- package/src/util/__tests__/file-use.test.ts +61 -0
- package/src/util/__tests__/process-table.test.ts +66 -0
- package/src/util/__tests__/process-tree.test.ts +46 -0
- package/src/util/errors.ts +1 -0
- package/src/util/file-use.ts +91 -0
- package/src/util/host-process.test.ts +17 -0
- package/src/util/host-process.ts +75 -0
- package/src/util/image-conversion.ts +39 -7
- package/src/util/platform.ts +32 -13
- package/src/util/process-table.ts +249 -0
- package/src/util/process-tree.ts +85 -129
- package/src/util/provider-error-patterns.ts +20 -0
- package/src/util/spawn.ts +9 -2
- package/src/workspace/git-service.ts +4 -14
- package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
- package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
- package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
- package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/permission-types.test.ts +0 -17
- package/src/channels/gateway-discord-admission.ts +0 -32
- package/src/persistence/fork-message-copy.ts +0 -239
- package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
- package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
- package/src/security/oauth2-device-code.ts +0 -312
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
import { AssistantConfigSchema } from "../../config/schema.js";
|
|
49
49
|
import { getSchemaAtPath } from "../../config/schema-utils.js";
|
|
50
50
|
import {
|
|
51
|
+
collectFallbackProfileIssues,
|
|
51
52
|
DefaultProviderSchema,
|
|
52
53
|
LLMConfigBase,
|
|
53
54
|
LLMConfigFragment,
|
|
@@ -156,6 +157,7 @@ type LlmContextRouteResult = Omit<LlmContextNormalizationResult, "summary"> & {
|
|
|
156
157
|
import {
|
|
157
158
|
CODE_OWNED_PROFILE_NAMES,
|
|
158
159
|
getEffectiveProfilesForProvider,
|
|
160
|
+
getUserSelectableProfilesForProvider,
|
|
159
161
|
INVARIANT_PROFILE_NAMES,
|
|
160
162
|
MANAGED_PROFILE_NAMES,
|
|
161
163
|
resolveDefaultProfileForProvider,
|
|
@@ -241,6 +243,13 @@ function replaceInferenceProfileConfig(
|
|
|
241
243
|
for (const key of INFERENCE_PROFILE_UI_KEYS) {
|
|
242
244
|
delete nextProfile[key];
|
|
243
245
|
}
|
|
246
|
+
// Converting a profile to a mix is an explicit replacement: a mix carries
|
|
247
|
+
// no model route of its own to fall back from, so an existing
|
|
248
|
+
// `fallbackProfile` pointer is dropped rather than preserved alongside
|
|
249
|
+
// `mix` (a combination `LLMSchema` rejects on the next full reload).
|
|
250
|
+
if (fragment.mix != null) {
|
|
251
|
+
delete nextProfile.fallbackProfile;
|
|
252
|
+
}
|
|
244
253
|
const fragmentTopLevel = { ...fragment };
|
|
245
254
|
delete fragmentTopLevel.contextWindow;
|
|
246
255
|
profiles[name] = { ...nextProfile, ...fragmentTopLevel };
|
|
@@ -891,7 +900,7 @@ function overlayEffectiveProfilesForWire(config: unknown): void {
|
|
|
891
900
|
if (!existingLlm) {
|
|
892
901
|
root.llm = llm;
|
|
893
902
|
}
|
|
894
|
-
llm.profiles =
|
|
903
|
+
llm.profiles = getUserSelectableProfilesForProvider(
|
|
895
904
|
readPlainObject(llm.profiles) as Record<string, ProfileEntry> | undefined,
|
|
896
905
|
getConfig().llm.defaultProvider ?? null,
|
|
897
906
|
);
|
|
@@ -1399,6 +1408,28 @@ function assertRoutableIdentityEntries(
|
|
|
1399
1408
|
}
|
|
1400
1409
|
}
|
|
1401
1410
|
|
|
1411
|
+
/**
|
|
1412
|
+
* Reject writes that would persist unsupported `fallbackProfile` metadata.
|
|
1413
|
+
* Automatic fallbacks are code-owned for managed default profiles. The write
|
|
1414
|
+
* paths save raw config without a full-schema parse, so this check keeps a
|
|
1415
|
+
* custom pointer from reaching disk. It runs unconditionally so an existing
|
|
1416
|
+
* hand-edited pointer blocks unrelated rewrites until the user clears it with
|
|
1417
|
+
* `null`.
|
|
1418
|
+
*/
|
|
1419
|
+
function assertCodeOwnedFallbackProfiles(raw: Record<string, unknown>): void {
|
|
1420
|
+
const llm = readPlainObject(raw.llm);
|
|
1421
|
+
const profiles = readPlainObject(llm?.profiles);
|
|
1422
|
+
// The sibling `llm.defaultProvider` decides whether the managed backups are
|
|
1423
|
+
// valid fallback targets: they resolve on the managed column alone.
|
|
1424
|
+
const issues = collectFallbackProfileIssues(
|
|
1425
|
+
profiles ?? undefined,
|
|
1426
|
+
llm?.defaultProvider,
|
|
1427
|
+
);
|
|
1428
|
+
if (issues.length > 0) {
|
|
1429
|
+
throw new BadRequestError(issues.map((issue) => issue.message).join(" "));
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1402
1433
|
export async function commitConfigWrite(
|
|
1403
1434
|
raw: Record<string, unknown>,
|
|
1404
1435
|
opLabel: string,
|
|
@@ -1411,6 +1442,7 @@ export async function commitConfigWrite(
|
|
|
1411
1442
|
completeChangedCustomProfiles(preWrite, raw);
|
|
1412
1443
|
assertInvariantProfilesPreserved(preWrite, raw);
|
|
1413
1444
|
assertRoutableIdentityEntries(preWrite, raw);
|
|
1445
|
+
assertCodeOwnedFallbackProfiles(raw);
|
|
1414
1446
|
|
|
1415
1447
|
// Suppress the file-watcher callback for the duration of the debounce
|
|
1416
1448
|
// window. Without this, the ConfigWatcher detects the config.json write
|
|
@@ -1714,6 +1746,18 @@ async function handleReplaceInferenceProfile({
|
|
|
1714
1746
|
const isManaged =
|
|
1715
1747
|
MANAGED_PROFILE_NAMES.has(name) &&
|
|
1716
1748
|
(existingProfile == null || existingProfile.source === "managed");
|
|
1749
|
+
if (CODE_OWNED_PROFILE_NAMES.has(name)) {
|
|
1750
|
+
// A code-owned profile resolves from the catalog whatever the workspace
|
|
1751
|
+
// holds, so even a status re-enable would persist a stub that never
|
|
1752
|
+
// governs anything. Reject the write rather than accept a silent no-op.
|
|
1753
|
+
// Checked ahead of the availability gate below so the code-owned backups,
|
|
1754
|
+
// which are always available yet are not `DEFAULT_PROFILE_KEYS` members,
|
|
1755
|
+
// report why the write is refused rather than claiming they do not exist.
|
|
1756
|
+
throw new BadRequestError(
|
|
1757
|
+
`Profile "${name}" is code-owned and cannot be edited. ` +
|
|
1758
|
+
`Duplicate it to a custom profile to customize.`,
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1717
1761
|
// A flag-gated managed name (`os-beta`) with no materialized entry cannot
|
|
1718
1762
|
// be patched: it only resolves while the flag reconcile has created its
|
|
1719
1763
|
// stub, so writing status here would persist an entry that fights the
|
|
@@ -1728,15 +1772,6 @@ async function handleReplaceInferenceProfile({
|
|
|
1728
1772
|
`Profile "${name}" is not currently available and cannot be edited.`,
|
|
1729
1773
|
);
|
|
1730
1774
|
}
|
|
1731
|
-
if (CODE_OWNED_PROFILE_NAMES.has(name)) {
|
|
1732
|
-
// A code-owned profile resolves from the catalog whatever the workspace
|
|
1733
|
-
// holds, so even a status re-enable would persist a stub that never
|
|
1734
|
-
// governs anything. Reject the write rather than accept a silent no-op.
|
|
1735
|
-
throw new BadRequestError(
|
|
1736
|
-
`Profile "${name}" is code-owned and cannot be edited. ` +
|
|
1737
|
-
`Duplicate it to a custom profile to customize.`,
|
|
1738
|
-
);
|
|
1739
|
-
}
|
|
1740
1775
|
if (isManaged) {
|
|
1741
1776
|
// Managed profiles are daemon-seeded and read-only — the commit guard
|
|
1742
1777
|
// (`assertInvariantProfilesPreserved`) rejects every write to them
|
|
@@ -1808,6 +1843,59 @@ async function handleReplaceInferenceProfile({
|
|
|
1808
1843
|
);
|
|
1809
1844
|
}
|
|
1810
1845
|
});
|
|
1846
|
+
// A fallback target must stay a standard profile: converting one to a
|
|
1847
|
+
// mix would leave another profile's fallbackProfile pointing at a mix,
|
|
1848
|
+
// which `LLMSchema.superRefine` rejects on the next full reparse.
|
|
1849
|
+
for (const [otherName, other] of Object.entries(existingProfiles)) {
|
|
1850
|
+
if (otherName !== name && other?.fallbackProfile === name) {
|
|
1851
|
+
throw new BadRequestError(
|
|
1852
|
+
`Profile "${otherName}" declares profile "${name}" as its fallbackProfile; a fallback must be a standard profile, so "${name}" cannot become a mix.`,
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
// `fallbackProfile` references another profile by name. As with `mix`, the
|
|
1859
|
+
// cross-profile integrity rules `LLMSchema.superRefine` enforces on
|
|
1860
|
+
// full-config load (target exists, no self-reference, target is not a mix,
|
|
1861
|
+
// single hop) must be checked here against the live profile set; otherwise
|
|
1862
|
+
// a dangling or chained pointer would persist and break the next full
|
|
1863
|
+
// config reparse.
|
|
1864
|
+
if (parsed.data.fallbackProfile != null) {
|
|
1865
|
+
const fallback = parsed.data.fallbackProfile;
|
|
1866
|
+
if (fallback === name) {
|
|
1867
|
+
throw new BadRequestError(
|
|
1868
|
+
`Profile "${name}" cannot declare itself as its fallbackProfile.`,
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
const { llm: parsedLlm } = getConfig();
|
|
1872
|
+
const existingProfiles = getEffectiveProfilesForProvider(
|
|
1873
|
+
parsedLlm.profiles,
|
|
1874
|
+
parsedLlm.defaultProvider ?? null,
|
|
1875
|
+
);
|
|
1876
|
+
const target = existingProfiles[fallback];
|
|
1877
|
+
if (target == null) {
|
|
1878
|
+
throw new BadRequestError(
|
|
1879
|
+
`Profile "${name}" declares fallbackProfile "${fallback}" which is not defined.`,
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1882
|
+
if (target.mix != null) {
|
|
1883
|
+
throw new BadRequestError(
|
|
1884
|
+
`Profile "${name}" declares fallbackProfile "${fallback}" which is a mix profile; a fallback must be a standard profile.`,
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
if (target.fallbackProfile != null) {
|
|
1888
|
+
throw new BadRequestError(
|
|
1889
|
+
`Profile "${name}" declares fallbackProfile "${fallback}" which sets its own fallbackProfile; fallback is a single hop, chains are not allowed.`,
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1892
|
+
for (const [otherName, other] of Object.entries(existingProfiles)) {
|
|
1893
|
+
if (otherName !== name && other?.fallbackProfile === name) {
|
|
1894
|
+
throw new BadRequestError(
|
|
1895
|
+
`Profile "${otherName}" already declares profile "${name}" as its fallbackProfile; fallback is a single hop, chains are not allowed.`,
|
|
1896
|
+
);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1811
1899
|
}
|
|
1812
1900
|
|
|
1813
1901
|
// When the UI sends provider but no provider_connection, derive the connection
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
supportsHostProxy,
|
|
40
40
|
} from "../../channels/types.js";
|
|
41
41
|
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
42
|
-
import {
|
|
42
|
+
import { getUserSelectableProfilesForProvider } from "../../config/default-profile-catalog.js";
|
|
43
43
|
import { isHttpAuthDisabled } from "../../config/env.js";
|
|
44
44
|
import { getConfig } from "../../config/loader.js";
|
|
45
45
|
import {
|
|
@@ -1559,7 +1559,7 @@ export async function handleSendMessage(
|
|
|
1559
1559
|
}
|
|
1560
1560
|
if (requestedInferenceProfile !== undefined) {
|
|
1561
1561
|
const { llm } = getConfig();
|
|
1562
|
-
const profiles =
|
|
1562
|
+
const profiles = getUserSelectableProfilesForProvider(
|
|
1563
1563
|
llm.profiles,
|
|
1564
1564
|
llm.defaultProvider ?? null,
|
|
1565
1565
|
);
|
|
@@ -72,7 +72,8 @@ function scrubSecret(secret: string | undefined): string {
|
|
|
72
72
|
if (secret.length <= 4) {
|
|
73
73
|
return "****";
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
// Keep the leading 4 characters so vendor prefixes stay visible.
|
|
76
|
+
return secret.slice(0, 4) + "****";
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
function safeGetConnectionByProvider(
|
|
@@ -45,7 +45,8 @@ export interface ApprovalInterceptionParams {
|
|
|
45
45
|
assistantId: string;
|
|
46
46
|
approvalCopyGenerator?: ApprovalCopyGenerator;
|
|
47
47
|
approvalConversationGenerator?: ApprovalConversationGenerator;
|
|
48
|
-
/**
|
|
48
|
+
/** Id of the channel message holding the approval buttons (Slack ts,
|
|
49
|
+
* Telegram message id), for editing the card after resolution. */
|
|
49
50
|
approvalMessageId?: string;
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -242,7 +243,7 @@ export async function handleApprovalInterception(
|
|
|
242
243
|
"Callback request ID does not match any pending interaction, ignoring stale button press",
|
|
243
244
|
);
|
|
244
245
|
|
|
245
|
-
// Edit the original
|
|
246
|
+
// Edit the original approval message to remove stale buttons
|
|
246
247
|
if (approvalMessageId) {
|
|
247
248
|
editStaleApprovalMessage({
|
|
248
249
|
replyCallbackUrl,
|
|
@@ -260,8 +261,10 @@ export async function handleApprovalInterception(
|
|
|
260
261
|
const result = await handleChannelDecision(conversationId, cbDecision);
|
|
261
262
|
|
|
262
263
|
if (result.applied) {
|
|
263
|
-
// Edit the original
|
|
264
|
-
//
|
|
264
|
+
// Edit the original approval message to show the decision and drop
|
|
265
|
+
// its action buttons. Routed by the callback URL through the channel
|
|
266
|
+
// edit capability; a transport without edit declines, so this is
|
|
267
|
+
// safe to attempt on every channel.
|
|
265
268
|
if (approvalMessageId) {
|
|
266
269
|
const decisionOutcome: "approved" | "denied" =
|
|
267
270
|
cbDecision.action === "reject" ? "denied" : "approved";
|
|
@@ -276,7 +279,7 @@ export async function handleApprovalInterception(
|
|
|
276
279
|
}).catch((err) => {
|
|
277
280
|
log.error(
|
|
278
281
|
{ err, conversationId, messageTs: approvalMessageId },
|
|
279
|
-
"Failed to edit
|
|
282
|
+
"Failed to edit approval message after decision",
|
|
280
283
|
);
|
|
281
284
|
});
|
|
282
285
|
}
|
|
@@ -287,8 +290,8 @@ export async function handleApprovalInterception(
|
|
|
287
290
|
}
|
|
288
291
|
|
|
289
292
|
// Race condition: request was already resolved between the stale check
|
|
290
|
-
// above and the decision attempt.
|
|
291
|
-
//
|
|
293
|
+
// above and the decision attempt. Edit the original approval message to
|
|
294
|
+
// remove stale buttons
|
|
292
295
|
if (approvalMessageId) {
|
|
293
296
|
editStaleApprovalMessage({
|
|
294
297
|
replyCallbackUrl,
|
|
@@ -368,7 +371,7 @@ export async function handleApprovalInterception(
|
|
|
368
371
|
}
|
|
369
372
|
|
|
370
373
|
// ---------------------------------------------------------------------------
|
|
371
|
-
//
|
|
374
|
+
// Approval message edit helper
|
|
372
375
|
// ---------------------------------------------------------------------------
|
|
373
376
|
|
|
374
377
|
/**
|
|
@@ -399,7 +402,7 @@ function editStaleApprovalMessage(params: {
|
|
|
399
402
|
conversationId: params.conversationId,
|
|
400
403
|
messageId: params.messageId,
|
|
401
404
|
},
|
|
402
|
-
"Failed to edit stale
|
|
405
|
+
"Failed to edit stale approval message",
|
|
403
406
|
);
|
|
404
407
|
});
|
|
405
408
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* messages reach this handler.
|
|
7
7
|
*/
|
|
8
8
|
import type { SourceMetadata } from "@vellumai/gateway-client";
|
|
9
|
+
import { resolveInboundEventKind } from "@vellumai/gateway-client";
|
|
9
10
|
import {
|
|
10
11
|
ADMISSION_POLICY_DEFAULT,
|
|
11
12
|
type AdmissionPolicy,
|
|
@@ -281,6 +282,7 @@ export async function handleChannelInbound({
|
|
|
281
282
|
conversationExternalId?: string;
|
|
282
283
|
externalMessageId?: string;
|
|
283
284
|
content?: string;
|
|
285
|
+
eventKind?: string;
|
|
284
286
|
isEdit?: boolean;
|
|
285
287
|
actorDisplayName?: string;
|
|
286
288
|
attachmentIds?: string[];
|
|
@@ -297,11 +299,16 @@ export async function handleChannelInbound({
|
|
|
297
299
|
conversationExternalId,
|
|
298
300
|
externalMessageId,
|
|
299
301
|
content,
|
|
300
|
-
isEdit,
|
|
301
302
|
attachmentIds,
|
|
302
303
|
sourceMetadata,
|
|
303
304
|
} = body;
|
|
304
305
|
|
|
306
|
+
// The named event family. Stamped by every gateway producer; replayed
|
|
307
|
+
// retry payloads arrive unstamped and classify by their flag and sentinel
|
|
308
|
+
// fields instead.
|
|
309
|
+
const eventKind = resolveInboundEventKind(body);
|
|
310
|
+
const isEdit = eventKind === "edit";
|
|
311
|
+
|
|
305
312
|
if (!body.sourceChannel || typeof body.sourceChannel !== "string") {
|
|
306
313
|
throw new BadRequestError("sourceChannel is required");
|
|
307
314
|
}
|
|
@@ -500,7 +507,10 @@ export async function handleChannelInbound({
|
|
|
500
507
|
// untouched for audit purposes — rendering elides based on the deletedAt
|
|
501
508
|
// marker. Gated behind ingress ACL so non-members cannot drive deletes
|
|
502
509
|
// (matches the edit-intercept policy).
|
|
503
|
-
|
|
510
|
+
// The Slack gate mirrors the reaction intercept's: deletes are ingested
|
|
511
|
+
// from Slack alone today, and each further channel arrives as its own
|
|
512
|
+
// decision with its own wire shape.
|
|
513
|
+
if (sourceChannel === "slack" && eventKind === "delete") {
|
|
504
514
|
const deletedMessageTs =
|
|
505
515
|
typeof sourceMetadata?.messageId === "string"
|
|
506
516
|
? sourceMetadata.messageId
|
|
@@ -764,9 +774,10 @@ export async function handleChannelInbound({
|
|
|
764
774
|
// re-verification challenge — §8.2). The gateway kill switch already
|
|
765
775
|
// dropped `no_one` upstream, but the stage handles it defensively.
|
|
766
776
|
//
|
|
767
|
-
//
|
|
768
|
-
//
|
|
769
|
-
//
|
|
777
|
+
// Exempt channels (`platform`, `a2a`) short-circuit admit inside
|
|
778
|
+
// `enforceAdmissionPolicy`: defense in depth alongside the gateway's
|
|
779
|
+
// exempt-channel skip and the PUT-handler's 403. `vellum` is hidden, not
|
|
780
|
+
// exempt: its floor is evaluated like any enforced channel's.
|
|
770
781
|
//
|
|
771
782
|
// Bootstrap deep-link: when ACL resolved a validated pending_bootstrap
|
|
772
783
|
// session, skip the floor entirely. The bootstrap intercept stage below
|
|
@@ -1091,10 +1102,11 @@ export async function handleChannelInbound({
|
|
|
1091
1102
|
!result.duplicate &&
|
|
1092
1103
|
!guardianReplyResult.skipApprovalInterception
|
|
1093
1104
|
) {
|
|
1094
|
-
//
|
|
1095
|
-
//
|
|
1096
|
-
//
|
|
1097
|
-
// the button
|
|
1105
|
+
// The id of the message holding the approval buttons, for editing the
|
|
1106
|
+
// card after resolution. The gateway sets sourceMetadata.messageId on
|
|
1107
|
+
// every button-press forward: Slack's block_actions carry the ts of the
|
|
1108
|
+
// message containing the button, Telegram's callback_query the id of the
|
|
1109
|
+
// message the keyboard is attached to.
|
|
1098
1110
|
const approvalMessageId =
|
|
1099
1111
|
typeof sourceMetadata?.messageId === "string"
|
|
1100
1112
|
? sourceMetadata.messageId
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* Invite code/token redemption is intercepted at gateway ingress; redeemed
|
|
7
7
|
* messages never reach this stage.
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
import {
|
|
10
|
+
ACCESS_DENIED_NOT_APPROVED_REPLY,
|
|
11
|
+
type AdmissionPolicy,
|
|
12
|
+
type SourceMetadata,
|
|
13
|
+
type TrustVerdict,
|
|
13
14
|
} from "@vellumai/gateway-client";
|
|
14
15
|
|
|
15
16
|
import type { VerificationSessionWire } from "../../../channels/gateway-verification-sessions.js";
|
|
@@ -78,7 +79,7 @@ export function composeAccessDenialReply(params: {
|
|
|
78
79
|
if (params.guardianNotified) {
|
|
79
80
|
return `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(params.verdict)} know you tried talking to me and get back to you.`;
|
|
80
81
|
}
|
|
81
|
-
return
|
|
82
|
+
return ACCESS_DENIED_NOT_APPROVED_REPLY;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
// ---------------------------------------------------------------------------
|
|
@@ -98,15 +99,20 @@ export interface AclEnforcementParams {
|
|
|
98
99
|
replyCallbackUrl: string | undefined;
|
|
99
100
|
assistantId: string;
|
|
100
101
|
/**
|
|
101
|
-
* Effective admission policy for this request (gateway
|
|
102
|
-
*
|
|
103
|
-
*
|
|
102
|
+
* Effective admission policy for this request (the gateway's per-channel
|
|
103
|
+
* floor). When set, ACL skips its hard-deny paths when the policy makes
|
|
104
|
+
* the floor stage the right place to answer:
|
|
104
105
|
* - `strangers`: non-members and inactive (non-blocked) members are passed
|
|
105
106
|
* through so the admission floor can emit the final verdict.
|
|
106
|
-
* - `
|
|
107
|
+
* - `guardian_only`: non-members and inactive `pending`/`unverified`
|
|
108
|
+
* members are passed through; the floor denies everyone below guardian.
|
|
109
|
+
* - `any_contact`: inactive `pending`/`unverified` members are passed
|
|
110
|
+
* through.
|
|
107
111
|
*
|
|
108
|
-
* Passing this in
|
|
109
|
-
* replies for senders
|
|
112
|
+
* Passing this in keeps ACL from firing guardian notifications and canned
|
|
113
|
+
* replies for senders whose final answer belongs to the floor stage:
|
|
114
|
+
* admission under the permissive floors, or a `guardian_only` denial
|
|
115
|
+
* delivered without a misleading verification challenge.
|
|
110
116
|
*/
|
|
111
117
|
effectiveAdmissionPolicy?: AdmissionPolicy;
|
|
112
118
|
/**
|
|
@@ -880,8 +886,7 @@ export async function enforceIngressAcl(
|
|
|
880
886
|
{ sourceChannel, channelId: resolvedMember.channelId },
|
|
881
887
|
"Ingress ACL: member policy deny",
|
|
882
888
|
);
|
|
883
|
-
const denyReplyText =
|
|
884
|
-
"Sorry, you haven't been approved to message this assistant.";
|
|
889
|
+
const denyReplyText = ACCESS_DENIED_NOT_APPROVED_REPLY;
|
|
885
890
|
let denyReplyDelivered = false;
|
|
886
891
|
if (replyCallbackUrl) {
|
|
887
892
|
const denyPayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* floor (`sourceMetadata.admissionPolicy`); this stage compares the floor
|
|
8
8
|
* to the resolved trust class's rank and either admits or denies.
|
|
9
9
|
*
|
|
10
|
-
* Deny semantics
|
|
10
|
+
* Deny semantics (the locked §8.2 decisions):
|
|
11
11
|
*
|
|
12
12
|
* - `shouldChallenge: true` when the policy is one that re-verification
|
|
13
13
|
* could lift past (`any_contact`, `strangers`). The caller fires the
|
|
@@ -66,7 +66,7 @@ export type AdmissionPolicyResult =
|
|
|
66
66
|
* resolved trust class could clear the floor after verification.
|
|
67
67
|
*/
|
|
68
68
|
shouldChallenge: boolean;
|
|
69
|
-
/**
|
|
69
|
+
/** Policy that produced the deny. */
|
|
70
70
|
effectivePolicy: AdmissionPolicy;
|
|
71
71
|
};
|
|
72
72
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* `unknown` (drop). Reactions never drive an agent turn.
|
|
21
21
|
*/
|
|
22
22
|
import type { SourceMetadata } from "@vellumai/gateway-client";
|
|
23
|
+
import { resolveInboundEventKind } from "@vellumai/gateway-client";
|
|
23
24
|
|
|
24
25
|
import type { ChannelId, InterfaceId } from "../../../channels/types.js";
|
|
25
26
|
import { getDiskPressureStatus } from "../../../daemon/disk-pressure-guard.js";
|
|
@@ -58,16 +59,16 @@ const log = getLogger("runtime-http");
|
|
|
58
59
|
*/
|
|
59
60
|
export function isSlackReactionEvent(body: {
|
|
60
61
|
sourceChannel?: string;
|
|
62
|
+
eventKind?: string;
|
|
61
63
|
callbackData?: string;
|
|
62
64
|
}): boolean {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return cb.startsWith("reaction:") || cb.startsWith("reaction_removed:");
|
|
65
|
+
// The Slack gate is deliberate, not residue: Slack is the only channel
|
|
66
|
+
// whose reactions are ingested today, and widening this is a per-channel
|
|
67
|
+
// decision made with that channel's reaction wire shape, not a fallthrough.
|
|
68
|
+
return (
|
|
69
|
+
body.sourceChannel === "slack" &&
|
|
70
|
+
resolveInboundEventKind(body) === "reaction"
|
|
71
|
+
);
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
/**
|
|
@@ -53,7 +53,7 @@ export async function tryTranscribeAudioAttachments(
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// Resolve STT provider via daemon batch transcriber facade
|
|
56
|
-
const transcriber = await resolveBatchTranscriber();
|
|
56
|
+
const transcriber = await resolveBatchTranscriber({ role: "batch" });
|
|
57
57
|
if (!transcriber) {
|
|
58
58
|
return {
|
|
59
59
|
status: "no_provider",
|
|
@@ -93,6 +93,7 @@ import { ROUTES as INFERENCE_PROVIDER_CONNECTION_ROUTES } from "./inference-prov
|
|
|
93
93
|
import { ROUTES as INFERENCE_SEND_ROUTES } from "./inference-send-routes.js";
|
|
94
94
|
import { ROUTES as INGRESS_STATUS_ROUTES } from "./ingress-status-routes.js";
|
|
95
95
|
import { ROUTES as A2A_ROUTES } from "./integrations/a2a.js";
|
|
96
|
+
import { ROUTES as DISCORD_CONFIG_ROUTES } from "./integrations/discord.js";
|
|
96
97
|
import { ROUTES as SLACK_CHANNEL_CONFIG_ROUTES } from "./integrations/slack/channel.js";
|
|
97
98
|
import { ROUTES as SLACK_CHANNELS_ROUTES } from "./integrations/slack/channels.js";
|
|
98
99
|
import { ROUTES as SLACK_SHARE_ROUTES } from "./integrations/slack/share.js";
|
|
@@ -287,6 +288,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
287
288
|
...SOUNDS_CONFIG_ROUTES,
|
|
288
289
|
...SKILL_ROUTES,
|
|
289
290
|
...A2A_ROUTES,
|
|
291
|
+
...DISCORD_CONFIG_ROUTES,
|
|
290
292
|
...SLACK_CHANNEL_CONFIG_ROUTES,
|
|
291
293
|
...SLACK_CHANNEL_RESOLVE_ROUTES,
|
|
292
294
|
...SLACK_CHANNELS_ROUTES,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { getUserSelectableProfilesForProvider } from "../../config/default-profile-catalog.js";
|
|
18
18
|
import { loadConfig } from "../../config/loader.js";
|
|
19
19
|
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
20
20
|
import {
|
|
@@ -153,7 +153,7 @@ export async function setInferenceProfileSession({
|
|
|
153
153
|
|
|
154
154
|
// --- Validate profile ---
|
|
155
155
|
const { llm } = loadConfig();
|
|
156
|
-
const profiles =
|
|
156
|
+
const profiles = getUserSelectableProfilesForProvider(
|
|
157
157
|
llm?.profiles,
|
|
158
158
|
llm?.defaultProvider ?? null,
|
|
159
159
|
);
|
|
@@ -20,7 +20,7 @@ import { z } from "zod";
|
|
|
20
20
|
|
|
21
21
|
import { validateInferenceProfileConfig } from "../../api/constants/profile-config-validation.js";
|
|
22
22
|
import {
|
|
23
|
-
|
|
23
|
+
getUserSelectableProfilesForProvider,
|
|
24
24
|
MANAGED_PROFILE_NAMES,
|
|
25
25
|
resolveDefaultProfileForProvider,
|
|
26
26
|
} from "../../config/default-profile-catalog.js";
|
|
@@ -436,8 +436,9 @@ function fragmentFromBody(
|
|
|
436
436
|
|
|
437
437
|
/**
|
|
438
438
|
* Enumerate every live reference to profile `name` in the raw `llm` config
|
|
439
|
-
* block: `activeProfile`, `advisorProfile`, each `callSites.<id>.profile`,
|
|
440
|
-
* every mix arm (`profiles.<mix>.mix[].profile`)
|
|
439
|
+
* block: `activeProfile`, `advisorProfile`, each `callSites.<id>.profile`,
|
|
440
|
+
* every mix arm (`profiles.<mix>.mix[].profile`), and every fallback pointer
|
|
441
|
+
* (`profiles.<p>.fallbackProfile`). Deleting a profile while any
|
|
441
442
|
* of these point at it would leave a dangling reference that `LLMSchema`'s
|
|
442
443
|
* superRefine rejects on the next load — silently resetting the user's chat
|
|
443
444
|
* model or call-site pins. The delete handler rejects instead.
|
|
@@ -467,13 +468,17 @@ export function collectProfileReferences(
|
|
|
467
468
|
const profiles = asPlainObject(llm.profiles);
|
|
468
469
|
if (profiles) {
|
|
469
470
|
for (const [profileName, profileEntry] of Object.entries(profiles)) {
|
|
470
|
-
const
|
|
471
|
+
const entry = asPlainObject(profileEntry);
|
|
472
|
+
const mix = entry?.mix;
|
|
471
473
|
if (
|
|
472
474
|
Array.isArray(mix) &&
|
|
473
475
|
mix.some((arm) => asPlainObject(arm)?.profile === name)
|
|
474
476
|
) {
|
|
475
477
|
refs.push(`llm.profiles.${profileName}.mix`);
|
|
476
478
|
}
|
|
479
|
+
if (entry?.fallbackProfile === name) {
|
|
480
|
+
refs.push(`llm.profiles.${profileName}.fallbackProfile`);
|
|
481
|
+
}
|
|
477
482
|
}
|
|
478
483
|
}
|
|
479
484
|
return refs;
|
|
@@ -514,7 +519,7 @@ function assertSaneMaxTokens(
|
|
|
514
519
|
|
|
515
520
|
async function handleListProfiles() {
|
|
516
521
|
const config = getConfigReadOnly();
|
|
517
|
-
const effective =
|
|
522
|
+
const effective = getUserSelectableProfilesForProvider(
|
|
518
523
|
config.llm.profiles,
|
|
519
524
|
config.llm.defaultProvider ?? null,
|
|
520
525
|
);
|
|
@@ -870,7 +875,7 @@ async function handleSetActiveProfile({ body = {} }: RouteHandlerArgs) {
|
|
|
870
875
|
// is rejected here instead of silently stripped on the next config load —
|
|
871
876
|
// which would reset the user's chat-model selection.
|
|
872
877
|
const config = getConfigReadOnly();
|
|
873
|
-
const effective =
|
|
878
|
+
const effective = getUserSelectableProfilesForProvider(
|
|
874
879
|
config.llm.profiles,
|
|
875
880
|
config.llm.defaultProvider ?? null,
|
|
876
881
|
);
|
|
@@ -1011,7 +1016,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
1011
1016
|
"404": { description: "Profile not found" },
|
|
1012
1017
|
"409": {
|
|
1013
1018
|
description:
|
|
1014
|
-
"Profile is still referenced by activeProfile, advisorProfile, a call site, a default-tier override,
|
|
1019
|
+
"Profile is still referenced by activeProfile, advisorProfile, a call site, a default-tier override, a mix arm, or another profile's fallbackProfile",
|
|
1015
1020
|
},
|
|
1016
1021
|
},
|
|
1017
1022
|
handler: handleDeleteProfile,
|
|
@@ -86,7 +86,7 @@ const savedConnectionResponseSchema = ProviderConnectionSchema.extend({
|
|
|
86
86
|
* Parse and validate `base_url` and `models` from the request body.
|
|
87
87
|
*
|
|
88
88
|
* `base_url` is only accepted for providers in
|
|
89
|
-
* `PROVIDERS_ALLOWING_CUSTOM_BASE_URL` (openai-compatible
|
|
89
|
+
* `PROVIDERS_ALLOWING_CUSTOM_BASE_URL` (openai-compatible, ollama, opencode).
|
|
90
90
|
* For all other providers, supplying `base_url` returns a 400. This prevents
|
|
91
91
|
* API-key exfiltration: an attacker cannot create an `anthropic` connection
|
|
92
92
|
* with a `base_url` pointing to their own server, which would redirect all
|
|
@@ -119,7 +119,7 @@ async function parseCustomProviderFields(
|
|
|
119
119
|
!PROVIDERS_ALLOWING_CUSTOM_BASE_URL.has(provider)
|
|
120
120
|
) {
|
|
121
121
|
throw new BadRequestError(
|
|
122
|
-
`base_url is only valid for openai-compatible and
|
|
122
|
+
`base_url is only valid for openai-compatible, ollama, and opencode providers. Remove base_url or use a provider that accepts a custom endpoint.`,
|
|
123
123
|
);
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { getUserSelectableProfilesForProvider } from "../../config/default-profile-catalog.js";
|
|
11
11
|
import type { ResolutionFallbackReason } from "../../config/llm-resolver.js";
|
|
12
12
|
import { selectWinningProfile } from "../../config/llm-resolver.js";
|
|
13
13
|
import { getConfigReadOnly } from "../../config/loader.js";
|
|
@@ -42,7 +42,7 @@ async function handleInferenceSend({ body = {} }: RouteHandlerArgs) {
|
|
|
42
42
|
// Validate --profile against the configured profile catalog.
|
|
43
43
|
if (profile !== undefined) {
|
|
44
44
|
const { llm } = getConfigReadOnly();
|
|
45
|
-
const profiles =
|
|
45
|
+
const profiles = getUserSelectableProfilesForProvider(
|
|
46
46
|
llm?.profiles,
|
|
47
47
|
llm?.defaultProvider ?? null,
|
|
48
48
|
);
|
|
@@ -24,9 +24,9 @@ import { z } from "zod";
|
|
|
24
24
|
import {
|
|
25
25
|
getIngressConfigResult,
|
|
26
26
|
isVelayManagedIngress,
|
|
27
|
-
loadLastTunnelRecord,
|
|
28
27
|
loadPairingTunnelRecord,
|
|
29
28
|
loadRecordedAssistantId,
|
|
29
|
+
loadRestartTunnelRecord,
|
|
30
30
|
} from "../../daemon/handlers/config-ingress.js";
|
|
31
31
|
import { probeTunnel } from "../../inbound/tunnel-probe.js";
|
|
32
32
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
@@ -100,7 +100,7 @@ async function handleIngressStatus(): Promise<IngressStatusResponse> {
|
|
|
100
100
|
if (isVelayManagedIngress() && !pairingTunnel) {
|
|
101
101
|
return { state: "unconfigured" };
|
|
102
102
|
}
|
|
103
|
-
const lastTunnel =
|
|
103
|
+
const lastTunnel = loadRestartTunnelRecord();
|
|
104
104
|
|
|
105
105
|
const publicBaseUrl =
|
|
106
106
|
pairingTunnel?.publicBaseUrl ?? config.publicBaseUrl.trim();
|