@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
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards over the schedule worker entrypoint's feature flag bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* The override cache is process-local, so this worker has to populate its own.
|
|
5
|
+
* When it does not, every flag check inside it resolves to the registry default
|
|
6
|
+
* and ignores remote values and local overrides, and this is the process that
|
|
7
|
+
* decides at fire time whether a schedule runs. The entrypoint installs signal
|
|
8
|
+
* handlers and calls `process.exit`, so it cannot be imported into a test
|
|
9
|
+
* process; these assertions read its source the same way
|
|
10
|
+
* `src/__tests__/worker-entrypoint-guards.test.ts` does.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { describe, expect, test } from "bun:test";
|
|
16
|
+
|
|
17
|
+
const INIT_CALL = "await initFeatureFlagOverrides(";
|
|
18
|
+
const REFRESH_CALL = "refreshOverridesFromGateway(";
|
|
19
|
+
const REFRESH_INTERVAL = "FLAG_REFRESH_INTERVAL_MS";
|
|
20
|
+
|
|
21
|
+
/** The worker's first call that can execute a schedule. */
|
|
22
|
+
const WORK_START = "void tick()";
|
|
23
|
+
|
|
24
|
+
function readWorkerSource(): string {
|
|
25
|
+
return readFileSync(join(process.cwd(), "src/schedule/worker.ts"), "utf8");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("schedule worker feature flags", () => {
|
|
29
|
+
test("loads flag overrides before the first schedule tick", () => {
|
|
30
|
+
const source = readWorkerSource();
|
|
31
|
+
const initAt = source.indexOf(INIT_CALL);
|
|
32
|
+
const workAt = source.indexOf(WORK_START);
|
|
33
|
+
|
|
34
|
+
expect(
|
|
35
|
+
initAt,
|
|
36
|
+
`The schedule worker must \`${INIT_CALL})\` at startup. Without it ` +
|
|
37
|
+
"its process-local override cache stays unset and fire-time flag " +
|
|
38
|
+
"checks fall back to registry defaults.",
|
|
39
|
+
).toBeGreaterThanOrEqual(0);
|
|
40
|
+
expect(workAt).toBeGreaterThanOrEqual(0);
|
|
41
|
+
expect(
|
|
42
|
+
initAt < workAt,
|
|
43
|
+
"The flag load must complete before the first tick, so the schedules " +
|
|
44
|
+
"that tick fires are gated on real flag values.",
|
|
45
|
+
).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("re-reads the flag cache on a timer while running", () => {
|
|
49
|
+
const source = readWorkerSource();
|
|
50
|
+
|
|
51
|
+
expect(
|
|
52
|
+
source.includes(REFRESH_CALL) && source.includes(REFRESH_INTERVAL),
|
|
53
|
+
`This process outlives any single flag value, so it must call ` +
|
|
54
|
+
`${REFRESH_CALL}) on the ${REFRESH_INTERVAL} timer rather than ` +
|
|
55
|
+
"holding whatever was cached at startup.",
|
|
56
|
+
).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -41,15 +41,19 @@ export function decideRetry(
|
|
|
41
41
|
export async function applyRetryDecision(params: {
|
|
42
42
|
job: RetryPolicyJob;
|
|
43
43
|
isOneShot: boolean;
|
|
44
|
-
errorMsg: string;
|
|
45
44
|
decision: RetryDecision;
|
|
46
45
|
scheduleRetry: (id: string, nextRetryAt: number) => void | Promise<void>;
|
|
47
46
|
failOneShotPermanently: (id: string) => void | Promise<void>;
|
|
48
47
|
resetRetryCount: (id: string) => void | Promise<void>;
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Fired exactly once, when retries are exhausted. This is the single
|
|
50
|
+
* user-facing alert for a failed schedule (per-attempt failures are
|
|
51
|
+
* retried silently), and the caller owns its content and dedupe key.
|
|
52
|
+
*/
|
|
53
|
+
emitAlert: () => void;
|
|
50
54
|
log: Logger;
|
|
51
55
|
}): Promise<void> {
|
|
52
|
-
const { job, isOneShot,
|
|
56
|
+
const { job, isOneShot, decision } = params;
|
|
53
57
|
|
|
54
58
|
if (decision.action === "retry") {
|
|
55
59
|
await params.scheduleRetry(job.id, decision.nextRetryAt);
|
|
@@ -69,11 +73,7 @@ export async function applyRetryDecision(params: {
|
|
|
69
73
|
} else {
|
|
70
74
|
await params.resetRetryCount(job.id);
|
|
71
75
|
}
|
|
72
|
-
params.emitAlert(
|
|
73
|
-
`${job.name}: Retries exhausted`,
|
|
74
|
-
`Failed after ${job.retryCount + 1} attempt(s): ${errorMsg}`,
|
|
75
|
-
`schedule-retries-exhausted:${job.id}:${Date.now()}`,
|
|
76
|
-
);
|
|
76
|
+
params.emitAlert();
|
|
77
77
|
params.log.warn(
|
|
78
78
|
{ jobId: job.id, name: job.name, attempts: job.retryCount + 1 },
|
|
79
79
|
"Schedule retries exhausted",
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { buildSanitizedEnv } from "../tools/terminal/safe-env.js";
|
|
2
|
+
import {
|
|
3
|
+
buildShellInvocation,
|
|
4
|
+
terminateProcessTree,
|
|
5
|
+
} from "../util/host-process.js";
|
|
2
6
|
import { getLogger } from "../util/logger.js";
|
|
3
7
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
4
8
|
|
|
@@ -34,14 +38,13 @@ export interface ScriptResult {
|
|
|
34
38
|
/**
|
|
35
39
|
* Run a shell command and capture its output.
|
|
36
40
|
*
|
|
37
|
-
* Uses
|
|
38
|
-
*
|
|
39
|
-
* {@link MAX_OUTPUT_BYTES} to keep schedule_runs rows bounded.
|
|
41
|
+
* Uses the platform shell so the command string supports pipes, redirects,
|
|
42
|
+
* and shell builtins. Windows uses PowerShell and POSIX hosts use Bash. Output
|
|
43
|
+
* is truncated to {@link MAX_OUTPUT_BYTES} to keep schedule_runs rows bounded.
|
|
40
44
|
*
|
|
41
|
-
* The command runs in its own process group
|
|
42
|
-
* on timeout and swept after exit
|
|
43
|
-
*
|
|
44
|
-
* deliberately survives.
|
|
45
|
+
* The command runs in its own process group or Windows process tree. The tree
|
|
46
|
+
* is terminated on timeout and swept after exit so background children cannot
|
|
47
|
+
* outlive the run.
|
|
45
48
|
*/
|
|
46
49
|
export async function runScript(
|
|
47
50
|
command: string,
|
|
@@ -57,7 +60,8 @@ export async function runScript(
|
|
|
57
60
|
|
|
58
61
|
log.info({ command, cwd, timeoutMs }, "Running script");
|
|
59
62
|
|
|
60
|
-
const
|
|
63
|
+
const shell = buildShellInvocation(command);
|
|
64
|
+
const proc = Bun.spawn([shell.command, ...shell.args], {
|
|
61
65
|
cwd,
|
|
62
66
|
detached: true,
|
|
63
67
|
stdout: "pipe",
|
|
@@ -82,7 +86,7 @@ export async function runScript(
|
|
|
82
86
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
83
87
|
const timer = setTimeout(() => {
|
|
84
88
|
timedOut = true;
|
|
85
|
-
|
|
89
|
+
terminateProcessTree(proc);
|
|
86
90
|
reject(new Error(`Script timed out after ${timeoutMs}ms`));
|
|
87
91
|
}, timeoutMs);
|
|
88
92
|
timer.unref();
|
|
@@ -116,7 +120,7 @@ export async function runScript(
|
|
|
116
120
|
// Sweep anything the script left running. This reaps orphaned background
|
|
117
121
|
// children and closes their copies of the pipe fds so the stream reads
|
|
118
122
|
// below can reach EOF.
|
|
119
|
-
|
|
123
|
+
terminateProcessTree(proc);
|
|
120
124
|
|
|
121
125
|
const [stdoutStr, stderrStr] = await drainStreams(
|
|
122
126
|
stdoutCollector,
|
|
@@ -133,19 +137,6 @@ export async function runScript(
|
|
|
133
137
|
return { exitCode, stdout, stderr };
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
/**
|
|
137
|
-
* SIGKILL every process in the script's process group. The group is often
|
|
138
|
-
* already empty, in which case the signal fails with ESRCH and there is
|
|
139
|
-
* nothing to reap.
|
|
140
|
-
*/
|
|
141
|
-
function killProcessGroup(pid: number): void {
|
|
142
|
-
try {
|
|
143
|
-
process.kill(-pid, "SIGKILL");
|
|
144
|
-
} catch {
|
|
145
|
-
// Process group may have already exited.
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
140
|
interface StreamCollector {
|
|
150
141
|
/** Resolves with the text read so far once the stream ends or is cancelled. */
|
|
151
142
|
promise: Promise<string>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { refreshBackgroundWakeIntent } from "../background-wake/publisher.js";
|
|
2
|
+
import { resolveSingleRouteProfileKey } from "../config/llm-resolver.js";
|
|
2
3
|
import { getConfig } from "../config/loader.js";
|
|
3
4
|
import {
|
|
4
5
|
checkDiskPressureBackgroundGate,
|
|
@@ -7,13 +8,18 @@ import {
|
|
|
7
8
|
} from "../daemon/disk-pressure-background-gate.js";
|
|
8
9
|
import { processMessage } from "../daemon/process-message.js";
|
|
9
10
|
import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../daemon/trust-context.js";
|
|
11
|
+
import { emitBackgroundFailureSignal } from "../notifications/background-failure-signal.js";
|
|
10
12
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
11
13
|
import { getConversation } from "../persistence/conversation-crud.js";
|
|
12
14
|
import { isLifecycleQuiesced } from "../persistence/lifecycle-quiesce.js";
|
|
13
15
|
import { invalidateAssistantInferredItemsForConversation } from "../plugins/defaults/memory/task-memory-cleanup.js";
|
|
16
|
+
import { dispatchProviderResolvable } from "../providers/provider-resolvability.js";
|
|
14
17
|
import { wakeAgentForOpportunity } from "../runtime/agent-wake.js";
|
|
15
18
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
16
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
type BackgroundJobErrorKind,
|
|
21
|
+
runBackgroundJob,
|
|
22
|
+
} from "../runtime/background-job-runner.js";
|
|
17
23
|
import { publishConversationListChanged } from "../runtime/sync/resource-sync-events.js";
|
|
18
24
|
import { runSequencesOnce } from "../sequence/engine.js";
|
|
19
25
|
import type { TurnFailure } from "../telemetry/turn-outcome.js";
|
|
@@ -223,27 +229,84 @@ async function handleExecutionFailure(params: {
|
|
|
223
229
|
job: ScheduleJob;
|
|
224
230
|
errorMsg: string;
|
|
225
231
|
isOneShot: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Classified failure cause from the runner, when the failing path had one.
|
|
234
|
+
* Keys the exhaustion alert's dedupe so same-cause failures across jobs
|
|
235
|
+
* collapse; absent means a generic exception, which keys per schedule.
|
|
236
|
+
*/
|
|
237
|
+
errorKind?: BackgroundJobErrorKind;
|
|
238
|
+
/**
|
|
239
|
+
* The failing turn's carried classification, when the failure was a
|
|
240
|
+
* non-throwing turn failure. The exhaustion alert renders its authored
|
|
241
|
+
* message and scopes by the route that actually served the runs.
|
|
242
|
+
*/
|
|
243
|
+
turnFailure?: TurnFailure;
|
|
226
244
|
}): Promise<void> {
|
|
227
245
|
const decision = decideRetry(params.job);
|
|
228
246
|
await applyRetryDecision({
|
|
229
247
|
job: params.job,
|
|
230
248
|
isOneShot: params.isOneShot,
|
|
231
|
-
errorMsg: params.errorMsg,
|
|
232
249
|
decision,
|
|
233
250
|
scheduleRetry,
|
|
234
251
|
failOneShotPermanently,
|
|
235
252
|
resetRetryCount,
|
|
236
|
-
emitAlert: (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
253
|
+
emitAlert: () => {
|
|
254
|
+
const fallbackProviderScope = resolveScheduleProviderScope(params.job);
|
|
255
|
+
const carried = params.turnFailure;
|
|
256
|
+
emitBackgroundFailureSignal({
|
|
257
|
+
jobName: `schedule:${params.job.id}`,
|
|
258
|
+
displayName: `schedule:${params.job.name}`,
|
|
259
|
+
sourceChannel: "scheduler",
|
|
260
|
+
sourceContextId: params.job.id,
|
|
261
|
+
errorKind: params.errorKind ?? "exception",
|
|
240
262
|
errorMessage: params.errorMsg,
|
|
241
|
-
|
|
242
|
-
|
|
263
|
+
...(carried?.failureCode !== undefined
|
|
264
|
+
? { failureCode: carried.failureCode }
|
|
265
|
+
: {}),
|
|
266
|
+
...(carried?.userMessage !== undefined
|
|
267
|
+
? { failureSummary: carried.userMessage }
|
|
268
|
+
: {}),
|
|
269
|
+
...(carried?.errorCategory !== undefined
|
|
270
|
+
? { errorCategory: carried.errorCategory }
|
|
271
|
+
: {}),
|
|
272
|
+
...(carried?.connectionName !== undefined
|
|
273
|
+
? { connectionName: carried.connectionName }
|
|
274
|
+
: {}),
|
|
275
|
+
...(carried?.profileName !== undefined
|
|
276
|
+
? { profileName: carried.profileName }
|
|
277
|
+
: {}),
|
|
278
|
+
...(fallbackProviderScope !== undefined
|
|
279
|
+
? { fallbackProviderScope }
|
|
280
|
+
: {}),
|
|
281
|
+
});
|
|
282
|
+
},
|
|
243
283
|
log,
|
|
244
284
|
});
|
|
245
285
|
}
|
|
246
286
|
|
|
287
|
+
/**
|
|
288
|
+
* The provider scope a schedule's failing runs resolved through: its pinned
|
|
289
|
+
* profile when that names a single provider route, else the route the
|
|
290
|
+
* mainAgent winner actually served (a deleted pin falls through to it). A
|
|
291
|
+
* mix pin, no named winner, or an unreadable config yield no scope, so the
|
|
292
|
+
* exhaustion alert keys per schedule rather than claiming one scope for
|
|
293
|
+
* arms that can span providers.
|
|
294
|
+
*/
|
|
295
|
+
function resolveScheduleProviderScope(job: ScheduleJob): string | undefined {
|
|
296
|
+
try {
|
|
297
|
+
return resolveSingleRouteProfileKey("mainAgent", getConfig().llm, {
|
|
298
|
+
...(job.inferenceProfile
|
|
299
|
+
? { overrideProfile: job.inferenceProfile }
|
|
300
|
+
: {}),
|
|
301
|
+
// Dispatch's own resolvability predicate, so a stale pin dispatch
|
|
302
|
+
// rejected (force-deleted connection) is not accepted as the scope.
|
|
303
|
+
isResolvableProvider: dispatchProviderResolvable,
|
|
304
|
+
});
|
|
305
|
+
} catch {
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
247
310
|
/** The running scheduler, retained so shutdown can stop it. */
|
|
248
311
|
let instance: SchedulerHandle | null = null;
|
|
249
312
|
|
|
@@ -896,6 +959,8 @@ export async function runDueSchedulesOnce(
|
|
|
896
959
|
let conversationId: string;
|
|
897
960
|
let ok: boolean;
|
|
898
961
|
let errorMsg: string | undefined;
|
|
962
|
+
let errorKind: BackgroundJobErrorKind | undefined;
|
|
963
|
+
let failedTurn: TurnFailure | undefined;
|
|
899
964
|
const conversationReused = reusedConversationId != null;
|
|
900
965
|
let runConversationId = reusedConversationId;
|
|
901
966
|
const runId = await createScheduleRun(job.id, reusedConversationId);
|
|
@@ -929,17 +994,20 @@ export async function runDueSchedulesOnce(
|
|
|
929
994
|
if (turnFailure) {
|
|
930
995
|
ok = false;
|
|
931
996
|
errorMsg = describeTurnFailure(turnFailure);
|
|
997
|
+
errorKind = "model_provider";
|
|
998
|
+
failedTurn = turnFailure;
|
|
932
999
|
} else {
|
|
933
1000
|
ok = true;
|
|
934
1001
|
}
|
|
935
1002
|
} catch (err) {
|
|
936
1003
|
ok = false;
|
|
937
1004
|
errorMsg = err instanceof Error ? err.message : String(err);
|
|
1005
|
+
errorKind = "exception";
|
|
938
1006
|
}
|
|
939
1007
|
} else {
|
|
940
|
-
// Fresh-bootstrap path: route through the shared runner
|
|
941
|
-
//
|
|
942
|
-
//
|
|
1008
|
+
// Fresh-bootstrap path: route through the shared runner for the
|
|
1009
|
+
// standard timeout + error-classification policy applied to every
|
|
1010
|
+
// background producer.
|
|
943
1011
|
// The runner fires `onConversationCreated` synchronously after bootstrap
|
|
944
1012
|
// (before `processMessage` starts) so the macOS sidebar gets the new
|
|
945
1013
|
// conversation immediately rather than after the up-to-30-min job ends.
|
|
@@ -962,7 +1030,12 @@ export async function runDueSchedulesOnce(
|
|
|
962
1030
|
groupId: resolveScheduleConversationGroupId(job),
|
|
963
1031
|
conversationType: "scheduled",
|
|
964
1032
|
scheduleJobId: job.id,
|
|
965
|
-
|
|
1033
|
+
// The retry policy owns the single user-facing alert for a failing
|
|
1034
|
+
// schedule (fired once, when retries are exhausted). Letting the
|
|
1035
|
+
// runner also emit per attempt would notify for failures that a
|
|
1036
|
+
// retry may yet recover, and pair every exhaustion alert with a
|
|
1037
|
+
// duplicate.
|
|
1038
|
+
suppressFailureNotifications: true,
|
|
966
1039
|
onConversationCreated: async (newConversationId) => {
|
|
967
1040
|
runConversationId = newConversationId;
|
|
968
1041
|
await setScheduleRunConversationId(runId, newConversationId);
|
|
@@ -988,6 +1061,8 @@ export async function runDueSchedulesOnce(
|
|
|
988
1061
|
}
|
|
989
1062
|
ok = result.ok;
|
|
990
1063
|
errorMsg = result.error?.message;
|
|
1064
|
+
errorKind = result.errorKind;
|
|
1065
|
+
failedTurn = result.turnFailure;
|
|
991
1066
|
}
|
|
992
1067
|
|
|
993
1068
|
if (ok) {
|
|
@@ -1016,6 +1091,8 @@ export async function runDueSchedulesOnce(
|
|
|
1016
1091
|
job,
|
|
1017
1092
|
errorMsg: errorMsg ?? "Schedule run failed",
|
|
1018
1093
|
isOneShot,
|
|
1094
|
+
...(errorKind !== undefined ? { errorKind } : {}),
|
|
1095
|
+
...(failedTurn !== undefined ? { turnFailure: failedTurn } : {}),
|
|
1019
1096
|
});
|
|
1020
1097
|
|
|
1021
1098
|
// Only skip invalidation when the conversation was *actually* reused,
|
|
@@ -1039,42 +1116,3 @@ export async function runDueSchedulesOnce(
|
|
|
1039
1116
|
|
|
1040
1117
|
return result;
|
|
1041
1118
|
}
|
|
1042
|
-
|
|
1043
|
-
/**
|
|
1044
|
-
* Emit an `activity.failed` notification for a schedule whose retries have
|
|
1045
|
-
* been exhausted. Fires once when the retry policy has given up, so the
|
|
1046
|
-
* dedupeKey caller is the per-attempt key passed in by `applyRetryDecision`
|
|
1047
|
-
* (already includes the job id and a timestamp).
|
|
1048
|
-
*/
|
|
1049
|
-
function emitScheduleActivityFailed(args: {
|
|
1050
|
-
jobId: string;
|
|
1051
|
-
jobName: string;
|
|
1052
|
-
errorMessage: string;
|
|
1053
|
-
dedupKey: string;
|
|
1054
|
-
}): void {
|
|
1055
|
-
emitNotificationSignal({
|
|
1056
|
-
sourceChannel: "scheduler",
|
|
1057
|
-
sourceContextId: args.jobId,
|
|
1058
|
-
sourceEventName: "activity.failed",
|
|
1059
|
-
dedupeKey: args.dedupKey,
|
|
1060
|
-
contextPayload: {
|
|
1061
|
-
jobName: `schedule:${args.jobName}`,
|
|
1062
|
-
errorMessage: args.errorMessage,
|
|
1063
|
-
errorKind: "exception",
|
|
1064
|
-
},
|
|
1065
|
-
attentionHints: {
|
|
1066
|
-
requiresAction: false,
|
|
1067
|
-
urgency: "medium",
|
|
1068
|
-
isAsyncBackground: true,
|
|
1069
|
-
visibleInSourceNow: false,
|
|
1070
|
-
},
|
|
1071
|
-
}).catch((emitErr) => {
|
|
1072
|
-
log.warn(
|
|
1073
|
-
{
|
|
1074
|
-
err: emitErr instanceof Error ? emitErr.message : String(emitErr),
|
|
1075
|
-
jobId: args.jobId,
|
|
1076
|
-
},
|
|
1077
|
-
"Failed to emit activity.failed notification for exhausted schedule",
|
|
1078
|
-
);
|
|
1079
|
-
});
|
|
1080
|
-
}
|
package/src/schedule/worker.ts
CHANGED
|
@@ -13,8 +13,13 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { writeFileSync } from "node:fs";
|
|
15
15
|
|
|
16
|
+
import {
|
|
17
|
+
initFeatureFlagOverrides,
|
|
18
|
+
refreshOverridesFromGateway,
|
|
19
|
+
} from "../config/assistant-feature-flags.js";
|
|
16
20
|
import { getConfig } from "../config/loader.js";
|
|
17
21
|
import { rehydratePlatformCredentials } from "../config/platform-rehydration.js";
|
|
22
|
+
import { startConversationEvictor } from "../daemon/conversation-evictor.js";
|
|
18
23
|
import { resetDb } from "../persistence/db-connection.js";
|
|
19
24
|
import { registerWorkerPluginSurface } from "../plugins/worker-plugin-surface.js";
|
|
20
25
|
import { disableStreamSeqStamping } from "../runtime/assistant-stream-state.js";
|
|
@@ -32,6 +37,9 @@ const log = getLogger("schedule-worker-process");
|
|
|
32
37
|
/** Same cadence as the daemon scheduler's tick. */
|
|
33
38
|
const TICK_INTERVAL_MS = 15_000;
|
|
34
39
|
|
|
40
|
+
/** How often this process re-reads flag overrides from the gateway. */
|
|
41
|
+
const FLAG_REFRESH_INTERVAL_MS = 60_000;
|
|
42
|
+
|
|
35
43
|
async function main(): Promise<void> {
|
|
36
44
|
// Only the daemon stamps SSE seqs and writes the shared reservation file.
|
|
37
45
|
disableStreamSeqStamping();
|
|
@@ -44,6 +52,29 @@ async function main(): Promise<void> {
|
|
|
44
52
|
writeFileSync(pidPath, String(process.pid), { flag: "w" });
|
|
45
53
|
log.info({ pid: process.pid, pidPath }, "Schedule worker process started");
|
|
46
54
|
|
|
55
|
+
// The override cache is per-process, so this worker has to populate its own
|
|
56
|
+
// even though the daemon already populated the daemon's. Without it every
|
|
57
|
+
// flag check here resolves to its registry default and ignores both remote
|
|
58
|
+
// values and local overrides, which decides at fire time whether a schedule
|
|
59
|
+
// runs at all. Awaited so the first tick reads real values, and best-effort:
|
|
60
|
+
// a gateway that never answers leaves the cache unset and the worker falls
|
|
61
|
+
// back to registry defaults rather than failing to start.
|
|
62
|
+
// Bounded to one short attempt: a gateway that accepts the socket but never
|
|
63
|
+
// answers would otherwise hold the first tick for the full production retry
|
|
64
|
+
// schedule. A miss here leaves the cache unset (registry defaults) and the
|
|
65
|
+
// periodic refresh below converges to real values within a minute.
|
|
66
|
+
try {
|
|
67
|
+
await initFeatureFlagOverrides({
|
|
68
|
+
retryBackoffsMs: [],
|
|
69
|
+
callTimeoutMs: 2_000,
|
|
70
|
+
});
|
|
71
|
+
} catch (err) {
|
|
72
|
+
log.warn(
|
|
73
|
+
{ err },
|
|
74
|
+
"Failed to load feature flag overrides in schedule worker; continuing on registry defaults",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
47
78
|
// Rehydrate the platform base URL and IDs from the credential store before
|
|
48
79
|
// the first tick. The daemon does this in initializeProvidersAndTools(); this
|
|
49
80
|
// standalone process must do it itself so getPlatformBaseUrl() resolves to
|
|
@@ -71,9 +102,15 @@ async function main(): Promise<void> {
|
|
|
71
102
|
);
|
|
72
103
|
}
|
|
73
104
|
|
|
105
|
+
// Sweep idle conversations out of the in-memory pool. The daemon starts
|
|
106
|
+
// this at startup; this process hosts conversations too, so without it
|
|
107
|
+
// every scheduled run's conversation is retained for the process lifetime.
|
|
108
|
+
startConversationEvictor();
|
|
109
|
+
|
|
74
110
|
let stopped = false;
|
|
75
111
|
let tickRunning = false;
|
|
76
112
|
let timer: ReturnType<typeof setInterval> | null = null;
|
|
113
|
+
let flagRefreshTimer: ReturnType<typeof setInterval> | null = null;
|
|
77
114
|
let disposePidGuard: (() => void) | null = null;
|
|
78
115
|
const shutdown = (signal: string) => {
|
|
79
116
|
log.info({ signal }, "Schedule worker process shutting down");
|
|
@@ -81,6 +118,9 @@ async function main(): Promise<void> {
|
|
|
81
118
|
if (timer != null) {
|
|
82
119
|
clearInterval(timer);
|
|
83
120
|
}
|
|
121
|
+
if (flagRefreshTimer != null) {
|
|
122
|
+
clearInterval(flagRefreshTimer);
|
|
123
|
+
}
|
|
84
124
|
disposePidGuard?.();
|
|
85
125
|
cleanupWorkerPidFile(pidPath);
|
|
86
126
|
process.exit(0);
|
|
@@ -127,6 +167,20 @@ async function main(): Promise<void> {
|
|
|
127
167
|
}, TICK_INTERVAL_MS);
|
|
128
168
|
void tick();
|
|
129
169
|
|
|
170
|
+
// This process outlives any single flag value, so poll the gateway instead of
|
|
171
|
+
// holding whatever was cached at startup. The daemon's gateway flag listener
|
|
172
|
+
// is not reusable here: it also reconciles managed profiles and broadcasts
|
|
173
|
+
// sync events, both of which belong to the daemon alone. The refresh swaps the
|
|
174
|
+
// cache atomically, so a tick reading a flag mid-refresh still sees the last
|
|
175
|
+
// known values. Unref'd so the tick interval above stays the one thing
|
|
176
|
+
// keeping this process alive.
|
|
177
|
+
flagRefreshTimer = setInterval(() => {
|
|
178
|
+
void refreshOverridesFromGateway().catch((err) => {
|
|
179
|
+
log.warn({ err }, "Failed to refresh feature flag overrides");
|
|
180
|
+
});
|
|
181
|
+
}, FLAG_REFRESH_INTERVAL_MS);
|
|
182
|
+
flagRefreshTimer.unref();
|
|
183
|
+
|
|
130
184
|
process.on("SIGUSR1", () => {
|
|
131
185
|
log.info("Received SIGUSR1 — refreshing database connections");
|
|
132
186
|
resetDb();
|
|
@@ -155,6 +209,9 @@ async function main(): Promise<void> {
|
|
|
155
209
|
if (timer != null) {
|
|
156
210
|
clearInterval(timer);
|
|
157
211
|
}
|
|
212
|
+
if (flagRefreshTimer != null) {
|
|
213
|
+
clearInterval(flagRefreshTimer);
|
|
214
|
+
}
|
|
158
215
|
cleanupWorkerPidFile(getScheduleWorkerPidPath());
|
|
159
216
|
});
|
|
160
217
|
}
|