@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
|
@@ -10,11 +10,15 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { getPlatformBaseUrl } from "../../config/env.js";
|
|
13
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
13
14
|
import { getSecureKey } from "../../security/secure-keys.js";
|
|
14
15
|
import { MANAGED_PROVIDER_META } from "./constants.js";
|
|
15
16
|
|
|
16
17
|
/** Storage key for the assistant API key credential. */
|
|
17
|
-
const ASSISTANT_API_KEY_STORAGE_KEY =
|
|
18
|
+
const ASSISTANT_API_KEY_STORAGE_KEY = credentialKey(
|
|
19
|
+
"vellum",
|
|
20
|
+
"assistant_api_key",
|
|
21
|
+
);
|
|
18
22
|
|
|
19
23
|
export interface ManagedProxyContext {
|
|
20
24
|
/** Whether managed proxy prerequisites are satisfied. */
|
|
@@ -74,6 +74,15 @@ export class RateLimitProvider implements Provider {
|
|
|
74
74
|
|
|
75
75
|
if (this.requestTimestamps.length >= limit) {
|
|
76
76
|
const waitSec = Math.ceil((oldestInWindow + 60_000 - now) / 1000);
|
|
77
|
+
log.warn(
|
|
78
|
+
{
|
|
79
|
+
provider: this.name,
|
|
80
|
+
limit,
|
|
81
|
+
currentCount: this.requestTimestamps.length,
|
|
82
|
+
retryAfterSec: waitSec,
|
|
83
|
+
},
|
|
84
|
+
`Provider rate limit exceeded: ${limit} requests/minute for ${this.name}`,
|
|
85
|
+
);
|
|
77
86
|
throw new RateLimitError(
|
|
78
87
|
`Rate limit exceeded: ${limit} requests/minute. Try again in ${waitSec}s.`,
|
|
79
88
|
);
|
|
@@ -85,6 +94,14 @@ export class RateLimitProvider implements Provider {
|
|
|
85
94
|
if (limit <= 0) return;
|
|
86
95
|
|
|
87
96
|
if (this.sessionTokens >= limit) {
|
|
97
|
+
log.warn(
|
|
98
|
+
{
|
|
99
|
+
provider: this.name,
|
|
100
|
+
sessionTokens: this.sessionTokens,
|
|
101
|
+
limit,
|
|
102
|
+
},
|
|
103
|
+
`Session token budget exhausted for ${this.name}: ${this.sessionTokens.toLocaleString()}/${limit.toLocaleString()}`,
|
|
104
|
+
);
|
|
88
105
|
throw new RateLimitError(
|
|
89
106
|
`Session token budget exhausted: ${this.sessionTokens.toLocaleString()}/${limit.toLocaleString()} tokens used. Start a new session to continue.`,
|
|
90
107
|
);
|
|
@@ -289,8 +289,8 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
289
289
|
);
|
|
290
290
|
routingSources.set("gemini", "user-key");
|
|
291
291
|
} else {
|
|
292
|
-
// No user Gemini key —
|
|
293
|
-
const managedBaseUrl = buildManagedBaseUrl("
|
|
292
|
+
// No user Gemini key — route through Vertex managed proxy
|
|
293
|
+
const managedBaseUrl = buildManagedBaseUrl("vertex");
|
|
294
294
|
if (managedBaseUrl) {
|
|
295
295
|
const ctx = resolveManagedProxyContext();
|
|
296
296
|
const model = resolveModel(config, "gemini");
|
package/src/providers/retry.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProviderError } from "../util/errors.js";
|
|
2
|
-
import { getLogger
|
|
2
|
+
import { getLogger } from "../util/logger.js";
|
|
3
3
|
import {
|
|
4
4
|
computeRetryDelay,
|
|
5
5
|
DEFAULT_BASE_DELAY_MS,
|
|
@@ -96,43 +96,17 @@ export class RetryProvider implements Provider {
|
|
|
96
96
|
options?: SendMessageOptions,
|
|
97
97
|
): Promise<ProviderResponse> {
|
|
98
98
|
let lastError: unknown;
|
|
99
|
-
const debug = isDebug();
|
|
100
|
-
|
|
101
|
-
if (debug) {
|
|
102
|
-
log.debug(
|
|
103
|
-
{
|
|
104
|
-
provider: this.name,
|
|
105
|
-
messageCount: messages.length,
|
|
106
|
-
toolCount: tools?.length ?? 0,
|
|
107
|
-
},
|
|
108
|
-
"Provider sendMessage start",
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
99
|
|
|
112
100
|
const normalizedOptions = normalizeSendMessageOptions(this.name, options);
|
|
113
101
|
|
|
114
102
|
for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
|
|
115
103
|
try {
|
|
116
|
-
const start = Date.now();
|
|
117
104
|
const result = await this.inner.sendMessage(
|
|
118
105
|
messages,
|
|
119
106
|
tools,
|
|
120
107
|
systemPrompt,
|
|
121
108
|
normalizedOptions,
|
|
122
109
|
);
|
|
123
|
-
if (debug) {
|
|
124
|
-
log.debug(
|
|
125
|
-
{
|
|
126
|
-
provider: this.name,
|
|
127
|
-
durationMs: Date.now() - start,
|
|
128
|
-
attempt: attempt + 1,
|
|
129
|
-
model: result.model,
|
|
130
|
-
inputTokens: result.usage.inputTokens,
|
|
131
|
-
outputTokens: result.usage.outputTokens,
|
|
132
|
-
},
|
|
133
|
-
"Provider sendMessage success",
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
110
|
return result;
|
|
137
111
|
} catch (error) {
|
|
138
112
|
lastError = error;
|
package/src/runtime/AGENTS.md
CHANGED
|
@@ -51,6 +51,23 @@ Channel approval flows use `requestId` (not `runId`) as the primary identifier:
|
|
|
51
51
|
- Guardian approval records in `channelGuardianApprovalRequests` link via `requestId`.
|
|
52
52
|
- The conversational approval engine classifies user intent and resolves via `session.handleConfirmationResponse(requestId, decision)`.
|
|
53
53
|
|
|
54
|
+
## Rate Limiting & Diagnostics
|
|
55
|
+
|
|
56
|
+
All `/v1/*` endpoints share a per-client-IP sliding-window rate limiter (`middleware/rate-limiter.ts`):
|
|
57
|
+
|
|
58
|
+
- **Authenticated**: 300 requests/minute
|
|
59
|
+
- **Unauthenticated**: 20 requests/minute
|
|
60
|
+
|
|
61
|
+
When the limit is exceeded, the limiter returns 429 and logs a structured warning (module: `rate-limiter`) with the denied endpoint and a breakdown of which endpoints consumed the budget in the current window. This makes it easy to identify whether the cause is rapid thread switching, polling, or unexpected request volume.
|
|
62
|
+
|
|
63
|
+
Logs are written to `~/.vellum/workspace/data/logs/vellum.log` by default. If `logFile.dir` is configured, logs rotate daily as `assistant-YYYY-MM-DD.log` in that directory. To watch rate limit events in real time:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
tail -f ~/.vellum/workspace/data/logs/vellum.log | grep rate-limit
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The provider-level rate limiter (`providers/ratelimit.ts`) also logs warnings (module: `rate-limit`) when request rate or token budget limits are enforced.
|
|
70
|
+
|
|
54
71
|
## HTTP-Only Transport
|
|
55
72
|
|
|
56
73
|
HTTP is the sole transport for client-daemon communication. The runtime HTTP server (`assistant/src/runtime/http-server.ts`) is the canonical API surface. Clients connect via HTTP for request/response operations and SSE (`GET /v1/events`) for streaming server-to-client events.
|
|
@@ -358,9 +358,6 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
|
|
|
358
358
|
{ endpoint: "computer-use/sessions/abort", scopes: ["chat.write"] },
|
|
359
359
|
{ endpoint: "computer-use/observations", scopes: ["chat.write"] },
|
|
360
360
|
{ endpoint: "computer-use/tasks", scopes: ["chat.write"] },
|
|
361
|
-
{ endpoint: "computer-use/ride-shotgun/start", scopes: ["chat.write"] },
|
|
362
|
-
{ endpoint: "computer-use/ride-shotgun/stop", scopes: ["chat.write"] },
|
|
363
|
-
{ endpoint: "computer-use/ride-shotgun/status", scopes: ["chat.write"] },
|
|
364
361
|
{ endpoint: "computer-use/watch", scopes: ["chat.write"] },
|
|
365
362
|
|
|
366
363
|
// Recordings
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
saveRawConfig,
|
|
17
17
|
setNestedValue,
|
|
18
18
|
} from "../../config/loader.js";
|
|
19
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
19
20
|
import { getSecureKey } from "../../security/secure-keys.js";
|
|
20
21
|
import { getTelegramBotUsername } from "../../telegram/bot-username.js";
|
|
21
22
|
import { getLogger } from "../../util/logger.js";
|
|
@@ -40,7 +41,7 @@ export async function ensureTelegramBotUsernameResolved(): Promise<void> {
|
|
|
40
41
|
return; // Username already cached in config
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
const token = getSecureKey("
|
|
44
|
+
const token = getSecureKey(credentialKey("telegram", "bot_token"));
|
|
44
45
|
if (!token) return;
|
|
45
46
|
|
|
46
47
|
try {
|
|
@@ -3,6 +3,7 @@ import { hasTwilioCredentials } from "../calls/twilio-rest.js";
|
|
|
3
3
|
import { getChannelInvitePolicy } from "../channels/config.js";
|
|
4
4
|
import { loadRawConfig } from "../config/loader.js";
|
|
5
5
|
import { getEmailService } from "../email/service.js";
|
|
6
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
6
7
|
import { getSecureKey } from "../security/secure-keys.js";
|
|
7
8
|
import { resolveWhatsAppDisplayNumber } from "./channel-invite-transports/whatsapp.js";
|
|
8
9
|
import type {
|
|
@@ -11,6 +12,7 @@ import type {
|
|
|
11
12
|
ChannelProbeContext,
|
|
12
13
|
ChannelReadinessSnapshot,
|
|
13
14
|
ReadinessCheckResult,
|
|
15
|
+
SetupStatus,
|
|
14
16
|
} from "./channel-readiness-types.js";
|
|
15
17
|
import { probeLocalGatewayHealth } from "./local-gateway-health.js";
|
|
16
18
|
|
|
@@ -31,55 +33,83 @@ function hasIngressConfigured(): boolean {
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
// ── Shared check helpers ────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
/** Build a check result from a boolean condition. */
|
|
39
|
+
function check(
|
|
40
|
+
name: string,
|
|
41
|
+
passed: boolean,
|
|
42
|
+
passMessage: string,
|
|
43
|
+
failMessage: string,
|
|
44
|
+
): ReadinessCheckResult {
|
|
45
|
+
return { name, passed, message: passed ? passMessage : failMessage };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Check that a secure credential key exists. */
|
|
49
|
+
function checkCredential(
|
|
50
|
+
name: string,
|
|
51
|
+
service: string,
|
|
52
|
+
field: string,
|
|
53
|
+
label: string,
|
|
54
|
+
): ReadinessCheckResult {
|
|
55
|
+
const exists = !!getSecureKey(credentialKey(service, field));
|
|
56
|
+
return check(
|
|
57
|
+
name,
|
|
58
|
+
exists,
|
|
59
|
+
`${label} is configured`,
|
|
60
|
+
`${label} is not configured`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Check that public ingress is configured and enabled. */
|
|
65
|
+
function checkIngress(): ReadinessCheckResult {
|
|
66
|
+
const has = hasIngressConfigured();
|
|
67
|
+
return check(
|
|
68
|
+
"ingress",
|
|
69
|
+
has,
|
|
70
|
+
"Public ingress URL is configured",
|
|
71
|
+
"Public ingress URL is not configured or disabled",
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
34
75
|
// ── Voice Probe ─────────────────────────────────────────────────────────────
|
|
35
76
|
|
|
36
77
|
const voiceProbe: ChannelProbe = {
|
|
37
78
|
channel: "phone",
|
|
38
79
|
async runLocalChecks(): Promise<ReadinessCheckResult[]> {
|
|
39
|
-
const results: ReadinessCheckResult[] = [];
|
|
40
|
-
|
|
41
80
|
const hasCreds = hasTwilioCredentials();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
results.push({
|
|
72
|
-
name: "gateway_health",
|
|
73
|
-
passed: gatewayHealth.healthy,
|
|
74
|
-
message: gatewayHealth.healthy
|
|
75
|
-
? `Local gateway is serving requests at ${gatewayHealth.target}`
|
|
76
|
-
: `Local gateway is not serving requests at ${gatewayHealth.target}${
|
|
77
|
-
gatewayHealth.error ? `: ${gatewayHealth.error}` : ""
|
|
78
|
-
}`,
|
|
79
|
-
});
|
|
81
|
+
const hasPhone = !!resolveTwilioPhoneNumber();
|
|
82
|
+
const ingress = checkIngress();
|
|
83
|
+
|
|
84
|
+
const checks: ReadinessCheckResult[] = [
|
|
85
|
+
check(
|
|
86
|
+
"twilio_credentials",
|
|
87
|
+
hasCreds,
|
|
88
|
+
"Twilio credentials are configured",
|
|
89
|
+
"Twilio Account SID and Auth Token are not configured",
|
|
90
|
+
),
|
|
91
|
+
check(
|
|
92
|
+
"phone_number",
|
|
93
|
+
hasPhone,
|
|
94
|
+
"Phone number is assigned for voice calls",
|
|
95
|
+
"No phone number assigned for voice calls",
|
|
96
|
+
),
|
|
97
|
+
ingress,
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
if (ingress.passed) {
|
|
101
|
+
const gw = await probeLocalGatewayHealth();
|
|
102
|
+
checks.push(
|
|
103
|
+
check(
|
|
104
|
+
"gateway_health",
|
|
105
|
+
gw.healthy,
|
|
106
|
+
`Local gateway is serving requests at ${gw.target}`,
|
|
107
|
+
`Local gateway is not serving requests at ${gw.target}${gw.error ? `: ${gw.error}` : ""}`,
|
|
108
|
+
),
|
|
109
|
+
);
|
|
80
110
|
}
|
|
81
111
|
|
|
82
|
-
return
|
|
112
|
+
return checks;
|
|
83
113
|
},
|
|
84
114
|
};
|
|
85
115
|
|
|
@@ -87,41 +117,16 @@ const voiceProbe: ChannelProbe = {
|
|
|
87
117
|
|
|
88
118
|
const telegramProbe: ChannelProbe = {
|
|
89
119
|
channel: "telegram",
|
|
90
|
-
runLocalChecks()
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
const hasWebhookSecret = !!getSecureKey(
|
|
103
|
-
"credential:telegram:webhook_secret",
|
|
104
|
-
);
|
|
105
|
-
results.push({
|
|
106
|
-
name: "webhook_secret",
|
|
107
|
-
passed: hasWebhookSecret,
|
|
108
|
-
message: hasWebhookSecret
|
|
109
|
-
? "Telegram webhook secret is configured"
|
|
110
|
-
: "Telegram webhook secret is not configured",
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const hasIngress = hasIngressConfigured();
|
|
114
|
-
results.push({
|
|
115
|
-
name: "ingress",
|
|
116
|
-
passed: hasIngress,
|
|
117
|
-
message: hasIngress
|
|
118
|
-
? "Public ingress URL is configured"
|
|
119
|
-
: "Public ingress URL is not configured or disabled",
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
return results;
|
|
123
|
-
},
|
|
124
|
-
// Telegram has no remote checks currently
|
|
120
|
+
runLocalChecks: () => [
|
|
121
|
+
checkCredential("bot_token", "telegram", "bot_token", "Telegram bot token"),
|
|
122
|
+
checkCredential(
|
|
123
|
+
"webhook_secret",
|
|
124
|
+
"telegram",
|
|
125
|
+
"webhook_secret",
|
|
126
|
+
"Telegram webhook secret",
|
|
127
|
+
),
|
|
128
|
+
checkIngress(),
|
|
129
|
+
],
|
|
125
130
|
};
|
|
126
131
|
|
|
127
132
|
// ── Email Probe ─────────────────────────────────────────────────────────────
|
|
@@ -129,43 +134,33 @@ const telegramProbe: ChannelProbe = {
|
|
|
129
134
|
const emailProbe: ChannelProbe = {
|
|
130
135
|
channel: "email",
|
|
131
136
|
runLocalChecks(): ReadinessCheckResult[] {
|
|
132
|
-
const results: ReadinessCheckResult[] = [];
|
|
133
|
-
|
|
134
137
|
const hasApiKey = !!(
|
|
135
|
-
getSecureKey("agentmail") ||
|
|
138
|
+
getSecureKey("agentmail") ||
|
|
139
|
+
getSecureKey(credentialKey("agentmail", "api_key"))
|
|
136
140
|
);
|
|
137
|
-
results.push({
|
|
138
|
-
name: "agentmail_api_key",
|
|
139
|
-
passed: hasApiKey,
|
|
140
|
-
message: hasApiKey
|
|
141
|
-
? "AgentMail API key is configured"
|
|
142
|
-
: "AgentMail API key is not configured",
|
|
143
|
-
});
|
|
144
|
-
|
|
145
141
|
const invitePolicy = getChannelInvitePolicy("email");
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
return results;
|
|
142
|
+
return [
|
|
143
|
+
check(
|
|
144
|
+
"agentmail_api_key",
|
|
145
|
+
hasApiKey,
|
|
146
|
+
"AgentMail API key is configured",
|
|
147
|
+
"AgentMail API key is not configured",
|
|
148
|
+
),
|
|
149
|
+
check(
|
|
150
|
+
"invite_policy",
|
|
151
|
+
invitePolicy.codeRedemptionEnabled,
|
|
152
|
+
"Email invite code redemption is enabled",
|
|
153
|
+
"Email invite code redemption is disabled",
|
|
154
|
+
),
|
|
155
|
+
checkIngress(),
|
|
156
|
+
];
|
|
164
157
|
},
|
|
158
|
+
// runRemoteChecks UNCHANGED — keep the existing inbox_configured check as-is
|
|
165
159
|
async runRemoteChecks(): Promise<ReadinessCheckResult[]> {
|
|
166
160
|
// Only worth checking if the API key is present
|
|
167
161
|
const hasApiKey = !!(
|
|
168
|
-
getSecureKey("agentmail") ||
|
|
162
|
+
getSecureKey("agentmail") ||
|
|
163
|
+
getSecureKey(credentialKey("agentmail", "api_key"))
|
|
169
164
|
);
|
|
170
165
|
if (!hasApiKey) return [];
|
|
171
166
|
|
|
@@ -199,77 +194,47 @@ const emailProbe: ChannelProbe = {
|
|
|
199
194
|
const whatsappProbe: ChannelProbe = {
|
|
200
195
|
channel: "whatsapp",
|
|
201
196
|
runLocalChecks(): ReadinessCheckResult[] {
|
|
202
|
-
const results: ReadinessCheckResult[] = [];
|
|
203
|
-
|
|
204
|
-
const hasPhoneNumberId = !!getSecureKey(
|
|
205
|
-
"credential:whatsapp:phone_number_id",
|
|
206
|
-
);
|
|
207
|
-
results.push({
|
|
208
|
-
name: "whatsapp_phone_number_id",
|
|
209
|
-
passed: hasPhoneNumberId,
|
|
210
|
-
message: hasPhoneNumberId
|
|
211
|
-
? "WhatsApp phone number ID is configured"
|
|
212
|
-
: "WhatsApp phone number ID is not configured",
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
const hasAccessToken = !!getSecureKey("credential:whatsapp:access_token");
|
|
216
|
-
results.push({
|
|
217
|
-
name: "whatsapp_access_token",
|
|
218
|
-
passed: hasAccessToken,
|
|
219
|
-
message: hasAccessToken
|
|
220
|
-
? "WhatsApp access token is configured"
|
|
221
|
-
: "WhatsApp access token is not configured",
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
const hasAppSecret = !!getSecureKey("credential:whatsapp:app_secret");
|
|
225
|
-
results.push({
|
|
226
|
-
name: "whatsapp_app_secret",
|
|
227
|
-
passed: hasAppSecret,
|
|
228
|
-
message: hasAppSecret
|
|
229
|
-
? "WhatsApp app secret is configured"
|
|
230
|
-
: "WhatsApp app secret is not configured",
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
const hasWebhookVerifyToken = !!getSecureKey(
|
|
234
|
-
"credential:whatsapp:webhook_verify_token",
|
|
235
|
-
);
|
|
236
|
-
results.push({
|
|
237
|
-
name: "whatsapp_webhook_verify_token",
|
|
238
|
-
passed: hasWebhookVerifyToken,
|
|
239
|
-
message: hasWebhookVerifyToken
|
|
240
|
-
? "WhatsApp webhook verify token is configured"
|
|
241
|
-
: "WhatsApp webhook verify token is not configured",
|
|
242
|
-
});
|
|
243
|
-
|
|
244
197
|
const displayNumber = resolveWhatsAppDisplayNumber();
|
|
245
|
-
const hasDisplayNumber = !!displayNumber;
|
|
246
|
-
results.push({
|
|
247
|
-
name: "whatsapp_display_phone_number",
|
|
248
|
-
passed: hasDisplayNumber,
|
|
249
|
-
message: hasDisplayNumber
|
|
250
|
-
? `WhatsApp display phone number is configured (${displayNumber})`
|
|
251
|
-
: "WhatsApp display phone number is not configured — set whatsapp.phoneNumber in workspace config",
|
|
252
|
-
});
|
|
253
|
-
|
|
254
198
|
const invitePolicy = getChannelInvitePolicy("whatsapp");
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
199
|
+
return [
|
|
200
|
+
checkCredential(
|
|
201
|
+
"whatsapp_phone_number_id",
|
|
202
|
+
"whatsapp",
|
|
203
|
+
"phone_number_id",
|
|
204
|
+
"WhatsApp phone number ID",
|
|
205
|
+
),
|
|
206
|
+
checkCredential(
|
|
207
|
+
"whatsapp_access_token",
|
|
208
|
+
"whatsapp",
|
|
209
|
+
"access_token",
|
|
210
|
+
"WhatsApp access token",
|
|
211
|
+
),
|
|
212
|
+
checkCredential(
|
|
213
|
+
"whatsapp_app_secret",
|
|
214
|
+
"whatsapp",
|
|
215
|
+
"app_secret",
|
|
216
|
+
"WhatsApp app secret",
|
|
217
|
+
),
|
|
218
|
+
checkCredential(
|
|
219
|
+
"whatsapp_webhook_verify_token",
|
|
220
|
+
"whatsapp",
|
|
221
|
+
"webhook_verify_token",
|
|
222
|
+
"WhatsApp webhook verify token",
|
|
223
|
+
),
|
|
224
|
+
check(
|
|
225
|
+
"whatsapp_display_phone_number",
|
|
226
|
+
!!displayNumber,
|
|
227
|
+
`WhatsApp display phone number is configured (${displayNumber})`,
|
|
228
|
+
"WhatsApp display phone number is not configured — set whatsapp.phoneNumber in workspace config",
|
|
229
|
+
),
|
|
230
|
+
check(
|
|
231
|
+
"invite_policy",
|
|
232
|
+
invitePolicy.codeRedemptionEnabled,
|
|
233
|
+
"WhatsApp invite code redemption is enabled",
|
|
234
|
+
"WhatsApp invite code redemption is disabled",
|
|
235
|
+
),
|
|
236
|
+
checkIngress(),
|
|
237
|
+
];
|
|
273
238
|
},
|
|
274
239
|
};
|
|
275
240
|
|
|
@@ -277,26 +242,20 @@ const whatsappProbe: ChannelProbe = {
|
|
|
277
242
|
|
|
278
243
|
const slackProbe: ChannelProbe = {
|
|
279
244
|
channel: "slack",
|
|
280
|
-
runLocalChecks()
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
message: hasAppToken
|
|
295
|
-
? "Slack app token is configured"
|
|
296
|
-
: "Slack app token is not configured",
|
|
297
|
-
},
|
|
298
|
-
];
|
|
299
|
-
},
|
|
245
|
+
runLocalChecks: () => [
|
|
246
|
+
checkCredential(
|
|
247
|
+
"bot_token",
|
|
248
|
+
"slack_channel",
|
|
249
|
+
"bot_token",
|
|
250
|
+
"Slack bot token",
|
|
251
|
+
),
|
|
252
|
+
checkCredential(
|
|
253
|
+
"app_token",
|
|
254
|
+
"slack_channel",
|
|
255
|
+
"app_token",
|
|
256
|
+
"Slack app token",
|
|
257
|
+
),
|
|
258
|
+
],
|
|
300
259
|
};
|
|
301
260
|
|
|
302
261
|
// ── Service ─────────────────────────────────────────────────────────────────
|
|
@@ -369,6 +328,18 @@ export class ChannelReadinessService {
|
|
|
369
328
|
: true;
|
|
370
329
|
const ready = allLocalPassed && allRemotePassed;
|
|
371
330
|
|
|
331
|
+
// setupStatus: considers all checks (credentials + infrastructure)
|
|
332
|
+
const consideredChecks = [
|
|
333
|
+
...localChecks,
|
|
334
|
+
...(remoteChecks && remoteChecksAffectReadiness ? remoteChecks : []),
|
|
335
|
+
];
|
|
336
|
+
const anyCheckPassed = consideredChecks.some((c) => c.passed);
|
|
337
|
+
const setupStatus: SetupStatus = !anyCheckPassed
|
|
338
|
+
? "not_configured"
|
|
339
|
+
: ready
|
|
340
|
+
? "ready"
|
|
341
|
+
: "incomplete";
|
|
342
|
+
|
|
372
343
|
const reasons: Array<{ code: string; text: string }> = [];
|
|
373
344
|
for (const check of localChecks) {
|
|
374
345
|
if (!check.passed) {
|
|
@@ -386,6 +357,7 @@ export class ChannelReadinessService {
|
|
|
386
357
|
const snapshot: ChannelReadinessSnapshot = {
|
|
387
358
|
channel: ch,
|
|
388
359
|
ready,
|
|
360
|
+
setupStatus,
|
|
389
361
|
checkedAt:
|
|
390
362
|
remoteChecks && cached && !remoteChecksFreshlyFetched
|
|
391
363
|
? cached.checkedAt
|
|
@@ -422,6 +394,7 @@ export class ChannelReadinessService {
|
|
|
422
394
|
return {
|
|
423
395
|
channel,
|
|
424
396
|
ready: false,
|
|
397
|
+
setupStatus: "not_configured",
|
|
425
398
|
checkedAt: Date.now(),
|
|
426
399
|
stale: false,
|
|
427
400
|
reasons: [
|
|
@@ -4,6 +4,9 @@ import type { ChannelId } from "../channels/types.js";
|
|
|
4
4
|
|
|
5
5
|
export type { ChannelId };
|
|
6
6
|
|
|
7
|
+
/** Setup progress for a channel: not_configured → incomplete → ready. */
|
|
8
|
+
export type SetupStatus = "not_configured" | "incomplete" | "ready";
|
|
9
|
+
|
|
7
10
|
/** Result of a single readiness check (local or remote). */
|
|
8
11
|
export interface ReadinessCheckResult {
|
|
9
12
|
name: string;
|
|
@@ -15,6 +18,7 @@ export interface ReadinessCheckResult {
|
|
|
15
18
|
export interface ChannelReadinessSnapshot {
|
|
16
19
|
channel: ChannelId;
|
|
17
20
|
ready: boolean;
|
|
21
|
+
setupStatus: SetupStatus;
|
|
18
22
|
checkedAt: number;
|
|
19
23
|
stale: boolean;
|
|
20
24
|
reasons: Array<{ code: string; text: string }>;
|
|
@@ -208,43 +208,3 @@ export async function deliverReplyViaCallback(
|
|
|
208
208
|
break;
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Deliver only the attachments from the last assistant message, skipping text.
|
|
214
|
-
* Used when streaming already delivered the text content and only file
|
|
215
|
-
* attachments remain to be sent via the normal delivery path.
|
|
216
|
-
*/
|
|
217
|
-
export async function deliverAttachmentsOnly(
|
|
218
|
-
conversationId: string,
|
|
219
|
-
externalChatId: string,
|
|
220
|
-
callbackUrl: string,
|
|
221
|
-
bearerToken?: string,
|
|
222
|
-
assistantId?: string,
|
|
223
|
-
): Promise<void> {
|
|
224
|
-
const msgs = getMessages(conversationId);
|
|
225
|
-
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
226
|
-
if (msgs[i].role !== "assistant") continue;
|
|
227
|
-
|
|
228
|
-
const linked = attachmentsStore.getAttachmentMetadataForMessage(msgs[i].id);
|
|
229
|
-
if (linked.length === 0) return;
|
|
230
|
-
|
|
231
|
-
const replyAttachments: RuntimeAttachmentMetadata[] = linked.map((a) => ({
|
|
232
|
-
id: a.id,
|
|
233
|
-
filename: a.originalFilename,
|
|
234
|
-
mimeType: a.mimeType,
|
|
235
|
-
sizeBytes: a.sizeBytes,
|
|
236
|
-
kind: a.kind,
|
|
237
|
-
}));
|
|
238
|
-
|
|
239
|
-
await deliverChannelReply(
|
|
240
|
-
callbackUrl,
|
|
241
|
-
{
|
|
242
|
-
chatId: externalChatId,
|
|
243
|
-
attachments: replyAttachments,
|
|
244
|
-
assistantId,
|
|
245
|
-
},
|
|
246
|
-
bearerToken,
|
|
247
|
-
);
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
}
|