@vellumai/assistant 0.11.1 → 0.11.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/Dockerfile +1 -3
- package/README.md +1 -1
- package/eslint.config.mjs +28 -8
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/slack-text/src/index.test.ts +48 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +25 -6
- package/openapi.yaml +292 -5
- package/package.json +1 -1
- package/src/__tests__/agent-loop-resume-interrupted.test.ts +223 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +43 -0
- package/src/__tests__/cli-logger-boundary-guard.test.ts +87 -0
- package/src/__tests__/compaction-events.test.ts +139 -2
- package/src/__tests__/config-loader-backfill.test.ts +35 -3
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +30 -0
- package/src/__tests__/conversation-agent-loop.test.ts +180 -7
- package/src/__tests__/conversation-error.test.ts +211 -4
- package/src/__tests__/conversation-options-turn-scoped-transport.test.ts +64 -0
- package/src/__tests__/conversation-queue.test.ts +126 -0
- package/src/__tests__/conversation-retry-route.test.ts +46 -7
- package/src/__tests__/conversation-slash.test.ts +4 -2
- package/src/__tests__/conversation-summarize-route.test.ts +7 -1
- package/src/__tests__/conversation-summarize-up-to.test.ts +71 -5
- package/src/__tests__/document-create-dedupe.test.ts +2 -1
- package/src/__tests__/document-find-replace.test.ts +2 -1
- package/src/__tests__/document-tool-security.test.ts +2 -1
- package/src/__tests__/document-update-default-surface.test.ts +2 -1
- package/src/__tests__/document-workspace-file.test.ts +467 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +290 -49
- package/src/__tests__/guardian-card-withdrawal.test.ts +91 -3
- package/src/__tests__/http-user-message-parity.test.ts +66 -0
- package/src/__tests__/list-messages-attachments.test.ts +87 -0
- package/src/__tests__/list-messages-provider-error.test.ts +143 -0
- package/src/__tests__/list-messages-system-card.test.ts +100 -0
- package/src/__tests__/llm-resolver.test.ts +19 -9
- package/src/__tests__/managed-profile-guard.test.ts +23 -0
- package/src/__tests__/notification-platform-adapter.test.ts +130 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +6 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +45 -0
- package/src/__tests__/plugin-api-model-profiles.test.ts +10 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +3 -0
- package/src/__tests__/provider-error-scenarios.test.ts +140 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +95 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +66 -3
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +209 -0
- package/src/__tests__/skills.test.ts +27 -0
- package/src/__tests__/slack-channels-routes.test.ts +0 -2
- package/src/__tests__/slack-share-routes.test.ts +0 -3
- package/src/__tests__/slack-users-routes.test.ts +0 -2
- package/src/__tests__/subagent-tools.test.ts +11 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +58 -0
- package/src/__tests__/tool-result-spool.test.ts +5 -4
- package/src/__tests__/turn-boundary-resolution.test.ts +53 -0
- package/src/__tests__/turn-events-store.test.ts +26 -0
- package/src/__tests__/ui-visual-surface.test.ts +695 -0
- package/src/__tests__/unified-turn-context-visible-app.test.ts +99 -0
- package/src/__tests__/visible-app-context.test.ts +189 -0
- package/src/__tests__/workspace-git-service.test.ts +173 -33
- package/src/__tests__/workspace-migration-137-repair-retired-fireworks-minimax-model-id.test.ts +157 -0
- package/src/__tests__/workspace-migration-138-backfill-home-feed-titles.test.ts +373 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +137 -0
- package/src/agent/loop.ts +36 -7
- package/src/api/events/context-window-usage.ts +31 -0
- package/src/api/events/notification-intent.ts +8 -0
- package/src/api/events/ui-surface-pending.ts +35 -0
- package/src/api/index.ts +14 -0
- package/src/api/responses/conversation-message.ts +25 -4
- package/src/api/surfaces.ts +90 -1
- package/src/approvals/guardian-card-withdrawal.ts +66 -31
- package/src/approvals/guardian-decision-primitive.ts +4 -0
- package/src/calls/__tests__/call-setup-router.test.ts +156 -26
- package/src/calls/__tests__/voice-session-bridge.test.ts +201 -12
- package/src/calls/call-setup-router.ts +80 -36
- package/src/calls/voice-session-bridge.ts +173 -26
- package/src/cli/commands/inference.help.ts +3 -3
- package/src/cli/commands/notifications.help.ts +3 -2
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +42 -128
- package/src/cli/commands/platform/__tests__/credits.test.ts +9 -78
- package/src/cli/commands/platform/__tests__/helpers.ts +90 -0
- package/src/cli/commands/platform/__tests__/invoices.test.ts +238 -0
- package/src/cli/commands/platform/__tests__/plans.test.ts +9 -88
- package/src/cli/commands/platform/__tests__/status.test.ts +12 -87
- package/src/cli/commands/platform/__tests__/subscription.test.ts +9 -86
- package/src/cli/commands/platform/index.help.ts +92 -0
- package/src/cli/commands/platform/index.ts +7 -0
- package/src/cli/commands/platform/invoices.ts +132 -0
- package/src/cli/commands/usage.help.ts +1 -1
- package/src/cli/lib/list-installed-plugins.ts +2 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +7 -10
- package/src/config/__tests__/deployment-context-defaults.test.ts +27 -5
- package/src/config/assistant-feature-flags.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -2
- package/src/config/bundled-skills/subagent/SKILL.md +3 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +3 -3
- package/src/config/bundled-skills/visualize/SKILL.md +163 -0
- package/src/config/call-site-defaults.ts +3 -2
- package/src/config/default-profile-catalog.ts +75 -70
- package/src/config/default-profile-names.ts +13 -28
- package/src/config/env-registry.ts +1 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/llm-resolver.ts +21 -5
- package/src/config/loader.ts +18 -11
- package/src/config/schemas/llm.ts +1 -9
- package/src/config/schemas/memory-retrospective.ts +9 -0
- package/src/config/schemas/monitoring.ts +28 -2
- package/src/config/schemas/workspace-git.ts +15 -0
- package/src/config/seed-inference-profiles.ts +19 -5
- package/src/context/post-turn-tool-result-truncation.ts +2 -2
- package/src/conversations/__tests__/message-consolidation.test.ts +54 -0
- package/src/conversations/message-consolidation.ts +17 -15
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +182 -0
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +181 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +90 -7
- package/src/daemon/conversation-agent-loop.ts +107 -22
- package/src/daemon/conversation-error.ts +169 -48
- package/src/daemon/conversation-messaging.ts +87 -4
- package/src/daemon/conversation-process.ts +57 -45
- package/src/daemon/conversation-runtime-assembly.ts +57 -0
- package/src/daemon/conversation-store.ts +36 -1
- package/src/daemon/conversation-surfaces.ts +68 -6
- package/src/daemon/conversation-turn-finalize.ts +76 -18
- package/src/daemon/conversation.ts +122 -27
- package/src/daemon/lifecycle.ts +9 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/surfaces.ts +3 -0
- package/src/documents/document-store.ts +247 -10
- package/src/home/__tests__/feed-types.test.ts +43 -8
- package/src/home/__tests__/feed-writer.test.ts +59 -0
- package/src/home/feed-types.ts +34 -7
- package/src/home/feed-writer.ts +15 -6
- package/src/live-voice/__tests__/activity-label.test.ts +95 -0
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +86 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +310 -25
- package/src/live-voice/__tests__/live-voice-events.test.ts +4 -4
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +8 -4
- package/src/live-voice/__tests__/live-voice-vad.test.ts +9 -1
- package/src/live-voice/activity-label.ts +169 -0
- package/src/live-voice/live-activity-reporter.ts +35 -6
- package/src/live-voice/live-voice-session.ts +282 -41
- package/src/live-voice/protocol.ts +35 -0
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +84 -2
- package/src/messaging/providers/slack/adapter.ts +86 -19
- package/src/messaging/providers/slack/api.test.ts +85 -1
- package/src/messaging/providers/slack/api.ts +121 -288
- package/src/messaging/providers/slack/client.ts +20 -251
- package/src/messaging/providers/slack/send.test.ts +4 -9
- package/src/messaging/providers/slack/send.ts +1 -1
- package/src/messaging/providers/slack/types.ts +5 -0
- package/src/messaging/providers/slack/web-api-transport.test.ts +181 -0
- package/src/messaging/providers/slack/web-api-transport.ts +367 -0
- package/src/messaging/providers/slack/withdraw.ts +6 -14
- package/src/messaging/providers/telegram-bot/send.test.ts +31 -1
- package/src/messaging/providers/telegram-bot/send.ts +23 -2
- package/src/messaging/providers/telegram-bot/withdraw.test.ts +152 -0
- package/src/messaging/providers/telegram-bot/withdraw.ts +166 -0
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +18 -4
- package/src/monitoring/__tests__/file-descriptors.test.ts +144 -0
- package/src/monitoring/file-descriptors.ts +262 -0
- package/src/monitoring/process-memory.ts +4 -11
- package/src/monitoring/resource-sampler.ts +5 -0
- package/src/monitoring/worker.ts +12 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +740 -0
- package/src/notifications/__tests__/broadcaster.test.ts +347 -7
- package/src/notifications/__tests__/copy-composer.test.ts +53 -3
- package/src/notifications/__tests__/decision-engine.test.ts +349 -29
- package/src/notifications/__tests__/deterministic-checks.test.ts +21 -0
- package/src/notifications/__tests__/edit-notification.test.ts +330 -0
- package/src/notifications/__tests__/guardian-delivery-recorder.test.ts +71 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +170 -12
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/adapters/platform.ts +90 -14
- package/src/notifications/adapters/telegram.ts +6 -4
- package/src/notifications/assistant-reply-producer.ts +219 -0
- package/src/notifications/broadcaster.ts +523 -317
- package/src/notifications/copy-composer.ts +27 -6
- package/src/notifications/decision-engine.ts +154 -77
- package/src/notifications/deterministic-checks.ts +6 -7
- package/src/notifications/edit-notification.ts +8 -4
- package/src/notifications/emit-signal.ts +49 -16
- package/src/notifications/guardian-delivery-recorder.ts +8 -2
- package/src/notifications/home-feed-side-effect.ts +43 -6
- package/src/notifications/notification-utils.ts +39 -4
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +17 -0
- package/src/persistence/bookmark-crud.ts +18 -9
- package/src/persistence/conversation-attention-store.ts +26 -0
- package/src/persistence/conversation-crud.ts +79 -32
- package/src/persistence/conversation-queries.ts +30 -15
- package/src/persistence/conversation-title-service.ts +5 -170
- package/src/persistence/conversation-types.ts +192 -1
- package/src/persistence/db-init.ts +14 -1
- package/src/persistence/db-maintenance.ts +5 -4
- package/src/persistence/embeddings/__tests__/plugin-index-qdrant-init.test.ts +219 -0
- package/src/persistence/embeddings/__tests__/plugin-index.test.ts +22 -0
- package/src/persistence/embeddings/__tests__/worker-script-version.test.ts +76 -0
- package/src/persistence/embeddings/embedding-local.ts +2 -1
- package/src/persistence/embeddings/embedding-runtime-manager.ts +29 -5
- package/src/persistence/embeddings/plugin-index.ts +84 -12
- package/src/persistence/migrations/360-add-document-workspace-path.test.ts +110 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +39 -0
- package/src/persistence/planner-statistics.ts +14 -0
- package/src/persistence/schema/documents.ts +26 -11
- package/src/persistence/steps.ts +2 -0
- package/src/platform/client.test.ts +172 -2
- package/src/platform/client.ts +178 -55
- package/src/plugin-api/conversation-turn.ts +13 -0
- package/src/plugin-api/model-profiles.test.ts +6 -2
- package/src/plugins/defaults/memory/__tests__/bookmark-crud.test.ts +34 -0
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +22 -0
- package/src/plugins/defaults/memory/__tests__/jobs-store-enqueue-gate.test.ts +11 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +199 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +98 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +105 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +24 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +524 -0
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1 -1
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +9 -4
- package/src/plugins/defaults/memory/host-utils.ts +5 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +105 -1
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +64 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +36 -1
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +12 -3
- package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +1 -0
- package/src/plugins/defaults/memory/substrate/__tests__/skill-store.test.ts +195 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +1 -1
- package/src/plugins/defaults/memory/substrate/skill-store.ts +167 -29
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +131 -0
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +1 -0
- package/src/plugins/defaults/memory/v2/activation-log-store.ts +4 -0
- package/src/plugins/defaults/memory/v2/injection.ts +48 -3
- package/src/plugins/defaults/memory/v2/rerank-local.ts +6 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -1
- package/src/plugins/defaults/platform-hosted/routes/reengage.ts +1 -1
- package/src/plugins/defaults/turn-context/injectors.ts +1 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +26 -0
- package/src/plugins/types.ts +17 -0
- package/src/prompts/templates/system-sections.ts +2 -2
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +43 -2
- package/src/providers/__tests__/vellum-mismatch-routing.test.ts +8 -0
- package/src/providers/call-site-routing.ts +77 -14
- package/src/providers/connection-resolution.ts +42 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +127 -1
- package/src/providers/inference/adapter-factory.ts +70 -12
- package/src/providers/model-catalog.ts +0 -11
- package/src/providers/model-intents.ts +11 -0
- package/src/providers/registry.ts +8 -0
- package/src/providers/retry.ts +85 -17
- package/src/providers/types.ts +8 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/agent-wake.ts +132 -9
- package/src/runtime/channel-approval-types.ts +25 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +2 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +11 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +121 -9
- package/src/runtime/routes/__tests__/platform-invoice-routes.test.ts +392 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/workspace-commit-routes.test.ts +63 -0
- package/src/runtime/routes/consolidation-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +18 -6
- package/src/runtime/routes/conversation-query-routes.ts +14 -23
- package/src/runtime/routes/conversation-routes.ts +113 -8
- package/src/runtime/routes/credential-routes.ts +1 -5
- package/src/runtime/routes/documents-routes.ts +207 -12
- package/src/runtime/routes/identity-routes.ts +3 -93
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +31 -1
- package/src/runtime/routes/inbound-stages/admission-policy.ts +18 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +52 -3
- package/src/runtime/routes/notification-routes.ts +4 -1
- package/src/runtime/routes/platform-routes.ts +238 -3
- package/src/runtime/routes/playground/guard.ts +1 -2
- package/src/runtime/routes/slack-channel-routes.ts +2 -4
- package/src/runtime/routes/workspace-commit-routes.ts +49 -2
- package/src/runtime/routes/workspace-routes.ts +11 -12
- package/src/runtime/routes/workspace-utils.ts +49 -2
- package/src/runtime/services/conversation-serializer.ts +2 -4
- package/src/subagent/__tests__/consult-context-gating.test.ts +98 -0
- package/src/subagent/__tests__/consult-context-skills.test.ts +82 -0
- package/src/subagent/__tests__/consult-context.test.ts +84 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +36 -0
- package/src/subagent/consult-context.ts +410 -0
- package/src/subagent/consult-prompt.ts +38 -5
- package/src/subagent/manager.ts +9 -4
- package/src/subagent/types.ts +8 -0
- package/src/telemetry/telemetry-event-sources.ts +7 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +1 -0
- package/src/telemetry/turn-events-store.ts +30 -1
- package/src/telemetry/types.ts +24 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/tools/browser/pinned-tabs.ts +3 -1
- package/src/tools/subagent/spawn.ts +23 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/ui-surface/definitions.ts +39 -1
- package/src/tools/ui-surface/surface-shape-docs.ts +38 -4
- package/src/tools/ui-surface/visual-validation.ts +787 -0
- package/src/tools/workflows/run-workflow.test.ts +1 -0
- package/src/util/__tests__/short-title.test.ts +229 -0
- package/src/util/__tests__/worker-compute.test.ts +65 -0
- package/src/util/cgroup-cpu.ts +93 -0
- package/src/util/errors.ts +27 -0
- package/src/util/process-tree.ts +19 -0
- package/src/util/short-title.ts +189 -0
- package/src/util/worker-compute.ts +83 -0
- package/src/workspace/byok-default-profile-ensure.ts +25 -15
- package/src/workspace/git-service.ts +79 -30
- package/src/workspace/migrations/137-repair-retired-fireworks-minimax-model-id.ts +131 -0
- package/src/workspace/migrations/138-backfill-home-feed-titles.ts +179 -0
- package/src/workspace/migrations/139-clear-renamed-cost-profile-label.ts +92 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/plugins/defaults/memory/substrate/constants.ts +0 -8
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
+
buildSlackChannelLabelMap,
|
|
11
12
|
buildSlackUserLabelMap,
|
|
12
13
|
renderSlackTextForModel,
|
|
13
14
|
} from "@vellumai/slack-text";
|
|
@@ -32,7 +33,6 @@ import type {
|
|
|
32
33
|
} from "../../provider-types.js";
|
|
33
34
|
import { resolveSlackAuth } from "./auth.js";
|
|
34
35
|
import * as slack from "./client.js";
|
|
35
|
-
import { SlackApiError } from "./client.js";
|
|
36
36
|
import {
|
|
37
37
|
classifyConversationType,
|
|
38
38
|
isPrivateConversation,
|
|
@@ -44,6 +44,7 @@ import type {
|
|
|
44
44
|
SlackSearchMatch,
|
|
45
45
|
SlackUser,
|
|
46
46
|
} from "./types.js";
|
|
47
|
+
import { SlackApiError } from "./web-api-transport.js";
|
|
47
48
|
|
|
48
49
|
interface NormalizedSlackUserInfo {
|
|
49
50
|
displayName: string;
|
|
@@ -69,6 +70,15 @@ const PERMANENT_USER_INFO_SLACK_ERRORS = new Set([
|
|
|
69
70
|
// Cache normalized Slack user facts to avoid repeated API calls within a session.
|
|
70
71
|
const userInfoCache = new Map<string, Promise<SlackUserInfoLookupResult>>();
|
|
71
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Cache resolved channel names for inline `<#C…>` mention rendering, same
|
|
75
|
+
* lifetime and keying discipline as {@link userInfoCache}. Holds `undefined`
|
|
76
|
+
* for channels this auth provably cannot resolve (not found / no permission)
|
|
77
|
+
* so a wall of history mentioning an inaccessible channel doesn't re-fire a
|
|
78
|
+
* doomed lookup per message batch.
|
|
79
|
+
*/
|
|
80
|
+
const channelNameCache = new Map<string, Promise<string | undefined>>();
|
|
81
|
+
|
|
72
82
|
/**
|
|
73
83
|
* Cached auth resolved during resolveConnection(), split by direction.
|
|
74
84
|
*
|
|
@@ -307,15 +317,54 @@ function isPermanentSlackUserInfoFailure(err: unknown): boolean {
|
|
|
307
317
|
);
|
|
308
318
|
}
|
|
309
319
|
|
|
320
|
+
function slackAuthCacheScope(auth: OAuthConnection | string): string {
|
|
321
|
+
return typeof auth === "string"
|
|
322
|
+
? `token:${createHash("sha256").update(auth).digest("hex")}`
|
|
323
|
+
: `connection:${auth.id}:${auth.accountInfo ?? ""}`;
|
|
324
|
+
}
|
|
325
|
+
|
|
310
326
|
function slackUserInfoCacheKey(
|
|
311
327
|
auth: OAuthConnection | string,
|
|
312
328
|
userId: string,
|
|
313
329
|
): string {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
330
|
+
return `${slackAuthCacheScope(auth)}:user:${userId}`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Resolve a channel's display name for inline mention rendering, cached per
|
|
335
|
+
* auth scope. Returns undefined when the channel has no name (DMs) or this
|
|
336
|
+
* auth cannot see it; transient failures are not cached so a later batch
|
|
337
|
+
* retries.
|
|
338
|
+
*/
|
|
339
|
+
async function resolveChannelName(
|
|
340
|
+
auth: OAuthConnection | string,
|
|
341
|
+
channelId: string,
|
|
342
|
+
): Promise<string | undefined> {
|
|
343
|
+
if (!channelId) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
const cacheKey = `${slackAuthCacheScope(auth)}:channel:${channelId}`;
|
|
347
|
+
const cached = channelNameCache.get(cacheKey);
|
|
348
|
+
if (cached) {
|
|
349
|
+
return cached;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const resolved = slack.conversationInfo(auth, channelId).then(
|
|
353
|
+
(resp) => trimNonEmpty(resp.channel.name),
|
|
354
|
+
(err: unknown) => {
|
|
355
|
+
// Cache the definitive "this auth cannot resolve it" answers; drop
|
|
356
|
+
// everything else so transient failures retry on the next batch.
|
|
357
|
+
const permanent =
|
|
358
|
+
err instanceof SlackApiError &&
|
|
359
|
+
(err.category === "channel_not_found" || err.category === "permission");
|
|
360
|
+
if (!permanent) {
|
|
361
|
+
channelNameCache.delete(cacheKey);
|
|
362
|
+
}
|
|
363
|
+
return undefined;
|
|
364
|
+
},
|
|
365
|
+
);
|
|
366
|
+
channelNameCache.set(cacheKey, resolved);
|
|
367
|
+
return resolved;
|
|
319
368
|
}
|
|
320
369
|
|
|
321
370
|
function normalizeSlackUserInfo(
|
|
@@ -346,8 +395,9 @@ function trimNonEmpty(value: unknown): string | undefined {
|
|
|
346
395
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
347
396
|
}
|
|
348
397
|
|
|
349
|
-
export function
|
|
398
|
+
export function __resetSlackMentionCachesForTests(): void {
|
|
350
399
|
userInfoCache.clear();
|
|
400
|
+
channelNameCache.clear();
|
|
351
401
|
}
|
|
352
402
|
|
|
353
403
|
function slackUserInfoMetadata(
|
|
@@ -460,13 +510,13 @@ function mapMessage(
|
|
|
460
510
|
|
|
461
511
|
function mapSearchMatch(
|
|
462
512
|
match: SlackSearchMatch,
|
|
463
|
-
|
|
513
|
+
labels: SlackMentionLabels,
|
|
464
514
|
): Message {
|
|
465
515
|
return {
|
|
466
516
|
id: match.ts,
|
|
467
517
|
conversationId: match.channel.id,
|
|
468
518
|
sender: { id: match.user ?? "unknown", name: match.username ?? "unknown" },
|
|
469
|
-
text: renderSlackTextForModel(match.text,
|
|
519
|
+
text: renderSlackTextForModel(match.text, labels),
|
|
470
520
|
timestamp: parseFloat(match.ts) * 1000,
|
|
471
521
|
threadId: match.thread_ts,
|
|
472
522
|
platform: "slack",
|
|
@@ -479,7 +529,7 @@ async function mapSlackMessages(
|
|
|
479
529
|
channelId: string,
|
|
480
530
|
slackMessages: SlackMessage[],
|
|
481
531
|
): Promise<Message[]> {
|
|
482
|
-
const
|
|
532
|
+
const labels = await buildMentionLabels(
|
|
483
533
|
auth,
|
|
484
534
|
slackMessages.map((msg) => msg.text),
|
|
485
535
|
);
|
|
@@ -491,31 +541,48 @@ async function mapSlackMessages(
|
|
|
491
541
|
msg,
|
|
492
542
|
channelId,
|
|
493
543
|
senderInfo,
|
|
494
|
-
renderSlackTextForModel(msg.text,
|
|
544
|
+
renderSlackTextForModel(msg.text, labels),
|
|
495
545
|
),
|
|
496
546
|
);
|
|
497
547
|
}
|
|
498
548
|
return messages;
|
|
499
549
|
}
|
|
500
550
|
|
|
501
|
-
|
|
551
|
+
interface SlackMentionLabels {
|
|
552
|
+
userLabels: Record<string, string>;
|
|
553
|
+
channelLabels: Record<string, string>;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Resolve display labels for every user and channel mentioned inline in the
|
|
558
|
+
* given texts, so bare `<@U…>` / `<#C…>` tokens render as names instead of
|
|
559
|
+
* falling back to `@unknown-user` / `#unknown-channel`. Pipe-form tokens
|
|
560
|
+
* carry their own label and are skipped by the map builders.
|
|
561
|
+
*/
|
|
562
|
+
async function buildMentionLabels(
|
|
502
563
|
auth: OAuthConnection | string,
|
|
503
|
-
textValues:
|
|
504
|
-
): Promise<
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
564
|
+
textValues: readonly (string | undefined)[],
|
|
565
|
+
): Promise<SlackMentionLabels> {
|
|
566
|
+
const [userLabels, channelLabels] = await Promise.all([
|
|
567
|
+
buildSlackUserLabelMap(textValues, (userId) =>
|
|
568
|
+
resolveUserName(auth, userId),
|
|
569
|
+
),
|
|
570
|
+
buildSlackChannelLabelMap(textValues, (channelId) =>
|
|
571
|
+
resolveChannelName(auth, channelId),
|
|
572
|
+
),
|
|
573
|
+
]);
|
|
574
|
+
return { userLabels, channelLabels };
|
|
508
575
|
}
|
|
509
576
|
|
|
510
577
|
async function mapSearchMatches(
|
|
511
578
|
auth: OAuthConnection | string,
|
|
512
579
|
matches: SlackSearchMatch[],
|
|
513
580
|
): Promise<Message[]> {
|
|
514
|
-
const
|
|
581
|
+
const labels = await buildMentionLabels(
|
|
515
582
|
auth,
|
|
516
583
|
matches.map((match) => match.text),
|
|
517
584
|
);
|
|
518
|
-
return matches.map((match) => mapSearchMatch(match,
|
|
585
|
+
return matches.map((match) => mapSearchMatch(match, labels));
|
|
519
586
|
}
|
|
520
587
|
|
|
521
588
|
export const slackProvider: MessagingProvider = {
|
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
import { afterAll, afterEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
const BOT_TOKEN = "xoxb-test";
|
|
4
|
+
const USER_TOKEN = "xoxp-test";
|
|
4
5
|
|
|
6
|
+
/** Set false to simulate an install whose Slack setup stored no user token. */
|
|
7
|
+
let userTokenStored = true;
|
|
8
|
+
|
|
9
|
+
function secureKeyFor(account: string): string | undefined {
|
|
10
|
+
if (account.endsWith("/user_token")) {
|
|
11
|
+
return userTokenStored ? USER_TOKEN : undefined;
|
|
12
|
+
}
|
|
13
|
+
return BOT_TOKEN;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// `api.ts` resolves identity through `resolveSlackAuth`, whose import graph
|
|
17
|
+
// (oauth/manual-token-connection.ts and siblings) reads several secure-keys
|
|
18
|
+
// exports beyond the two this test overrides. Spread the real module so the
|
|
19
|
+
// mock keeps every export those imports expect; the key accessors are the
|
|
20
|
+
// only behavior under the test's control.
|
|
21
|
+
const realSecureKeys = await import("../../../security/secure-keys.js");
|
|
5
22
|
mock.module("../../../security/secure-keys.js", () => ({
|
|
6
|
-
|
|
23
|
+
...realSecureKeys,
|
|
24
|
+
getSecureKeyAsync: async (account: string) => secureKeyFor(account),
|
|
25
|
+
getSecureKeyResultAsync: async (account: string) => ({
|
|
26
|
+
value: secureKeyFor(account),
|
|
27
|
+
unreachable: false,
|
|
28
|
+
}),
|
|
7
29
|
}));
|
|
8
30
|
|
|
9
31
|
const { appendSlackStream, getSlackConversationInfo, startSlackStream } =
|
|
@@ -52,6 +74,68 @@ describe("getSlackConversationInfo", () => {
|
|
|
52
74
|
Authorization: `Bearer ${BOT_TOKEN}`,
|
|
53
75
|
});
|
|
54
76
|
});
|
|
77
|
+
|
|
78
|
+
test("retries as the user when the bot cannot see the channel", async () => {
|
|
79
|
+
userTokenStored = true;
|
|
80
|
+
const authHeaders: string[] = [];
|
|
81
|
+
globalThis.fetch = mock(async (_input, init) => {
|
|
82
|
+
const auth = (init?.headers as Record<string, string>).Authorization;
|
|
83
|
+
authHeaders.push(auth);
|
|
84
|
+
if (auth === `Bearer ${BOT_TOKEN}`) {
|
|
85
|
+
return new Response(
|
|
86
|
+
JSON.stringify({ ok: false, error: "channel_not_found" }),
|
|
87
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return new Response(
|
|
91
|
+
JSON.stringify({ ok: true, channel: { id: "C9", name: "private" } }),
|
|
92
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
93
|
+
);
|
|
94
|
+
}) as unknown as typeof fetch;
|
|
95
|
+
|
|
96
|
+
const info = await getSlackConversationInfo("C9");
|
|
97
|
+
|
|
98
|
+
expect(info).toEqual({ id: "C9", name: "private" });
|
|
99
|
+
expect(authHeaders).toEqual([
|
|
100
|
+
`Bearer ${BOT_TOKEN}`,
|
|
101
|
+
`Bearer ${USER_TOKEN}`,
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("does not retry as the user for non-visibility errors", async () => {
|
|
106
|
+
userTokenStored = true;
|
|
107
|
+
let calls = 0;
|
|
108
|
+
globalThis.fetch = mock(async () => {
|
|
109
|
+
calls++;
|
|
110
|
+
return new Response(
|
|
111
|
+
JSON.stringify({ ok: false, error: "invalid_auth" }),
|
|
112
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
113
|
+
);
|
|
114
|
+
}) as unknown as typeof fetch;
|
|
115
|
+
|
|
116
|
+
await expect(getSlackConversationInfo("C9")).rejects.toThrow(
|
|
117
|
+
"Slack API error: invalid_auth",
|
|
118
|
+
);
|
|
119
|
+
expect(calls).toBe(1);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("does not retry when no distinct user token is stored", async () => {
|
|
123
|
+
userTokenStored = false;
|
|
124
|
+
let calls = 0;
|
|
125
|
+
globalThis.fetch = mock(async () => {
|
|
126
|
+
calls++;
|
|
127
|
+
return new Response(
|
|
128
|
+
JSON.stringify({ ok: false, error: "channel_not_found" }),
|
|
129
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
130
|
+
);
|
|
131
|
+
}) as unknown as typeof fetch;
|
|
132
|
+
|
|
133
|
+
await expect(getSlackConversationInfo("C9")).rejects.toThrow(
|
|
134
|
+
"Slack API error: channel_not_found",
|
|
135
|
+
);
|
|
136
|
+
expect(calls).toBe(1);
|
|
137
|
+
userTokenStored = true;
|
|
138
|
+
});
|
|
55
139
|
});
|
|
56
140
|
|
|
57
141
|
describe("startSlackStream", () => {
|