@vellumai/assistant 0.8.1 → 0.8.3
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 +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +642 -5
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +31 -65
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +59 -1
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +670 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +25 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +288 -11
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +74 -22
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +187 -42
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +40 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +98 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +17 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +107 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A direct delivery adapter.
|
|
3
|
+
*
|
|
4
|
+
* Completes an A2A task with response artifacts and optionally POSTs the
|
|
5
|
+
* completed task to the requester's push notification URL.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
ChannelDeliveryResult,
|
|
10
|
+
ChannelReplyPayload,
|
|
11
|
+
} from "@vellumai/gateway-client";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
A2A_CONTENT_TYPE,
|
|
15
|
+
A2A_VERSION,
|
|
16
|
+
A2A_VERSION_HEADER,
|
|
17
|
+
} from "../../../a2a/protocol-constants.js";
|
|
18
|
+
import type { Part } from "../../../a2a/protocol-types.js";
|
|
19
|
+
import * as taskStore from "../../../a2a/task-store.js";
|
|
20
|
+
import { getLogger } from "../../../util/logger.js";
|
|
21
|
+
import {
|
|
22
|
+
computeRetryDelay,
|
|
23
|
+
isRetryableStatus,
|
|
24
|
+
sleep,
|
|
25
|
+
} from "../../../util/retry.js";
|
|
26
|
+
|
|
27
|
+
const log = getLogger("a2a-deliver");
|
|
28
|
+
|
|
29
|
+
const MAX_RETRIES = 3;
|
|
30
|
+
const PUSH_TIMEOUT_MS = 15_000;
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
/** Extract the `taskId` query parameter from a callback URL. */
|
|
37
|
+
function parseTaskId(callbackUrl: string): string | null {
|
|
38
|
+
try {
|
|
39
|
+
return new URL(callbackUrl).searchParams.get("taskId");
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Build A2A parts from a channel reply payload. */
|
|
46
|
+
function buildParts(payload: ChannelReplyPayload): Part[] {
|
|
47
|
+
const parts: Part[] = [];
|
|
48
|
+
|
|
49
|
+
if (payload.text) {
|
|
50
|
+
parts.push({ kind: "text", text: payload.text });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (payload.attachments) {
|
|
54
|
+
for (const att of payload.attachments) {
|
|
55
|
+
parts.push({
|
|
56
|
+
kind: "file",
|
|
57
|
+
filename: att.filename,
|
|
58
|
+
media_type: att.mimeType,
|
|
59
|
+
url: att.data,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return parts;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** POST the completed task to the requester's push URL with retry. */
|
|
68
|
+
async function pushNotification(
|
|
69
|
+
pushUrl: string,
|
|
70
|
+
taskJson: unknown,
|
|
71
|
+
): Promise<void> {
|
|
72
|
+
let lastError: Error | null = null;
|
|
73
|
+
|
|
74
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
75
|
+
if (attempt > 0) {
|
|
76
|
+
await sleep(computeRetryDelay(attempt - 1));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch(pushUrl, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: {
|
|
83
|
+
"Content-Type": A2A_CONTENT_TYPE,
|
|
84
|
+
[A2A_VERSION_HEADER]: A2A_VERSION,
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify(taskJson),
|
|
87
|
+
signal: AbortSignal.timeout(PUSH_TIMEOUT_MS),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (response.ok) return;
|
|
91
|
+
|
|
92
|
+
const body = await response.text().catch(() => "");
|
|
93
|
+
lastError = new Error(
|
|
94
|
+
`Push notification failed with status ${response.status}: ${body}`,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (!isRetryableStatus(response.status)) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Push failure is logged but doesn't propagate
|
|
106
|
+
log.warn(
|
|
107
|
+
{ pushUrl, error: lastError?.message },
|
|
108
|
+
"A2A push notification failed after retries",
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Public API
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
/** Deliver an assistant reply as an A2A task completion. */
|
|
117
|
+
export async function deliverA2AReply(
|
|
118
|
+
callbackUrl: string,
|
|
119
|
+
payload: ChannelReplyPayload,
|
|
120
|
+
): Promise<ChannelDeliveryResult> {
|
|
121
|
+
const taskId = parseTaskId(callbackUrl);
|
|
122
|
+
if (!taskId) {
|
|
123
|
+
return { ok: false };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const parts = buildParts(payload);
|
|
127
|
+
if (parts.length === 0) {
|
|
128
|
+
log.debug({ taskId }, "No content to deliver; skipping A2A completion");
|
|
129
|
+
return { ok: true };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let completedTask;
|
|
133
|
+
try {
|
|
134
|
+
completedTask = taskStore.completeWithArtifacts(taskId, [
|
|
135
|
+
{ artifact_id: crypto.randomUUID(), parts },
|
|
136
|
+
]);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
139
|
+
log.error({ taskId, error: message }, "Failed to complete A2A task");
|
|
140
|
+
return { ok: false };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Push notification — fire-and-forget
|
|
144
|
+
const pushUrl = taskStore.getPushUrl(taskId);
|
|
145
|
+
if (pushUrl) {
|
|
146
|
+
pushNotification(pushUrl, completedTask).catch((err) => {
|
|
147
|
+
log.error(
|
|
148
|
+
{ taskId, pushUrl, error: String(err) },
|
|
149
|
+
"Unexpected push notification error",
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
log.info({ taskId }, "A2A reply delivered");
|
|
155
|
+
return { ok: true };
|
|
156
|
+
}
|
|
@@ -422,13 +422,16 @@ async function executeBatchCall(
|
|
|
422
422
|
/** Max concurrent individual getMessage requests (matches batch concurrency) */
|
|
423
423
|
const INDIVIDUAL_CONCURRENCY = BATCH_CONCURRENCY;
|
|
424
424
|
|
|
425
|
+
/** Delay between waves of individual fetches to avoid rate-limit storms (ms). */
|
|
426
|
+
const INTER_WAVE_DELAY_MS = 500;
|
|
427
|
+
|
|
425
428
|
/**
|
|
426
429
|
* Fetch all messages individually using getMessage (no batch endpoint).
|
|
427
430
|
* Used as a fallback when the batch API is unavailable (e.g. platform connections
|
|
428
431
|
* that cannot expose raw tokens for the multipart batch endpoint).
|
|
429
432
|
*
|
|
430
|
-
* Processes messages in waves of INDIVIDUAL_CONCURRENCY
|
|
431
|
-
*
|
|
433
|
+
* Processes messages in waves of INDIVIDUAL_CONCURRENCY with a brief inter-wave
|
|
434
|
+
* delay to avoid triggering upstream rate limits on high-volume paths.
|
|
432
435
|
*/
|
|
433
436
|
async function fetchMessagesIndividually(
|
|
434
437
|
connection: OAuthConnection,
|
|
@@ -441,6 +444,10 @@ async function fetchMessagesIndividually(
|
|
|
441
444
|
const results: GmailMessage[] = [];
|
|
442
445
|
for (let i = 0; i < messageIds.length; i += INDIVIDUAL_CONCURRENCY) {
|
|
443
446
|
signal?.throwIfAborted();
|
|
447
|
+
// Delay between waves (skip before the first wave)
|
|
448
|
+
if (i > 0) {
|
|
449
|
+
await signalAwareSleep(INTER_WAVE_DELAY_MS, signal);
|
|
450
|
+
}
|
|
444
451
|
const wave = messageIds.slice(i, i + INDIVIDUAL_CONCURRENCY);
|
|
445
452
|
const waveResults = await Promise.all(
|
|
446
453
|
wave.map((id) =>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* matcher and send logic here. The gateway-client consults
|
|
6
6
|
* `isDirectDelivery()` before falling back to the HTTP proxy path.
|
|
7
7
|
*
|
|
8
|
-
* Currently supported: WhatsApp, Telegram, Slack.
|
|
8
|
+
* Currently supported: WhatsApp, Telegram, Slack, A2A.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type {
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
import { ChannelDeliveryError } from "@vellumai/gateway-client/http-delivery";
|
|
16
16
|
|
|
17
17
|
import { getLogger } from "../../util/logger.js";
|
|
18
|
+
import { deliverA2AReply } from "./a2a/deliver.js";
|
|
18
19
|
import {
|
|
19
20
|
sendSlackAssistantThreadStatus,
|
|
20
21
|
sendSlackAttachments,
|
|
@@ -59,6 +60,10 @@ function isSlackCallback(callbackUrl: string): boolean {
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
function isA2ACallback(callbackUrl: string): boolean {
|
|
64
|
+
return matchesPathname(callbackUrl, "/deliver/a2a");
|
|
65
|
+
}
|
|
66
|
+
|
|
62
67
|
function parseSlackCallbackParams(callbackUrl: string): {
|
|
63
68
|
channel?: string;
|
|
64
69
|
threadTs?: string;
|
|
@@ -233,7 +238,8 @@ export function isDirectDelivery(callbackUrl: string): boolean {
|
|
|
233
238
|
return (
|
|
234
239
|
isWhatsAppCallback(callbackUrl) ||
|
|
235
240
|
isTelegramCallback(callbackUrl) ||
|
|
236
|
-
isSlackCallback(callbackUrl)
|
|
241
|
+
isSlackCallback(callbackUrl) ||
|
|
242
|
+
isA2ACallback(callbackUrl)
|
|
237
243
|
);
|
|
238
244
|
}
|
|
239
245
|
|
|
@@ -254,6 +260,9 @@ export async function deliverDirect(
|
|
|
254
260
|
if (isSlackCallback(callbackUrl)) {
|
|
255
261
|
return deliverSlack(callbackUrl, payload);
|
|
256
262
|
}
|
|
263
|
+
if (isA2ACallback(callbackUrl)) {
|
|
264
|
+
return deliverA2AReply(callbackUrl, payload);
|
|
265
|
+
}
|
|
257
266
|
|
|
258
267
|
// Defensive — isDirectDelivery should have returned false.
|
|
259
268
|
throw new Error(
|
|
@@ -24,13 +24,18 @@ mock.module("../../../../security/secure-keys.js", () => ({
|
|
|
24
24
|
}));
|
|
25
25
|
|
|
26
26
|
// OAuth helpers are exercised only when no bot_token is cached. The adapter
|
|
27
|
-
// imports them at module load
|
|
28
|
-
// OAuth fallback with a distinctive error so tests can assert on it.
|
|
27
|
+
// imports them at module load, so route them through a configurable stub.
|
|
29
28
|
const OAUTH_FALLBACK_SENTINEL = "OAUTH_FALLBACK_NOT_STUBBED";
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const resolveOAuthConnectionMock = mock(
|
|
30
|
+
async (
|
|
31
|
+
_provider: string,
|
|
32
|
+
_opts?: { account?: string },
|
|
33
|
+
): Promise<OAuthConnection> => {
|
|
32
34
|
throw new Error(OAUTH_FALLBACK_SENTINEL);
|
|
33
35
|
},
|
|
36
|
+
);
|
|
37
|
+
mock.module("../../../../oauth/connection-resolver.js", () => ({
|
|
38
|
+
resolveOAuthConnection: resolveOAuthConnectionMock,
|
|
34
39
|
}));
|
|
35
40
|
mock.module("../../../../oauth/oauth-store.js", () => ({
|
|
36
41
|
isProviderConnected: async () => false,
|
|
@@ -44,7 +49,7 @@ mock.module("../../../../contacts/contacts-write.js", () => ({
|
|
|
44
49
|
upsertContactChannel: () => {},
|
|
45
50
|
}));
|
|
46
51
|
|
|
47
|
-
import { slackProvider } from "../adapter.js";
|
|
52
|
+
import { slackProvider, withSlackBotToken } from "../adapter.js";
|
|
48
53
|
|
|
49
54
|
// ── fetch capture ───────────────────────────────────────────────────────────
|
|
50
55
|
|
|
@@ -108,9 +113,23 @@ function fakeSlackResponse(url: string): Record<string, unknown> {
|
|
|
108
113
|
const BOT_TOKEN = "xoxb-BOT";
|
|
109
114
|
const USER_TOKEN = "xoxp-USER";
|
|
110
115
|
|
|
116
|
+
function makeOAuthConnection(account: string, token: string): OAuthConnection {
|
|
117
|
+
return {
|
|
118
|
+
id: `conn-${account}`,
|
|
119
|
+
provider: "slack",
|
|
120
|
+
accountInfo: account,
|
|
121
|
+
request: async () => ({ status: 200, headers: {}, body: { ok: true } }),
|
|
122
|
+
withToken: async <T>(fn: (rawToken: string) => Promise<T>) => fn(token),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
111
126
|
describe("Slack adapter token routing", () => {
|
|
112
127
|
beforeEach(() => {
|
|
113
128
|
captured.length = 0;
|
|
129
|
+
resolveOAuthConnectionMock.mockReset();
|
|
130
|
+
resolveOAuthConnectionMock.mockImplementation(async () => {
|
|
131
|
+
throw new Error(OAUTH_FALLBACK_SENTINEL);
|
|
132
|
+
});
|
|
114
133
|
getSecureKeyAsyncMock.mockReset();
|
|
115
134
|
getSecureKeyAsyncMock.mockImplementation(async (key: string) => {
|
|
116
135
|
if (key === credentialKey("slack_channel", "bot_token")) return BOT_TOKEN;
|
|
@@ -279,4 +298,25 @@ describe("Slack adapter token routing", () => {
|
|
|
279
298
|
OAUTH_FALLBACK_SENTINEL,
|
|
280
299
|
);
|
|
281
300
|
});
|
|
301
|
+
|
|
302
|
+
test("raw bot token helper resolves the requested OAuth account even when cache is warm", async () => {
|
|
303
|
+
getSecureKeyAsyncMock.mockImplementation(async () => null);
|
|
304
|
+
resolveOAuthConnectionMock.mockImplementation(
|
|
305
|
+
async (_provider: string, opts?: { account?: string }) => {
|
|
306
|
+
const account = opts?.account ?? "default";
|
|
307
|
+
return makeOAuthConnection(account, `token-${account}`);
|
|
308
|
+
},
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
await slackProvider.resolveConnection!("workspace-a");
|
|
312
|
+
|
|
313
|
+
const result = await withSlackBotToken("workspace-b", async (token) => {
|
|
314
|
+
return token;
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
expect(result).toBe("token-workspace-b");
|
|
318
|
+
expect(resolveOAuthConnectionMock).toHaveBeenCalledWith("slack", {
|
|
319
|
+
account: "workspace-b",
|
|
320
|
+
});
|
|
321
|
+
});
|
|
282
322
|
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the assistant-side Slack file downloader used by the
|
|
3
|
+
* thread-backfill image-hydration path.
|
|
4
|
+
*
|
|
5
|
+
* The downloader has three contract-level behaviors worth pinning:
|
|
6
|
+
* 1. URL selection — `url_private_download` is preferred over `url_private`.
|
|
7
|
+
* 2. Bearer auth — the bot token MUST be sent on the initial request.
|
|
8
|
+
* 3. Manual cross-origin redirect handling — the CDN URL is signed and the
|
|
9
|
+
* Authorization header MUST NOT be re-sent on the second hop (Slack
|
|
10
|
+
* rejects the signed URL when an unexpected Authorization is present).
|
|
11
|
+
* 4. Returns null when no usable URL is present.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
15
|
+
|
|
16
|
+
mock.module("../../../../util/logger.js", () => ({
|
|
17
|
+
getLogger: () =>
|
|
18
|
+
new Proxy({} as Record<string, unknown>, {
|
|
19
|
+
get: () => () => {},
|
|
20
|
+
}),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
import { downloadSlackFile } from "../download.js";
|
|
24
|
+
|
|
25
|
+
interface CapturedFetchCall {
|
|
26
|
+
url: string;
|
|
27
|
+
init: RequestInit | undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let calls: CapturedFetchCall[];
|
|
31
|
+
let responses: Response[];
|
|
32
|
+
let originalFetch: typeof fetch;
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
calls = [];
|
|
36
|
+
responses = [];
|
|
37
|
+
originalFetch = globalThis.fetch;
|
|
38
|
+
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
39
|
+
calls.push({
|
|
40
|
+
url: typeof input === "string" ? input : input.toString(),
|
|
41
|
+
init,
|
|
42
|
+
});
|
|
43
|
+
const next = responses.shift();
|
|
44
|
+
if (!next) {
|
|
45
|
+
throw new Error("downloadSlackFile test: no canned response available");
|
|
46
|
+
}
|
|
47
|
+
return next;
|
|
48
|
+
}) as typeof fetch;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
globalThis.fetch = originalFetch;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("downloadSlackFile", () => {
|
|
56
|
+
test("returns null when neither url_private_download nor url_private is present", async () => {
|
|
57
|
+
const result = await downloadSlackFile(
|
|
58
|
+
{ name: "screenshot.png", mimetype: "image/png" },
|
|
59
|
+
"xoxb-test",
|
|
60
|
+
);
|
|
61
|
+
expect(result).toBeNull();
|
|
62
|
+
expect(calls.length).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("prefers url_private_download over url_private", async () => {
|
|
66
|
+
responses.push(
|
|
67
|
+
new Response(new Uint8Array([1, 2, 3]).buffer, {
|
|
68
|
+
status: 200,
|
|
69
|
+
headers: { "Content-Type": "image/png" },
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
await downloadSlackFile(
|
|
73
|
+
{
|
|
74
|
+
id: "F1",
|
|
75
|
+
name: "shot.png",
|
|
76
|
+
mimetype: "image/png",
|
|
77
|
+
urlPrivateDownload: "https://files.slack.com/files-pri/T/F1/download",
|
|
78
|
+
urlPrivate: "https://files.slack.com/files-pri/T/F1/inline",
|
|
79
|
+
},
|
|
80
|
+
"xoxb-test",
|
|
81
|
+
);
|
|
82
|
+
expect(calls.length).toBe(1);
|
|
83
|
+
expect(calls[0].url).toBe(
|
|
84
|
+
"https://files.slack.com/files-pri/T/F1/download",
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("sends bot token as Bearer on the initial request and base64-encodes the body", async () => {
|
|
89
|
+
responses.push(
|
|
90
|
+
new Response(new Uint8Array([0xde, 0xad, 0xbe, 0xef]).buffer, {
|
|
91
|
+
status: 200,
|
|
92
|
+
headers: { "Content-Type": "image/png" },
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
const result = await downloadSlackFile(
|
|
96
|
+
{
|
|
97
|
+
id: "F1",
|
|
98
|
+
name: "shot.png",
|
|
99
|
+
mimetype: "image/png",
|
|
100
|
+
urlPrivate: "https://files.slack.com/files-pri/T/F1/inline",
|
|
101
|
+
},
|
|
102
|
+
"xoxb-test-token",
|
|
103
|
+
);
|
|
104
|
+
expect(calls.length).toBe(1);
|
|
105
|
+
const auth = (calls[0].init?.headers as Record<string, string>)
|
|
106
|
+
?.Authorization;
|
|
107
|
+
expect(auth).toBe("Bearer xoxb-test-token");
|
|
108
|
+
expect(calls[0].init?.redirect).toBe("manual");
|
|
109
|
+
expect(result).not.toBeNull();
|
|
110
|
+
expect(result?.filename).toBe("shot.png");
|
|
111
|
+
expect(result?.mimeType).toBe("image/png");
|
|
112
|
+
// 0xdeadbeef → "3q2+7w==" in base64.
|
|
113
|
+
expect(result?.data).toBe("3q2+7w==");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("follows a 302 to the signed CDN URL without re-sending the bearer token", async () => {
|
|
117
|
+
responses.push(
|
|
118
|
+
new Response(null, {
|
|
119
|
+
status: 302,
|
|
120
|
+
headers: {
|
|
121
|
+
Location:
|
|
122
|
+
"https://files-edge.slack.com/files-tmb/T-F1-abc/cdn-signed?t=1700000000",
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
responses.push(
|
|
127
|
+
new Response(new Uint8Array([1, 2]).buffer, {
|
|
128
|
+
status: 200,
|
|
129
|
+
headers: { "Content-Type": "image/jpeg" },
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
const result = await downloadSlackFile(
|
|
133
|
+
{
|
|
134
|
+
id: "F1",
|
|
135
|
+
name: "photo.jpg",
|
|
136
|
+
mimetype: "image/jpeg",
|
|
137
|
+
urlPrivateDownload: "https://files.slack.com/files-pri/T/F1/download",
|
|
138
|
+
},
|
|
139
|
+
"xoxb-test",
|
|
140
|
+
);
|
|
141
|
+
expect(calls.length).toBe(2);
|
|
142
|
+
expect(calls[0].init?.redirect).toBe("manual");
|
|
143
|
+
const secondAuth = (calls[1].init?.headers as Record<string, string>)
|
|
144
|
+
?.Authorization;
|
|
145
|
+
expect(secondAuth).toBeUndefined();
|
|
146
|
+
expect(calls[1].url).toBe(
|
|
147
|
+
"https://files-edge.slack.com/files-tmb/T-F1-abc/cdn-signed?t=1700000000",
|
|
148
|
+
);
|
|
149
|
+
expect(result?.data).toBe(Buffer.from([1, 2]).toString("base64"));
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("resolves a relative Location header against the original URL", async () => {
|
|
153
|
+
responses.push(
|
|
154
|
+
new Response(null, {
|
|
155
|
+
status: 302,
|
|
156
|
+
headers: { Location: "/files-tmb/cdn-signed?t=1700" },
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
159
|
+
responses.push(
|
|
160
|
+
new Response(new Uint8Array([9]).buffer, {
|
|
161
|
+
status: 200,
|
|
162
|
+
headers: { "Content-Type": "image/png" },
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
await downloadSlackFile(
|
|
166
|
+
{
|
|
167
|
+
id: "F1",
|
|
168
|
+
name: "x.png",
|
|
169
|
+
urlPrivateDownload: "https://files.slack.com/a/b/download",
|
|
170
|
+
},
|
|
171
|
+
"xoxb-test",
|
|
172
|
+
);
|
|
173
|
+
expect(calls[1].url).toBe(
|
|
174
|
+
"https://files.slack.com/files-tmb/cdn-signed?t=1700",
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("throws when the second hop responds non-2xx", async () => {
|
|
179
|
+
responses.push(
|
|
180
|
+
new Response(null, {
|
|
181
|
+
status: 302,
|
|
182
|
+
headers: { Location: "https://files-edge.slack.com/cdn?t=1" },
|
|
183
|
+
}),
|
|
184
|
+
);
|
|
185
|
+
responses.push(
|
|
186
|
+
new Response(null, { status: 403, statusText: "Forbidden" }),
|
|
187
|
+
);
|
|
188
|
+
await expect(
|
|
189
|
+
downloadSlackFile(
|
|
190
|
+
{
|
|
191
|
+
id: "F1",
|
|
192
|
+
name: "x.png",
|
|
193
|
+
urlPrivateDownload: "https://files.slack.com/a/b/download",
|
|
194
|
+
},
|
|
195
|
+
"xoxb-test",
|
|
196
|
+
),
|
|
197
|
+
).rejects.toThrow(/403/);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("throws when a redirect has no Location header", async () => {
|
|
201
|
+
responses.push(new Response(null, { status: 302 }));
|
|
202
|
+
await expect(
|
|
203
|
+
downloadSlackFile(
|
|
204
|
+
{
|
|
205
|
+
id: "F1",
|
|
206
|
+
name: "x.png",
|
|
207
|
+
urlPrivateDownload: "https://files.slack.com/a/b/download",
|
|
208
|
+
},
|
|
209
|
+
"xoxb-test",
|
|
210
|
+
),
|
|
211
|
+
).rejects.toThrow(/no Location header/);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("falls back to response Content-Type when file.mimetype is absent", async () => {
|
|
215
|
+
responses.push(
|
|
216
|
+
new Response(new Uint8Array([1]).buffer, {
|
|
217
|
+
status: 200,
|
|
218
|
+
headers: { "Content-Type": "image/webp; charset=binary" },
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
const result = await downloadSlackFile(
|
|
222
|
+
{
|
|
223
|
+
id: "F1",
|
|
224
|
+
name: "photo.webp",
|
|
225
|
+
urlPrivate: "https://files.slack.com/files-pri/T/F1/inline",
|
|
226
|
+
},
|
|
227
|
+
"xoxb-test",
|
|
228
|
+
);
|
|
229
|
+
expect(result?.mimeType).toBe("image/webp");
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -99,6 +99,30 @@ function getWriteAuth(connection?: OAuthConnection): OAuthConnection | string {
|
|
|
99
99
|
return getSlackAuth(connection);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Resolve the bot token (raw string) and pass it to `fn`. Returns the
|
|
104
|
+
* callback's result, or `null` when no Slack auth is available.
|
|
105
|
+
*
|
|
106
|
+
* Bridges the Socket Mode case (cached string token) and the OAuth case
|
|
107
|
+
* (`OAuthConnection.withToken`) for callers that need a raw token to hand
|
|
108
|
+
* to a non-Slack-client API call — currently `downloadSlackFile` for inline
|
|
109
|
+
* file/image fetches. Slack-client method calls should keep going through
|
|
110
|
+
* `getReadAuth` / `getWriteAuth` and pass the union through.
|
|
111
|
+
*/
|
|
112
|
+
export async function withSlackBotToken<T>(
|
|
113
|
+
account: string | undefined,
|
|
114
|
+
fn: (token: string) => Promise<T>,
|
|
115
|
+
): Promise<T | null> {
|
|
116
|
+
// Resolve for this call's account even when the process cache is warm.
|
|
117
|
+
// Multi-workspace backfills can interleave, so use the returned connection
|
|
118
|
+
// directly instead of accepting any previously cached workspace token.
|
|
119
|
+
const resolvedAuth = await slackProvider.resolveConnection?.(account);
|
|
120
|
+
const auth = resolvedAuth ?? _cachedSlackWriteAuth;
|
|
121
|
+
if (!auth) return null;
|
|
122
|
+
if (typeof auth === "string") return fn(auth);
|
|
123
|
+
return auth.withToken(fn);
|
|
124
|
+
}
|
|
125
|
+
|
|
102
126
|
/**
|
|
103
127
|
* Run a read-path Slack call, falling back to the bot token if the cached
|
|
104
128
|
* user token is rejected with an auth error. On fallback, the read cache is
|
|
@@ -192,15 +216,31 @@ function mapConversation(conv: SlackConversation): Conversation {
|
|
|
192
216
|
};
|
|
193
217
|
}
|
|
194
218
|
|
|
195
|
-
function mapSlackFiles(
|
|
196
|
-
|
|
197
|
-
|
|
219
|
+
function mapSlackFiles(files: SlackMessage["files"]):
|
|
220
|
+
| Array<{
|
|
221
|
+
id?: string;
|
|
222
|
+
name: string;
|
|
223
|
+
mimetype?: string;
|
|
224
|
+
/**
|
|
225
|
+
* Transient — only present on the in-flight `ProviderMessage.metadata`.
|
|
226
|
+
* The persisted `slackFiles` shape carries `{ id, name, mimetype }` only
|
|
227
|
+
* (see `slackFileMetadataSchema`). Callers that hydrate image attachments
|
|
228
|
+
* during backfill rely on this URL; persistence strips it before write.
|
|
229
|
+
*/
|
|
230
|
+
urlPrivateDownload?: string;
|
|
231
|
+
urlPrivate?: string;
|
|
232
|
+
}>
|
|
233
|
+
| undefined {
|
|
198
234
|
if (!files || files.length === 0) return undefined;
|
|
199
235
|
const mapped = files
|
|
200
236
|
.map((file) => ({
|
|
201
237
|
...(file.id ? { id: file.id } : {}),
|
|
202
238
|
name: file.name,
|
|
203
239
|
...(file.mimetype ? { mimetype: file.mimetype } : {}),
|
|
240
|
+
...(file.url_private_download
|
|
241
|
+
? { urlPrivateDownload: file.url_private_download }
|
|
242
|
+
: {}),
|
|
243
|
+
...(file.url_private ? { urlPrivate: file.url_private } : {}),
|
|
204
244
|
}))
|
|
205
245
|
.filter((file) => file.name.length > 0);
|
|
206
246
|
return mapped.length > 0 ? mapped : undefined;
|
|
@@ -419,8 +459,6 @@ export const slackProvider: MessagingProvider = {
|
|
|
419
459
|
if (conv.type === "dm" && conv.metadata?.dmUserId) {
|
|
420
460
|
const dmUserId = conv.metadata.dmUserId as string;
|
|
421
461
|
conv.name = await resolveUserName(auth, dmUserId);
|
|
422
|
-
|
|
423
|
-
|
|
424
462
|
}
|
|
425
463
|
}
|
|
426
464
|
|
|
@@ -20,8 +20,10 @@ import type {
|
|
|
20
20
|
SlackConversationsListResponse,
|
|
21
21
|
SlackConversationsOpenResponse,
|
|
22
22
|
SlackPostMessageResponse,
|
|
23
|
+
SlackReactionsAddResponse,
|
|
23
24
|
SlackSearchMessagesResponse,
|
|
24
25
|
SlackUserInfoResponse,
|
|
26
|
+
SlackUsersListResponse,
|
|
25
27
|
} from "./types.js";
|
|
26
28
|
|
|
27
29
|
const SLACK_API_BASE = "https://slack.com/api";
|
|
@@ -432,3 +434,28 @@ export async function searchMessages(
|
|
|
432
434
|
},
|
|
433
435
|
);
|
|
434
436
|
}
|
|
437
|
+
|
|
438
|
+
export async function addReaction(
|
|
439
|
+
connectionOrToken: OAuthConnection | string,
|
|
440
|
+
channel: string,
|
|
441
|
+
timestamp: string,
|
|
442
|
+
name: string,
|
|
443
|
+
): Promise<SlackReactionsAddResponse> {
|
|
444
|
+
return request<SlackReactionsAddResponse>(
|
|
445
|
+
connectionOrToken,
|
|
446
|
+
"reactions.add",
|
|
447
|
+
undefined,
|
|
448
|
+
{ channel, timestamp, name },
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export async function listUsers(
|
|
453
|
+
connectionOrToken: OAuthConnection | string,
|
|
454
|
+
limit = 200,
|
|
455
|
+
cursor?: string,
|
|
456
|
+
): Promise<SlackUsersListResponse> {
|
|
457
|
+
return request<SlackUsersListResponse>(connectionOrToken, "users.list", {
|
|
458
|
+
limit: String(limit),
|
|
459
|
+
cursor,
|
|
460
|
+
});
|
|
461
|
+
}
|