@vellumai/assistant 0.5.16 → 0.6.1
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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +69 -16
- package/Dockerfile +2 -5
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/knip.json +2 -1
- package/openapi.yaml +1198 -83
- package/package.json +5 -1
- package/src/__tests__/actor-token-service.test.ts +68 -0
- package/src/__tests__/agent-loop.test.ts +0 -32
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/anthropic-provider.test.ts +217 -98
- package/src/__tests__/app-compiler.test.ts +120 -0
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/call-conversation-messages.test.ts +2 -6
- package/src/__tests__/call-domain.test.ts +2 -6
- package/src/__tests__/call-pointer-messages.test.ts +2 -14
- package/src/__tests__/call-recovery.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +2 -6
- package/src/__tests__/call-store.test.ts +2 -6
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
- package/src/__tests__/canonical-guardian-store.test.ts +2 -6
- package/src/__tests__/channel-delivery-store.test.ts +2 -6
- package/src/__tests__/channel-retry-sweep.test.ts +2 -6
- package/src/__tests__/checker.test.ts +63 -9
- package/src/__tests__/clawhub.test.ts +54 -24
- package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
- package/src/__tests__/config-schema.test.ts +6 -1
- package/src/__tests__/config-set-platform-guard.test.ts +302 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
- package/src/__tests__/contacts-tools.test.ts +31 -0
- package/src/__tests__/context-overflow-reducer.test.ts +86 -0
- package/src/__tests__/context-token-estimator.test.ts +175 -10
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
- package/src/__tests__/conversation-agent-loop.test.ts +13 -51
- package/src/__tests__/conversation-attachments.test.ts +2 -6
- package/src/__tests__/conversation-attention-store.test.ts +2 -6
- package/src/__tests__/conversation-clear-safety.test.ts +2 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
- package/src/__tests__/conversation-disk-view.test.ts +2 -6
- package/src/__tests__/conversation-error.test.ts +33 -2
- package/src/__tests__/conversation-fork-crud.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
- package/src/__tests__/conversation-media-retry.test.ts +91 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-starter-routes.test.ts +20 -11
- package/src/__tests__/conversation-store.test.ts +2 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-usage.test.ts +2 -6
- package/src/__tests__/conversation-wipe.test.ts +13 -414
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
- package/src/__tests__/credential-security-e2e.test.ts +2 -0
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/followup-tools.test.ts +2 -6
- package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
- package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
- package/src/__tests__/guardian-action-store.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
- package/src/__tests__/guardian-dispatch.test.ts +2 -6
- package/src/__tests__/guardian-grant-minting.test.ts +2 -14
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
- package/src/__tests__/guardian-routing-state.test.ts +2 -6
- package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
- package/src/__tests__/injection-block.test.ts +178 -0
- package/src/__tests__/install-meta.test.ts +506 -0
- package/src/__tests__/install-skill-routing.test.ts +293 -0
- package/src/__tests__/invite-redemption-service.test.ts +2 -6
- package/src/__tests__/invite-routes-http.test.ts +2 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
- package/src/__tests__/list-messages-attachments.test.ts +2 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +103 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
- package/src/__tests__/llm-usage-store.test.ts +2 -6
- package/src/__tests__/log-export-workspace.test.ts +74 -111
- package/src/__tests__/managed-store.test.ts +38 -11
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
- package/src/__tests__/memory-recall-log-store.test.ts +134 -6
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/non-member-access-request.test.ts +2 -6
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/notification-guardian-path.test.ts +2 -6
- package/src/__tests__/oauth-cli.test.ts +364 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
- package/src/__tests__/onboarding-template-contract.test.ts +62 -14
- package/src/__tests__/outlook-attachments.test.ts +301 -0
- package/src/__tests__/outlook-automation-tools.test.ts +425 -0
- package/src/__tests__/outlook-categories.test.ts +212 -0
- package/src/__tests__/outlook-client-automation.test.ts +246 -0
- package/src/__tests__/outlook-compose-tools.test.ts +325 -0
- package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
- package/src/__tests__/outlook-email-watcher.test.ts +322 -0
- package/src/__tests__/outlook-follow-up.test.ts +196 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
- package/src/__tests__/outlook-trash.test.ts +77 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/platform-callback-registration.test.ts +4 -4
- package/src/__tests__/playbook-execution.test.ts +76 -80
- package/src/__tests__/playbook-tools.test.ts +5 -7
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/provider-error-scenarios.test.ts +21 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
- package/src/__tests__/registry.test.ts +3 -3
- package/src/__tests__/require-fresh-approval.test.ts +64 -2
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
- package/src/__tests__/runtime-events-sse.test.ts +2 -6
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/schedule-store.test.ts +2 -6
- package/src/__tests__/schedule-tools.test.ts +2 -6
- package/src/__tests__/scheduler-recurrence.test.ts +1 -5
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scoped-approval-grants.test.ts +2 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +422 -0
- package/src/__tests__/secret-onetime-send.test.ts +2 -0
- package/src/__tests__/send-endpoint-busy.test.ts +44 -9
- package/src/__tests__/sequence-store.test.ts +2 -6
- package/src/__tests__/server-history-render.test.ts +2 -6
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
- package/src/__tests__/skill-memory.test.ts +2 -741
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -6
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-compiler.test.ts +2 -6
- package/src/__tests__/task-management-tools.test.ts +2 -6
- package/src/__tests__/task-memory-cleanup.test.ts +185 -241
- package/src/__tests__/task-runner.test.ts +2 -6
- package/src/__tests__/task-scheduler.test.ts +2 -6
- package/src/__tests__/terminal-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +7 -0
- package/src/__tests__/tool-approval-handler.test.ts +2 -6
- package/src/__tests__/tool-executor.test.ts +4 -26
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/trust-store.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +2 -6
- package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
- package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
- package/src/__tests__/usage-routes.test.ts +2 -6
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +2 -6
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
- package/src/__tests__/voice-session-bridge.test.ts +2 -6
- package/src/__tests__/volume-security-guard.test.ts +2 -0
- package/src/__tests__/workspace-lifecycle.test.ts +29 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/agent/attachments.ts +7 -2
- package/src/agent/image-optimize.ts +165 -0
- package/src/agent/loop.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/bundler/app-compiler.ts +179 -2
- package/src/bundler/package-resolver.ts +3 -5
- package/src/cli/__tests__/notifications.test.ts +1 -2
- package/src/cli/__tests__/run-assistant-command.ts +29 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/avatar.ts +3 -3
- package/src/cli/commands/config.ts +26 -13
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +37 -84
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
- package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +25 -11
- package/src/cli/commands/oauth/mode.ts +7 -0
- package/src/cli/commands/oauth/shared.ts +39 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
- package/src/cli/commands/platform/index.ts +16 -16
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +218 -36
- package/src/cli/commands/trust.ts +2 -2
- package/src/cli/lib/daemon-credential-client.ts +2 -3
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/acp/TOOLS.json +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- package/src/config/bundled-skills/contacts/SKILL.md +0 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
- package/src/config/bundled-skills/gmail/SKILL.md +4 -12
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
- package/src/config/bundled-skills/messaging/SKILL.md +17 -18
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
- package/src/config/bundled-skills/outlook/SKILL.md +189 -0
- package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
- package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
- package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/slack/SKILL.md +3 -7
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/bundled-tool-registry.ts +56 -4
- package/src/config/env-registry.ts +78 -8
- package/src/config/feature-flag-registry.json +38 -125
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/config/skills.ts +18 -7
- package/src/context/token-estimator.ts +25 -18
- package/src/context/window-manager.ts +6 -2
- package/src/credential-execution/process-manager.ts +3 -1
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/context-overflow-reducer.ts +46 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
- package/src/daemon/conversation-agent-loop.ts +236 -108
- package/src/daemon/conversation-error.ts +31 -8
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +36 -9
- package/src/daemon/conversation-media-retry.ts +85 -7
- package/src/daemon/conversation-notifiers.ts +4 -1
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +305 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +59 -2
- package/src/daemon/daemon-control.ts +8 -2
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +85 -17
- package/src/daemon/handlers/skills.ts +416 -209
- package/src/daemon/lifecycle.ts +212 -131
- package/src/daemon/main.ts +5 -1
- package/src/daemon/message-types/conversations.ts +29 -7
- package/src/daemon/message-types/messages.ts +12 -2
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/message-types/skills.ts +97 -36
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/providers-setup.ts +5 -0
- package/src/daemon/server.ts +100 -11
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +50 -8
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +97 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/runner.ts +15 -38
- package/src/inbound/platform-callback-registration.ts +14 -14
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +42 -75
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +3 -1
- package/src/memory/conversation-crud.ts +211 -288
- package/src/memory/conversation-group-migration.ts +157 -0
- package/src/memory/conversation-queries.ts +61 -13
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +194 -361
- package/src/memory/embed.ts +73 -0
- package/src/memory/embedding-backend.ts +8 -14
- package/src/memory/embedding-runtime-manager.ts +12 -114
- package/src/memory/fingerprint.ts +2 -2
- package/src/memory/graph/bootstrap.ts +521 -0
- package/src/memory/graph/capability-seed.ts +449 -0
- package/src/memory/graph/consolidation.ts +725 -0
- package/src/memory/graph/conversation-graph-memory.ts +659 -0
- package/src/memory/graph/decay.test.ts +208 -0
- package/src/memory/graph/decay.ts +195 -0
- package/src/memory/graph/extraction-job.ts +74 -0
- package/src/memory/graph/extraction.test.ts +936 -0
- package/src/memory/graph/extraction.ts +1297 -0
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +280 -0
- package/src/memory/graph/image-ref-utils.ts +29 -0
- package/src/memory/graph/injection.test.ts +513 -0
- package/src/memory/graph/injection.ts +469 -0
- package/src/memory/graph/inspect.ts +543 -0
- package/src/memory/graph/narrative.ts +267 -0
- package/src/memory/graph/pattern-scan.ts +269 -0
- package/src/memory/graph/retriever.ts +1111 -0
- package/src/memory/graph/scoring.test.ts +548 -0
- package/src/memory/graph/scoring.ts +232 -0
- package/src/memory/graph/serendipity.ts +65 -0
- package/src/memory/graph/store.test.ts +1098 -0
- package/src/memory/graph/store.ts +838 -0
- package/src/memory/graph/tool-handlers.ts +301 -0
- package/src/memory/graph/tools.ts +97 -0
- package/src/memory/graph/triggers.test.ts +487 -0
- package/src/memory/graph/triggers.ts +223 -0
- package/src/memory/graph/types.ts +295 -0
- package/src/memory/group-crud.ts +191 -0
- package/src/memory/indexer.ts +37 -19
- package/src/memory/job-handlers/cleanup.ts +32 -42
- package/src/memory/job-handlers/conversation-starters.ts +91 -53
- package/src/memory/job-handlers/embedding.ts +5 -31
- package/src/memory/job-handlers/index-maintenance.ts +23 -11
- package/src/memory/job-handlers/summarization.ts +32 -17
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +21 -31
- package/src/memory/jobs-worker.ts +180 -129
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/message-content.ts +1 -0
- package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
- package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
- package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
- package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-client.ts +44 -17
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +154 -0
- package/src/memory/search/semantic.ts +47 -91
- package/src/memory/task-memory-cleanup.ts +58 -61
- package/src/messaging/providers/outlook/adapter.ts +8 -1
- package/src/messaging/providers/outlook/client.ts +299 -0
- package/src/messaging/providers/outlook/types.ts +118 -0
- package/src/notifications/adapters/macos.ts +1 -0
- package/src/notifications/copy-composer.ts +95 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/signal.ts +16 -0
- package/src/oauth/seed-providers.ts +2 -1
- package/src/permissions/checker.ts +36 -4
- package/src/permissions/defaults.ts +4 -4
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +10 -1
- package/src/playbooks/playbook-compiler.ts +19 -18
- package/src/playbooks/types.ts +4 -3
- package/src/prompts/system-prompt.ts +62 -36
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +136 -220
- package/src/providers/gemini/client.ts +1 -1
- package/src/providers/openai/client.ts +1 -1
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +19 -3
- package/src/runtime/actor-trust-resolver.ts +5 -1
- package/src/runtime/auth/route-policy.ts +30 -0
- package/src/runtime/guardian-reply-router.ts +5 -1
- package/src/runtime/http-server.ts +55 -5
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/middleware/auth.ts +20 -0
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/attachment-routes.test.ts +106 -0
- package/src/runtime/routes/attachment-routes.ts +106 -16
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/brain-graph-routes.ts +21 -22
- package/src/runtime/routes/btw-routes.ts +22 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
- package/src/runtime/routes/conversation-management-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +264 -44
- package/src/runtime/routes/conversation-starter-routes.ts +2 -2
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/global-search-routes.ts +21 -19
- package/src/runtime/routes/group-routes.ts +207 -0
- package/src/runtime/routes/guardian-action-routes.ts +21 -10
- package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/inbound-message-handler.ts +19 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export-routes.ts +23 -275
- package/src/runtime/routes/memory-item-routes.test.ts +170 -247
- package/src/runtime/routes/memory-item-routes.ts +341 -388
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +28 -11
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/skills-routes.ts +103 -37
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/work-items-routes.test.ts +2 -6
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +52 -18
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +4 -8
- package/src/shared/provider-env-vars.ts +19 -0
- package/src/skills/catalog-cache.ts +5 -0
- package/src/skills/catalog-install.ts +25 -16
- package/src/skills/clawhub.ts +134 -154
- package/src/skills/install-meta.ts +208 -0
- package/src/skills/managed-store.ts +29 -18
- package/src/skills/skill-memory.ts +12 -229
- package/src/skills/skillssh-registry.ts +19 -17
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +7 -5
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/browser/runtime-check.ts +3 -1
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/memory/register.ts +63 -46
- package/src/tools/permission-checker.ts +85 -1
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/image-read.ts +22 -85
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +17 -1
- package/src/tools/tool-manifest.ts +9 -3
- package/src/tools/types.ts +2 -0
- package/src/util/browser.ts +25 -10
- package/src/util/bun-runtime.ts +172 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/providers/outlook-calendar.ts +343 -0
- package/src/watcher/providers/outlook.ts +198 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
- package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
- package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +84 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -372
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/__tests__/context-memory-e2e.test.ts +0 -415
- package/src/__tests__/journal-context.test.ts +0 -268
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
- package/src/__tests__/memory-query-builder.test.ts +0 -59
- package/src/__tests__/memory-recall-quality.test.ts +0 -1046
- package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
- package/src/__tests__/memory-regressions.test.ts +0 -3696
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
- package/src/cli/cli-memory.ts +0 -176
- package/src/daemon/conversation-memory.ts +0 -207
- package/src/memory/conversation-starters-cadence.ts +0 -74
- package/src/memory/items-extractor.ts +0 -860
- package/src/memory/job-handlers/batch-extraction.ts +0 -753
- package/src/memory/job-handlers/extraction.ts +0 -40
- package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
- package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
- package/src/memory/journal-memory.ts +0 -224
- package/src/memory/query-builder.ts +0 -47
- package/src/memory/query-expansion.ts +0 -83
- package/src/memory/retriever.test.ts +0 -1592
- package/src/memory/retriever.ts +0 -1331
- package/src/memory/search/formatting.test.ts +0 -140
- package/src/memory/search/formatting.ts +0 -262
- package/src/memory/search/mmr.ts +0 -139
- package/src/memory/search/ranking.ts +0 -15
- package/src/memory/search/staleness.ts +0 -40
- package/src/memory/search/tier-classifier.ts +0 -18
- package/src/memory/search/types.ts +0 -121
- package/src/prompts/journal-context.ts +0 -154
- package/src/tools/memory/definitions.ts +0 -69
- package/src/tools/memory/handlers.test.ts +0 -562
- package/src/tools/memory/handlers.ts +0 -434
- package/src/util/clipboard.ts +0 -34
|
@@ -1,954 +1,703 @@
|
|
|
1
|
-
|
|
2
|
-
* CLI command group: `assistant email`
|
|
3
|
-
*
|
|
4
|
-
* Provider-agnostic email operations routed through the service facade.
|
|
5
|
-
* All commands output JSON to stdout. Use --json for machine-readable output.
|
|
6
|
-
* Exit codes: 0 = success, 1 = error, 2 = guardrail blocked.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { Command } from "commander";
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
SUPPORTED_PROVIDERS,
|
|
13
|
-
type SupportedProvider,
|
|
14
|
-
} from "../../email/providers/index.js";
|
|
15
|
-
import { getEmailService, GuardrailError } from "../../email/service.js";
|
|
16
|
-
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
// Helpers
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
|
|
21
|
-
function output(data: unknown, json: boolean): void {
|
|
22
|
-
process.stdout.write(
|
|
23
|
-
json ? JSON.stringify(data) + "\n" : JSON.stringify(data, null, 2) + "\n",
|
|
24
|
-
);
|
|
25
|
-
}
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
26
2
|
|
|
27
|
-
|
|
28
|
-
output(data, true);
|
|
29
|
-
process.exitCode = code;
|
|
30
|
-
}
|
|
3
|
+
import type { Command } from "commander";
|
|
31
4
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
5
|
+
import { VellumPlatformClient } from "../../platform/client.js";
|
|
6
|
+
import { getCliLogger } from "../logger.js";
|
|
7
|
+
import { shouldOutputJson, writeOutput } from "../output.js";
|
|
35
8
|
|
|
36
|
-
|
|
37
|
-
let c: Command | null = cmd;
|
|
38
|
-
while (c) {
|
|
39
|
-
if ((c.opts() as { json?: boolean }).json) return true;
|
|
40
|
-
c = c.parent;
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function run(cmd: Command, fn: () => Promise<unknown>): Promise<void> {
|
|
46
|
-
try {
|
|
47
|
-
const result = await fn();
|
|
48
|
-
output({ ok: true, ...(result as Record<string, unknown>) }, getJson(cmd));
|
|
49
|
-
} catch (err) {
|
|
50
|
-
if (err instanceof GuardrailError) {
|
|
51
|
-
outputError({ ok: false, error: err.code, ...err.details }, 2);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
outputError(
|
|
55
|
-
{ ok: false, error: err instanceof Error ? err.message : String(err) },
|
|
56
|
-
1,
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
// Command registration
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
9
|
+
const log = getCliLogger("email");
|
|
64
10
|
|
|
65
11
|
export function registerEmailCommand(program: Command): void {
|
|
66
12
|
const email = program
|
|
67
13
|
.command("email")
|
|
68
|
-
.description("Email operations
|
|
69
|
-
.option("--json", "Machine-readable JSON output");
|
|
14
|
+
.description("Email channel operations")
|
|
15
|
+
.option("--json", "Machine-readable compact JSON output");
|
|
70
16
|
|
|
71
17
|
email.addHelpText(
|
|
72
18
|
"after",
|
|
73
19
|
`
|
|
74
|
-
|
|
75
|
-
the configured email provider (e.g. agentmail, resend). Use "email provider"
|
|
76
|
-
to switch between providers.
|
|
77
|
-
|
|
78
|
-
Outbound emails follow a draft-based sending model:
|
|
79
|
-
1. Create a draft with "email draft create"
|
|
80
|
-
2. Approve and send with "email draft approve-send"
|
|
81
|
-
3. Optionally reject with "email draft reject"
|
|
82
|
-
|
|
83
|
-
Guardrails (outbound pause, daily send cap, address allow/block rules) are
|
|
84
|
-
enforced at send time. If a guardrail blocks sending, exit code 2 is returned.
|
|
85
|
-
|
|
86
|
-
Exit codes: 0 = success, 1 = error, 2 = guardrail blocked.
|
|
20
|
+
Manage the assistant's email channel on the Vellum platform.
|
|
87
21
|
|
|
88
22
|
Examples:
|
|
23
|
+
$ assistant email register mybot
|
|
24
|
+
$ assistant email unregister --confirm
|
|
25
|
+
$ assistant email send user@example.com -s "Hello" -b "Hi there"
|
|
89
26
|
$ assistant email status
|
|
90
|
-
$ assistant email
|
|
91
|
-
$ assistant email
|
|
92
|
-
$ assistant email guardrails set --daily-cap 50
|
|
93
|
-
$ assistant email setup domain --domain example.com`,
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
const svc = getEmailService();
|
|
97
|
-
|
|
98
|
-
// =========================================================================
|
|
99
|
-
// Provider subcommands
|
|
100
|
-
// =========================================================================
|
|
101
|
-
const provider = email
|
|
102
|
-
.command("provider")
|
|
103
|
-
.description("Manage email provider");
|
|
104
|
-
|
|
105
|
-
provider.addHelpText(
|
|
106
|
-
"after",
|
|
107
|
-
`
|
|
108
|
-
Switch between email providers without changing the rest of your email
|
|
109
|
-
configuration. The active provider determines which backend handles domain
|
|
110
|
-
setup, inbox creation, DNS records, and message delivery.
|
|
111
|
-
|
|
112
|
-
Examples:
|
|
113
|
-
$ assistant email provider get
|
|
114
|
-
$ assistant email provider set agentmail`,
|
|
27
|
+
$ assistant email list
|
|
28
|
+
$ assistant email register mybot --json`,
|
|
115
29
|
);
|
|
116
30
|
|
|
117
|
-
|
|
118
|
-
.command("
|
|
119
|
-
.description("
|
|
120
|
-
.addHelpText(
|
|
121
|
-
"after",
|
|
122
|
-
`
|
|
123
|
-
Returns the name of the currently active email provider (e.g. agentmail,
|
|
124
|
-
resend). Use this to confirm which backend is handling email operations
|
|
125
|
-
before making changes.
|
|
126
|
-
|
|
127
|
-
Examples:
|
|
128
|
-
$ assistant email provider get
|
|
129
|
-
$ assistant email provider get --json`,
|
|
130
|
-
)
|
|
131
|
-
.action((_opts: unknown, cmd: Command) => {
|
|
132
|
-
output({ ok: true, provider: svc.getProviderName() }, getJson(cmd));
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
provider
|
|
136
|
-
.command("set <provider>")
|
|
137
|
-
.description(
|
|
138
|
-
`Set the active email provider (${SUPPORTED_PROVIDERS.join(", ")})`,
|
|
139
|
-
)
|
|
31
|
+
email
|
|
32
|
+
.command("register <username>")
|
|
33
|
+
.description("Register an @vellum.me email address for this assistant")
|
|
140
34
|
.addHelpText(
|
|
141
35
|
"after",
|
|
142
36
|
`
|
|
143
37
|
Arguments:
|
|
144
|
-
|
|
38
|
+
username The local part of the email address (e.g. "mybot" → mybot@vellum.me)
|
|
145
39
|
|
|
146
|
-
|
|
147
|
-
|
|
40
|
+
Registers a new email address on the Vellum platform for the current
|
|
41
|
+
assistant. Each assistant can have one email address. The address is
|
|
42
|
+
immediately active for receiving inbound email.
|
|
148
43
|
|
|
149
44
|
Examples:
|
|
150
|
-
$ assistant email
|
|
151
|
-
|
|
152
|
-
.action((name: string, _opts: unknown, cmd: Command) => {
|
|
153
|
-
if (!SUPPORTED_PROVIDERS.includes(name as SupportedProvider)) {
|
|
154
|
-
exitError(
|
|
155
|
-
`Unknown provider: ${name}. Supported: ${SUPPORTED_PROVIDERS.join(", ")}`,
|
|
156
|
-
);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
svc.setProvider(name as SupportedProvider);
|
|
160
|
-
output({ ok: true, provider: name }, getJson(cmd));
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// =========================================================================
|
|
164
|
-
// Status
|
|
165
|
-
// =========================================================================
|
|
166
|
-
email
|
|
167
|
-
.command("status")
|
|
168
|
-
.description("Show provider health, inboxes, and guardrail state")
|
|
169
|
-
.addHelpText(
|
|
170
|
-
"after",
|
|
171
|
-
`
|
|
172
|
-
Returns a combined view of the email subsystem: active provider and its health
|
|
173
|
-
status, configured inboxes with their addresses, and current guardrail state
|
|
174
|
-
(paused flag, daily send cap, today's send count).
|
|
175
|
-
|
|
176
|
-
Use this to verify the email stack is fully configured before sending.
|
|
45
|
+
$ assistant email register mybot
|
|
46
|
+
✓ Registered mybot@vellum.me
|
|
177
47
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
$ assistant email status --json`,
|
|
48
|
+
$ assistant email register support --json
|
|
49
|
+
{"address":"support@vellum.me","id":"...","created_at":"..."}`,
|
|
181
50
|
)
|
|
182
|
-
.action(async (_opts: unknown, cmd: Command) => {
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
51
|
+
.action(async (username: string, _opts: unknown, cmd: Command) => {
|
|
52
|
+
try {
|
|
53
|
+
const client = await VellumPlatformClient.create();
|
|
54
|
+
if (!client) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (!client.platformAssistantId) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
188
64
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
65
|
+
const response = await client.fetch(
|
|
66
|
+
`/v1/assistants/${client.platformAssistantId}/email-addresses/`,
|
|
67
|
+
{
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: { "Content-Type": "application/json" },
|
|
70
|
+
body: JSON.stringify({ username }),
|
|
71
|
+
},
|
|
72
|
+
);
|
|
195
73
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const body = (await response.json().catch(() => ({}))) as Record<
|
|
76
|
+
string,
|
|
77
|
+
unknown
|
|
78
|
+
>;
|
|
79
|
+
const detail =
|
|
80
|
+
body.detail ??
|
|
81
|
+
(Array.isArray(body.username) ? body.username[0] : undefined) ??
|
|
82
|
+
(Array.isArray(body.assistant_id)
|
|
83
|
+
? body.assistant_id[0]
|
|
84
|
+
: undefined) ??
|
|
85
|
+
`HTTP ${response.status}`;
|
|
86
|
+
throw new Error(String(detail));
|
|
87
|
+
}
|
|
205
88
|
|
|
206
|
-
|
|
89
|
+
const data = (await response.json()) as {
|
|
90
|
+
id: string;
|
|
91
|
+
address: string;
|
|
92
|
+
created_at: string;
|
|
93
|
+
};
|
|
207
94
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
95
|
+
if (shouldOutputJson(cmd)) {
|
|
96
|
+
writeOutput(cmd, data);
|
|
97
|
+
} else {
|
|
98
|
+
log.info(`✓ Registered ${data.address}`);
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
102
|
+
if (shouldOutputJson(cmd)) {
|
|
103
|
+
writeOutput(cmd, { error: message });
|
|
104
|
+
} else {
|
|
105
|
+
log.error(`Error: ${message}`);
|
|
106
|
+
}
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
213
110
|
|
|
214
|
-
|
|
215
|
-
.command("
|
|
216
|
-
.description("
|
|
217
|
-
.
|
|
218
|
-
.option("--dry-run", "Preview without creating")
|
|
111
|
+
email
|
|
112
|
+
.command("unregister")
|
|
113
|
+
.description("Remove the email address registered for this assistant")
|
|
114
|
+
.option("--confirm", "Skip confirmation prompt")
|
|
219
115
|
.addHelpText(
|
|
220
116
|
"after",
|
|
221
117
|
`
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
the DNS records that would need to be configured without committing changes.
|
|
118
|
+
Removes the email address currently registered for this assistant.
|
|
119
|
+
The address is deactivated immediately — inbound email will no longer
|
|
120
|
+
be delivered. The username enters a cooldown period and is not
|
|
121
|
+
immediately available for reuse.
|
|
227
122
|
|
|
228
123
|
Examples:
|
|
229
|
-
$ assistant email
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
.action(
|
|
233
|
-
async (opts: { domain: string; dryRun?: boolean }, cmd: Command) => {
|
|
234
|
-
await run(cmd, async () => {
|
|
235
|
-
const domain = await svc.setupDomain(opts.domain, opts.dryRun);
|
|
236
|
-
return { domain };
|
|
237
|
-
});
|
|
238
|
-
},
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
setup
|
|
242
|
-
.command("dns")
|
|
243
|
-
.description("Get DNS records (SPF/DKIM/DMARC) for a domain")
|
|
244
|
-
.requiredOption("--domain <domain>", "Domain name")
|
|
245
|
-
.addHelpText(
|
|
246
|
-
"after",
|
|
247
|
-
`
|
|
248
|
-
Returns the SPF, DKIM, and DMARC DNS records that must be added to your
|
|
249
|
-
domain's DNS zone. These records authorize the email provider to send on
|
|
250
|
-
behalf of your domain and improve deliverability.
|
|
124
|
+
$ assistant email unregister
|
|
125
|
+
Remove mybot@vellum.me? (y/N) y
|
|
126
|
+
✓ Unregistered mybot@vellum.me
|
|
251
127
|
|
|
252
|
-
|
|
128
|
+
$ assistant email unregister --confirm
|
|
129
|
+
✓ Unregistered mybot@vellum.me
|
|
253
130
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
$ assistant email setup dns --domain example.com --json`,
|
|
131
|
+
$ assistant email unregister --json
|
|
132
|
+
{"unregistered":"mybot@vellum.me"}`,
|
|
257
133
|
)
|
|
258
|
-
.action(async (
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
`
|
|
272
|
-
Checks that the required DNS records (SPF, DKIM, DMARC) have propagated and
|
|
273
|
-
are correctly configured. DNS propagation can take minutes to hours depending
|
|
274
|
-
on your DNS provider's TTL settings.
|
|
134
|
+
.action(async (_opts: { confirm?: boolean }, cmd: Command) => {
|
|
135
|
+
try {
|
|
136
|
+
const client = await VellumPlatformClient.create();
|
|
137
|
+
if (!client) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (!client.platformAssistantId) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
145
|
+
);
|
|
146
|
+
}
|
|
275
147
|
|
|
276
|
-
|
|
277
|
-
|
|
148
|
+
const listResponse = await client.fetch(
|
|
149
|
+
`/v1/assistants/${client.platformAssistantId}/email-addresses/`,
|
|
150
|
+
);
|
|
278
151
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const domain = await svc.verifyDomain(opts.domain);
|
|
285
|
-
return { domain };
|
|
286
|
-
});
|
|
287
|
-
});
|
|
152
|
+
if (!listResponse.ok) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Failed to list email addresses: HTTP ${listResponse.status}`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
288
157
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
.requiredOption("--domain <domain>", "Domain name")
|
|
293
|
-
.addHelpText(
|
|
294
|
-
"after",
|
|
295
|
-
`
|
|
296
|
-
Creates the standard set of inboxes (hello@, support@, ops@) on the
|
|
297
|
-
specified domain. Idempotent — re-running skips already existing inboxes.
|
|
158
|
+
const listData = (await listResponse.json()) as {
|
|
159
|
+
results: { id: string; address: string }[];
|
|
160
|
+
};
|
|
298
161
|
|
|
299
|
-
|
|
162
|
+
const addresses = listData.results ?? [];
|
|
163
|
+
if (addresses.length === 0) {
|
|
164
|
+
throw new Error("No email address registered for this assistant.");
|
|
165
|
+
}
|
|
300
166
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
167
|
+
const target = addresses[0];
|
|
168
|
+
|
|
169
|
+
if (!_opts.confirm && !shouldOutputJson(cmd)) {
|
|
170
|
+
const rl = await import("node:readline");
|
|
171
|
+
const iface = rl.createInterface({
|
|
172
|
+
input: process.stdin,
|
|
173
|
+
output: process.stderr,
|
|
174
|
+
});
|
|
175
|
+
const answer = await new Promise<string>((resolve) => {
|
|
176
|
+
iface.question(`Remove ${target.address}? (y/N) `, resolve);
|
|
177
|
+
});
|
|
178
|
+
iface.close();
|
|
179
|
+
if (answer.trim().toLowerCase() !== "y") {
|
|
180
|
+
log.info("Cancelled.");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
310
184
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
.option("--secret <secret>", "Webhook signing secret")
|
|
316
|
-
.addHelpText(
|
|
317
|
-
"after",
|
|
318
|
-
`
|
|
319
|
-
Registers a webhook URL with the email provider to receive inbound messages.
|
|
320
|
-
The provider will POST incoming emails to this URL as JSON payloads.
|
|
185
|
+
const deleteResponse = await client.fetch(
|
|
186
|
+
`/v1/assistants/${client.platformAssistantId}/email-addresses/${target.id}/`,
|
|
187
|
+
{ method: "DELETE" },
|
|
188
|
+
);
|
|
321
189
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
190
|
+
if (!deleteResponse.ok) {
|
|
191
|
+
const body = (await deleteResponse
|
|
192
|
+
.json()
|
|
193
|
+
.catch(() => ({}))) as Record<string, unknown>;
|
|
194
|
+
const detail = body.detail ?? `HTTP ${deleteResponse.status}`;
|
|
195
|
+
throw new Error(String(detail));
|
|
196
|
+
}
|
|
325
197
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
198
|
+
if (shouldOutputJson(cmd)) {
|
|
199
|
+
writeOutput(cmd, { unregistered: target.address });
|
|
200
|
+
} else {
|
|
201
|
+
log.info(`✓ Unregistered ${target.address}`);
|
|
202
|
+
}
|
|
203
|
+
} catch (err) {
|
|
204
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
205
|
+
if (shouldOutputJson(cmd)) {
|
|
206
|
+
writeOutput(cmd, { error: message });
|
|
207
|
+
} else {
|
|
208
|
+
log.error(`Error: ${message}`);
|
|
209
|
+
}
|
|
210
|
+
process.exitCode = 1;
|
|
211
|
+
}
|
|
335
212
|
});
|
|
336
213
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const inbox = email.command("inbox").description("Manage inboxes");
|
|
341
|
-
|
|
342
|
-
inbox.addHelpText(
|
|
343
|
-
"after",
|
|
344
|
-
`
|
|
345
|
-
Inboxes are email addresses that can send and receive messages through the
|
|
346
|
-
configured provider. Each inbox has a username (local part), domain, and
|
|
347
|
-
optional display name.
|
|
348
|
-
|
|
349
|
-
Examples:
|
|
350
|
-
$ assistant email inbox list
|
|
351
|
-
$ assistant email inbox create --username sam --domain example.com --display-name "Samwise"`,
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
inbox
|
|
355
|
-
.command("create")
|
|
356
|
-
.description("Create a new inbox")
|
|
357
|
-
.requiredOption("--username <username>", 'Local part (e.g. "sam")')
|
|
358
|
-
.option(
|
|
359
|
-
"--domain <domain>",
|
|
360
|
-
'Domain (e.g. "agentmail.to"). Omit for provider default.',
|
|
361
|
-
)
|
|
362
|
-
.option("--display-name <name>", 'Display name (e.g. "Samwise")')
|
|
214
|
+
email
|
|
215
|
+
.command("status")
|
|
216
|
+
.description("Show email address info and usage for this assistant")
|
|
363
217
|
.addHelpText(
|
|
364
218
|
"after",
|
|
365
219
|
`
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
The --display-name sets the friendly name shown in the "From" header
|
|
370
|
-
(e.g. "Samwise <sam@example.com>").
|
|
220
|
+
Shows the email address registered for this assistant along with
|
|
221
|
+
current usage and quota information from the platform.
|
|
371
222
|
|
|
372
223
|
Examples:
|
|
373
|
-
$ assistant email
|
|
374
|
-
|
|
375
|
-
|
|
224
|
+
$ assistant email status
|
|
225
|
+
Address: mybot@vellum.me
|
|
226
|
+
Status: active
|
|
227
|
+
Sent: 12 / 100 (daily)
|
|
228
|
+
|
|
229
|
+
$ assistant email status --json
|
|
230
|
+
{"address":"mybot@vellum.me","status":"active","usage":{"sent_today":12,"daily_limit":100}}`,
|
|
376
231
|
)
|
|
377
|
-
.action(
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const created = await svc.createInbox(
|
|
384
|
-
opts.username,
|
|
385
|
-
opts.domain,
|
|
386
|
-
opts.displayName,
|
|
232
|
+
.action(async (_opts: unknown, cmd: Command) => {
|
|
233
|
+
try {
|
|
234
|
+
const client = await VellumPlatformClient.create();
|
|
235
|
+
if (!client) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
387
238
|
);
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
239
|
+
}
|
|
240
|
+
if (!client.platformAssistantId) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
243
|
+
);
|
|
244
|
+
}
|
|
392
245
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
"after",
|
|
398
|
-
`
|
|
399
|
-
Lists all inboxes configured on the active email provider. Each inbox
|
|
400
|
-
entry includes its address, display name, and inbox ID.
|
|
246
|
+
// 1. List addresses to find the registered one
|
|
247
|
+
const listResponse = await client.fetch(
|
|
248
|
+
`/v1/assistants/${client.platformAssistantId}/email-addresses/`,
|
|
249
|
+
);
|
|
401
250
|
|
|
402
|
-
|
|
403
|
-
|
|
251
|
+
if (!listResponse.ok) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`Failed to list email addresses: HTTP ${listResponse.status}`,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
404
256
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
)
|
|
409
|
-
.action(async (_opts: unknown, cmd: Command) => {
|
|
410
|
-
await run(cmd, async () => {
|
|
411
|
-
const inboxes = await svc.listInboxes();
|
|
412
|
-
return { inboxes };
|
|
413
|
-
});
|
|
414
|
-
});
|
|
257
|
+
const listData = (await listResponse.json()) as {
|
|
258
|
+
results: { id: string; address: string }[];
|
|
259
|
+
};
|
|
415
260
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
261
|
+
const addresses = listData.results ?? [];
|
|
262
|
+
if (addresses.length === 0) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
"No email address registered for this assistant. Run: assistant email register <username>",
|
|
265
|
+
);
|
|
266
|
+
}
|
|
420
267
|
|
|
421
|
-
|
|
422
|
-
"after",
|
|
423
|
-
`
|
|
424
|
-
Drafts follow a lifecycle: create -> approve-send or reject.
|
|
268
|
+
const target = addresses[0];
|
|
425
269
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
270
|
+
// 2. Fetch status/usage for this address
|
|
271
|
+
const statusResponse = await client.fetch(
|
|
272
|
+
`/v1/assistants/${client.platformAssistantId}/email-addresses/${target.id}/status/`,
|
|
273
|
+
);
|
|
429
274
|
|
|
430
|
-
|
|
431
|
-
|
|
275
|
+
if (!statusResponse.ok) {
|
|
276
|
+
const body = (await statusResponse
|
|
277
|
+
.json()
|
|
278
|
+
.catch(() => ({}))) as Record<string, unknown>;
|
|
279
|
+
const detail = body.detail ?? `HTTP ${statusResponse.status}`;
|
|
280
|
+
throw new Error(String(detail));
|
|
281
|
+
}
|
|
432
282
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
283
|
+
const statusData = (await statusResponse.json()) as {
|
|
284
|
+
address: string;
|
|
285
|
+
status: string;
|
|
286
|
+
usage: {
|
|
287
|
+
sent_today: number;
|
|
288
|
+
daily_limit: number;
|
|
289
|
+
received_today: number;
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
if (shouldOutputJson(cmd)) {
|
|
294
|
+
writeOutput(cmd, statusData);
|
|
295
|
+
} else {
|
|
296
|
+
log.info(`Address: ${statusData.address}`);
|
|
297
|
+
log.info(`Status: ${statusData.status}`);
|
|
298
|
+
if (statusData.usage) {
|
|
299
|
+
log.info(
|
|
300
|
+
`Sent: ${statusData.usage.sent_today} / ${statusData.usage.daily_limit} (daily)`,
|
|
301
|
+
);
|
|
302
|
+
log.info(`Received today: ${statusData.usage.received_today}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (err) {
|
|
306
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
307
|
+
if (shouldOutputJson(cmd)) {
|
|
308
|
+
writeOutput(cmd, { error: message });
|
|
309
|
+
} else {
|
|
310
|
+
log.error(`Error: ${message}`);
|
|
311
|
+
}
|
|
312
|
+
process.exitCode = 1;
|
|
313
|
+
}
|
|
314
|
+
});
|
|
438
315
|
|
|
439
|
-
|
|
440
|
-
.command("
|
|
441
|
-
.description("
|
|
442
|
-
.
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
.option("
|
|
316
|
+
email
|
|
317
|
+
.command("list")
|
|
318
|
+
.description("List received and sent emails for this assistant")
|
|
319
|
+
.option(
|
|
320
|
+
"-d, --direction <direction>",
|
|
321
|
+
"Filter by direction: inbound, outbound, or all",
|
|
322
|
+
"all",
|
|
323
|
+
)
|
|
324
|
+
.option("-l, --limit <count>", "Maximum number of results", "20")
|
|
325
|
+
.option("--since <date>", "Only show messages since this date (ISO 8601)")
|
|
448
326
|
.addHelpText(
|
|
449
327
|
"after",
|
|
450
328
|
`
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
Required fields:
|
|
455
|
-
--from Sender address (must match a configured inbox) or inbox ID
|
|
456
|
-
--to Recipient email address
|
|
457
|
-
--subject Email subject line
|
|
458
|
-
--body Email body in plain text
|
|
459
|
-
|
|
460
|
-
Optional fields:
|
|
461
|
-
--cc CC recipient address
|
|
462
|
-
--in-reply-to Message ID of the email being replied to (for threading)
|
|
329
|
+
Lists email messages for this assistant. Shows subject, from, to,
|
|
330
|
+
direction, and timestamp for each message.
|
|
463
331
|
|
|
464
332
|
Examples:
|
|
465
|
-
$ assistant email
|
|
466
|
-
$ assistant email
|
|
467
|
-
$ assistant email
|
|
333
|
+
$ assistant email list
|
|
334
|
+
$ assistant email list --direction inbound --limit 5
|
|
335
|
+
$ assistant email list --since 2026-04-01 --json`,
|
|
468
336
|
)
|
|
469
337
|
.action(
|
|
470
338
|
async (
|
|
471
339
|
opts: {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
body: string;
|
|
476
|
-
cc?: string;
|
|
477
|
-
inReplyTo?: string;
|
|
340
|
+
direction?: string;
|
|
341
|
+
limit?: string;
|
|
342
|
+
since?: string;
|
|
478
343
|
},
|
|
479
344
|
cmd: Command,
|
|
480
345
|
) => {
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
346
|
+
try {
|
|
347
|
+
const client = await VellumPlatformClient.create();
|
|
348
|
+
if (!client) {
|
|
349
|
+
throw new Error(
|
|
350
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
if (!client.platformAssistantId) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const params = new URLSearchParams();
|
|
360
|
+
if (opts.direction && opts.direction !== "all") {
|
|
361
|
+
params.set("direction", opts.direction);
|
|
362
|
+
}
|
|
363
|
+
if (opts.limit) {
|
|
364
|
+
params.set("limit", opts.limit);
|
|
365
|
+
}
|
|
366
|
+
if (opts.since) {
|
|
367
|
+
params.set("since", opts.since);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const qs = params.toString();
|
|
371
|
+
const path = `/v1/assistants/${client.platformAssistantId}/emails/${qs ? `?${qs}` : ""}`;
|
|
372
|
+
const response = await client.fetch(path);
|
|
373
|
+
|
|
374
|
+
if (!response.ok) {
|
|
375
|
+
const body = (await response.json().catch(() => ({}))) as Record<
|
|
376
|
+
string,
|
|
377
|
+
unknown
|
|
378
|
+
>;
|
|
379
|
+
const detail = body.detail ?? `HTTP ${response.status}`;
|
|
380
|
+
throw new Error(String(detail));
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const data = (await response.json()) as {
|
|
384
|
+
results: {
|
|
385
|
+
id: string;
|
|
386
|
+
direction: string;
|
|
387
|
+
from_address: string;
|
|
388
|
+
to_addresses: string[];
|
|
389
|
+
subject: string;
|
|
390
|
+
created_at: string;
|
|
391
|
+
}[];
|
|
392
|
+
count: number;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
if (shouldOutputJson(cmd)) {
|
|
396
|
+
writeOutput(cmd, data);
|
|
397
|
+
} else {
|
|
398
|
+
const messages = data.results ?? [];
|
|
399
|
+
if (messages.length === 0) {
|
|
400
|
+
log.info("No email messages found.");
|
|
401
|
+
} else {
|
|
402
|
+
for (const msg of messages) {
|
|
403
|
+
const dir = msg.direction === "inbound" ? "←" : "→";
|
|
404
|
+
const to = Array.isArray(msg.to_addresses)
|
|
405
|
+
? msg.to_addresses.join(", ")
|
|
406
|
+
: "";
|
|
407
|
+
const date = new Date(msg.created_at).toLocaleString();
|
|
408
|
+
log.info(
|
|
409
|
+
`${dir} ${date} ${msg.from_address} → ${to} "${msg.subject || "(no subject)"}"`,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
log.info(`\n${data.count} total message(s)`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
} catch (err) {
|
|
416
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
417
|
+
if (shouldOutputJson(cmd)) {
|
|
418
|
+
writeOutput(cmd, { error: message });
|
|
419
|
+
} else {
|
|
420
|
+
log.error(`Error: ${message}`);
|
|
421
|
+
}
|
|
422
|
+
process.exitCode = 1;
|
|
423
|
+
}
|
|
485
424
|
},
|
|
486
425
|
);
|
|
487
426
|
|
|
488
|
-
|
|
489
|
-
.command("
|
|
490
|
-
.description("
|
|
427
|
+
email
|
|
428
|
+
.command("download <message-id>")
|
|
429
|
+
.description("Download a specific email message")
|
|
491
430
|
.option(
|
|
492
|
-
"--
|
|
493
|
-
"
|
|
494
|
-
|
|
495
|
-
.addHelpText(
|
|
496
|
-
"after",
|
|
497
|
-
`
|
|
498
|
-
Lists all email drafts, optionally filtered by status. Returns an array
|
|
499
|
-
of draft objects with their IDs, recipients, subjects, and current status.
|
|
500
|
-
|
|
501
|
-
Use --status to narrow results to a specific lifecycle stage:
|
|
502
|
-
pending — created but not yet approved
|
|
503
|
-
approved — approved and queued for sending
|
|
504
|
-
sent — successfully delivered
|
|
505
|
-
rejected — delivery failed by the provider (e.g. bounce or send error)
|
|
506
|
-
|
|
507
|
-
Note: drafts rejected via "draft reject" are permanently deleted and will
|
|
508
|
-
not appear here. The "rejected" status only applies to provider-side
|
|
509
|
-
delivery failures.
|
|
510
|
-
|
|
511
|
-
Examples:
|
|
512
|
-
$ assistant email draft list
|
|
513
|
-
$ assistant email draft list --status pending
|
|
514
|
-
$ assistant email draft list --status sent --json`,
|
|
431
|
+
"--format <type>",
|
|
432
|
+
"Output format: text, html, json (default: text)",
|
|
433
|
+
"text",
|
|
515
434
|
)
|
|
516
|
-
.
|
|
517
|
-
await run(cmd, async () => {
|
|
518
|
-
const drafts = await svc.listDrafts(opts.status);
|
|
519
|
-
return { drafts };
|
|
520
|
-
});
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
draft
|
|
524
|
-
.command("get <draftId>")
|
|
525
|
-
.description("Get a draft by ID")
|
|
526
|
-
.option("--inbox <id>", "Inbox ID (for multi-inbox setups)")
|
|
435
|
+
.option("-o, --output <path>", "Write to file instead of stdout")
|
|
527
436
|
.addHelpText(
|
|
528
437
|
"after",
|
|
529
438
|
`
|
|
530
439
|
Arguments:
|
|
531
|
-
|
|
440
|
+
message-id Email message ID (from \`assistant email list --json\`)
|
|
532
441
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
442
|
+
Downloads a specific email message by ID. The default format shows
|
|
443
|
+
headers and the plain-text body. Use --format html for the HTML body,
|
|
444
|
+
or --format json for the full message object.
|
|
536
445
|
|
|
537
446
|
Examples:
|
|
538
|
-
$ assistant email
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
await run(cmd, async () => {
|
|
544
|
-
const d = await svc.getDraft(draftId, opts.inbox);
|
|
545
|
-
return { draft: d };
|
|
546
|
-
});
|
|
547
|
-
});
|
|
447
|
+
$ assistant email download msg_abc123
|
|
448
|
+
From: user@example.com
|
|
449
|
+
To: mybot@vellum.me
|
|
450
|
+
Subject: Hello
|
|
451
|
+
Date: 2026-04-05 12:00:00
|
|
548
452
|
|
|
549
|
-
|
|
550
|
-
.command("approve-send")
|
|
551
|
-
.alias("send")
|
|
552
|
-
.alias("approve")
|
|
553
|
-
.description("Check guardrails and send a draft")
|
|
554
|
-
.requiredOption("--draft-id <id>", "Draft ID to send")
|
|
555
|
-
.option("--inbox <id>", "Inbox ID (for multi-inbox setups)")
|
|
556
|
-
.requiredOption("--confirm", "Explicit confirmation flag (required)")
|
|
557
|
-
.addHelpText(
|
|
558
|
-
"after",
|
|
559
|
-
`
|
|
560
|
-
Runs guardrail checks (outbound pause, daily send cap, address block/allow
|
|
561
|
-
rules) and sends the draft if all checks pass. The --confirm flag is required
|
|
562
|
-
as an explicit safety gate.
|
|
563
|
-
|
|
564
|
-
If a guardrail blocks the send, the command exits with code 2 and returns
|
|
565
|
-
the guardrail error details in the response JSON. The draft remains in
|
|
566
|
-
pending state and can be retried after adjusting guardrails.
|
|
453
|
+
Hi, this is a test message.
|
|
567
454
|
|
|
568
|
-
|
|
455
|
+
$ assistant email download msg_abc123 --format json
|
|
456
|
+
{"id":"msg_abc123","direction":"inbound",...}
|
|
569
457
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
$ assistant email draft approve-send --draft-id d_abc123 --confirm --json`,
|
|
458
|
+
$ assistant email download msg_abc123 -o email.txt
|
|
459
|
+
✓ Saved to email.txt`,
|
|
573
460
|
)
|
|
574
461
|
.action(
|
|
575
462
|
async (
|
|
576
|
-
|
|
463
|
+
messageId: string,
|
|
464
|
+
opts: {
|
|
465
|
+
format?: string;
|
|
466
|
+
output?: string;
|
|
467
|
+
},
|
|
577
468
|
cmd: Command,
|
|
578
469
|
) => {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
470
|
+
try {
|
|
471
|
+
const client = await VellumPlatformClient.create();
|
|
472
|
+
if (!client) {
|
|
473
|
+
throw new Error(
|
|
474
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
if (!client.platformAssistantId) {
|
|
478
|
+
throw new Error(
|
|
479
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const response = await client.fetch(
|
|
484
|
+
`/v1/assistants/${client.platformAssistantId}/emails/${messageId}/`,
|
|
485
|
+
);
|
|
593
486
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
487
|
+
if (!response.ok) {
|
|
488
|
+
const body = (await response.json().catch(() => ({}))) as Record<
|
|
489
|
+
string,
|
|
490
|
+
unknown
|
|
491
|
+
>;
|
|
492
|
+
const detail = body.detail ?? `HTTP ${response.status}`;
|
|
493
|
+
throw new Error(String(detail));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const msg = (await response.json()) as {
|
|
497
|
+
id: string;
|
|
498
|
+
direction: string;
|
|
499
|
+
from_address: string;
|
|
500
|
+
to_addresses: string[];
|
|
501
|
+
subject: string;
|
|
502
|
+
body_text: string;
|
|
503
|
+
body_html: string;
|
|
504
|
+
in_reply_to: string;
|
|
505
|
+
references: string[];
|
|
506
|
+
created_at: string;
|
|
507
|
+
};
|
|
606
508
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
509
|
+
const fmt = opts.format ?? "text";
|
|
510
|
+
|
|
511
|
+
let content: string;
|
|
512
|
+
if (fmt === "json" || shouldOutputJson(cmd)) {
|
|
513
|
+
content = JSON.stringify(msg, null, 2) + "\n";
|
|
514
|
+
} else if (fmt === "html") {
|
|
515
|
+
if (!msg.body_html) {
|
|
516
|
+
throw new Error("No HTML body available for this message.");
|
|
517
|
+
}
|
|
518
|
+
content = msg.body_html;
|
|
519
|
+
} else {
|
|
520
|
+
// text format: headers + body
|
|
521
|
+
const to = Array.isArray(msg.to_addresses)
|
|
522
|
+
? msg.to_addresses.join(", ")
|
|
523
|
+
: "";
|
|
524
|
+
const date = new Date(msg.created_at).toLocaleString();
|
|
525
|
+
const lines = [
|
|
526
|
+
`From: ${msg.from_address}`,
|
|
527
|
+
`To: ${to}`,
|
|
528
|
+
`Subject: ${msg.subject || "(no subject)"}`,
|
|
529
|
+
`Date: ${date}`,
|
|
530
|
+
];
|
|
531
|
+
if (msg.in_reply_to) {
|
|
532
|
+
lines.push(`In-Reply-To: ${msg.in_reply_to}`);
|
|
533
|
+
}
|
|
534
|
+
lines.push("", msg.body_text || "(no plain-text body)");
|
|
535
|
+
content = lines.join("\n") + "\n";
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (opts.output) {
|
|
539
|
+
writeFileSync(opts.output, content, "utf-8");
|
|
540
|
+
if (!shouldOutputJson(cmd)) {
|
|
541
|
+
log.info(`✓ Saved to ${opts.output}`);
|
|
542
|
+
} else {
|
|
543
|
+
writeOutput(cmd, { saved: opts.output, bytes: content.length });
|
|
544
|
+
}
|
|
545
|
+
} else {
|
|
546
|
+
process.stdout.write(content);
|
|
547
|
+
}
|
|
548
|
+
} catch (err) {
|
|
549
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
550
|
+
if (shouldOutputJson(cmd)) {
|
|
551
|
+
writeOutput(cmd, { error: message });
|
|
552
|
+
} else {
|
|
553
|
+
log.error(`Error: ${message}`);
|
|
554
|
+
}
|
|
555
|
+
process.exitCode = 1;
|
|
556
|
+
}
|
|
621
557
|
},
|
|
622
558
|
);
|
|
623
559
|
|
|
624
|
-
|
|
625
|
-
.command("
|
|
626
|
-
.description("
|
|
627
|
-
.option("--
|
|
560
|
+
email
|
|
561
|
+
.command("send <to>")
|
|
562
|
+
.description("Send an email from this assistant")
|
|
563
|
+
.option("-s, --subject <text>", "Subject line")
|
|
564
|
+
.option("-b, --body <text>", "Email body (plain text)")
|
|
565
|
+
.option("-f, --file <path>", "Read body from file")
|
|
566
|
+
.option("--html <path>", "HTML body file (optional)")
|
|
628
567
|
.addHelpText(
|
|
629
568
|
"after",
|
|
630
569
|
`
|
|
631
570
|
Arguments:
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
Permanently removes a draft from the system. Both "reject" and "delete"
|
|
635
|
-
result in permanent deletion; use "reject" when you want to log a reason
|
|
636
|
-
for not sending. Use --inbox to scope the deletion in multi-inbox setups.
|
|
637
|
-
|
|
638
|
-
Examples:
|
|
639
|
-
$ assistant email draft delete d_abc123
|
|
640
|
-
$ assistant email draft delete d_abc123 --inbox inbox_456`,
|
|
641
|
-
)
|
|
642
|
-
.action(async (draftId: string, opts: { inbox?: string }, cmd: Command) => {
|
|
643
|
-
await run(cmd, async () => {
|
|
644
|
-
await svc.deleteDraft(draftId, opts.inbox);
|
|
645
|
-
return { draftId, action: "deleted" };
|
|
646
|
-
});
|
|
647
|
-
});
|
|
571
|
+
to Recipient email address
|
|
648
572
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const inbound = email.command("inbound").description("View inbound messages");
|
|
653
|
-
|
|
654
|
-
inbound.addHelpText(
|
|
655
|
-
"after",
|
|
656
|
-
`
|
|
657
|
-
View messages received by your inboxes. Inbound messages are emails sent
|
|
658
|
-
to your configured inbox addresses by external senders.
|
|
573
|
+
Sends an email from the assistant's registered email address via the
|
|
574
|
+
Vellum runtime proxy. The "from" address is automatically resolved
|
|
575
|
+
from the assistant's registered email address.
|
|
659
576
|
|
|
660
|
-
|
|
661
|
-
retrieve the full content of a specific message.
|
|
577
|
+
Body source priority: --body flag > --file flag > stdin (if not a TTY).
|
|
662
578
|
|
|
663
579
|
Examples:
|
|
664
|
-
$ assistant email
|
|
665
|
-
|
|
666
|
-
$ assistant email inbound get msg_def456`,
|
|
667
|
-
);
|
|
580
|
+
$ assistant email send user@example.com -s "Hello" -b "Hi there"
|
|
581
|
+
✓ Sent to user@example.com (delivery_id: abc123)
|
|
668
582
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
.description("List inbound messages")
|
|
672
|
-
.option("--thread-id <id>", "Filter by thread ID")
|
|
673
|
-
.option("--inbox <id>", "Inbox ID (for multi-inbox setups)")
|
|
674
|
-
.addHelpText(
|
|
675
|
-
"after",
|
|
676
|
-
`
|
|
677
|
-
Lists inbound messages received by your inboxes. Optionally filter by
|
|
678
|
-
thread ID to see only messages belonging to a specific conversation, or
|
|
679
|
-
by inbox ID to scope to a particular inbox.
|
|
583
|
+
$ echo "Body text" | assistant email send user@example.com -s "Hello"
|
|
584
|
+
✓ Sent to user@example.com (delivery_id: def456)
|
|
680
585
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
$ assistant email inbound list --thread-id thr_abc123
|
|
684
|
-
$ assistant email inbound list --inbox inbox_456
|
|
685
|
-
$ assistant email inbound list --json`,
|
|
586
|
+
$ assistant email send user@example.com -s "Hello" -b "Hi" --json
|
|
587
|
+
{"delivery_id":"abc123","status":"accepted"}`,
|
|
686
588
|
)
|
|
687
589
|
.action(
|
|
688
|
-
async (
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
return { message };
|
|
716
|
-
});
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
// =========================================================================
|
|
720
|
-
// Thread subcommands
|
|
721
|
-
// =========================================================================
|
|
722
|
-
const thread = email.command("thread").description("View email threads");
|
|
723
|
-
|
|
724
|
-
thread.addHelpText(
|
|
725
|
-
"after",
|
|
726
|
-
`
|
|
727
|
-
Threads group related emails (original message and replies) into a single
|
|
728
|
-
conversation. Each thread has a unique ID and contains one or more messages.
|
|
729
|
-
|
|
730
|
-
Use "thread list" to browse all threads and "thread get" to retrieve
|
|
731
|
-
the full conversation history for a specific thread.
|
|
732
|
-
|
|
733
|
-
Examples:
|
|
734
|
-
$ assistant email thread list
|
|
735
|
-
$ assistant email thread get thr_abc123`,
|
|
736
|
-
);
|
|
737
|
-
|
|
738
|
-
thread
|
|
739
|
-
.command("list")
|
|
740
|
-
.description("List threads")
|
|
741
|
-
.addHelpText(
|
|
742
|
-
"after",
|
|
743
|
-
`
|
|
744
|
-
Lists all email threads. Each thread entry includes its ID, subject,
|
|
745
|
-
participant addresses, message count, and timestamps.
|
|
746
|
-
|
|
747
|
-
Examples:
|
|
748
|
-
$ assistant email thread list
|
|
749
|
-
$ assistant email thread list --json`,
|
|
750
|
-
)
|
|
751
|
-
.action(async (_opts: unknown, cmd: Command) => {
|
|
752
|
-
await run(cmd, async () => {
|
|
753
|
-
const threads = await svc.listThreads();
|
|
754
|
-
return { threads };
|
|
755
|
-
});
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
thread
|
|
759
|
-
.command("get <threadId>")
|
|
760
|
-
.description("Get a specific thread")
|
|
761
|
-
.addHelpText(
|
|
762
|
-
"after",
|
|
763
|
-
`
|
|
764
|
-
Arguments:
|
|
765
|
-
threadId The ID of the thread to retrieve (e.g. thr_abc123)
|
|
766
|
-
|
|
767
|
-
Returns the full thread including all messages (inbound and outbound),
|
|
768
|
-
participants, subject, and timestamps. Messages are ordered chronologically.
|
|
769
|
-
|
|
770
|
-
Examples:
|
|
771
|
-
$ assistant email thread get thr_abc123
|
|
772
|
-
$ assistant email thread get thr_abc123 --json`,
|
|
773
|
-
)
|
|
774
|
-
.action(async (threadId: string, _opts: unknown, cmd: Command) => {
|
|
775
|
-
await run(cmd, async () => {
|
|
776
|
-
const t = await svc.getThread(threadId);
|
|
777
|
-
return { thread: t };
|
|
778
|
-
});
|
|
779
|
-
});
|
|
780
|
-
|
|
781
|
-
// =========================================================================
|
|
782
|
-
// Guardrails subcommands
|
|
783
|
-
// =========================================================================
|
|
784
|
-
const guardrails = email
|
|
785
|
-
.command("guardrails")
|
|
786
|
-
.description("Manage email guardrails");
|
|
787
|
-
|
|
788
|
-
guardrails.addHelpText(
|
|
789
|
-
"after",
|
|
790
|
-
`
|
|
791
|
-
Guardrails are safety controls enforced at send time (during "draft
|
|
792
|
-
approve-send"). Three types of guardrails exist:
|
|
793
|
-
|
|
794
|
-
1. Outbound pause — when paused=true, all sends are blocked
|
|
795
|
-
2. Daily send cap — limits the total number of emails sent per day
|
|
796
|
-
3. Address rules — block or allow patterns (e.g. *@spam.com)
|
|
797
|
-
|
|
798
|
-
When a guardrail blocks a send, exit code 2 is returned with the specific
|
|
799
|
-
guardrail error in the response.
|
|
800
|
-
|
|
801
|
-
Examples:
|
|
802
|
-
$ assistant email guardrails get
|
|
803
|
-
$ assistant email guardrails set --paused true
|
|
804
|
-
$ assistant email guardrails set --daily-cap 100
|
|
805
|
-
$ assistant email guardrails block "*@spam.com"`,
|
|
806
|
-
);
|
|
807
|
-
|
|
808
|
-
guardrails
|
|
809
|
-
.command("get")
|
|
810
|
-
.description("Show current guardrail settings")
|
|
811
|
-
.addHelpText(
|
|
812
|
-
"after",
|
|
813
|
-
`
|
|
814
|
-
Returns the current guardrail configuration: outbound pause state,
|
|
815
|
-
daily send cap, today's send count, and a summary of address rules.
|
|
816
|
-
|
|
817
|
-
Use this to verify guardrail settings before sending emails.
|
|
590
|
+
async (
|
|
591
|
+
to: string,
|
|
592
|
+
opts: {
|
|
593
|
+
subject?: string;
|
|
594
|
+
body?: string;
|
|
595
|
+
file?: string;
|
|
596
|
+
html?: string;
|
|
597
|
+
},
|
|
598
|
+
cmd: Command,
|
|
599
|
+
) => {
|
|
600
|
+
try {
|
|
601
|
+
const client = await VellumPlatformClient.create();
|
|
602
|
+
if (!client) {
|
|
603
|
+
throw new Error(
|
|
604
|
+
"Platform credentials not configured. Run: assistant platform connect",
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
if (!client.platformAssistantId) {
|
|
608
|
+
throw new Error(
|
|
609
|
+
"Assistant ID not configured. Set PLATFORM_ASSISTANT_ID or run: assistant platform connect",
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// 1. Resolve the assistant's registered email address (the "from").
|
|
614
|
+
const listResponse = await client.fetch(
|
|
615
|
+
`/v1/assistants/${client.platformAssistantId}/email-addresses/`,
|
|
616
|
+
);
|
|
818
617
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
output({ ok: true, ...svc.getGuardrails() }, getJson(cmd));
|
|
825
|
-
});
|
|
618
|
+
if (!listResponse.ok) {
|
|
619
|
+
throw new Error(
|
|
620
|
+
`Failed to list email addresses: HTTP ${listResponse.status}`,
|
|
621
|
+
);
|
|
622
|
+
}
|
|
826
623
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
.option("--paused <value>", "Pause outbound (true/false)")
|
|
831
|
-
.option("--daily-cap <n>", "Daily send cap")
|
|
832
|
-
.addHelpText(
|
|
833
|
-
"after",
|
|
834
|
-
`
|
|
835
|
-
Updates one or both guardrail settings. Omitted flags leave the existing
|
|
836
|
-
value unchanged.
|
|
624
|
+
const listData = (await listResponse.json()) as {
|
|
625
|
+
results: { id: string; address: string }[];
|
|
626
|
+
};
|
|
837
627
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
628
|
+
const addresses = listData.results ?? [];
|
|
629
|
+
if (addresses.length === 0) {
|
|
630
|
+
throw new Error(
|
|
631
|
+
"No email address registered for this assistant. Run: assistant email register <username>",
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const fromAddress = addresses[0].address;
|
|
636
|
+
|
|
637
|
+
// 2. Resolve body text: --body > --file > stdin
|
|
638
|
+
let text = opts.body;
|
|
639
|
+
if (!text && opts.file) {
|
|
640
|
+
text = readFileSync(opts.file, "utf-8");
|
|
641
|
+
}
|
|
642
|
+
if (!text && !process.stdin.isTTY) {
|
|
643
|
+
text = readFileSync("/dev/stdin", "utf-8");
|
|
644
|
+
}
|
|
645
|
+
if (!text) {
|
|
646
|
+
throw new Error(
|
|
647
|
+
"Email body is required. Use --body, --file, or pipe via stdin.",
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// 3. Resolve optional HTML body from file
|
|
652
|
+
let html: string | undefined;
|
|
653
|
+
if (opts.html) {
|
|
654
|
+
html = readFileSync(opts.html, "utf-8");
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// 4. Build payload
|
|
658
|
+
const payload: Record<string, string> = {
|
|
659
|
+
to,
|
|
660
|
+
from_address: fromAddress,
|
|
661
|
+
text,
|
|
662
|
+
};
|
|
663
|
+
if (opts.subject) payload.subject = opts.subject;
|
|
664
|
+
if (html) payload.html = html;
|
|
665
|
+
|
|
666
|
+
// 5. Send via runtime proxy
|
|
667
|
+
const response = await client.fetch("/v1/runtime-proxy/email/send/", {
|
|
668
|
+
method: "POST",
|
|
669
|
+
headers: { "Content-Type": "application/json" },
|
|
670
|
+
body: JSON.stringify(payload),
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
if (!response.ok) {
|
|
674
|
+
const body = (await response.json().catch(() => ({}))) as Record<
|
|
675
|
+
string,
|
|
676
|
+
unknown
|
|
677
|
+
>;
|
|
678
|
+
const detail = body.detail ?? `HTTP ${response.status}`;
|
|
679
|
+
throw new Error(String(detail));
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const data = (await response.json()) as {
|
|
683
|
+
delivery_id: string;
|
|
684
|
+
status: string;
|
|
685
|
+
};
|
|
842
686
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
exitError("daily-cap must be a non-negative integer");
|
|
857
|
-
return;
|
|
687
|
+
if (shouldOutputJson(cmd)) {
|
|
688
|
+
writeOutput(cmd, data);
|
|
689
|
+
} else {
|
|
690
|
+
log.info(`✓ Sent to ${to} (delivery_id: ${data.delivery_id})`);
|
|
691
|
+
}
|
|
692
|
+
} catch (err) {
|
|
693
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
694
|
+
if (shouldOutputJson(cmd)) {
|
|
695
|
+
writeOutput(cmd, { error: message });
|
|
696
|
+
} else {
|
|
697
|
+
log.error(`Error: ${message}`);
|
|
698
|
+
}
|
|
699
|
+
process.exitCode = 1;
|
|
858
700
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const result = svc.setGuardrails(updates);
|
|
862
|
-
output({ ok: true, ...result }, getJson(cmd));
|
|
863
|
-
});
|
|
864
|
-
|
|
865
|
-
guardrails
|
|
866
|
-
.command("block <pattern>")
|
|
867
|
-
.description("Block addresses matching pattern (e.g., *@spam.com)")
|
|
868
|
-
.addHelpText(
|
|
869
|
-
"after",
|
|
870
|
-
`
|
|
871
|
-
Arguments:
|
|
872
|
-
pattern Glob pattern matching email addresses to block. Supports * as
|
|
873
|
-
wildcard. Examples: "*@spam.com", "user@*", "*@*.example.com"
|
|
874
|
-
|
|
875
|
-
Creates a block rule. Any "approve-send" to a recipient matching this
|
|
876
|
-
pattern will be rejected with exit code 2. Rules are evaluated in order;
|
|
877
|
-
block rules take precedence over allow rules for the same address.
|
|
878
|
-
|
|
879
|
-
Examples:
|
|
880
|
-
$ assistant email guardrails block "*@spam.com"
|
|
881
|
-
$ assistant email guardrails block "marketing@*"`,
|
|
882
|
-
)
|
|
883
|
-
.action((pattern: string, _opts: unknown, cmd: Command) => {
|
|
884
|
-
const rule = svc.addRule("block", pattern);
|
|
885
|
-
output({ ok: true, rule }, getJson(cmd));
|
|
886
|
-
});
|
|
887
|
-
|
|
888
|
-
guardrails
|
|
889
|
-
.command("allow <pattern>")
|
|
890
|
-
.description("Allow addresses matching pattern")
|
|
891
|
-
.addHelpText(
|
|
892
|
-
"after",
|
|
893
|
-
`
|
|
894
|
-
Arguments:
|
|
895
|
-
pattern Glob pattern matching email addresses to allow. Supports * as
|
|
896
|
-
wildcard. Examples: "*@partner.com", "vip@*", "*@*.trusted.com"
|
|
897
|
-
|
|
898
|
-
Creates an allow rule. Addresses matching this pattern will pass the
|
|
899
|
-
address-rule guardrail check during "approve-send". Note that block rules
|
|
900
|
-
take precedence over allow rules for the same address.
|
|
901
|
-
|
|
902
|
-
Examples:
|
|
903
|
-
$ assistant email guardrails allow "*@partner.com"
|
|
904
|
-
$ assistant email guardrails allow "vip@example.com"`,
|
|
905
|
-
)
|
|
906
|
-
.action((pattern: string, _opts: unknown, cmd: Command) => {
|
|
907
|
-
const rule = svc.addRule("allow", pattern);
|
|
908
|
-
output({ ok: true, rule }, getJson(cmd));
|
|
909
|
-
});
|
|
910
|
-
|
|
911
|
-
guardrails
|
|
912
|
-
.command("rules")
|
|
913
|
-
.description("List all address rules")
|
|
914
|
-
.addHelpText(
|
|
915
|
-
"after",
|
|
916
|
-
`
|
|
917
|
-
Lists all configured address rules (both block and allow). Each rule
|
|
918
|
-
entry includes its ID, type (block or allow), and the glob pattern.
|
|
919
|
-
|
|
920
|
-
Use the rule ID with "guardrails unrule" to remove a specific rule.
|
|
921
|
-
|
|
922
|
-
Examples:
|
|
923
|
-
$ assistant email guardrails rules
|
|
924
|
-
$ assistant email guardrails rules --json`,
|
|
925
|
-
)
|
|
926
|
-
.action((_opts: unknown, cmd: Command) => {
|
|
927
|
-
output({ ok: true, rules: svc.listAddressRules() }, getJson(cmd));
|
|
928
|
-
});
|
|
929
|
-
|
|
930
|
-
guardrails
|
|
931
|
-
.command("unrule <ruleId>")
|
|
932
|
-
.description("Remove an address rule by ID")
|
|
933
|
-
.addHelpText(
|
|
934
|
-
"after",
|
|
935
|
-
`
|
|
936
|
-
Arguments:
|
|
937
|
-
ruleId The ID of the address rule to remove. Use "guardrails rules" to
|
|
938
|
-
list all rules and their IDs.
|
|
939
|
-
|
|
940
|
-
Permanently removes a block or allow rule. The rule takes effect immediately —
|
|
941
|
-
subsequent "approve-send" calls will no longer be affected by the removed rule.
|
|
942
|
-
|
|
943
|
-
Examples:
|
|
944
|
-
$ assistant email guardrails unrule rule_abc123
|
|
945
|
-
$ assistant email guardrails rules # list rules to find the ID first`,
|
|
946
|
-
)
|
|
947
|
-
.action((ruleId: string, _opts: unknown, cmd: Command) => {
|
|
948
|
-
if (svc.removeRule(ruleId)) {
|
|
949
|
-
output({ ok: true, ruleId, action: "removed" }, getJson(cmd));
|
|
950
|
-
} else {
|
|
951
|
-
exitError(`No rule found matching "${ruleId}"`);
|
|
952
|
-
}
|
|
953
|
-
});
|
|
701
|
+
},
|
|
702
|
+
);
|
|
954
703
|
}
|