@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,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for PluginSourceWatcher — filesystem watcher that detects plugin
|
|
3
|
+
* directory changes and triggers debounced reregistration.
|
|
4
|
+
*
|
|
5
|
+
* Key regression: the watcher must survive (and recover from) the Linux/Bun
|
|
6
|
+
* recursive-watch limitation where subdirectories created after watch starts
|
|
7
|
+
* are silently dropped. We test that close→reopen + rescan catches these.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Mocks — must be set up before importing the module under test
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
const TEST_PLUGINS_DIR = "/tmp/test-plugins";
|
|
17
|
+
|
|
18
|
+
let capturedWatchCallback:
|
|
19
|
+
| ((eventType: string, filename: string | null) => void)
|
|
20
|
+
| null = null;
|
|
21
|
+
let mockWatchShouldThrow = false;
|
|
22
|
+
const mockWatcher = { close: mock(() => {}) };
|
|
23
|
+
|
|
24
|
+
const mockWatch = mock(
|
|
25
|
+
(
|
|
26
|
+
_path: string,
|
|
27
|
+
_opts: Record<string, unknown>,
|
|
28
|
+
callback: (eventType: string, filename: string | null) => void,
|
|
29
|
+
) => {
|
|
30
|
+
if (mockWatchShouldThrow) throw new Error("watch failed");
|
|
31
|
+
capturedWatchCallback = callback;
|
|
32
|
+
return mockWatcher;
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const mockRereadirSync = mock((_path: string): string[] => []);
|
|
37
|
+
|
|
38
|
+
let mockGetRegisteredPluginImpl: (name: string) => unknown | undefined = (
|
|
39
|
+
_name,
|
|
40
|
+
) => undefined;
|
|
41
|
+
const mockGetRegisteredPlugin = mock((name: string) =>
|
|
42
|
+
mockGetRegisteredPluginImpl(name),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
let mockReregisterExternalPluginImpl = mock(async (_name: string) => {});
|
|
46
|
+
const mockReregisterExternalPlugin = mock(async (name: string) =>
|
|
47
|
+
mockReregisterExternalPluginImpl(name),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
mock.module("node:fs", () => {
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
52
|
+
const actualFs = require("node:fs");
|
|
53
|
+
return {
|
|
54
|
+
...actualFs,
|
|
55
|
+
watch: mockWatch,
|
|
56
|
+
readdirSync: mockRereadirSync,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
mock.module("../plugins/registry.js", () => ({
|
|
61
|
+
getRegisteredPlugin: mockGetRegisteredPlugin,
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
mock.module("../util/platform.js", () => ({
|
|
65
|
+
getWorkspacePluginsDir: mock(() => TEST_PLUGINS_DIR),
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
mock.module("../daemon/external-plugins-bootstrap.js", () => ({
|
|
69
|
+
reregisterExternalPlugin: mockReregisterExternalPlugin,
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
mock.module("../util/logger.js", () => ({
|
|
73
|
+
getLogger: mock(() => ({
|
|
74
|
+
info: mock(() => {}),
|
|
75
|
+
warn: mock(() => {}),
|
|
76
|
+
})),
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Import after mocks
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
import { PluginSourceWatcher } from "../daemon/plugin-source-watcher.js";
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Tests
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
describe("PluginSourceWatcher", () => {
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
PluginSourceWatcher.resetForTests();
|
|
92
|
+
capturedWatchCallback = null;
|
|
93
|
+
mockWatchShouldThrow = false;
|
|
94
|
+
mockWatcher.close.mockClear();
|
|
95
|
+
mockWatch.mockClear();
|
|
96
|
+
mockRereadirSync.mockClear();
|
|
97
|
+
mockGetRegisteredPlugin.mockClear();
|
|
98
|
+
mockReregisterExternalPlugin.mockClear();
|
|
99
|
+
mockGetRegisteredPluginImpl = (_name: string) => undefined;
|
|
100
|
+
mockReregisterExternalPluginImpl = mock(async (_name: string) => {});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
afterEach(() => {
|
|
104
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
105
|
+
watcher.stop();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("start() creates a recursive watcher on the plugins directory", () => {
|
|
109
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
110
|
+
watcher.start();
|
|
111
|
+
expect(capturedWatchCallback).not.toBeNull();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("plugin directory creation triggers rebuild", async () => {
|
|
115
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
116
|
+
watcher.start();
|
|
117
|
+
|
|
118
|
+
capturedWatchCallback!("change", "my-plugin");
|
|
119
|
+
|
|
120
|
+
// Wait for debounce
|
|
121
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
122
|
+
expect(mockReregisterExternalPlugin).toHaveBeenCalledWith("my-plugin");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("nested file change within plugin triggers rebuild", async () => {
|
|
126
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
127
|
+
watcher.start();
|
|
128
|
+
|
|
129
|
+
capturedWatchCallback!("change", "my-plugin/src/index.ts");
|
|
130
|
+
|
|
131
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
132
|
+
expect(mockReregisterExternalPlugin).toHaveBeenCalledWith("my-plugin");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("deeply nested file change triggers rebuild", async () => {
|
|
136
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
137
|
+
watcher.start();
|
|
138
|
+
|
|
139
|
+
capturedWatchCallback!("change", "my-plugin/src/handlers/util/helper.ts");
|
|
140
|
+
|
|
141
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
142
|
+
expect(mockReregisterExternalPlugin).toHaveBeenCalledWith("my-plugin");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("dotfiles in plugins root are ignored", async () => {
|
|
146
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
147
|
+
watcher.start();
|
|
148
|
+
|
|
149
|
+
capturedWatchCallback!("change", ".DS_Store");
|
|
150
|
+
|
|
151
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
152
|
+
expect(mockReregisterExternalPlugin).not.toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("null filename is ignored", async () => {
|
|
156
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
157
|
+
watcher.start();
|
|
158
|
+
|
|
159
|
+
capturedWatchCallback!("change", null);
|
|
160
|
+
|
|
161
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
162
|
+
expect(mockReregisterExternalPlugin).not.toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("rapid changes to same plugin are debounced into single rebuild", async () => {
|
|
166
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
167
|
+
watcher.start();
|
|
168
|
+
|
|
169
|
+
capturedWatchCallback!("change", "my-plugin/src/index.ts");
|
|
170
|
+
capturedWatchCallback!("change", "my-plugin/src/handlers.ts");
|
|
171
|
+
capturedWatchCallback!("change", "my-plugin/package.json");
|
|
172
|
+
|
|
173
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
174
|
+
expect(mockReregisterExternalPlugin).toHaveBeenCalledTimes(1);
|
|
175
|
+
expect(mockReregisterExternalPlugin).toHaveBeenCalledWith("my-plugin");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("changes to different plugins trigger separate rebuilds (debounced)", async () => {
|
|
179
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
180
|
+
watcher.start();
|
|
181
|
+
|
|
182
|
+
capturedWatchCallback!("change", "plugin-a/src/index.ts");
|
|
183
|
+
capturedWatchCallback!("change", "plugin-b/src/index.ts");
|
|
184
|
+
|
|
185
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
186
|
+
expect(mockReregisterExternalPlugin).toHaveBeenCalledTimes(2);
|
|
187
|
+
expect(mockReregisterExternalPlugin).toHaveBeenNthCalledWith(1, "plugin-a");
|
|
188
|
+
expect(mockReregisterExternalPlugin).toHaveBeenNthCalledWith(2, "plugin-b");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("stop() closes watcher and cancels pending rebuilds", () => {
|
|
192
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
193
|
+
watcher.start();
|
|
194
|
+
|
|
195
|
+
capturedWatchCallback!("change", "my-plugin/src/index.ts");
|
|
196
|
+
watcher.stop();
|
|
197
|
+
|
|
198
|
+
expect(mockWatcher.close).toHaveBeenCalledTimes(1);
|
|
199
|
+
// No rebuild should fire after stop
|
|
200
|
+
expect(mockReregisterExternalPlugin).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("ensureStarted() initializes watcher after start() if watch coverage was lost", () => {
|
|
204
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
205
|
+
watcher.start();
|
|
206
|
+
expect(capturedWatchCallback).not.toBeNull();
|
|
207
|
+
|
|
208
|
+
// Simulate lost coverage while the daemon lifecycle is still started
|
|
209
|
+
// (e.g. a previous watch attempt failed after startup).
|
|
210
|
+
(watcher as unknown as { watcher: unknown }).watcher = null;
|
|
211
|
+
capturedWatchCallback = null;
|
|
212
|
+
|
|
213
|
+
watcher.ensureStarted();
|
|
214
|
+
expect(capturedWatchCallback).not.toBeNull();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("ensureStarted() is a no-op when watcher is already running", () => {
|
|
218
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
219
|
+
watcher.start();
|
|
220
|
+
const callCountAfterStart = mockWatch.mock.calls.length;
|
|
221
|
+
|
|
222
|
+
watcher.ensureStarted();
|
|
223
|
+
expect(mockWatch.mock.calls.length).toBe(callCountAfterStart);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("watcher restart keeps the previous watcher active if replacement fails", async () => {
|
|
227
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
228
|
+
watcher.start();
|
|
229
|
+
const firstCallback = capturedWatchCallback;
|
|
230
|
+
|
|
231
|
+
mockWatchShouldThrow = true;
|
|
232
|
+
capturedWatchCallback!("change", "my-plugin/src/index.ts");
|
|
233
|
+
|
|
234
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
235
|
+
|
|
236
|
+
expect(mockWatcher.close).not.toHaveBeenCalled();
|
|
237
|
+
expect((watcher as unknown as { watcher: unknown }).watcher).toBe(
|
|
238
|
+
mockWatcher,
|
|
239
|
+
);
|
|
240
|
+
expect(capturedWatchCallback).toBe(firstCallback);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("singleton instance is shared across calls", () => {
|
|
244
|
+
const watcher1 = PluginSourceWatcher.getInstance();
|
|
245
|
+
const watcher2 = PluginSourceWatcher.getInstance();
|
|
246
|
+
expect(watcher1).toBe(watcher2);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("resetForTests() clears the singleton", () => {
|
|
250
|
+
const watcher1 = PluginSourceWatcher.getInstance();
|
|
251
|
+
watcher1.start();
|
|
252
|
+
|
|
253
|
+
PluginSourceWatcher.resetForTests();
|
|
254
|
+
const watcher2 = PluginSourceWatcher.getInstance();
|
|
255
|
+
|
|
256
|
+
expect(watcher1).not.toBe(watcher2);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* REGRESSION: When an event arrives during a close→reopen swap, rescan
|
|
261
|
+
* must catch any plugin not yet in the registry.
|
|
262
|
+
*
|
|
263
|
+
* Scenario:
|
|
264
|
+
* 1. Plugin "new-plugin" directory is created
|
|
265
|
+
* 2. Event fires, triggering a watcher restart (close + reopen)
|
|
266
|
+
* 3. Before the old watcher's callback is fully fired, another plugin
|
|
267
|
+
* "late-plugin" is created
|
|
268
|
+
* 4. The new watcher doesn't yet know about "late-plugin"
|
|
269
|
+
* 5. After the reopen, rescan walks the directory and finds "late-plugin"
|
|
270
|
+
* not in the registry, and schedules its rebuild
|
|
271
|
+
*/
|
|
272
|
+
test("watcher restart + rescan catches plugins created during close→reopen", async () => {
|
|
273
|
+
const watcher = PluginSourceWatcher.getInstance();
|
|
274
|
+
watcher.start();
|
|
275
|
+
|
|
276
|
+
// Track which plugins are "registered" at each point
|
|
277
|
+
const registeredPlugins = new Set<string>();
|
|
278
|
+
mockGetRegisteredPluginImpl = (name: string) =>
|
|
279
|
+
registeredPlugins.has(name) ? { name } : undefined;
|
|
280
|
+
|
|
281
|
+
// Simulate multiple plugins on disk
|
|
282
|
+
mockRereadirSync.mockImplementation(() => [
|
|
283
|
+
"new-plugin",
|
|
284
|
+
"late-plugin",
|
|
285
|
+
".DS_Store",
|
|
286
|
+
]);
|
|
287
|
+
|
|
288
|
+
// Fire an event on new-plugin (this will trigger a watcher restart)
|
|
289
|
+
capturedWatchCallback!("change", "new-plugin/src/index.ts");
|
|
290
|
+
|
|
291
|
+
// Wait for the direct rebuild debounce, the watcher-restart debounce,
|
|
292
|
+
// and the rescan-triggered rebuild debounce.
|
|
293
|
+
await new Promise((r) => setTimeout(r, 1100));
|
|
294
|
+
|
|
295
|
+
// At this point, rescan should have run and discovered late-plugin,
|
|
296
|
+
// even though no direct fs.watch event was delivered for it.
|
|
297
|
+
const calls = mockReregisterExternalPlugin.mock.calls.map((c) => c[0]);
|
|
298
|
+
expect(calls).toContain("new-plugin");
|
|
299
|
+
expect(calls).toContain("late-plugin");
|
|
300
|
+
expect(calls).not.toContain(".DS_Store");
|
|
301
|
+
});
|
|
302
|
+
});
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
unregisterPluginTools,
|
|
60
60
|
} from "../tools/registry.js";
|
|
61
61
|
import type {
|
|
62
|
-
|
|
62
|
+
LoadedPluginTool,
|
|
63
63
|
ToolContext,
|
|
64
64
|
ToolExecutionResult,
|
|
65
65
|
} from "../tools/types.js";
|
|
@@ -81,8 +81,8 @@ const fakeCtx: DaemonContext = {
|
|
|
81
81
|
|
|
82
82
|
function makeFakeTool(
|
|
83
83
|
name: string,
|
|
84
|
-
extras: Partial<
|
|
85
|
-
):
|
|
84
|
+
extras: Partial<LoadedPluginTool> = {},
|
|
85
|
+
): LoadedPluginTool {
|
|
86
86
|
return {
|
|
87
87
|
name,
|
|
88
88
|
description: `Fake ${name}`,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
type ToolResultTruncateResult,
|
|
47
47
|
type TurnContext,
|
|
48
48
|
} from "../plugins/types.js";
|
|
49
|
-
import type {
|
|
49
|
+
import type { LoadedPluginTool } from "../tools/types.js";
|
|
50
50
|
|
|
51
51
|
const sampleTrust: TrustContext = {
|
|
52
52
|
sourceChannel: "vellum",
|
|
@@ -207,7 +207,7 @@ describe("plugin core types", () => {
|
|
|
207
207
|
},
|
|
208
208
|
};
|
|
209
209
|
|
|
210
|
-
const sampleTool:
|
|
210
|
+
const sampleTool: LoadedPluginTool = {
|
|
211
211
|
name: "sample-tool",
|
|
212
212
|
description: "Sample plugin tool",
|
|
213
213
|
defaultRiskLevel: RiskLevel.Low,
|
|
@@ -95,7 +95,6 @@ type PersistUserMessageMock = ReturnType<
|
|
|
95
95
|
type RunAgentLoopMock = ReturnType<
|
|
96
96
|
typeof mock<(...args: unknown[]) => Promise<void>>
|
|
97
97
|
>;
|
|
98
|
-
type NoticeMock = ReturnType<typeof mock<(notice: string | undefined) => void>>;
|
|
99
98
|
interface TestConversation {
|
|
100
99
|
conversationId: string;
|
|
101
100
|
trustContext: unknown;
|
|
@@ -123,13 +122,10 @@ interface TestConversation {
|
|
|
123
122
|
estimatedCost: number;
|
|
124
123
|
};
|
|
125
124
|
persistUserMessage: PersistUserMessageMock;
|
|
126
|
-
setSlackRuntimeContextNotice: NoticeMock;
|
|
127
125
|
runAgentLoop: RunAgentLoopMock;
|
|
128
126
|
updateClient: (sender: (...args: unknown[]) => void) => void;
|
|
129
127
|
getCurrentSender: () => ((...args: unknown[]) => void) | undefined;
|
|
130
128
|
__loopDeferred: Deferred<void>;
|
|
131
|
-
__noticeCalls: Array<string | undefined>;
|
|
132
|
-
__loopNotices: Array<string | undefined>;
|
|
133
129
|
__clientSenders: Array<((...args: unknown[]) => void) | undefined>;
|
|
134
130
|
}
|
|
135
131
|
|
|
@@ -171,11 +167,8 @@ async function waitForRunAgentLoopCall(): Promise<void> {
|
|
|
171
167
|
function makeConversation(): TestConversation {
|
|
172
168
|
let turnChannelContext: TurnChannelContext | null = null;
|
|
173
169
|
let turnInterfaceContext: TurnInterfaceContext | null = null;
|
|
174
|
-
let slackNotice: string | undefined;
|
|
175
170
|
let currentSender: ((...args: unknown[]) => void) | undefined;
|
|
176
|
-
const noticeCalls: Array<string | undefined> = [];
|
|
177
171
|
const loopDeferred = createDeferred<void>();
|
|
178
|
-
const loopNotices: Array<string | undefined> = [];
|
|
179
172
|
const clientSenders: Array<((...args: unknown[]) => void) | undefined> = [];
|
|
180
173
|
const messages: unknown[] = [];
|
|
181
174
|
|
|
@@ -223,12 +216,7 @@ function makeConversation(): TestConversation {
|
|
|
223
216
|
_metadata?: Record<string, unknown>,
|
|
224
217
|
) => "persisted-user-message-id",
|
|
225
218
|
),
|
|
226
|
-
setSlackRuntimeContextNotice: mock((notice: string | undefined) => {
|
|
227
|
-
slackNotice = notice;
|
|
228
|
-
noticeCalls.push(notice);
|
|
229
|
-
}),
|
|
230
219
|
runAgentLoop: mock(async (..._args: unknown[]) => {
|
|
231
|
-
loopNotices.push(slackNotice);
|
|
232
220
|
await loopDeferred.promise;
|
|
233
221
|
}),
|
|
234
222
|
updateClient: (sender: (...args: unknown[]) => void) => {
|
|
@@ -237,8 +225,6 @@ function makeConversation(): TestConversation {
|
|
|
237
225
|
},
|
|
238
226
|
getCurrentSender: () => currentSender,
|
|
239
227
|
__loopDeferred: loopDeferred,
|
|
240
|
-
__noticeCalls: noticeCalls,
|
|
241
|
-
__loopNotices: loopNotices,
|
|
242
228
|
__clientSenders: clientSenders,
|
|
243
229
|
};
|
|
244
230
|
|
|
@@ -252,15 +238,13 @@ describe("processMessageInBackground Slack option propagation", () => {
|
|
|
252
238
|
broadcastMessages.length = 0;
|
|
253
239
|
});
|
|
254
240
|
|
|
255
|
-
test("passes Slack inbound metadata to persistence
|
|
241
|
+
test("passes Slack inbound metadata to persistence during background processing", async () => {
|
|
256
242
|
const slackInbound = {
|
|
257
243
|
channelId: "C0123CHANNEL",
|
|
258
244
|
channelTs: "1700000001.111111",
|
|
259
245
|
threadTs: "1700000000.000001",
|
|
260
246
|
displayName: "Alice",
|
|
261
247
|
};
|
|
262
|
-
const notice =
|
|
263
|
-
"Slack context note: this turn joined an existing thread. 2 earlier messages were backfilled.";
|
|
264
248
|
|
|
265
249
|
const result = await processMessageInBackground(
|
|
266
250
|
"conv-background-slack",
|
|
@@ -268,7 +252,6 @@ describe("processMessageInBackground Slack option propagation", () => {
|
|
|
268
252
|
undefined,
|
|
269
253
|
{
|
|
270
254
|
slackInbound,
|
|
271
|
-
slackRuntimeContextNotice: notice,
|
|
272
255
|
},
|
|
273
256
|
"slack",
|
|
274
257
|
"slack",
|
|
@@ -280,43 +263,10 @@ describe("processMessageInBackground Slack option propagation", () => {
|
|
|
280
263
|
slackInbound,
|
|
281
264
|
});
|
|
282
265
|
expect(activeConversation.runAgentLoop).toHaveBeenCalledTimes(1);
|
|
283
|
-
expect(activeConversation.__loopNotices).toEqual([notice]);
|
|
284
266
|
|
|
285
267
|
activeConversation.__loopDeferred.resolve();
|
|
286
268
|
await activeConversation.__loopDeferred.promise;
|
|
287
269
|
await Promise.resolve();
|
|
288
|
-
|
|
289
|
-
expect(activeConversation.__noticeCalls).toEqual([notice, undefined]);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
test("clears the Slack runtime notice after normal message processing", async () => {
|
|
293
|
-
const notice =
|
|
294
|
-
"Slack context note: this turn joined an existing thread. 2 earlier messages were backfilled.";
|
|
295
|
-
|
|
296
|
-
const processing = processMessage(
|
|
297
|
-
"conv-background-slack",
|
|
298
|
-
"Reply from Slack",
|
|
299
|
-
undefined,
|
|
300
|
-
{
|
|
301
|
-
slackRuntimeContextNotice: notice,
|
|
302
|
-
isInteractive: true,
|
|
303
|
-
},
|
|
304
|
-
"slack",
|
|
305
|
-
"slack",
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
await waitForRunAgentLoopCall();
|
|
309
|
-
|
|
310
|
-
expect(activeConversation.runAgentLoop).toHaveBeenCalledTimes(1);
|
|
311
|
-
expect(activeConversation.__loopNotices).toEqual([notice]);
|
|
312
|
-
|
|
313
|
-
activeConversation.__loopDeferred.resolve();
|
|
314
|
-
await expect(processing).resolves.toEqual({
|
|
315
|
-
messageId: "persisted-user-message-id",
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
expect(activeConversation.__noticeCalls).toEqual([notice, undefined]);
|
|
319
|
-
expect(activeConversation.__clientSenders).toHaveLength(2);
|
|
320
270
|
});
|
|
321
271
|
|
|
322
272
|
test("observes live agent events without replacing the broadcast emitter", async () => {
|
|
@@ -196,7 +196,6 @@ function makeTestConversation() {
|
|
|
196
196
|
metadata,
|
|
197
197
|
displayContent,
|
|
198
198
|
),
|
|
199
|
-
setSlackRuntimeContextNotice: () => {},
|
|
200
199
|
runAgentLoop,
|
|
201
200
|
updateClient: () => {},
|
|
202
201
|
getCurrentSender: () => undefined,
|
|
@@ -361,6 +360,7 @@ describe("processMessage displayContent", () => {
|
|
|
361
360
|
expect(persistedBlocks).toEqual([
|
|
362
361
|
{
|
|
363
362
|
type: "file",
|
|
363
|
+
_attachmentId: "att-1",
|
|
364
364
|
source: {
|
|
365
365
|
type: "base64",
|
|
366
366
|
media_type: "application/pdf",
|
|
@@ -370,21 +370,26 @@ describe("processMessage displayContent", () => {
|
|
|
370
370
|
},
|
|
371
371
|
]);
|
|
372
372
|
expect(addMessageCalls[0]!.content).not.toContain("<external_content");
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
373
|
+
const inMemoryMessage = conversation.getMessages()[0]!;
|
|
374
|
+
expect(inMemoryMessage.role).toBe("user");
|
|
375
|
+
expect(inMemoryMessage.content[0]).toEqual({
|
|
376
|
+
type: "text",
|
|
377
|
+
text: modelContent,
|
|
378
|
+
});
|
|
379
|
+
const inMemoryFileBlock = inMemoryMessage.content[1] as unknown as Record<
|
|
380
|
+
string,
|
|
381
|
+
unknown
|
|
382
|
+
>;
|
|
383
|
+
expect(inMemoryFileBlock._attachmentId).toBe("att-1");
|
|
384
|
+
expect(inMemoryFileBlock).toMatchObject({
|
|
385
|
+
type: "file",
|
|
386
|
+
source: {
|
|
387
|
+
type: "base64",
|
|
388
|
+
media_type: "application/pdf",
|
|
389
|
+
data: Buffer.from("pdf bytes").toString("base64"),
|
|
390
|
+
filename: "attachment.pdf",
|
|
391
|
+
},
|
|
392
|
+
extracted_text: undefined,
|
|
388
393
|
});
|
|
389
394
|
});
|
|
390
395
|
|
|
@@ -19,6 +19,22 @@ function makeConfig(): AssistantConfig {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
describe("getVisibleProviderCatalog", () => {
|
|
22
|
+
test("hides openai-compatible endpoints by default", () => {
|
|
23
|
+
_setOverridesForTesting({});
|
|
24
|
+
|
|
25
|
+
const visible = getVisibleProviderCatalog(makeConfig());
|
|
26
|
+
|
|
27
|
+
expect(visible.find((p) => p.id === "openai-compatible")).toBeUndefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("shows openai-compatible endpoints when its flag is enabled", () => {
|
|
31
|
+
_setOverridesForTesting({ "openai-compatible-endpoints": true });
|
|
32
|
+
|
|
33
|
+
const visible = getVisibleProviderCatalog(makeConfig());
|
|
34
|
+
|
|
35
|
+
expect(visible.find((p) => p.id === "openai-compatible")).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
22
38
|
test("returns the full catalog when all feature flags are enabled", () => {
|
|
23
39
|
const allFlags: Record<string, boolean> = {};
|
|
24
40
|
for (const entry of PROVIDER_CATALOG) {
|
|
@@ -113,7 +113,7 @@ const DIRECT_OR_MANAGED_PROVIDER_KEYS: string[] = [
|
|
|
113
113
|
"fireworks",
|
|
114
114
|
"openrouter",
|
|
115
115
|
];
|
|
116
|
-
const MANAGED_FALLBACK_PROVIDERS: string[] = ["anthropic", "gemini", "openai"];
|
|
116
|
+
const MANAGED_FALLBACK_PROVIDERS: string[] = ["anthropic", "gemini", "openai", "fireworks"];
|
|
117
117
|
|
|
118
118
|
function enableManagedProxy() {
|
|
119
119
|
mockPlatformBaseUrl = PLATFORM_BASE;
|
|
@@ -209,20 +209,19 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
209
209
|
},
|
|
210
210
|
);
|
|
211
211
|
|
|
212
|
-
test("managed bootstrap registers anthropic, openai, and
|
|
212
|
+
test("managed bootstrap registers anthropic, openai, gemini, and fireworks", async () => {
|
|
213
213
|
enableManagedProxy();
|
|
214
214
|
mockProviderKeys = {};
|
|
215
215
|
await initializeProviders(makeProvidersConfig("anthropic", "test-model"));
|
|
216
216
|
expect(listProviders()).toEqual(
|
|
217
|
-
expect.arrayContaining(["anthropic", "openai", "gemini"]),
|
|
217
|
+
expect.arrayContaining(["anthropic", "openai", "gemini", "fireworks"]),
|
|
218
218
|
);
|
|
219
|
-
expect(listProviders()).toHaveLength(
|
|
219
|
+
expect(listProviders()).toHaveLength(4);
|
|
220
220
|
expect(getProviderRoutingSource("anthropic")).toBe("managed-proxy");
|
|
221
221
|
expect(getProviderRoutingSource("openai")).toBe("managed-proxy");
|
|
222
222
|
expect(getProviderRoutingSource("gemini")).toBe("managed-proxy");
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
223
|
+
expect(getProviderRoutingSource("fireworks")).toBe("managed-proxy");
|
|
224
|
+
expect(getProviderRoutingSource("openrouter")).toBeUndefined();
|
|
226
225
|
});
|
|
227
226
|
|
|
228
227
|
test("managed anthropic uses anthropic proxy path", async () => {
|
|
@@ -376,7 +375,7 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
376
375
|
});
|
|
377
376
|
|
|
378
377
|
describe("mixed: some user keys + managed fallback fills gaps", () => {
|
|
379
|
-
test("user key for anthropic routes direct and managed fallback fills openai and
|
|
378
|
+
test("user key for anthropic routes direct and managed fallback fills openai, gemini, and fireworks", async () => {
|
|
380
379
|
enableManagedProxy();
|
|
381
380
|
setUserKeysFor("anthropic");
|
|
382
381
|
await initializeProviders(makeProvidersConfig("anthropic", "test-model"));
|
|
@@ -387,13 +386,13 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
387
386
|
expect(getProviderRoutingSource("openai")).toBe("managed-proxy");
|
|
388
387
|
expect(registered).toContain("gemini");
|
|
389
388
|
expect(getProviderRoutingSource("gemini")).toBe("managed-proxy");
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
389
|
+
expect(registered).toContain("fireworks");
|
|
390
|
+
expect(getProviderRoutingSource("fireworks")).toBe("managed-proxy");
|
|
391
|
+
expect(registered).not.toContain("openrouter");
|
|
392
|
+
expect(getProviderRoutingSource("openrouter")).toBeUndefined();
|
|
394
393
|
});
|
|
395
394
|
|
|
396
|
-
test("user key for openai routes direct while anthropic and
|
|
395
|
+
test("user key for openai routes direct while anthropic, gemini, and fireworks still bootstrap via managed proxy", async () => {
|
|
397
396
|
enableManagedProxy();
|
|
398
397
|
setUserKeysFor("openai");
|
|
399
398
|
await initializeProviders(makeProvidersConfig("openai", "test-model"));
|
|
@@ -404,11 +403,10 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
404
403
|
expect(getProviderRoutingSource("anthropic")).toBe("managed-proxy");
|
|
405
404
|
expect(registered).toContain("gemini");
|
|
406
405
|
expect(getProviderRoutingSource("gemini")).toBe("managed-proxy");
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
406
|
+
expect(registered).toContain("fireworks");
|
|
407
|
+
expect(getProviderRoutingSource("fireworks")).toBe("managed-proxy");
|
|
408
|
+
expect(registered).not.toContain("openrouter");
|
|
409
|
+
expect(getProviderRoutingSource("openrouter")).toBeUndefined();
|
|
412
410
|
});
|
|
413
411
|
});
|
|
414
412
|
});
|
|
@@ -476,8 +474,8 @@ describe("config mode flip → provider reinit", () => {
|
|
|
476
474
|
});
|
|
477
475
|
|
|
478
476
|
describe("managed proxy integration — constants integrity", () => {
|
|
479
|
-
test("anthropic, openai, and
|
|
480
|
-
for (const provider of ["anthropic", "openai", "gemini"]) {
|
|
477
|
+
test("anthropic, openai, gemini, and fireworks have metadata with managed=true and a proxyPath", () => {
|
|
478
|
+
for (const provider of ["anthropic", "openai", "gemini", "fireworks"]) {
|
|
481
479
|
const meta = PLATFORM_PROVIDER_META[provider];
|
|
482
480
|
expect(meta).toBeDefined();
|
|
483
481
|
expect(meta.managed).toBe(true);
|
|
@@ -504,10 +502,14 @@ describe("managed proxy integration — constants integrity", () => {
|
|
|
504
502
|
);
|
|
505
503
|
});
|
|
506
504
|
|
|
507
|
-
test("fireworks
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
505
|
+
test("fireworks routes through fireworks proxy path", () => {
|
|
506
|
+
expect(PLATFORM_PROVIDER_META.fireworks.proxyPath).toBe(
|
|
507
|
+
"/v1/runtime-proxy/fireworks",
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test("openrouter is not managed proxy capable", () => {
|
|
512
|
+
expect(PLATFORM_PROVIDER_META.openrouter.managed).toBe(false);
|
|
513
|
+
expect(PLATFORM_PROVIDER_META.openrouter.proxyPath).toBeUndefined();
|
|
512
514
|
});
|
|
513
515
|
});
|
|
@@ -12,7 +12,7 @@ let providerRefreshCalls = 0;
|
|
|
12
12
|
const PLATFORM_BASE_URL = "https://platform.example.com";
|
|
13
13
|
const ASSISTANT_API_KEY_PATH = credentialKey("vellum", "assistant_api_key");
|
|
14
14
|
const PLATFORM_BASE_URL_PATH = credentialKey("vellum", "platform_base_url");
|
|
15
|
-
const MANAGED_PROVIDERS = ["anthropic", "openai", "gemini"] as const;
|
|
15
|
+
const MANAGED_PROVIDERS = ["anthropic", "openai", "gemini", "fireworks"] as const;
|
|
16
16
|
|
|
17
17
|
let platformBaseUrlOverride: string | undefined;
|
|
18
18
|
|