@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
|
@@ -10,12 +10,23 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
10
10
|
let injectedProcessMessageForRunner:
|
|
11
11
|
| ((conversationId: string, message: string) => Promise<unknown>)
|
|
12
12
|
| null = null;
|
|
13
|
+
// Options of every mocked `runBackgroundJob` call, for asserting what the
|
|
14
|
+
// scheduler asked of the runner (e.g. that per-attempt failure emissions are
|
|
15
|
+
// suppressed in favor of the retries-exhausted alert).
|
|
16
|
+
const runnerCalls: Array<Record<string, unknown>> = [];
|
|
17
|
+
// When set, a failing mocked run reports this classification instead of the
|
|
18
|
+
// default generic exception, letting a test drive the cause-keyed alert path.
|
|
19
|
+
let runnerFailureClassification: {
|
|
20
|
+
errorKind: "timeout" | "model_provider" | "exception";
|
|
21
|
+
failureCode?: string;
|
|
22
|
+
} | null = null;
|
|
13
23
|
mock.module("../runtime/background-job-runner.js", () => ({
|
|
14
24
|
runBackgroundJob: async (opts: {
|
|
15
25
|
jobName: string;
|
|
16
26
|
prompt: string;
|
|
17
27
|
onConversationCreated?: (conversationId: string) => void;
|
|
18
28
|
}) => {
|
|
29
|
+
runnerCalls.push(opts as unknown as Record<string, unknown>);
|
|
19
30
|
const conversationId = `mock-conv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
20
31
|
opts.onConversationCreated?.(conversationId);
|
|
21
32
|
try {
|
|
@@ -25,11 +36,17 @@ mock.module("../runtime/background-job-runner.js", () => ({
|
|
|
25
36
|
return { conversationId, ok: true };
|
|
26
37
|
} catch (err) {
|
|
27
38
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
39
|
+
const classification = runnerFailureClassification ?? {
|
|
40
|
+
errorKind: "exception" as const,
|
|
41
|
+
};
|
|
28
42
|
return {
|
|
29
43
|
conversationId,
|
|
30
44
|
ok: false,
|
|
31
45
|
error,
|
|
32
|
-
errorKind:
|
|
46
|
+
errorKind: classification.errorKind,
|
|
47
|
+
...(classification.failureCode !== undefined
|
|
48
|
+
? { turnFailure: { failureCode: classification.failureCode } }
|
|
49
|
+
: {}),
|
|
33
50
|
};
|
|
34
51
|
}
|
|
35
52
|
},
|
|
@@ -466,6 +483,171 @@ describe("scheduler retry integration", () => {
|
|
|
466
483
|
});
|
|
467
484
|
});
|
|
468
485
|
|
|
486
|
+
// ── Execute-mode failure notifications ────────────────────────────────
|
|
487
|
+
|
|
488
|
+
describe("execute-mode failure notifications", () => {
|
|
489
|
+
let scheduler: SchedulerHandle;
|
|
490
|
+
|
|
491
|
+
beforeEach(() => {
|
|
492
|
+
emitNotificationSignalImpl = async () => {};
|
|
493
|
+
runnerCalls.length = 0;
|
|
494
|
+
runnerFailureClassification = null;
|
|
495
|
+
const db = getDb();
|
|
496
|
+
db.run("DELETE FROM cron_runs");
|
|
497
|
+
db.run("DELETE FROM cron_jobs");
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
afterEach(() => {
|
|
501
|
+
scheduler?.stop();
|
|
502
|
+
runnerFailureClassification = null;
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test("a failing schedule alerts once, at exhaustion, keyed on the failure cause", async () => {
|
|
506
|
+
const emitted: Array<Record<string, unknown>> = [];
|
|
507
|
+
emitNotificationSignalImpl = async (payload) => {
|
|
508
|
+
emitted.push(payload as Record<string, unknown>);
|
|
509
|
+
};
|
|
510
|
+
runnerFailureClassification = {
|
|
511
|
+
errorKind: "model_provider",
|
|
512
|
+
failureCode: "PROVIDER_BILLING",
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const schedule = await createSchedule({
|
|
516
|
+
name: "Billing victim",
|
|
517
|
+
cronExpression: "0 * * * *",
|
|
518
|
+
message: "do work",
|
|
519
|
+
syntax: "cron",
|
|
520
|
+
maxRetries: 1,
|
|
521
|
+
retryBackoffMs: 1000,
|
|
522
|
+
});
|
|
523
|
+
forceScheduleDue(schedule.id);
|
|
524
|
+
|
|
525
|
+
scheduler = startScheduler(async () => {
|
|
526
|
+
throw new Error("PROVIDER_BILLING: Agent turn failed.");
|
|
527
|
+
});
|
|
528
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
529
|
+
|
|
530
|
+
// First failure: a retry is pending, so nothing is user-visible yet, and
|
|
531
|
+
// the scheduler told the runner to keep its per-attempt emission quiet.
|
|
532
|
+
expect(emitted).toHaveLength(0);
|
|
533
|
+
expect(runnerCalls.length).toBeGreaterThanOrEqual(1);
|
|
534
|
+
for (const call of runnerCalls) {
|
|
535
|
+
expect(call.suppressFailureNotifications).toBe(true);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Second failure exhausts retries (maxRetries: 1) and fires the single
|
|
539
|
+
// alert, deduped on the classified cause rather than the schedule.
|
|
540
|
+
forceScheduleDue(schedule.id);
|
|
541
|
+
await scheduler.runOnce();
|
|
542
|
+
scheduler.stop();
|
|
543
|
+
|
|
544
|
+
expect(emitted).toHaveLength(1);
|
|
545
|
+
const alert = emitted[0];
|
|
546
|
+
expect(alert.sourceEventName).toBe("activity.failed");
|
|
547
|
+
expect(alert.sourceContextId).toBe(schedule.id);
|
|
548
|
+
// Schedules snapshot a concrete inference profile at creation, and that
|
|
549
|
+
// profile is the alert's provider scope.
|
|
550
|
+
const scope = schedule.inferenceProfile;
|
|
551
|
+
expect(scope).toBeTruthy();
|
|
552
|
+
expect(alert.dedupeKey).toMatch(
|
|
553
|
+
new RegExp(
|
|
554
|
+
`^activity-failed:cause:PROVIDER_BILLING:${scope}:\\d{4}-\\d{2}-\\d{2}$`,
|
|
555
|
+
),
|
|
556
|
+
);
|
|
557
|
+
expect(alert.contextPayload).toMatchObject({
|
|
558
|
+
jobName: "schedule:Billing victim",
|
|
559
|
+
errorKind: "model_provider",
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test("schedules on different profiles alert separately for the same cause", async () => {
|
|
564
|
+
// Two provider connections can fail for the same class of reason with
|
|
565
|
+
// different remedies; schedule A's alert must not suppress schedule B's.
|
|
566
|
+
const emitted: Array<Record<string, unknown>> = [];
|
|
567
|
+
emitNotificationSignalImpl = async (payload) => {
|
|
568
|
+
emitted.push(payload as Record<string, unknown>);
|
|
569
|
+
};
|
|
570
|
+
runnerFailureClassification = {
|
|
571
|
+
errorKind: "model_provider",
|
|
572
|
+
failureCode: "PROVIDER_INVALID_KEY",
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const scheduleA = await createSchedule({
|
|
576
|
+
name: "Profile A victim",
|
|
577
|
+
cronExpression: "0 * * * *",
|
|
578
|
+
message: "do work",
|
|
579
|
+
syntax: "cron",
|
|
580
|
+
maxRetries: 1,
|
|
581
|
+
retryBackoffMs: 1000,
|
|
582
|
+
inferenceProfile: "cost-optimized",
|
|
583
|
+
});
|
|
584
|
+
const scheduleB = await createSchedule({
|
|
585
|
+
name: "Profile B victim",
|
|
586
|
+
cronExpression: "0 * * * *",
|
|
587
|
+
message: "do work",
|
|
588
|
+
syntax: "cron",
|
|
589
|
+
maxRetries: 1,
|
|
590
|
+
retryBackoffMs: 1000,
|
|
591
|
+
inferenceProfile: "balanced",
|
|
592
|
+
});
|
|
593
|
+
forceScheduleDue(scheduleA.id);
|
|
594
|
+
forceScheduleDue(scheduleB.id);
|
|
595
|
+
|
|
596
|
+
scheduler = startScheduler(async () => {
|
|
597
|
+
throw new Error("provider rejected the key");
|
|
598
|
+
});
|
|
599
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
600
|
+
forceScheduleDue(scheduleA.id);
|
|
601
|
+
forceScheduleDue(scheduleB.id);
|
|
602
|
+
await scheduler.runOnce();
|
|
603
|
+
scheduler.stop();
|
|
604
|
+
|
|
605
|
+
expect(emitted).toHaveLength(2);
|
|
606
|
+
const keys = emitted.map((signal) => signal.dedupeKey as string).sort();
|
|
607
|
+
expect(keys[0]).toMatch(
|
|
608
|
+
/^activity-failed:cause:PROVIDER_INVALID_KEY:balanced:\d{4}-\d{2}-\d{2}$/,
|
|
609
|
+
);
|
|
610
|
+
expect(keys[1]).toMatch(
|
|
611
|
+
/^activity-failed:cause:PROVIDER_INVALID_KEY:cost-optimized:\d{4}-\d{2}-\d{2}$/,
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
test("a cause-less failure keys the exhaustion alert per schedule", async () => {
|
|
616
|
+
const emitted: Array<Record<string, unknown>> = [];
|
|
617
|
+
emitNotificationSignalImpl = async (payload) => {
|
|
618
|
+
emitted.push(payload as Record<string, unknown>);
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
const schedule = await createSchedule({
|
|
622
|
+
name: "Crashing job",
|
|
623
|
+
cronExpression: "0 * * * *",
|
|
624
|
+
message: "do work",
|
|
625
|
+
syntax: "cron",
|
|
626
|
+
maxRetries: 1,
|
|
627
|
+
retryBackoffMs: 1000,
|
|
628
|
+
});
|
|
629
|
+
forceScheduleDue(schedule.id);
|
|
630
|
+
|
|
631
|
+
scheduler = startScheduler(async () => {
|
|
632
|
+
throw new Error("boom");
|
|
633
|
+
});
|
|
634
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
635
|
+
forceScheduleDue(schedule.id);
|
|
636
|
+
await scheduler.runOnce();
|
|
637
|
+
scheduler.stop();
|
|
638
|
+
|
|
639
|
+
expect(emitted).toHaveLength(1);
|
|
640
|
+
expect(emitted[0].dedupeKey).toMatch(
|
|
641
|
+
new RegExp(
|
|
642
|
+
`^activity-failed:schedule:${schedule.id}:\\d{4}-\\d{2}-\\d{2}$`,
|
|
643
|
+
),
|
|
644
|
+
);
|
|
645
|
+
expect(emitted[0].contextPayload).toMatchObject({
|
|
646
|
+
errorKind: "exception",
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
|
|
469
651
|
// ── Crash recovery ────────────────────────────────────────────────────
|
|
470
652
|
|
|
471
653
|
describe("crash recovery", () => {
|
|
@@ -667,7 +849,6 @@ describe("scheduler-recovery equivalence", () => {
|
|
|
667
849
|
await applyRetryDecision({
|
|
668
850
|
job: jobB,
|
|
669
851
|
isOneShot: false,
|
|
670
|
-
errorMsg: "fail",
|
|
671
852
|
decision,
|
|
672
853
|
scheduleRetry,
|
|
673
854
|
failOneShotPermanently: () => {},
|
|
@@ -747,7 +928,6 @@ describe("scheduler-recovery equivalence", () => {
|
|
|
747
928
|
await applyRetryDecision({
|
|
748
929
|
job: jobB1,
|
|
749
930
|
isOneShot: true,
|
|
750
|
-
errorMsg: "fail",
|
|
751
931
|
decision: decision1,
|
|
752
932
|
scheduleRetry,
|
|
753
933
|
failOneShotPermanently: (id: string) => {
|
|
@@ -774,7 +954,6 @@ describe("scheduler-recovery equivalence", () => {
|
|
|
774
954
|
await applyRetryDecision({
|
|
775
955
|
job: jobB2,
|
|
776
956
|
isOneShot: true,
|
|
777
|
-
errorMsg: "fail",
|
|
778
957
|
decision: decision2,
|
|
779
958
|
scheduleRetry,
|
|
780
959
|
failOneShotPermanently: (id: string) => {
|
|
@@ -456,7 +456,7 @@ describe("scheduler talk-mode runner option propagation", () => {
|
|
|
456
456
|
runBackgroundJobBootstrapFails = false;
|
|
457
457
|
});
|
|
458
458
|
|
|
459
|
-
test("talk-mode propagates conversationType=scheduled
|
|
459
|
+
test("talk-mode propagates conversationType=scheduled and scheduleJobId", async () => {
|
|
460
460
|
const rruleExpr = buildEveryMinuteRrule();
|
|
461
461
|
const schedule = await createSchedule({
|
|
462
462
|
name: "Quiet Talk Mode",
|
|
@@ -475,10 +475,12 @@ describe("scheduler talk-mode runner option propagation", () => {
|
|
|
475
475
|
expect(opts.conversationType).toBe("scheduled");
|
|
476
476
|
expect(opts.scheduleJobId).toBe(schedule.id);
|
|
477
477
|
expect(opts.groupId).toBe("system:scheduled");
|
|
478
|
-
expect(opts.suppressFailureNotifications).toBe(true);
|
|
479
478
|
});
|
|
480
479
|
|
|
481
|
-
test("talk-mode
|
|
480
|
+
test("talk-mode always suppresses the runner's per-attempt failure emission", async () => {
|
|
481
|
+
// The retry policy owns the single user-facing alert for a failing
|
|
482
|
+
// schedule (fired once, at exhaustion), so the runner must stay quiet
|
|
483
|
+
// per attempt whether or not the schedule itself is quiet.
|
|
482
484
|
const rruleExpr = buildEveryMinuteRrule();
|
|
483
485
|
const schedule = await createSchedule({
|
|
484
486
|
name: "Loud Talk Mode",
|
|
@@ -493,9 +495,7 @@ describe("scheduler talk-mode runner option propagation", () => {
|
|
|
493
495
|
await runDueSchedulesOnce();
|
|
494
496
|
|
|
495
497
|
expect(runBackgroundJobOptions).toHaveLength(1);
|
|
496
|
-
expect(runBackgroundJobOptions[0]!.suppressFailureNotifications).toBe(
|
|
497
|
-
false,
|
|
498
|
-
);
|
|
498
|
+
expect(runBackgroundJobOptions[0]!.suppressFailureNotifications).toBe(true);
|
|
499
499
|
});
|
|
500
500
|
|
|
501
501
|
test("talk-mode bootstrap failure writes sentinel conversationId, not empty string", async () => {
|
|
@@ -12,6 +12,9 @@ let secureKeyStore: Record<string, string | undefined> = {};
|
|
|
12
12
|
const metadataUpserts: Array<{ service: string; field: string }> = [];
|
|
13
13
|
const metadataDeletes: Array<{ service: string; field: string }> = [];
|
|
14
14
|
let providerRefreshCalls = 0;
|
|
15
|
+
let identitySyncCalls = 0;
|
|
16
|
+
let avatarSyncCalls = 0;
|
|
17
|
+
let failSecureKeyWrites = false;
|
|
15
18
|
|
|
16
19
|
const PLATFORM_BASE_URL = "https://platform.example.com";
|
|
17
20
|
const ASSISTANT_API_KEY_PATH = credentialKey("vellum", "assistant_api_key");
|
|
@@ -73,6 +76,9 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
73
76
|
unreachable: false,
|
|
74
77
|
}),
|
|
75
78
|
setSecureKeyAsync: async (key: string, value: string) => {
|
|
79
|
+
if (failSecureKeyWrites) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
76
82
|
secureKeyStore[key] = value;
|
|
77
83
|
return true;
|
|
78
84
|
},
|
|
@@ -99,6 +105,19 @@ mock.module("../plugins/defaults/memory/substrate/boot-maintenance.js", () => ({
|
|
|
99
105
|
maybeReseedCapabilitiesAfterManagedCredential: async () => {},
|
|
100
106
|
}));
|
|
101
107
|
|
|
108
|
+
// Live platform registration (`vellum login` against a running assistant)
|
|
109
|
+
// must re-enqueue the name and avatar syncs that no-op'd at startup.
|
|
110
|
+
mock.module("../platform/sync-identity.js", () => ({
|
|
111
|
+
syncWorkspaceIdentityToPlatform: () => {
|
|
112
|
+
identitySyncCalls++;
|
|
113
|
+
},
|
|
114
|
+
}));
|
|
115
|
+
mock.module("../platform/sync-avatar.js", () => ({
|
|
116
|
+
syncAvatarToPlatform: () => {
|
|
117
|
+
avatarSyncCalls++;
|
|
118
|
+
},
|
|
119
|
+
}));
|
|
120
|
+
|
|
102
121
|
// secret-routes evicts conversations after a credential change so the next turn
|
|
103
122
|
// rebuilds against the new providers; count the calls to assert that happens.
|
|
104
123
|
mock.module("../daemon/conversation-store.js", () => ({
|
|
@@ -164,6 +183,9 @@ describe("secret routes managed proxy registry sync", () => {
|
|
|
164
183
|
lastGeminiConstructorOpts = null;
|
|
165
184
|
platformBaseUrlOverride = undefined;
|
|
166
185
|
providerRefreshCalls = 0;
|
|
186
|
+
identitySyncCalls = 0;
|
|
187
|
+
avatarSyncCalls = 0;
|
|
188
|
+
failSecureKeyWrites = false;
|
|
167
189
|
getDb()
|
|
168
190
|
.delete(providerConnections)
|
|
169
191
|
.where(eq(providerConnections.name, "openrouter-connection"))
|
|
@@ -301,6 +323,39 @@ describe("secret routes managed proxy registry sync", () => {
|
|
|
301
323
|
expect(providerRefreshCalls).toBe(0);
|
|
302
324
|
});
|
|
303
325
|
|
|
326
|
+
test("storing a platform registration credential enqueues name and avatar syncs", async () => {
|
|
327
|
+
for (const name of [
|
|
328
|
+
"vellum:assistant_api_key",
|
|
329
|
+
"vellum:platform_assistant_id",
|
|
330
|
+
"vellum:platform_base_url",
|
|
331
|
+
]) {
|
|
332
|
+
identitySyncCalls = 0;
|
|
333
|
+
avatarSyncCalls = 0;
|
|
334
|
+
await addCredential(name, "value");
|
|
335
|
+
expect(identitySyncCalls).toBe(1);
|
|
336
|
+
expect(avatarSyncCalls).toBe(1);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("unrelated credentials do not enqueue platform syncs", async () => {
|
|
341
|
+
await addCredential("openrouter:api_key", "openrouter-key");
|
|
342
|
+
await addCredential("vellum:platform_user_id", "user-1");
|
|
343
|
+
|
|
344
|
+
expect(identitySyncCalls).toBe(0);
|
|
345
|
+
expect(avatarSyncCalls).toBe(0);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test("a failed platform credential write does not enqueue platform syncs", async () => {
|
|
349
|
+
failSecureKeyWrites = true;
|
|
350
|
+
|
|
351
|
+
await expect(
|
|
352
|
+
addCredential("vellum:platform_assistant_id", "asst-1"),
|
|
353
|
+
).rejects.toThrow("Failed to store credential");
|
|
354
|
+
|
|
355
|
+
expect(identitySyncCalls).toBe(0);
|
|
356
|
+
expect(avatarSyncCalls).toBe(0);
|
|
357
|
+
});
|
|
358
|
+
|
|
304
359
|
test("storing vellum:platform_base_url sets override and triggers initializeProviders", async () => {
|
|
305
360
|
await addCredential(
|
|
306
361
|
"vellum:platform_base_url",
|
|
@@ -2,6 +2,8 @@ import { mkdirSync, symlinkSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
4
4
|
|
|
5
|
+
import type { ClientOs } from "../channels/types.js";
|
|
6
|
+
|
|
5
7
|
const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
6
8
|
|
|
7
9
|
// Build a mock that covers every export from platform.ts — any function not
|
|
@@ -66,6 +68,7 @@ function writeToolsJson(
|
|
|
66
68
|
input_schema?: Record<string, unknown>;
|
|
67
69
|
executor?: string;
|
|
68
70
|
execution_target?: string;
|
|
71
|
+
supported_client_os?: ClientOs[];
|
|
69
72
|
}>,
|
|
70
73
|
): void {
|
|
71
74
|
const skillDir = join(TEST_DIR, "skills", skillId);
|
|
@@ -80,6 +83,7 @@ function writeToolsJson(
|
|
|
80
83
|
input_schema: t.input_schema ?? { type: "object", properties: {} },
|
|
81
84
|
executor: t.executor ?? "scripts/run.sh",
|
|
82
85
|
execution_target: t.execution_target ?? "host",
|
|
86
|
+
supported_client_os: t.supported_client_os,
|
|
83
87
|
})),
|
|
84
88
|
};
|
|
85
89
|
writeFileSync(join(skillDir, "TOOLS.json"), JSON.stringify(manifest));
|
|
@@ -87,6 +91,7 @@ function writeToolsJson(
|
|
|
87
91
|
|
|
88
92
|
async function executeSkillLoad(
|
|
89
93
|
input: Record<string, unknown>,
|
|
94
|
+
clientOs?: ClientOs,
|
|
90
95
|
): Promise<{ content: string; isError: boolean }> {
|
|
91
96
|
const tool = skillLoadTool;
|
|
92
97
|
|
|
@@ -94,6 +99,7 @@ async function executeSkillLoad(
|
|
|
94
99
|
workingDir: "/tmp",
|
|
95
100
|
conversationId: "conversation-1",
|
|
96
101
|
trustClass: "guardian",
|
|
102
|
+
clientOs,
|
|
97
103
|
});
|
|
98
104
|
return { content: result.content, isError: result.isError };
|
|
99
105
|
}
|
|
@@ -769,6 +775,34 @@ describe("skill_load tool", () => {
|
|
|
769
775
|
expect(result.content).not.toContain("skill_execute");
|
|
770
776
|
});
|
|
771
777
|
|
|
778
|
+
test("omits tools unsupported by the current client operating system", async () => {
|
|
779
|
+
writeSkill(
|
|
780
|
+
"platform-tools",
|
|
781
|
+
"Platform Tools",
|
|
782
|
+
"Has platform-specific tools",
|
|
783
|
+
"Body.",
|
|
784
|
+
);
|
|
785
|
+
writeToolsJson("platform-tools", [
|
|
786
|
+
{
|
|
787
|
+
name: "mac_only_action",
|
|
788
|
+
description: "Runs only on macOS",
|
|
789
|
+
supported_client_os: ["macos"],
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
name: "desktop_action",
|
|
793
|
+
description: "Runs on every desktop",
|
|
794
|
+
},
|
|
795
|
+
]);
|
|
796
|
+
|
|
797
|
+
const result = await executeSkillLoad(
|
|
798
|
+
{ skill: "platform-tools" },
|
|
799
|
+
"windows",
|
|
800
|
+
);
|
|
801
|
+
|
|
802
|
+
expect(result.content).not.toContain("mac_only_action");
|
|
803
|
+
expect(result.content).toContain("desktop_action");
|
|
804
|
+
});
|
|
805
|
+
|
|
772
806
|
test("included child skill with TOOLS.json has its tool schemas in output", async () => {
|
|
773
807
|
writeSkillWithIncludes(
|
|
774
808
|
"parent-tools",
|
|
@@ -83,11 +83,16 @@ afterAll(async () => {
|
|
|
83
83
|
|
|
84
84
|
describe("createSkillTool", () => {
|
|
85
85
|
test("produces a tool with correct name, description, and category", () => {
|
|
86
|
-
const tool = createSkillTool(
|
|
86
|
+
const tool = createSkillTool(
|
|
87
|
+
makeEntry({ supported_client_os: ["macos"] }),
|
|
88
|
+
"/skills/my-skill",
|
|
89
|
+
"v1:test",
|
|
90
|
+
);
|
|
87
91
|
|
|
88
92
|
expect(tool.name).toBe("test_tool");
|
|
89
93
|
expect(tool.description).toBe("A test tool");
|
|
90
94
|
expect(tool.category).toBe("testing");
|
|
95
|
+
expect(tool.supportedClientOs).toEqual(["macos"]);
|
|
91
96
|
});
|
|
92
97
|
|
|
93
98
|
// Removed "sets origin to skill" test — the factory no longer stamps an
|
|
@@ -165,6 +165,26 @@ describe("parseToolManifest", () => {
|
|
|
165
165
|
}
|
|
166
166
|
});
|
|
167
167
|
|
|
168
|
+
test("accepts supported client operating systems", () => {
|
|
169
|
+
const result = parseToolManifest(
|
|
170
|
+
makeManifest({
|
|
171
|
+
tools: [makeToolEntry({ supported_client_os: ["macos", "windows"] })],
|
|
172
|
+
}),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect(result.tools[0].supported_client_os).toEqual(["macos", "windows"]);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("rejects unknown supported client operating systems", () => {
|
|
179
|
+
expect(() =>
|
|
180
|
+
parseToolManifest(
|
|
181
|
+
makeManifest({
|
|
182
|
+
tools: [makeToolEntry({ supported_client_os: ["beos"] })],
|
|
183
|
+
}),
|
|
184
|
+
),
|
|
185
|
+
).toThrow(/supported_client_os/);
|
|
186
|
+
});
|
|
187
|
+
|
|
168
188
|
test("accepts an empty input_schema object", () => {
|
|
169
189
|
const raw = makeManifest({
|
|
170
190
|
tools: [makeToolEntry({ input_schema: {} })],
|
|
@@ -78,6 +78,31 @@ describe("skills catalog loading", () => {
|
|
|
78
78
|
expect(catalog.map((skill) => skill.id)).toEqual(["alpha", "zeta"]);
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
test("parses supported host platforms from Vellum metadata", () => {
|
|
82
|
+
const skillDir = join(TEST_DIR, "skills", "platform-skill");
|
|
83
|
+
mkdirSync(skillDir, { recursive: true });
|
|
84
|
+
writeFileSync(
|
|
85
|
+
join(skillDir, "SKILL.md"),
|
|
86
|
+
`---
|
|
87
|
+
name: platform-skill
|
|
88
|
+
description: Platform-specific skill
|
|
89
|
+
metadata:
|
|
90
|
+
vellum:
|
|
91
|
+
platforms:
|
|
92
|
+
- macos
|
|
93
|
+
- linux
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
Skill body
|
|
97
|
+
`,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const skill = loadUserSkillCatalog().find(
|
|
101
|
+
(entry) => entry.id === "platform-skill",
|
|
102
|
+
);
|
|
103
|
+
expect(skill?.platforms).toEqual(["macos", "linux"]);
|
|
104
|
+
});
|
|
105
|
+
|
|
81
106
|
test("ignores stale SKILLS.md while discovering valid skill directories", () => {
|
|
82
107
|
writeSkill("first", "First Skill", "First");
|
|
83
108
|
writeSkill("second", "Second Skill", "Second");
|
|
@@ -533,6 +533,44 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
|
|
|
533
533
|
});
|
|
534
534
|
});
|
|
535
535
|
|
|
536
|
+
describe("SubagentManager hasActiveChildren", () => {
|
|
537
|
+
test("is true for pending, running, and awaiting_input children", () => {
|
|
538
|
+
const manager = new SubagentManager();
|
|
539
|
+
injectFakeSubagent(
|
|
540
|
+
manager,
|
|
541
|
+
"sub-running",
|
|
542
|
+
makeState("sub-running", { status: "running" }),
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
expect(manager.hasActiveChildren("parent-sess-1")).toBe(true);
|
|
546
|
+
|
|
547
|
+
asInternals(manager).subagents.get("sub-running")!.state.status =
|
|
548
|
+
"awaiting_input";
|
|
549
|
+
expect(manager.hasActiveChildren("parent-sess-1")).toBe(true);
|
|
550
|
+
|
|
551
|
+
asInternals(manager).subagents.get("sub-running")!.state.status = "pending";
|
|
552
|
+
expect(manager.hasActiveChildren("parent-sess-1")).toBe(true);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
test("is false when every child is terminal or the parent has none", () => {
|
|
556
|
+
const manager = new SubagentManager();
|
|
557
|
+
expect(manager.hasActiveChildren("parent-sess-1")).toBe(false);
|
|
558
|
+
|
|
559
|
+
injectFakeSubagent(
|
|
560
|
+
manager,
|
|
561
|
+
"sub-done",
|
|
562
|
+
makeState("sub-done", { status: "completed" }),
|
|
563
|
+
);
|
|
564
|
+
injectFakeSubagent(
|
|
565
|
+
manager,
|
|
566
|
+
"sub-aborted",
|
|
567
|
+
makeState("sub-aborted", { status: "aborted" }),
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
expect(manager.hasActiveChildren("parent-sess-1")).toBe(false);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
536
574
|
describe("SubagentManager abortAllForParent", () => {
|
|
537
575
|
test("aborts active children but keeps every child's state readable", () => {
|
|
538
576
|
clearCaptured();
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
KATA_INJECTED_ENV_VARS,
|
|
48
48
|
KATA_SAFE_ENV_VARS,
|
|
49
49
|
SAFE_ENV_VARS,
|
|
50
|
+
WINDOWS_SAFE_ENV_VARS,
|
|
50
51
|
} from "../tools/terminal/safe-env.js";
|
|
51
52
|
import { shellTool } from "../tools/terminal/shell.js";
|
|
52
53
|
|
|
@@ -194,6 +195,7 @@ describe("buildSanitizedEnv", () => {
|
|
|
194
195
|
...KATA_SAFE_ENV_VARS,
|
|
195
196
|
...KATA_INJECTED_ENV_VARS,
|
|
196
197
|
...ALWAYS_INJECTED_ENV_VARS,
|
|
198
|
+
...WINDOWS_SAFE_ENV_VARS,
|
|
197
199
|
];
|
|
198
200
|
for (const key of keys) {
|
|
199
201
|
expect(safeKeys).toContain(key);
|