@vellumai/assistant 0.8.1 → 0.8.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/ARCHITECTURE.md +2 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -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 +325 -3
- 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-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 +41 -0
- 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 +1 -0
- 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 +2 -0
- 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-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 +76 -9
- 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__/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 +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- 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 +266 -10
- 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-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -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-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -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__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- 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} +1 -1
- 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 +142 -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} +8 -8
- 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__/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 +737 -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-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -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 +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -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__/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 +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- 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/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/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- 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 +34 -0
- 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.ts +169 -9
- 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 +16 -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 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- 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 +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -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/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- 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 +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- 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 +110 -10
- package/src/memory/db-init.ts +6 -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 +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- 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 +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- 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/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- 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/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- 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/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/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- 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 +36 -3
- 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 +105 -32
- 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 +31 -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 +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- 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 +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- 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__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- 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 +121 -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 +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -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 +6 -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 +65 -21
- 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/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 +57 -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 +15 -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/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 +72 -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/registry.ts +8 -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/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -30,49 +30,49 @@ For the full plugin authoring guide, see
|
|
|
30
30
|
|
|
31
31
|
## Install locally
|
|
32
32
|
|
|
33
|
-
The assistant scans
|
|
33
|
+
The assistant scans `<workspaceDir>/plugins/*` (e.g.
|
|
34
|
+
`~/.vellum/workspace/plugins/`) for subdirectories containing a
|
|
34
35
|
`register.{ts,js}` file and dynamic-imports each one during assistant
|
|
35
36
|
startup. Dropping (or symlinking) this directory in place is enough to
|
|
36
37
|
enable it.
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
The plugin reads `registerPlugin` from `globalThis.__vellumPluginRuntime`,
|
|
40
|
+
which the daemon attaches before scanning plugins. This works against both
|
|
41
|
+
the `bun --compile`-bundled daemon binary AND a daemon running from
|
|
42
|
+
source — no special install procedure required either way.
|
|
43
|
+
|
|
44
|
+
### Option 1 — symlink from the repo (simplest in-repo dev)
|
|
39
45
|
|
|
40
46
|
From the repo root:
|
|
41
47
|
|
|
42
48
|
```bash
|
|
43
|
-
mkdir -p ~/.vellum/plugins
|
|
44
|
-
ln -s "$(pwd)/assistant/examples/plugins/echo" ~/.vellum/plugins/echo
|
|
49
|
+
mkdir -p ~/.vellum/workspace/plugins
|
|
50
|
+
ln -s "$(pwd)/assistant/examples/plugins/echo" ~/.vellum/workspace/plugins/echo
|
|
45
51
|
```
|
|
46
52
|
|
|
47
53
|
Symlinks let you edit the plugin in-place and restart the assistant to
|
|
48
|
-
pick up changes.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
in `register.ts` resolve to `~/.vellum/src/plugins/...`, which does not
|
|
60
|
-
exist. The assistant does not currently publish the plugin API as an npm
|
|
61
|
-
package, so to copy-and-adapt this template into a standalone plugin you
|
|
62
|
-
must rewrite the imports in `register.ts` to point at an absolute path
|
|
63
|
-
inside a vellum-assistant checkout, for example:
|
|
54
|
+
pick up changes.
|
|
55
|
+
|
|
56
|
+
### Option 2 — standalone copy
|
|
57
|
+
|
|
58
|
+
A plain `cp -R` of this directory into `~/.vellum/workspace/plugins/echo/`
|
|
59
|
+
works for the runtime imports (which go through the global bridge), but
|
|
60
|
+
the `import type` lines at the top of `register.ts` still resolve into
|
|
61
|
+
the in-repo assistant source tree. If your standalone copy lives outside
|
|
62
|
+
a vellum-assistant checkout, rewrite those `import type` paths to point
|
|
63
|
+
at an absolute path inside any checkout — they're erased at compile time
|
|
64
|
+
and have no module-identity effect at runtime:
|
|
64
65
|
|
|
65
66
|
```ts
|
|
66
67
|
// before (repo-local):
|
|
67
|
-
import {
|
|
68
|
+
import type { VellumPluginRuntime } from "../../../src/plugins/external-api.js";
|
|
69
|
+
import type { Plugin } from "../../../src/plugins/types.js";
|
|
68
70
|
// after (standalone, edit to your checkout path):
|
|
69
|
-
import {
|
|
71
|
+
import type { VellumPluginRuntime } from "/path/to/vellum-assistant/assistant/src/plugins/external-api.js";
|
|
72
|
+
import type { Plugin } from "/path/to/vellum-assistant/assistant/src/plugins/types.js";
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
`../../../src/plugins/types.js`. Until a published package exists, the
|
|
74
|
-
symlink recipe above is simpler and more portable for day-to-day
|
|
75
|
-
development.
|
|
75
|
+
No runtime-import rewriting is needed — the bridge already handles that.
|
|
76
76
|
|
|
77
77
|
### Restart the assistant
|
|
78
78
|
|
|
@@ -113,7 +113,7 @@ original error still propagates.
|
|
|
113
113
|
Remove the symlink (or the copied directory) and restart the assistant:
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
|
-
rm ~/.vellum/plugins/echo
|
|
116
|
+
rm ~/.vellum/workspace/plugins/echo
|
|
117
117
|
vellum restart
|
|
118
118
|
```
|
|
119
119
|
|
|
@@ -2,34 +2,26 @@
|
|
|
2
2
|
* Echo plugin — observes every assistant pipeline and logs one structured
|
|
3
3
|
* line per invocation to stderr.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `
|
|
9
|
-
* `assistant/docs/plugins.md` for general plugin authoring docs.
|
|
5
|
+
* Bundled in the repository as an authoring reference. To try it locally,
|
|
6
|
+
* symlink (or copy) this directory into `<workspaceDir>/plugins/echo/` and
|
|
7
|
+
* restart the assistant. See `README.md` in this directory for the install
|
|
8
|
+
* recipe and `assistant/docs/plugins.md` for general plugin authoring docs.
|
|
10
9
|
*
|
|
11
|
-
* ##
|
|
10
|
+
* ## Runtime bridge
|
|
12
11
|
*
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
12
|
+
* The plugin reads `registerPlugin` from `globalThis.__vellumPluginRuntime`,
|
|
13
|
+
* a stable handle the daemon attaches at startup. This lets the same plugin
|
|
14
|
+
* file work whether the daemon is running from source (relative or absolute
|
|
15
|
+
* imports would resolve to the daemon's modules) or as a `bun --compile`
|
|
16
|
+
* binary (where absolute imports would load a disjoint disk copy with a
|
|
17
|
+
* separate registry instance). The bridge is documented in
|
|
18
|
+
* `assistant/src/plugins/external-api.ts`.
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* For a standalone copy that lives outside the repo, you must either:
|
|
26
|
-
* - Point the imports at an absolute path into a vellum-assistant checkout
|
|
27
|
-
* (`/path/to/vellum-assistant/assistant/src/plugins/registry.js`), or
|
|
28
|
-
* - Rewrite the plugin to consume only the public types you need and drop
|
|
29
|
-
* the direct registry import in favor of your own entry-point wiring.
|
|
30
|
-
*
|
|
31
|
-
* See README.md "Option 2 — standalone copy" for the recommended
|
|
32
|
-
* standalone-template adaptation steps.
|
|
20
|
+
* Type imports below still come from the in-repo source tree. Types are
|
|
21
|
+
* erased at runtime, so they don't affect module identity — but they only
|
|
22
|
+
* resolve while this file lives inside the vellum-assistant checkout. For a
|
|
23
|
+
* standalone-copy install, rewrite the `import type` paths to absolute paths
|
|
24
|
+
* inside a checkout (or vendor only the types you need).
|
|
33
25
|
*
|
|
34
26
|
* ## Design
|
|
35
27
|
*
|
|
@@ -48,7 +40,7 @@
|
|
|
48
40
|
* user-plugin-loader contract (see `assistant/src/plugins/user-loader.ts`).
|
|
49
41
|
*/
|
|
50
42
|
|
|
51
|
-
import {
|
|
43
|
+
import type { VellumPluginRuntime } from "../../../src/plugins/external-api.js";
|
|
52
44
|
import type {
|
|
53
45
|
CircuitBreakerArgs,
|
|
54
46
|
CircuitBreakerResult,
|
|
@@ -81,6 +73,15 @@ import type {
|
|
|
81
73
|
TurnResult,
|
|
82
74
|
} from "../../../src/plugins/types.js";
|
|
83
75
|
|
|
76
|
+
const runtime = (globalThis as { __vellumPluginRuntime?: VellumPluginRuntime })
|
|
77
|
+
.__vellumPluginRuntime;
|
|
78
|
+
if (!runtime || runtime.version !== 1) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
"echo plugin: globalThis.__vellumPluginRuntime is missing or has an unexpected version — install a recent assistant build",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const { registerPlugin } = runtime;
|
|
84
|
+
|
|
84
85
|
const PLUGIN_NAME = "echo";
|
|
85
86
|
|
|
86
87
|
/**
|
|
@@ -136,9 +137,10 @@ function makeObserver<A, R>(
|
|
|
136
137
|
* `PipelineMiddlewareMap` — all thin observers produced by `makeObserver`.
|
|
137
138
|
*
|
|
138
139
|
* Manifest:
|
|
139
|
-
* -
|
|
140
|
-
*
|
|
141
|
-
*
|
|
140
|
+
* - Host-compat range lives in `package.json` under
|
|
141
|
+
* `peerDependencies["@vellumai/plugin-api"]`. The external-plugin loader
|
|
142
|
+
* validates it against the running assistant version via
|
|
143
|
+
* `semver.satisfies()` before this file is even imported.
|
|
142
144
|
* - No `requiresCredential` or `requiresFlag` — the plugin needs no external
|
|
143
145
|
* state and runs unconditionally.
|
|
144
146
|
*/
|
|
@@ -146,8 +148,6 @@ const echoPlugin: Plugin = {
|
|
|
146
148
|
manifest: {
|
|
147
149
|
name: PLUGIN_NAME,
|
|
148
150
|
version: "0.1.0",
|
|
149
|
-
provides: {},
|
|
150
|
-
requires: { pluginRuntime: "v1" },
|
|
151
151
|
},
|
|
152
152
|
middleware: {
|
|
153
153
|
turn: makeObserver<TurnArgs, TurnResult>("turn"),
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
buildSlackChannelLabelMap,
|
|
4
5
|
buildSlackUserLabelMap,
|
|
6
|
+
extractSlackChannelReferenceIds,
|
|
5
7
|
extractSlackUserMentionIds,
|
|
6
8
|
renderSlackTextForModel,
|
|
7
9
|
} from "./index.js";
|
|
@@ -9,17 +11,27 @@ import {
|
|
|
9
11
|
describe("extractSlackUserMentionIds", () => {
|
|
10
12
|
test("returns unique user mention IDs in encounter order", () => {
|
|
11
13
|
expect(
|
|
12
|
-
extractSlackUserMentionIds("<@U123> hi <@W456> and <@U123> again")
|
|
14
|
+
extractSlackUserMentionIds("<@U123> hi <@W456> and <@U123> again"),
|
|
13
15
|
).toEqual(["U123", "W456"]);
|
|
14
16
|
});
|
|
15
17
|
});
|
|
16
18
|
|
|
19
|
+
describe("extractSlackChannelReferenceIds", () => {
|
|
20
|
+
test("returns unique channel reference IDs in encounter order", () => {
|
|
21
|
+
expect(
|
|
22
|
+
extractSlackChannelReferenceIds(
|
|
23
|
+
"<#C123> hi <#G456|private> and <#C123> again",
|
|
24
|
+
),
|
|
25
|
+
).toEqual(["C123", "G456"]);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
17
29
|
describe("renderSlackTextForModel", () => {
|
|
18
30
|
test("renders resolved user mentions", () => {
|
|
19
31
|
expect(
|
|
20
32
|
renderSlackTextForModel("hello <@U123>", {
|
|
21
33
|
userLabels: { U123: "Alice" },
|
|
22
|
-
})
|
|
34
|
+
}),
|
|
23
35
|
).toBe("hello @Alice");
|
|
24
36
|
});
|
|
25
37
|
|
|
@@ -36,7 +48,7 @@ describe("renderSlackTextForModel", () => {
|
|
|
36
48
|
expect(
|
|
37
49
|
renderSlackTextForModel("hello <@U123>", {
|
|
38
50
|
userLabels: { U123: "U123" },
|
|
39
|
-
})
|
|
51
|
+
}),
|
|
40
52
|
).toBe("hello @unknown-user");
|
|
41
53
|
});
|
|
42
54
|
|
|
@@ -44,29 +56,61 @@ describe("renderSlackTextForModel", () => {
|
|
|
44
56
|
expect(
|
|
45
57
|
renderSlackTextForModel("<#C123|general> <#C456>", {
|
|
46
58
|
channelLabels: { C456: "support" },
|
|
47
|
-
})
|
|
59
|
+
}),
|
|
48
60
|
).toBe("#general #support");
|
|
49
61
|
|
|
50
62
|
expect(renderSlackTextForModel("<#C789>")).toBe("#unknown-channel");
|
|
51
63
|
});
|
|
52
64
|
|
|
65
|
+
test("prefers embedded Slack labels over resolved channel labels", () => {
|
|
66
|
+
expect(
|
|
67
|
+
renderSlackTextForModel("<#C123|old-name>", {
|
|
68
|
+
channelLabels: { C123: "new-name" },
|
|
69
|
+
}),
|
|
70
|
+
).toBe("#old-name");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("falls back to embedded channel labels when resolved labels are empty", () => {
|
|
74
|
+
expect(
|
|
75
|
+
renderSlackTextForModel("<#C123|general> <#C456|ops>", {
|
|
76
|
+
channelLabels: { C123: "", C456: " " },
|
|
77
|
+
}),
|
|
78
|
+
).toBe("#general #ops");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("does not emit raw channel IDs from embedded or resolved labels", () => {
|
|
82
|
+
expect(
|
|
83
|
+
renderSlackTextForModel("<#C123|C123> <#C456|general>", {
|
|
84
|
+
channelLabels: { C456: "C456" },
|
|
85
|
+
}),
|
|
86
|
+
).toBe("#unknown-channel #general");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("uses resolved channel labels when embedded labels are missing or ID-shaped", () => {
|
|
90
|
+
expect(
|
|
91
|
+
renderSlackTextForModel("<#C123> <#C456|C456>", {
|
|
92
|
+
channelLabels: { C123: "general", C456: "ops" },
|
|
93
|
+
}),
|
|
94
|
+
).toBe("#general #ops");
|
|
95
|
+
});
|
|
96
|
+
|
|
53
97
|
test("renders special broadcasts", () => {
|
|
54
98
|
expect(renderSlackTextForModel("<!here> <!channel> <!everyone>")).toBe(
|
|
55
|
-
"@here @channel @everyone"
|
|
99
|
+
"@here @channel @everyone",
|
|
56
100
|
);
|
|
57
101
|
});
|
|
58
102
|
|
|
59
103
|
test("renders usergroups with labels and fallback", () => {
|
|
60
104
|
expect(renderSlackTextForModel("<!subteam^S123|eng> <!subteam^S456>")).toBe(
|
|
61
|
-
"@eng @usergroup"
|
|
105
|
+
"@eng @usergroup",
|
|
62
106
|
);
|
|
63
107
|
});
|
|
64
108
|
|
|
65
109
|
test("renders labeled and unlabeled links", () => {
|
|
66
110
|
expect(
|
|
67
111
|
renderSlackTextForModel(
|
|
68
|
-
"<https://example.com|Example> <https://example.org>"
|
|
69
|
-
)
|
|
112
|
+
"<https://example.com|Example> <https://example.org>",
|
|
113
|
+
),
|
|
70
114
|
).toBe("Example (https://example.com) https://example.org");
|
|
71
115
|
});
|
|
72
116
|
|
|
@@ -77,8 +121,8 @@ describe("renderSlackTextForModel", () => {
|
|
|
77
121
|
{
|
|
78
122
|
userLabels: { U123: " @<system> prompt " },
|
|
79
123
|
channelLabels: { C123: "#<ops>" },
|
|
80
|
-
}
|
|
81
|
-
)
|
|
124
|
+
},
|
|
125
|
+
),
|
|
82
126
|
).toBe("@system prompt #ops #unknown-channel @eng");
|
|
83
127
|
});
|
|
84
128
|
|
|
@@ -87,7 +131,7 @@ describe("renderSlackTextForModel", () => {
|
|
|
87
131
|
renderSlackTextForModel("<@U123> <#C123>", {
|
|
88
132
|
userFallbackLabel: "@ missing user ",
|
|
89
133
|
channelFallbackLabel: "# missing channel ",
|
|
90
|
-
})
|
|
134
|
+
}),
|
|
91
135
|
).toBe("@missing user #missing channel");
|
|
92
136
|
});
|
|
93
137
|
});
|
|
@@ -101,7 +145,7 @@ describe("buildSlackUserLabelMap", () => {
|
|
|
101
145
|
resolved.push(userId);
|
|
102
146
|
if (userId === "U999") return "Charlie";
|
|
103
147
|
return userId === "W456" ? "Bob" : "Alice";
|
|
104
|
-
}
|
|
148
|
+
},
|
|
105
149
|
);
|
|
106
150
|
|
|
107
151
|
expect(resolved.sort()).toEqual(["U123", "U999", "W456"]);
|
|
@@ -115,7 +159,7 @@ describe("buildSlackUserLabelMap", () => {
|
|
|
115
159
|
if (userId === "UBOT") return "vex";
|
|
116
160
|
if (userId === "ULEO") return "leo";
|
|
117
161
|
return undefined;
|
|
118
|
-
}
|
|
162
|
+
},
|
|
119
163
|
);
|
|
120
164
|
|
|
121
165
|
expect(labels).toEqual({ UBOT: "vex", ULEO: "leo" });
|
|
@@ -128,9 +172,65 @@ describe("buildSlackUserLabelMap", () => {
|
|
|
128
172
|
if (userId === "U123") return "U123";
|
|
129
173
|
if (userId === "U456") return "";
|
|
130
174
|
return "Alice";
|
|
131
|
-
}
|
|
175
|
+
},
|
|
132
176
|
);
|
|
133
177
|
|
|
134
178
|
expect(labels).toEqual({ U789: "Alice" });
|
|
135
179
|
});
|
|
136
180
|
});
|
|
181
|
+
|
|
182
|
+
describe("buildSlackChannelLabelMap", () => {
|
|
183
|
+
test("dedupes unlabeled channel references and resolves them in parallel", async () => {
|
|
184
|
+
const resolved: string[] = [];
|
|
185
|
+
const labels = await buildSlackChannelLabelMap(
|
|
186
|
+
["<#C123> hi <#G999> <#G999>", undefined, "<#C123> and <#D456>"],
|
|
187
|
+
async (channelId) => {
|
|
188
|
+
resolved.push(channelId);
|
|
189
|
+
if (channelId === "G999") return "private-team";
|
|
190
|
+
return channelId === "D456" ? "direct-chat" : "general";
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
expect(resolved.sort()).toEqual(["C123", "D456", "G999"]);
|
|
195
|
+
expect(labels).toEqual({
|
|
196
|
+
C123: "general",
|
|
197
|
+
D456: "direct-chat",
|
|
198
|
+
G999: "private-team",
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("does not resolve channel references that already have usable embedded labels", async () => {
|
|
203
|
+
const resolved: string[] = [];
|
|
204
|
+
const labels = await buildSlackChannelLabelMap(
|
|
205
|
+
["<#C123|general> <#C456> <#C789|C789> <#CEMPTY| >"],
|
|
206
|
+
async (channelId) => {
|
|
207
|
+
resolved.push(channelId);
|
|
208
|
+
return channelId === "C456"
|
|
209
|
+
? "support"
|
|
210
|
+
: channelId === "CEMPTY"
|
|
211
|
+
? "empty-label"
|
|
212
|
+
: "resolved";
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
expect(resolved.sort()).toEqual(["C456", "C789", "CEMPTY"]);
|
|
217
|
+
expect(labels).toEqual({
|
|
218
|
+
C456: "support",
|
|
219
|
+
C789: "resolved",
|
|
220
|
+
CEMPTY: "empty-label",
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("omits unresolved labels and labels equal to the Slack channel ID", async () => {
|
|
225
|
+
const labels = await buildSlackChannelLabelMap(
|
|
226
|
+
["<#C123> <#C456> <#C789>"],
|
|
227
|
+
async (channelId) => {
|
|
228
|
+
if (channelId === "C123") return "C123";
|
|
229
|
+
if (channelId === "C456") return "";
|
|
230
|
+
return "support";
|
|
231
|
+
},
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(labels).toEqual({ C789: "support" });
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -6,6 +6,7 @@ export interface RenderSlackTextOptions {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
const SLACK_USER_MENTION_RE = /<@([UW][A-Z0-9]+)>/g;
|
|
9
|
+
const SLACK_CHANNEL_REFERENCE_RE = /<#([CDG][A-Z0-9]+)(?:\|[^>]*)?>/g;
|
|
9
10
|
|
|
10
11
|
export function extractSlackUserMentionIds(text: string): string[] {
|
|
11
12
|
const seen = new Set<string>();
|
|
@@ -22,9 +23,24 @@ export function extractSlackUserMentionIds(text: string): string[] {
|
|
|
22
23
|
return ids;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
export function extractSlackChannelReferenceIds(text: string): string[] {
|
|
27
|
+
const seen = new Set<string>();
|
|
28
|
+
const ids: string[] = [];
|
|
29
|
+
|
|
30
|
+
for (const match of text.matchAll(SLACK_CHANNEL_REFERENCE_RE)) {
|
|
31
|
+
const id = match[1];
|
|
32
|
+
if (!seen.has(id)) {
|
|
33
|
+
seen.add(id);
|
|
34
|
+
ids.push(id);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return ids;
|
|
39
|
+
}
|
|
40
|
+
|
|
25
41
|
export function renderSlackTextForModel(
|
|
26
42
|
text: string,
|
|
27
|
-
options: RenderSlackTextOptions = {}
|
|
43
|
+
options: RenderSlackTextOptions = {},
|
|
28
44
|
): string {
|
|
29
45
|
return text.replace(/<([^<>\s][^<>]*)>/g, (token, content: string) => {
|
|
30
46
|
if (content.startsWith("@")) {
|
|
@@ -49,7 +65,7 @@ export function renderSlackTextForModel(
|
|
|
49
65
|
|
|
50
66
|
export async function buildSlackUserLabelMap(
|
|
51
67
|
texts: Iterable<string | undefined>,
|
|
52
|
-
resolveLabel: (userId: string) => Promise<string | undefined | null
|
|
68
|
+
resolveLabel: (userId: string) => Promise<string | undefined | null>,
|
|
53
69
|
): Promise<Record<string, string>> {
|
|
54
70
|
const ids: string[] = [];
|
|
55
71
|
const seen = new Set<string>();
|
|
@@ -66,18 +82,55 @@ export async function buildSlackUserLabelMap(
|
|
|
66
82
|
if (ids.length === 0) return {};
|
|
67
83
|
|
|
68
84
|
const entries = await Promise.all(
|
|
69
|
-
ids.map(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
ids.map(async (id): Promise<[string, string] | undefined> => {
|
|
86
|
+
try {
|
|
87
|
+
const label = await resolveLabel(id);
|
|
88
|
+
const sanitized = sanitizeOptionalLabel(label ?? undefined);
|
|
89
|
+
if (!sanitized || sanitized === id) return undefined;
|
|
90
|
+
return [id, sanitized];
|
|
91
|
+
} catch {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return Object.fromEntries(entries.filter((entry) => entry !== undefined));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function buildSlackChannelLabelMap(
|
|
101
|
+
texts: Iterable<string | undefined>,
|
|
102
|
+
resolveLabel: (channelId: string) => Promise<string | undefined | null>,
|
|
103
|
+
): Promise<Record<string, string>> {
|
|
104
|
+
const ids: string[] = [];
|
|
105
|
+
const seen = new Set<string>();
|
|
106
|
+
|
|
107
|
+
for (const text of texts) {
|
|
108
|
+
if (!text) continue;
|
|
109
|
+
for (const match of text.matchAll(SLACK_CHANNEL_REFERENCE_RE)) {
|
|
110
|
+
const id = match[1];
|
|
111
|
+
const [, embeddedLabel] = splitSlackLabel(match[0].slice(1, -1));
|
|
112
|
+
const sanitizedEmbeddedLabel = sanitizeOptionalLabel(embeddedLabel);
|
|
113
|
+
if (sanitizedEmbeddedLabel && sanitizedEmbeddedLabel !== id) continue;
|
|
114
|
+
if (!seen.has(id)) {
|
|
115
|
+
seen.add(id);
|
|
116
|
+
ids.push(id);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (ids.length === 0) return {};
|
|
122
|
+
|
|
123
|
+
const entries = await Promise.all(
|
|
124
|
+
ids.map(async (id): Promise<[string, string] | undefined> => {
|
|
125
|
+
try {
|
|
126
|
+
const label = await resolveLabel(id);
|
|
127
|
+
const sanitized = sanitizeOptionalLabel(label ?? undefined);
|
|
128
|
+
if (!sanitized || sanitized === id) return undefined;
|
|
129
|
+
return [id, sanitized];
|
|
130
|
+
} catch {
|
|
131
|
+
return undefined;
|
|
79
132
|
}
|
|
80
|
-
)
|
|
133
|
+
}),
|
|
81
134
|
);
|
|
82
135
|
|
|
83
136
|
return Object.fromEntries(entries.filter((entry) => entry !== undefined));
|
|
@@ -85,7 +138,7 @@ export async function buildSlackUserLabelMap(
|
|
|
85
138
|
|
|
86
139
|
function renderUserMention(
|
|
87
140
|
content: string,
|
|
88
|
-
options: RenderSlackTextOptions
|
|
141
|
+
options: RenderSlackTextOptions,
|
|
89
142
|
): string {
|
|
90
143
|
const id = content.slice(1);
|
|
91
144
|
if (!isSlackUserId(id)) {
|
|
@@ -102,16 +155,27 @@ function renderUserMention(
|
|
|
102
155
|
|
|
103
156
|
function renderChannelReference(
|
|
104
157
|
content: string,
|
|
105
|
-
options: RenderSlackTextOptions
|
|
158
|
+
options: RenderSlackTextOptions,
|
|
106
159
|
): string {
|
|
107
160
|
const [idWithPrefix, label] = splitSlackLabel(content);
|
|
108
161
|
const channelId = idWithPrefix.slice(1);
|
|
109
162
|
const fallback = sanitizeLabel(
|
|
110
163
|
options.channelFallbackLabel,
|
|
111
|
-
"unknown-channel"
|
|
164
|
+
"unknown-channel",
|
|
112
165
|
);
|
|
113
|
-
const
|
|
114
|
-
|
|
166
|
+
const embeddedLabel = sanitizeOptionalLabel(label);
|
|
167
|
+
if (embeddedLabel && embeddedLabel !== channelId) {
|
|
168
|
+
return `#${embeddedLabel}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const resolvedLabel = sanitizeOptionalLabel(
|
|
172
|
+
options.channelLabels?.[channelId],
|
|
173
|
+
);
|
|
174
|
+
if (resolvedLabel && resolvedLabel !== channelId) {
|
|
175
|
+
return `#${resolvedLabel}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return `#${fallback}`;
|
|
115
179
|
}
|
|
116
180
|
|
|
117
181
|
function renderSpecialReference(content: string): string {
|