@vellumai/assistant 0.12.1 → 0.12.2-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +360 -51
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +596 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +168 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/ui-channel-variants.test.ts +1 -56
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/claude-token-refresh.ts +150 -0
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/prepare-agent-env.ts +21 -6
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +120 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +56 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +155 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +92 -72
- package/src/calls/voice-triage-escalate.ts +178 -59
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/oauth/status.ts +5 -0
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +29 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +33 -13
- package/src/config/loader.ts +2 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +20 -7
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +45 -59
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +224 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/protocol.test.ts +39 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +79 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +297 -699
- package/src/live-voice/protocol.ts +60 -2
- package/src/live-voice/session-controls.ts +111 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +314 -27
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +90 -2
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +20 -14
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +50 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/ui-surface/channel-variants.ts +10 -59
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-retro.ts +6 -8
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-owned MCP servers in `$VELLUM_WORKSPACE_DIR/mcp.json`.
|
|
3
|
+
*
|
|
4
|
+
* The file is an Agent Plugins 1.0.0 document (`$schema` + `mcpServers`).
|
|
5
|
+
* This module is the only reader and writer of that file. Presence of a
|
|
6
|
+
* server starts it. Headers are never persisted here.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
existsSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
renameSync,
|
|
14
|
+
writeFileSync,
|
|
15
|
+
} from "node:fs";
|
|
16
|
+
import { dirname } from "node:path";
|
|
17
|
+
|
|
18
|
+
import type { McpConfig, McpServerConfig } from "../config/schemas/mcp.js";
|
|
19
|
+
import { McpConfigSchema } from "../config/schemas/mcp.js";
|
|
20
|
+
import { getLogger } from "../util/logger.js";
|
|
21
|
+
import { getWorkspaceMcpConfigPath } from "../util/platform.js";
|
|
22
|
+
import {
|
|
23
|
+
AGENT_PLUGINS_MCP_SCHEMA_URL,
|
|
24
|
+
projectSpecServerToTransport,
|
|
25
|
+
projectTransportToSpecServer,
|
|
26
|
+
SpecMcpDocumentSchema,
|
|
27
|
+
SpecMcpServerSchema,
|
|
28
|
+
} from "./spec-schema.js";
|
|
29
|
+
|
|
30
|
+
const log = getLogger("workspace-mcp-config");
|
|
31
|
+
|
|
32
|
+
export type WorkspaceMcpFileState = "missing" | "ok" | "invalid";
|
|
33
|
+
|
|
34
|
+
export interface WorkspaceMcpLoadResult {
|
|
35
|
+
readonly config: McpConfig;
|
|
36
|
+
readonly issues: readonly string[];
|
|
37
|
+
readonly fileState: WorkspaceMcpFileState;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Read `/workspace/mcp.json` and project it onto the in-memory MCP config.
|
|
42
|
+
*
|
|
43
|
+
* A missing file is an empty server map. An invalid file disables the
|
|
44
|
+
* workspace half only (plugin servers still load) and is reported so a
|
|
45
|
+
* write can refuse to overwrite it.
|
|
46
|
+
*/
|
|
47
|
+
export function readWorkspaceMcpFile(
|
|
48
|
+
path: string = getWorkspaceMcpConfigPath(),
|
|
49
|
+
): WorkspaceMcpLoadResult {
|
|
50
|
+
if (!existsSync(path)) {
|
|
51
|
+
return {
|
|
52
|
+
config: McpConfigSchema.parse({}),
|
|
53
|
+
issues: [],
|
|
54
|
+
fileState: "missing",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let raw: string;
|
|
59
|
+
try {
|
|
60
|
+
raw = readFileSync(path, "utf8");
|
|
61
|
+
} catch (err) {
|
|
62
|
+
const message = `mcp.json unreadable: ${err instanceof Error ? err.message : String(err)}`;
|
|
63
|
+
return {
|
|
64
|
+
config: McpConfigSchema.parse({}),
|
|
65
|
+
issues: [message],
|
|
66
|
+
fileState: "invalid",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let json: unknown;
|
|
71
|
+
try {
|
|
72
|
+
json = JSON.parse(raw);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
const message = `mcp.json invalid JSON: ${err instanceof Error ? err.message : String(err)}`;
|
|
75
|
+
return {
|
|
76
|
+
config: McpConfigSchema.parse({}),
|
|
77
|
+
issues: [message],
|
|
78
|
+
fileState: "invalid",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const document = SpecMcpDocumentSchema.safeParse(json);
|
|
83
|
+
if (!document.success) {
|
|
84
|
+
return {
|
|
85
|
+
config: McpConfigSchema.parse({}),
|
|
86
|
+
issues: ['mcp.json is missing a valid "mcpServers" object'],
|
|
87
|
+
fileState: "invalid",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const servers: Record<string, McpServerConfig> = {};
|
|
92
|
+
const issues: string[] = [];
|
|
93
|
+
|
|
94
|
+
for (const [serverKey, rawEntry] of Object.entries(document.data.mcpServers)) {
|
|
95
|
+
if (serverKey.trim().length === 0) {
|
|
96
|
+
issues.push("server key must be a non-empty string; skipping");
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const entry = SpecMcpServerSchema.safeParse(rawEntry);
|
|
101
|
+
if (!entry.success) {
|
|
102
|
+
issues.push(
|
|
103
|
+
`invalid server entry "${serverKey}": ${entry.error.issues
|
|
104
|
+
.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`)
|
|
105
|
+
.join("; ")}`,
|
|
106
|
+
);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (entry.data.type === "stdio" && entry.data.cwd) {
|
|
111
|
+
issues.push(
|
|
112
|
+
`"${serverKey}": "cwd" is not supported by this host and was ignored; the server runs in the assistant's working directory`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
servers[serverKey] = {
|
|
117
|
+
transport: projectSpecServerToTransport(entry.data),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
config: { servers },
|
|
123
|
+
issues,
|
|
124
|
+
fileState: "ok",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Load the workspace MCP config, logging any declaration problems. */
|
|
129
|
+
export function loadWorkspaceMcpConfig(): McpConfig {
|
|
130
|
+
const result = readWorkspaceMcpFile();
|
|
131
|
+
for (const issue of result.issues) {
|
|
132
|
+
log.warn(issue);
|
|
133
|
+
}
|
|
134
|
+
return result.config;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Refuse a write when `mcp.json` exists but is not a usable document, so a
|
|
139
|
+
* later add/remove cannot clobber a hand-edit the user still means to fix.
|
|
140
|
+
*/
|
|
141
|
+
export function assertWorkspaceMcpWritable(): void {
|
|
142
|
+
const result = readWorkspaceMcpFile();
|
|
143
|
+
if (result.fileState === "invalid") {
|
|
144
|
+
throw new Error(result.issues[0] ?? "mcp.json is not a valid document");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Persist the in-memory workspace MCP config as a spec-pure document. */
|
|
149
|
+
export function saveWorkspaceMcpConfig(config: McpConfig): void {
|
|
150
|
+
assertWorkspaceMcpWritable();
|
|
151
|
+
const path = getWorkspaceMcpConfigPath();
|
|
152
|
+
const mcpServers: Record<string, unknown> = {};
|
|
153
|
+
for (const [id, server] of Object.entries(config.servers)) {
|
|
154
|
+
mcpServers[id] = projectTransportToSpecServer(server.transport);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const document = {
|
|
158
|
+
$schema: AGENT_PLUGINS_MCP_SCHEMA_URL,
|
|
159
|
+
mcpServers,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
163
|
+
const tmpPath = `${path}.tmp`;
|
|
164
|
+
writeFileSync(tmpPath, JSON.stringify(document, null, 2) + "\n");
|
|
165
|
+
renameSync(tmpPath, path);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Replace `config.mcp` on a settings/export payload with the projected
|
|
170
|
+
* workspace servers (transport only, headers stripped). Old clients that
|
|
171
|
+
* still read `mcp.servers` from `/v1/config` keep seeing the same shape.
|
|
172
|
+
*/
|
|
173
|
+
export function overlayWorkspaceMcpForConfigRead(config: unknown): void {
|
|
174
|
+
if (config === null || typeof config !== "object" || Array.isArray(config)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const root = config as Record<string, unknown>;
|
|
178
|
+
const { servers } = loadWorkspaceMcpConfig();
|
|
179
|
+
const projected: Record<string, unknown> = {};
|
|
180
|
+
for (const [id, server] of Object.entries(servers)) {
|
|
181
|
+
const { headers: _headers, ...safeTransport } = server.transport as Record<
|
|
182
|
+
string,
|
|
183
|
+
unknown
|
|
184
|
+
>;
|
|
185
|
+
projected[id] = { transport: safeTransport };
|
|
186
|
+
}
|
|
187
|
+
root.mcp = { servers: projected };
|
|
188
|
+
}
|
|
@@ -56,15 +56,22 @@ export interface MessagingProvider {
|
|
|
56
56
|
query: string,
|
|
57
57
|
options?: SearchOptions,
|
|
58
58
|
): Promise<SearchResult>;
|
|
59
|
-
|
|
59
|
+
|
|
60
|
+
// ── Optional operations (platforms implement what they support) ───
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Send through the provider's own API. A provider whose channel has a
|
|
64
|
+
* direct transport omits this: its sends go through the transport, which
|
|
65
|
+
* is the one send implementation per addressable channel, and the
|
|
66
|
+
* messaging tool routes there before it looks for this method.
|
|
67
|
+
*/
|
|
68
|
+
sendMessage?(
|
|
60
69
|
connection: OAuthConnection | undefined,
|
|
61
70
|
conversationId: string,
|
|
62
71
|
text: string,
|
|
63
72
|
options?: SendOptions,
|
|
64
73
|
): Promise<SendResult>;
|
|
65
74
|
|
|
66
|
-
// ── Optional operations (platforms implement what they support) ───
|
|
67
|
-
|
|
68
75
|
getThreadReplies?(
|
|
69
76
|
connection: OAuthConnection | undefined,
|
|
70
77
|
conversationId: string,
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
// The addressing capability resolves a named chat into exactly what each
|
|
3
|
+
// transport's own operations read: the callback URL `channelForCallback`
|
|
4
|
+
// resolves and the params `deliver` consumes. Resolution is local, so these
|
|
5
|
+
// assertions make no platform call; the tests import the transports'
|
|
6
|
+
// modules, which pull in their provider-API senders, so the senders'
|
|
7
|
+
// credential lookups are stubbed to keep the import inert.
|
|
8
|
+
import { mock } from "bun:test";
|
|
9
|
+
|
|
10
|
+
const actualSecureKeys = await import("../../../security/secure-keys.js");
|
|
11
|
+
mock.module("../../../security/secure-keys.js", () => ({
|
|
12
|
+
...actualSecureKeys,
|
|
13
|
+
getSecureKeyAsync: async () => null,
|
|
14
|
+
getSecureKeyResultAsync: async () => ({ value: null }),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const { channelForCallback } = await import("../callback-routing.js");
|
|
18
|
+
const { discordTransport } = await import("../discord/transport.js");
|
|
19
|
+
const { slackTransport } = await import("../slack/transport.js");
|
|
20
|
+
const { telegramTransport } = await import("../telegram-bot/transport.js");
|
|
21
|
+
const { whatsappTransport } = await import("../whatsapp/transport.js");
|
|
22
|
+
|
|
23
|
+
describe("addressFor resolves to the channel's own routing state", () => {
|
|
24
|
+
test("slack: a chat, optionally under a thread", () => {
|
|
25
|
+
const room = slackTransport.addressFor!({ kind: "chat", chatId: "C123" });
|
|
26
|
+
expect(room).toEqual({
|
|
27
|
+
ctx: { callbackUrl: "/deliver/slack", params: {} },
|
|
28
|
+
chatId: "C123",
|
|
29
|
+
});
|
|
30
|
+
const thread = slackTransport.addressFor!({
|
|
31
|
+
kind: "chat",
|
|
32
|
+
chatId: "C123",
|
|
33
|
+
threadId: "1690000000.000001",
|
|
34
|
+
});
|
|
35
|
+
expect(thread).toEqual({
|
|
36
|
+
ctx: {
|
|
37
|
+
callbackUrl: "/deliver/slack?threadTs=1690000000.000001",
|
|
38
|
+
params: { threadTs: "1690000000.000001" },
|
|
39
|
+
},
|
|
40
|
+
chatId: "C123",
|
|
41
|
+
threadId: "1690000000.000001",
|
|
42
|
+
});
|
|
43
|
+
expect(channelForCallback(thread!.ctx.callbackUrl)).toBe("slack");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("telegram: a chat with a topic; binds on send", () => {
|
|
47
|
+
expect(
|
|
48
|
+
telegramTransport.addressFor!({
|
|
49
|
+
kind: "chat",
|
|
50
|
+
chatId: "123",
|
|
51
|
+
threadId: "42",
|
|
52
|
+
}),
|
|
53
|
+
).toEqual({
|
|
54
|
+
ctx: {
|
|
55
|
+
callbackUrl: "/deliver/telegram?threadId=42",
|
|
56
|
+
params: { threadId: "42" },
|
|
57
|
+
},
|
|
58
|
+
chatId: "123",
|
|
59
|
+
threadId: "42",
|
|
60
|
+
});
|
|
61
|
+
expect(telegramTransport.bindsChatOnProactiveSend).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("discord: a chat with a thread, which is its own channel", () => {
|
|
65
|
+
const thread = discordTransport.addressFor!({
|
|
66
|
+
kind: "chat",
|
|
67
|
+
chatId: "C1",
|
|
68
|
+
threadId: "T1",
|
|
69
|
+
});
|
|
70
|
+
expect(thread).toEqual({
|
|
71
|
+
ctx: {
|
|
72
|
+
callbackUrl: "/deliver/discord?threadId=T1",
|
|
73
|
+
params: { threadId: "T1" },
|
|
74
|
+
},
|
|
75
|
+
chatId: "C1",
|
|
76
|
+
threadId: "T1",
|
|
77
|
+
});
|
|
78
|
+
expect(channelForCallback(thread!.ctx.callbackUrl)).toBe("discord");
|
|
79
|
+
expect(discordTransport.bindsChatOnProactiveSend).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("whatsapp: the number is the chat; no threads; binds on send", () => {
|
|
83
|
+
expect(
|
|
84
|
+
whatsappTransport.addressFor!({
|
|
85
|
+
kind: "chat",
|
|
86
|
+
chatId: "12125550100",
|
|
87
|
+
threadId: "ignored",
|
|
88
|
+
}),
|
|
89
|
+
).toEqual({
|
|
90
|
+
ctx: { callbackUrl: "/deliver/whatsapp", params: {} },
|
|
91
|
+
chatId: "12125550100",
|
|
92
|
+
});
|
|
93
|
+
expect(whatsappTransport.bindsChatOnProactiveSend).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChannelId } from "../../channels/types.js";
|
|
2
|
+
import type { CallbackContext } from "./channel-transport.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Channels whose outbound replies the assistant delivers directly to the
|
|
@@ -22,6 +23,23 @@ export type DirectDeliveryChannel = (typeof DIRECT_DELIVERY_CHANNELS)[number];
|
|
|
22
23
|
|
|
23
24
|
const CALLBACK_PREFIX = "/deliver/";
|
|
24
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The callback context a channel's operations read for a direct delivery
|
|
28
|
+
* that no inbound message brought a callback for: the channel's `/deliver`
|
|
29
|
+
* path with its per-channel params, spelled the way `channelForCallback`
|
|
30
|
+
* resolves it and the way the transports read it.
|
|
31
|
+
*/
|
|
32
|
+
export function directDeliveryContext(
|
|
33
|
+
channel: DirectDeliveryChannel,
|
|
34
|
+
params: Readonly<Record<string, string>> = {},
|
|
35
|
+
): CallbackContext {
|
|
36
|
+
const query = new URLSearchParams(params).toString();
|
|
37
|
+
return {
|
|
38
|
+
callbackUrl: `${CALLBACK_PREFIX}${channel}${query ? `?${query}` : ""}`,
|
|
39
|
+
params,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
25
43
|
/**
|
|
26
44
|
* Resolve a gateway callback URL to the direct-delivery channel that owns it, or
|
|
27
45
|
* `undefined` when no channel delivers it directly.
|
|
@@ -97,6 +97,35 @@ export interface ReactionTarget {
|
|
|
97
97
|
readonly action: "add" | "remove";
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Where a proactive send goes, in the caller's vocabulary rather than any
|
|
102
|
+
* channel's: a chat the caller already knows, optionally a thread within it.
|
|
103
|
+
* How a thread is spelled is each channel's own business.
|
|
104
|
+
*
|
|
105
|
+
* Tagged with its kind so a second way of naming a destination is additive
|
|
106
|
+
* rather than a reinterpretation of `chatId`. Reaching a person who has not
|
|
107
|
+
* been named as a chat is deliberately not a kind here: on Slack and Discord
|
|
108
|
+
* it costs a platform call whose result must be resolved at send time rather
|
|
109
|
+
* than stored, and nothing asks for it yet.
|
|
110
|
+
*/
|
|
111
|
+
export type ProactiveTarget = {
|
|
112
|
+
readonly kind: "chat";
|
|
113
|
+
readonly chatId: string;
|
|
114
|
+
readonly threadId?: string;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* A proactive target resolved into what this channel's operations read: the
|
|
119
|
+
* callback context carrying the channel's routing state, the chat id
|
|
120
|
+
* `deliver` posts to, and the thread the post lands in, absent when it is
|
|
121
|
+
* not in one.
|
|
122
|
+
*/
|
|
123
|
+
export interface ProactiveAddress {
|
|
124
|
+
readonly ctx: CallbackContext;
|
|
125
|
+
readonly chatId: string;
|
|
126
|
+
readonly threadId?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
100
129
|
/**
|
|
101
130
|
* Direct outbound delivery for one channel, wrapping the channel's provider-API
|
|
102
131
|
* send functions behind a uniform surface. Transports are registered statically
|
|
@@ -111,6 +140,33 @@ export interface ChannelTransport {
|
|
|
111
140
|
/** Canonical source channel id, e.g. `"slack"`. */
|
|
112
141
|
readonly channel: ChannelId;
|
|
113
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Resolve a target the caller names into this channel's own routing state,
|
|
145
|
+
* for a send that no inbound message brought a callback for.
|
|
146
|
+
*
|
|
147
|
+
* Implementing it is the whole of declaring that the channel can be
|
|
148
|
+
* addressed from a named chat. A transport that can only answer on the
|
|
149
|
+
* callback an inbound message carried omits it, and a caller reads the
|
|
150
|
+
* omission as "not addressable" rather than switching on the channel's
|
|
151
|
+
* name. Returning `undefined` for a target this channel cannot address is
|
|
152
|
+
* the same answer for that target alone.
|
|
153
|
+
*
|
|
154
|
+
* Resolution is local: it reads the target and the channel's own
|
|
155
|
+
* vocabulary, and makes no platform call. A destination that has to be
|
|
156
|
+
* resolved over the network belongs behind its own capability, so this one
|
|
157
|
+
* stays cheap enough to call on every send.
|
|
158
|
+
*/
|
|
159
|
+
addressFor?(target: ProactiveTarget): ProactiveAddress | undefined;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Whether a proactive send to a chat must also bind that chat's inbound
|
|
163
|
+
* conversation, so the next message from the chat resolves to the same
|
|
164
|
+
* conversation the post lives in. Declared by a channel whose inbound
|
|
165
|
+
* conversations are keyed per chat and can be reset between sends; omitted
|
|
166
|
+
* by one whose inbound binding is made at ingress and keyed by thread.
|
|
167
|
+
*/
|
|
168
|
+
readonly bindsChatOnProactiveSend?: boolean;
|
|
169
|
+
|
|
114
170
|
/** Deliver a rendered reply (text / approval / attachments). */
|
|
115
171
|
deliver(
|
|
116
172
|
ctx: CallbackContext,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ChannelDeliveryError } from "@vellumai/gateway-client/http-delivery";
|
|
2
2
|
|
|
3
3
|
import { getLogger } from "../../../util/logger.js";
|
|
4
|
+
import { directDeliveryContext } from "../callback-routing.js";
|
|
4
5
|
import type {
|
|
5
6
|
CallbackContext,
|
|
6
7
|
ChannelTransport,
|
|
@@ -51,6 +52,22 @@ async function sendTarget(
|
|
|
51
52
|
export const discordTransport: ChannelTransport = {
|
|
52
53
|
channel: "discord",
|
|
53
54
|
|
|
55
|
+
/**
|
|
56
|
+
* A chat is a channel id, with `threadId` naming the thread, which is its
|
|
57
|
+
* own channel. Reaching a person who has not been named as a chat would
|
|
58
|
+
* mean opening their DM first, a platform call this resolution does not
|
|
59
|
+
* make; the inbound `dm` param below is how an event-carried callback says
|
|
60
|
+
* its `chatId` is a recipient rather than a room.
|
|
61
|
+
*/
|
|
62
|
+
addressFor(target) {
|
|
63
|
+
const threadId = target.threadId?.trim();
|
|
64
|
+
return {
|
|
65
|
+
ctx: directDeliveryContext("discord", threadId ? { threadId } : {}),
|
|
66
|
+
chatId: target.chatId,
|
|
67
|
+
...(threadId ? { threadId } : {}),
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
|
|
54
71
|
// Discord clears a typing indicator after ten seconds.
|
|
55
72
|
activityRefreshMs: 8_000,
|
|
56
73
|
|
|
@@ -162,10 +162,11 @@ describe("Slack adapter token routing", () => {
|
|
|
162
162
|
globalThis.fetch = originalFetch;
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
test("bot-token only: reads
|
|
165
|
+
test("bot-token only: reads authenticate with the bot token (regression guard for pre-user-token behavior)", async () => {
|
|
166
166
|
// With only a bot token stored, reads must fall back to the bot token
|
|
167
167
|
// so the adapter keeps working for installs that haven't re-consented
|
|
168
|
-
// the user scope. Writes
|
|
168
|
+
// the user scope. Writes are not the adapter's: they go through the
|
|
169
|
+
// channel transport's senders, which always post as the bot.
|
|
169
170
|
const resolved = await slackProvider.resolveConnection!();
|
|
170
171
|
expect(resolved).toBeUndefined();
|
|
171
172
|
|
|
@@ -176,18 +177,12 @@ describe("Slack adapter token routing", () => {
|
|
|
176
177
|
);
|
|
177
178
|
expect(readCall).toBeDefined();
|
|
178
179
|
expect(readCall!.authorization).toBe(`Bearer ${BOT_TOKEN}`);
|
|
179
|
-
|
|
180
|
-
// Write path: sendMessage → /chat.postMessage must also use bot token.
|
|
181
|
-
await slackProvider.sendMessage(undefined, "C123", "hello");
|
|
182
|
-
const writeCall = captured.find((c) => c.url.includes("/chat.postMessage"));
|
|
183
|
-
expect(writeCall).toBeDefined();
|
|
184
|
-
expect(writeCall!.authorization).toBe(`Bearer ${BOT_TOKEN}`);
|
|
185
180
|
});
|
|
186
181
|
|
|
187
|
-
test("bot + user tokens: reads authenticate with the user token
|
|
182
|
+
test("bot + user tokens: reads authenticate with the user token", async () => {
|
|
188
183
|
// With both tokens stored, reads MUST flip to the user token so the
|
|
189
|
-
// adapter can see channels the user is in but the bot isn't.
|
|
190
|
-
//
|
|
184
|
+
// adapter can see channels the user is in but the bot isn't. Posts are
|
|
185
|
+
// the transport's and come from the bot identity there.
|
|
191
186
|
getSecureKeyAsyncMock.mockImplementation(async (key: string) => {
|
|
192
187
|
if (key === credentialKey("slack_channel", "bot_token")) {
|
|
193
188
|
return BOT_TOKEN;
|
|
@@ -217,12 +212,6 @@ describe("Slack adapter token routing", () => {
|
|
|
217
212
|
expect(historyCall).toBeDefined();
|
|
218
213
|
expect(historyCall!.authorization).toBe(`Bearer ${USER_TOKEN}`);
|
|
219
214
|
|
|
220
|
-
// Writes: sendMessage → bot token.
|
|
221
|
-
await slackProvider.sendMessage(undefined, "C123", "hello");
|
|
222
|
-
const sendCall = captured.find((c) => c.url.includes("/chat.postMessage"));
|
|
223
|
-
expect(sendCall).toBeDefined();
|
|
224
|
-
expect(sendCall!.authorization).toBe(`Bearer ${BOT_TOKEN}`);
|
|
225
|
-
|
|
226
215
|
// markRead → user token. conversations.mark sets the read cursor for
|
|
227
216
|
// the authenticated identity, so it must use the same token whose unread
|
|
228
217
|
// counts the adapter exposes.
|
|
@@ -28,8 +28,6 @@ import type {
|
|
|
28
28
|
Message,
|
|
29
29
|
SearchOptions,
|
|
30
30
|
SearchResult,
|
|
31
|
-
SendOptions,
|
|
32
|
-
SendResult,
|
|
33
31
|
} from "../../provider-types.js";
|
|
34
32
|
import { resolveSlackAuth } from "./auth.js";
|
|
35
33
|
import * as slack from "./client.js";
|
|
@@ -144,24 +142,12 @@ function getReadAuth(connection?: OAuthConnection): OAuthConnection | string {
|
|
|
144
142
|
return getSlackAuth(connection);
|
|
145
143
|
}
|
|
146
144
|
|
|
147
|
-
// SAFETY: content-creating writes (
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
// identity's own state (e.g. conversations.mark)
|
|
151
|
-
// the cursor matches the perspective the adapter
|
|
152
|
-
|
|
153
|
-
* Resolve auth for content-creating write operations (postMessage and any
|
|
154
|
-
* future reactions, joins, leaves, updates, or deletes).
|
|
155
|
-
*/
|
|
156
|
-
function getWriteAuth(connection?: OAuthConnection): OAuthConnection | string {
|
|
157
|
-
if (connection) {
|
|
158
|
-
return connection;
|
|
159
|
-
}
|
|
160
|
-
if (_cachedSlackWriteAuth) {
|
|
161
|
-
return _cachedSlackWriteAuth;
|
|
162
|
-
}
|
|
163
|
-
return getSlackAuth(connection);
|
|
164
|
-
}
|
|
145
|
+
// SAFETY: content-creating writes (posts, updates, deletes, reactions) go
|
|
146
|
+
// through the channel transport's senders, which authenticate with the bot
|
|
147
|
+
// token so posts come from the bot identity. State-changing methods that
|
|
148
|
+
// target the authenticated identity's own state (e.g. conversations.mark)
|
|
149
|
+
// use the read auth so the cursor matches the perspective the adapter
|
|
150
|
+
// exposes.
|
|
165
151
|
|
|
166
152
|
/**
|
|
167
153
|
* Resolve the bot token (raw string) and pass it to `fn`. Returns the
|
|
@@ -171,7 +157,7 @@ function getWriteAuth(connection?: OAuthConnection): OAuthConnection | string {
|
|
|
171
157
|
* (`OAuthConnection.withToken`) for callers that need a raw token to hand
|
|
172
158
|
* to a non-Slack-client API call — currently `downloadSlackFile` for inline
|
|
173
159
|
* file/image fetches. Slack-client method calls should keep going through
|
|
174
|
-
* `getReadAuth`
|
|
160
|
+
* `getReadAuth` and pass the union through.
|
|
175
161
|
*/
|
|
176
162
|
export async function withSlackBotToken<T>(
|
|
177
163
|
account: string | undefined,
|
|
@@ -830,29 +816,6 @@ export const slackProvider: MessagingProvider = {
|
|
|
830
816
|
};
|
|
831
817
|
},
|
|
832
818
|
|
|
833
|
-
async sendMessage(
|
|
834
|
-
connection: OAuthConnection | undefined,
|
|
835
|
-
conversationId: string,
|
|
836
|
-
text: string,
|
|
837
|
-
options?: SendOptions,
|
|
838
|
-
): Promise<SendResult> {
|
|
839
|
-
const auth = getWriteAuth(connection);
|
|
840
|
-
const resp = await slack.postMessage(
|
|
841
|
-
auth,
|
|
842
|
-
conversationId,
|
|
843
|
-
text,
|
|
844
|
-
options?.threadId,
|
|
845
|
-
);
|
|
846
|
-
// The thread reported is the one the post landed in, which is the one
|
|
847
|
-
// requested, since chat.postMessage threads exactly where it is told.
|
|
848
|
-
return {
|
|
849
|
-
id: resp.ts,
|
|
850
|
-
timestamp: parseFloat(resp.ts) * 1000,
|
|
851
|
-
conversationId: resp.channel,
|
|
852
|
-
...(options?.threadId ? { threadId: options.threadId } : {}),
|
|
853
|
-
};
|
|
854
|
-
},
|
|
855
|
-
|
|
856
819
|
async getThreadReplies(
|
|
857
820
|
connection: OAuthConnection | undefined,
|
|
858
821
|
conversationId: string,
|
|
@@ -3,6 +3,7 @@ import { ChannelDeliveryError } from "@vellumai/gateway-client/http-delivery";
|
|
|
3
3
|
|
|
4
4
|
import { extractThreadTsFromCallbackUrl } from "../../../channels/slack-callback-url.js";
|
|
5
5
|
import { getLogger } from "../../../util/logger.js";
|
|
6
|
+
import { directDeliveryContext } from "../callback-routing.js";
|
|
6
7
|
import type { ChannelTransport } from "../channel-transport.js";
|
|
7
8
|
import { SLACK_STREAM_MARKDOWN_LIMIT } from "./api.js";
|
|
8
9
|
import {
|
|
@@ -25,6 +26,21 @@ function mutedBlocks(text: string): KnownBlock[] {
|
|
|
25
26
|
export const slackTransport: ChannelTransport = {
|
|
26
27
|
channel: "slack",
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* A chat is a channel or DM id, with `threadTs` naming the thread to post
|
|
31
|
+
* under. Reaching a person who has not been named as a chat would mean
|
|
32
|
+
* opening the DM first (`conversations.open`), a platform call this
|
|
33
|
+
* resolution does not make.
|
|
34
|
+
*/
|
|
35
|
+
addressFor(target) {
|
|
36
|
+
const threadTs = target.threadId?.trim();
|
|
37
|
+
return {
|
|
38
|
+
ctx: directDeliveryContext("slack", threadTs ? { threadTs } : {}),
|
|
39
|
+
chatId: target.chatId,
|
|
40
|
+
...(threadTs ? { threadId: threadTs } : {}),
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
|
|
28
44
|
describeReactionEmoji: describeSlackReactionEmoji,
|
|
29
45
|
|
|
30
46
|
async react(target) {
|
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
import type { OAuthConnection } from "../../../oauth/connection.js";
|
|
8
8
|
import { getConnectionByProvider } from "../../../oauth/oauth-store.js";
|
|
9
|
-
import { getOrCreateConversation } from "../../../persistence/conversation-key-store.js";
|
|
10
|
-
import { buildScopedConversationKey } from "../../../persistence/delivery-crud.js";
|
|
11
|
-
import { upsertOutboundBinding } from "../../../persistence/external-conversation-store.js";
|
|
12
9
|
import { credentialKey } from "../../../security/credential-key.js";
|
|
13
10
|
import { getSecureKeyAsync } from "../../../security/secure-keys.js";
|
|
14
11
|
import type { MessagingProvider } from "../../provider.js";
|
|
@@ -20,8 +17,6 @@ import type {
|
|
|
20
17
|
Message,
|
|
21
18
|
SearchOptions,
|
|
22
19
|
SearchResult,
|
|
23
|
-
SendOptions,
|
|
24
|
-
SendResult,
|
|
25
20
|
} from "../../provider-types.js";
|
|
26
21
|
import * as telegram from "./client.js";
|
|
27
22
|
|
|
@@ -34,7 +29,7 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
34
29
|
id: "telegram",
|
|
35
30
|
displayName: "Telegram",
|
|
36
31
|
credentialService: "telegram",
|
|
37
|
-
capabilities: new Set(
|
|
32
|
+
capabilities: new Set(),
|
|
38
33
|
|
|
39
34
|
async isConnected(): Promise<boolean> {
|
|
40
35
|
const conn = getConnectionByProvider("telegram");
|
|
@@ -95,48 +90,6 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
95
90
|
}
|
|
96
91
|
},
|
|
97
92
|
|
|
98
|
-
async sendMessage(
|
|
99
|
-
_connection: OAuthConnection | undefined,
|
|
100
|
-
conversationId: string,
|
|
101
|
-
text: string,
|
|
102
|
-
_options?: SendOptions,
|
|
103
|
-
): Promise<SendResult> {
|
|
104
|
-
const sent = await telegram.sendMessage(conversationId, text);
|
|
105
|
-
if (!sent.lastMessageId) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
"Telegram accepted the message but returned no message id",
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Upsert external conversation binding so deleted/reset syncs are
|
|
112
|
-
// resurrected when an outbound message is sent. This ensures the
|
|
113
|
-
// conversation key mapping and binding exist for the next inbound.
|
|
114
|
-
try {
|
|
115
|
-
const sourceChannel = "telegram";
|
|
116
|
-
const conversationKey = buildScopedConversationKey(
|
|
117
|
-
sourceChannel,
|
|
118
|
-
conversationId,
|
|
119
|
-
);
|
|
120
|
-
const { conversationId: internalId } =
|
|
121
|
-
getOrCreateConversation(conversationKey);
|
|
122
|
-
upsertOutboundBinding({
|
|
123
|
-
conversationId: internalId,
|
|
124
|
-
sourceChannel,
|
|
125
|
-
externalChatId: conversationId,
|
|
126
|
-
});
|
|
127
|
-
} catch {
|
|
128
|
-
// Best-effort — don't fail the send if binding upsert fails
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// The id is the one Telegram assigned, so a later reaction, edit, or
|
|
132
|
-
// delete carrying that id resolves to this send.
|
|
133
|
-
return {
|
|
134
|
-
id: sent.lastMessageId,
|
|
135
|
-
timestamp: Date.now(),
|
|
136
|
-
conversationId,
|
|
137
|
-
};
|
|
138
|
-
},
|
|
139
|
-
|
|
140
93
|
async listConversations(
|
|
141
94
|
_connection?: OAuthConnection,
|
|
142
95
|
_options?: ListOptions,
|