@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
|
@@ -51,6 +51,11 @@ export interface ContextWindowResult {
|
|
|
51
51
|
thresholdTokens: number;
|
|
52
52
|
compactedMessages: number;
|
|
53
53
|
compactedPersistedMessages: number;
|
|
54
|
+
/**
|
|
55
|
+
* Number of recent ("tail") messages preserved verbatim alongside the
|
|
56
|
+
* summary. Omitted on no-op / skipped results — defaults to 0 at render.
|
|
57
|
+
*/
|
|
58
|
+
preservedTailMessages?: number;
|
|
54
59
|
summaryCalls: number;
|
|
55
60
|
summaryInputTokens: number;
|
|
56
61
|
summaryOutputTokens: number;
|
|
@@ -216,6 +221,26 @@ export class ContextWindowManager {
|
|
|
216
221
|
return getConfig().compaction;
|
|
217
222
|
}
|
|
218
223
|
|
|
224
|
+
get maxInputTokens(): number {
|
|
225
|
+
return this.config.maxInputTokens;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Estimate the prompt-token cost of `messages` using the same path as the
|
|
230
|
+
* auto-compaction pre-check. Clears the system-prompt cache so the next
|
|
231
|
+
* turn re-resolves it (the system prompt is lazy and may have changed).
|
|
232
|
+
*/
|
|
233
|
+
estimateInputTokens(messages: Message[]): number {
|
|
234
|
+
try {
|
|
235
|
+
return estimatePromptTokens(messages, this.systemPrompt, {
|
|
236
|
+
providerName: this.estimationProviderName,
|
|
237
|
+
toolTokenBudget: this.toolTokenBudget,
|
|
238
|
+
});
|
|
239
|
+
} finally {
|
|
240
|
+
this.clearSystemPromptCache();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
219
244
|
/**
|
|
220
245
|
* Cheap pre-check — estimate the current token count and compare against
|
|
221
246
|
* `compaction.autoThreshold`. Callers pass the estimate back through
|
|
@@ -315,9 +315,11 @@ async function checkManagedProvider(
|
|
|
315
315
|
const client = await VellumPlatformClient.create();
|
|
316
316
|
if (!client?.platformAssistantId) return results;
|
|
317
317
|
|
|
318
|
+
// Query without a status filter so we can distinguish "never
|
|
319
|
+
// connected" (empty result) from "previously connected but now
|
|
320
|
+
// inactive" (non-empty result with no ACTIVE entries).
|
|
318
321
|
const params = new URLSearchParams();
|
|
319
322
|
params.set("provider", providerRow.provider);
|
|
320
|
-
params.set("status", "ACTIVE");
|
|
321
323
|
|
|
322
324
|
const path = `/v1/assistants/${encodeURIComponent(client.platformAssistantId)}/oauth/connections/?${params.toString()}`;
|
|
323
325
|
const response = await client.fetch(path);
|
|
@@ -331,19 +333,30 @@ async function checkManagedProvider(
|
|
|
331
333
|
}
|
|
332
334
|
|
|
333
335
|
const body = (await response.json()) as unknown;
|
|
334
|
-
const
|
|
336
|
+
const allConnections = (
|
|
335
337
|
Array.isArray(body)
|
|
336
338
|
? body
|
|
337
339
|
: ((body as Record<string, unknown>).results ?? [])
|
|
338
|
-
) as Array<{ id: string; account_label?: string }>;
|
|
340
|
+
) as Array<{ id: string; account_label?: string; status?: string }>;
|
|
341
|
+
|
|
342
|
+
if (allConnections.length === 0) {
|
|
343
|
+
// No connections of any status — the user has never connected this
|
|
344
|
+
// provider. The suggested-prompts system handles prompting them to
|
|
345
|
+
// connect; this is not a health issue.
|
|
346
|
+
return results;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const connections = allConnections.filter(
|
|
350
|
+
(c) => (c.status ?? "ACTIVE").toUpperCase() === "ACTIVE",
|
|
351
|
+
);
|
|
339
352
|
|
|
340
353
|
if (connections.length === 0) {
|
|
341
|
-
//
|
|
342
|
-
//
|
|
354
|
+
// Connections exist but none are active — the user previously
|
|
355
|
+
// connected and the connection was revoked/deactivated.
|
|
343
356
|
results.push({
|
|
344
357
|
connectionId: `managed:${providerRow.provider}`,
|
|
345
358
|
provider: providerRow.provider,
|
|
346
|
-
accountInfo: null,
|
|
359
|
+
accountInfo: allConnections[0]?.account_label ?? null,
|
|
347
360
|
status: "missing_token",
|
|
348
361
|
details: `No active managed connection for ${providerRow.provider}. Reconnect on the Vellum platform.`,
|
|
349
362
|
missingScopes: [],
|
|
@@ -550,9 +563,20 @@ export async function checkAllCredentials(): Promise<CredentialHealthReport> {
|
|
|
550
563
|
for (const providerRow of providers) {
|
|
551
564
|
if (!(await isManagedProvider(providerRow))) continue;
|
|
552
565
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
566
|
+
let managedResults: CredentialHealthResult[] = [];
|
|
567
|
+
try {
|
|
568
|
+
managedResults = await checkManagedProvider(providerRow);
|
|
569
|
+
} catch (err) {
|
|
570
|
+
log.warn(
|
|
571
|
+
{ err, provider: providerRow.provider },
|
|
572
|
+
"Failed to check managed provider health",
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Only replace BYO results with managed results when the managed
|
|
577
|
+
// check returned something. If managed returned empty (user never
|
|
578
|
+
// connected via managed mode), keep any existing BYO results.
|
|
579
|
+
if (managedResults.length > 0 && byoProviders.has(providerRow.provider)) {
|
|
556
580
|
const beforeLen = results.length;
|
|
557
581
|
const filtered = results.filter(
|
|
558
582
|
(r) => r.provider !== providerRow.provider,
|
|
@@ -562,16 +586,7 @@ export async function checkAllCredentials(): Promise<CredentialHealthReport> {
|
|
|
562
586
|
results.push(...filtered);
|
|
563
587
|
}
|
|
564
588
|
}
|
|
565
|
-
|
|
566
|
-
try {
|
|
567
|
-
const managedResults = await checkManagedProvider(providerRow);
|
|
568
|
-
results.push(...managedResults);
|
|
569
|
-
} catch (err) {
|
|
570
|
-
log.warn(
|
|
571
|
-
{ err, provider: providerRow.provider },
|
|
572
|
-
"Failed to check managed provider health",
|
|
573
|
-
);
|
|
574
|
-
}
|
|
589
|
+
results.push(...managedResults);
|
|
575
590
|
}
|
|
576
591
|
|
|
577
592
|
const unhealthy = results.filter((r) => r.status !== "healthy");
|
|
@@ -97,7 +97,6 @@ mock.module("../../memory/auto-analysis-enqueue.js", () => ({
|
|
|
97
97
|
},
|
|
98
98
|
}));
|
|
99
99
|
|
|
100
|
-
let memoryRetroEnabled = false;
|
|
101
100
|
const memoryRetroCalls: Array<{
|
|
102
101
|
conversationId: string;
|
|
103
102
|
trigger: string;
|
|
@@ -108,7 +107,6 @@ mock.module("../../memory/memory-retrospective-enqueue.js", () => ({
|
|
|
108
107
|
conversationId: string;
|
|
109
108
|
trigger: string;
|
|
110
109
|
}) => {
|
|
111
|
-
if (!memoryRetroEnabled) return;
|
|
112
110
|
memoryRetroCalls.push(args);
|
|
113
111
|
},
|
|
114
112
|
// Also export sibling functions other modules import from this file, so
|
|
@@ -205,7 +203,6 @@ describe("disposeConversation — auto-analysis enqueue", () => {
|
|
|
205
203
|
autoAnalyzeCalls.length = 0;
|
|
206
204
|
memoryRetroCalls.length = 0;
|
|
207
205
|
autoAnalyzeEnabled = true;
|
|
208
|
-
memoryRetroEnabled = false;
|
|
209
206
|
autoAnalysisConversations.clear();
|
|
210
207
|
v2Enabled = false;
|
|
211
208
|
});
|
|
@@ -390,13 +387,11 @@ describe("disposeConversation — memory-retrospective lifecycle safety net", ()
|
|
|
390
387
|
autoAnalyzeCalls.length = 0;
|
|
391
388
|
memoryRetroCalls.length = 0;
|
|
392
389
|
autoAnalyzeEnabled = false;
|
|
393
|
-
memoryRetroEnabled = false;
|
|
394
390
|
autoAnalysisConversations.clear();
|
|
395
391
|
v2Enabled = false;
|
|
396
392
|
});
|
|
397
393
|
|
|
398
|
-
test("guardian conversation
|
|
399
|
-
memoryRetroEnabled = true;
|
|
394
|
+
test("guardian conversation — enqueues memory-retrospective with trigger 'lifecycle'", () => {
|
|
400
395
|
const ctx = makeDisposeContext({
|
|
401
396
|
conversationId: "conv-retro",
|
|
402
397
|
trustClass: "guardian",
|
|
@@ -411,20 +406,7 @@ describe("disposeConversation — memory-retrospective lifecycle safety net", ()
|
|
|
411
406
|
});
|
|
412
407
|
});
|
|
413
408
|
|
|
414
|
-
test("
|
|
415
|
-
memoryRetroEnabled = false;
|
|
416
|
-
const ctx = makeDisposeContext({
|
|
417
|
-
conversationId: "conv-retro-off",
|
|
418
|
-
trustClass: "guardian",
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
disposeConversation(ctx);
|
|
422
|
-
|
|
423
|
-
expect(memoryRetroCalls).toHaveLength(0);
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
test("untrusted actor — no memory-retrospective enqueue even when flag is on", () => {
|
|
427
|
-
memoryRetroEnabled = true;
|
|
409
|
+
test("untrusted actor — no memory-retrospective enqueue", () => {
|
|
428
410
|
const ctx = makeDisposeContext({
|
|
429
411
|
conversationId: "conv-retro-untrusted",
|
|
430
412
|
trustClass: "unknown",
|
|
@@ -443,8 +425,7 @@ describe("disposeConversation — memory-retrospective lifecycle safety net", ()
|
|
|
443
425
|
// outside the `!isAutoAnalysis` guard, so it fired even for auto-analysis
|
|
444
426
|
// conversations. Mirrors the indexer-time gate in `indexer.ts` and
|
|
445
427
|
// matches the existing graph_extract recursion-guard semantics.
|
|
446
|
-
test("auto-analysis conversation — does NOT enqueue memory-retrospective
|
|
447
|
-
memoryRetroEnabled = true;
|
|
428
|
+
test("auto-analysis conversation — does NOT enqueue memory-retrospective", () => {
|
|
448
429
|
autoAnalysisConversations.add("conv-auto-retro");
|
|
449
430
|
const ctx = makeDisposeContext({
|
|
450
431
|
conversationId: "conv-auto-retro",
|
|
@@ -49,11 +49,8 @@ mock.module("../../runtime/assistant-event-hub.js", () => ({
|
|
|
49
49
|
|
|
50
50
|
// Dynamic imports after mock.module calls so the stubs take effect
|
|
51
51
|
// before the modules under test are loaded.
|
|
52
|
-
const {
|
|
53
|
-
|
|
54
|
-
HOST_TOOL_TO_CAPABILITY,
|
|
55
|
-
isToolActiveForContext,
|
|
56
|
-
} = await import("../conversation-tool-setup.js");
|
|
52
|
+
const { HOST_TOOL_NAMES, HOST_TOOL_TO_CAPABILITY, isToolActiveForContext } =
|
|
53
|
+
await import("../conversation-tool-setup.js");
|
|
57
54
|
type SkillProjectionContext =
|
|
58
55
|
import("../conversation-tool-setup.js").SkillProjectionContext;
|
|
59
56
|
type SkillProjectionCache =
|
|
@@ -75,6 +72,66 @@ beforeEach(() => {
|
|
|
75
72
|
mockClientCountByCapability.clear();
|
|
76
73
|
});
|
|
77
74
|
|
|
75
|
+
describe("isToolActiveForContext — Slack task_progress UI exception", () => {
|
|
76
|
+
test("ui_show and ui_update are active for Slack task_progress turns", () => {
|
|
77
|
+
const ctx = makeCtx({
|
|
78
|
+
hasNoClient: false,
|
|
79
|
+
channelCapabilities: {
|
|
80
|
+
channel: "slack",
|
|
81
|
+
supportsDynamicUi: false,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(isToolActiveForContext("ui_show", ctx)).toBe(true);
|
|
86
|
+
expect(isToolActiveForContext("ui_update", ctx)).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("ui_dismiss remains hidden for Slack without dynamic UI support", () => {
|
|
90
|
+
expect(
|
|
91
|
+
isToolActiveForContext(
|
|
92
|
+
"ui_dismiss",
|
|
93
|
+
makeCtx({
|
|
94
|
+
hasNoClient: false,
|
|
95
|
+
channelCapabilities: {
|
|
96
|
+
channel: "slack",
|
|
97
|
+
supportsDynamicUi: false,
|
|
98
|
+
},
|
|
99
|
+
}),
|
|
100
|
+
),
|
|
101
|
+
).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("Slack task_progress UI tools still require a connected client path", () => {
|
|
105
|
+
expect(
|
|
106
|
+
isToolActiveForContext(
|
|
107
|
+
"ui_show",
|
|
108
|
+
makeCtx({
|
|
109
|
+
hasNoClient: true,
|
|
110
|
+
channelCapabilities: {
|
|
111
|
+
channel: "slack",
|
|
112
|
+
supportsDynamicUi: false,
|
|
113
|
+
},
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
|
+
).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("other non-dynamic channels still hide UI surface tools", () => {
|
|
120
|
+
expect(
|
|
121
|
+
isToolActiveForContext(
|
|
122
|
+
"ui_show",
|
|
123
|
+
makeCtx({
|
|
124
|
+
hasNoClient: false,
|
|
125
|
+
channelCapabilities: {
|
|
126
|
+
channel: "telegram",
|
|
127
|
+
supportsDynamicUi: false,
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
),
|
|
131
|
+
).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
78
135
|
describe("isToolActiveForContext — host tool capability gating", () => {
|
|
79
136
|
// macOS transport: SSE-based interactive approval required.
|
|
80
137
|
test("host_bash is active for macOS with a connected client", () => {
|
|
@@ -331,7 +388,10 @@ describe("isToolActiveForContext — cross-client exposure for host_file_*", ()
|
|
|
331
388
|
expect(
|
|
332
389
|
isToolActiveForContext(
|
|
333
390
|
tool,
|
|
334
|
-
makeCtx({
|
|
391
|
+
makeCtx({
|
|
392
|
+
hasNoClient: true,
|
|
393
|
+
transportInterface: "chrome-extension",
|
|
394
|
+
}),
|
|
335
395
|
),
|
|
336
396
|
).toBe(false);
|
|
337
397
|
});
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that the native Anthropic `server_tool_complete` agent event produces
|
|
3
|
+
* a structured `WebSearchMetadata` payload on the emitted `tool_result` server
|
|
4
|
+
* message while keeping the back-compat `result: string` byte-identical.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the mocked-dependency collector pattern used in
|
|
7
|
+
* tool-result-metadata-plumbing.test.ts.
|
|
8
|
+
*/
|
|
9
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
// ── Mock platform (must precede imports that read it) ─────────────────────────
|
|
12
|
+
mock.module("../../util/logger.js", () => ({
|
|
13
|
+
getLogger: () =>
|
|
14
|
+
new Proxy({} as Record<string, unknown>, {
|
|
15
|
+
get: () => () => {},
|
|
16
|
+
}),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
mock.module("../../config/loader.js", () => ({
|
|
20
|
+
getConfig: () => ({
|
|
21
|
+
skills: {
|
|
22
|
+
entries: {},
|
|
23
|
+
load: { extraDirs: [], watch: false, watchDebounceMs: 0 },
|
|
24
|
+
install: { nodeManager: "npm" },
|
|
25
|
+
allowBundled: null,
|
|
26
|
+
remoteProviders: {
|
|
27
|
+
skillssh: { enabled: true },
|
|
28
|
+
clawhub: { enabled: true },
|
|
29
|
+
},
|
|
30
|
+
remotePolicy: {
|
|
31
|
+
blockSuspicious: true,
|
|
32
|
+
blockMalware: true,
|
|
33
|
+
maxSkillsShRisk: "medium",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
loadConfig: () => ({}),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
mock.module("../../memory/conversation-crud.js", () => ({
|
|
41
|
+
addMessage: () => ({ id: "mock-msg-id" }),
|
|
42
|
+
getMessageById: () => null,
|
|
43
|
+
updateMessageContent: () => {},
|
|
44
|
+
provenanceFromTrustContext: () => ({}),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
mock.module("../../memory/llm-request-log-store.js", () => ({
|
|
48
|
+
recordRequestLog: () => {},
|
|
49
|
+
backfillMessageIdOnLogs: () => {},
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
// ── Imports (after mocks) ─────────────────────────────────────────────────────
|
|
53
|
+
import type {
|
|
54
|
+
EventHandlerDeps,
|
|
55
|
+
EventHandlerState,
|
|
56
|
+
} from "../conversation-agent-loop-handlers.js";
|
|
57
|
+
import {
|
|
58
|
+
createEventHandlerState,
|
|
59
|
+
dispatchAgentEvent,
|
|
60
|
+
} from "../conversation-agent-loop-handlers.js";
|
|
61
|
+
import type { ServerMessage } from "../message-protocol.js";
|
|
62
|
+
|
|
63
|
+
type ToolResultEvent = Extract<ServerMessage, { type: "tool_result" }>;
|
|
64
|
+
|
|
65
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
function createCollectorDeps(providerName = "anthropic"): {
|
|
68
|
+
deps: EventHandlerDeps;
|
|
69
|
+
events: ServerMessage[];
|
|
70
|
+
} {
|
|
71
|
+
const events: ServerMessage[] = [];
|
|
72
|
+
const deps = {
|
|
73
|
+
ctx: {
|
|
74
|
+
conversationId: "conv-native-meta",
|
|
75
|
+
provider: { name: providerName },
|
|
76
|
+
traceEmitter: { emit: () => {} },
|
|
77
|
+
streamThinking: false,
|
|
78
|
+
emitActivityState: () => {},
|
|
79
|
+
markWorkspaceTopLevelDirty: () => {},
|
|
80
|
+
currentTurnSurfaces: [],
|
|
81
|
+
} as unknown as EventHandlerDeps["ctx"],
|
|
82
|
+
onEvent: (msg: ServerMessage) => events.push(msg),
|
|
83
|
+
reqId: "req-native-meta",
|
|
84
|
+
isFirstMessage: false,
|
|
85
|
+
shouldGenerateTitle: false,
|
|
86
|
+
rlog: new Proxy({} as Record<string, unknown>, {
|
|
87
|
+
get: () => () => {},
|
|
88
|
+
}) as unknown as EventHandlerDeps["rlog"],
|
|
89
|
+
turnChannelContext: {
|
|
90
|
+
userMessageChannel: "vellum",
|
|
91
|
+
assistantMessageChannel: "vellum",
|
|
92
|
+
} as EventHandlerDeps["turnChannelContext"],
|
|
93
|
+
turnInterfaceContext: {
|
|
94
|
+
userMessageInterface: "macos",
|
|
95
|
+
assistantMessageInterface: "macos",
|
|
96
|
+
} as EventHandlerDeps["turnInterfaceContext"],
|
|
97
|
+
} as EventHandlerDeps;
|
|
98
|
+
return { deps, events };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
describe("native server_tool_complete metadata", () => {
|
|
104
|
+
let state: EventHandlerState;
|
|
105
|
+
|
|
106
|
+
beforeEach(() => {
|
|
107
|
+
state = createEventHandlerState();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("builds structured WebSearchMetadata and keeps result text byte-identical", async () => {
|
|
111
|
+
const { deps, events } = createCollectorDeps();
|
|
112
|
+
const toolUseId = "tu1";
|
|
113
|
+
|
|
114
|
+
await dispatchAgentEvent(state, deps, {
|
|
115
|
+
type: "server_tool_start",
|
|
116
|
+
name: "web_search",
|
|
117
|
+
toolUseId,
|
|
118
|
+
input: { query: "Air Jordan auction" },
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await dispatchAgentEvent(state, deps, {
|
|
122
|
+
type: "server_tool_complete",
|
|
123
|
+
toolUseId,
|
|
124
|
+
isError: false,
|
|
125
|
+
content: [
|
|
126
|
+
{
|
|
127
|
+
type: "web_search_result",
|
|
128
|
+
title: "X",
|
|
129
|
+
url: "https://www.cnn.com/a",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
type: "web_search_result",
|
|
133
|
+
title: "Y",
|
|
134
|
+
url: "https://www.nbcnews.com/b",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const toolResultEvent = events.find(
|
|
140
|
+
(e): e is ToolResultEvent => e.type === "tool_result",
|
|
141
|
+
);
|
|
142
|
+
expect(toolResultEvent).toBeDefined();
|
|
143
|
+
|
|
144
|
+
const meta = toolResultEvent?.activityMetadata?.webSearch;
|
|
145
|
+
expect(meta).toBeDefined();
|
|
146
|
+
expect(meta?.query).toBe("Air Jordan auction");
|
|
147
|
+
expect(meta?.provider).toBe("anthropic-native");
|
|
148
|
+
expect(meta?.resultCount).toBe(2);
|
|
149
|
+
expect(meta?.results[0]?.domain).toBe("www.cnn.com");
|
|
150
|
+
expect(meta?.results[1]?.domain).toBe("www.nbcnews.com");
|
|
151
|
+
expect(meta?.results[0]?.rank).toBe(1);
|
|
152
|
+
expect(meta?.results[1]?.rank).toBe(2);
|
|
153
|
+
expect(meta?.durationMs).toBeGreaterThanOrEqual(0);
|
|
154
|
+
|
|
155
|
+
// Favicons are synthesized from the result domain so clients can render
|
|
156
|
+
// a per-result icon without an extra round-trip.
|
|
157
|
+
expect(meta?.results[0]?.faviconUrl).toContain("google.com/s2/favicons");
|
|
158
|
+
expect(meta?.results[1]?.faviconUrl).toContain("google.com/s2/favicons");
|
|
159
|
+
|
|
160
|
+
// Back-compat: result text must be byte-identical to the legacy format.
|
|
161
|
+
expect(toolResultEvent?.result).toBe(
|
|
162
|
+
"X\nhttps://www.cnn.com/a\n\nY\nhttps://www.nbcnews.com/b",
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Per-toolUseId scratch maps must be cleaned up after completion.
|
|
166
|
+
expect(state.serverToolStartedAt.has(toolUseId)).toBe(false);
|
|
167
|
+
expect(state.serverToolInputs.has(toolUseId)).toBe(false);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("prefers `resolvedInput.query` over the input captured at server_tool_start", async () => {
|
|
171
|
+
// Mirrors the live Anthropic flow: server_tool_start fires with `input: {}`
|
|
172
|
+
// because the SDK streams server-tool input via `input_json_delta`, then
|
|
173
|
+
// the provider populates `resolvedInput` on `server_tool_complete` from
|
|
174
|
+
// the accumulated JSON. Without this plumb-through, `meta.query` is empty.
|
|
175
|
+
const { deps, events } = createCollectorDeps();
|
|
176
|
+
const toolUseId = "tu_resolved";
|
|
177
|
+
|
|
178
|
+
await dispatchAgentEvent(state, deps, {
|
|
179
|
+
type: "server_tool_start",
|
|
180
|
+
name: "web_search",
|
|
181
|
+
toolUseId,
|
|
182
|
+
input: {},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await dispatchAgentEvent(state, deps, {
|
|
186
|
+
type: "server_tool_complete",
|
|
187
|
+
toolUseId,
|
|
188
|
+
isError: false,
|
|
189
|
+
resolvedInput: { query: "OpenAI Dev Day recap" },
|
|
190
|
+
content: [
|
|
191
|
+
{
|
|
192
|
+
type: "web_search_result",
|
|
193
|
+
title: "Recap",
|
|
194
|
+
url: "https://example.com/a",
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const toolResultEvent = events.find(
|
|
200
|
+
(e): e is ToolResultEvent => e.type === "tool_result",
|
|
201
|
+
);
|
|
202
|
+
expect(toolResultEvent?.activityMetadata?.webSearch?.query).toBe(
|
|
203
|
+
"OpenAI Dev Day recap",
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("fires per-toolUseId for parallel server tool calls with independent queries and timings", async () => {
|
|
208
|
+
const { deps, events } = createCollectorDeps();
|
|
209
|
+
|
|
210
|
+
await dispatchAgentEvent(state, deps, {
|
|
211
|
+
type: "server_tool_start",
|
|
212
|
+
name: "web_search",
|
|
213
|
+
toolUseId: "tu_a",
|
|
214
|
+
input: { query: "alpha" },
|
|
215
|
+
});
|
|
216
|
+
await dispatchAgentEvent(state, deps, {
|
|
217
|
+
type: "server_tool_start",
|
|
218
|
+
name: "web_search",
|
|
219
|
+
toolUseId: "tu_b",
|
|
220
|
+
input: { query: "beta" },
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Hold long enough that the two completions can't share a duration.
|
|
224
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
225
|
+
|
|
226
|
+
await dispatchAgentEvent(state, deps, {
|
|
227
|
+
type: "server_tool_complete",
|
|
228
|
+
toolUseId: "tu_a",
|
|
229
|
+
isError: false,
|
|
230
|
+
content: [
|
|
231
|
+
{
|
|
232
|
+
type: "web_search_result",
|
|
233
|
+
title: "A1",
|
|
234
|
+
url: "https://a.example.com/1",
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
240
|
+
|
|
241
|
+
await dispatchAgentEvent(state, deps, {
|
|
242
|
+
type: "server_tool_complete",
|
|
243
|
+
toolUseId: "tu_b",
|
|
244
|
+
isError: false,
|
|
245
|
+
content: [
|
|
246
|
+
{
|
|
247
|
+
type: "web_search_result",
|
|
248
|
+
title: "B1",
|
|
249
|
+
url: "https://b.example.com/1",
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const toolResults = events.filter(
|
|
255
|
+
(e): e is ToolResultEvent => e.type === "tool_result",
|
|
256
|
+
);
|
|
257
|
+
expect(toolResults).toHaveLength(2);
|
|
258
|
+
|
|
259
|
+
const byId = new Map(
|
|
260
|
+
toolResults.map((r) => [r.toolUseId, r] as const),
|
|
261
|
+
);
|
|
262
|
+
expect(byId.get("tu_a")?.activityMetadata?.webSearch?.query).toBe("alpha");
|
|
263
|
+
expect(byId.get("tu_b")?.activityMetadata?.webSearch?.query).toBe("beta");
|
|
264
|
+
|
|
265
|
+
const durA = byId.get("tu_a")?.activityMetadata?.webSearch?.durationMs ?? 0;
|
|
266
|
+
const durB = byId.get("tu_b")?.activityMetadata?.webSearch?.durationMs ?? 0;
|
|
267
|
+
// Each duration is computed against its own startedAt — tu_b should run
|
|
268
|
+
// longer because we slept once more between its start and its complete.
|
|
269
|
+
expect(durB).toBeGreaterThanOrEqual(durA);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("forwards provider error codes instead of generic 'Search failed'", async () => {
|
|
273
|
+
const { deps, events } = createCollectorDeps();
|
|
274
|
+
const toolUseId = "tu_err_code";
|
|
275
|
+
|
|
276
|
+
await dispatchAgentEvent(state, deps, {
|
|
277
|
+
type: "server_tool_start",
|
|
278
|
+
name: "web_search",
|
|
279
|
+
toolUseId,
|
|
280
|
+
input: { query: "over the limit" },
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
await dispatchAgentEvent(state, deps, {
|
|
284
|
+
type: "server_tool_complete",
|
|
285
|
+
toolUseId,
|
|
286
|
+
isError: true,
|
|
287
|
+
errorCode: "max_uses_exceeded",
|
|
288
|
+
content: [],
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const toolResultEvent = events.find(
|
|
292
|
+
(e): e is ToolResultEvent => e.type === "tool_result",
|
|
293
|
+
);
|
|
294
|
+
expect(toolResultEvent?.activityMetadata?.webSearch?.errorMessage).toBe(
|
|
295
|
+
"max_uses_exceeded",
|
|
296
|
+
);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("does NOT emit activityMetadata for non-Anthropic providers", async () => {
|
|
300
|
+
// OpenAI's responses provider shares `server_tool_start`/`server_tool_complete`
|
|
301
|
+
// for `web_search_call`, but its results live inside the assistant text
|
|
302
|
+
// stream — emitting "anthropic-native" metadata for an OpenAI search would
|
|
303
|
+
// mis-label the provider and ship an empty `results` array.
|
|
304
|
+
const { deps, events } = createCollectorDeps("openai");
|
|
305
|
+
const toolUseId = "tu_openai";
|
|
306
|
+
|
|
307
|
+
await dispatchAgentEvent(state, deps, {
|
|
308
|
+
type: "server_tool_start",
|
|
309
|
+
name: "web_search",
|
|
310
|
+
toolUseId,
|
|
311
|
+
input: {},
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
await dispatchAgentEvent(state, deps, {
|
|
315
|
+
type: "server_tool_complete",
|
|
316
|
+
toolUseId,
|
|
317
|
+
isError: false,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const toolResultEvent = events.find(
|
|
321
|
+
(e): e is ToolResultEvent => e.type === "tool_result",
|
|
322
|
+
);
|
|
323
|
+
expect(toolResultEvent).toBeDefined();
|
|
324
|
+
expect(toolResultEvent?.activityMetadata).toBeUndefined();
|
|
325
|
+
// The back-compat `result: string` channel still fires (empty for OpenAI
|
|
326
|
+
// since the results are woven into the text stream, not structured).
|
|
327
|
+
expect(toolResultEvent?.result).toBe("");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("sets errorMessage when the search errored", async () => {
|
|
331
|
+
const { deps, events } = createCollectorDeps();
|
|
332
|
+
const toolUseId = "tu_err";
|
|
333
|
+
|
|
334
|
+
await dispatchAgentEvent(state, deps, {
|
|
335
|
+
type: "server_tool_start",
|
|
336
|
+
name: "web_search",
|
|
337
|
+
toolUseId,
|
|
338
|
+
input: { query: "broken" },
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
await dispatchAgentEvent(state, deps, {
|
|
342
|
+
type: "server_tool_complete",
|
|
343
|
+
toolUseId,
|
|
344
|
+
isError: true,
|
|
345
|
+
content: [],
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const toolResultEvent = events.find(
|
|
349
|
+
(e): e is ToolResultEvent => e.type === "tool_result",
|
|
350
|
+
);
|
|
351
|
+
const meta = toolResultEvent?.activityMetadata?.webSearch;
|
|
352
|
+
expect(meta?.errorMessage).toBe("Search failed");
|
|
353
|
+
expect(meta?.resultCount).toBe(0);
|
|
354
|
+
expect(meta?.results).toEqual([]);
|
|
355
|
+
expect(toolResultEvent?.isError).toBe(true);
|
|
356
|
+
});
|
|
357
|
+
});
|