@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
|
@@ -1,466 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `assistant browser chrome relay <action>` CLI shim.
|
|
3
|
-
*
|
|
4
|
-
* Translates the legacy relay actions (find_tab, new_tab, navigate,
|
|
5
|
-
* evaluate, get_cookies, set_cookie, screenshot) into Chrome DevTools
|
|
6
|
-
* Protocol commands and forwards them to the daemon's
|
|
7
|
-
* `/v1/browser-cdp` HTTP endpoint, which routes the command through
|
|
8
|
-
* the connected chrome-extension WebSocket.
|
|
9
|
-
*
|
|
10
|
-
* Why this exists: PR #24329 deleted the in-process extension relay
|
|
11
|
-
* server and the original CLI surface. Two in-tree skills (amazon and
|
|
12
|
-
* influencer) still spawn `assistant browser chrome relay <action>` as
|
|
13
|
-
* a subprocess and parse the JSON output. Until those skills migrate
|
|
14
|
-
* onto the new CDP-based skill API, this shim keeps them working by
|
|
15
|
-
* preserving the legacy stdout contract:
|
|
16
|
-
*
|
|
17
|
-
* { "ok": true, "tabId"?: <id>, "result"?: <unknown> }
|
|
18
|
-
* { "ok": false, "error": <string> }
|
|
19
|
-
*
|
|
20
|
-
* The CLI mints a short-lived daemon delivery JWT (same audience and
|
|
21
|
-
* scope profile as the daemon's internal callbacks) and POSTs directly
|
|
22
|
-
* to the runtime's loopback HTTP port — no gateway involvement
|
|
23
|
-
* required.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
27
|
-
|
|
28
|
-
import type { Command } from "commander";
|
|
29
|
-
|
|
30
|
-
import { getRuntimeHttpPort } from "../../config/env.js";
|
|
31
|
-
import { CURRENT_POLICY_EPOCH } from "../../runtime/auth/policy.js";
|
|
32
|
-
import { mintToken } from "../../runtime/auth/token-service.js";
|
|
33
|
-
import {
|
|
34
|
-
initAuthSigningKey,
|
|
35
|
-
isSigningKeyInitialized,
|
|
36
|
-
loadOrCreateSigningKey,
|
|
37
|
-
} from "../../runtime/auth/token-service.js";
|
|
38
|
-
import { getRuntimePortFilePath } from "../../util/platform.js";
|
|
39
|
-
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
// Daemon HTTP client
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
|
|
44
|
-
interface BrowserCdpResponse {
|
|
45
|
-
result?: unknown;
|
|
46
|
-
error?: { code: string; message: string };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Resolve the daemon's runtime HTTP port. Prefers the runtime-port
|
|
51
|
-
* file written by the daemon at startup so non-default ports
|
|
52
|
-
* (RUNTIME_HTTP_PORT) are picked up automatically without an env var
|
|
53
|
-
* roundtrip. Falls back to the env-var-derived default.
|
|
54
|
-
*/
|
|
55
|
-
function resolveRuntimePort(): number {
|
|
56
|
-
try {
|
|
57
|
-
const portFile = getRuntimePortFilePath();
|
|
58
|
-
if (existsSync(portFile)) {
|
|
59
|
-
const raw = readFileSync(portFile, "utf-8").trim();
|
|
60
|
-
const parsed = Number(raw);
|
|
61
|
-
if (Number.isFinite(parsed) && parsed > 0 && parsed < 65536) {
|
|
62
|
-
return parsed;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
} catch {
|
|
66
|
-
// Fall through to env-var default
|
|
67
|
-
}
|
|
68
|
-
return getRuntimeHttpPort();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Mint a short-lived JWT acceptable to the runtime auth middleware.
|
|
73
|
-
* Mirrors `mintDaemonDeliveryToken` (sub=svc:daemon:self,
|
|
74
|
-
* scope_profile=gateway_service_v1, aud=vellum-daemon) but is minted
|
|
75
|
-
* out-of-process by the CLI using the on-disk signing key.
|
|
76
|
-
*/
|
|
77
|
-
function mintCliToken(): string {
|
|
78
|
-
if (!isSigningKeyInitialized()) {
|
|
79
|
-
initAuthSigningKey(loadOrCreateSigningKey());
|
|
80
|
-
}
|
|
81
|
-
return mintToken({
|
|
82
|
-
aud: "vellum-daemon",
|
|
83
|
-
sub: "svc:daemon:self",
|
|
84
|
-
scope_profile: "gateway_service_v1",
|
|
85
|
-
policy_epoch: CURRENT_POLICY_EPOCH,
|
|
86
|
-
ttlSeconds: 60,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Send a single CDP command to the daemon's /v1/browser-cdp route and
|
|
92
|
-
* return the parsed response. Throws on transport-level errors; the
|
|
93
|
-
* caller wraps the throw into a `{ ok: false, error }` envelope.
|
|
94
|
-
*/
|
|
95
|
-
async function postBrowserCdp(payload: {
|
|
96
|
-
cdpMethod: string;
|
|
97
|
-
cdpParams?: Record<string, unknown>;
|
|
98
|
-
cdpSessionId?: string;
|
|
99
|
-
timeoutMs?: number;
|
|
100
|
-
}): Promise<BrowserCdpResponse> {
|
|
101
|
-
const port = resolveRuntimePort();
|
|
102
|
-
const token = mintCliToken();
|
|
103
|
-
const url = `http://127.0.0.1:${port}/v1/browser-cdp`;
|
|
104
|
-
|
|
105
|
-
const resp = await fetch(url, {
|
|
106
|
-
method: "POST",
|
|
107
|
-
headers: {
|
|
108
|
-
"Content-Type": "application/json",
|
|
109
|
-
Authorization: `Bearer ${token}`,
|
|
110
|
-
},
|
|
111
|
-
body: JSON.stringify(payload),
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const bodyText = await resp.text();
|
|
115
|
-
let parsed: BrowserCdpResponse;
|
|
116
|
-
try {
|
|
117
|
-
parsed = JSON.parse(bodyText) as BrowserCdpResponse;
|
|
118
|
-
} catch {
|
|
119
|
-
throw new Error(
|
|
120
|
-
`Daemon returned non-JSON response (HTTP ${resp.status}): ${bodyText.slice(0, 200)}`,
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!resp.ok) {
|
|
125
|
-
const message = parsed.error?.message ?? `HTTP ${resp.status}`;
|
|
126
|
-
throw new Error(message);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return parsed;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
// Stdout helpers
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
|
|
136
|
-
interface RelayResultOk {
|
|
137
|
-
ok: true;
|
|
138
|
-
tabId?: number | string;
|
|
139
|
-
result?: unknown;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
interface RelayResultErr {
|
|
143
|
-
ok: false;
|
|
144
|
-
error: string;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function emitOk(payload: Omit<RelayResultOk, "ok">): void {
|
|
148
|
-
const out: RelayResultOk = { ok: true, ...payload };
|
|
149
|
-
process.stdout.write(JSON.stringify(out) + "\n");
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function emitError(message: string): void {
|
|
153
|
-
const out: RelayResultErr = { ok: false, error: message };
|
|
154
|
-
process.stdout.write(JSON.stringify(out) + "\n");
|
|
155
|
-
process.exitCode = 1;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async function readStdin(): Promise<string> {
|
|
159
|
-
const chunks: Buffer[] = [];
|
|
160
|
-
for await (const chunk of process.stdin) {
|
|
161
|
-
chunks.push(chunk as Buffer);
|
|
162
|
-
}
|
|
163
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ---------------------------------------------------------------------------
|
|
167
|
-
// URL glob matching for find-tab
|
|
168
|
-
// ---------------------------------------------------------------------------
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Convert a Chrome match-pattern style glob (e.g. `*://*.amazon.com/*`)
|
|
172
|
-
* into a regular expression. Matches the chrome.tabs.query semantics
|
|
173
|
-
* the legacy relay CLI exposed:
|
|
174
|
-
*
|
|
175
|
-
* - `*` is a wildcard that matches any sequence (including `/` in
|
|
176
|
-
* the path component, mirroring the legacy minimatch behaviour).
|
|
177
|
-
* - All other regex metacharacters are escaped.
|
|
178
|
-
*/
|
|
179
|
-
function globToRegex(glob: string): RegExp {
|
|
180
|
-
const escaped = glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
181
|
-
const pattern = "^" + escaped.replace(/\*/g, ".*") + "$";
|
|
182
|
-
return new RegExp(pattern);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// ---------------------------------------------------------------------------
|
|
186
|
-
// Action handlers — translate legacy actions into CDP commands
|
|
187
|
-
// ---------------------------------------------------------------------------
|
|
188
|
-
|
|
189
|
-
interface CdpTarget {
|
|
190
|
-
targetId: string;
|
|
191
|
-
type: string;
|
|
192
|
-
url: string;
|
|
193
|
-
title?: string;
|
|
194
|
-
attached?: boolean;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
interface CdpTargetsResult {
|
|
198
|
-
targetInfos: CdpTarget[];
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async function actionFindTab(urlPattern: string): Promise<void> {
|
|
202
|
-
try {
|
|
203
|
-
const resp = await postBrowserCdp({ cdpMethod: "Target.getTargets" });
|
|
204
|
-
const targets =
|
|
205
|
-
(resp.result as CdpTargetsResult | undefined)?.targetInfos ?? [];
|
|
206
|
-
const re = globToRegex(urlPattern);
|
|
207
|
-
const match = targets.find((t) => t.type === "page" && re.test(t.url));
|
|
208
|
-
if (!match) {
|
|
209
|
-
emitError(`No tab matched URL pattern: ${urlPattern}`);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
emitOk({ tabId: match.targetId });
|
|
213
|
-
} catch (err) {
|
|
214
|
-
emitError(err instanceof Error ? err.message : String(err));
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
async function actionNewTab(url: string): Promise<void> {
|
|
219
|
-
try {
|
|
220
|
-
const resp = await postBrowserCdp({
|
|
221
|
-
cdpMethod: "Target.createTarget",
|
|
222
|
-
cdpParams: { url },
|
|
223
|
-
});
|
|
224
|
-
const targetId = (resp.result as { targetId?: string } | undefined)
|
|
225
|
-
?.targetId;
|
|
226
|
-
if (!targetId) {
|
|
227
|
-
emitError("Target.createTarget did not return a targetId");
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
emitOk({ tabId: targetId });
|
|
231
|
-
} catch (err) {
|
|
232
|
-
emitError(err instanceof Error ? err.message : String(err));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
async function actionNavigate(tabId: string, url: string): Promise<void> {
|
|
237
|
-
try {
|
|
238
|
-
await postBrowserCdp({
|
|
239
|
-
cdpMethod: "Page.navigate",
|
|
240
|
-
cdpParams: { url },
|
|
241
|
-
cdpSessionId: tabId,
|
|
242
|
-
});
|
|
243
|
-
emitOk({});
|
|
244
|
-
} catch (err) {
|
|
245
|
-
emitError(err instanceof Error ? err.message : String(err));
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async function actionEvaluate(tabId: string, code: string): Promise<void> {
|
|
250
|
-
try {
|
|
251
|
-
const resp = await postBrowserCdp({
|
|
252
|
-
cdpMethod: "Runtime.evaluate",
|
|
253
|
-
cdpParams: {
|
|
254
|
-
expression: code,
|
|
255
|
-
returnByValue: true,
|
|
256
|
-
awaitPromise: true,
|
|
257
|
-
},
|
|
258
|
-
cdpSessionId: tabId,
|
|
259
|
-
});
|
|
260
|
-
// CDP Runtime.evaluate returns { result: { type, value }, exceptionDetails? }.
|
|
261
|
-
// Surface exceptions as relay errors so callers don't silently get undefined.
|
|
262
|
-
const result = resp.result as
|
|
263
|
-
| {
|
|
264
|
-
result?: { value?: unknown };
|
|
265
|
-
exceptionDetails?: {
|
|
266
|
-
text?: string;
|
|
267
|
-
exception?: { description?: string };
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
| undefined;
|
|
271
|
-
if (result?.exceptionDetails) {
|
|
272
|
-
const desc =
|
|
273
|
-
result.exceptionDetails.exception?.description ??
|
|
274
|
-
result.exceptionDetails.text ??
|
|
275
|
-
"Runtime exception during evaluate";
|
|
276
|
-
emitError(desc);
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
emitOk({ result: result?.result?.value });
|
|
280
|
-
} catch (err) {
|
|
281
|
-
emitError(err instanceof Error ? err.message : String(err));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
async function actionGetCookies(domain: string): Promise<void> {
|
|
286
|
-
try {
|
|
287
|
-
const resp = await postBrowserCdp({ cdpMethod: "Network.getCookies" });
|
|
288
|
-
const cookies =
|
|
289
|
-
(resp.result as { cookies?: Array<Record<string, unknown>> } | undefined)
|
|
290
|
-
?.cookies ?? [];
|
|
291
|
-
// Filter by domain (Chrome stores cookies with leading-dot or bare-host
|
|
292
|
-
// domains depending on the Set-Cookie source). Match either form so the
|
|
293
|
-
// legacy "amazon.com" / ".amazon.com" callers both succeed.
|
|
294
|
-
const trimmed = domain.startsWith(".") ? domain.slice(1) : domain;
|
|
295
|
-
const filtered = cookies.filter((c) => {
|
|
296
|
-
const d = String(c.domain ?? "");
|
|
297
|
-
const dTrim = d.startsWith(".") ? d.slice(1) : d;
|
|
298
|
-
return dTrim === trimmed || dTrim.endsWith("." + trimmed);
|
|
299
|
-
});
|
|
300
|
-
emitOk({ result: filtered });
|
|
301
|
-
} catch (err) {
|
|
302
|
-
emitError(err instanceof Error ? err.message : String(err));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
async function actionSetCookie(cookie: Record<string, unknown>): Promise<void> {
|
|
307
|
-
try {
|
|
308
|
-
await postBrowserCdp({
|
|
309
|
-
cdpMethod: "Network.setCookie",
|
|
310
|
-
cdpParams: cookie,
|
|
311
|
-
});
|
|
312
|
-
emitOk({});
|
|
313
|
-
} catch (err) {
|
|
314
|
-
emitError(err instanceof Error ? err.message : String(err));
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async function actionScreenshot(tabId?: string): Promise<void> {
|
|
319
|
-
try {
|
|
320
|
-
const resp = await postBrowserCdp({
|
|
321
|
-
cdpMethod: "Page.captureScreenshot",
|
|
322
|
-
cdpParams: { format: "png" },
|
|
323
|
-
...(tabId !== undefined ? { cdpSessionId: tabId } : {}),
|
|
324
|
-
});
|
|
325
|
-
const data = (resp.result as { data?: string } | undefined)?.data;
|
|
326
|
-
if (data === undefined) {
|
|
327
|
-
emitError("Page.captureScreenshot returned no data");
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
emitOk({ result: data });
|
|
331
|
-
} catch (err) {
|
|
332
|
-
emitError(err instanceof Error ? err.message : String(err));
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// ---------------------------------------------------------------------------
|
|
337
|
-
// Command registration
|
|
338
|
-
// ---------------------------------------------------------------------------
|
|
339
|
-
|
|
340
|
-
export function registerBrowserRelayCommand(program: Command): void {
|
|
341
|
-
const browser = program
|
|
342
|
-
.command("browser")
|
|
343
|
-
.description(
|
|
344
|
-
"Browser automation surface (`chrome relay <action>` CDP shim)",
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
browser.addHelpText(
|
|
348
|
-
"after",
|
|
349
|
-
`
|
|
350
|
-
Provides a thin CDP-over-HTTP shim used by in-tree skills that have not
|
|
351
|
-
yet migrated onto the new CDP-based skill API. Each command translates
|
|
352
|
-
the legacy action into a Chrome DevTools Protocol call and forwards it
|
|
353
|
-
to the daemon's /v1/browser-cdp route, which routes through the
|
|
354
|
-
connected chrome-extension WebSocket.
|
|
355
|
-
|
|
356
|
-
Examples:
|
|
357
|
-
$ assistant browser chrome relay find-tab --url "*://*.amazon.com/*"
|
|
358
|
-
$ assistant browser chrome relay new-tab --url "https://example.com"
|
|
359
|
-
$ assistant browser chrome relay evaluate --tab-id <id> --code "document.title"
|
|
360
|
-
$ assistant browser chrome relay screenshot --tab-id <id>`,
|
|
361
|
-
);
|
|
362
|
-
|
|
363
|
-
const chrome = browser
|
|
364
|
-
.command("chrome")
|
|
365
|
-
.description("Chrome browser automation via the chrome-extension proxy");
|
|
366
|
-
|
|
367
|
-
const relay = chrome
|
|
368
|
-
.command("relay")
|
|
369
|
-
.description(
|
|
370
|
-
"Send a single CDP command to a Chrome tab via the chrome extension",
|
|
371
|
-
);
|
|
372
|
-
|
|
373
|
-
// -- find-tab --
|
|
374
|
-
|
|
375
|
-
relay
|
|
376
|
-
.command("find-tab")
|
|
377
|
-
.description("Find a tab matching a URL glob pattern")
|
|
378
|
-
.requiredOption(
|
|
379
|
-
"--url <pattern>",
|
|
380
|
-
"URL glob pattern to match (e.g. *://*.instagram.com/*)",
|
|
381
|
-
)
|
|
382
|
-
.action(async (opts: { url: string }) => {
|
|
383
|
-
await actionFindTab(opts.url);
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
// -- new-tab --
|
|
387
|
-
|
|
388
|
-
relay
|
|
389
|
-
.command("new-tab")
|
|
390
|
-
.description("Open a new tab with the given URL")
|
|
391
|
-
.requiredOption("--url <url>", "URL to open in a new tab")
|
|
392
|
-
.action(async (opts: { url: string }) => {
|
|
393
|
-
await actionNewTab(opts.url);
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
// -- navigate --
|
|
397
|
-
|
|
398
|
-
relay
|
|
399
|
-
.command("navigate")
|
|
400
|
-
.description("Navigate an existing tab to a new URL")
|
|
401
|
-
.requiredOption("--tab-id <id>", "Target tab ID")
|
|
402
|
-
.requiredOption("--url <url>", "URL to navigate to")
|
|
403
|
-
.action(async (opts: { tabId: string; url: string }) => {
|
|
404
|
-
await actionNavigate(opts.tabId, opts.url);
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
// -- evaluate --
|
|
408
|
-
|
|
409
|
-
relay
|
|
410
|
-
.command("evaluate")
|
|
411
|
-
.description("Execute JavaScript in a Chrome tab")
|
|
412
|
-
.requiredOption("--tab-id <id>", "Target tab ID")
|
|
413
|
-
.option(
|
|
414
|
-
"--code <script>",
|
|
415
|
-
"JavaScript code to evaluate (or read from stdin)",
|
|
416
|
-
)
|
|
417
|
-
.action(async (opts: { tabId: string; code?: string }) => {
|
|
418
|
-
let code: string;
|
|
419
|
-
if (opts.code) {
|
|
420
|
-
code = opts.code;
|
|
421
|
-
} else if (process.stdin.isTTY) {
|
|
422
|
-
emitError("No code provided. Use --code or pipe JavaScript via stdin.");
|
|
423
|
-
return;
|
|
424
|
-
} else {
|
|
425
|
-
code = await readStdin();
|
|
426
|
-
}
|
|
427
|
-
await actionEvaluate(opts.tabId, code);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
// -- get-cookies --
|
|
431
|
-
|
|
432
|
-
relay
|
|
433
|
-
.command("get-cookies")
|
|
434
|
-
.description("Fetch cookies for a domain")
|
|
435
|
-
.requiredOption("--domain <domain>", "Cookie domain to fetch")
|
|
436
|
-
.action(async (opts: { domain: string }) => {
|
|
437
|
-
await actionGetCookies(opts.domain);
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
// -- set-cookie --
|
|
441
|
-
|
|
442
|
-
relay
|
|
443
|
-
.command("set-cookie")
|
|
444
|
-
.description("Set a cookie in the browser")
|
|
445
|
-
.requiredOption("--cookie <json>", "Cookie specification as JSON")
|
|
446
|
-
.action(async (opts: { cookie: string }) => {
|
|
447
|
-
let parsed: Record<string, unknown>;
|
|
448
|
-
try {
|
|
449
|
-
parsed = JSON.parse(opts.cookie) as Record<string, unknown>;
|
|
450
|
-
} catch {
|
|
451
|
-
emitError("Invalid JSON in --cookie argument");
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
await actionSetCookie(parsed);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// -- screenshot --
|
|
458
|
-
|
|
459
|
-
relay
|
|
460
|
-
.command("screenshot")
|
|
461
|
-
.description("Capture a base64-encoded PNG screenshot of a Chrome tab")
|
|
462
|
-
.option("--tab-id <id>", "Target tab ID (defaults to active tab)")
|
|
463
|
-
.action(async (opts: { tabId?: string }) => {
|
|
464
|
-
await actionScreenshot(opts.tabId);
|
|
465
|
-
});
|
|
466
|
-
}
|
package/src/email/guardrails.ts
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local guardrails state for email operations.
|
|
3
|
-
* Stores state in ~/.vellum/email-guardrails.json.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
|
|
9
|
-
import { minimatch } from "minimatch";
|
|
10
|
-
|
|
11
|
-
import { getWorkspaceDir } from "../util/platform.js";
|
|
12
|
-
|
|
13
|
-
export interface AddressRule {
|
|
14
|
-
id: string;
|
|
15
|
-
type: "block" | "allow";
|
|
16
|
-
pattern: string;
|
|
17
|
-
createdAt: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface DailyCount {
|
|
21
|
-
[date: string]: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface GuardrailsState {
|
|
25
|
-
paused: boolean;
|
|
26
|
-
dailyCap: number;
|
|
27
|
-
dailyCounts: DailyCount;
|
|
28
|
-
addressRules: AddressRule[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const DEFAULT_STATE: GuardrailsState = {
|
|
32
|
-
paused: false,
|
|
33
|
-
dailyCap: 25,
|
|
34
|
-
dailyCounts: {},
|
|
35
|
-
addressRules: [],
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
function getGuardrailsPath(): string {
|
|
39
|
-
return join(getWorkspaceDir(), "email-guardrails.json");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function loadState(): GuardrailsState {
|
|
43
|
-
const path = getGuardrailsPath();
|
|
44
|
-
if (!existsSync(path))
|
|
45
|
-
return { ...DEFAULT_STATE, dailyCounts: {}, addressRules: [] };
|
|
46
|
-
try {
|
|
47
|
-
const raw = readFileSync(path, "utf-8");
|
|
48
|
-
const parsed = JSON.parse(raw) as Partial<GuardrailsState>;
|
|
49
|
-
return {
|
|
50
|
-
paused: parsed.paused ?? DEFAULT_STATE.paused,
|
|
51
|
-
dailyCap: parsed.dailyCap ?? DEFAULT_STATE.dailyCap,
|
|
52
|
-
dailyCounts: parsed.dailyCounts ?? {},
|
|
53
|
-
addressRules: parsed.addressRules ?? [],
|
|
54
|
-
};
|
|
55
|
-
} catch {
|
|
56
|
-
return { ...DEFAULT_STATE, dailyCounts: {}, addressRules: [] };
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function saveState(state: GuardrailsState): void {
|
|
61
|
-
const path = getGuardrailsPath();
|
|
62
|
-
const dir = getWorkspaceDir();
|
|
63
|
-
if (!existsSync(dir)) {
|
|
64
|
-
mkdirSync(dir, { recursive: true });
|
|
65
|
-
}
|
|
66
|
-
writeFileSync(path, JSON.stringify(state, null, 2));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function todayKey(): string {
|
|
70
|
-
return new Date().toISOString().slice(0, 10);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function getDailySendCount(): number {
|
|
74
|
-
const state = loadState();
|
|
75
|
-
return state.dailyCounts[todayKey()] ?? 0;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function incrementDailySendCount(): number {
|
|
79
|
-
const state = loadState();
|
|
80
|
-
const key = todayKey();
|
|
81
|
-
const newCount = (state.dailyCounts[key] ?? 0) + 1;
|
|
82
|
-
state.dailyCounts[key] = newCount;
|
|
83
|
-
saveState(state);
|
|
84
|
-
return newCount;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function isOutboundPaused(): boolean {
|
|
88
|
-
return loadState().paused;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function setOutboundPaused(paused: boolean): void {
|
|
92
|
-
const state = loadState();
|
|
93
|
-
state.paused = paused;
|
|
94
|
-
saveState(state);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function getDailySendCap(): number {
|
|
98
|
-
return loadState().dailyCap;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function setDailySendCap(cap: number): void {
|
|
102
|
-
const state = loadState();
|
|
103
|
-
state.dailyCap = cap;
|
|
104
|
-
saveState(state);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function isAddressAllowed(email: string): {
|
|
108
|
-
allowed: boolean;
|
|
109
|
-
reason?: string;
|
|
110
|
-
rule?: AddressRule;
|
|
111
|
-
} {
|
|
112
|
-
const state = loadState();
|
|
113
|
-
const normalized = email.toLowerCase().trim();
|
|
114
|
-
|
|
115
|
-
// Check block rules first
|
|
116
|
-
for (const rule of state.addressRules) {
|
|
117
|
-
if (
|
|
118
|
-
rule.type === "block" &&
|
|
119
|
-
minimatch(normalized, rule.pattern, { nocase: true })
|
|
120
|
-
) {
|
|
121
|
-
return {
|
|
122
|
-
allowed: false,
|
|
123
|
-
reason: `blocked by rule: ${rule.pattern}`,
|
|
124
|
-
rule,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// If there are allow rules, address must match at least one
|
|
130
|
-
const allowRules = state.addressRules.filter((r) => r.type === "allow");
|
|
131
|
-
if (allowRules.length > 0) {
|
|
132
|
-
const matched = allowRules.some((r) =>
|
|
133
|
-
minimatch(normalized, r.pattern, { nocase: true }),
|
|
134
|
-
);
|
|
135
|
-
if (!matched) {
|
|
136
|
-
return { allowed: false, reason: "not in allowlist" };
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return { allowed: true };
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function addAddressRule(
|
|
144
|
-
type: "block" | "allow",
|
|
145
|
-
pattern: string,
|
|
146
|
-
): AddressRule {
|
|
147
|
-
const state = loadState();
|
|
148
|
-
const rule: AddressRule = {
|
|
149
|
-
id: crypto.randomUUID(),
|
|
150
|
-
type,
|
|
151
|
-
pattern: pattern.toLowerCase(),
|
|
152
|
-
createdAt: new Date().toISOString(),
|
|
153
|
-
};
|
|
154
|
-
state.addressRules.push(rule);
|
|
155
|
-
saveState(state);
|
|
156
|
-
return rule;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function removeAddressRule(ruleId: string): boolean {
|
|
160
|
-
const state = loadState();
|
|
161
|
-
const idx = state.addressRules.findIndex(
|
|
162
|
-
(r) => r.id === ruleId || r.id.startsWith(ruleId),
|
|
163
|
-
);
|
|
164
|
-
if (idx === -1) return false;
|
|
165
|
-
state.addressRules.splice(idx, 1);
|
|
166
|
-
saveState(state);
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export function listRules(): AddressRule[] {
|
|
171
|
-
return loadState().addressRules;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export function getGuardrailsStatus(): {
|
|
175
|
-
paused: boolean;
|
|
176
|
-
dailyCap: number;
|
|
177
|
-
dailyCount: number;
|
|
178
|
-
rules: AddressRule[];
|
|
179
|
-
} {
|
|
180
|
-
const state = loadState();
|
|
181
|
-
return {
|
|
182
|
-
paused: state.paused,
|
|
183
|
-
dailyCap: state.dailyCap,
|
|
184
|
-
dailyCount: state.dailyCounts[todayKey()] ?? 0,
|
|
185
|
-
rules: state.addressRules,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Check all guardrails before sending. Returns null if all clear,
|
|
191
|
-
* or an error object describing what blocked the send.
|
|
192
|
-
*/
|
|
193
|
-
export function checkSendGuardrails(recipients: string[]): {
|
|
194
|
-
error: string;
|
|
195
|
-
address?: string;
|
|
196
|
-
reason?: string;
|
|
197
|
-
count?: number;
|
|
198
|
-
cap?: number;
|
|
199
|
-
} | null {
|
|
200
|
-
if (isOutboundPaused()) {
|
|
201
|
-
return { error: "outbound_paused" };
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const count = getDailySendCount();
|
|
205
|
-
const cap = getDailySendCap();
|
|
206
|
-
if (count >= cap) {
|
|
207
|
-
return { error: "daily_cap_reached", count, cap };
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
for (const addr of recipients) {
|
|
211
|
-
const check = isAddressAllowed(addr);
|
|
212
|
-
if (!check.allowed) {
|
|
213
|
-
return { error: "address_blocked", address: addr, reason: check.reason };
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** @internal Test-only: reset state file path override. */
|
|
221
|
-
export { getGuardrailsPath as _getGuardrailsPath };
|