@vellumai/assistant 0.11.6 → 0.11.7-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +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/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/node_modules/@vellumai/ces-client/src/http-credentials.ts +161 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- 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/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- 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/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/openapi.yaml +419 -55
- package/package.json +4 -1
- package/scripts/sync-llm-catalog.ts +6 -0
- 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-record-write-through.test.ts +78 -0
- 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__/delete-propagation.test.ts +92 -2
- package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
- package/src/__tests__/discord-channel-config.test.ts +237 -0
- package/src/__tests__/edit-propagation.test.ts +42 -2
- 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-catalog-parity.test.ts +4 -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__/oauth-connect-orchestrator.test.ts +95 -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-catalog-visibility.test.ts +17 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +8 -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 +57 -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 +19 -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 +34 -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 +263 -15
- 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-session-telemetry.test.ts +64 -0
- 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 +224 -9
- 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/messaging/read-provider-metadata.ts +32 -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/connect-orchestrator.ts +16 -0
- package/src/oauth/seed-providers.ts +262 -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 +88 -134
- 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__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +55 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +21 -3
- 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 +246 -3
- package/src/providers/inference/auth.ts +15 -14
- 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 +100 -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/platform-proxy/constants.ts +5 -0
- package/src/providers/provider-resolvability.ts +20 -0
- package/src/providers/provider-secret-catalog.ts +3 -2
- 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/providers/vellum/client.ts +29 -0
- package/src/providers/vellum-model-routing.test.ts +2 -0
- package/src/providers/vellum-model-routing.ts +3 -3
- 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 +47 -48
- 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/edit-intercept.ts +20 -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/migration-routes.ts +3 -1
- 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/ces-rpc-record-backend.ts +123 -0
- package/src/security/oauth2.ts +7 -0
- package/src/security/openai-device-auth.ts +364 -0
- package/src/security/secure-keys.ts +15 -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/credentials/metadata-store.ts +65 -16
- package/src/tools/credentials/store.ts +2 -0
- 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/watch/__tests__/watch-retro.test.ts +48 -1
- package/src/watch/watch-retro.ts +46 -4
- 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,
|
|
@@ -63,6 +64,7 @@ import {
|
|
|
63
64
|
type SlackMessageMetadata,
|
|
64
65
|
writeSlackMetadata,
|
|
65
66
|
} from "../../messaging/providers/slack/message-metadata.js";
|
|
67
|
+
import { mergeProviderMessageMetadata } from "../../messaging/read-provider-metadata.js";
|
|
66
68
|
import { MESSAGE_PREVIEW_MAX_LENGTH } from "../../notifications/notification-utils.js";
|
|
67
69
|
import {
|
|
68
70
|
attachInlineAttachmentToMessage,
|
|
@@ -93,6 +95,7 @@ import { upsertBinding } from "../../persistence/external-conversation-store.js"
|
|
|
93
95
|
import type { ContentBlock } from "../../providers/types.js";
|
|
94
96
|
import { checkIngressForSecrets } from "../../security/secret-ingress.js";
|
|
95
97
|
import { canonicalizeInboundIdentity } from "../../util/canonicalize-identity.js";
|
|
98
|
+
import { safeParseRecord } from "../../util/json.js";
|
|
96
99
|
import { getLogger } from "../../util/logger.js";
|
|
97
100
|
import { truncate } from "../../util/truncate.js";
|
|
98
101
|
import {
|
|
@@ -281,6 +284,7 @@ export async function handleChannelInbound({
|
|
|
281
284
|
conversationExternalId?: string;
|
|
282
285
|
externalMessageId?: string;
|
|
283
286
|
content?: string;
|
|
287
|
+
eventKind?: string;
|
|
284
288
|
isEdit?: boolean;
|
|
285
289
|
actorDisplayName?: string;
|
|
286
290
|
attachmentIds?: string[];
|
|
@@ -297,11 +301,16 @@ export async function handleChannelInbound({
|
|
|
297
301
|
conversationExternalId,
|
|
298
302
|
externalMessageId,
|
|
299
303
|
content,
|
|
300
|
-
isEdit,
|
|
301
304
|
attachmentIds,
|
|
302
305
|
sourceMetadata,
|
|
303
306
|
} = body;
|
|
304
307
|
|
|
308
|
+
// The named event family. Stamped by every gateway producer; replayed
|
|
309
|
+
// retry payloads arrive unstamped and classify by their flag and sentinel
|
|
310
|
+
// fields instead.
|
|
311
|
+
const eventKind = resolveInboundEventKind(body);
|
|
312
|
+
const isEdit = eventKind === "edit";
|
|
313
|
+
|
|
305
314
|
if (!body.sourceChannel || typeof body.sourceChannel !== "string") {
|
|
306
315
|
throw new BadRequestError("sourceChannel is required");
|
|
307
316
|
}
|
|
@@ -500,7 +509,10 @@ export async function handleChannelInbound({
|
|
|
500
509
|
// untouched for audit purposes — rendering elides based on the deletedAt
|
|
501
510
|
// marker. Gated behind ingress ACL so non-members cannot drive deletes
|
|
502
511
|
// (matches the edit-intercept policy).
|
|
503
|
-
|
|
512
|
+
// The Slack gate mirrors the reaction intercept's: deletes are ingested
|
|
513
|
+
// from Slack alone today, and each further channel arrives as its own
|
|
514
|
+
// decision with its own wire shape.
|
|
515
|
+
if (sourceChannel === "slack" && eventKind === "delete") {
|
|
504
516
|
const deletedMessageTs =
|
|
505
517
|
typeof sourceMetadata?.messageId === "string"
|
|
506
518
|
? sourceMetadata.messageId
|
|
@@ -557,43 +569,15 @@ export async function handleChannelInbound({
|
|
|
557
569
|
return { accepted: true, deleted: false };
|
|
558
570
|
}
|
|
559
571
|
|
|
560
|
-
// Merge deletedAt into the existing slackMeta sub-key
|
|
561
|
-
//
|
|
562
|
-
//
|
|
563
|
-
//
|
|
564
|
-
//
|
|
572
|
+
// Merge deletedAt into the existing slackMeta sub-key when the row has
|
|
573
|
+
// one, so the Slack transcript renderer keeps seeing its own envelope.
|
|
574
|
+
// A row without it (a legacy pre-enrichment row) stamps the neutral
|
|
575
|
+
// shape instead: readProviderMetadata serves either to every
|
|
576
|
+
// channel-agnostic reader, so no delete goes unmarked over a metadata
|
|
577
|
+
// accident.
|
|
565
578
|
const row = getMessageById(original.messageId);
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
{
|
|
569
|
-
conversationExternalId,
|
|
570
|
-
deletedMessageTs,
|
|
571
|
-
messageId: original.messageId,
|
|
572
|
-
},
|
|
573
|
-
"Stored Slack message has no metadata; skipping delete marker",
|
|
574
|
-
);
|
|
575
|
-
return { accepted: true, deleted: false };
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
let parentMetadata: Record<string, unknown>;
|
|
579
|
-
try {
|
|
580
|
-
const parsed = JSON.parse(row.metadata) as unknown;
|
|
581
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
582
|
-
parentMetadata = parsed as Record<string, unknown>;
|
|
583
|
-
} else {
|
|
584
|
-
parentMetadata = {};
|
|
585
|
-
}
|
|
586
|
-
} catch {
|
|
587
|
-
log.debug(
|
|
588
|
-
{
|
|
589
|
-
conversationExternalId,
|
|
590
|
-
deletedMessageTs,
|
|
591
|
-
messageId: original.messageId,
|
|
592
|
-
},
|
|
593
|
-
"Failed to parse stored metadata; skipping delete marker",
|
|
594
|
-
);
|
|
595
|
-
return { accepted: true, deleted: false };
|
|
596
|
-
}
|
|
579
|
+
const parentMetadata: Record<string, unknown> =
|
|
580
|
+
row?.metadata != null ? safeParseRecord(row.metadata) : {};
|
|
597
581
|
|
|
598
582
|
const existingSlackMeta =
|
|
599
583
|
typeof parentMetadata.slackMeta === "string"
|
|
@@ -601,15 +585,28 @@ export async function handleChannelInbound({
|
|
|
601
585
|
: null;
|
|
602
586
|
|
|
603
587
|
if (!existingSlackMeta) {
|
|
604
|
-
|
|
588
|
+
const providerMeta = mergeProviderMessageMetadata(
|
|
589
|
+
row?.metadata ?? null,
|
|
590
|
+
{
|
|
591
|
+
source: sourceChannel,
|
|
592
|
+
conversationExternalId,
|
|
593
|
+
messageId: deletedMessageTs,
|
|
594
|
+
...(sourceMetadata?.threadId
|
|
595
|
+
? { threadId: sourceMetadata.threadId }
|
|
596
|
+
: {}),
|
|
597
|
+
},
|
|
598
|
+
{ deletedAt: Date.now() },
|
|
599
|
+
);
|
|
600
|
+
updateMessageMetadata(original.messageId, { providerMeta });
|
|
601
|
+
log.info(
|
|
605
602
|
{
|
|
606
603
|
conversationExternalId,
|
|
607
604
|
deletedMessageTs,
|
|
608
605
|
messageId: original.messageId,
|
|
609
606
|
},
|
|
610
|
-
"
|
|
607
|
+
"Marked message deleted via neutral metadata",
|
|
611
608
|
);
|
|
612
|
-
return { accepted: true, deleted:
|
|
609
|
+
return { accepted: true, deleted: true, messageId: original.messageId };
|
|
613
610
|
}
|
|
614
611
|
|
|
615
612
|
const updatedSlackMeta = mergeSlackMetadata(existingSlackMeta, {
|
|
@@ -764,9 +761,10 @@ export async function handleChannelInbound({
|
|
|
764
761
|
// re-verification challenge — §8.2). The gateway kill switch already
|
|
765
762
|
// dropped `no_one` upstream, but the stage handles it defensively.
|
|
766
763
|
//
|
|
767
|
-
//
|
|
768
|
-
//
|
|
769
|
-
//
|
|
764
|
+
// Exempt channels (`platform`, `a2a`) short-circuit admit inside
|
|
765
|
+
// `enforceAdmissionPolicy`: defense in depth alongside the gateway's
|
|
766
|
+
// exempt-channel skip and the PUT-handler's 403. `vellum` is hidden, not
|
|
767
|
+
// exempt: its floor is evaluated like any enforced channel's.
|
|
770
768
|
//
|
|
771
769
|
// Bootstrap deep-link: when ACL resolved a validated pending_bootstrap
|
|
772
770
|
// session, skip the floor entirely. The bootstrap intercept stage below
|
|
@@ -1091,10 +1089,11 @@ export async function handleChannelInbound({
|
|
|
1091
1089
|
!result.duplicate &&
|
|
1092
1090
|
!guardianReplyResult.skipApprovalInterception
|
|
1093
1091
|
) {
|
|
1094
|
-
//
|
|
1095
|
-
//
|
|
1096
|
-
//
|
|
1097
|
-
// the button
|
|
1092
|
+
// The id of the message holding the approval buttons, for editing the
|
|
1093
|
+
// card after resolution. The gateway sets sourceMetadata.messageId on
|
|
1094
|
+
// every button-press forward: Slack's block_actions carry the ts of the
|
|
1095
|
+
// message containing the button, Telegram's callback_query the id of the
|
|
1096
|
+
// message the keyboard is attached to.
|
|
1098
1097
|
const approvalMessageId =
|
|
1099
1098
|
typeof sourceMetadata?.messageId === "string"
|
|
1100
1099
|
? 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,10 +20,10 @@ import {
|
|
|
20
20
|
mergeSlackMetadata,
|
|
21
21
|
readSlackMetadata,
|
|
22
22
|
} from "../../../messaging/providers/slack/message-metadata.js";
|
|
23
|
+
import { mergeProviderMessageMetadata } from "../../../messaging/read-provider-metadata.js";
|
|
23
24
|
import type { MessageRow } from "../../../persistence/conversation-crud.js";
|
|
24
25
|
import {
|
|
25
26
|
getMessageById,
|
|
26
|
-
updateMessageContent,
|
|
27
27
|
updateMessageContentAndMetadata,
|
|
28
28
|
} from "../../../persistence/conversation-crud.js";
|
|
29
29
|
import {
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
findMessageBySourceId,
|
|
32
32
|
recordInbound,
|
|
33
33
|
} from "../../../persistence/delivery-crud.js";
|
|
34
|
+
import { markProcessed } from "../../../persistence/delivery-status.js";
|
|
34
35
|
import { enqueueLexicalIndexForMessage } from "../../../persistence/job-handlers/message-lexical.js";
|
|
35
36
|
import { stringifyMessageContent } from "../../../persistence/message-content.js";
|
|
36
37
|
import { safeParseRecord } from "../../../util/json.js";
|
|
@@ -182,6 +183,7 @@ export async function handleEditIntercept(
|
|
|
182
183
|
},
|
|
183
184
|
"Edit text unchanged; skipping update",
|
|
184
185
|
);
|
|
186
|
+
markProcessed(editResult.eventId);
|
|
185
187
|
return {
|
|
186
188
|
accepted: true,
|
|
187
189
|
duplicate: false,
|
|
@@ -203,13 +205,29 @@ export async function handleEditIntercept(
|
|
|
203
205
|
newContent,
|
|
204
206
|
});
|
|
205
207
|
} else {
|
|
206
|
-
|
|
208
|
+
// Every channel marks its edits. Slack's envelope carries the extra
|
|
209
|
+
// fields its own renderer needs; the rest stamp the neutral shape that
|
|
210
|
+
// readProviderMetadata serves to every channel-agnostic reader.
|
|
211
|
+
const providerMeta = mergeProviderMessageMetadata(
|
|
212
|
+
existingRow?.metadata ?? null,
|
|
213
|
+
{
|
|
214
|
+
source: sourceChannel,
|
|
215
|
+
conversationExternalId,
|
|
216
|
+
messageId: sourceMessageId,
|
|
217
|
+
...(sourceThreadId ? { threadId: sourceThreadId } : {}),
|
|
218
|
+
},
|
|
219
|
+
{ editedAt: Date.now() },
|
|
220
|
+
);
|
|
221
|
+
updateMessageContentAndMetadata(original.messageId, newContent, {
|
|
222
|
+
providerMeta,
|
|
223
|
+
});
|
|
207
224
|
}
|
|
208
225
|
// The edit changed searchable text (the no-op guard above already returned
|
|
209
226
|
// for identical content) and this path bypasses the `addMessage` persist
|
|
210
227
|
// path, so reindex the message into the lexical index — the idempotent
|
|
211
228
|
// upsert replaces the stale Qdrant point with the edited content.
|
|
212
229
|
enqueueLexicalIndexForMessage(original.messageId);
|
|
230
|
+
markProcessed(editResult.eventId);
|
|
213
231
|
log.info(
|
|
214
232
|
{ assistantId, sourceMessageId, messageId: original.messageId },
|
|
215
233
|
"Updated message content from edited_message",
|
|
@@ -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
|
);
|