@vellumai/assistant 0.6.3 → 0.6.4
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 +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +5 -13
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/openapi.yaml +982 -72
- package/package.json +4 -6
- package/scripts/generate-openapi.ts +0 -1
- package/scripts/test.sh +73 -18
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +11 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +138 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -0
- package/src/__tests__/config-schema.test.ts +1013 -66
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +98 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -4
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +901 -60
- package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/credential-vault-unit.test.ts +379 -3
- package/src/__tests__/credentials-cli.test.ts +40 -16
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
- package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/headless-browser-interactions.test.ts +43 -0
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +142 -5
- package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +0 -5
- package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
- package/src/__tests__/host-cu-proxy.test.ts +0 -5
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-export-http.test.ts +6 -6
- package/src/__tests__/migration-import-commit-http.test.ts +8 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +2 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
- package/src/__tests__/oauth-providers-routes.test.ts +2 -0
- package/src/__tests__/oauth-store.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
- package/src/__tests__/onboarding-template-contract.test.ts +6 -13
- package/src/__tests__/openai-provider.test.ts +176 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +5 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
- package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +564 -1
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +112 -26
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +18 -7
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -0
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +43 -3
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- package/src/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/types.ts +16 -0
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +0 -1
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +255 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +15 -0
- package/src/cli/commands/oauth/shared.ts +2 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
- package/src/cli/program.ts +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
- package/src/config/bundled-skills/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +2 -2
- package/src/config/bundled-skills/gmail/SKILL.md +53 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +2 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +24 -0
- package/src/config/env.ts +34 -10
- package/src/config/feature-flag-registry.json +46 -14
- package/src/config/loader.ts +26 -12
- package/src/config/schema.ts +35 -10
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +47 -1
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/services.ts +44 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +3 -2
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +45 -12
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +101 -24
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +581 -19
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +11 -1
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +94 -4
- package/src/daemon/conversation-tool-setup.ts +25 -0
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation.ts +86 -28
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +4 -1
- package/src/daemon/handlers/shared.ts +22 -0
- package/src/daemon/handlers/skills.ts +321 -77
- package/src/daemon/host-browser-proxy.ts +2 -1
- package/src/daemon/lifecycle.ts +122 -25
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/conversations.ts +34 -1
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +14 -0
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +16 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +347 -2
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +9 -0
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +12 -3
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +1 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +99 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -5
- package/src/memory/db-init.ts +6 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +27 -18
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +92 -56
- package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
- package/src/oauth/byo-connection.test.ts +18 -1
- package/src/oauth/byo-connection.ts +3 -1
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -2
- package/src/oauth/connection.ts +2 -0
- package/src/oauth/oauth-store.ts +9 -0
- package/src/oauth/platform-connection.test.ts +98 -0
- package/src/oauth/platform-connection.ts +52 -31
- package/src/oauth/seed-providers.ts +7 -0
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +59 -18
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -61
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +9 -0
- package/src/runtime/AGENTS.md +43 -1
- package/src/runtime/__tests__/agent-wake.test.ts +831 -0
- package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -0
- package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
- package/src/runtime/auth/route-policy.ts +30 -5
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +10 -10
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +38 -2
- package/src/runtime/http-server.ts +395 -10
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +1 -0
- package/src/runtime/migrations/migration-wizard.ts +1 -0
- package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
- package/src/runtime/migrations/vbundle-importer.ts +34 -0
- package/src/runtime/pending-interactions.ts +0 -11
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
- package/src/runtime/routes/conversation-management-routes.ts +115 -0
- package/src/runtime/routes/conversation-routes.ts +367 -146
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +3 -14
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +3 -17
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/migration-routes.ts +40 -5
- package/src/runtime/routes/settings-routes.ts +22 -5
- package/src/runtime/routes/skills-routes.ts +76 -7
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +30 -2
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- package/src/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +50 -0
- package/src/security/oauth2.ts +26 -4
- package/src/security/secure-keys.ts +25 -2
- package/src/security/token-manager.ts +8 -0
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +64 -2
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +38 -14
- package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/browser-execution.ts +1163 -23
- package/src/tools/browser/browser-manager.ts +45 -0
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
- package/src/tools/browser/cdp-client/errors.ts +15 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
- package/src/tools/browser/cdp-client/factory.ts +797 -87
- package/src/tools/browser/cdp-client/index.ts +16 -2
- package/src/tools/browser/cdp-client/types.ts +68 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- package/src/tools/terminal/safe-env.ts +10 -2
- package/src/tools/terminal/shell.ts +15 -4
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +17 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +54 -10
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +13 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/cli/commands/browser-relay.ts +0 -466
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/runtime/routes/browser-cdp-routes.ts +0 -229
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
Provider,
|
|
7
7
|
} from "../providers/types.js";
|
|
8
8
|
import { getLogger } from "../util/logger.js";
|
|
9
|
+
import { safeStringSlice } from "../util/unicode.js";
|
|
9
10
|
import {
|
|
10
11
|
estimateContentBlockTokens,
|
|
11
12
|
estimatePromptTokens,
|
|
@@ -110,6 +111,16 @@ export class ContextWindowManager {
|
|
|
110
111
|
* after a successful compaction pass.
|
|
111
112
|
*/
|
|
112
113
|
nonPersistedPrefixCount = 0;
|
|
114
|
+
/**
|
|
115
|
+
* True when the message at index 0 is a context summary that was inherited
|
|
116
|
+
* from a parent fork (i.e. injected as part of the non-persisted prefix),
|
|
117
|
+
* rather than produced by this conversation's own compaction. The parent
|
|
118
|
+
* summary sits at index 0 but is excluded from `compactableMessages` by
|
|
119
|
+
* `summaryOffset`, so its slot in `nonPersistedPrefixCount` must be
|
|
120
|
+
* accounted for separately. Cleared after the first compaction replaces
|
|
121
|
+
* the parent summary with a child-owned one.
|
|
122
|
+
*/
|
|
123
|
+
summaryIsInjected = false;
|
|
113
124
|
/**
|
|
114
125
|
* Cached resolved system prompt. Lazily populated on first access via the
|
|
115
126
|
* `systemPrompt` getter and cleared after each compaction pass so the next
|
|
@@ -124,6 +135,16 @@ export class ContextWindowManager {
|
|
|
124
135
|
this.toolTokenBudget = options.toolTokenBudget ?? 0;
|
|
125
136
|
}
|
|
126
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Provider key for the local token estimator. Wrapper providers (e.g.
|
|
140
|
+
* OpenRouter routing to `anthropic/*`) override `tokenEstimationProvider`
|
|
141
|
+
* so image/PDF sizing uses the same rules as the upstream API instead of
|
|
142
|
+
* the generic `base64/4` fallback.
|
|
143
|
+
*/
|
|
144
|
+
private get estimationProviderName(): string {
|
|
145
|
+
return this.provider.tokenEstimationProvider ?? this.provider.name;
|
|
146
|
+
}
|
|
147
|
+
|
|
127
148
|
/** Lazily resolve and cache the system prompt for the duration of a compaction pass. */
|
|
128
149
|
private get systemPrompt(): string {
|
|
129
150
|
if (this._resolvedSystemPrompt !== undefined) {
|
|
@@ -151,7 +172,7 @@ export class ContextWindowManager {
|
|
|
151
172
|
if (!this.config.enabled) return { needed: false, estimatedTokens: 0 };
|
|
152
173
|
try {
|
|
153
174
|
const estimated = estimatePromptTokens(messages, this.systemPrompt, {
|
|
154
|
-
providerName: this.
|
|
175
|
+
providerName: this.estimationProviderName,
|
|
155
176
|
toolTokenBudget: this.toolTokenBudget,
|
|
156
177
|
});
|
|
157
178
|
const threshold = Math.floor(
|
|
@@ -183,7 +204,7 @@ export class ContextWindowManager {
|
|
|
183
204
|
const previousEstimatedInputTokens =
|
|
184
205
|
options?.precomputedEstimate ??
|
|
185
206
|
estimatePromptTokens(messages, this.systemPrompt, {
|
|
186
|
-
providerName: this.
|
|
207
|
+
providerName: this.estimationProviderName,
|
|
187
208
|
toolTokenBudget: this.toolTokenBudget,
|
|
188
209
|
});
|
|
189
210
|
const thresholdTokens = Math.floor(
|
|
@@ -266,7 +287,7 @@ export class ContextWindowManager {
|
|
|
266
287
|
const didTruncate = truncatedCount > 0;
|
|
267
288
|
const estimatedAfterTruncation = didTruncate
|
|
268
289
|
? estimatePromptTokens(truncatedMessages, this.systemPrompt, {
|
|
269
|
-
providerName: this.
|
|
290
|
+
providerName: this.estimationProviderName,
|
|
270
291
|
toolTokenBudget: this.toolTokenBudget,
|
|
271
292
|
})
|
|
272
293
|
: previousEstimatedInputTokens;
|
|
@@ -313,8 +334,15 @@ export class ContextWindowManager {
|
|
|
313
334
|
};
|
|
314
335
|
}
|
|
315
336
|
|
|
337
|
+
// When the summary at index 0 was injected from a parent fork, it
|
|
338
|
+
// contributes 1 to `nonPersistedPrefixCount` but is excluded from
|
|
339
|
+
// `compactableMessages` by `summaryOffset`; subtract it here so the
|
|
340
|
+
// remaining injected count lines up with compactableMessages. A summary
|
|
341
|
+
// produced by this conversation's own prior compaction is not part of
|
|
342
|
+
// `nonPersistedPrefixCount` (already decremented), so no subtraction.
|
|
343
|
+
const injectedSummaryOffset = this.summaryIsInjected ? summaryOffset : 0;
|
|
316
344
|
const injectedInCompactable = Math.min(
|
|
317
|
-
Math.max(0, this.nonPersistedPrefixCount -
|
|
345
|
+
Math.max(0, this.nonPersistedPrefixCount - injectedSummaryOffset),
|
|
318
346
|
compactableMessages.length,
|
|
319
347
|
);
|
|
320
348
|
const compactedPersistedMessages =
|
|
@@ -331,7 +359,7 @@ export class ContextWindowManager {
|
|
|
331
359
|
projectedMessages,
|
|
332
360
|
this.systemPrompt,
|
|
333
361
|
{
|
|
334
|
-
providerName: this.
|
|
362
|
+
providerName: this.estimationProviderName,
|
|
335
363
|
toolTokenBudget: this.toolTokenBudget,
|
|
336
364
|
},
|
|
337
365
|
);
|
|
@@ -460,15 +488,20 @@ export class ContextWindowManager {
|
|
|
460
488
|
compactedMessages,
|
|
461
489
|
this.systemPrompt,
|
|
462
490
|
{
|
|
463
|
-
providerName: this.
|
|
491
|
+
providerName: this.estimationProviderName,
|
|
464
492
|
toolTokenBudget: this.toolTokenBudget,
|
|
465
493
|
},
|
|
466
494
|
);
|
|
467
|
-
// Consume the injected prefix messages that were compacted away.
|
|
495
|
+
// Consume the injected prefix messages that were compacted away. When the
|
|
496
|
+
// parent-injected summary was replaced by a freshly produced child summary,
|
|
497
|
+
// also consume its slot (it was excluded from injectedInCompactable via
|
|
498
|
+
// injectedSummaryOffset) and clear the flag so subsequent compactions treat
|
|
499
|
+
// the summary at index 0 as child-owned.
|
|
468
500
|
this.nonPersistedPrefixCount = Math.max(
|
|
469
501
|
0,
|
|
470
|
-
this.nonPersistedPrefixCount - injectedInCompactable,
|
|
502
|
+
this.nonPersistedPrefixCount - injectedInCompactable - injectedSummaryOffset,
|
|
471
503
|
);
|
|
504
|
+
this.summaryIsInjected = false;
|
|
472
505
|
|
|
473
506
|
log.info(
|
|
474
507
|
{
|
|
@@ -541,7 +574,7 @@ export class ContextWindowManager {
|
|
|
541
574
|
COMPACTION_TOOL_RESULT_MAX_CHARS,
|
|
542
575
|
);
|
|
543
576
|
return estimatePromptTokens(projectedMessages, this.systemPrompt, {
|
|
544
|
-
providerName: this.
|
|
577
|
+
providerName: this.estimationProviderName,
|
|
545
578
|
toolTokenBudget: this.toolTokenBudget,
|
|
546
579
|
});
|
|
547
580
|
};
|
|
@@ -610,7 +643,7 @@ export class ContextWindowManager {
|
|
|
610
643
|
);
|
|
611
644
|
|
|
612
645
|
const estimateBlockTokens = (b: ContentBlock): number =>
|
|
613
|
-
estimateContentBlockTokens(b, { providerName: this.
|
|
646
|
+
estimateContentBlockTokens(b, { providerName: this.estimationProviderName });
|
|
614
647
|
|
|
615
648
|
let totalTokens = 0;
|
|
616
649
|
for (const block of blocks) {
|
|
@@ -752,7 +785,7 @@ export class ContextWindowManager {
|
|
|
752
785
|
// Budget in tokens → approximate char limit (4 chars ≈ 1 token).
|
|
753
786
|
const maxChars = this.summaryMaxTokens * 4;
|
|
754
787
|
if (summary.length <= maxChars) return summary;
|
|
755
|
-
return `${summary
|
|
788
|
+
return `${safeStringSlice(summary, 0, maxChars)}...`;
|
|
756
789
|
}
|
|
757
790
|
}
|
|
758
791
|
|
|
@@ -1025,7 +1058,7 @@ function serializeBlock(block: ContentBlock): string {
|
|
|
1025
1058
|
|
|
1026
1059
|
function clampText(text: string): string {
|
|
1027
1060
|
if (text.length <= MAX_BLOCK_PREVIEW_CHARS) return text;
|
|
1028
|
-
return `${text
|
|
1061
|
+
return `${safeStringSlice(text, 0, MAX_BLOCK_PREVIEW_CHARS)}... [truncated ${
|
|
1029
1062
|
text.length - MAX_BLOCK_PREVIEW_CHARS
|
|
1030
1063
|
} chars]`;
|
|
1031
1064
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { existsSync } from "node:fs";
|
|
20
|
-
import { join } from "node:path";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
21
|
|
|
22
22
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
23
23
|
import { getLogger } from "../util/logger.js";
|
|
@@ -65,9 +65,19 @@ function getManagedBootstrapSocketPath(): string {
|
|
|
65
65
|
* `getDataDir()` (under `~/.vellum/workspace/data`) was previously included
|
|
66
66
|
* but is inside the sandbox write boundary, so a sandboxed tool could plant
|
|
67
67
|
* a malicious binary there. Removed to close the sandbox-escape vector.
|
|
68
|
+
*
|
|
69
|
+
* Search order:
|
|
70
|
+
* 1. Alongside the running executable (packaged macOS app:
|
|
71
|
+
* `<App>.app/Contents/MacOS/credential-executor`). When running from
|
|
72
|
+
* source via `bun run`, `process.execPath` points at the bun binary
|
|
73
|
+
* itself, so this path won't exist and the search falls through.
|
|
74
|
+
* 2. `<binDir>/credential-executor` — user-installed override (dev flow).
|
|
68
75
|
*/
|
|
69
76
|
function getLocalBinarySearchPaths(): string[] {
|
|
70
|
-
return [
|
|
77
|
+
return [
|
|
78
|
+
join(dirname(process.execPath), "credential-executor"),
|
|
79
|
+
join(getBinDir(), "credential-executor"),
|
|
80
|
+
];
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
// ---------------------------------------------------------------------------
|
|
@@ -240,7 +240,7 @@ export function createCesProcessManager(
|
|
|
240
240
|
cmd: [discovery.executablePath],
|
|
241
241
|
stdin: "pipe",
|
|
242
242
|
stdout: "pipe",
|
|
243
|
-
stderr: "
|
|
243
|
+
stderr: "pipe",
|
|
244
244
|
env: {
|
|
245
245
|
...process.env,
|
|
246
246
|
// Signal to CES that it was launched by the assistant
|
|
@@ -251,6 +251,7 @@ export function createCesProcessManager(
|
|
|
251
251
|
childProcess = proc;
|
|
252
252
|
|
|
253
253
|
log.info({ pid: proc.pid }, "CES child process started");
|
|
254
|
+
forwardStderrToLogger(proc);
|
|
254
255
|
|
|
255
256
|
return createStdioTransport(proc);
|
|
256
257
|
}
|
|
@@ -272,7 +273,7 @@ export function createCesProcessManager(
|
|
|
272
273
|
cmd: [bunPath, "run", discovery.sourcePath],
|
|
273
274
|
stdin: "pipe",
|
|
274
275
|
stdout: "pipe",
|
|
275
|
-
stderr: "
|
|
276
|
+
stderr: "pipe",
|
|
276
277
|
env: {
|
|
277
278
|
...process.env,
|
|
278
279
|
// Signal to CES that it was launched by the assistant
|
|
@@ -283,6 +284,7 @@ export function createCesProcessManager(
|
|
|
283
284
|
childProcess = proc;
|
|
284
285
|
|
|
285
286
|
log.info({ pid: proc.pid }, "CES child process started (from source)");
|
|
287
|
+
forwardStderrToLogger(proc);
|
|
286
288
|
|
|
287
289
|
return createStdioTransport(proc);
|
|
288
290
|
}
|
|
@@ -442,6 +444,35 @@ function createSocketTransport(socket: Socket): CesTransport {
|
|
|
442
444
|
// Helpers
|
|
443
445
|
// ---------------------------------------------------------------------------
|
|
444
446
|
|
|
447
|
+
function forwardStderrToLogger(proc: Subprocess): void {
|
|
448
|
+
if (!proc.stderr || typeof proc.stderr === "number") return;
|
|
449
|
+
|
|
450
|
+
const reader = proc.stderr.getReader();
|
|
451
|
+
const decoder = new TextDecoder();
|
|
452
|
+
let buffer = "";
|
|
453
|
+
|
|
454
|
+
void (async () => {
|
|
455
|
+
try {
|
|
456
|
+
while (true) {
|
|
457
|
+
const { value, done } = await reader.read();
|
|
458
|
+
if (done) break;
|
|
459
|
+
|
|
460
|
+
buffer += decoder.decode(value, { stream: true });
|
|
461
|
+
let newlineIdx: number;
|
|
462
|
+
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
463
|
+
const line = buffer.slice(0, newlineIdx).trimEnd();
|
|
464
|
+
buffer = buffer.slice(newlineIdx + 1);
|
|
465
|
+
if (line) log.error({ pid: proc.pid }, `[ces-stderr] ${line}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const trailing = buffer.trimEnd();
|
|
469
|
+
if (trailing) log.error({ pid: proc.pid }, `[ces-stderr] ${trailing}`);
|
|
470
|
+
} catch {
|
|
471
|
+
// Process ended or stream closed; nothing to forward.
|
|
472
|
+
}
|
|
473
|
+
})();
|
|
474
|
+
}
|
|
475
|
+
|
|
445
476
|
async function stopLocalProcess(proc: Subprocess): Promise<void> {
|
|
446
477
|
log.info({ pid: proc.pid }, "Stopping CES child process");
|
|
447
478
|
proc.kill("SIGTERM");
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proactive credential health monitoring.
|
|
3
|
+
*
|
|
4
|
+
* Enumerates all active OAuth connections and validates each one for:
|
|
5
|
+
* - Token presence in secure storage
|
|
6
|
+
* - Token expiry (expired or expiring within the warning window)
|
|
7
|
+
* - Scope coverage (grantedScopes vs provider defaultScopes)
|
|
8
|
+
* - Liveness ping (for providers with a pingUrl)
|
|
9
|
+
*
|
|
10
|
+
* Designed to run during the heartbeat cycle. All checks are diagnostic —
|
|
11
|
+
* no token refresh or recovery is attempted.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
isTokenExpired,
|
|
16
|
+
oauthConnectionAccessTokenPath,
|
|
17
|
+
} from "@vellumai/credential-storage";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
getProvider,
|
|
21
|
+
listActiveConnectionsByProvider,
|
|
22
|
+
listProviders,
|
|
23
|
+
} from "../oauth/oauth-store.js";
|
|
24
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
25
|
+
import { getLogger } from "../util/logger.js";
|
|
26
|
+
|
|
27
|
+
const log = getLogger("credential-health");
|
|
28
|
+
|
|
29
|
+
/** 7 days in milliseconds — warn if token expires within this window. */
|
|
30
|
+
const EXPIRY_WARNING_MS = 7 * 24 * 60 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
/** Timeout for liveness pings. */
|
|
33
|
+
const PING_TIMEOUT_MS = 5_000;
|
|
34
|
+
|
|
35
|
+
// ── Types ─────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
export type CredentialHealthStatus =
|
|
38
|
+
| "healthy"
|
|
39
|
+
| "expiring"
|
|
40
|
+
| "expired"
|
|
41
|
+
| "missing_token"
|
|
42
|
+
| "missing_scopes"
|
|
43
|
+
| "revoked"
|
|
44
|
+
| "ping_failed";
|
|
45
|
+
|
|
46
|
+
export interface CredentialHealthResult {
|
|
47
|
+
connectionId: string;
|
|
48
|
+
provider: string;
|
|
49
|
+
accountInfo: string | null;
|
|
50
|
+
status: CredentialHealthStatus;
|
|
51
|
+
details: string;
|
|
52
|
+
missingScopes: string[];
|
|
53
|
+
canAutoRecover: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CredentialHealthReport {
|
|
57
|
+
checkedAt: number;
|
|
58
|
+
results: CredentialHealthResult[];
|
|
59
|
+
unhealthy: CredentialHealthResult[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
function safeJsonParse<T>(raw: string | null | undefined, fallback: T): T {
|
|
65
|
+
if (!raw) return fallback;
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(raw) as T;
|
|
68
|
+
} catch {
|
|
69
|
+
return fallback;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function scopeDifference(required: string[], granted: string[]): string[] {
|
|
74
|
+
const grantedSet = new Set(granted);
|
|
75
|
+
return required.filter((s) => !grantedSet.has(s));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── Liveness ping ─────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
/** @internal Exposed for test injection. */
|
|
81
|
+
export let _fetchFn: typeof fetch = fetch;
|
|
82
|
+
|
|
83
|
+
/** @internal Test-only: override the fetch function used for pings. */
|
|
84
|
+
export function _setFetchFn(fn: typeof fetch): void {
|
|
85
|
+
_fetchFn = fn;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function pingProvider(
|
|
89
|
+
token: string,
|
|
90
|
+
pingUrl: string,
|
|
91
|
+
pingMethod: string | null,
|
|
92
|
+
pingHeaders: string | null,
|
|
93
|
+
pingBody: string | null,
|
|
94
|
+
): Promise<{ ok: boolean; authError: boolean }> {
|
|
95
|
+
const method = pingMethod ?? "GET";
|
|
96
|
+
const headers: Record<string, string> = {
|
|
97
|
+
Authorization: `Bearer ${token}`,
|
|
98
|
+
...safeJsonParse<Record<string, string>>(pingHeaders, {}),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const body =
|
|
102
|
+
method !== "GET" && pingBody
|
|
103
|
+
? (typeof pingBody === "string" ? pingBody : JSON.stringify(pingBody))
|
|
104
|
+
: undefined;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const response = await _fetchFn(pingUrl, {
|
|
108
|
+
method,
|
|
109
|
+
headers,
|
|
110
|
+
body,
|
|
111
|
+
signal: AbortSignal.timeout(PING_TIMEOUT_MS),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (response.ok) return { ok: true, authError: false };
|
|
115
|
+
if (response.status === 401 || response.status === 403) {
|
|
116
|
+
return { ok: false, authError: true };
|
|
117
|
+
}
|
|
118
|
+
return { ok: false, authError: false };
|
|
119
|
+
} catch {
|
|
120
|
+
// Network error or timeout — treat as non-auth failure
|
|
121
|
+
return { ok: false, authError: false };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Core check ────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
interface CheckConnectionOpts {
|
|
128
|
+
connectionId: string;
|
|
129
|
+
provider: string;
|
|
130
|
+
accountInfo: string | null;
|
|
131
|
+
expiresAt: number | null;
|
|
132
|
+
hasRefreshToken: boolean;
|
|
133
|
+
grantedScopesRaw: string;
|
|
134
|
+
defaultScopesRaw: string;
|
|
135
|
+
pingUrl: string | null;
|
|
136
|
+
pingMethod: string | null;
|
|
137
|
+
pingHeaders: string | null;
|
|
138
|
+
pingBody: string | null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function checkConnection(
|
|
142
|
+
opts: CheckConnectionOpts,
|
|
143
|
+
): Promise<CredentialHealthResult> {
|
|
144
|
+
const {
|
|
145
|
+
connectionId,
|
|
146
|
+
provider,
|
|
147
|
+
accountInfo,
|
|
148
|
+
expiresAt,
|
|
149
|
+
hasRefreshToken,
|
|
150
|
+
grantedScopesRaw,
|
|
151
|
+
defaultScopesRaw,
|
|
152
|
+
pingUrl,
|
|
153
|
+
pingMethod,
|
|
154
|
+
pingHeaders,
|
|
155
|
+
pingBody,
|
|
156
|
+
} = opts;
|
|
157
|
+
|
|
158
|
+
const base = { connectionId, provider, accountInfo, missingScopes: [] as string[] };
|
|
159
|
+
|
|
160
|
+
// 1. Check token presence
|
|
161
|
+
const token = await getSecureKeyAsync(
|
|
162
|
+
oauthConnectionAccessTokenPath(connectionId),
|
|
163
|
+
);
|
|
164
|
+
if (!token) {
|
|
165
|
+
return {
|
|
166
|
+
...base,
|
|
167
|
+
status: "missing_token",
|
|
168
|
+
details: `No access token found for ${provider}. Re-authorization required.`,
|
|
169
|
+
canAutoRecover: false,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 2. Check token expiry
|
|
174
|
+
if (isTokenExpired(expiresAt)) {
|
|
175
|
+
return {
|
|
176
|
+
...base,
|
|
177
|
+
status: hasRefreshToken ? "expiring" : "expired",
|
|
178
|
+
details: hasRefreshToken
|
|
179
|
+
? `Token for ${provider} is expired but has a refresh token — auto-recovery may work.`
|
|
180
|
+
: `Token for ${provider} is expired with no refresh token. Re-authorization required.`,
|
|
181
|
+
canAutoRecover: hasRefreshToken,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check if expiring within warning window (but not yet expired by the 5-min buffer)
|
|
186
|
+
if (expiresAt && Date.now() >= expiresAt - EXPIRY_WARNING_MS) {
|
|
187
|
+
// Token works now but will expire soon
|
|
188
|
+
if (!hasRefreshToken) {
|
|
189
|
+
return {
|
|
190
|
+
...base,
|
|
191
|
+
status: "expiring",
|
|
192
|
+
details: `Token for ${provider} expires within 7 days and has no refresh token. Re-authorization will be needed soon.`,
|
|
193
|
+
canAutoRecover: false,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Has refresh token — not an issue, auto-refresh will handle it
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 3. Check scope coverage
|
|
200
|
+
const grantedScopes = safeJsonParse<string[]>(grantedScopesRaw, []);
|
|
201
|
+
const defaultScopes = safeJsonParse<string[]>(defaultScopesRaw, []);
|
|
202
|
+
if (defaultScopes.length > 0 && grantedScopes.length > 0) {
|
|
203
|
+
const missing = scopeDifference(defaultScopes, grantedScopes);
|
|
204
|
+
if (missing.length > 0) {
|
|
205
|
+
return {
|
|
206
|
+
...base,
|
|
207
|
+
status: "missing_scopes",
|
|
208
|
+
details: `${provider} is missing required scopes: ${missing.join(", ")}. Features may not work correctly.`,
|
|
209
|
+
missingScopes: missing,
|
|
210
|
+
canAutoRecover: false,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 4. Liveness ping
|
|
216
|
+
if (pingUrl) {
|
|
217
|
+
const pingResult = await pingProvider(
|
|
218
|
+
token,
|
|
219
|
+
pingUrl,
|
|
220
|
+
pingMethod,
|
|
221
|
+
pingHeaders,
|
|
222
|
+
pingBody,
|
|
223
|
+
);
|
|
224
|
+
if (!pingResult.ok) {
|
|
225
|
+
if (pingResult.authError) {
|
|
226
|
+
return {
|
|
227
|
+
...base,
|
|
228
|
+
status: "revoked",
|
|
229
|
+
details: `${provider} token was rejected (401/403). The token may have been revoked. Re-authorization required.`,
|
|
230
|
+
canAutoRecover: false,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// Non-auth ping failure — log but don't mark as critical.
|
|
234
|
+
// Could be a transient API issue.
|
|
235
|
+
log.debug(
|
|
236
|
+
{ provider, connectionId },
|
|
237
|
+
"Credential ping failed with non-auth error",
|
|
238
|
+
);
|
|
239
|
+
return {
|
|
240
|
+
...base,
|
|
241
|
+
status: "ping_failed",
|
|
242
|
+
details: `${provider} liveness check failed (non-auth error). This may be transient.`,
|
|
243
|
+
canAutoRecover: false,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
...base,
|
|
250
|
+
status: "healthy",
|
|
251
|
+
details: `${provider} credential is healthy.`,
|
|
252
|
+
canAutoRecover: hasRefreshToken,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ── Public API ────────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Check the health of all active OAuth connections.
|
|
260
|
+
*
|
|
261
|
+
* Iterates every registered provider, looks up active connections, and
|
|
262
|
+
* validates each one. Returns a structured report with overall results
|
|
263
|
+
* and a filtered list of unhealthy credentials.
|
|
264
|
+
*/
|
|
265
|
+
export async function checkAllCredentials(): Promise<CredentialHealthReport> {
|
|
266
|
+
const checkedAt = Date.now();
|
|
267
|
+
const results: CredentialHealthResult[] = [];
|
|
268
|
+
|
|
269
|
+
let providers;
|
|
270
|
+
try {
|
|
271
|
+
providers = listProviders();
|
|
272
|
+
} catch (err) {
|
|
273
|
+
log.warn({ err }, "Failed to list OAuth providers");
|
|
274
|
+
return { checkedAt, results, unhealthy: [] };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
for (const providerRow of providers) {
|
|
278
|
+
let connections;
|
|
279
|
+
try {
|
|
280
|
+
connections = listActiveConnectionsByProvider(providerRow.provider);
|
|
281
|
+
} catch (err) {
|
|
282
|
+
log.warn(
|
|
283
|
+
{ err, provider: providerRow.provider },
|
|
284
|
+
"Failed to list connections for provider",
|
|
285
|
+
);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
for (const conn of connections) {
|
|
290
|
+
try {
|
|
291
|
+
const result = await checkConnection({
|
|
292
|
+
connectionId: conn.id,
|
|
293
|
+
provider: conn.provider,
|
|
294
|
+
accountInfo: conn.accountInfo,
|
|
295
|
+
expiresAt: conn.expiresAt,
|
|
296
|
+
hasRefreshToken: !!conn.hasRefreshToken,
|
|
297
|
+
grantedScopesRaw: conn.grantedScopes,
|
|
298
|
+
defaultScopesRaw: providerRow.defaultScopes,
|
|
299
|
+
pingUrl: providerRow.pingUrl,
|
|
300
|
+
pingMethod: providerRow.pingMethod,
|
|
301
|
+
pingHeaders: providerRow.pingHeaders,
|
|
302
|
+
pingBody: providerRow.pingBody,
|
|
303
|
+
});
|
|
304
|
+
results.push(result);
|
|
305
|
+
} catch (err) {
|
|
306
|
+
log.warn(
|
|
307
|
+
{ err, provider: conn.provider, connectionId: conn.id },
|
|
308
|
+
"Failed to check credential health",
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const unhealthy = results.filter((r) => r.status !== "healthy");
|
|
315
|
+
if (unhealthy.length > 0) {
|
|
316
|
+
log.info(
|
|
317
|
+
{
|
|
318
|
+
total: results.length,
|
|
319
|
+
unhealthy: unhealthy.length,
|
|
320
|
+
providers: [...new Set(unhealthy.map((r) => r.provider))],
|
|
321
|
+
},
|
|
322
|
+
"Credential health check found issues",
|
|
323
|
+
);
|
|
324
|
+
} else {
|
|
325
|
+
log.debug({ total: results.length }, "All credentials healthy");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return { checkedAt, results, unhealthy };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Check credential health for a single provider. Returns the health
|
|
333
|
+
* result for the most recent active connection, or null if no connection
|
|
334
|
+
* exists.
|
|
335
|
+
*
|
|
336
|
+
* Used by the watcher engine for pre-poll gating.
|
|
337
|
+
*/
|
|
338
|
+
export async function checkCredentialForProvider(
|
|
339
|
+
provider: string,
|
|
340
|
+
): Promise<CredentialHealthResult | null> {
|
|
341
|
+
let connections;
|
|
342
|
+
try {
|
|
343
|
+
connections = listActiveConnectionsByProvider(provider);
|
|
344
|
+
} catch {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
if (connections.length === 0) return null;
|
|
348
|
+
|
|
349
|
+
const conn = connections[0]!;
|
|
350
|
+
const providerRow = getProvider(conn.provider);
|
|
351
|
+
if (!providerRow) return null;
|
|
352
|
+
|
|
353
|
+
return checkConnection({
|
|
354
|
+
connectionId: conn.id,
|
|
355
|
+
provider: conn.provider,
|
|
356
|
+
accountInfo: conn.accountInfo,
|
|
357
|
+
expiresAt: conn.expiresAt,
|
|
358
|
+
hasRefreshToken: !!conn.hasRefreshToken,
|
|
359
|
+
grantedScopesRaw: conn.grantedScopes,
|
|
360
|
+
defaultScopesRaw: providerRow.defaultScopes,
|
|
361
|
+
pingUrl: providerRow.pingUrl,
|
|
362
|
+
pingMethod: providerRow.pingMethod,
|
|
363
|
+
pingHeaders: providerRow.pingHeaders,
|
|
364
|
+
pingBody: providerRow.pingBody,
|
|
365
|
+
});
|
|
366
|
+
}
|