@vellumai/assistant 0.8.2 → 0.8.4
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 +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- 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-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- 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 +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- 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 +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -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__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- 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/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- 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 +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -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/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- 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-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- 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/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- 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 +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- 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 +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -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 +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- 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 +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- 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__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for {@link searchPlugins}.
|
|
3
|
+
*
|
|
4
|
+
* Network is replaced with an in-memory fixture passed via the `fetch`
|
|
5
|
+
* dependency — no globals are monkey-patched and no `--test-hook` exports
|
|
6
|
+
* leak into production code.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, expect, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
type FetchLike,
|
|
13
|
+
InvalidSearchPatternError,
|
|
14
|
+
searchPlugins,
|
|
15
|
+
} from "../search-plugins.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build a GitHub Contents API fixture from an in-memory directory listing.
|
|
19
|
+
*
|
|
20
|
+
* `entries` maps each name under `experimental/plugins/` to its `type`. The
|
|
21
|
+
* fixture answers GET requests against
|
|
22
|
+
* - `https://api.github.com/repos/vellum-ai/vellum-assistant/contents/experimental/plugins...`
|
|
23
|
+
* and returns 500 for anything else (forces test bugs to surface loudly).
|
|
24
|
+
*/
|
|
25
|
+
function fixtureFetch(
|
|
26
|
+
entries: Record<string, "dir" | "file" | "symlink" | "submodule">,
|
|
27
|
+
): FetchLike {
|
|
28
|
+
const PREFIX_API =
|
|
29
|
+
"https://api.github.com/repos/vellum-ai/vellum-assistant/contents/experimental/plugins";
|
|
30
|
+
|
|
31
|
+
return (async (input: RequestInfo | URL) => {
|
|
32
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
33
|
+
if (!url.startsWith(PREFIX_API)) {
|
|
34
|
+
return new Response("unexpected url: " + url, { status: 500 });
|
|
35
|
+
}
|
|
36
|
+
const body = Object.entries(entries).map(([name, type]) => ({
|
|
37
|
+
name,
|
|
38
|
+
path: `experimental/plugins/${name}`,
|
|
39
|
+
type,
|
|
40
|
+
size: type === "file" ? 1 : 0,
|
|
41
|
+
download_url:
|
|
42
|
+
type === "file"
|
|
43
|
+
? `https://raw.githubusercontent.com/vellum-ai/vellum-assistant/main/experimental/plugins/${name}`
|
|
44
|
+
: null,
|
|
45
|
+
}));
|
|
46
|
+
return new Response(JSON.stringify(body), {
|
|
47
|
+
status: 200,
|
|
48
|
+
headers: { "content-type": "application/json" },
|
|
49
|
+
});
|
|
50
|
+
}) as FetchLike;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("searchPlugins", () => {
|
|
54
|
+
test("matches the query as a case-insensitive regex against directory names", async () => {
|
|
55
|
+
const result = await searchPlugins(
|
|
56
|
+
{ query: "memory" },
|
|
57
|
+
{
|
|
58
|
+
fetch: fixtureFetch({
|
|
59
|
+
"simple-memory": "dir",
|
|
60
|
+
"memory-graph": "dir",
|
|
61
|
+
"git-tools": "dir",
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(result.matches.map((m) => m.name)).toEqual([
|
|
67
|
+
"memory-graph",
|
|
68
|
+
"simple-memory",
|
|
69
|
+
]);
|
|
70
|
+
expect(result.matches[0]!.path).toBe("experimental/plugins/memory-graph");
|
|
71
|
+
expect(result.query).toBe("memory");
|
|
72
|
+
expect(result.ref).toBe("main");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("matches regardless of query casing (case-insensitive)", async () => {
|
|
76
|
+
const result = await searchPlugins(
|
|
77
|
+
{ query: "MEMORY" },
|
|
78
|
+
{ fetch: fixtureFetch({ "simple-memory": "dir" }) },
|
|
79
|
+
);
|
|
80
|
+
expect(result.matches.map((m) => m.name)).toEqual(["simple-memory"]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("anchored patterns work without escaping", async () => {
|
|
84
|
+
const result = await searchPlugins(
|
|
85
|
+
{ query: "^memory-" },
|
|
86
|
+
{
|
|
87
|
+
fetch: fixtureFetch({
|
|
88
|
+
"memory-graph": "dir",
|
|
89
|
+
"simple-memory": "dir",
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
expect(result.matches.map((m) => m.name)).toEqual(["memory-graph"]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("empty query matches all directories", async () => {
|
|
97
|
+
const result = await searchPlugins(
|
|
98
|
+
{ query: "" },
|
|
99
|
+
{
|
|
100
|
+
fetch: fixtureFetch({
|
|
101
|
+
"simple-memory": "dir",
|
|
102
|
+
"memory-graph": "dir",
|
|
103
|
+
"git-tools": "dir",
|
|
104
|
+
}),
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
expect(result.matches.map((m) => m.name)).toEqual([
|
|
108
|
+
"git-tools",
|
|
109
|
+
"memory-graph",
|
|
110
|
+
"simple-memory",
|
|
111
|
+
]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("skips entries that are not directories", async () => {
|
|
115
|
+
const result = await searchPlugins(
|
|
116
|
+
{ query: "" },
|
|
117
|
+
{
|
|
118
|
+
fetch: fixtureFetch({
|
|
119
|
+
"simple-memory": "dir",
|
|
120
|
+
"README.md": "file",
|
|
121
|
+
"broken-symlink": "symlink",
|
|
122
|
+
"old-plugin": "submodule",
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
expect(result.matches.map((m) => m.name)).toEqual(["simple-memory"]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("rejects invalid regex patterns up front (no network call)", async () => {
|
|
130
|
+
let fetchCalled = false;
|
|
131
|
+
const fetch: FetchLike = (async () => {
|
|
132
|
+
fetchCalled = true;
|
|
133
|
+
return new Response("", { status: 200 });
|
|
134
|
+
}) as FetchLike;
|
|
135
|
+
|
|
136
|
+
await expect(
|
|
137
|
+
searchPlugins({ query: "(unterminated" }, { fetch }),
|
|
138
|
+
).rejects.toBeInstanceOf(InvalidSearchPatternError);
|
|
139
|
+
expect(fetchCalled).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("empty result set on no matches", async () => {
|
|
143
|
+
const result = await searchPlugins(
|
|
144
|
+
{ query: "nothing-matches" },
|
|
145
|
+
{ fetch: fixtureFetch({ "simple-memory": "dir" }) },
|
|
146
|
+
);
|
|
147
|
+
expect(result.matches).toEqual([]);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("respects `ref` option by forwarding to GitHub", async () => {
|
|
151
|
+
// The CLI does not surface a `--ref` flag (the source-path convention
|
|
152
|
+
// may change), but the underlying function keeps `ref` for test
|
|
153
|
+
// injection and future internal callers.
|
|
154
|
+
let seenRef: string | undefined;
|
|
155
|
+
const result = await searchPlugins(
|
|
156
|
+
{ query: "memory", ref: "feat-branch" },
|
|
157
|
+
{
|
|
158
|
+
fetch: (async (input: RequestInfo | URL) => {
|
|
159
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
160
|
+
const m = /[?&]ref=([^&]+)/.exec(url);
|
|
161
|
+
seenRef = m ? decodeURIComponent(m[1]!) : undefined;
|
|
162
|
+
return new Response(
|
|
163
|
+
JSON.stringify([
|
|
164
|
+
{
|
|
165
|
+
name: "simple-memory",
|
|
166
|
+
path: "experimental/plugins/simple-memory",
|
|
167
|
+
type: "dir",
|
|
168
|
+
size: 0,
|
|
169
|
+
download_url: null,
|
|
170
|
+
},
|
|
171
|
+
]),
|
|
172
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
173
|
+
);
|
|
174
|
+
}) as FetchLike,
|
|
175
|
+
},
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
expect(seenRef).toBe("feat-branch");
|
|
179
|
+
expect(result.ref).toBe("feat-branch");
|
|
180
|
+
expect(result.matches.map((m) => m.name)).toEqual(["simple-memory"]);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("HTTP 5xx from GitHub propagates with the status code", async () => {
|
|
184
|
+
await expect(
|
|
185
|
+
searchPlugins(
|
|
186
|
+
{ query: "memory" },
|
|
187
|
+
{
|
|
188
|
+
fetch: (async () =>
|
|
189
|
+
new Response("upstream broken", { status: 503 })) as FetchLike,
|
|
190
|
+
},
|
|
191
|
+
),
|
|
192
|
+
).rejects.toThrow(/HTTP 503/);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("HTTP 403 (rate-limited / forbidden) surfaces as an error", async () => {
|
|
196
|
+
await expect(
|
|
197
|
+
searchPlugins(
|
|
198
|
+
{ query: "memory" },
|
|
199
|
+
{
|
|
200
|
+
fetch: (async () =>
|
|
201
|
+
new Response("rate limit exceeded", { status: 403 })) as FetchLike,
|
|
202
|
+
},
|
|
203
|
+
),
|
|
204
|
+
).rejects.toThrow(/HTTP 403/);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("404 on the plugins prefix surfaces as an error (not silently empty)", async () => {
|
|
208
|
+
// Distinct from `installPlugin`, where 404 on a specific plugin name is
|
|
209
|
+
// normal "not found". For the search, 404 on the prefix means the
|
|
210
|
+
// canonical source path itself is gone — that's an upstream problem
|
|
211
|
+
// worth surfacing, not a clean empty result.
|
|
212
|
+
await expect(
|
|
213
|
+
searchPlugins(
|
|
214
|
+
{ query: "memory" },
|
|
215
|
+
{
|
|
216
|
+
fetch: (async () =>
|
|
217
|
+
new Response("not found", { status: 404 })) as FetchLike,
|
|
218
|
+
},
|
|
219
|
+
),
|
|
220
|
+
).rejects.toThrow(/HTTP 404/);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("returns matches sorted by name", async () => {
|
|
224
|
+
const result = await searchPlugins(
|
|
225
|
+
{ query: "" },
|
|
226
|
+
{
|
|
227
|
+
fetch: fixtureFetch({
|
|
228
|
+
"zeta-plugin": "dir",
|
|
229
|
+
"alpha-plugin": "dir",
|
|
230
|
+
"mu-plugin": "dir",
|
|
231
|
+
}),
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
expect(result.matches.map((m) => m.name)).toEqual([
|
|
235
|
+
"alpha-plugin",
|
|
236
|
+
"mu-plugin",
|
|
237
|
+
"zeta-plugin",
|
|
238
|
+
]);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("sends no Authorization header (canonical source is a public repo)", async () => {
|
|
242
|
+
let seenAuth: string | undefined;
|
|
243
|
+
let seenUserAgent: string | undefined;
|
|
244
|
+
await searchPlugins(
|
|
245
|
+
{ query: "memory" },
|
|
246
|
+
{
|
|
247
|
+
fetch: (async (_input: RequestInfo | URL, init?: RequestInit) => {
|
|
248
|
+
const headers = init?.headers as Record<string, string> | undefined;
|
|
249
|
+
seenAuth = headers?.Authorization;
|
|
250
|
+
seenUserAgent = headers?.["User-Agent"];
|
|
251
|
+
return new Response("[]", {
|
|
252
|
+
status: 200,
|
|
253
|
+
headers: { "content-type": "application/json" },
|
|
254
|
+
});
|
|
255
|
+
}) as FetchLike,
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
expect(seenAuth).toBeUndefined();
|
|
259
|
+
expect(seenUserAgent).toBe("vellum-assistant-cli");
|
|
260
|
+
});
|
|
261
|
+
});
|
|
@@ -286,19 +286,18 @@ async function copyFile(
|
|
|
286
286
|
|
|
287
287
|
/**
|
|
288
288
|
* Wraps `fetchFn` with the headers we want to send to GitHub for every
|
|
289
|
-
* request.
|
|
290
|
-
*
|
|
289
|
+
* request. Unauthenticated — the canonical source is a public repo, so
|
|
290
|
+
* there is nothing for an `Authorization` header to do.
|
|
291
291
|
*/
|
|
292
292
|
async function githubFetch(
|
|
293
293
|
url: string,
|
|
294
294
|
accept: string,
|
|
295
295
|
fetchFn: FetchLike,
|
|
296
296
|
): Promise<Response> {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return fetchFn(url, { headers });
|
|
297
|
+
return fetchFn(url, {
|
|
298
|
+
headers: {
|
|
299
|
+
Accept: accept,
|
|
300
|
+
"User-Agent": "vellum-assistant-cli",
|
|
301
|
+
},
|
|
302
|
+
});
|
|
304
303
|
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search for plugin directories in the canonical GitHub source.
|
|
3
|
+
*
|
|
4
|
+
* Lists `vellum-ai/vellum-assistant/experimental/plugins/` at the configured
|
|
5
|
+
* git ref and filters the directory entries by case-insensitive ECMAScript
|
|
6
|
+
* regex. A plain query like `"memory"` matches anywhere in the name; anchors
|
|
7
|
+
* like `"^simple"` work without escaping.
|
|
8
|
+
*
|
|
9
|
+
* Designed for direct programmatic use. The CLI command
|
|
10
|
+
* `assistant plugins search <query>` is a thin wrapper that supplies
|
|
11
|
+
* production deps (`globalThis.fetch`) and formats the result for the
|
|
12
|
+
* terminal; downstream callers may supply their own `fetch` (e.g. a
|
|
13
|
+
* retry-decorated client, or a test fixture).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { FetchLike } from "./install-from-github.js";
|
|
17
|
+
import { DEFAULT_PLUGIN_REF } from "./install-from-github.js";
|
|
18
|
+
|
|
19
|
+
// Re-export the dep-injection type so callers can grab everything they need
|
|
20
|
+
// from one module rather than reaching into `install-from-github.js`.
|
|
21
|
+
export type { FetchLike } from "./install-from-github.js";
|
|
22
|
+
|
|
23
|
+
const PLUGIN_SOURCE_OWNER = "vellum-ai";
|
|
24
|
+
const PLUGIN_SOURCE_REPO = "vellum-assistant";
|
|
25
|
+
const PLUGIN_SOURCE_PATH_PREFIX = "experimental/plugins";
|
|
26
|
+
|
|
27
|
+
/** Entry shape returned by the GitHub Contents API for a directory listing. */
|
|
28
|
+
interface GitHubContentEntry {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly path: string;
|
|
31
|
+
readonly type: "file" | "dir" | "symlink" | "submodule";
|
|
32
|
+
readonly size: number;
|
|
33
|
+
readonly download_url: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Options that control the search. */
|
|
37
|
+
export interface SearchPluginsOptions {
|
|
38
|
+
/**
|
|
39
|
+
* ECMAScript regex pattern. Matched case-insensitively against directory
|
|
40
|
+
* names. Empty string matches everything.
|
|
41
|
+
*/
|
|
42
|
+
readonly query: string;
|
|
43
|
+
/** Git ref to list from. Defaults to {@link DEFAULT_PLUGIN_REF}. */
|
|
44
|
+
readonly ref?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Dependencies injected by the caller. */
|
|
48
|
+
export interface SearchPluginsDeps {
|
|
49
|
+
/** HTTP client. Production callers pass `globalThis.fetch.bind(globalThis)`. */
|
|
50
|
+
readonly fetch: FetchLike;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** One matching plugin directory. */
|
|
54
|
+
export interface PluginSearchMatch {
|
|
55
|
+
/** Directory name under `experimental/plugins/`. */
|
|
56
|
+
readonly name: string;
|
|
57
|
+
/** Path within the repo (e.g. `experimental/plugins/<name>`). */
|
|
58
|
+
readonly path: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Search result envelope. */
|
|
62
|
+
export interface SearchPluginsResult {
|
|
63
|
+
readonly query: string;
|
|
64
|
+
readonly ref: string;
|
|
65
|
+
readonly matches: readonly PluginSearchMatch[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Caller passed a query that doesn't compile as an ECMAScript regex. */
|
|
69
|
+
export class InvalidSearchPatternError extends Error {
|
|
70
|
+
constructor(pattern: string, cause: unknown) {
|
|
71
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
72
|
+
super(`Invalid regex pattern ${JSON.stringify(pattern)}: ${detail}`);
|
|
73
|
+
this.name = "InvalidSearchPatternError";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* List directories under `experimental/plugins/` at {@link opts.ref} and
|
|
79
|
+
* filter by {@link opts.query}.
|
|
80
|
+
*
|
|
81
|
+
* Only `type === "dir"` entries are returned — `experimental/plugins/`
|
|
82
|
+
* follows a convention where each plugin lives in its own directory, so
|
|
83
|
+
* loose files at the prefix are not plugins.
|
|
84
|
+
*/
|
|
85
|
+
export async function searchPlugins(
|
|
86
|
+
opts: SearchPluginsOptions,
|
|
87
|
+
deps: SearchPluginsDeps,
|
|
88
|
+
): Promise<SearchPluginsResult> {
|
|
89
|
+
const ref = opts.ref ?? DEFAULT_PLUGIN_REF;
|
|
90
|
+
|
|
91
|
+
// Compile the matcher up front so an invalid regex fails before we hit
|
|
92
|
+
// the network — keeps "user typo" cheap to recover from.
|
|
93
|
+
const matcher = buildMatcher(opts.query);
|
|
94
|
+
|
|
95
|
+
const entries = await listDir(PLUGIN_SOURCE_PATH_PREFIX, ref, deps.fetch);
|
|
96
|
+
|
|
97
|
+
const matches: PluginSearchMatch[] = [];
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
if (entry.type !== "dir") continue;
|
|
100
|
+
if (!matcher(entry.name)) continue;
|
|
101
|
+
matches.push({ name: entry.name, path: entry.path });
|
|
102
|
+
}
|
|
103
|
+
matches.sort((a, b) => a.name.localeCompare(b.name));
|
|
104
|
+
|
|
105
|
+
return { query: opts.query, ref, matches };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildMatcher(query: string): (name: string) => boolean {
|
|
109
|
+
let re: RegExp;
|
|
110
|
+
try {
|
|
111
|
+
re = new RegExp(query, "i");
|
|
112
|
+
} catch (err) {
|
|
113
|
+
throw new InvalidSearchPatternError(query, err);
|
|
114
|
+
}
|
|
115
|
+
return (name) => re.test(name);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function listDir(
|
|
119
|
+
apiPath: string,
|
|
120
|
+
ref: string,
|
|
121
|
+
fetchFn: FetchLike,
|
|
122
|
+
): Promise<readonly GitHubContentEntry[]> {
|
|
123
|
+
const url =
|
|
124
|
+
`https://api.github.com/repos/${PLUGIN_SOURCE_OWNER}/${PLUGIN_SOURCE_REPO}` +
|
|
125
|
+
`/contents/${encodeURIComponent(apiPath).replaceAll("%2F", "/")}` +
|
|
126
|
+
`?ref=${encodeURIComponent(ref)}`;
|
|
127
|
+
|
|
128
|
+
const res = await githubFetch(url, fetchFn);
|
|
129
|
+
if (!res.ok) {
|
|
130
|
+
// Unlike `installPlugin`, where 404 on a specific plugin name is a
|
|
131
|
+
// legitimate "not found" outcome, 404 on the plugins prefix itself
|
|
132
|
+
// means the canonical source path is gone — surface it as an error
|
|
133
|
+
// rather than silently returning empty results.
|
|
134
|
+
throw new Error(
|
|
135
|
+
`GitHub contents listing failed for ${apiPath} @ ${ref}: HTTP ${res.status}`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const body = (await res.json()) as unknown;
|
|
140
|
+
if (!Array.isArray(body)) {
|
|
141
|
+
// A non-array body for a /contents/<dir> path means the path is a
|
|
142
|
+
// file, not a directory — treat the prefix as empty rather than crash.
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
return body as readonly GitHubContentEntry[];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Wraps `fetchFn` with the headers we want to send to GitHub for every
|
|
150
|
+
* request. Unauthenticated — the canonical source is a public repo, mirroring
|
|
151
|
+
* `installPlugin` which uses the same envelope.
|
|
152
|
+
*/
|
|
153
|
+
async function githubFetch(
|
|
154
|
+
url: string,
|
|
155
|
+
fetchFn: FetchLike,
|
|
156
|
+
): Promise<Response> {
|
|
157
|
+
return fetchFn(url, {
|
|
158
|
+
headers: {
|
|
159
|
+
Accept: "application/vnd.github+json",
|
|
160
|
+
"User-Agent": "vellum-assistant-cli",
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
package/src/cli/program.ts
CHANGED
|
@@ -62,6 +62,20 @@ import { log } from "./logger.js";
|
|
|
62
62
|
*/
|
|
63
63
|
export async function buildCliProgram(): Promise<Command> {
|
|
64
64
|
await initFeatureFlagOverrides({ retryBackoffsMs: [], callTimeoutMs: 200 });
|
|
65
|
+
return buildCliProgramTree();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Synchronously build the CLI program tree without pre-populating the
|
|
70
|
+
* feature-flag cache. Use this from inside the daemon, where flags are
|
|
71
|
+
* already initialized — calling `buildCliProgram` from there would round-trip
|
|
72
|
+
* to the gateway unnecessarily.
|
|
73
|
+
*
|
|
74
|
+
* Same shape as `buildCliProgram` minus the async feature-flag init: registers
|
|
75
|
+
* the full subcommand set (conditionally gated on email / external-plugins
|
|
76
|
+
* flags via `getConfigReadOnly()`) and installs the workspace-preAction hook.
|
|
77
|
+
*/
|
|
78
|
+
export function buildCliProgramTree(): Command {
|
|
65
79
|
const program = new Command();
|
|
66
80
|
|
|
67
81
|
program
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* 1. Explicit value provided in `opts.explicit`
|
|
4
4
|
* 2. `__SKILL_CONTEXT_JSON` env var (set by skill sandbox runner)
|
|
5
5
|
* 3. `__CONVERSATION_ID` env var (set by bash tool subprocess)
|
|
6
|
-
* 4.
|
|
6
|
+
* 4. `undefined`
|
|
7
7
|
*/
|
|
8
|
-
export function
|
|
9
|
-
explicit?: string
|
|
10
|
-
|
|
11
|
-
}): string {
|
|
8
|
+
export function tryResolveConversationId(
|
|
9
|
+
opts: { explicit?: string } = {},
|
|
10
|
+
): string | undefined {
|
|
12
11
|
if (opts.explicit) return opts.explicit;
|
|
13
12
|
|
|
14
13
|
const contextJson = process.env.__SKILL_CONTEXT_JSON;
|
|
@@ -26,5 +25,18 @@ export function resolveConversationId(opts: {
|
|
|
26
25
|
const envConvId = process.env.__CONVERSATION_ID;
|
|
27
26
|
if (envConvId && typeof envConvId === "string") return envConvId;
|
|
28
27
|
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Same precedence as `tryResolveConversationId` but throws with the
|
|
33
|
+
* provided `failureHelp` when no source produces a value.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveConversationId(opts: {
|
|
36
|
+
explicit?: string;
|
|
37
|
+
failureHelp: string;
|
|
38
|
+
}): string {
|
|
39
|
+
const resolved = tryResolveConversationId({ explicit: opts.explicit });
|
|
40
|
+
if (resolved) return resolved;
|
|
29
41
|
throw new Error(opts.failureHelp);
|
|
30
42
|
}
|
|
@@ -110,20 +110,22 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
// ---------------------------------------------------------------------------
|
|
113
|
-
// Override loading — reads from gateway IPC socket
|
|
113
|
+
// Override loading — reads from gateway IPC socket
|
|
114
114
|
// ---------------------------------------------------------------------------
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
|
-
* Module-level cache of feature flag override values. Populated
|
|
118
|
-
*
|
|
117
|
+
* Module-level cache of feature flag override values. Populated by
|
|
118
|
+
* `initFeatureFlagOverrides()` at startup, invalidated by
|
|
119
|
+
* `clearFeatureFlagOverridesCache()`.
|
|
119
120
|
*/
|
|
120
121
|
let cachedOverrides: Record<string, boolean> | null = null;
|
|
121
122
|
|
|
122
123
|
/**
|
|
123
|
-
* True when `cachedOverrides` was populated by the gateway IPC fetch
|
|
124
|
-
* preseeded by a test
|
|
125
|
-
*
|
|
126
|
-
*
|
|
124
|
+
* True when `cachedOverrides` was populated by the gateway IPC fetch or
|
|
125
|
+
* preseeded by a test via `_setOverridesForTesting()`. Guards
|
|
126
|
+
* `initFeatureFlagOverrides()` from clobbering an existing populated cache
|
|
127
|
+
* when called a second time (e.g. by a CLI entry point after the daemon
|
|
128
|
+
* has already initialized).
|
|
127
129
|
*/
|
|
128
130
|
let cachedOverridesFromGateway = false;
|
|
129
131
|
|
|
@@ -247,59 +249,30 @@ function loadOverrides(): Record<string, boolean> {
|
|
|
247
249
|
return cachedOverrides ?? {};
|
|
248
250
|
}
|
|
249
251
|
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
|
-
// Remote values — platform-pushed flags cached in a local JSON file
|
|
252
|
-
// ---------------------------------------------------------------------------
|
|
253
|
-
|
|
254
252
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*/
|
|
258
|
-
let cachedRemoteValues: Record<string, boolean> | null = null;
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Load remote values with module-level caching.
|
|
253
|
+
* Invalidate the cached overrides so the next call to
|
|
254
|
+
* `isAssistantFeatureFlagEnabled` re-reads from the gateway.
|
|
262
255
|
*
|
|
263
|
-
*
|
|
264
|
-
* server-side), so this only returns the injected test cache. In production,
|
|
265
|
-
* remote values flow through the overrides cache.
|
|
266
|
-
*/
|
|
267
|
-
function loadRemoteValues(): Record<string, boolean> {
|
|
268
|
-
return cachedRemoteValues ?? {};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Invalidate the cached override and remote values so the next call to
|
|
273
|
-
* `isAssistantFeatureFlagEnabled` re-reads from the source.
|
|
274
|
-
*
|
|
275
|
-
* Called by the config watcher when the feature-flags file changes.
|
|
256
|
+
* Used by tests between cases to reset module state.
|
|
276
257
|
*/
|
|
277
258
|
export function clearFeatureFlagOverridesCache(): void {
|
|
278
259
|
cachedOverrides = null;
|
|
279
260
|
cachedOverridesFromGateway = false;
|
|
280
|
-
cachedRemoteValues = null;
|
|
281
261
|
}
|
|
282
262
|
|
|
283
263
|
/**
|
|
284
264
|
* Directly inject override values into the module-level cache.
|
|
285
265
|
*
|
|
286
|
-
* **Test-only** — bypasses
|
|
287
|
-
* flag state without
|
|
288
|
-
* use `clearFeatureFlagOverridesCache()` instead and let
|
|
289
|
-
* re-read from the
|
|
290
|
-
*
|
|
291
|
-
* Forces `cachedRemoteValues` to an empty record (not `null`) so the resolver
|
|
292
|
-
* does not fall through to reading `feature-flags-remote.json` from disk. This
|
|
293
|
-
* matters because a developer's local remote-cache file can leak platform-set
|
|
294
|
-
* values into the test environment (e.g. `email-channel: true`), defeating
|
|
295
|
-
* test isolation.
|
|
266
|
+
* **Test-only** — bypasses the gateway IPC fetch so unit tests can control
|
|
267
|
+
* flag state without standing up a real gateway. Production code should
|
|
268
|
+
* never call this; use `clearFeatureFlagOverridesCache()` instead and let
|
|
269
|
+
* the resolver re-read from the gateway.
|
|
296
270
|
*/
|
|
297
271
|
export function _setOverridesForTesting(
|
|
298
272
|
overrides: Record<string, boolean>,
|
|
299
273
|
): void {
|
|
300
274
|
cachedOverrides = { ...overrides };
|
|
301
275
|
cachedOverridesFromGateway = true;
|
|
302
|
-
cachedRemoteValues = {};
|
|
303
276
|
}
|
|
304
277
|
|
|
305
278
|
// ---------------------------------------------------------------------------
|
|
@@ -310,9 +283,11 @@ export function _setOverridesForTesting(
|
|
|
310
283
|
* Resolve whether an assistant feature flag is enabled.
|
|
311
284
|
*
|
|
312
285
|
* Resolution order:
|
|
313
|
-
* 1. Override from gateway IPC
|
|
314
|
-
*
|
|
315
|
-
*
|
|
286
|
+
* 1. Override from the gateway IPC fetch (includes platform-pushed remote
|
|
287
|
+
* values, which the gateway merges server-side: persisted > remote >
|
|
288
|
+
* registry)
|
|
289
|
+
* 2. Registry `defaultEnabled` (for declared assistant-scope keys)
|
|
290
|
+
* 3. `true` (for undeclared keys with no override)
|
|
316
291
|
*/
|
|
317
292
|
export function isAssistantFeatureFlagEnabled(
|
|
318
293
|
key: string,
|
|
@@ -322,18 +297,13 @@ export function isAssistantFeatureFlagEnabled(
|
|
|
322
297
|
const declared = defaults[key];
|
|
323
298
|
const overrides = loadOverrides();
|
|
324
299
|
|
|
325
|
-
// 1. Check overrides from gateway
|
|
300
|
+
// 1. Check overrides from the gateway IPC cache.
|
|
326
301
|
const explicit = overrides[key];
|
|
327
302
|
if (typeof explicit === "boolean") return explicit;
|
|
328
303
|
|
|
329
|
-
// 2.
|
|
330
|
-
const remote = loadRemoteValues();
|
|
331
|
-
const remoteValue = remote[key];
|
|
332
|
-
if (typeof remoteValue === "boolean") return remoteValue;
|
|
333
|
-
|
|
334
|
-
// 3. For declared keys, use the registry default
|
|
304
|
+
// 2. For declared keys, use the registry default.
|
|
335
305
|
if (declared) return declared.defaultEnabled;
|
|
336
306
|
|
|
337
|
-
//
|
|
307
|
+
// 3. Undeclared keys with no override default to enabled.
|
|
338
308
|
return true;
|
|
339
309
|
}
|