@vellumai/assistant 0.4.46 → 0.4.49
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 +7 -7
- package/README.md +2 -23
- package/docs/architecture/integrations.md +45 -41
- package/docs/architecture/keychain-broker.md +3 -3
- package/docs/architecture/security.md +5 -5
- package/docs/runbook-trusted-contacts.md +3 -8
- package/hook-templates/debug-prompt-logger/hook.json +1 -1
- package/hook-templates/debug-prompt-logger/run.sh +1 -3
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +0 -1
- package/src/__tests__/anthropic-provider.test.ts +156 -0
- package/src/__tests__/approval-cascade.test.ts +810 -0
- package/src/__tests__/approval-primitive.test.ts +0 -1
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +12 -34
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
- package/src/__tests__/browser-fill-credential.test.ts +5 -2
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
- package/src/__tests__/channel-guardian.test.ts +0 -2
- package/src/__tests__/channel-readiness-routes.test.ts +35 -25
- package/src/__tests__/channel-readiness-service.test.ts +10 -9
- package/src/__tests__/checker.test.ts +9 -29
- package/src/__tests__/cli.test.ts +23 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-tools.test.ts +2 -19
- package/src/__tests__/config-watcher.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-image-dimensions.test.ts +332 -0
- package/src/__tests__/context-token-estimator.test.ts +196 -13
- package/src/__tests__/conversation-attention-store.test.ts +0 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +144 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
- package/src/__tests__/credential-broker-server-use.test.ts +22 -21
- package/src/__tests__/credential-broker.test.ts +2 -1
- package/src/__tests__/credential-metadata-store.test.ts +239 -26
- package/src/__tests__/credential-resolve.test.ts +5 -4
- package/src/__tests__/credential-security-e2e.test.ts +8 -8
- package/src/__tests__/credential-security-invariants.test.ts +111 -7
- package/src/__tests__/credential-vault-unit.test.ts +287 -54
- package/src/__tests__/credential-vault.test.ts +406 -12
- package/src/__tests__/credentials-cli.test.ts +82 -6
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-image-service.test.ts +75 -45
- package/src/__tests__/gemini-provider.test.ts +9 -6
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
- package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
- package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
- package/src/__tests__/guardian-grant-minting.test.ts +35 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
- package/src/__tests__/heartbeat-service.test.ts +0 -1
- package/src/__tests__/host-cu-proxy.test.ts +629 -0
- package/src/__tests__/host-shell-tool.test.ts +27 -15
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/ingress-url-consistency.test.ts +14 -21
- package/src/__tests__/integration-status.test.ts +38 -25
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +10 -9
- package/src/__tests__/keychain-broker-client.test.ts +11 -43
- package/src/__tests__/managed-proxy-context.test.ts +5 -3
- package/src/__tests__/media-generate-image.test.ts +63 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
- package/src/__tests__/messaging-send-tool.test.ts +4 -6
- package/src/__tests__/notification-routing-intent.test.ts +0 -1
- package/src/__tests__/oauth-cli.test.ts +373 -14
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/oauth-store.test.ts +756 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +0 -1
- package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
- package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +15 -21
- package/src/__tests__/recording-handler.test.ts +3 -4
- package/src/__tests__/registry.test.ts +2 -2
- package/src/__tests__/runtime-events-sse.test.ts +55 -7
- package/src/__tests__/schedule-store.test.ts +0 -1
- package/src/__tests__/scheduler-recurrence.test.ts +0 -1
- package/src/__tests__/schema-transforms.test.ts +226 -0
- package/src/__tests__/scoped-approval-grants.test.ts +0 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
- package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
- package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
- package/src/__tests__/secret-ingress-handler.test.ts +0 -1
- package/src/__tests__/secret-onetime-send.test.ts +5 -3
- package/src/__tests__/send-endpoint-busy.test.ts +21 -6
- package/src/__tests__/sequence-store.test.ts +0 -1
- package/src/__tests__/session-init.benchmark.test.ts +4 -5
- package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
- package/src/__tests__/skill-include-graph.test.ts +66 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-load-tool.test.ts +149 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skills-uninstall.test.ts +3 -3
- package/src/__tests__/skills.test.ts +3 -12
- package/src/__tests__/slack-channel-config.test.ts +76 -11
- package/src/__tests__/slack-share-routes.test.ts +17 -14
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
- package/src/__tests__/terminal-tools.test.ts +4 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
- package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
- package/src/__tests__/trust-store.test.ts +1 -22
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +2 -1
- package/src/__tests__/twilio-provider.test.ts +4 -2
- package/src/__tests__/twilio-routes.test.ts +5 -20
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/agent/ax-tree-compaction.test.ts +235 -0
- package/src/agent/loop.ts +76 -130
- package/src/calls/call-domain.ts +8 -10
- package/src/calls/relay-server.ts +9 -13
- package/src/calls/twilio-config.ts +4 -8
- package/src/calls/twilio-provider.ts +2 -1
- package/src/calls/twilio-rest.ts +2 -1
- package/src/calls/twilio-routes.ts +1 -2
- package/src/calls/voice-ingress-preflight.ts +1 -1
- package/src/cli/commands/browser-relay.ts +46 -15
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/credentials.ts +110 -23
- package/src/cli/commands/oauth/apps.ts +255 -0
- package/src/cli/commands/oauth/connections.ts +299 -0
- package/src/cli/commands/oauth/index.ts +52 -0
- package/src/cli/commands/oauth/providers.ts +242 -0
- package/src/cli/commands/skills.ts +4 -338
- package/src/cli/program.ts +1 -5
- package/src/cli/reference.ts +1 -3
- package/src/cli.ts +3 -2
- package/src/config/assistant-feature-flags.ts +0 -3
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
- package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +22 -4
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
- package/src/config/bundled-skills/gmail/SKILL.md +4 -4
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
- package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +90 -44
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
- package/src/config/bundled-skills/messaging/SKILL.md +6 -6
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +12 -15
- package/src/config/bundled-skills/settings/SKILL.md +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +2 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
- package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
- package/src/config/env-registry.ts +14 -83
- package/src/config/env.ts +11 -50
- package/src/config/feature-flag-registry.json +16 -16
- package/src/config/schema.ts +3 -1
- package/src/config/skills.ts +21 -2
- package/src/context/image-dimensions.ts +229 -0
- package/src/context/token-estimator.ts +75 -12
- package/src/context/window-manager.ts +49 -10
- package/src/daemon/assistant-attachments.ts +1 -13
- package/src/daemon/guardian-action-generators.ts +4 -5
- package/src/daemon/handlers/config-ingress.ts +8 -33
- package/src/daemon/handlers/config-slack-channel.ts +76 -56
- package/src/daemon/handlers/config-telegram.ts +53 -24
- package/src/daemon/handlers/sessions.ts +10 -24
- package/src/daemon/handlers/shared.ts +0 -130
- package/src/daemon/host-cu-proxy.ts +401 -0
- package/src/daemon/lifecycle.ts +39 -63
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/computer-use.ts +2 -119
- package/src/daemon/message-types/host-cu.ts +19 -0
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/message-types/messages.ts +3 -0
- package/src/daemon/server.ts +14 -21
- package/src/daemon/session-agent-loop-handlers.ts +2 -0
- package/src/daemon/session-attachments.ts +1 -2
- package/src/daemon/session-messaging.ts +3 -1
- package/src/daemon/session-slash.ts +1 -1
- package/src/daemon/session-surfaces.ts +40 -28
- package/src/daemon/session-tool-setup.ts +20 -11
- package/src/daemon/session.ts +139 -16
- package/src/daemon/tool-side-effects.ts +2 -8
- package/src/daemon/watch-handler.ts +2 -2
- package/src/email/providers/index.ts +2 -1
- package/src/events/tool-metrics-listener.ts +2 -2
- package/src/hooks/manager.ts +1 -4
- package/src/inbound/public-ingress-urls.ts +7 -7
- package/src/instrument.ts +15 -1
- package/src/logfire.ts +16 -5
- package/src/media/app-icon-generator.ts +30 -4
- package/src/media/avatar-router.ts +26 -3
- package/src/media/gemini-image-service.ts +28 -2
- package/src/memory/conversation-key-store.ts +21 -0
- package/src/memory/db-init.ts +4 -0
- package/src/memory/guardian-action-store.ts +1 -1
- package/src/memory/migrations/149-oauth-tables.ts +60 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema/guardian.ts +1 -1
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/oauth.ts +65 -0
- package/src/messaging/provider.ts +19 -13
- package/src/messaging/providers/gmail/adapter.ts +40 -23
- package/src/messaging/providers/gmail/client.ts +283 -122
- package/src/messaging/providers/gmail/people-client.ts +32 -24
- package/src/messaging/providers/slack/adapter.ts +29 -19
- package/src/messaging/providers/slack/client.ts +265 -78
- package/src/messaging/providers/telegram-bot/adapter.ts +19 -18
- package/src/messaging/providers/whatsapp/adapter.ts +17 -11
- package/src/messaging/registry.ts +2 -31
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/signal.ts +4 -5
- package/src/oauth/byo-connection.test.ts +537 -0
- package/src/oauth/byo-connection.ts +128 -0
- package/src/oauth/connect-orchestrator.ts +139 -56
- package/src/oauth/connect-types.ts +17 -23
- package/src/oauth/connection-resolver.ts +58 -0
- package/src/oauth/connection.ts +38 -0
- package/src/oauth/manual-token-connection.ts +104 -0
- package/src/oauth/oauth-store.ts +496 -0
- package/src/oauth/platform-connection.test.ts +192 -0
- package/src/oauth/platform-connection.ts +111 -0
- package/src/oauth/provider-behaviors.ts +124 -0
- package/src/oauth/scope-policy.ts +9 -2
- package/src/oauth/seed-providers.ts +161 -0
- package/src/oauth/token-persistence.ts +74 -78
- package/src/permissions/checker.ts +8 -4
- package/src/permissions/defaults.ts +0 -1
- package/src/permissions/prompter.ts +10 -1
- package/src/permissions/trust-store.ts +13 -0
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
- package/src/prompts/system-prompt.ts +70 -45
- package/src/providers/anthropic/client.ts +133 -24
- package/src/providers/gemini/client.ts +15 -6
- package/src/providers/managed-proxy/constants.ts +2 -2
- package/src/providers/managed-proxy/context.ts +5 -1
- package/src/providers/ratelimit.ts +17 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -27
- package/src/runtime/AGENTS.md +17 -0
- package/src/runtime/auth/route-policy.ts +0 -3
- package/src/runtime/channel-invite-transports/telegram.ts +2 -1
- package/src/runtime/channel-readiness-service.ts +168 -195
- package/src/runtime/channel-readiness-types.ts +4 -0
- package/src/runtime/channel-reply-delivery.ts +0 -40
- package/src/runtime/gateway-client.ts +0 -7
- package/src/runtime/guardian-action-conversation-turn.ts +1 -3
- package/src/runtime/guardian-action-followup-executor.ts +1 -1
- package/src/runtime/guardian-action-message-composer.ts +3 -23
- package/src/runtime/http-server.ts +17 -10
- package/src/runtime/http-types.ts +2 -3
- package/src/runtime/middleware/rate-limiter.ts +74 -20
- package/src/runtime/middleware/twilio-validation.ts +1 -11
- package/src/runtime/pending-interactions.ts +14 -12
- package/src/runtime/routes/channel-delivery-routes.ts +0 -1
- package/src/runtime/routes/channel-readiness-routes.ts +2 -0
- package/src/runtime/routes/conversation-routes.ts +73 -19
- package/src/runtime/routes/diagnostics-routes.ts +11 -9
- package/src/runtime/routes/events-routes.ts +21 -11
- package/src/runtime/routes/guardian-approval-interception.ts +20 -5
- package/src/runtime/routes/host-cu-routes.ts +97 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
- package/src/runtime/routes/integrations/slack/share.ts +6 -6
- package/src/runtime/routes/integrations/twilio.ts +6 -5
- package/src/runtime/routes/log-export-routes.ts +126 -8
- package/src/runtime/routes/secret-routes.ts +3 -2
- package/src/runtime/routes/settings-routes.ts +113 -48
- package/src/runtime/routes/surface-action-routes.ts +1 -1
- package/src/runtime/routes/watch-routes.ts +128 -0
- package/src/schedule/integration-status.ts +10 -8
- package/src/security/credential-key.ts +14 -0
- package/src/security/keychain-broker-client.ts +5 -6
- package/src/security/oauth2.ts +1 -1
- package/src/security/token-manager.ts +145 -43
- package/src/skills/catalog-install.ts +358 -0
- package/src/skills/include-graph.ts +32 -0
- package/src/telegram/bot-username.ts +2 -3
- package/src/tools/apps/definitions.ts +0 -5
- package/src/tools/assets/materialize.ts +0 -5
- package/src/tools/assets/search.ts +0 -5
- package/src/tools/browser/headless-browser.ts +1 -67
- package/src/tools/browser/network-recorder.ts +1 -1
- package/src/tools/browser/network-recording-types.ts +1 -1
- package/src/tools/claude-code/claude-code.ts +0 -5
- package/src/tools/computer-use/definitions.ts +46 -11
- package/src/tools/computer-use/registry.ts +4 -5
- package/src/tools/credentials/broker.ts +5 -4
- package/src/tools/credentials/metadata-store.ts +22 -74
- package/src/tools/credentials/resolve.ts +2 -1
- package/src/tools/credentials/vault.ts +139 -151
- package/src/tools/filesystem/edit.ts +1 -6
- package/src/tools/filesystem/read.ts +0 -5
- package/src/tools/filesystem/write.ts +1 -6
- package/src/tools/host-filesystem/edit.ts +1 -6
- package/src/tools/host-filesystem/read.ts +1 -6
- package/src/tools/host-filesystem/write.ts +1 -6
- package/src/tools/mcp/mcp-tool-factory.ts +18 -1
- package/src/tools/memory/definitions.ts +0 -5
- package/src/tools/network/web-fetch.ts +0 -5
- package/src/tools/network/web-search.ts +0 -5
- package/src/tools/registry.ts +2 -7
- package/src/tools/schema-transforms.ts +99 -0
- package/src/tools/skills/load.ts +62 -8
- package/src/tools/swarm/delegate.ts +0 -5
- package/src/tools/system/avatar-generator.ts +0 -5
- package/src/tools/ui-surface/definitions.ts +0 -15
- package/src/tools/watch/screen-watch.ts +0 -5
- package/src/tools/watch/watch-state.ts +0 -12
- package/src/util/logger.ts +7 -41
- package/src/util/platform.ts +9 -28
- package/src/version.ts +10 -0
- package/src/watcher/providers/github.ts +51 -52
- package/src/watcher/providers/gmail.ts +88 -80
- package/src/watcher/providers/google-calendar.ts +94 -86
- package/src/watcher/providers/linear.ts +87 -93
- package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
- package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
- package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
- package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
- package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
- package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
- package/src/cli/commands/dev.ts +0 -129
- package/src/cli/commands/map.ts +0 -391
- package/src/cli/commands/oauth.ts +0 -77
- package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +0 -16
- package/src/daemon/computer-use-session.ts +0 -1020
- package/src/daemon/ride-shotgun-handler.ts +0 -567
- package/src/oauth/provider-profiles.ts +0 -192
- package/src/prompts/computer-use-prompt.ts +0 -98
- package/src/runtime/routes/computer-use-routes.ts +0 -641
- package/src/runtime/telegram-streaming-delivery.test.ts +0 -597
- package/src/runtime/telegram-streaming-delivery.ts +0 -383
- package/src/tools/computer-use/request-computer-control.ts +0 -61
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
listMessages,
|
|
6
6
|
modifyMessage,
|
|
7
7
|
} from "../../../../messaging/providers/gmail/client.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import type { OAuthConnection } from "../../../../oauth/connection.js";
|
|
9
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
10
10
|
import type {
|
|
11
11
|
ToolContext,
|
|
12
12
|
ToolExecutionResult,
|
|
@@ -15,12 +15,14 @@ import { err, ok } from "./shared.js";
|
|
|
15
15
|
|
|
16
16
|
const FOLLOW_UP_LABEL_NAME = "Follow-up";
|
|
17
17
|
|
|
18
|
-
async function getOrCreateFollowUpLabel(
|
|
19
|
-
|
|
18
|
+
async function getOrCreateFollowUpLabel(
|
|
19
|
+
connection: OAuthConnection,
|
|
20
|
+
): Promise<string> {
|
|
21
|
+
const labels = await listLabels(connection);
|
|
20
22
|
const existing = labels.find((l) => l.name === FOLLOW_UP_LABEL_NAME);
|
|
21
23
|
if (existing) return existing.id;
|
|
22
24
|
|
|
23
|
-
const created = await createLabel(
|
|
25
|
+
const created = await createLabel(connection, FOLLOW_UP_LABEL_NAME);
|
|
24
26
|
return created.id;
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -35,67 +37,68 @@ export async function run(
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
try {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (!messageId)
|
|
44
|
-
return err("message_id is required for track action.");
|
|
40
|
+
const connection = resolveOAuthConnection("integration:gmail");
|
|
41
|
+
switch (action) {
|
|
42
|
+
case "track": {
|
|
43
|
+
const messageId = input.message_id as string;
|
|
44
|
+
if (!messageId) return err("message_id is required for track action.");
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
case "list": {
|
|
52
|
-
const labelId = await getOrCreateFollowUpLabel(token);
|
|
53
|
-
const listResp = await listMessages(token, undefined, 50, undefined, [
|
|
54
|
-
labelId,
|
|
55
|
-
]);
|
|
56
|
-
const messageIds = (listResp.messages ?? []).map((m) => m.id);
|
|
57
|
-
|
|
58
|
-
if (messageIds.length === 0) {
|
|
59
|
-
return ok("No messages are currently tracked for follow-up.");
|
|
60
|
-
}
|
|
46
|
+
const labelId = await getOrCreateFollowUpLabel(connection);
|
|
47
|
+
await modifyMessage(connection, messageId, { addLabelIds: [labelId] });
|
|
48
|
+
return ok("Message marked for follow-up.");
|
|
49
|
+
}
|
|
61
50
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const subject =
|
|
73
|
-
headers.find((h) => h.name.toLowerCase() === "subject")?.value ??
|
|
74
|
-
"";
|
|
75
|
-
const date =
|
|
76
|
-
headers.find((h) => h.name.toLowerCase() === "date")?.value ?? "";
|
|
77
|
-
return { id: m.id, threadId: m.threadId, from, subject, date };
|
|
78
|
-
});
|
|
51
|
+
case "list": {
|
|
52
|
+
const labelId = await getOrCreateFollowUpLabel(connection);
|
|
53
|
+
const listResp = await listMessages(
|
|
54
|
+
connection,
|
|
55
|
+
undefined,
|
|
56
|
+
50,
|
|
57
|
+
undefined,
|
|
58
|
+
[labelId],
|
|
59
|
+
);
|
|
60
|
+
const messageIds = (listResp.messages ?? []).map((m) => m.id);
|
|
79
61
|
|
|
80
|
-
|
|
62
|
+
if (messageIds.length === 0) {
|
|
63
|
+
return ok("No messages are currently tracked for follow-up.");
|
|
81
64
|
}
|
|
82
65
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
66
|
+
const messages = await batchGetMessages(
|
|
67
|
+
connection,
|
|
68
|
+
messageIds,
|
|
69
|
+
"metadata",
|
|
70
|
+
["From", "Subject", "Date"],
|
|
71
|
+
);
|
|
72
|
+
const items = messages.map((m) => {
|
|
73
|
+
const headers = m.payload?.headers ?? [];
|
|
74
|
+
const from =
|
|
75
|
+
headers.find((h) => h.name.toLowerCase() === "from")?.value ?? "";
|
|
76
|
+
const subject =
|
|
77
|
+
headers.find((h) => h.name.toLowerCase() === "subject")?.value ??
|
|
78
|
+
"";
|
|
79
|
+
const date =
|
|
80
|
+
headers.find((h) => h.name.toLowerCase() === "date")?.value ?? "";
|
|
81
|
+
return { id: m.id, threadId: m.threadId, from, subject, date };
|
|
82
|
+
});
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return ok("Follow-up tracking removed from message.");
|
|
91
|
-
}
|
|
84
|
+
return ok(JSON.stringify(items, null, 2));
|
|
85
|
+
}
|
|
92
86
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
);
|
|
87
|
+
case "untrack": {
|
|
88
|
+
const messageId = input.message_id as string;
|
|
89
|
+
if (!messageId)
|
|
90
|
+
return err("message_id is required for untrack action.");
|
|
91
|
+
|
|
92
|
+
const labelId = await getOrCreateFollowUpLabel(connection);
|
|
93
|
+
await modifyMessage(connection, messageId, {
|
|
94
|
+
removeLabelIds: [labelId],
|
|
95
|
+
});
|
|
96
|
+
return ok("Follow-up tracking removed from message.");
|
|
97
97
|
}
|
|
98
|
-
|
|
98
|
+
|
|
99
|
+
default:
|
|
100
|
+
return err(`Unknown action "${action}". Use track, list, or untrack.`);
|
|
101
|
+
}
|
|
99
102
|
} catch (e) {
|
|
100
103
|
return err(e instanceof Error ? e.message : String(e));
|
|
101
104
|
}
|
|
@@ -5,8 +5,7 @@ import {
|
|
|
5
5
|
} from "../../../../messaging/providers/gmail/client.js";
|
|
6
6
|
import { buildMultipartMime } from "../../../../messaging/providers/gmail/mime-builder.js";
|
|
7
7
|
import type { GmailMessagePart } from "../../../../messaging/providers/gmail/types.js";
|
|
8
|
-
import {
|
|
9
|
-
import { withValidToken } from "../../../../security/token-manager.js";
|
|
8
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
10
9
|
import type {
|
|
11
10
|
ToolContext,
|
|
12
11
|
ToolExecutionResult,
|
|
@@ -76,66 +75,68 @@ export async function run(
|
|
|
76
75
|
if (!forwardTo) return err("to is required.");
|
|
77
76
|
|
|
78
77
|
try {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const originalBody = extractPlainTextBody(message.payload);
|
|
78
|
+
const connection = resolveOAuthConnection("integration:gmail");
|
|
79
|
+
const message = await getMessage(connection, messageId, "full");
|
|
80
|
+
const headers = message.payload?.headers ?? [];
|
|
81
|
+
const originalFrom = extractHeader(headers, "From");
|
|
82
|
+
const originalDate = extractHeader(headers, "Date");
|
|
83
|
+
const originalSubject = extractHeader(headers, "Subject");
|
|
84
|
+
const originalBody = extractPlainTextBody(message.payload);
|
|
87
85
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
const forwardHeader = [
|
|
87
|
+
additionalText ? `${additionalText}\n\n` : "",
|
|
88
|
+
"---------- Forwarded message ----------",
|
|
89
|
+
`From: ${originalFrom}`,
|
|
90
|
+
`Date: ${originalDate}`,
|
|
91
|
+
`Subject: ${originalSubject}`,
|
|
92
|
+
"",
|
|
93
|
+
originalBody,
|
|
94
|
+
].join("\n");
|
|
97
95
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
const subject = originalSubject.startsWith("Fwd:")
|
|
97
|
+
? originalSubject
|
|
98
|
+
: `Fwd: ${originalSubject}`;
|
|
101
99
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (attachments.length > 0) {
|
|
116
|
-
const raw = buildMultipartMime({
|
|
117
|
-
to: forwardTo,
|
|
118
|
-
subject,
|
|
119
|
-
body: forwardHeader,
|
|
120
|
-
attachments,
|
|
121
|
-
});
|
|
122
|
-
const draft = await createDraftRaw(token, raw);
|
|
123
|
-
return ok(
|
|
124
|
-
`Forward draft created to ${forwardTo} with ${attachments.length} attachment(s) (Draft ID: ${draft.id}). Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
100
|
+
// Collect and download attachments from the original message
|
|
101
|
+
const attachmentRefs = collectAttachmentRefs(message.payload?.parts);
|
|
102
|
+
const attachments = await Promise.all(
|
|
103
|
+
attachmentRefs.map(async (ref) => {
|
|
104
|
+
const att = await getAttachment(
|
|
105
|
+
connection,
|
|
106
|
+
messageId,
|
|
107
|
+
ref.attachmentId,
|
|
108
|
+
);
|
|
109
|
+
const data = Buffer.from(
|
|
110
|
+
att.data.replace(/-/g, "+").replace(/_/g, "/"),
|
|
111
|
+
"base64",
|
|
125
112
|
);
|
|
126
|
-
|
|
113
|
+
return { filename: ref.filename, mimeType: ref.mimeType, data };
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
127
116
|
|
|
117
|
+
if (attachments.length > 0) {
|
|
128
118
|
const raw = buildMultipartMime({
|
|
129
119
|
to: forwardTo,
|
|
130
120
|
subject,
|
|
131
121
|
body: forwardHeader,
|
|
132
|
-
attachments
|
|
122
|
+
attachments,
|
|
133
123
|
});
|
|
134
|
-
const draft = await createDraftRaw(
|
|
124
|
+
const draft = await createDraftRaw(connection, raw);
|
|
135
125
|
return ok(
|
|
136
|
-
`Forward draft created to ${forwardTo} (Draft ID: ${draft.id}). Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
126
|
+
`Forward draft created to ${forwardTo} with ${attachments.length} attachment(s) (Draft ID: ${draft.id}). Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
137
127
|
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const raw = buildMultipartMime({
|
|
131
|
+
to: forwardTo,
|
|
132
|
+
subject,
|
|
133
|
+
body: forwardHeader,
|
|
134
|
+
attachments: [],
|
|
138
135
|
});
|
|
136
|
+
const draft = await createDraftRaw(connection, raw);
|
|
137
|
+
return ok(
|
|
138
|
+
`Forward draft created to ${forwardTo} (Draft ID: ${draft.id}). Review in Gmail Drafts, then tell me to send it or send it yourself.`,
|
|
139
|
+
);
|
|
139
140
|
} catch (e) {
|
|
140
141
|
return err(e instanceof Error ? e.message : String(e));
|
|
141
142
|
}
|
|
@@ -2,8 +2,7 @@ import {
|
|
|
2
2
|
batchModifyMessages,
|
|
3
3
|
modifyMessage,
|
|
4
4
|
} from "../../../../messaging/providers/gmail/client.js";
|
|
5
|
-
import {
|
|
6
|
-
import { withValidToken } from "../../../../security/token-manager.js";
|
|
5
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
7
6
|
import type {
|
|
8
7
|
ToolContext,
|
|
9
8
|
ToolExecutionResult,
|
|
@@ -21,14 +20,12 @@ export async function run(
|
|
|
21
20
|
|
|
22
21
|
if (messageIds && messageIds.length > 0) {
|
|
23
22
|
try {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
removeLabelIds,
|
|
29
|
-
});
|
|
30
|
-
return ok(`Labels updated on ${messageIds.length} message(s).`);
|
|
23
|
+
const connection = resolveOAuthConnection("integration:gmail");
|
|
24
|
+
await batchModifyMessages(connection, messageIds, {
|
|
25
|
+
addLabelIds,
|
|
26
|
+
removeLabelIds,
|
|
31
27
|
});
|
|
28
|
+
return ok(`Labels updated on ${messageIds.length} message(s).`);
|
|
32
29
|
} catch (e) {
|
|
33
30
|
return err(e instanceof Error ? e.message : String(e));
|
|
34
31
|
}
|
|
@@ -36,11 +33,12 @@ export async function run(
|
|
|
36
33
|
|
|
37
34
|
if (messageId) {
|
|
38
35
|
try {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
const connection = resolveOAuthConnection("integration:gmail");
|
|
37
|
+
await modifyMessage(connection, messageId, {
|
|
38
|
+
addLabelIds,
|
|
39
|
+
removeLabelIds,
|
|
43
40
|
});
|
|
41
|
+
return ok("Labels updated.");
|
|
44
42
|
} catch (e) {
|
|
45
43
|
return err(e instanceof Error ? e.message : String(e));
|
|
46
44
|
}
|
|
@@ -3,8 +3,7 @@ import {
|
|
|
3
3
|
listMessages,
|
|
4
4
|
} from "../../../../messaging/providers/gmail/client.js";
|
|
5
5
|
import type { GmailMessage } from "../../../../messaging/providers/gmail/types.js";
|
|
6
|
-
import {
|
|
7
|
-
import { withValidToken } from "../../../../security/token-manager.js";
|
|
6
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
8
7
|
import type {
|
|
9
8
|
ToolContext,
|
|
10
9
|
ToolExecutionResult,
|
|
@@ -56,159 +55,162 @@ export async function run(
|
|
|
56
55
|
const query = `in:inbox -has:unsubscribe newer_than:${timeRange}`;
|
|
57
56
|
|
|
58
57
|
try {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
timeBudgetExceeded = true;
|
|
74
|
-
truncated = true;
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
const pageSize = Math.min(100, maxMessages - allMessageIds.length);
|
|
78
|
-
const listResp = await listMessages(token, query, pageSize, pageToken);
|
|
79
|
-
const ids = (listResp.messages ?? []).map((m) => m.id);
|
|
80
|
-
if (ids.length === 0) break;
|
|
81
|
-
allMessageIds.push(...ids);
|
|
82
|
-
fetchPromises.push(
|
|
83
|
-
batchGetMessages(
|
|
84
|
-
token,
|
|
85
|
-
ids,
|
|
86
|
-
"metadata",
|
|
87
|
-
metadataHeaders,
|
|
88
|
-
"id,internalDate,payload/headers",
|
|
89
|
-
),
|
|
90
|
-
);
|
|
91
|
-
pageToken = listResp.nextPageToken ?? undefined;
|
|
92
|
-
if (!pageToken) break;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (allMessageIds.length >= maxMessages && pageToken) {
|
|
58
|
+
const connection = resolveOAuthConnection("integration:gmail");
|
|
59
|
+
// Pipeline: fire metadata fetches for each page of IDs as they arrive
|
|
60
|
+
const allMessageIds: string[] = [];
|
|
61
|
+
const fetchPromises: Promise<GmailMessage[]>[] = [];
|
|
62
|
+
let pageToken: string | undefined = inputPageToken;
|
|
63
|
+
let truncated = false;
|
|
64
|
+
let timeBudgetExceeded = false;
|
|
65
|
+
const metadataHeaders = ["From", "Subject", "Date"];
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
const TIME_BUDGET_MS = 90_000;
|
|
68
|
+
|
|
69
|
+
while (allMessageIds.length < maxMessages) {
|
|
70
|
+
if (Date.now() - startTime > TIME_BUDGET_MS) {
|
|
71
|
+
timeBudgetExceeded = true;
|
|
96
72
|
truncated = true;
|
|
73
|
+
break;
|
|
97
74
|
}
|
|
75
|
+
const pageSize = Math.min(100, maxMessages - allMessageIds.length);
|
|
76
|
+
const listResp = await listMessages(
|
|
77
|
+
connection,
|
|
78
|
+
query,
|
|
79
|
+
pageSize,
|
|
80
|
+
pageToken,
|
|
81
|
+
);
|
|
82
|
+
const ids = (listResp.messages ?? []).map((m) => m.id);
|
|
83
|
+
if (ids.length === 0) break;
|
|
84
|
+
allMessageIds.push(...ids);
|
|
85
|
+
fetchPromises.push(
|
|
86
|
+
batchGetMessages(
|
|
87
|
+
connection,
|
|
88
|
+
ids,
|
|
89
|
+
"metadata",
|
|
90
|
+
metadataHeaders,
|
|
91
|
+
"id,internalDate,payload/headers",
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
pageToken = listResp.nextPageToken ?? undefined;
|
|
95
|
+
if (!pageToken) break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (allMessageIds.length >= maxMessages && pageToken) {
|
|
99
|
+
truncated = true;
|
|
100
|
+
}
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
if (allMessageIds.length === 0) {
|
|
103
|
+
return ok(
|
|
104
|
+
JSON.stringify({
|
|
105
|
+
senders: [],
|
|
106
|
+
total_scanned: 0,
|
|
107
|
+
note: "No emails found matching the query.",
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const messages = (await Promise.all(fetchPromises)).flat();
|
|
113
|
+
|
|
114
|
+
// Aggregate all fetched messages by sender
|
|
115
|
+
const senderMap = new Map<string, OutreachSenderAggregation>();
|
|
116
|
+
|
|
117
|
+
for (const msg of messages) {
|
|
118
|
+
const headers = msg.payload?.headers ?? [];
|
|
119
|
+
const fromHeader =
|
|
120
|
+
headers.find((h) => h.name.toLowerCase() === "from")?.value ?? "";
|
|
121
|
+
const subject =
|
|
122
|
+
headers.find((h) => h.name.toLowerCase() === "subject")?.value ?? "";
|
|
123
|
+
const dateStr =
|
|
124
|
+
headers.find((h) => h.name.toLowerCase() === "date")?.value ?? "";
|
|
125
|
+
|
|
126
|
+
const { displayName, email } = parseFrom(fromHeader);
|
|
127
|
+
if (!email) continue;
|
|
128
|
+
|
|
129
|
+
let agg = senderMap.get(email);
|
|
130
|
+
if (!agg) {
|
|
131
|
+
agg = {
|
|
132
|
+
displayName,
|
|
133
|
+
email,
|
|
134
|
+
messageCount: 0,
|
|
135
|
+
newestMessageId: msg.id,
|
|
136
|
+
oldestDate: dateStr,
|
|
137
|
+
newestDate: dateStr,
|
|
138
|
+
messageIds: [],
|
|
139
|
+
hasMore: false,
|
|
140
|
+
sampleSubjects: [],
|
|
141
|
+
};
|
|
142
|
+
senderMap.set(email, agg);
|
|
107
143
|
}
|
|
108
144
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
headers.find((h) => h.name.toLowerCase() === "from")?.value ?? "";
|
|
118
|
-
const subject =
|
|
119
|
-
headers.find((h) => h.name.toLowerCase() === "subject")?.value ?? "";
|
|
120
|
-
const dateStr =
|
|
121
|
-
headers.find((h) => h.name.toLowerCase() === "date")?.value ?? "";
|
|
122
|
-
|
|
123
|
-
const { displayName, email } = parseFrom(fromHeader);
|
|
124
|
-
if (!email) continue;
|
|
125
|
-
|
|
126
|
-
let agg = senderMap.get(email);
|
|
127
|
-
if (!agg) {
|
|
128
|
-
agg = {
|
|
129
|
-
displayName,
|
|
130
|
-
email,
|
|
131
|
-
messageCount: 0,
|
|
132
|
-
newestMessageId: msg.id,
|
|
133
|
-
oldestDate: dateStr,
|
|
134
|
-
newestDate: dateStr,
|
|
135
|
-
messageIds: [],
|
|
136
|
-
hasMore: false,
|
|
137
|
-
sampleSubjects: [],
|
|
138
|
-
};
|
|
139
|
-
senderMap.set(email, agg);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
agg.messageCount++;
|
|
143
|
-
|
|
144
|
-
if (!agg.displayName && displayName) agg.displayName = displayName;
|
|
145
|
-
|
|
146
|
-
if (agg.messageIds.length < MAX_IDS_PER_SENDER) {
|
|
147
|
-
agg.messageIds.push(msg.id);
|
|
148
|
-
} else {
|
|
149
|
-
agg.hasMore = true;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Track date range
|
|
153
|
-
const msgEpoch = msg.internalDate ? Number(msg.internalDate) : 0;
|
|
154
|
-
const oldestEpoch = agg.oldestDate
|
|
155
|
-
? new Date(agg.oldestDate).getTime()
|
|
156
|
-
: Infinity;
|
|
157
|
-
const newestEpoch = agg.newestDate
|
|
158
|
-
? new Date(agg.newestDate).getTime()
|
|
159
|
-
: 0;
|
|
160
|
-
|
|
161
|
-
if (msgEpoch > 0 && msgEpoch < oldestEpoch) {
|
|
162
|
-
agg.oldestDate = dateStr || agg.oldestDate;
|
|
163
|
-
}
|
|
164
|
-
if (msgEpoch > newestEpoch) {
|
|
165
|
-
agg.newestDate = dateStr || agg.newestDate;
|
|
166
|
-
agg.newestMessageId = msg.id;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (subject && agg.sampleSubjects.length < MAX_SAMPLE_SUBJECTS) {
|
|
170
|
-
agg.sampleSubjects.push(subject);
|
|
171
|
-
}
|
|
145
|
+
agg.messageCount++;
|
|
146
|
+
|
|
147
|
+
if (!agg.displayName && displayName) agg.displayName = displayName;
|
|
148
|
+
|
|
149
|
+
if (agg.messageIds.length < MAX_IDS_PER_SENDER) {
|
|
150
|
+
agg.messageIds.push(msg.id);
|
|
151
|
+
} else {
|
|
152
|
+
agg.hasMore = true;
|
|
172
153
|
}
|
|
173
154
|
|
|
174
|
-
//
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
.
|
|
155
|
+
// Track date range
|
|
156
|
+
const msgEpoch = msg.internalDate ? Number(msg.internalDate) : 0;
|
|
157
|
+
const oldestEpoch = agg.oldestDate
|
|
158
|
+
? new Date(agg.oldestDate).getTime()
|
|
159
|
+
: Infinity;
|
|
160
|
+
const newestEpoch = agg.newestDate
|
|
161
|
+
? new Date(agg.newestDate).getTime()
|
|
162
|
+
: 0;
|
|
163
|
+
|
|
164
|
+
if (msgEpoch > 0 && msgEpoch < oldestEpoch) {
|
|
165
|
+
agg.oldestDate = dateStr || agg.oldestDate;
|
|
166
|
+
}
|
|
167
|
+
if (msgEpoch > newestEpoch) {
|
|
168
|
+
agg.newestDate = dateStr || agg.newestDate;
|
|
169
|
+
agg.newestMessageId = msg.id;
|
|
170
|
+
}
|
|
178
171
|
|
|
179
|
-
|
|
172
|
+
if (subject && agg.sampleSubjects.length < MAX_SAMPLE_SUBJECTS) {
|
|
173
|
+
agg.sampleSubjects.push(subject);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Sort by message count desc, take top N
|
|
178
|
+
const sorted = [...senderMap.values()]
|
|
179
|
+
.sort((a, b) => b.messageCount - a.messageCount)
|
|
180
|
+
.slice(0, maxSenders);
|
|
181
|
+
|
|
182
|
+
const senders = sorted.map((s) => ({
|
|
183
|
+
id: Buffer.from(s.email).toString("base64url"),
|
|
184
|
+
display_name: s.displayName || s.email.split("@")[0],
|
|
185
|
+
email: s.email,
|
|
186
|
+
message_count: s.messageCount,
|
|
187
|
+
newest_message_id: s.newestMessageId,
|
|
188
|
+
oldest_date: s.oldestDate,
|
|
189
|
+
newest_date: s.newestDate,
|
|
190
|
+
search_query: `from:${s.email}`,
|
|
191
|
+
sample_subjects: s.sampleSubjects,
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
// Store message IDs server-side to keep them out of LLM context
|
|
195
|
+
const scanId = storeScanResult(
|
|
196
|
+
sorted.map((s) => ({
|
|
180
197
|
id: Buffer.from(s.email).toString("base64url"),
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
newestUnsubscribableMessageId: null,
|
|
198
|
-
})),
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
return ok(
|
|
202
|
-
JSON.stringify({
|
|
203
|
-
scan_id: scanId,
|
|
204
|
-
senders,
|
|
205
|
-
total_scanned: allMessageIds.length,
|
|
206
|
-
...(truncated ? { truncated: true } : {}),
|
|
207
|
-
...(timeBudgetExceeded ? { time_budget_exceeded: true } : {}),
|
|
208
|
-
note: "Scanned inbox for senders without List-Unsubscribe headers (potential cold outreach). Use gmail_archive and gmail_filters for cleanup.",
|
|
209
|
-
}),
|
|
210
|
-
);
|
|
211
|
-
});
|
|
198
|
+
messageIds: s.messageIds,
|
|
199
|
+
newestMessageId: s.newestMessageId,
|
|
200
|
+
newestUnsubscribableMessageId: null,
|
|
201
|
+
})),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return ok(
|
|
205
|
+
JSON.stringify({
|
|
206
|
+
scan_id: scanId,
|
|
207
|
+
senders,
|
|
208
|
+
total_scanned: allMessageIds.length,
|
|
209
|
+
...(truncated ? { truncated: true } : {}),
|
|
210
|
+
...(timeBudgetExceeded ? { time_budget_exceeded: true } : {}),
|
|
211
|
+
note: "Scanned inbox for senders without List-Unsubscribe headers (potential cold outreach). Use gmail_archive and gmail_filters for cleanup.",
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
212
214
|
} catch (e) {
|
|
213
215
|
return err(e instanceof Error ? e.message : String(e));
|
|
214
216
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { sendDraft } from "../../../../messaging/providers/gmail/client.js";
|
|
2
|
-
import {
|
|
3
|
-
import { withValidToken } from "../../../../security/token-manager.js";
|
|
2
|
+
import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
|
|
4
3
|
import type {
|
|
5
4
|
ToolContext,
|
|
6
5
|
ToolExecutionResult,
|
|
@@ -15,11 +14,9 @@ export async function run(
|
|
|
15
14
|
if (!draftId) return err("draft_id is required.");
|
|
16
15
|
|
|
17
16
|
try {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return ok(`Draft sent (Message ID: ${msg.id}).`);
|
|
22
|
-
});
|
|
17
|
+
const connection = resolveOAuthConnection("integration:gmail");
|
|
18
|
+
const msg = await sendDraft(connection, draftId);
|
|
19
|
+
return ok(`Draft sent (Message ID: ${msg.id}).`);
|
|
23
20
|
} catch (e) {
|
|
24
21
|
return err(e instanceof Error ? e.message : String(e));
|
|
25
22
|
}
|