@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
|
@@ -10,6 +10,7 @@ import { shouldAutoStartDaemon } from "../../daemon/connection-policy.js";
|
|
|
10
10
|
import { healthCheckHost, isHttpHealthy } from "../../daemon/daemon-control.js";
|
|
11
11
|
import { ensureDaemonRunning } from "../../daemon/lifecycle.js";
|
|
12
12
|
import { formatJson, formatMarkdown } from "../../export/formatter.js";
|
|
13
|
+
import { cliIpcCall } from "../../ipc/cli-client.js";
|
|
13
14
|
import {
|
|
14
15
|
clearAll as clearAllConversations,
|
|
15
16
|
countConversationsByScheduleJobId,
|
|
@@ -388,4 +389,80 @@ Examples:
|
|
|
388
389
|
`cancelled ${result.cancelledJobCount} jobs.`,
|
|
389
390
|
);
|
|
390
391
|
});
|
|
392
|
+
|
|
393
|
+
conversations
|
|
394
|
+
.command("wake <conversationId>")
|
|
395
|
+
.description(
|
|
396
|
+
"Wake the agent on an existing conversation with an internal hint",
|
|
397
|
+
)
|
|
398
|
+
.requiredOption(
|
|
399
|
+
"--hint <text>",
|
|
400
|
+
"Hint message visible to the LLM (not persisted to transcript)",
|
|
401
|
+
)
|
|
402
|
+
.option(
|
|
403
|
+
"--source <label>",
|
|
404
|
+
"Source label for logging (e.g. github-notification)",
|
|
405
|
+
"cli",
|
|
406
|
+
)
|
|
407
|
+
.option("--json", "Output result as JSON")
|
|
408
|
+
.addHelpText(
|
|
409
|
+
"after",
|
|
410
|
+
`
|
|
411
|
+
Arguments:
|
|
412
|
+
conversationId Conversation ID to wake.
|
|
413
|
+
|
|
414
|
+
Wake the assistant's agent loop on an existing conversation without a user
|
|
415
|
+
message. The hint is injected as a non-persisted internal message visible
|
|
416
|
+
only to the LLM — it never appears in the transcript or SSE feed. If the
|
|
417
|
+
agent produces output (text or tool calls), it is persisted and emitted to
|
|
418
|
+
connected clients. Otherwise the wake is a silent no-op.
|
|
419
|
+
|
|
420
|
+
Requires the assistant to be running. Communicates via IPC socket.
|
|
421
|
+
|
|
422
|
+
Examples:
|
|
423
|
+
$ assistant conversations wake abc123 --hint "PR #25933 received a review requesting changes"
|
|
424
|
+
$ assistant conversations wake abc123 --hint "CI failed on commit abc" --source github-ci
|
|
425
|
+
$ assistant conversations wake abc123 --hint "New Slack DM from Vargas" --source slack --json`,
|
|
426
|
+
)
|
|
427
|
+
.action(
|
|
428
|
+
async (
|
|
429
|
+
conversationId: string,
|
|
430
|
+
opts: { hint: string; source: string; json?: boolean },
|
|
431
|
+
) => {
|
|
432
|
+
const result = await cliIpcCall<{
|
|
433
|
+
invoked: boolean;
|
|
434
|
+
producedToolCalls: boolean;
|
|
435
|
+
}>("wake_conversation", {
|
|
436
|
+
conversationId,
|
|
437
|
+
hint: opts.hint,
|
|
438
|
+
source: opts.source,
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
if (!result.ok) {
|
|
442
|
+
if (opts.json) {
|
|
443
|
+
log.info(JSON.stringify({ ok: false, error: result.error }));
|
|
444
|
+
} else {
|
|
445
|
+
log.error(`Error: ${result.error}`);
|
|
446
|
+
}
|
|
447
|
+
process.exitCode = 1;
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const wake = result.result!;
|
|
452
|
+
if (opts.json) {
|
|
453
|
+
log.info(JSON.stringify({ ok: true, ...wake }));
|
|
454
|
+
} else if (wake.invoked) {
|
|
455
|
+
log.info(
|
|
456
|
+
wake.producedToolCalls
|
|
457
|
+
? `Wake produced output on conversation ${conversationId}`
|
|
458
|
+
: `Wake invoked on ${conversationId} (no output produced)`,
|
|
459
|
+
);
|
|
460
|
+
} else {
|
|
461
|
+
log.error(
|
|
462
|
+
`Could not wake conversation ${conversationId} — conversation not found`,
|
|
463
|
+
);
|
|
464
|
+
process.exitCode = 1;
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
);
|
|
391
468
|
}
|
|
@@ -248,7 +248,6 @@ storage convention used internally (credential/{service}/{field}):
|
|
|
248
248
|
--service telegram --field bot_token Telegram bot token
|
|
249
249
|
--service slack_channel --field bot_token Slack channel bot token
|
|
250
250
|
--service github --field token GitHub personal access token
|
|
251
|
-
--service agentmail --field api_key AgentMail API key
|
|
252
251
|
|
|
253
252
|
Secrets are stored in AES-256-GCM encrypted storage. Metadata (policy,
|
|
254
253
|
timestamps, labels) is tracked separately and never contains secret values.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import { getAssistantDomain } from "../../config/env.js";
|
|
4
|
+
import { VellumPlatformClient } from "../../platform/client.js";
|
|
5
|
+
import { getCliLogger } from "../logger.js";
|
|
6
|
+
import { shouldOutputJson, writeOutput } from "../output.js";
|
|
7
|
+
|
|
8
|
+
const log = getCliLogger("domain");
|
|
9
|
+
|
|
10
|
+
export function registerDomainCommand(program: Command): void {
|
|
11
|
+
const baseDomain = getAssistantDomain();
|
|
12
|
+
const domain = program
|
|
13
|
+
.command("domain")
|
|
14
|
+
.description(
|
|
15
|
+
`Register and manage this assistant's custom subdomain on ${baseDomain}`,
|
|
16
|
+
)
|
|
17
|
+
.option("--json", "Machine-readable compact JSON output");
|
|
18
|
+
|
|
19
|
+
domain.addHelpText(
|
|
20
|
+
"after",
|
|
21
|
+
`
|
|
22
|
+
Each assistant can register its own subdomain (e.g. velly.${baseDomain})
|
|
23
|
+
for email and web presence. DNS managed by the Vellum platform.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
$ assistant domain register velly
|
|
27
|
+
$ assistant domain register --json
|
|
28
|
+
$ assistant domain status`,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
domain
|
|
32
|
+
.command("register [subdomain]")
|
|
33
|
+
.description(
|
|
34
|
+
`Register a custom subdomain on ${baseDomain} for this assistant`,
|
|
35
|
+
)
|
|
36
|
+
.addHelpText(
|
|
37
|
+
"after",
|
|
38
|
+
`
|
|
39
|
+
Arguments:
|
|
40
|
+
subdomain The subdomain to register (e.g. "velly" → velly.${baseDomain}).
|
|
41
|
+
If omitted, the platform derives it from the assistant's name.
|
|
42
|
+
|
|
43
|
+
Registers a subdomain at <subdomain>.${baseDomain}. DNS managed by the
|
|
44
|
+
Vellum platform — no manual DNS changes needed.
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
$ assistant domain register velly
|
|
48
|
+
✓ Registered velly.${baseDomain}
|
|
49
|
+
|
|
50
|
+
$ assistant domain register
|
|
51
|
+
✓ Registered my-assistant.${baseDomain}
|
|
52
|
+
|
|
53
|
+
$ assistant domain register velly --json
|
|
54
|
+
{"domain":"velly.${baseDomain}","id":"...","status":"active","verified":true}`,
|
|
55
|
+
)
|
|
56
|
+
.action(
|
|
57
|
+
async (subdomain: string | undefined, _opts: unknown, cmd: Command) => {
|
|
58
|
+
try {
|
|
59
|
+
const client = await VellumPlatformClient.create();
|
|
60
|
+
if (!client) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (!client.platformAssistantId) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const body: Record<string, string> = {};
|
|
72
|
+
if (subdomain) {
|
|
73
|
+
body.subdomain = subdomain;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const response = await client.fetch(
|
|
77
|
+
`/v1/assistants/${client.platformAssistantId}/domains/`,
|
|
78
|
+
{
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: { "Content-Type": "application/json" },
|
|
81
|
+
body: JSON.stringify(body),
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
const respBody = (await response
|
|
87
|
+
.json()
|
|
88
|
+
.catch(() => ({}))) as Record<string, unknown>;
|
|
89
|
+
const detail =
|
|
90
|
+
respBody.detail ??
|
|
91
|
+
(Array.isArray(respBody.subdomain)
|
|
92
|
+
? respBody.subdomain[0]
|
|
93
|
+
: undefined) ??
|
|
94
|
+
`HTTP ${response.status}`;
|
|
95
|
+
throw new Error(String(detail));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const data = (await response.json()) as {
|
|
99
|
+
id: string;
|
|
100
|
+
domain: string;
|
|
101
|
+
status: string;
|
|
102
|
+
verified: boolean;
|
|
103
|
+
created_at: string;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (shouldOutputJson(cmd)) {
|
|
107
|
+
writeOutput(cmd, data);
|
|
108
|
+
} else {
|
|
109
|
+
log.info(`✓ Registered ${data.domain}`);
|
|
110
|
+
if (!data.verified) {
|
|
111
|
+
log.info(
|
|
112
|
+
" ⚠ Domain verification pending — this usually resolves within a few seconds.",
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} catch (err) {
|
|
117
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
118
|
+
if (shouldOutputJson(cmd)) {
|
|
119
|
+
writeOutput(cmd, { error: message });
|
|
120
|
+
} else {
|
|
121
|
+
log.error(`Error: ${message}`);
|
|
122
|
+
}
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
domain
|
|
129
|
+
.command("status")
|
|
130
|
+
.description("Show this assistant's domain registration and health")
|
|
131
|
+
.addHelpText(
|
|
132
|
+
"after",
|
|
133
|
+
`
|
|
134
|
+
Shows the domain currently registered for this assistant, including
|
|
135
|
+
verification status and DNS health.
|
|
136
|
+
|
|
137
|
+
Examples:
|
|
138
|
+
$ assistant domain status
|
|
139
|
+
Domain: velly.${baseDomain}
|
|
140
|
+
Status: active
|
|
141
|
+
Verified: yes
|
|
142
|
+
Created: 2026-04-15
|
|
143
|
+
|
|
144
|
+
$ assistant domain status --json
|
|
145
|
+
{"domain":"velly.${baseDomain}","status":"active","verified":true,...}`,
|
|
146
|
+
)
|
|
147
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
148
|
+
try {
|
|
149
|
+
const client = await VellumPlatformClient.create();
|
|
150
|
+
if (!client) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (!client.platformAssistantId) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const response = await client.fetch(
|
|
162
|
+
`/v1/assistants/${client.platformAssistantId}/domains/`,
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
if (!response.ok) {
|
|
166
|
+
const respBody = (await response.json().catch(() => ({}))) as Record<
|
|
167
|
+
string,
|
|
168
|
+
unknown
|
|
169
|
+
>;
|
|
170
|
+
const detail = respBody.detail ?? `HTTP ${response.status}`;
|
|
171
|
+
throw new Error(String(detail));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const data = (await response.json()) as {
|
|
175
|
+
results: {
|
|
176
|
+
id: string;
|
|
177
|
+
domain: string;
|
|
178
|
+
status: string;
|
|
179
|
+
verified: boolean;
|
|
180
|
+
created_at: string;
|
|
181
|
+
}[];
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const domains = data.results ?? [];
|
|
185
|
+
|
|
186
|
+
if (shouldOutputJson(cmd)) {
|
|
187
|
+
writeOutput(cmd, data);
|
|
188
|
+
} else if (domains.length === 0) {
|
|
189
|
+
log.info(
|
|
190
|
+
"No domain registered for this assistant. Run: assistant domain register [subdomain]",
|
|
191
|
+
);
|
|
192
|
+
} else {
|
|
193
|
+
for (const d of domains) {
|
|
194
|
+
log.info(`Domain: ${d.domain}`);
|
|
195
|
+
log.info(`Status: ${d.status}`);
|
|
196
|
+
log.info(`Verified: ${d.verified ? "yes" : "no"}`);
|
|
197
|
+
log.info(`Created: ${d.created_at.split("T")[0]}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch (err) {
|
|
201
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
202
|
+
if (shouldOutputJson(cmd)) {
|
|
203
|
+
writeOutput(cmd, { error: message });
|
|
204
|
+
} else {
|
|
205
|
+
log.error(`Error: ${message}`);
|
|
206
|
+
}
|
|
207
|
+
process.exitCode = 1;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { createWriteStream, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import type { Readable } from "node:stream";
|
|
4
|
+
import { pipeline } from "node:stream/promises";
|
|
2
5
|
|
|
3
6
|
import type { Command } from "commander";
|
|
4
7
|
|
|
5
8
|
import { getAssistantDomain } from "../../config/env.js";
|
|
9
|
+
import { markdownToEmailHtml } from "../../email/html-renderer.js";
|
|
6
10
|
import { VellumPlatformClient } from "../../platform/client.js";
|
|
7
11
|
import { getCliLogger } from "../logger.js";
|
|
8
12
|
import { shouldOutputJson, writeOutput } from "../output.js";
|
|
@@ -30,6 +34,8 @@ Examples:
|
|
|
30
34
|
$ assistant email send user@example.com -s "Hello" -b "Hi there"
|
|
31
35
|
$ assistant email status
|
|
32
36
|
$ assistant email list
|
|
37
|
+
$ assistant email attachment msg_abc1 --list
|
|
38
|
+
$ assistant email attachment msg_abc1 att_xyz1
|
|
33
39
|
$ assistant email register mybot --json`,
|
|
34
40
|
);
|
|
35
41
|
|
|
@@ -569,6 +575,10 @@ Examples:
|
|
|
569
575
|
.option("-b, --body <text>", "Email body (plain text)")
|
|
570
576
|
.option("-f, --file <path>", "Read body from file")
|
|
571
577
|
.option("--html <path>", "HTML body file (optional)")
|
|
578
|
+
.option(
|
|
579
|
+
"--reply-to <email_id>",
|
|
580
|
+
"Reply to an email by its ID (auto-resolves threading headers and subject)",
|
|
581
|
+
)
|
|
572
582
|
.addHelpText(
|
|
573
583
|
"after",
|
|
574
584
|
`
|
|
@@ -581,13 +591,20 @@ from the assistant's registered email address.
|
|
|
581
591
|
|
|
582
592
|
Body source priority: --body flag > --file flag > stdin (if not a TTY).
|
|
583
593
|
|
|
594
|
+
When --reply-to is provided, the platform auto-resolves In-Reply-To,
|
|
595
|
+
References, and Subject headers from the referenced email. You can
|
|
596
|
+
still override subject with -s.
|
|
597
|
+
|
|
584
598
|
Examples:
|
|
585
599
|
$ assistant email send user@example.com -s "Hello" -b "Hi there"
|
|
586
600
|
✓ Sent to user@example.com (delivery_id: abc123)
|
|
587
601
|
|
|
588
|
-
$
|
|
602
|
+
$ assistant email send user@example.com -b "Thanks!" --reply-to 019d96e4-e5d2-7201-890e-04a21e8f95bb
|
|
589
603
|
✓ Sent to user@example.com (delivery_id: def456)
|
|
590
604
|
|
|
605
|
+
$ echo "Body text" | assistant email send user@example.com -s "Hello"
|
|
606
|
+
✓ Sent to user@example.com (delivery_id: ghi789)
|
|
607
|
+
|
|
591
608
|
$ assistant email send user@example.com -s "Hello" -b "Hi" --json
|
|
592
609
|
{"delivery_id":"abc123","status":"accepted"}`,
|
|
593
610
|
)
|
|
@@ -599,6 +616,7 @@ Examples:
|
|
|
599
616
|
body?: string;
|
|
600
617
|
file?: string;
|
|
601
618
|
html?: string;
|
|
619
|
+
replyTo?: string;
|
|
602
620
|
},
|
|
603
621
|
cmd: Command,
|
|
604
622
|
) => {
|
|
@@ -653,10 +671,13 @@ Examples:
|
|
|
653
671
|
);
|
|
654
672
|
}
|
|
655
673
|
|
|
656
|
-
// 3. Resolve
|
|
674
|
+
// 3. Resolve HTML body: explicit file > auto-generate from text
|
|
657
675
|
let html: string | undefined;
|
|
658
676
|
if (opts.html) {
|
|
659
677
|
html = readFileSync(opts.html, "utf-8");
|
|
678
|
+
} else {
|
|
679
|
+
// Auto-generate HTML from the text body (markdown → email HTML).
|
|
680
|
+
html = markdownToEmailHtml(text);
|
|
660
681
|
}
|
|
661
682
|
|
|
662
683
|
// 4. Build payload
|
|
@@ -667,6 +688,7 @@ Examples:
|
|
|
667
688
|
};
|
|
668
689
|
if (opts.subject) payload.subject = opts.subject;
|
|
669
690
|
if (html) payload.html = html;
|
|
691
|
+
if (opts.replyTo) payload.reply_to = opts.replyTo;
|
|
670
692
|
|
|
671
693
|
// 5. Send via runtime proxy
|
|
672
694
|
const response = await client.fetch("/v1/runtime-proxy/email/send/", {
|
|
@@ -705,4 +727,234 @@ Examples:
|
|
|
705
727
|
}
|
|
706
728
|
},
|
|
707
729
|
);
|
|
730
|
+
|
|
731
|
+
email
|
|
732
|
+
.command("attachment <message-id> [attachment-id]")
|
|
733
|
+
.description("Download email attachments")
|
|
734
|
+
.option("--all", "Download all attachments for the message")
|
|
735
|
+
.option(
|
|
736
|
+
"-o, --output <dir>",
|
|
737
|
+
"Output directory (default: current directory)",
|
|
738
|
+
".",
|
|
739
|
+
)
|
|
740
|
+
.option("--list", "List attachments without downloading")
|
|
741
|
+
.addHelpText(
|
|
742
|
+
"after",
|
|
743
|
+
`
|
|
744
|
+
Arguments:
|
|
745
|
+
message-id Email message ID (from \`assistant email list --json\`)
|
|
746
|
+
attachment-id Attachment ID (optional — required unless --all or --list)
|
|
747
|
+
|
|
748
|
+
Download one or all attachments from a specific email message. Use
|
|
749
|
+
--list to see available attachments without downloading.
|
|
750
|
+
|
|
751
|
+
Examples:
|
|
752
|
+
$ assistant email attachment msg_abc1 --list
|
|
753
|
+
$ assistant email attachment msg_abc1 att_xyz1
|
|
754
|
+
$ assistant email attachment msg_abc1 att_xyz1 -o ./downloads/
|
|
755
|
+
$ assistant email attachment msg_abc1 --all
|
|
756
|
+
$ assistant email attachment msg_abc1 --all -o ./attachments/
|
|
757
|
+
$ assistant email attachment msg_abc1 --list --json`,
|
|
758
|
+
)
|
|
759
|
+
.action(
|
|
760
|
+
async (
|
|
761
|
+
messageId: string,
|
|
762
|
+
attachmentId: string | undefined,
|
|
763
|
+
opts: {
|
|
764
|
+
all?: boolean;
|
|
765
|
+
output?: string;
|
|
766
|
+
list?: boolean;
|
|
767
|
+
},
|
|
768
|
+
cmd: Command,
|
|
769
|
+
) => {
|
|
770
|
+
try {
|
|
771
|
+
const client = await VellumPlatformClient.create();
|
|
772
|
+
if (!client) {
|
|
773
|
+
throw new Error(
|
|
774
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
if (!client.platformAssistantId) {
|
|
778
|
+
throw new Error(
|
|
779
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
const assistantId = client.platformAssistantId;
|
|
784
|
+
const basePath = `/v1/assistants/${assistantId}/emails/${messageId}/attachments`;
|
|
785
|
+
|
|
786
|
+
if (opts.list) {
|
|
787
|
+
// List mode — show attachment metadata without downloading
|
|
788
|
+
const response = await client.fetch(`${basePath}/`);
|
|
789
|
+
if (!response.ok) {
|
|
790
|
+
const body = (await response
|
|
791
|
+
.json()
|
|
792
|
+
.catch(() => ({}))) as Record<string, unknown>;
|
|
793
|
+
const detail = body.detail ?? `HTTP ${response.status}`;
|
|
794
|
+
throw new Error(String(detail));
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const data = (await response.json()) as {
|
|
798
|
+
results: AttachmentMeta[];
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
if (shouldOutputJson(cmd)) {
|
|
802
|
+
writeOutput(cmd, data);
|
|
803
|
+
} else {
|
|
804
|
+
const attachments = data.results ?? [];
|
|
805
|
+
if (attachments.length === 0) {
|
|
806
|
+
log.info("No attachments for this message.");
|
|
807
|
+
} else {
|
|
808
|
+
for (const att of attachments) {
|
|
809
|
+
log.info(
|
|
810
|
+
` ${att.id} ${att.filename} (${att.content_type}, ${formatBytes(att.size_bytes)})`,
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
log.info(`\n${attachments.length} attachment(s)`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (!opts.all && !attachmentId) {
|
|
820
|
+
throw new Error(
|
|
821
|
+
"Specify an attachment ID, or use --all to download all attachments. Use --list to see available attachments.",
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Ensure output directory exists
|
|
826
|
+
const outDir = opts.output ?? ".";
|
|
827
|
+
mkdirSync(outDir, { recursive: true });
|
|
828
|
+
|
|
829
|
+
if (opts.all) {
|
|
830
|
+
// Download all attachments
|
|
831
|
+
const listResponse = await client.fetch(`${basePath}/`);
|
|
832
|
+
if (!listResponse.ok) {
|
|
833
|
+
const body = (await listResponse
|
|
834
|
+
.json()
|
|
835
|
+
.catch(() => ({}))) as Record<string, unknown>;
|
|
836
|
+
const detail = body.detail ?? `HTTP ${listResponse.status}`;
|
|
837
|
+
throw new Error(String(detail));
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const listData = (await listResponse.json()) as {
|
|
841
|
+
results: AttachmentMeta[];
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
const attachments = listData.results ?? [];
|
|
845
|
+
if (attachments.length === 0) {
|
|
846
|
+
throw new Error("No attachments for this message.");
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const downloaded: { filename: string; size_bytes: number }[] = [];
|
|
850
|
+
for (const att of attachments) {
|
|
851
|
+
const dest = join(outDir, safeFilename(att.filename));
|
|
852
|
+
await downloadAttachment(client, basePath, att.id, dest);
|
|
853
|
+
downloaded.push({
|
|
854
|
+
filename: att.filename,
|
|
855
|
+
size_bytes: att.size_bytes,
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
if (shouldOutputJson(cmd)) {
|
|
860
|
+
writeOutput(cmd, {
|
|
861
|
+
downloaded: downloaded.length,
|
|
862
|
+
directory: outDir,
|
|
863
|
+
files: downloaded,
|
|
864
|
+
});
|
|
865
|
+
} else {
|
|
866
|
+
log.info(
|
|
867
|
+
`✓ Downloaded ${downloaded.length} attachment(s) to ${outDir}`,
|
|
868
|
+
);
|
|
869
|
+
for (const f of downloaded) {
|
|
870
|
+
log.info(` - ${f.filename} (${formatBytes(f.size_bytes)})`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
} else {
|
|
874
|
+
// Download single attachment — first get metadata for the filename
|
|
875
|
+
const metaResponse = await client.fetch(
|
|
876
|
+
`${basePath}/${attachmentId}/`,
|
|
877
|
+
);
|
|
878
|
+
if (!metaResponse.ok) {
|
|
879
|
+
const body = (await metaResponse
|
|
880
|
+
.json()
|
|
881
|
+
.catch(() => ({}))) as Record<string, unknown>;
|
|
882
|
+
const detail = body.detail ?? `HTTP ${metaResponse.status}`;
|
|
883
|
+
throw new Error(String(detail));
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const meta = (await metaResponse.json()) as AttachmentMeta;
|
|
887
|
+
const dest = join(outDir, safeFilename(meta.filename));
|
|
888
|
+
await downloadAttachment(client, basePath, meta.id, dest);
|
|
889
|
+
|
|
890
|
+
if (shouldOutputJson(cmd)) {
|
|
891
|
+
writeOutput(cmd, {
|
|
892
|
+
filename: meta.filename,
|
|
893
|
+
size_bytes: meta.size_bytes,
|
|
894
|
+
saved: dest,
|
|
895
|
+
});
|
|
896
|
+
} else {
|
|
897
|
+
log.info(
|
|
898
|
+
`✓ Downloaded ${meta.filename} (${formatBytes(meta.size_bytes)})`,
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
} catch (err) {
|
|
903
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
904
|
+
if (shouldOutputJson(cmd)) {
|
|
905
|
+
writeOutput(cmd, { error: message });
|
|
906
|
+
} else {
|
|
907
|
+
log.error(`Error: ${message}`);
|
|
908
|
+
}
|
|
909
|
+
process.exitCode = 1;
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
interface AttachmentMeta {
|
|
916
|
+
id: string;
|
|
917
|
+
filename: string;
|
|
918
|
+
content_type: string;
|
|
919
|
+
size_bytes: number;
|
|
920
|
+
content_id: string;
|
|
921
|
+
created_at: string;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function formatBytes(bytes: number): string {
|
|
925
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
926
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
927
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function safeFilename(name: string): string {
|
|
931
|
+
// Strip path separators and null bytes — keep the basename only
|
|
932
|
+
return basename(name).replace(/[\x00/\\]/g, "_") || "attachment";
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
async function downloadAttachment(
|
|
936
|
+
client: VellumPlatformClient,
|
|
937
|
+
basePath: string,
|
|
938
|
+
attachmentId: string,
|
|
939
|
+
dest: string,
|
|
940
|
+
): Promise<void> {
|
|
941
|
+
const response = await client.fetch(
|
|
942
|
+
`${basePath}/${attachmentId}/download/`,
|
|
943
|
+
);
|
|
944
|
+
|
|
945
|
+
if (!response.ok) {
|
|
946
|
+
const body = (await response.json().catch(() => ({}))) as Record<
|
|
947
|
+
string,
|
|
948
|
+
unknown
|
|
949
|
+
>;
|
|
950
|
+
const detail = body.detail ?? `HTTP ${response.status}`;
|
|
951
|
+
throw new Error(`Failed to download attachment: ${detail}`);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
if (!response.body) {
|
|
955
|
+
throw new Error("Empty response body from download endpoint.");
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
const fileStream = createWriteStream(dest);
|
|
959
|
+
await pipeline(response.body as unknown as Readable, fileStream);
|
|
708
960
|
}
|