@vellumai/assistant 0.11.6 → 0.11.7-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +31 -19
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +3 -3
- package/docs/flux-turn-detection-spike.md +8 -7
- package/docs/runbook-trusted-contacts.md +14 -0
- package/docs/stt-provider-onboarding.md +8 -4
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
- package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
- package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
- package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
- package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
- package/node_modules/@vellumai/environments/src/shell.ts +65 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
- package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
- package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/openapi.yaml +419 -55
- package/package.json +4 -1
- package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
- package/src/__tests__/approval-card-edit.test.ts +153 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
- package/src/__tests__/available-skills.test.ts +54 -0
- package/src/__tests__/avatar-identity-sync.test.ts +5 -1
- package/src/__tests__/catalog-install-normalize.test.ts +9 -1
- package/src/__tests__/channel-policy.test.ts +1 -3
- package/src/__tests__/channel-readiness-discord.test.ts +9 -70
- package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
- package/src/__tests__/config-loader-backfill.test.ts +69 -0
- package/src/__tests__/config-schema.test.ts +2 -3
- package/src/__tests__/conversation-agent-loop.test.ts +238 -0
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +44 -0
- package/src/__tests__/conversation-evictor.test.ts +19 -0
- package/src/__tests__/conversation-fork-referential.test.ts +73 -63
- package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
- package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
- package/src/__tests__/conversation-slash.test.ts +9 -0
- package/src/__tests__/conversation-title-service.test.ts +97 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
- package/src/__tests__/credential-broker-server-use.test.ts +0 -218
- package/src/__tests__/credential-broker.test.ts +43 -420
- package/src/__tests__/credential-routes.test.ts +17 -1
- package/src/__tests__/credential-security-invariants.test.ts +8 -9
- package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
- package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
- package/src/__tests__/discord-channel-config.test.ts +237 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
- package/src/__tests__/fallback-breaker.test.ts +1335 -0
- package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
- package/src/__tests__/image-conversion.test.ts +38 -4
- package/src/__tests__/install-skill-routing.test.ts +25 -0
- package/src/__tests__/llm-resolver.test.ts +74 -0
- package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
- package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
- package/src/__tests__/managed-profile-guard.test.ts +28 -0
- package/src/__tests__/managed-speech-defaults.test.ts +196 -1
- package/src/__tests__/mcp-cli.test.ts +23 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
- package/src/__tests__/media-stream-stt-session.test.ts +2 -0
- package/src/__tests__/notification-decision-fallback.test.ts +106 -0
- package/src/__tests__/notification-discord-adapter.test.ts +162 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
- package/src/__tests__/persist-unsendable-image.test.ts +3 -3
- package/src/__tests__/platform.test.ts +19 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/provider-usage-tracking.test.ts +56 -0
- package/src/__tests__/require-fresh-approval.test.ts +5 -2
- package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
- package/src/__tests__/schedule-retry.test.ts +183 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
- package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
- package/src/__tests__/skill-load-tool.test.ts +34 -0
- package/src/__tests__/skill-tool-factory.test.ts +6 -1
- package/src/__tests__/skill-tool-manifest.test.ts +20 -0
- package/src/__tests__/skills.test.ts +25 -0
- package/src/__tests__/subagent-manager-notify.test.ts +38 -0
- package/src/__tests__/terminal-tools.test.ts +2 -0
- package/src/__tests__/tool-approval-handler.test.ts +146 -2
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
- package/src/__tests__/tool-audit.test.ts +111 -40
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
- package/src/__tests__/ui-shape-teaching.test.ts +1 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
- package/src/__tests__/voice-config-update.test.ts +43 -0
- package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
- package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
- package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
- package/src/agent/loop.ts +31 -2
- package/src/approvals/guardian-card-withdrawal.ts +22 -0
- package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
- package/src/avatar/avatar-manifest.ts +48 -100
- package/src/avatar/avatar-store.ts +7 -7
- package/src/avatar/ensure-raster.ts +146 -0
- package/src/avatar/resvg-lazy.test.ts +0 -1
- package/src/avatar/traits-png-sync.ts +9 -10
- package/src/background-wake/wake-intent-hooks.test.ts +0 -1
- package/src/backup/__tests__/paths.test.ts +6 -2
- package/src/backup/paths.ts +6 -4
- package/src/bundler/app-compiler.ts +2 -2
- package/src/bundler/compiler-tools.test.ts +36 -0
- package/src/bundler/compiler-tools.ts +54 -18
- package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +20 -0
- package/src/calls/media-stream-stt-session.ts +2 -1
- package/src/calls/telephony-synthesis-language.ts +13 -5
- package/src/calls/voice-session-bridge.ts +6 -4
- package/src/channels/config.ts +23 -12
- package/src/cli/AGENTS.md +3 -1
- package/src/cli/commands/backup.help.ts +10 -13
- package/src/cli/commands/contacts.help.ts +16 -4
- package/src/cli/commands/contacts.ts +5 -1
- package/src/cli/commands/credentials.help.ts +1 -1
- package/src/cli/commands/inference-providers.ts +18 -53
- package/src/cli/commands/mcp.help.ts +6 -5
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/memory/memory-retrospective.ts +1 -0
- package/src/cli/commands/stt.help.ts +2 -2
- package/src/config/__tests__/backup-schema.test.ts +1 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
- package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
- package/src/config/bundled-skills/settings/TOOLS.json +11 -5
- package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
- package/src/config/default-profile-catalog.ts +195 -10
- package/src/config/default-profile-names.ts +41 -0
- package/src/config/feature-flag-registry.json +18 -2
- package/src/config/inference-profile-validation.ts +11 -2
- package/src/config/llm-resolver.ts +25 -0
- package/src/config/loader.ts +3 -0
- package/src/config/managed-speech-defaults.ts +160 -10
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
- package/src/config/schemas/__tests__/stt.test.ts +94 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/channels.ts +32 -0
- package/src/config/schemas/live-voice.ts +13 -4
- package/src/config/schemas/llm.ts +259 -11
- package/src/config/schemas/mcp.ts +10 -2
- package/src/config/schemas/memory-retrospective.ts +1 -19
- package/src/config/schemas/services.ts +24 -0
- package/src/config/schemas/stt.ts +128 -5
- package/src/config/seed-inference-profiles.ts +32 -3
- package/src/config/skill-state.ts +5 -0
- package/src/config/skills.ts +21 -0
- package/src/contacts/notify-contacts-changed.ts +5 -2
- package/src/context/compactor.ts +74 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
- package/src/daemon/config-watcher.ts +2 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
- package/src/daemon/conversation-agent-loop.ts +86 -10
- package/src/daemon/conversation-error.ts +25 -0
- package/src/daemon/conversation-evictor.ts +2 -4
- package/src/daemon/conversation-runtime-assembly.ts +19 -7
- package/src/daemon/conversation-skill-tools.ts +5 -1
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-store.ts +8 -1
- package/src/daemon/conversation-tool-setup.ts +37 -0
- package/src/daemon/conversation.ts +0 -4
- package/src/daemon/handlers/channel-config-result.ts +30 -0
- package/src/daemon/handlers/config-discord-channel.ts +301 -0
- package/src/daemon/handlers/config-ingress.ts +9 -0
- package/src/daemon/handlers/config-slack-channel.ts +13 -15
- package/src/daemon/handlers/config-telegram.ts +2 -5
- package/src/daemon/handlers/conversations.ts +0 -2
- package/src/daemon/handlers/skills.ts +25 -7
- package/src/daemon/lifecycle.ts +3 -1
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/tool-setup-types.ts +5 -3
- package/src/daemon/trust-context-types.test.ts +33 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
- package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
- package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
- package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
- package/src/live-voice/__tests__/protocol.test.ts +224 -0
- package/src/live-voice/live-voice-credential-preflight.ts +5 -1
- package/src/live-voice/live-voice-session-manager.ts +39 -2
- package/src/live-voice/live-voice-session.ts +213 -8
- package/src/live-voice/protocol.ts +187 -1
- package/src/messaging/providers/discord/api.ts +2 -1
- package/src/messaging/providers/discord/withdraw.ts +62 -0
- package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
- package/src/monitoring/__tests__/process-memory.test.ts +34 -1
- package/src/monitoring/db-integrity-sample.ts +1 -0
- package/src/monitoring/file-descriptors.ts +39 -6
- package/src/monitoring/process-memory.ts +37 -5
- package/src/monitoring/resource-sampler.ts +25 -8
- package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
- package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
- package/src/notifications/__tests__/connected-channels.test.ts +12 -0
- package/src/notifications/__tests__/copy-composer.test.ts +83 -0
- package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
- package/src/notifications/activity-failed-dedupe.ts +95 -0
- package/src/notifications/adapters/discord.ts +132 -0
- package/src/notifications/adapters/shared.ts +16 -0
- package/src/notifications/adapters/slack.ts +6 -1
- package/src/notifications/adapters/telegram.ts +5 -8
- package/src/notifications/approval-card-data.ts +94 -1
- package/src/notifications/background-failure-signal.ts +116 -0
- package/src/notifications/broadcaster.ts +3 -9
- package/src/notifications/copy-composer.ts +41 -8
- package/src/notifications/decision-engine.ts +55 -0
- package/src/notifications/destination-resolver.ts +45 -5
- package/src/notifications/emit-signal.ts +19 -5
- package/src/notifications/guardian-question-mode.ts +57 -0
- package/src/notifications/types.ts +2 -8
- package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
- package/src/oauth/seed-providers.ts +261 -0
- package/src/permissions/checker.test.ts +21 -0
- package/src/permissions/checker.ts +6 -0
- package/src/permissions/gateway-threshold-reader.test.ts +184 -0
- package/src/permissions/gateway-threshold-reader.ts +144 -10
- package/src/permissions/prompter.ts +0 -6
- package/src/permissions/types.ts +5 -4
- package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
- package/src/persistence/bulk-write-gate.ts +9 -10
- package/src/persistence/conversation-crud.ts +85 -133
- package/src/persistence/conversation-title-service.ts +43 -29
- package/src/persistence/embeddings/embedding-local.ts +14 -85
- package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
- package/src/persistence/lifecycle-events-store.test.ts +50 -0
- package/src/persistence/lifecycle-events-store.ts +75 -5
- package/src/platform/platform-patch-queue.test.ts +368 -0
- package/src/platform/platform-patch-queue.ts +190 -0
- package/src/platform/sync-avatar.test.ts +527 -0
- package/src/platform/sync-avatar.ts +209 -0
- package/src/platform/sync-identity.test.ts +75 -0
- package/src/platform/sync-identity.ts +17 -110
- package/src/plugin-api/model-profiles.test.ts +2 -2
- package/src/plugin-api/model-profiles.ts +7 -7
- package/src/plugin-api/transcription-session.ts +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +4 -0
- package/src/plugins/defaults/image-recovery/recover.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
- package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
- package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +7 -0
- package/src/providers/__tests__/retry-callsite.test.ts +55 -0
- package/src/providers/connection-resolution.ts +2 -17
- package/src/providers/fallback-breaker.ts +389 -0
- package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
- package/src/providers/inference/adapter-factory.ts +231 -3
- package/src/providers/inference/auth.ts +1 -0
- package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
- package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
- package/src/providers/inference/resolve-auth.ts +1 -1
- package/src/providers/media-resolve.ts +2 -3
- package/src/providers/model-catalog.ts +75 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
- package/src/providers/openai/api-error-normalization.ts +13 -0
- package/src/providers/openai/chat-completions-provider.ts +84 -0
- package/src/providers/opencode/client.test.ts +150 -0
- package/src/providers/opencode/client.ts +61 -0
- package/src/providers/provider-resolvability.ts +20 -0
- package/src/providers/retry.ts +956 -68
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
- package/src/providers/speech-to-text/provider-catalog.ts +191 -3
- package/src/providers/speech-to-text/resolve.ts +132 -24
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
- package/src/providers/types.ts +39 -0
- package/src/providers/usage-tracking.ts +12 -1
- package/src/runtime/AGENTS.md +1 -2
- package/src/runtime/__tests__/agent-wake.test.ts +105 -3
- package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
- package/src/runtime/agent-wake.ts +44 -5
- package/src/runtime/background-job-runner.ts +68 -50
- package/src/runtime/channel-readiness-service.ts +0 -39
- package/src/runtime/http-server.ts +12 -4
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
- package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
- package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
- package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
- package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
- package/src/runtime/routes/approval-routes.ts +0 -12
- package/src/runtime/routes/avatar-routes.ts +17 -30
- package/src/runtime/routes/backup-routes.ts +2 -2
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
- package/src/runtime/routes/contact-prompt-routes.ts +65 -6
- package/src/runtime/routes/contact-routes.ts +20 -15
- package/src/runtime/routes/conversation-query-routes.ts +98 -10
- package/src/runtime/routes/conversation-routes.ts +2 -2
- package/src/runtime/routes/credential-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +12 -9
- package/src/runtime/routes/inbound-message-handler.ts +21 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
- package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
- package/src/runtime/routes/inference-profiles-routes.ts +12 -7
- package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
- package/src/runtime/routes/inference-send-routes.ts +2 -2
- package/src/runtime/routes/ingress-status-routes.ts +2 -2
- package/src/runtime/routes/integrations/discord.ts +119 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
- package/src/runtime/routes/mcp-auth-routes.ts +12 -6
- package/src/runtime/routes/oauth-pending-flows.ts +136 -0
- package/src/runtime/routes/secret-routes.ts +7 -0
- package/src/runtime/routes/stt-routes.ts +38 -6
- package/src/runtime/routes/wake-conversation-routes.ts +8 -1
- package/src/runtime/routes/watch-routes.ts +9 -5
- package/src/runtime/sync/resource-sync-events.ts +2 -0
- package/src/runtime/trust-verdict-consumer.ts +3 -3
- package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
- package/src/schedule/retry-policy.ts +8 -8
- package/src/schedule/run-script.ts +14 -23
- package/src/schedule/schedule-recovery.ts +0 -1
- package/src/schedule/scheduler.ts +89 -51
- package/src/schedule/worker.ts +57 -0
- package/src/security/__tests__/openai-device-auth.test.ts +355 -0
- package/src/security/oauth2.ts +7 -0
- package/src/security/openai-device-auth.ts +364 -0
- package/src/skills/available-skills.ts +10 -1
- package/src/skills/catalog-install.ts +19 -1
- package/src/skills/inline-command-runner.ts +6 -2
- package/src/skills/platform-compatibility.test.ts +49 -0
- package/src/skills/platform-compatibility.ts +65 -0
- package/src/skills/tool-manifest.ts +20 -0
- package/src/stt/__tests__/roles.test.ts +152 -0
- package/src/stt/daemon-batch-transcriber.ts +4 -1
- package/src/stt/roles.ts +180 -0
- package/src/stt/stt-stream-session.ts +3 -2
- package/src/stt/types.ts +33 -3
- package/src/subagent/manager.ts +12 -0
- package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
- package/src/telemetry/live-voice-funnel.ts +12 -2
- package/src/telemetry/telemetry-wire.generated.ts +6 -0
- package/src/telemetry/tool-audit.ts +83 -30
- package/src/telemetry/tool-executed-events-store.test.ts +0 -1
- package/src/telemetry/turn-outcome.ts +75 -4
- package/src/tools/client-os.ts +115 -0
- package/src/tools/computer-use/definitions.ts +11 -7
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
- package/src/tools/credentials/broker-types.ts +0 -71
- package/src/tools/credentials/broker.ts +6 -201
- package/src/tools/executor.ts +2 -8
- package/src/tools/host-shell.test.ts +75 -0
- package/src/tools/host-shell.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +15 -44
- package/src/tools/mcp/mcp-tool-factory.ts +44 -9
- package/src/tools/permission-checker.ts +73 -21
- package/src/tools/policy-context.ts +1 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/find-similar-skills.ts +10 -2
- package/src/tools/skills/load.ts +41 -6
- package/src/tools/skills/sandbox-runner.ts +8 -16
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
- package/src/tools/terminal/safe-env.ts +39 -9
- package/src/tools/terminal/shell.ts +12 -31
- package/src/tools/tool-approval-handler.ts +64 -10
- package/src/tools/tool-types.ts +10 -0
- package/src/tools/types.ts +19 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
- package/src/tools/workflows/manage-workflows.ts +2 -2
- package/src/tools/workflows/run-workflow.test.ts +2 -1
- package/src/util/__tests__/file-use.test.ts +61 -0
- package/src/util/__tests__/process-table.test.ts +66 -0
- package/src/util/__tests__/process-tree.test.ts +46 -0
- package/src/util/errors.ts +1 -0
- package/src/util/file-use.ts +91 -0
- package/src/util/host-process.test.ts +17 -0
- package/src/util/host-process.ts +75 -0
- package/src/util/image-conversion.ts +39 -7
- package/src/util/platform.ts +32 -13
- package/src/util/process-table.ts +249 -0
- package/src/util/process-tree.ts +85 -129
- package/src/util/provider-error-patterns.ts +20 -0
- package/src/util/spawn.ts +9 -2
- package/src/workspace/git-service.ts +4 -14
- package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
- package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
- package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
- package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/permission-types.test.ts +0 -17
- package/src/channels/gateway-discord-admission.ts +0 -32
- package/src/persistence/fork-message-copy.ts +0 -239
- package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
- package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
- package/src/security/oauth2-device-code.ts +0 -312
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kind derivation is the single reader of the field encodings, so its
|
|
3
|
+
* matrix is the compatibility contract: every shape an unstamped replayed
|
|
4
|
+
* retry payload can carry must classify exactly as the consumers that read
|
|
5
|
+
* those fields directly would classify it.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, test } from "bun:test";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
inboundEventRefersToAnotherMessage,
|
|
11
|
+
resolveInboundEventKind,
|
|
12
|
+
} from "../inbound-event-kind.js";
|
|
13
|
+
|
|
14
|
+
describe("resolveInboundEventKind", () => {
|
|
15
|
+
test("a stamped kind wins over every legacy field", () => {
|
|
16
|
+
expect(
|
|
17
|
+
resolveInboundEventKind({
|
|
18
|
+
eventKind: "edit",
|
|
19
|
+
callbackData: "message_deleted",
|
|
20
|
+
}),
|
|
21
|
+
).toBe("edit");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("an unrecognized stamp falls back to legacy derivation", () => {
|
|
25
|
+
expect(
|
|
26
|
+
resolveInboundEventKind({ eventKind: "future-kind", isEdit: true }),
|
|
27
|
+
).toBe("edit");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("legacy isEdit classifies as edit", () => {
|
|
31
|
+
expect(resolveInboundEventKind({ isEdit: true })).toBe("edit");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("the delete sentinel classifies as delete", () => {
|
|
35
|
+
expect(resolveInboundEventKind({ callbackData: "message_deleted" })).toBe(
|
|
36
|
+
"delete",
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("reaction prefixes classify as reaction, both directions", () => {
|
|
41
|
+
expect(resolveInboundEventKind({ callbackData: "reaction:+1" })).toBe(
|
|
42
|
+
"reaction",
|
|
43
|
+
);
|
|
44
|
+
expect(
|
|
45
|
+
resolveInboundEventKind({ callbackData: "reaction_removed:+1" }),
|
|
46
|
+
).toBe("reaction");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("any other callbackData classifies as button", () => {
|
|
50
|
+
expect(
|
|
51
|
+
resolveInboundEventKind({ callbackData: "apr:req-1:approve_once" }),
|
|
52
|
+
).toBe("button");
|
|
53
|
+
expect(resolveInboundEventKind({ callbackData: "some-action-id" })).toBe(
|
|
54
|
+
"button",
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("an empty callbackData is not a family", () => {
|
|
59
|
+
// Mirrors the old hasCallbackData predicate, which required length > 0.
|
|
60
|
+
expect(resolveInboundEventKind({ callbackData: "" })).toBe("message");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("callbackQueryId alone classifies as button", () => {
|
|
64
|
+
expect(resolveInboundEventKind({ callbackQueryId: "cbq-1" })).toBe(
|
|
65
|
+
"button",
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("nothing stated is a plain message", () => {
|
|
70
|
+
expect(resolveInboundEventKind({})).toBe("message");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("inboundEventRefersToAnotherMessage", () => {
|
|
75
|
+
test("only a plain message stands alone", () => {
|
|
76
|
+
expect(inboundEventRefersToAnotherMessage("message")).toBe(false);
|
|
77
|
+
expect(inboundEventRefersToAnotherMessage("edit")).toBe(true);
|
|
78
|
+
expect(inboundEventRefersToAnotherMessage("delete")).toBe(true);
|
|
79
|
+
expect(inboundEventRefersToAnotherMessage("reaction")).toBe(true);
|
|
80
|
+
expect(inboundEventRefersToAnotherMessage("button")).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ACCESS_DENIED_NOT_APPROVED_REPLY,
|
|
5
|
+
PLUGIN_ADMISSION_DENIED_NOTICE_PATH,
|
|
6
|
+
PluginAdmissionDeniedNoticeSchema,
|
|
7
|
+
} from "../plugin-admission-denied-contract.js";
|
|
8
|
+
|
|
9
|
+
describe("plugin admission-denied contract", () => {
|
|
10
|
+
test("keeps the not-approved copy stable", () => {
|
|
11
|
+
expect(ACCESS_DENIED_NOT_APPROVED_REPLY).toBe(
|
|
12
|
+
"Sorry, you haven't been approved to message this assistant.",
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("names the internal plugin notice path", () => {
|
|
17
|
+
expect(PLUGIN_ADMISSION_DENIED_NOTICE_PATH).toBe(
|
|
18
|
+
"notices/admission-denied",
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("round-trips a floor-deny notice", () => {
|
|
23
|
+
const notice = {
|
|
24
|
+
reason: "admission_floor" as const,
|
|
25
|
+
plugin: "imessage",
|
|
26
|
+
ingressRoute: "events-linq",
|
|
27
|
+
admissionPolicy: "guardian_only" as const,
|
|
28
|
+
trustClass: "unknown" as const,
|
|
29
|
+
conversationExternalId: "chat-1",
|
|
30
|
+
actorExternalId: "+15550100",
|
|
31
|
+
externalMessageId: "msg-1",
|
|
32
|
+
replyText: ACCESS_DENIED_NOT_APPROVED_REPLY,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
expect(PluginAdmissionDeniedNoticeSchema.parse(notice)).toEqual(notice);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("rejects a notice that still carries a plugin-scoped id as the chat", () => {
|
|
39
|
+
// The send API addresses vendor chats. An empty chat id is the failure
|
|
40
|
+
// that matters; the schema only requires a non-empty string, so this
|
|
41
|
+
// asserts the required fields rather than the prefix rule.
|
|
42
|
+
expect(() =>
|
|
43
|
+
PluginAdmissionDeniedNoticeSchema.parse({
|
|
44
|
+
reason: "admission_floor",
|
|
45
|
+
plugin: "imessage",
|
|
46
|
+
ingressRoute: "events-linq",
|
|
47
|
+
admissionPolicy: "guardian_only",
|
|
48
|
+
trustClass: "unknown",
|
|
49
|
+
conversationExternalId: "",
|
|
50
|
+
actorExternalId: "+15550100",
|
|
51
|
+
externalMessageId: "msg-1",
|
|
52
|
+
replyText: ACCESS_DENIED_NOT_APPROVED_REPLY,
|
|
53
|
+
}),
|
|
54
|
+
).toThrow();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -31,6 +31,7 @@ const fullVerdict: TrustVerdict = {
|
|
|
31
31
|
verifiedAt: 1699999999,
|
|
32
32
|
interactionCount: 12,
|
|
33
33
|
memberDisplayName: "Member Name",
|
|
34
|
+
autoApproveThreshold: "high",
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
describe("TrustVerdictSchema", () => {
|
|
@@ -38,6 +39,23 @@ describe("TrustVerdictSchema", () => {
|
|
|
38
39
|
expect(TrustVerdictSchema.parse(fullVerdict)).toEqual(fullVerdict);
|
|
39
40
|
});
|
|
40
41
|
|
|
42
|
+
test("parses a verdict with a null contact auto-approve threshold", () => {
|
|
43
|
+
const verdict = {
|
|
44
|
+
...fullVerdict,
|
|
45
|
+
autoApproveThreshold: null,
|
|
46
|
+
} satisfies TrustVerdict;
|
|
47
|
+
expect(TrustVerdictSchema.parse(verdict)).toEqual(verdict);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("rejects an unknown contact auto-approve threshold", () => {
|
|
51
|
+
expect(() =>
|
|
52
|
+
TrustVerdictSchema.parse({
|
|
53
|
+
...fullVerdict,
|
|
54
|
+
autoApproveThreshold: "full",
|
|
55
|
+
}),
|
|
56
|
+
).toThrow();
|
|
57
|
+
});
|
|
58
|
+
|
|
41
59
|
test("parses a minimal verdict", () => {
|
|
42
60
|
const minimal = {
|
|
43
61
|
trustClass: "unknown",
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Both the gateway (channel admission policy storage + kill switch) and the
|
|
5
5
|
* runtime (admission-policy stage) consume these values. Keeping the type
|
|
6
|
-
* here avoids the runtime importing from `gateway/src` and
|
|
7
|
-
*
|
|
8
|
-
* `trustClass` enum.
|
|
6
|
+
* here avoids the runtime importing from `gateway/src` and keeps both
|
|
7
|
+
* sides on a single shared vocabulary.
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
import { z } from "zod";
|
|
@@ -15,7 +14,7 @@ import type { TrustClass } from "./trust-verdict-contract.js";
|
|
|
15
14
|
/**
|
|
16
15
|
* Per-channel inbound admission policy — ordered from most-restrictive
|
|
17
16
|
* (`no_one`, hard kill switch) to most-permissive (`strangers`, admits any
|
|
18
|
-
* sender).
|
|
17
|
+
* sender).
|
|
19
18
|
*/
|
|
20
19
|
export const ADMISSION_POLICY_VALUES = [
|
|
21
20
|
"no_one",
|
|
@@ -32,14 +31,14 @@ export const AdmissionPolicySchema = z.enum(ADMISSION_POLICY_VALUES);
|
|
|
32
31
|
/**
|
|
33
32
|
* Read-side default applied when a channel has no row in the DB. Matches
|
|
34
33
|
* today's effective semantics: guardian + active contacts admitted,
|
|
35
|
-
* strangers denied.
|
|
34
|
+
* strangers denied.
|
|
36
35
|
*/
|
|
37
36
|
export const ADMISSION_POLICY_DEFAULT: AdmissionPolicy = "trusted_contacts";
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
39
|
* Minimum trust rank required for each policy. Higher rank = more trusted.
|
|
41
40
|
* `no_one` is 5 — above the maximum guardian rank (4) — so no class is ever
|
|
42
|
-
* admitted.
|
|
41
|
+
* admitted.
|
|
43
42
|
*/
|
|
44
43
|
export const ADMISSION_FLOOR: Record<AdmissionPolicy, number> = {
|
|
45
44
|
no_one: 5,
|
|
@@ -82,15 +81,10 @@ export function isAdmissionPolicyExemptChannel(channelType: string): boolean {
|
|
|
82
81
|
* `vellum` is the local desktop/web client surface; the guardian is always
|
|
83
82
|
* max-rank there, so the seed default admits them regardless of the floor.
|
|
84
83
|
*
|
|
85
|
-
* `discord` has no ingress implementation, so there is nothing for a floor to
|
|
86
|
-
* gate and nothing for the user to configure. Hiding keeps it pinned at the
|
|
87
|
-
* seed default rather than offering a Channel Trust Floors row for a channel
|
|
88
|
-
* that receives nothing.
|
|
89
84
|
*/
|
|
90
85
|
export const ADMISSION_POLICY_HIDDEN_CHANNELS: ReadonlySet<string> = new Set([
|
|
91
86
|
"vellum",
|
|
92
87
|
"whatsapp",
|
|
93
|
-
"discord",
|
|
94
88
|
]);
|
|
95
89
|
|
|
96
90
|
export function isAdmissionPolicyHiddenChannel(channelType: string): boolean {
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Storage lives in the gateway DB (`channel_permission_overrides`) so the
|
|
12
12
|
* assistant cannot tamper with it, matching `channel_admission_policy` and
|
|
13
|
-
* `auto_approve_thresholds`.
|
|
13
|
+
* `auto_approve_thresholds`. A contact-level ceiling can live on
|
|
14
|
+
* `contacts.auto_approve_threshold` and is stamped on the trust verdict;
|
|
15
|
+
* that override is not a matrix cell.
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
18
|
import { z } from "zod";
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import { CHANNEL_IDS } from "@vellumai/service-contracts/channels";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
|
|
8
|
+
import { RiskThresholdSchema } from "./channel-permission-contract.js";
|
|
9
|
+
|
|
8
10
|
export const GATEWAY_LOG_LEVEL_NAMES = [
|
|
9
11
|
"trace",
|
|
10
12
|
"debug",
|
|
@@ -275,6 +277,11 @@ export const ContactReadSchema = z.object({
|
|
|
275
277
|
contactType: z.string().nullable().optional(),
|
|
276
278
|
lastInteraction: z.number().nullable().optional(),
|
|
277
279
|
interactionCount: z.number().nullable(),
|
|
280
|
+
/**
|
|
281
|
+
* Per-contact auto-approve ceiling. Null when unset (inherit cascade).
|
|
282
|
+
* Optional on the wire for version skew; gateway reads always emit it.
|
|
283
|
+
*/
|
|
284
|
+
autoApproveThreshold: RiskThresholdSchema.nullable().optional(),
|
|
278
285
|
createdAt: z.number(),
|
|
279
286
|
updatedAt: z.number(),
|
|
280
287
|
channels: z.array(ContactReadChannelSchema),
|
|
@@ -401,6 +408,8 @@ export type ClassifyRiskSkillMetadata = z.infer<
|
|
|
401
408
|
export const ClassifyRiskIpcParamsSchema = z.object({
|
|
402
409
|
tool: z.string().min(1),
|
|
403
410
|
command: z.string().optional(),
|
|
411
|
+
/** Shell grammar used by host_bash on the target desktop. */
|
|
412
|
+
shell: z.enum(["bash", "powershell"]).optional(),
|
|
404
413
|
url: z.string().optional(),
|
|
405
414
|
path: z.string().optional(),
|
|
406
415
|
/**
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* it in `handleChannelInbound()`.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { INBOUND_EVENT_KINDS } from "./inbound-event-kind.js";
|
|
13
14
|
import { z } from "zod";
|
|
14
15
|
|
|
15
16
|
import { ChannelConversationTypeSchema } from "./channel-permission-contract.js";
|
|
@@ -168,6 +169,9 @@ export const RuntimeInboundPayloadSchema = z.object({
|
|
|
168
169
|
conversationExternalId: z.string(),
|
|
169
170
|
externalMessageId: z.string(),
|
|
170
171
|
content: z.string(),
|
|
172
|
+
/** The named event family; absent only on replayed retry payloads,
|
|
173
|
+
* where resolveInboundEventKind derives it from the legacy fields. */
|
|
174
|
+
eventKind: z.enum(INBOUND_EVENT_KINDS).optional(),
|
|
171
175
|
isEdit: z.boolean().optional(),
|
|
172
176
|
callbackQueryId: z.string().optional(),
|
|
173
177
|
callbackData: z.string().optional(),
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The inbound event families, named.
|
|
3
|
+
*
|
|
4
|
+
* Every inbound event is one of these, and the kind decides which pipeline
|
|
5
|
+
* stages apply: only user-authored text (message, edit) can carry a
|
|
6
|
+
* verification code or an invite, only a message starts an agent turn, and
|
|
7
|
+
* edit/delete/reaction/button all refer to another message rather than
|
|
8
|
+
* standing alone. A payload can also encode its family in the field that
|
|
9
|
+
* carries it: `isEdit`, the sentinel string `"message_deleted"`, the
|
|
10
|
+
* `"reaction:"` prefix, the presence of `callbackData` at all. Replayed
|
|
11
|
+
* retry payloads arrive with only those encodings, so
|
|
12
|
+
* {@link resolveInboundEventKind} derives the kind from them exactly once;
|
|
13
|
+
* nothing else may sniff them.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const INBOUND_EVENT_KINDS = [
|
|
17
|
+
"message",
|
|
18
|
+
"edit",
|
|
19
|
+
"delete",
|
|
20
|
+
"reaction",
|
|
21
|
+
"button",
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
export type InboundEventKind = (typeof INBOUND_EVENT_KINDS)[number];
|
|
25
|
+
|
|
26
|
+
export function isInboundEventKind(value: string): value is InboundEventKind {
|
|
27
|
+
return (INBOUND_EVENT_KINDS as readonly string[]).includes(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The one place the field encodings are read. A payload stamped with a
|
|
32
|
+
* kind wins outright; an unstamped one (a replayed retry payload) is
|
|
33
|
+
* classified by the fields that carry its family. The fallback order is
|
|
34
|
+
* specific-before-generic: reaction prefixes and the delete sentinel are
|
|
35
|
+
* particular `callbackData` values, so they are tested before the generic
|
|
36
|
+
* button reading.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveInboundEventKind(fields: {
|
|
39
|
+
eventKind?: string;
|
|
40
|
+
isEdit?: boolean;
|
|
41
|
+
callbackData?: string;
|
|
42
|
+
callbackQueryId?: string;
|
|
43
|
+
}): InboundEventKind {
|
|
44
|
+
if (fields.eventKind !== undefined && isInboundEventKind(fields.eventKind)) {
|
|
45
|
+
return fields.eventKind;
|
|
46
|
+
}
|
|
47
|
+
if (fields.isEdit === true) {
|
|
48
|
+
return "edit";
|
|
49
|
+
}
|
|
50
|
+
const cb = fields.callbackData;
|
|
51
|
+
if (cb !== undefined && cb.length > 0) {
|
|
52
|
+
if (cb === "message_deleted") {
|
|
53
|
+
return "delete";
|
|
54
|
+
}
|
|
55
|
+
if (cb.startsWith("reaction:") || cb.startsWith("reaction_removed:")) {
|
|
56
|
+
return "reaction";
|
|
57
|
+
}
|
|
58
|
+
return "button";
|
|
59
|
+
}
|
|
60
|
+
if (fields.callbackQueryId !== undefined) {
|
|
61
|
+
return "button";
|
|
62
|
+
}
|
|
63
|
+
return "message";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Whether an event of this kind refers to another message rather than
|
|
68
|
+
* standing alone as new content. Referring events never mint a
|
|
69
|
+
* conversation of their own and never carry ingestable attachments.
|
|
70
|
+
*/
|
|
71
|
+
export function inboundEventRefersToAnotherMessage(
|
|
72
|
+
kind: InboundEventKind,
|
|
73
|
+
): boolean {
|
|
74
|
+
return kind !== "message";
|
|
75
|
+
}
|
|
@@ -83,6 +83,23 @@ export {
|
|
|
83
83
|
|
|
84
84
|
export type { AdmissionPolicy } from "./admission-policy-contract.js";
|
|
85
85
|
|
|
86
|
+
export {
|
|
87
|
+
INBOUND_EVENT_KINDS,
|
|
88
|
+
inboundEventRefersToAnotherMessage,
|
|
89
|
+
isInboundEventKind,
|
|
90
|
+
resolveInboundEventKind,
|
|
91
|
+
} from "./inbound-event-kind.js";
|
|
92
|
+
export type { InboundEventKind } from "./inbound-event-kind.js";
|
|
93
|
+
|
|
94
|
+
// Plugin admission-denied notice (gateway → plugin) — canned deny copy + envelope
|
|
95
|
+
export {
|
|
96
|
+
ACCESS_DENIED_NOT_APPROVED_REPLY,
|
|
97
|
+
PLUGIN_ADMISSION_DENIED_NOTICE_PATH,
|
|
98
|
+
PluginAdmissionDeniedNoticeSchema,
|
|
99
|
+
} from "./plugin-admission-denied-contract.js";
|
|
100
|
+
|
|
101
|
+
export type { PluginAdmissionDeniedNotice } from "./plugin-admission-denied-contract.js";
|
|
102
|
+
|
|
86
103
|
// Trust verdict contract (gateway → daemon) — Zod schemas + derived types
|
|
87
104
|
export {
|
|
88
105
|
isTrustClass,
|
|
@@ -61,6 +61,14 @@ export const ApprovalUIMetadataSchema = z.object({
|
|
|
61
61
|
actions: z.array(ApprovalActionOptionSchema),
|
|
62
62
|
plainTextFallback: z.string(),
|
|
63
63
|
permissionDetails: PermissionRequestDetailsSchema.optional(),
|
|
64
|
+
/**
|
|
65
|
+
* What the recipient is being asked to do, so a renderer can head the card
|
|
66
|
+
* with the right verb. Resolved once by the producer; a renderer that reads
|
|
67
|
+
* the actions to work it out would be parsing a payload it is meant only to
|
|
68
|
+
* draw. Absent means an approval, which is what every existing producer
|
|
69
|
+
* sends and what a renderer without this field already assumes.
|
|
70
|
+
*/
|
|
71
|
+
intent: z.enum(["approval", "question"]).optional(),
|
|
64
72
|
});
|
|
65
73
|
|
|
66
74
|
export type ApprovalUIMetadata = z.infer<typeof ApprovalUIMetadataSchema>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canned access-denial copy and the plugin notice the gateway posts when a
|
|
3
|
+
* plugin inbound delivery clears verification but fails the admission floor.
|
|
4
|
+
*
|
|
5
|
+
* Built-in channels send this reply through the runtime's channel transport.
|
|
6
|
+
* A plugin channel has no such transport: the gateway does not hold the
|
|
7
|
+
* vendor send credentials, so the notice is how the plugin learns it must
|
|
8
|
+
* send the same line itself, without running a turn.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
import { AdmissionPolicySchema } from "./admission-policy-contract.js";
|
|
14
|
+
import { TrustClassSchema } from "./trust-verdict-contract.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Requester-facing reply when access is denied and there is no handshake
|
|
18
|
+
* and no guardian notification. Single source for this line: the runtime
|
|
19
|
+
* ACL composer and the plugin admission-denied notice both send it.
|
|
20
|
+
*/
|
|
21
|
+
export const ACCESS_DENIED_NOT_APPROVED_REPLY =
|
|
22
|
+
"Sorry, you haven't been approved to message this assistant.";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Plugin route the gateway posts the notice to, under
|
|
26
|
+
* `/v1/x/plugins/<plugin>/`. Not a public ingress path.
|
|
27
|
+
*/
|
|
28
|
+
export const PLUGIN_ADMISSION_DENIED_NOTICE_PATH = "notices/admission-denied";
|
|
29
|
+
|
|
30
|
+
export const PluginAdmissionDeniedNoticeSchema = z.object({
|
|
31
|
+
reason: z.literal("admission_floor"),
|
|
32
|
+
plugin: z.string().min(1),
|
|
33
|
+
ingressRoute: z.string().min(1),
|
|
34
|
+
admissionPolicy: AdmissionPolicySchema,
|
|
35
|
+
trustClass: TrustClassSchema,
|
|
36
|
+
/** Vendor chat id, without the plugin prefix the gate stores. */
|
|
37
|
+
conversationExternalId: z.string().min(1),
|
|
38
|
+
/** Vendor sender id, without the plugin prefix the gate stores. */
|
|
39
|
+
actorExternalId: z.string().min(1),
|
|
40
|
+
/** Vendor message id, without the plugin prefix the gate stores. */
|
|
41
|
+
externalMessageId: z.string().min(1),
|
|
42
|
+
replyText: z.string().min(1),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export type PluginAdmissionDeniedNotice = z.infer<
|
|
46
|
+
typeof PluginAdmissionDeniedNoticeSchema
|
|
47
|
+
>;
|
|
@@ -78,6 +78,17 @@ export const TrustVerdictSchema = z.object({
|
|
|
78
78
|
policy: z.string().optional(),
|
|
79
79
|
verifiedAt: z.number().nullable().optional(),
|
|
80
80
|
memberDisplayName: z.string().optional(),
|
|
81
|
+
/**
|
|
82
|
+
* Contact-level auto-approve ceiling from the gateway `contacts` row.
|
|
83
|
+
* Present when a member channel resolves. Null means the contact has no
|
|
84
|
+
* explicit override (inherit the room / trust-class cascade).
|
|
85
|
+
* Same vocabulary as `RiskThreshold` (`none` | `low` | `medium` | `high`).
|
|
86
|
+
* Inlined here so this leaf contract does not import the permission matrix.
|
|
87
|
+
*/
|
|
88
|
+
autoApproveThreshold: z
|
|
89
|
+
.enum(["none", "low", "medium", "high"])
|
|
90
|
+
.nullable()
|
|
91
|
+
.optional(),
|
|
81
92
|
|
|
82
93
|
// Gateway-owned interaction telemetry (a trust signal, not an info field per
|
|
83
94
|
// the 2×2) — carried straight off the member `contact_channels` row.
|
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
normalizePublicBaseUrl,
|
|
7
7
|
parseRecordedAssistantId,
|
|
8
8
|
parseTunnelRecord,
|
|
9
|
+
selectRestartTunnelRecord,
|
|
9
10
|
trimmedNonEmptyString,
|
|
10
11
|
TUNNEL_PROVIDERS,
|
|
12
|
+
type TunnelRecord,
|
|
11
13
|
velayHostForPlatformHost,
|
|
12
14
|
} from "../ingress.js";
|
|
13
15
|
import {
|
|
@@ -221,6 +223,41 @@ describe("parseTunnelRecord", () => {
|
|
|
221
223
|
});
|
|
222
224
|
});
|
|
223
225
|
|
|
226
|
+
describe("selectRestartTunnelRecord", () => {
|
|
227
|
+
const PAIRING: TunnelRecord = {
|
|
228
|
+
provider: "tailscale",
|
|
229
|
+
publicBaseUrl: "https://assistant-1.example.ts.net",
|
|
230
|
+
};
|
|
231
|
+
const LAST: TunnelRecord = {
|
|
232
|
+
provider: "ngrok",
|
|
233
|
+
publicBaseUrl: "https://assistant-1.ngrok.app",
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
test("prefers the pairing tunnel over the last one that ran", () => {
|
|
237
|
+
// The pairing tunnel deliberately left the older public record beside it,
|
|
238
|
+
// so naming that one would point at an ingress nobody is using.
|
|
239
|
+
expect(
|
|
240
|
+
selectRestartTunnelRecord({ lastTunnel: LAST, pairingTunnel: PAIRING }),
|
|
241
|
+
).toEqual(PAIRING);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("falls back to the last tunnel and to null", () => {
|
|
245
|
+
expect(selectRestartTunnelRecord({ lastTunnel: LAST })).toEqual(LAST);
|
|
246
|
+
for (const ingress of [undefined, null, {}, "nonsense", 42]) {
|
|
247
|
+
expect(selectRestartTunnelRecord(ingress)).toBeNull();
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("skips a malformed pairing record for a usable last one", () => {
|
|
252
|
+
expect(
|
|
253
|
+
selectRestartTunnelRecord({
|
|
254
|
+
lastTunnel: LAST,
|
|
255
|
+
pairingTunnel: { provider: "wireguard", publicBaseUrl: "nope" },
|
|
256
|
+
}),
|
|
257
|
+
).toEqual(LAST);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
224
261
|
describe("trimmedNonEmptyString", () => {
|
|
225
262
|
test("trims and rejects blank or non-string values", () => {
|
|
226
263
|
expect(trimmedNonEmptyString(" assistant-1 ")).toBe("assistant-1");
|