@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
|
@@ -23,7 +23,7 @@ import type {
|
|
|
23
23
|
DeliveryResult,
|
|
24
24
|
NotificationChannel,
|
|
25
25
|
} from "../types.js";
|
|
26
|
-
import { resolveMessageText } from "./shared.js";
|
|
26
|
+
import { appendPlainTextFallback, resolveMessageText } from "./shared.js";
|
|
27
27
|
|
|
28
28
|
const log = getLogger("notif-adapter-telegram");
|
|
29
29
|
|
|
@@ -74,13 +74,10 @@ export class TelegramAdapter implements ChannelAdapter {
|
|
|
74
74
|
|
|
75
75
|
// When falling back from rich delivery, append the plain-text
|
|
76
76
|
// instructions so the guardian still knows how to approve/reject.
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
: messageText;
|
|
82
|
-
|
|
83
|
-
const sent = await sendTelegramReply(chatId, fallbackText);
|
|
77
|
+
const sent = await sendTelegramReply(
|
|
78
|
+
chatId,
|
|
79
|
+
appendPlainTextFallback(messageText, approval),
|
|
80
|
+
);
|
|
84
81
|
|
|
85
82
|
log.info(
|
|
86
83
|
{ sourceEventName: payload.sourceEventName, chatId },
|
|
@@ -29,11 +29,13 @@ import {
|
|
|
29
29
|
} from "./approval-card-builder.js";
|
|
30
30
|
import {
|
|
31
31
|
buildGuardianRequestCodeInstruction,
|
|
32
|
+
buildQuestionDeliveryText,
|
|
32
33
|
buildToolApprovalSourceView,
|
|
33
34
|
type GuardianQuestionPayload,
|
|
34
35
|
type LenientToolApprovalPayload,
|
|
35
36
|
LenientToolApprovalPayloadSchema,
|
|
36
37
|
parseGuardianQuestionPayload,
|
|
38
|
+
type PendingQuestionGuardianPayload,
|
|
37
39
|
resolveGuardianInstructionModeFromFields,
|
|
38
40
|
resolveGuardianInstructionModeFromPayload,
|
|
39
41
|
} from "./guardian-question-mode.js";
|
|
@@ -122,12 +124,21 @@ export interface ToolApprovalCardData {
|
|
|
122
124
|
card: ApprovalCardParams;
|
|
123
125
|
}
|
|
124
126
|
|
|
127
|
+
/** Resolved card data for an `ask_question` prompt awaiting an answer. */
|
|
128
|
+
export interface QuestionCardData {
|
|
129
|
+
kind: "question";
|
|
130
|
+
card: ApprovalCardParams;
|
|
131
|
+
}
|
|
132
|
+
|
|
125
133
|
/**
|
|
126
134
|
* Channel-agnostic approval card content, resolved once from `contextPayload`.
|
|
127
135
|
* The discriminant `kind` lets consumers branch on the approval type without
|
|
128
136
|
* re-parsing the payload.
|
|
129
137
|
*/
|
|
130
|
-
export type ApprovalCardData =
|
|
138
|
+
export type ApprovalCardData =
|
|
139
|
+
| AccessRequestCardData
|
|
140
|
+
| ToolApprovalCardData
|
|
141
|
+
| QuestionCardData;
|
|
131
142
|
|
|
132
143
|
// ── Shared source rendering ──────────────────────────────────────────────────
|
|
133
144
|
|
|
@@ -335,6 +346,65 @@ function extractToolApprovalCard(
|
|
|
335
346
|
};
|
|
336
347
|
}
|
|
337
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Shape a parked `ask_question` prompt into card params.
|
|
351
|
+
*
|
|
352
|
+
* The question itself is the subject, so it leads; the options become the
|
|
353
|
+
* card's actions under the index-based token scheme the resolver maps back to
|
|
354
|
+
* the pending interaction, plus an explicit skip. Channels without buttons get
|
|
355
|
+
* the same options enumerated in `fallbackText`, so the answer is reachable by
|
|
356
|
+
* a request-code reply on a channel that can only carry text.
|
|
357
|
+
*/
|
|
358
|
+
function extractQuestionCard(
|
|
359
|
+
p: PendingQuestionGuardianPayload,
|
|
360
|
+
): ApprovalCardParams {
|
|
361
|
+
const metadata: Array<{ label: string; value: string }> = [];
|
|
362
|
+
const sourceView = buildToolApprovalSourceView(p);
|
|
363
|
+
const sourceRow = sourceMetadataRow(
|
|
364
|
+
nonEmpty(p.sourceChannel),
|
|
365
|
+
sourceView?.chatId,
|
|
366
|
+
sourceView?.isSlackDm ?? false,
|
|
367
|
+
);
|
|
368
|
+
if (sourceRow) {
|
|
369
|
+
metadata.push(sourceRow);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const options = p.options ?? [];
|
|
373
|
+
const bodyParts: string[] = [];
|
|
374
|
+
if (options.length > 0) {
|
|
375
|
+
bodyParts.push(
|
|
376
|
+
options
|
|
377
|
+
.map((option, index) => `${index + 1}. ${option.label}`)
|
|
378
|
+
.join("\n"),
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
if (sourceView?.permalink) {
|
|
382
|
+
bodyParts.push(viewMessageLine(sourceView.permalink));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
// Deliberately the tool-approval prefix rather than one of its own: the
|
|
387
|
+
// withdrawal path recomputes a card's surface id from the request kind,
|
|
388
|
+
// and `pending_question` already maps here. A new prefix would orphan
|
|
389
|
+
// every question card written before it.
|
|
390
|
+
surfaceIdPrefix: TOOL_APPROVAL_SURFACE_PREFIX,
|
|
391
|
+
cardTitle: "Question",
|
|
392
|
+
primaryLine: p.questionText,
|
|
393
|
+
subtitle: "Waiting on your answer",
|
|
394
|
+
body: bodyParts.join("\n\n"),
|
|
395
|
+
metadata,
|
|
396
|
+
requestId: nonEmpty(p.requestId),
|
|
397
|
+
// Empty rather than absent, and empty rather than the answer options.
|
|
398
|
+
// Absent means the generic Approve/Reject pair, which is the wrong verb
|
|
399
|
+
// for a question. The options themselves are not offered here because the
|
|
400
|
+
// in-app surface route resolves only approval actions, so an `answer_`
|
|
401
|
+
// button on this card would be inert; channel buttons come from the
|
|
402
|
+
// broadcaster, whose taps the guardian reply router does resolve.
|
|
403
|
+
actions: [],
|
|
404
|
+
fallbackText: buildQuestionDeliveryText(p),
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
338
408
|
/**
|
|
339
409
|
* Resolve a guardian.question payload into tool-approval card params, or
|
|
340
410
|
* `null` when it does not represent a tool approval.
|
|
@@ -343,6 +413,21 @@ function extractToolApprovalCard(
|
|
|
343
413
|
* to lenient field extraction so cards still render when optional fields are
|
|
344
414
|
* absent.
|
|
345
415
|
*/
|
|
416
|
+
function resolveQuestionCard(
|
|
417
|
+
payload: Record<string, unknown>,
|
|
418
|
+
): ApprovalCardParams | null {
|
|
419
|
+
const parsed = parseGuardianQuestionPayload(payload);
|
|
420
|
+
if (!parsed || parsed.requestKind !== "pending_question") {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
// A `pending_question` carrying a tool name is a tool approval wearing the
|
|
424
|
+
// question kind, and the mode table says so; leave it to the approval card.
|
|
425
|
+
if (resolveGuardianInstructionModeFromPayload(parsed).mode !== "answer") {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
return extractQuestionCard(parsed);
|
|
429
|
+
}
|
|
430
|
+
|
|
346
431
|
function resolveToolApprovalCard(
|
|
347
432
|
payload: Record<string, unknown>,
|
|
348
433
|
): ApprovalCardParams | null {
|
|
@@ -388,6 +473,14 @@ export function resolveApprovalCardData(
|
|
|
388
473
|
}
|
|
389
474
|
|
|
390
475
|
if (sourceEventName === "guardian.question") {
|
|
476
|
+
// Both kinds arrive under this one event, split by instruction mode: a
|
|
477
|
+
// question is asked, an approval is granted, and the tool-approval
|
|
478
|
+
// resolver accepts only `approval`. Ask about the question first, since a
|
|
479
|
+
// `pending_question` carrying a tool name is an approval either way.
|
|
480
|
+
const question = resolveQuestionCard(contextPayload);
|
|
481
|
+
if (question) {
|
|
482
|
+
return { kind: "question", card: question };
|
|
483
|
+
}
|
|
391
484
|
const card = resolveToolApprovalCard(contextPayload);
|
|
392
485
|
return card ? { kind: "tool_approval", card } : null;
|
|
393
486
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { BackgroundJobErrorKind } from "../runtime/background-job-runner.js";
|
|
2
|
+
import { getLogger } from "../util/logger.js";
|
|
3
|
+
import { activityFailedDedupeKey } from "./activity-failed-dedupe.js";
|
|
4
|
+
import { emitNotificationSignal } from "./emit-signal.js";
|
|
5
|
+
import type { NotificationSourceChannel } from "./signal.js";
|
|
6
|
+
|
|
7
|
+
const log = getLogger("background-failure-signal");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* One background failure, as a producer reports it.
|
|
11
|
+
*
|
|
12
|
+
* The classification fields are the authored attribution carried out of the
|
|
13
|
+
* failing turn (`TurnFailure`): `failureSummary` is the classifier's
|
|
14
|
+
* user-facing message, and `profileName`/`connectionName` name the route the
|
|
15
|
+
* call actually resolved. Producers whose failures never ran a turn (a
|
|
16
|
+
* timeout, a bootstrap crash, a data job's own exception) leave them unset
|
|
17
|
+
* and the signal degrades to the classified kind.
|
|
18
|
+
*/
|
|
19
|
+
export interface BackgroundFailureReport {
|
|
20
|
+
/** Stable job identifier for logs and per-job dedupe, e.g. `heartbeat`. */
|
|
21
|
+
jobName: string;
|
|
22
|
+
/** Human-facing job label for notification copy. Defaults to `jobName`. */
|
|
23
|
+
displayName?: string;
|
|
24
|
+
sourceChannel: NotificationSourceChannel;
|
|
25
|
+
/** Conversation or job id the notification links back to. */
|
|
26
|
+
sourceContextId: string;
|
|
27
|
+
errorKind: BackgroundJobErrorKind;
|
|
28
|
+
/** Raw error text, carried for debugging; copy prefers `failureSummary`. */
|
|
29
|
+
errorMessage: string;
|
|
30
|
+
/** Stable classified code (`ConversationErrorCode`), when classified. */
|
|
31
|
+
failureCode?: string;
|
|
32
|
+
/** The classification's authored user-facing message, when carried. */
|
|
33
|
+
failureSummary?: string;
|
|
34
|
+
/** The classification's machine-readable category, when carried. */
|
|
35
|
+
errorCategory?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Connection row the failing call resolved, when carried. The most precise
|
|
38
|
+
* route identity, so it leads the dedupe-scope hierarchy.
|
|
39
|
+
*/
|
|
40
|
+
connectionName?: string;
|
|
41
|
+
/** Profile key the failing call resolved, when carried. */
|
|
42
|
+
profileName?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Recomputed single-route profile key (see `resolveSingleRouteProfileKey`).
|
|
45
|
+
* Used only when the failure carries no attribution of its own.
|
|
46
|
+
*/
|
|
47
|
+
fallbackProviderScope?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The single emission seam for background-job failure notifications.
|
|
52
|
+
*
|
|
53
|
+
* Owns the payload shape, the dedupe-key derivation, and the attention hints
|
|
54
|
+
* so every producer (the background-job runner, the scheduler's
|
|
55
|
+
* retries-exhausted alert, standalone data jobs) reports the same way and a
|
|
56
|
+
* new producer cannot invent a divergent one. Fire-and-forget: emission
|
|
57
|
+
* errors are logged, never thrown, because a notification failure must not
|
|
58
|
+
* break the job path that reports it.
|
|
59
|
+
*
|
|
60
|
+
* The dedupe scope is the most precise route identity the failure carries:
|
|
61
|
+
* the resolved connection, else the resolved profile, else the recomputed
|
|
62
|
+
* fallback. Failures carrying less precision can split an incident into an
|
|
63
|
+
* extra notification (never merge two incidents into one), and the split
|
|
64
|
+
* shrinks as more paths carry attribution.
|
|
65
|
+
*/
|
|
66
|
+
export function emitBackgroundFailureSignal(
|
|
67
|
+
report: BackgroundFailureReport,
|
|
68
|
+
): void {
|
|
69
|
+
const providerScope =
|
|
70
|
+
report.connectionName ?? report.profileName ?? report.fallbackProviderScope;
|
|
71
|
+
emitNotificationSignal({
|
|
72
|
+
sourceChannel: report.sourceChannel,
|
|
73
|
+
sourceContextId: report.sourceContextId,
|
|
74
|
+
sourceEventName: "activity.failed",
|
|
75
|
+
dedupeKey: activityFailedDedupeKey({
|
|
76
|
+
jobName: report.jobName,
|
|
77
|
+
errorKind: report.errorKind,
|
|
78
|
+
...(report.failureCode !== undefined
|
|
79
|
+
? { failureCode: report.failureCode }
|
|
80
|
+
: {}),
|
|
81
|
+
...(providerScope !== undefined ? { providerScope } : {}),
|
|
82
|
+
}),
|
|
83
|
+
contextPayload: {
|
|
84
|
+
jobName: report.displayName ?? report.jobName,
|
|
85
|
+
errorMessage: report.errorMessage,
|
|
86
|
+
errorKind: report.errorKind,
|
|
87
|
+
...(report.failureCode !== undefined
|
|
88
|
+
? { failureCode: report.failureCode }
|
|
89
|
+
: {}),
|
|
90
|
+
...(report.failureSummary !== undefined
|
|
91
|
+
? { failureSummary: report.failureSummary }
|
|
92
|
+
: {}),
|
|
93
|
+
...(report.errorCategory !== undefined
|
|
94
|
+
? { errorCategory: report.errorCategory }
|
|
95
|
+
: {}),
|
|
96
|
+
...(report.connectionName !== undefined
|
|
97
|
+
? { connectionName: report.connectionName }
|
|
98
|
+
: {}),
|
|
99
|
+
},
|
|
100
|
+
attentionHints: {
|
|
101
|
+
requiresAction: false,
|
|
102
|
+
urgency: "medium",
|
|
103
|
+
isAsyncBackground: true,
|
|
104
|
+
visibleInSourceNow: false,
|
|
105
|
+
},
|
|
106
|
+
}).catch((emitErr) => {
|
|
107
|
+
log.warn(
|
|
108
|
+
{
|
|
109
|
+
err: emitErr instanceof Error ? emitErr.message : String(emitErr),
|
|
110
|
+
jobName: report.jobName,
|
|
111
|
+
sourceContextId: report.sourceContextId,
|
|
112
|
+
},
|
|
113
|
+
"Failed to emit background-failure notification",
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -33,11 +33,10 @@ import {
|
|
|
33
33
|
} from "./deliveries-store.js";
|
|
34
34
|
import { resolveDestinations } from "./destination-resolver.js";
|
|
35
35
|
import {
|
|
36
|
-
|
|
36
|
+
buildQuestionAnswerActions,
|
|
37
37
|
buildToolApprovalSourceView,
|
|
38
38
|
parseGuardianQuestionPayload,
|
|
39
39
|
parseInteractiveApprovalPayload,
|
|
40
|
-
QUESTION_SKIP_ACTION_ID,
|
|
41
40
|
resolveGuardianInstructionModeFromPayload,
|
|
42
41
|
type ToolApprovalSourceView,
|
|
43
42
|
} from "./guardian-question-mode.js";
|
|
@@ -188,14 +187,9 @@ function resolveQuestionOptionsContext(
|
|
|
188
187
|
return {
|
|
189
188
|
approval: {
|
|
190
189
|
requestId,
|
|
191
|
-
actions:
|
|
192
|
-
...options.map((option, index) => ({
|
|
193
|
-
id: buildQuestionOptionActionId(index),
|
|
194
|
-
label: option.label,
|
|
195
|
-
})),
|
|
196
|
-
{ id: QUESTION_SKIP_ACTION_ID, label: "Skip" },
|
|
197
|
-
],
|
|
190
|
+
actions: buildQuestionAnswerActions(options),
|
|
198
191
|
plainTextFallback: `Reply "${code} <your answer>".`,
|
|
192
|
+
intent: "question",
|
|
199
193
|
},
|
|
200
194
|
};
|
|
201
195
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { normalizeTitle } from "../util/short-title.js";
|
|
13
|
+
import { truncate } from "../util/truncate.js";
|
|
13
14
|
import {
|
|
14
15
|
accessRequestCardTitle,
|
|
15
16
|
buildAccessRequestContractText,
|
|
@@ -41,6 +42,33 @@ import type { NotificationChannel, RenderedChannelCopy } from "./types.js";
|
|
|
41
42
|
|
|
42
43
|
type CopyTemplate = (payload: Record<string, unknown>) => RenderedChannelCopy;
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Failure prose for an `activity.failed` payload with no authored
|
|
47
|
+
* classification message. The classified kind supplies the readable part,
|
|
48
|
+
* and the raw error text is appended only when it reads as prose: a
|
|
49
|
+
* stack-shaped or constant-shaped message (PROVIDER_BILLING and friends)
|
|
50
|
+
* says nothing to the person reading the notification and never lands in
|
|
51
|
+
* the body.
|
|
52
|
+
*/
|
|
53
|
+
function describeUnclassifiedFailure(payload: Record<string, unknown>): string {
|
|
54
|
+
const kind = str(payload.errorKind, "exception");
|
|
55
|
+
const opening =
|
|
56
|
+
kind === "timeout"
|
|
57
|
+
? "It ran out of time before finishing."
|
|
58
|
+
: kind === "model_provider"
|
|
59
|
+
? "The model provider did not respond."
|
|
60
|
+
: "It stopped with an error.";
|
|
61
|
+
const raw =
|
|
62
|
+
typeof payload.errorMessage === "string"
|
|
63
|
+
? payload.errorMessage.replace(/\s+/g, " ").trim()
|
|
64
|
+
: "";
|
|
65
|
+
const readable =
|
|
66
|
+
raw.length > 0 &&
|
|
67
|
+
raw.length <= 200 &&
|
|
68
|
+
!/[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+/.test(raw);
|
|
69
|
+
return readable ? `${opening} ${raw}` : opening;
|
|
70
|
+
}
|
|
71
|
+
|
|
44
72
|
function str(value: unknown, fallback: string): string {
|
|
45
73
|
if (typeof value === "string" && value.length > 0) {
|
|
46
74
|
return value;
|
|
@@ -335,16 +363,21 @@ const TEMPLATES: Partial<Record<NotificationSourceEventName, CopyTemplate>> = {
|
|
|
335
363
|
|
|
336
364
|
"activity.failed": (payload) => {
|
|
337
365
|
const jobName = str(payload.jobName, "background job");
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
366
|
+
// The classifier's authored user-facing message rides the payload as
|
|
367
|
+
// `failureSummary` when the failing turn carried its classification;
|
|
368
|
+
// it already says what happened and what to do about it.
|
|
369
|
+
// Authored messages are short by construction, but the classifier's
|
|
370
|
+
// fallback branches can embed provider error text, so bound the body
|
|
371
|
+
// like any other untrusted payload string.
|
|
372
|
+
const summary = truncate(
|
|
373
|
+
typeof payload.failureSummary === "string"
|
|
374
|
+
? payload.failureSummary.trim()
|
|
375
|
+
: "",
|
|
376
|
+
300,
|
|
377
|
+
);
|
|
345
378
|
return {
|
|
346
379
|
title: `Background job failed: ${jobName}`,
|
|
347
|
-
body:
|
|
380
|
+
body: summary !== "" ? summary : describeUnclassifiedFailure(payload),
|
|
348
381
|
};
|
|
349
382
|
},
|
|
350
383
|
|
|
@@ -48,8 +48,11 @@ import { composeFallbackCopy, resolveTitle } from "./copy-composer.js";
|
|
|
48
48
|
import { createDecision } from "./decisions-store.js";
|
|
49
49
|
import {
|
|
50
50
|
buildGuardianRequestCodeInstruction,
|
|
51
|
+
buildQuestionDeliveryText,
|
|
51
52
|
hasGuardianRequestCodeInstruction,
|
|
53
|
+
parseGuardianQuestionPayload,
|
|
52
54
|
parseInteractiveApprovalPayload,
|
|
55
|
+
resolveGuardianInstructionModeFromPayload,
|
|
53
56
|
resolveGuardianQuestionInstructionMode,
|
|
54
57
|
stripConflictingGuardianRequestInstructions,
|
|
55
58
|
stripGuardianRequestCodeInstructions,
|
|
@@ -744,6 +747,55 @@ function ensureSeedContentBlocks(
|
|
|
744
747
|
};
|
|
745
748
|
}
|
|
746
749
|
|
|
750
|
+
/**
|
|
751
|
+
* Pin a question's channel text to the question itself.
|
|
752
|
+
*
|
|
753
|
+
* Every other notification's copy is composed here, and for most of them a
|
|
754
|
+
* paraphrase is an improvement. A question is the exception: the guardian is
|
|
755
|
+
* being asked to choose between these words, and the composed copy replaces
|
|
756
|
+
* `title` / `body` / `deliveryText` wholesale, so a summary of the question
|
|
757
|
+
* arrives where the question should be and the options never appear at all.
|
|
758
|
+
* Runs on every decision path for the same reason the seed-block guard does.
|
|
759
|
+
*
|
|
760
|
+
* Scoped to answer mode. A `pending_question` carrying a tool name is a voice
|
|
761
|
+
* tool approval, where composed copy is the right thing.
|
|
762
|
+
*/
|
|
763
|
+
function pinQuestionDeliveryCopy(
|
|
764
|
+
decision: NotificationDecision,
|
|
765
|
+
signal: NotificationSignal,
|
|
766
|
+
): NotificationDecision {
|
|
767
|
+
if (signal.sourceEventName !== "guardian.question") {
|
|
768
|
+
return decision;
|
|
769
|
+
}
|
|
770
|
+
const parsed = parseGuardianQuestionPayload(signal.contextPayload);
|
|
771
|
+
if (
|
|
772
|
+
!parsed ||
|
|
773
|
+
parsed.requestKind !== "pending_question" ||
|
|
774
|
+
resolveGuardianInstructionModeFromPayload(parsed).mode !== "answer"
|
|
775
|
+
) {
|
|
776
|
+
return decision;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const text = buildQuestionDeliveryText(parsed);
|
|
780
|
+
const nextCopy: Partial<Record<NotificationChannel, RenderedChannelCopy>> = {
|
|
781
|
+
...decision.renderedCopy,
|
|
782
|
+
};
|
|
783
|
+
for (const channel of Object.keys(nextCopy) as NotificationChannel[]) {
|
|
784
|
+
const copy = nextCopy[channel];
|
|
785
|
+
if (!copy) {
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
nextCopy[channel] = {
|
|
789
|
+
...copy,
|
|
790
|
+
body: text,
|
|
791
|
+
deliveryText: text,
|
|
792
|
+
conversationSeedMessage: text,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
return { ...decision, renderedCopy: nextCopy };
|
|
797
|
+
}
|
|
798
|
+
|
|
747
799
|
/**
|
|
748
800
|
* Tool-approval and tool-grant notifications need a Surface card with
|
|
749
801
|
* Approve/Reject buttons on all decision paths.
|
|
@@ -943,6 +995,7 @@ function buildPassThroughDecision(params: {
|
|
|
943
995
|
};
|
|
944
996
|
decision = enforceGuardianRequestCode(decision, signal);
|
|
945
997
|
decision = enforceToolApprovalSeedBlocks(decision, signal);
|
|
998
|
+
decision = pinQuestionDeliveryCopy(decision, signal);
|
|
946
999
|
decision = enforceAccessRequestInstructions(decision, signal);
|
|
947
1000
|
decision = enforceGuardianRequestConversationAffinity(decision, signal);
|
|
948
1001
|
decision = enforceConversationAffinity(
|
|
@@ -1068,6 +1121,7 @@ export async function evaluateSignal(
|
|
|
1068
1121
|
let decision = buildFallbackDecision(signal, availableChannels);
|
|
1069
1122
|
decision = enforceGuardianRequestCode(decision, signal);
|
|
1070
1123
|
decision = enforceToolApprovalSeedBlocks(decision, signal);
|
|
1124
|
+
decision = pinQuestionDeliveryCopy(decision, signal);
|
|
1071
1125
|
decision = enforceAccessRequestInstructions(decision, signal);
|
|
1072
1126
|
decision = enforceGuardianRequestConversationAffinity(decision, signal);
|
|
1073
1127
|
decision = enforceConversationAffinity(
|
|
@@ -1098,6 +1152,7 @@ export async function evaluateSignal(
|
|
|
1098
1152
|
|
|
1099
1153
|
decision = enforceGuardianRequestCode(decision, signal);
|
|
1100
1154
|
decision = enforceToolApprovalSeedBlocks(decision, signal);
|
|
1155
|
+
decision = pinQuestionDeliveryCopy(decision, signal);
|
|
1101
1156
|
decision = enforceAccessRequestInstructions(decision, signal);
|
|
1102
1157
|
decision = enforceGuardianRequestConversationAffinity(decision, signal);
|
|
1103
1158
|
decision = enforceConversationAffinity(
|
|
@@ -66,10 +66,9 @@ export function resolveDestinations(
|
|
|
66
66
|
continue;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
switch (channel as NotificationChannel) {
|
|
69
|
+
// `isNotificationDeliverable` is a type predicate, so past this point
|
|
70
|
+
// `channel` is a NotificationChannel and the switch below is exhaustive.
|
|
71
|
+
switch (channel) {
|
|
73
72
|
case "vellum": {
|
|
74
73
|
// Vellum delivery is local — no external endpoint required.
|
|
75
74
|
// Include the guardianPrincipalId so the adapter can annotate
|
|
@@ -155,6 +154,44 @@ export function resolveDestinations(
|
|
|
155
154
|
);
|
|
156
155
|
break;
|
|
157
156
|
}
|
|
157
|
+
case "discord": {
|
|
158
|
+
const guardian = resolveGuardian(guardians, "discord");
|
|
159
|
+
// The endpoint is the guardian's user snowflake: the person, never a
|
|
160
|
+
// stored room id. User and channel snowflakes are indistinguishable
|
|
161
|
+
// bare numbers, so the adapter resolves the DM channel from the
|
|
162
|
+
// person at send time and a guild room can never become a delivery
|
|
163
|
+
// target. The binding context reuses the conversation the guardian
|
|
164
|
+
// verified in, which the verification intercept scopes to the DM
|
|
165
|
+
// lane; delivery stays DM-safe regardless, since it never reads
|
|
166
|
+
// this id.
|
|
167
|
+
if (guardian?.address) {
|
|
168
|
+
result.set("discord", {
|
|
169
|
+
channel: "discord",
|
|
170
|
+
endpoint: guardian.address,
|
|
171
|
+
metadata: {
|
|
172
|
+
externalUserId: guardian.address,
|
|
173
|
+
},
|
|
174
|
+
...(guardian.externalChatId
|
|
175
|
+
? {
|
|
176
|
+
bindingContext: {
|
|
177
|
+
sourceChannel: "discord" as const,
|
|
178
|
+
externalChatId: guardian.externalChatId,
|
|
179
|
+
externalUserId: guardian.address,
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
: {}),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
log.debug(
|
|
186
|
+
{
|
|
187
|
+
channel: "discord",
|
|
188
|
+
source: "guardian-delivery",
|
|
189
|
+
hasEndpoint: !!guardian?.address,
|
|
190
|
+
},
|
|
191
|
+
"destination resolved",
|
|
192
|
+
);
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
158
195
|
case "platform": {
|
|
159
196
|
// Platform delivery goes through the daemon's VellumPlatformClient —
|
|
160
197
|
// no external binding needed. Include guardianPrincipalId so the
|
|
@@ -180,7 +217,10 @@ export function resolveDestinations(
|
|
|
180
217
|
break;
|
|
181
218
|
}
|
|
182
219
|
default: {
|
|
183
|
-
//
|
|
220
|
+
// Exhaustive over NotificationChannel: a channel whose policy turns
|
|
221
|
+
// deliveryEnabled on must answer addressing here at compile time,
|
|
222
|
+
// because a silent skip is how a half-wired channel disappears.
|
|
223
|
+
channel satisfies never;
|
|
184
224
|
break;
|
|
185
225
|
}
|
|
186
226
|
}
|
|
@@ -22,6 +22,7 @@ import { isPlatformClientConfigured } from "../platform/client.js";
|
|
|
22
22
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
23
23
|
import { getLogger } from "../util/logger.js";
|
|
24
24
|
import { withSqliteRetry } from "../util/sqlite-retry.js";
|
|
25
|
+
import { DiscordAdapter } from "./adapters/discord.js";
|
|
25
26
|
import { VellumAdapter } from "./adapters/macos.js";
|
|
26
27
|
import { PlatformPushAdapter } from "./adapters/platform.js";
|
|
27
28
|
import { SlackAdapter } from "./adapters/slack.js";
|
|
@@ -64,6 +65,7 @@ export function getBroadcaster(): NotificationBroadcaster {
|
|
|
64
65
|
new VellumAdapter(broadcastMessage),
|
|
65
66
|
new TelegramAdapter(),
|
|
66
67
|
new SlackAdapter(),
|
|
68
|
+
new DiscordAdapter(),
|
|
67
69
|
new PlatformPushAdapter(),
|
|
68
70
|
]);
|
|
69
71
|
|
|
@@ -117,9 +119,6 @@ export async function getConnectedChannels(): Promise<NotificationChannel[]> {
|
|
|
117
119
|
// unreachable, so binding-based connectivity falls back to the local read.
|
|
118
120
|
const guardians = await getGuardianDelivery();
|
|
119
121
|
|
|
120
|
-
// getDeliverableChannels() returns ChannelId[] but every returned channel
|
|
121
|
-
// has deliveryEnabled: true, making it a valid NotificationChannel at
|
|
122
|
-
// runtime. We iterate over the broad type and narrow via the switch.
|
|
123
122
|
for (const channel of getDeliverableChannels()) {
|
|
124
123
|
switch (channel) {
|
|
125
124
|
case "vellum":
|
|
@@ -153,9 +152,24 @@ export async function getConnectedChannels(): Promise<NotificationChannel[]> {
|
|
|
153
152
|
}
|
|
154
153
|
break;
|
|
155
154
|
}
|
|
155
|
+
case "discord": {
|
|
156
|
+
// Connected when a verified guardian binding names a user to DM,
|
|
157
|
+
// mirroring destination-resolver: the person is the endpoint, and
|
|
158
|
+
// the adapter resolves the DM channel from them at send time.
|
|
159
|
+
const guardian = guardians
|
|
160
|
+
? guardianForChannel(guardians, channel)
|
|
161
|
+
: undefined;
|
|
162
|
+
if (guardian?.address) {
|
|
163
|
+
channels.push(channel);
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
156
167
|
default:
|
|
157
|
-
//
|
|
158
|
-
//
|
|
168
|
+
// Exhaustive over NotificationChannel: a channel whose policy turns
|
|
169
|
+
// deliveryEnabled on must answer connectivity here at compile time.
|
|
170
|
+
// A silent skip is how a half-wired channel disappears without a
|
|
171
|
+
// trace, so the compiler holds the door instead.
|
|
172
|
+
channel satisfies never;
|
|
159
173
|
break;
|
|
160
174
|
}
|
|
161
175
|
}
|
|
@@ -250,6 +250,63 @@ export function buildQuestionOptionActionId(index: number): string {
|
|
|
250
250
|
return `answer_${index}`;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
/**
|
|
254
|
+
* The full answer action set for a question's options: one action per option
|
|
255
|
+
* in the interaction's own order, then an explicit skip.
|
|
256
|
+
*
|
|
257
|
+
* Shared because a question is rendered twice from the same options, once as
|
|
258
|
+
* an approval-metadata action set for a channel transport and once as card
|
|
259
|
+
* actions on the in-app surface, and the two must agree: the resolver maps
|
|
260
|
+
* the index back to the pending interaction, so a divergence answers the
|
|
261
|
+
* wrong option rather than failing.
|
|
262
|
+
*/
|
|
263
|
+
/**
|
|
264
|
+
* The message text for a question that must be answered: the question, its
|
|
265
|
+
* options numbered as they are ordered, and how to reply by reference code.
|
|
266
|
+
*
|
|
267
|
+
* Deterministic on purpose. A notification's channel copy is otherwise
|
|
268
|
+
* composed by the decision engine, and a question is the one payload that
|
|
269
|
+
* cannot survive being paraphrased: the guardian is being asked to choose
|
|
270
|
+
* between these words, so the words have to arrive.
|
|
271
|
+
*/
|
|
272
|
+
export function buildQuestionDeliveryText(p: {
|
|
273
|
+
questionText: string;
|
|
274
|
+
requestCode?: string;
|
|
275
|
+
options?: readonly { label: string }[];
|
|
276
|
+
}): string {
|
|
277
|
+
const parts = [p.questionText];
|
|
278
|
+
const options = p.options ?? [];
|
|
279
|
+
if (options.length > 0) {
|
|
280
|
+
parts.push(
|
|
281
|
+
options
|
|
282
|
+
.map((option, index) => `${index + 1}. ${option.label}`)
|
|
283
|
+
.join("\n"),
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
const requestCode = p.requestCode?.trim();
|
|
287
|
+
if (requestCode) {
|
|
288
|
+
parts.push(
|
|
289
|
+
buildGuardianRequestCodeInstruction(requestCode.toUpperCase(), "answer"),
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
return parts.join("\n\n");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function buildQuestionAnswerActions(
|
|
296
|
+
options: readonly { label: string }[],
|
|
297
|
+
): Array<{ id: string; label: string }> {
|
|
298
|
+
if (options.length === 0) {
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
return [
|
|
302
|
+
...options.map((option, index) => ({
|
|
303
|
+
id: buildQuestionOptionActionId(index),
|
|
304
|
+
label: option.label,
|
|
305
|
+
})),
|
|
306
|
+
{ id: QUESTION_SKIP_ACTION_ID, label: "Skip" },
|
|
307
|
+
];
|
|
308
|
+
}
|
|
309
|
+
|
|
253
310
|
export type QuestionAnswerSelection =
|
|
254
311
|
| { kind: "option"; index: number }
|
|
255
312
|
| { kind: "skip" };
|