@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
2
|
+
import { parseImageDimensions } from "./image-dimensions.js";
|
|
2
3
|
|
|
3
4
|
const CHARS_PER_TOKEN = 4;
|
|
4
5
|
const MESSAGE_OVERHEAD_TOKENS = 4;
|
|
@@ -12,6 +13,22 @@ const OTHER_BLOCK_TOKENS = 16;
|
|
|
12
13
|
const SYSTEM_PROMPT_OVERHEAD_TOKENS = 8;
|
|
13
14
|
const GEMINI_INLINE_FILE_MIME_TYPES = new Set(["application/pdf"]);
|
|
14
15
|
|
|
16
|
+
// Anthropic scales images to fit within 1568x1568 maintaining aspect ratio,
|
|
17
|
+
// then charges ~(width * height) / 750 tokens.
|
|
18
|
+
const ANTHROPIC_IMAGE_MAX_DIMENSION = 1568;
|
|
19
|
+
const ANTHROPIC_IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
20
|
+
const ANTHROPIC_IMAGE_MAX_TOKENS = Math.ceil(
|
|
21
|
+
ANTHROPIC_IMAGE_MAX_DIMENSION *
|
|
22
|
+
ANTHROPIC_IMAGE_MAX_DIMENSION *
|
|
23
|
+
ANTHROPIC_IMAGE_TOKENS_PER_PIXEL,
|
|
24
|
+
); // ~3,277 tokens
|
|
25
|
+
|
|
26
|
+
// Anthropic renders each PDF page as an image (~1,568 tokens at standard
|
|
27
|
+
// resolution) plus any extracted text. Typical PDF pages are 50-150 KB.
|
|
28
|
+
// Using ~100 KB/page and ~1,600 tokens/page gives ~0.016 tokens/byte.
|
|
29
|
+
const ANTHROPIC_PDF_TOKENS_PER_BYTE = 0.016;
|
|
30
|
+
const ANTHROPIC_PDF_MIN_TOKENS = 1600; // At least one page
|
|
31
|
+
|
|
15
32
|
export interface TokenEstimatorOptions {
|
|
16
33
|
providerName?: string;
|
|
17
34
|
}
|
|
@@ -21,21 +38,69 @@ export function estimateTextTokens(text: string): number {
|
|
|
21
38
|
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
22
39
|
}
|
|
23
40
|
|
|
24
|
-
function
|
|
41
|
+
function estimateAnthropicPdfTokens(base64Data: string): number {
|
|
42
|
+
const rawBytes = Math.ceil((base64Data.length * 3) / 4);
|
|
43
|
+
return Math.max(
|
|
44
|
+
ANTHROPIC_PDF_MIN_TOKENS,
|
|
45
|
+
Math.ceil(rawBytes * ANTHROPIC_PDF_TOKENS_PER_BYTE),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function estimateFileDataTokens(
|
|
25
50
|
block: Extract<ContentBlock, { type: "file" }>,
|
|
26
51
|
options?: TokenEstimatorOptions,
|
|
27
|
-
):
|
|
28
|
-
|
|
29
|
-
|
|
52
|
+
): number {
|
|
53
|
+
const providerName = options?.providerName;
|
|
54
|
+
|
|
55
|
+
// Anthropic sends PDFs as native document blocks and renders each page as an image
|
|
56
|
+
if (
|
|
57
|
+
providerName === "anthropic" &&
|
|
58
|
+
block.source.media_type === "application/pdf"
|
|
59
|
+
) {
|
|
60
|
+
return estimateAnthropicPdfTokens(block.source.data);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Gemini sends certain file types inline as base64
|
|
64
|
+
if (
|
|
65
|
+
providerName === "gemini" &&
|
|
66
|
+
GEMINI_INLINE_FILE_MIME_TYPES.has(block.source.media_type)
|
|
67
|
+
) {
|
|
68
|
+
return estimateTextTokens(block.source.data);
|
|
30
69
|
}
|
|
31
|
-
|
|
70
|
+
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function estimateAnthropicImageTokens(width: number, height: number): number {
|
|
75
|
+
// Scale down to fit within 1568x1568 bounding box, maintaining aspect ratio
|
|
76
|
+
const scale = Math.min(
|
|
77
|
+
1,
|
|
78
|
+
ANTHROPIC_IMAGE_MAX_DIMENSION / Math.max(width, height),
|
|
79
|
+
);
|
|
80
|
+
const scaledWidth = Math.round(width * scale);
|
|
81
|
+
const scaledHeight = Math.round(height * scale);
|
|
82
|
+
return Math.max(
|
|
83
|
+
IMAGE_BLOCK_TOKENS, // minimum 1024
|
|
84
|
+
Math.ceil(scaledWidth * scaledHeight * ANTHROPIC_IMAGE_TOKENS_PER_PIXEL),
|
|
85
|
+
);
|
|
32
86
|
}
|
|
33
87
|
|
|
34
|
-
function
|
|
88
|
+
function estimateImageTokens(
|
|
35
89
|
block: Extract<ContentBlock, { type: "image" }>,
|
|
90
|
+
options?: TokenEstimatorOptions,
|
|
36
91
|
): number {
|
|
37
|
-
|
|
38
|
-
|
|
92
|
+
if (options?.providerName === "anthropic") {
|
|
93
|
+
const dims = parseImageDimensions(
|
|
94
|
+
block.source.data,
|
|
95
|
+
block.source.media_type,
|
|
96
|
+
);
|
|
97
|
+
if (dims) {
|
|
98
|
+
return estimateAnthropicImageTokens(dims.width, dims.height);
|
|
99
|
+
}
|
|
100
|
+
// Fallback: if dimensions can't be parsed, use Anthropic's max
|
|
101
|
+
return ANTHROPIC_IMAGE_MAX_TOKENS;
|
|
102
|
+
}
|
|
103
|
+
// Non-Anthropic: keep existing base64-size heuristic
|
|
39
104
|
return estimateTextTokens(block.source.data);
|
|
40
105
|
}
|
|
41
106
|
|
|
@@ -69,16 +134,14 @@ export function estimateContentBlockTokens(
|
|
|
69
134
|
IMAGE_BLOCK_TOKENS,
|
|
70
135
|
IMAGE_BLOCK_OVERHEAD_TOKENS +
|
|
71
136
|
estimateTextTokens(block.source.media_type) +
|
|
72
|
-
|
|
137
|
+
estimateImageTokens(block, options),
|
|
73
138
|
);
|
|
74
139
|
case "file":
|
|
75
140
|
return (
|
|
76
141
|
FILE_BLOCK_OVERHEAD_TOKENS +
|
|
77
142
|
estimateTextTokens(block.source.filename) +
|
|
78
143
|
estimateTextTokens(block.source.media_type) +
|
|
79
|
-
(
|
|
80
|
-
? estimateTextTokens(block.source.data)
|
|
81
|
-
: 0) +
|
|
144
|
+
estimateFileDataTokens(block, options) +
|
|
82
145
|
estimateTextTokens(block.extracted_text ?? "")
|
|
83
146
|
);
|
|
84
147
|
case "thinking":
|
|
@@ -83,21 +83,44 @@ export interface ContextWindowCompactOptions {
|
|
|
83
83
|
|
|
84
84
|
export interface ContextWindowManagerOptions {
|
|
85
85
|
provider: Provider;
|
|
86
|
-
systemPrompt: string;
|
|
86
|
+
systemPrompt: string | (() => string);
|
|
87
87
|
config: ContextWindowConfig;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export class ContextWindowManager {
|
|
91
91
|
private readonly provider: Provider;
|
|
92
|
-
private readonly
|
|
92
|
+
private readonly _systemPrompt: string | (() => string);
|
|
93
93
|
private readonly config: ContextWindowConfig;
|
|
94
|
+
/**
|
|
95
|
+
* Cached resolved system prompt. Lazily populated on first access via the
|
|
96
|
+
* `systemPrompt` getter and cleared after each compaction pass so the next
|
|
97
|
+
* pass picks up any prompt changes.
|
|
98
|
+
*/
|
|
99
|
+
private _resolvedSystemPrompt: string | undefined;
|
|
94
100
|
|
|
95
101
|
constructor(options: ContextWindowManagerOptions) {
|
|
96
102
|
this.provider = options.provider;
|
|
97
|
-
this.
|
|
103
|
+
this._systemPrompt = options.systemPrompt;
|
|
98
104
|
this.config = options.config;
|
|
99
105
|
}
|
|
100
106
|
|
|
107
|
+
/** Lazily resolve and cache the system prompt for the duration of a compaction pass. */
|
|
108
|
+
private get systemPrompt(): string {
|
|
109
|
+
if (this._resolvedSystemPrompt !== undefined) {
|
|
110
|
+
return this._resolvedSystemPrompt;
|
|
111
|
+
}
|
|
112
|
+
const resolved =
|
|
113
|
+
typeof this._systemPrompt === "function"
|
|
114
|
+
? this._systemPrompt()
|
|
115
|
+
: this._systemPrompt;
|
|
116
|
+
this._resolvedSystemPrompt = resolved;
|
|
117
|
+
return resolved;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private clearSystemPromptCache(): void {
|
|
121
|
+
this._resolvedSystemPrompt = undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
101
124
|
/**
|
|
102
125
|
* Cheap pre-check: returns whether the estimated token count exceeds
|
|
103
126
|
* the compaction threshold, along with the estimated token count so
|
|
@@ -106,19 +129,35 @@ export class ContextWindowManager {
|
|
|
106
129
|
*/
|
|
107
130
|
shouldCompact(messages: Message[]): ShouldCompactResult {
|
|
108
131
|
if (!this.config.enabled) return { needed: false, estimatedTokens: 0 };
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
try {
|
|
133
|
+
const estimated = estimatePromptTokens(messages, this.systemPrompt, {
|
|
134
|
+
providerName: this.provider.name,
|
|
135
|
+
});
|
|
136
|
+
const threshold = Math.floor(
|
|
137
|
+
this.config.maxInputTokens * this.config.compactThreshold,
|
|
138
|
+
);
|
|
139
|
+
return { needed: estimated >= threshold, estimatedTokens: estimated };
|
|
140
|
+
} finally {
|
|
141
|
+
this.clearSystemPromptCache();
|
|
142
|
+
}
|
|
116
143
|
}
|
|
117
144
|
|
|
118
145
|
async maybeCompact(
|
|
119
146
|
messages: Message[],
|
|
120
147
|
signal?: AbortSignal,
|
|
121
148
|
options?: ContextWindowCompactOptions,
|
|
149
|
+
): Promise<ContextWindowResult> {
|
|
150
|
+
try {
|
|
151
|
+
return await this._maybeCompact(messages, signal, options);
|
|
152
|
+
} finally {
|
|
153
|
+
this.clearSystemPromptCache();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private async _maybeCompact(
|
|
158
|
+
messages: Message[],
|
|
159
|
+
signal?: AbortSignal,
|
|
160
|
+
options?: ContextWindowCompactOptions,
|
|
122
161
|
): Promise<ContextWindowResult> {
|
|
123
162
|
const previousEstimatedInputTokens =
|
|
124
163
|
options?.precomputedEstimate ??
|
|
@@ -17,9 +17,6 @@ import {
|
|
|
17
17
|
// Constants
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
|
|
20
|
-
/** Maximum number of attachments the assistant may emit per turn. */
|
|
21
|
-
export const MAX_ASSISTANT_ATTACHMENTS = 5;
|
|
22
|
-
|
|
23
20
|
/** Maximum size in bytes for a single assistant attachment (20 MB). */
|
|
24
21
|
export const MAX_ASSISTANT_ATTACHMENT_BYTES = 20 * 1024 * 1024;
|
|
25
22
|
|
|
@@ -122,10 +119,9 @@ export interface ValidatedDrafts {
|
|
|
122
119
|
}
|
|
123
120
|
|
|
124
121
|
/**
|
|
125
|
-
* Enforce per-
|
|
122
|
+
* Enforce per-attachment size cap.
|
|
126
123
|
*
|
|
127
124
|
* - Rejects individual drafts that exceed `MAX_ASSISTANT_ATTACHMENT_BYTES`.
|
|
128
|
-
* - Truncates the list at `MAX_ASSISTANT_ATTACHMENTS`.
|
|
129
125
|
*/
|
|
130
126
|
export function validateDrafts(
|
|
131
127
|
drafts: AssistantAttachmentDraft[],
|
|
@@ -144,14 +140,6 @@ export function validateDrafts(
|
|
|
144
140
|
continue;
|
|
145
141
|
}
|
|
146
142
|
|
|
147
|
-
if (accepted.length >= MAX_ASSISTANT_ATTACHMENTS) {
|
|
148
|
-
warnings.push(
|
|
149
|
-
`Skipped attachment "${draft.filename}": ` +
|
|
150
|
-
`exceeded maximum of ${MAX_ASSISTANT_ATTACHMENTS} attachments per turn.`,
|
|
151
|
-
);
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
143
|
accepted.push(draft);
|
|
156
144
|
}
|
|
157
145
|
|
|
@@ -83,8 +83,8 @@ const FOLLOWUP_CONVERSATION_MAX_TOKENS = 300;
|
|
|
83
83
|
const FOLLOWUP_CONVERSATION_SYSTEM_PROMPT =
|
|
84
84
|
"You are an assistant helping route a guardian's reply to a post-timeout follow-up message. " +
|
|
85
85
|
"A voice caller asked a question, but the call timed out before the guardian could answer. " +
|
|
86
|
-
"The guardian has now replied late, and was asked whether they want to call the caller back
|
|
87
|
-
"
|
|
86
|
+
"The guardian has now replied late, and was asked whether they want to call the caller back " +
|
|
87
|
+
"or skip it. " +
|
|
88
88
|
"Analyze the guardian's latest reply to determine their intent. " +
|
|
89
89
|
"When uncertain, default to keep_pending and ask a clarifying question. " +
|
|
90
90
|
"Always provide a natural, helpful reply along with your decision.";
|
|
@@ -101,10 +101,10 @@ const FOLLOWUP_CONVERSATION_TOOL_SCHEMA = {
|
|
|
101
101
|
properties: {
|
|
102
102
|
disposition: {
|
|
103
103
|
type: "string",
|
|
104
|
-
enum: ["call_back", "
|
|
104
|
+
enum: ["call_back", "decline", "keep_pending"],
|
|
105
105
|
description:
|
|
106
106
|
"The guardian's intent: call_back to call the original caller, " +
|
|
107
|
-
"
|
|
107
|
+
"decline to skip the follow-up, " +
|
|
108
108
|
"keep_pending if the intent is unclear (ask for clarification).",
|
|
109
109
|
},
|
|
110
110
|
replyText: {
|
|
@@ -118,7 +118,6 @@ const FOLLOWUP_CONVERSATION_TOOL_SCHEMA = {
|
|
|
118
118
|
|
|
119
119
|
const VALID_FOLLOWUP_DISPOSITIONS: ReadonlySet<string> = new Set([
|
|
120
120
|
"call_back",
|
|
121
|
-
"message_back",
|
|
122
121
|
"decline",
|
|
123
122
|
"keep_pending",
|
|
124
123
|
]);
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
} from "../../calls/twilio-rest.js";
|
|
6
6
|
import {
|
|
7
7
|
getGatewayInternalBaseUrl,
|
|
8
|
-
getIngressPublicBaseUrl,
|
|
9
8
|
setIngressPublicBaseUrl,
|
|
10
9
|
} from "../../config/env.js";
|
|
11
10
|
import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
|
|
@@ -26,20 +25,6 @@ import {
|
|
|
26
25
|
log,
|
|
27
26
|
} from "./shared.js";
|
|
28
27
|
|
|
29
|
-
// Lazily capture the env-provided INGRESS_PUBLIC_BASE_URL on first access
|
|
30
|
-
// rather than at module load time. The daemon loads ~/.vellum/.env inside
|
|
31
|
-
// runDaemon() (see lifecycle.ts), which runs AFTER static ES module imports
|
|
32
|
-
// resolve. A module-level snapshot would miss dotenv-provided values.
|
|
33
|
-
let _originalIngressEnvCaptured = false;
|
|
34
|
-
let _originalIngressEnv: string | undefined;
|
|
35
|
-
function getOriginalIngressEnv(): string | undefined {
|
|
36
|
-
if (!_originalIngressEnvCaptured) {
|
|
37
|
-
_originalIngressEnv = getIngressPublicBaseUrl();
|
|
38
|
-
_originalIngressEnvCaptured = true;
|
|
39
|
-
}
|
|
40
|
-
return _originalIngressEnv;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
28
|
export function computeGatewayTarget(): string {
|
|
44
29
|
return getGatewayInternalBaseUrl();
|
|
45
30
|
}
|
|
@@ -108,13 +93,11 @@ export async function handleIngressConfig(
|
|
|
108
93
|
});
|
|
109
94
|
} else if (msg.action === "set") {
|
|
110
95
|
const value = (msg.publicBaseUrl ?? "").trim().replace(/\/+$/, "");
|
|
111
|
-
// Ensure we capture the original env value before any mutation below
|
|
112
|
-
getOriginalIngressEnv();
|
|
113
96
|
const raw = loadRawConfig();
|
|
114
97
|
|
|
115
98
|
// Update ingress.publicBaseUrl — this is the single source of truth for
|
|
116
|
-
// the canonical public ingress URL. The gateway
|
|
117
|
-
// the
|
|
99
|
+
// the canonical public ingress URL. The gateway reads this value from
|
|
100
|
+
// the workspace config file via ConfigFileCache.
|
|
118
101
|
// The gateway also validates Twilio signatures against forwarded public
|
|
119
102
|
// URL headers, so local tunnel updates generally apply without restarts.
|
|
120
103
|
const ingress = (raw?.ingress ?? {}) as Record<string, unknown>;
|
|
@@ -139,24 +122,17 @@ export async function handleIngressConfig(
|
|
|
139
122
|
CONFIG_RELOAD_DEBOUNCE_MS,
|
|
140
123
|
);
|
|
141
124
|
|
|
142
|
-
// Propagate to the
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
// gateway is restarted (e.g. by the self-upgrade skill or a manual
|
|
147
|
-
// `pkill -f gateway`).
|
|
148
|
-
// Only export the URL when ingress is enabled; clearing it when
|
|
125
|
+
// Propagate to module-level state so the assistant's in-process URL
|
|
126
|
+
// resolution stays in sync. The gateway reads from the workspace config
|
|
127
|
+
// file directly via ConfigFileCache, so no env var propagation is needed.
|
|
128
|
+
// Only set the URL when ingress is enabled; clearing it when
|
|
149
129
|
// disabled ensures the gateway stops accepting inbound webhooks.
|
|
150
130
|
const isEnabled = (ingress.enabled as boolean | undefined) ?? false;
|
|
151
131
|
if (value && isEnabled) {
|
|
152
132
|
setIngressPublicBaseUrl(value);
|
|
153
|
-
} else if (isEnabled && getOriginalIngressEnv() !== undefined) {
|
|
154
|
-
// Ingress is enabled but the user cleared the URL — fall back to the
|
|
155
|
-
// env var that was present when the process started.
|
|
156
|
-
setIngressPublicBaseUrl(getOriginalIngressEnv()!);
|
|
157
133
|
} else {
|
|
158
|
-
// Ingress is disabled or no URL is configured
|
|
159
|
-
//
|
|
134
|
+
// Ingress is disabled or no URL is configured — clear the module-level
|
|
135
|
+
// URL so the gateway stops accepting webhooks.
|
|
160
136
|
setIngressPublicBaseUrl(undefined);
|
|
161
137
|
}
|
|
162
138
|
|
|
@@ -250,4 +226,3 @@ export async function handleIngressConfig(
|
|
|
250
226
|
});
|
|
251
227
|
}
|
|
252
228
|
}
|
|
253
|
-
|
|
@@ -5,6 +5,12 @@ import {
|
|
|
5
5
|
saveRawConfig,
|
|
6
6
|
setNestedValue,
|
|
7
7
|
} from "../../config/loader.js";
|
|
8
|
+
import {
|
|
9
|
+
ensureManualTokenConnection,
|
|
10
|
+
removeManualTokenConnection,
|
|
11
|
+
} from "../../oauth/manual-token-connection.js";
|
|
12
|
+
import { getConnectionByProvider } from "../../oauth/oauth-store.js";
|
|
13
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
8
14
|
import {
|
|
9
15
|
deleteSecureKeyAsync,
|
|
10
16
|
getSecureKey,
|
|
@@ -34,14 +40,21 @@ export interface SlackChannelConfigResult {
|
|
|
34
40
|
// -- Business logic --
|
|
35
41
|
|
|
36
42
|
export function getSlackChannelConfig(): SlackChannelConfigResult {
|
|
37
|
-
const hasBotToken = !!getSecureKey(
|
|
38
|
-
|
|
43
|
+
const hasBotToken = !!getSecureKey(
|
|
44
|
+
credentialKey("slack_channel", "bot_token"),
|
|
45
|
+
);
|
|
46
|
+
const hasAppToken = !!getSecureKey(
|
|
47
|
+
credentialKey("slack_channel", "app_token"),
|
|
48
|
+
);
|
|
49
|
+
const conn = getConnectionByProvider("slack_channel");
|
|
50
|
+
const connected =
|
|
51
|
+
!!(conn && conn.status === "active") && hasBotToken && hasAppToken;
|
|
39
52
|
const { teamId, teamName, botUserId, botUsername } = getConfig().slack;
|
|
40
53
|
return {
|
|
41
54
|
success: true,
|
|
42
55
|
hasBotToken,
|
|
43
56
|
hasAppToken,
|
|
44
|
-
connected
|
|
57
|
+
connected,
|
|
45
58
|
...(teamId ? { teamId } : {}),
|
|
46
59
|
...(teamName ? { teamName } : {}),
|
|
47
60
|
...(botUserId ? { botUserId } : {}),
|
|
@@ -78,17 +91,13 @@ export async function setSlackChannelConfig(
|
|
|
78
91
|
user?: string;
|
|
79
92
|
};
|
|
80
93
|
if (!data.ok) {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
);
|
|
84
|
-
const storedAppToken = !!getSecureKey(
|
|
85
|
-
"credential:slack_channel:app_token",
|
|
86
|
-
);
|
|
94
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
95
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
87
96
|
return {
|
|
88
97
|
success: false,
|
|
89
|
-
hasBotToken:
|
|
90
|
-
hasAppToken:
|
|
91
|
-
connected:
|
|
98
|
+
hasBotToken: errConnected,
|
|
99
|
+
hasAppToken: errConnected,
|
|
100
|
+
connected: errConnected,
|
|
92
101
|
error: `Slack API validation failed: ${
|
|
93
102
|
data.error ?? "unknown error"
|
|
94
103
|
}`,
|
|
@@ -102,37 +111,29 @@ export async function setSlackChannelConfig(
|
|
|
102
111
|
};
|
|
103
112
|
} catch (err) {
|
|
104
113
|
const message = err instanceof Error ? err.message : String(err);
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
);
|
|
108
|
-
const storedAppToken = !!getSecureKey(
|
|
109
|
-
"credential:slack_channel:app_token",
|
|
110
|
-
);
|
|
114
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
115
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
111
116
|
return {
|
|
112
117
|
success: false,
|
|
113
|
-
hasBotToken:
|
|
114
|
-
hasAppToken:
|
|
115
|
-
connected:
|
|
118
|
+
hasBotToken: errConnected,
|
|
119
|
+
hasAppToken: errConnected,
|
|
120
|
+
connected: errConnected,
|
|
116
121
|
error: `Failed to validate bot token: ${message}`,
|
|
117
122
|
};
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
const stored = await setSecureKeyAsync(
|
|
121
|
-
"
|
|
126
|
+
credentialKey("slack_channel", "bot_token"),
|
|
122
127
|
botToken,
|
|
123
128
|
);
|
|
124
129
|
if (!stored) {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
);
|
|
128
|
-
const storedAppToken = !!getSecureKey(
|
|
129
|
-
"credential:slack_channel:app_token",
|
|
130
|
-
);
|
|
130
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
131
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
131
132
|
return {
|
|
132
133
|
success: false,
|
|
133
|
-
hasBotToken:
|
|
134
|
-
hasAppToken:
|
|
135
|
-
connected:
|
|
134
|
+
hasBotToken: errConnected,
|
|
135
|
+
hasAppToken: errConnected,
|
|
136
|
+
connected: errConnected,
|
|
136
137
|
error: "Failed to store bot token in secure storage",
|
|
137
138
|
};
|
|
138
139
|
}
|
|
@@ -160,37 +161,29 @@ export async function setSlackChannelConfig(
|
|
|
160
161
|
// Validate and store app token
|
|
161
162
|
if (appToken) {
|
|
162
163
|
if (!appToken.startsWith("xapp-")) {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
);
|
|
166
|
-
const storedAppToken = !!getSecureKey(
|
|
167
|
-
"credential:slack_channel:app_token",
|
|
168
|
-
);
|
|
164
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
165
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
169
166
|
return {
|
|
170
167
|
success: false,
|
|
171
|
-
hasBotToken:
|
|
172
|
-
hasAppToken:
|
|
173
|
-
connected:
|
|
168
|
+
hasBotToken: errConnected,
|
|
169
|
+
hasAppToken: errConnected,
|
|
170
|
+
connected: errConnected,
|
|
174
171
|
error: 'Invalid app token: must start with "xapp-"',
|
|
175
172
|
};
|
|
176
173
|
}
|
|
177
174
|
|
|
178
175
|
const stored = await setSecureKeyAsync(
|
|
179
|
-
"
|
|
176
|
+
credentialKey("slack_channel", "app_token"),
|
|
180
177
|
appToken,
|
|
181
178
|
);
|
|
182
179
|
if (!stored) {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
);
|
|
186
|
-
const storedAppToken = !!getSecureKey(
|
|
187
|
-
"credential:slack_channel:app_token",
|
|
188
|
-
);
|
|
180
|
+
const errConn = getConnectionByProvider("slack_channel");
|
|
181
|
+
const errConnected = !!(errConn && errConn.status === "active");
|
|
189
182
|
return {
|
|
190
183
|
success: false,
|
|
191
|
-
hasBotToken:
|
|
192
|
-
hasAppToken:
|
|
193
|
-
connected:
|
|
184
|
+
hasBotToken: errConnected,
|
|
185
|
+
hasAppToken: errConnected,
|
|
186
|
+
connected: errConnected,
|
|
194
187
|
error: "Failed to store app token in secure storage",
|
|
195
188
|
};
|
|
196
189
|
}
|
|
@@ -198,8 +191,12 @@ export async function setSlackChannelConfig(
|
|
|
198
191
|
upsertCredentialMetadata("slack_channel", "app_token", {});
|
|
199
192
|
}
|
|
200
193
|
|
|
201
|
-
const hasBotToken = !!getSecureKey(
|
|
202
|
-
|
|
194
|
+
const hasBotToken = !!getSecureKey(
|
|
195
|
+
credentialKey("slack_channel", "bot_token"),
|
|
196
|
+
);
|
|
197
|
+
const hasAppToken = !!getSecureKey(
|
|
198
|
+
credentialKey("slack_channel", "app_token"),
|
|
199
|
+
);
|
|
203
200
|
|
|
204
201
|
if (hasBotToken && !hasAppToken) {
|
|
205
202
|
warning =
|
|
@@ -209,6 +206,17 @@ export async function setSlackChannelConfig(
|
|
|
209
206
|
"App token stored but bot token is missing — connection incomplete.";
|
|
210
207
|
}
|
|
211
208
|
|
|
209
|
+
// Sync oauth_connection record so getConnectionByProvider("slack_channel")
|
|
210
|
+
// reflects the current credential state.
|
|
211
|
+
if (hasBotToken && hasAppToken) {
|
|
212
|
+
const accountInfo = metadata.teamName
|
|
213
|
+
? `${metadata.teamName}${metadata.botUsername ? ` (@${metadata.botUsername})` : ""}`
|
|
214
|
+
: undefined;
|
|
215
|
+
await ensureManualTokenConnection("slack_channel", accountInfo);
|
|
216
|
+
} else {
|
|
217
|
+
removeManualTokenConnection("slack_channel");
|
|
218
|
+
}
|
|
219
|
+
|
|
212
220
|
return {
|
|
213
221
|
success: true,
|
|
214
222
|
hasBotToken,
|
|
@@ -220,12 +228,21 @@ export async function setSlackChannelConfig(
|
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResult> {
|
|
223
|
-
const r1 = await deleteSecureKeyAsync(
|
|
224
|
-
|
|
231
|
+
const r1 = await deleteSecureKeyAsync(
|
|
232
|
+
credentialKey("slack_channel", "bot_token"),
|
|
233
|
+
);
|
|
234
|
+
const r2 = await deleteSecureKeyAsync(
|
|
235
|
+
credentialKey("slack_channel", "app_token"),
|
|
236
|
+
);
|
|
225
237
|
|
|
226
238
|
if (r1 === "error" || r2 === "error") {
|
|
227
|
-
|
|
228
|
-
const
|
|
239
|
+
// Check each key individually so partial deletions report accurate status.
|
|
240
|
+
const hasBotToken = !!getSecureKey(
|
|
241
|
+
credentialKey("slack_channel", "bot_token"),
|
|
242
|
+
);
|
|
243
|
+
const hasAppToken = !!getSecureKey(
|
|
244
|
+
credentialKey("slack_channel", "app_token"),
|
|
245
|
+
);
|
|
229
246
|
return {
|
|
230
247
|
success: false,
|
|
231
248
|
hasBotToken,
|
|
@@ -238,6 +255,9 @@ export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResul
|
|
|
238
255
|
deleteCredentialMetadata("slack_channel", "bot_token");
|
|
239
256
|
deleteCredentialMetadata("slack_channel", "app_token");
|
|
240
257
|
|
|
258
|
+
// Remove the oauth_connection row so getConnectionByProvider returns undefined.
|
|
259
|
+
removeManualTokenConnection("slack_channel");
|
|
260
|
+
|
|
241
261
|
const raw = loadRawConfig();
|
|
242
262
|
setNestedValue(raw, "slack.teamId", "");
|
|
243
263
|
setNestedValue(raw, "slack.teamName", "");
|