@vellumai/assistant 0.8.3 → 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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- 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__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- 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 +25 -7
- 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-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 +264 -81
- 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__/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__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -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-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-chain.test.ts +2 -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__/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-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- 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.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- 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__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- 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-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- 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/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- 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 +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- 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/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- 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 +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- 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 +135 -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 -5
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- 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-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- 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/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/trust-context.ts +6 -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/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- 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/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -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/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- 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/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 +17 -0
- package/src/memory/migrations/registry.ts +25 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- 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/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/messaging/providers/index.ts +7 -1
- 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__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- package/src/notifications/home-feed-side-effect.ts +60 -30
- 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/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 +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- 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/adapter-factory.ts +15 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -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 +151 -56
- package/src/runtime/auth/route-policy.ts +7 -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-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- 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/chatgpt-subscription-auth-routes.ts +246 -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 +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -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 +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -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/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/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/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/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- 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/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- 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/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
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Resolves an `Auth` config into a `ResolvedAuth` that adapters consume.
|
|
3
3
|
*
|
|
4
4
|
* Resolution rules:
|
|
5
|
-
* - api_key
|
|
6
|
-
* - platform
|
|
7
|
-
* - none
|
|
8
|
-
* - oauth_subscription
|
|
5
|
+
* - api_key → fetch credential from vault → inject as bearer header
|
|
6
|
+
* - platform → build managed proxy URL and fetch the platform API key
|
|
7
|
+
* - none → pass through with no auth headers
|
|
8
|
+
* - oauth_subscription → fetch OAuth token from vault (with auto-refresh) → inject as bearer header
|
|
9
|
+
* - service_account → reject (v2 not yet shipped)
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -13,7 +14,11 @@ import {
|
|
|
13
14
|
resolveManagedProxyContext,
|
|
14
15
|
} from "../../providers/platform-proxy/context.js";
|
|
15
16
|
import { getSecureKeyAsync } from "../../security/secure-keys.js";
|
|
17
|
+
import { getLogger } from "../../util/logger.js";
|
|
16
18
|
import type { Auth, ResolvedAuth } from "./auth.js";
|
|
19
|
+
import { PROVIDERS_REQUIRING_BASE_URL_AND_MODELS } from "./connections.js";
|
|
20
|
+
|
|
21
|
+
const log = getLogger("resolve-auth");
|
|
17
22
|
|
|
18
23
|
export type ResolveAuthError =
|
|
19
24
|
| { code: "credential_not_found"; credential: string }
|
|
@@ -27,6 +32,19 @@ export async function resolveAuth(
|
|
|
27
32
|
): Promise<
|
|
28
33
|
{ ok: true; resolved: ResolvedAuth } | { ok: false; error: ResolveAuthError }
|
|
29
34
|
> {
|
|
35
|
+
// Defense-in-depth: strip baseUrl for providers that should not accept one.
|
|
36
|
+
// The route layer rejects base_url for non-openai-compatible providers, but
|
|
37
|
+
// this guard catches any code path that bypasses route validation (e.g.
|
|
38
|
+
// corrupted DB rows, direct calls from internal code).
|
|
39
|
+
let safeBaseUrl = opts.baseUrl;
|
|
40
|
+
if (safeBaseUrl && !PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(provider)) {
|
|
41
|
+
log.warn(
|
|
42
|
+
{ provider, baseUrl: safeBaseUrl },
|
|
43
|
+
`Stripping baseUrl for provider "${provider}" — base_url is only valid for openai-compatible providers.`,
|
|
44
|
+
);
|
|
45
|
+
safeBaseUrl = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
switch (auth.type) {
|
|
31
49
|
case "api_key": {
|
|
32
50
|
const value = await getSecureKeyAsync(auth.credential);
|
|
@@ -41,7 +59,7 @@ export async function resolveAuth(
|
|
|
41
59
|
resolved: {
|
|
42
60
|
kind: "header",
|
|
43
61
|
headers: { Authorization: `Bearer ${value}` },
|
|
44
|
-
...(
|
|
62
|
+
...(safeBaseUrl ? { baseUrl: safeBaseUrl } : {}),
|
|
45
63
|
},
|
|
46
64
|
};
|
|
47
65
|
}
|
|
@@ -65,7 +83,32 @@ export async function resolveAuth(
|
|
|
65
83
|
case "none":
|
|
66
84
|
return { ok: true, resolved: { kind: "none" } };
|
|
67
85
|
|
|
68
|
-
case "oauth_subscription":
|
|
86
|
+
case "oauth_subscription": {
|
|
87
|
+
// Extract the credential prefix from the credential key.
|
|
88
|
+
// The credential field stores "credential/openai-codex/access_token";
|
|
89
|
+
// we need the prefix "credential/openai-codex" for the refresh logic.
|
|
90
|
+
const credentialPrefix = auth.credential.replace(/\/access_token$/, "");
|
|
91
|
+
|
|
92
|
+
const { getValidCodexAccessToken } = await import(
|
|
93
|
+
"./codex-token-refresh.js"
|
|
94
|
+
);
|
|
95
|
+
const token = await getValidCodexAccessToken(credentialPrefix);
|
|
96
|
+
|
|
97
|
+
if (!token) {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
error: { code: "credential_not_found", credential: auth.credential },
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
resolved: {
|
|
106
|
+
kind: "header",
|
|
107
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
69
112
|
case "service_account":
|
|
70
113
|
return {
|
|
71
114
|
ok: false,
|
|
@@ -45,6 +45,13 @@ export interface CatalogModel {
|
|
|
45
45
|
supportsVision?: boolean;
|
|
46
46
|
supportsToolUse?: boolean;
|
|
47
47
|
pricing?: CatalogModelPricing;
|
|
48
|
+
/**
|
|
49
|
+
* Upper bound for `reasoning_effort` accepted by this model's upstream API.
|
|
50
|
+
* Used by providers (e.g. Fireworks) to clamp Vellum's `xhigh`/`max` tiers
|
|
51
|
+
* down to whatever the model documents. Omit to inherit the provider
|
|
52
|
+
* default.
|
|
53
|
+
*/
|
|
54
|
+
maxEffort?: "high" | "xhigh" | "max";
|
|
48
55
|
/** When set, this model is only visible when the named feature flag is enabled. */
|
|
49
56
|
featureFlag?: string;
|
|
50
57
|
}
|
|
@@ -393,6 +400,21 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
393
400
|
linkLabel: "Open Google AI Studio",
|
|
394
401
|
},
|
|
395
402
|
models: [
|
|
403
|
+
{
|
|
404
|
+
id: "gemini-3.5-flash",
|
|
405
|
+
displayName: "Gemini 3.5 Flash",
|
|
406
|
+
contextWindowTokens: 1048576,
|
|
407
|
+
maxOutputTokens: 65536,
|
|
408
|
+
supportsThinking: true,
|
|
409
|
+
supportsCaching: true,
|
|
410
|
+
supportsVision: true,
|
|
411
|
+
supportsToolUse: true,
|
|
412
|
+
pricing: {
|
|
413
|
+
inputPer1mTokens: 1.5,
|
|
414
|
+
outputPer1mTokens: 9.0,
|
|
415
|
+
cacheReadPer1mTokens: 0.15,
|
|
416
|
+
},
|
|
417
|
+
},
|
|
396
418
|
{
|
|
397
419
|
id: "gemini-3.1-pro-preview",
|
|
398
420
|
displayName: "Gemini 3.1 Pro Preview",
|
|
@@ -589,6 +611,7 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
589
611
|
supportsCaching: true,
|
|
590
612
|
supportsVision: true,
|
|
591
613
|
supportsToolUse: true,
|
|
614
|
+
maxEffort: "high",
|
|
592
615
|
pricing: {
|
|
593
616
|
inputPer1mTokens: 0.95,
|
|
594
617
|
outputPer1mTokens: 4.0,
|
|
@@ -636,10 +659,11 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
636
659
|
displayName: "DeepSeek V4 Pro",
|
|
637
660
|
contextWindowTokens: 1040000,
|
|
638
661
|
maxOutputTokens: 131072,
|
|
639
|
-
supportsThinking:
|
|
662
|
+
supportsThinking: true,
|
|
640
663
|
supportsCaching: false,
|
|
641
664
|
supportsVision: false,
|
|
642
665
|
supportsToolUse: true,
|
|
666
|
+
maxEffort: "max",
|
|
643
667
|
pricing: { inputPer1mTokens: 1.74, outputPer1mTokens: 3.48 },
|
|
644
668
|
},
|
|
645
669
|
],
|
|
@@ -775,6 +799,17 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
775
799
|
supportsToolUse: true,
|
|
776
800
|
pricing: { inputPer1mTokens: 3, outputPer1mTokens: 15 },
|
|
777
801
|
},
|
|
802
|
+
{
|
|
803
|
+
id: "x-ai/grok-4.3",
|
|
804
|
+
displayName: "Grok 4.3",
|
|
805
|
+
contextWindowTokens: 1000000,
|
|
806
|
+
maxOutputTokens: 16000,
|
|
807
|
+
supportsThinking: true,
|
|
808
|
+
supportsCaching: false,
|
|
809
|
+
supportsVision: true,
|
|
810
|
+
supportsToolUse: true,
|
|
811
|
+
pricing: { inputPer1mTokens: 1.25, outputPer1mTokens: 2.5 },
|
|
812
|
+
},
|
|
778
813
|
{
|
|
779
814
|
id: "x-ai/grok-4",
|
|
780
815
|
displayName: "Grok 4",
|
|
@@ -1057,6 +1092,18 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1057
1092
|
supportsToolUse: true,
|
|
1058
1093
|
pricing: { inputPer1mTokens: 0.8, outputPer1mTokens: 3.2 },
|
|
1059
1094
|
},
|
|
1095
|
+
// Owl (OpenRouter first-party)
|
|
1096
|
+
{
|
|
1097
|
+
id: "openrouter/owl-alpha",
|
|
1098
|
+
displayName: "Owl Alpha",
|
|
1099
|
+
contextWindowTokens: 1048576,
|
|
1100
|
+
maxOutputTokens: 262144,
|
|
1101
|
+
supportsThinking: false,
|
|
1102
|
+
supportsCaching: false,
|
|
1103
|
+
supportsVision: false,
|
|
1104
|
+
supportsToolUse: true,
|
|
1105
|
+
pricing: { inputPer1mTokens: 0, outputPer1mTokens: 0 },
|
|
1106
|
+
},
|
|
1060
1107
|
],
|
|
1061
1108
|
defaultModel: "x-ai/grok-4.20-beta",
|
|
1062
1109
|
apiKeyUrl: "https://openrouter.ai/keys",
|
|
@@ -66,34 +66,56 @@ export interface OpenAIChatCompletionsProviderOptions {
|
|
|
66
66
|
extraCreateParams?: Record<string, unknown>;
|
|
67
67
|
/** Upper bound for `reasoning_effort` sent on the wire. Defaults to "xhigh"
|
|
68
68
|
* (OpenAI's current ceiling). Compatibility providers whose APIs only
|
|
69
|
-
* document `low|medium|high`
|
|
70
|
-
*
|
|
71
|
-
|
|
69
|
+
* document `low|medium|high` should set this to "high" so Vellum's
|
|
70
|
+
* `xhigh`/`max` tiers don't 4xx upstream. Set to "max" for providers like
|
|
71
|
+
* Fireworks DeepSeek V4 that accept the full effort range. Subclasses can
|
|
72
|
+
* override {@link OpenAIChatCompletionsProvider.resolveMaxReasoningEffort}
|
|
73
|
+
* for per-model ceilings. */
|
|
74
|
+
maxReasoningEffort?: "high" | "xhigh" | "max";
|
|
72
75
|
/** Parse `<think>...</think>` tags from the content stream into thinking
|
|
73
76
|
* blocks. MiniMax and similar providers embed reasoning inside XML-style
|
|
74
77
|
* tags in the regular content field rather than using `reasoning_content`. */
|
|
75
78
|
parseThinkTags?: boolean;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
/** Wire-level reasoning_effort values. The OpenAI SDK type doesn't include
|
|
82
|
+
* `"max"`, but Fireworks accepts it for DeepSeek V4; the assignment to
|
|
83
|
+
* `params.reasoning_effort` casts through this union. */
|
|
84
|
+
type ReasoningEffortWire = "none" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
85
|
+
|
|
86
|
+
const REASONING_EFFORT_RANK: Record<ReasoningEffortWire, number> = {
|
|
87
|
+
none: 0,
|
|
88
|
+
low: 1,
|
|
89
|
+
medium: 2,
|
|
90
|
+
high: 3,
|
|
91
|
+
xhigh: 4,
|
|
92
|
+
max: 5,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/** Map our internal effort values to a reasoning_effort wire value. `"max"`
|
|
96
|
+
* is emitted raw — providers cap it down to their own ceiling
|
|
97
|
+
* ({@link OpenAIChatCompletionsProviderOptions.maxReasoningEffort}) at send
|
|
98
|
+
* time. `"none"` is passed through explicitly because OpenAI-compatible APIs
|
|
99
|
+
* default `reasoning_effort` to `"medium"` when the field is omitted, so the
|
|
100
|
+
* user's opt-out is only honored when we send it on the wire. */
|
|
101
|
+
export const EFFORT_TO_REASONING_EFFORT: Record<string, ReasoningEffortWire> = {
|
|
89
102
|
none: "none",
|
|
90
103
|
low: "low",
|
|
91
104
|
medium: "medium",
|
|
92
105
|
high: "high",
|
|
93
106
|
xhigh: "xhigh",
|
|
94
|
-
max: "
|
|
107
|
+
max: "max",
|
|
95
108
|
};
|
|
96
109
|
|
|
110
|
+
export function clampReasoningEffort(
|
|
111
|
+
value: ReasoningEffortWire,
|
|
112
|
+
ceiling: "high" | "xhigh" | "max",
|
|
113
|
+
): ReasoningEffortWire {
|
|
114
|
+
return REASONING_EFFORT_RANK[value] > REASONING_EFFORT_RANK[ceiling]
|
|
115
|
+
? ceiling
|
|
116
|
+
: value;
|
|
117
|
+
}
|
|
118
|
+
|
|
97
119
|
const OPENAI_SUPPORTED_IMAGE_TYPES = new Set([
|
|
98
120
|
"image/jpeg",
|
|
99
121
|
"image/png",
|
|
@@ -122,7 +144,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
122
144
|
private model: string;
|
|
123
145
|
private streamTimeoutMs: number;
|
|
124
146
|
private extraCreateParams: Record<string, unknown>;
|
|
125
|
-
private maxReasoningEffort: "high" | "xhigh";
|
|
147
|
+
private maxReasoningEffort: "high" | "xhigh" | "max";
|
|
126
148
|
private requestHeaders: Record<string, string>;
|
|
127
149
|
private parseThinkTags: boolean;
|
|
128
150
|
|
|
@@ -187,10 +209,13 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
187
209
|
? EFFORT_TO_REASONING_EFFORT[effort]
|
|
188
210
|
: undefined;
|
|
189
211
|
if (reasoningEffort && typeof nestedReasoningEffort !== "string") {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
212
|
+
const ceiling = this.resolveMaxReasoningEffort(
|
|
213
|
+
modelOverride ?? this.model,
|
|
214
|
+
);
|
|
215
|
+
params.reasoning_effort = clampReasoningEffort(
|
|
216
|
+
reasoningEffort,
|
|
217
|
+
ceiling,
|
|
218
|
+
) as OpenAI.Chat.Completions.ChatCompletionCreateParams["reasoning_effort"];
|
|
194
219
|
}
|
|
195
220
|
|
|
196
221
|
if (tools && tools.length > 0) {
|
|
@@ -546,6 +571,18 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
546
571
|
return this.extraCreateParams;
|
|
547
572
|
}
|
|
548
573
|
|
|
574
|
+
/**
|
|
575
|
+
* Per-request reasoning_effort ceiling. Defaults to the provider-wide
|
|
576
|
+
* `maxReasoningEffort` from constructor options. Subclasses (e.g. Fireworks)
|
|
577
|
+
* override to consult the model catalog so per-model accepted ranges are
|
|
578
|
+
* respected.
|
|
579
|
+
*/
|
|
580
|
+
protected resolveMaxReasoningEffort(
|
|
581
|
+
_model: string,
|
|
582
|
+
): "high" | "xhigh" | "max" {
|
|
583
|
+
return this.maxReasoningEffort;
|
|
584
|
+
}
|
|
585
|
+
|
|
549
586
|
/** Convert neutral messages + system prompt to OpenAI message format. */
|
|
550
587
|
private toOpenAIMessages(
|
|
551
588
|
messages: Message[],
|
|
@@ -23,6 +23,9 @@ export interface OpenAIResponsesProviderOptions {
|
|
|
23
23
|
providerLabel?: string;
|
|
24
24
|
streamTimeoutMs?: number;
|
|
25
25
|
useNativeWebSearch?: boolean;
|
|
26
|
+
/** When true, target the Codex subscription endpoint and strip fields it
|
|
27
|
+
* rejects (`max_output_tokens`, `metadata`). */
|
|
28
|
+
codexSubscription?: boolean;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/** Map our internal effort values to the Responses API reasoning.effort parameter.
|
|
@@ -103,6 +106,7 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
103
106
|
private model: string;
|
|
104
107
|
private streamTimeoutMs: number;
|
|
105
108
|
private useNativeWebSearch: boolean;
|
|
109
|
+
private codexSubscription: boolean;
|
|
106
110
|
|
|
107
111
|
constructor(
|
|
108
112
|
apiKey: string,
|
|
@@ -111,9 +115,12 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
111
115
|
) {
|
|
112
116
|
this.name = options.providerName ?? "openai";
|
|
113
117
|
this.providerLabel = options.providerLabel ?? "OpenAI";
|
|
118
|
+
this.codexSubscription = options.codexSubscription ?? false;
|
|
114
119
|
this.client = new OpenAI({
|
|
115
120
|
apiKey,
|
|
116
|
-
baseURL:
|
|
121
|
+
baseURL: this.codexSubscription
|
|
122
|
+
? "https://chatgpt.com/backend-api/codex"
|
|
123
|
+
: options.baseURL,
|
|
117
124
|
});
|
|
118
125
|
this.model = model;
|
|
119
126
|
this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
|
|
@@ -142,7 +149,6 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
142
149
|
const params: Record<string, unknown> = {
|
|
143
150
|
model: modelOverride ?? this.model,
|
|
144
151
|
input,
|
|
145
|
-
store: false,
|
|
146
152
|
};
|
|
147
153
|
|
|
148
154
|
if (systemPrompt) {
|
|
@@ -152,7 +158,7 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
152
158
|
);
|
|
153
159
|
}
|
|
154
160
|
|
|
155
|
-
if (maxTokens) {
|
|
161
|
+
if (maxTokens && !this.codexSubscription) {
|
|
156
162
|
params.max_output_tokens = maxTokens;
|
|
157
163
|
}
|
|
158
164
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ProviderError } from "../../util/errors.js";
|
|
2
2
|
import { AnthropicProvider } from "../anthropic/client.js";
|
|
3
3
|
import {
|
|
4
|
+
clampReasoningEffort,
|
|
4
5
|
EFFORT_TO_REASONING_EFFORT,
|
|
5
6
|
OpenAIChatCompletionsProvider,
|
|
6
7
|
} from "../openai/chat-completions-provider.js";
|
|
@@ -200,7 +201,10 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
|
|
|
200
201
|
const summaryOverride = extractReasoningSummaryOverride(config);
|
|
201
202
|
const reasoning: Record<string, unknown> = { enabled: thinkingEnabled };
|
|
202
203
|
if (mappedEffort) {
|
|
203
|
-
reasoning.effort =
|
|
204
|
+
reasoning.effort = clampReasoningEffort(
|
|
205
|
+
mappedEffort,
|
|
206
|
+
this.resolveMaxReasoningEffort(this.resolveEffectiveModel(options)),
|
|
207
|
+
);
|
|
204
208
|
}
|
|
205
209
|
if (thinkingEnabled) {
|
|
206
210
|
reasoning.summary = summaryOverride ?? "detailed";
|
package/src/providers/types.ts
CHANGED
|
@@ -27,6 +27,15 @@ export interface FileContent {
|
|
|
27
27
|
filename: string;
|
|
28
28
|
};
|
|
29
29
|
extracted_text?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Internal id linking this block to a row in the attachments table.
|
|
32
|
+
* Set when the file block originates from a persisted user-message
|
|
33
|
+
* attachment so downstream consumers (DB joins, inline-chip
|
|
34
|
+
* positioning) can correlate the block back to its attachment id.
|
|
35
|
+
* Stripped by `daemon/handlers/shared.ts` before sending to the
|
|
36
|
+
* model.
|
|
37
|
+
*/
|
|
38
|
+
_attachmentId?: string;
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
export interface ToolUseContent {
|
|
@@ -137,6 +146,22 @@ export type ProviderEvent =
|
|
|
137
146
|
toolUseId: string;
|
|
138
147
|
isError: boolean;
|
|
139
148
|
content?: unknown[];
|
|
149
|
+
/**
|
|
150
|
+
* Finalized input for the server tool call (e.g. the actual query).
|
|
151
|
+
* Anthropic streams `server_tool_use` block input via `input_json_delta`
|
|
152
|
+
* events, so consumers reading the input at `server_tool_start` see `{}`.
|
|
153
|
+
* The provider accumulates the JSON and surfaces it here once the block
|
|
154
|
+
* stops, so downstream handlers can build accurate activity metadata.
|
|
155
|
+
*/
|
|
156
|
+
resolvedInput?: Record<string, unknown>;
|
|
157
|
+
/**
|
|
158
|
+
* Provider-specific error code when `isError` is true (e.g. Anthropic's
|
|
159
|
+
* `max_uses_exceeded`, `query_too_long`). Surfaced so user-facing
|
|
160
|
+
* messages can be specific instead of a generic "Search failed".
|
|
161
|
+
*/
|
|
162
|
+
errorCode?: string;
|
|
163
|
+
/** Optional human-readable error message from the provider. */
|
|
164
|
+
errorMessage?: string;
|
|
140
165
|
};
|
|
141
166
|
|
|
142
167
|
export interface SendMessageConfig {
|
|
@@ -22,8 +22,29 @@ import type { DiskPressureStatus } from "../../daemon/disk-pressure-guard.js";
|
|
|
22
22
|
// Stub the DB-backed override-profile read so unit tests don't need a
|
|
23
23
|
// real SQLite database. The wake helper calls this on every invocation
|
|
24
24
|
// to honor the conversation's pinned inference profile.
|
|
25
|
+
// `getConversation` is consumed by `defaultResolveTarget` — most tests
|
|
26
|
+
// pass explicit `deps.resolveTarget` and bypass it, but the
|
|
27
|
+
// trust-context threading test below drives the default resolver and
|
|
28
|
+
// needs the existence/archived check to pass.
|
|
25
29
|
mock.module("../../memory/conversation-crud.js", () => ({
|
|
26
30
|
getConversationOverrideProfile: () => undefined,
|
|
31
|
+
getConversation: () => ({ archivedAt: null }),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
const mockGetOrCreateConversationCalls: Array<{
|
|
35
|
+
conversationId: string;
|
|
36
|
+
options: unknown;
|
|
37
|
+
}> = [];
|
|
38
|
+
mock.module("../../daemon/conversation-store.js", () => ({
|
|
39
|
+
getOrCreateConversation: (conversationId: string, options?: unknown) => {
|
|
40
|
+
mockGetOrCreateConversationCalls.push({ conversationId, options });
|
|
41
|
+
return Promise.resolve({ __mockConversation: true });
|
|
42
|
+
},
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
let mockResolverTarget: unknown = null;
|
|
46
|
+
mock.module("../../daemon/wake-target-adapter.js", () => ({
|
|
47
|
+
conversationToWakeTarget: () => mockResolverTarget,
|
|
27
48
|
}));
|
|
28
49
|
|
|
29
50
|
mock.module("../../config/loader.js", () => ({
|
|
@@ -248,6 +269,8 @@ function makeTarget(options: {
|
|
|
248
269
|
beforeEach(() => {
|
|
249
270
|
__resetWakeChainForTests();
|
|
250
271
|
recordRequestLogCalls.length = 0;
|
|
272
|
+
mockGetOrCreateConversationCalls.length = 0;
|
|
273
|
+
mockResolverTarget = null;
|
|
251
274
|
mockDiskPressureStatus = {
|
|
252
275
|
enabled: false,
|
|
253
276
|
state: "disabled",
|
|
@@ -1587,4 +1610,195 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1587
1610
|
// No log row was inserted because JSON.stringify threw.
|
|
1588
1611
|
expect(recordRequestLogCalls).toHaveLength(0);
|
|
1589
1612
|
});
|
|
1613
|
+
|
|
1614
|
+
// Regression guard for fork-based memory retrospectives: PR #31260
|
|
1615
|
+
// forked a conversation and waked it with a guardian trustContext,
|
|
1616
|
+
// but the wake's default resolver called
|
|
1617
|
+
// `getOrCreateConversation(conversationId)` with no options, so the
|
|
1618
|
+
// store hydrated with `trustContext === undefined`. `loadFromDb`
|
|
1619
|
+
// fail-closes to `trustClass: "unknown"` and filters out every
|
|
1620
|
+
// guardian-provenance message — so the LLM saw an empty history and
|
|
1621
|
+
// every fork sent `messages: []`. Threading trustContext through
|
|
1622
|
+
// ensures `setTrustContext` + `ensureActorScopedHistory` run during
|
|
1623
|
+
// hydration.
|
|
1624
|
+
const makeDefaultResolverTarget = (conversationId: string): WakeTarget => {
|
|
1625
|
+
const history: Message[] = [];
|
|
1626
|
+
let processing = false;
|
|
1627
|
+
return {
|
|
1628
|
+
conversationId,
|
|
1629
|
+
agentLoop: { run: async (input) => input },
|
|
1630
|
+
getMessages: () => history,
|
|
1631
|
+
pushMessage: () => {},
|
|
1632
|
+
emitAgentEvent: () => {},
|
|
1633
|
+
isProcessing: () => processing,
|
|
1634
|
+
markProcessing: (on) => {
|
|
1635
|
+
processing = on;
|
|
1636
|
+
},
|
|
1637
|
+
persistTailMessage: async () => {},
|
|
1638
|
+
};
|
|
1639
|
+
};
|
|
1640
|
+
|
|
1641
|
+
test("default resolver threads WakeOptions.trustContext into getOrCreateConversation", async () => {
|
|
1642
|
+
mockResolverTarget = makeDefaultResolverTarget("conv-thread-trust");
|
|
1643
|
+
const trustContext = {
|
|
1644
|
+
sourceChannel: "vellum",
|
|
1645
|
+
trustClass: "guardian",
|
|
1646
|
+
} as const;
|
|
1647
|
+
|
|
1648
|
+
await wakeAgentForOpportunity({
|
|
1649
|
+
conversationId: "conv-thread-trust",
|
|
1650
|
+
hint: "consolidate",
|
|
1651
|
+
source: "memory_v2_consolidation",
|
|
1652
|
+
trustContext,
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
expect(mockGetOrCreateConversationCalls).toEqual([
|
|
1656
|
+
{ conversationId: "conv-thread-trust", options: { trustContext } },
|
|
1657
|
+
]);
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1660
|
+
test("default resolver passes trustContext: undefined when WakeOptions.trustContext is omitted", async () => {
|
|
1661
|
+
// Inbound user-turn wakes get trust via processMessage(); the wake
|
|
1662
|
+
// must not synthesize a trust context out of thin air.
|
|
1663
|
+
mockResolverTarget = makeDefaultResolverTarget("conv-no-trust-default");
|
|
1664
|
+
|
|
1665
|
+
await wakeAgentForOpportunity({
|
|
1666
|
+
conversationId: "conv-no-trust-default",
|
|
1667
|
+
hint: "x",
|
|
1668
|
+
source: "unit-test",
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
expect(mockGetOrCreateConversationCalls).toEqual([
|
|
1672
|
+
{
|
|
1673
|
+
conversationId: "conv-no-trust-default",
|
|
1674
|
+
options: { trustContext: undefined },
|
|
1675
|
+
},
|
|
1676
|
+
]);
|
|
1677
|
+
});
|
|
1678
|
+
|
|
1679
|
+
describe("suppressWakeSurface option", () => {
|
|
1680
|
+
function makeCheckpointTarget(): {
|
|
1681
|
+
target: WakeTarget;
|
|
1682
|
+
persistedTailCalls: Message[];
|
|
1683
|
+
wakeProducedOutputCalls: string[];
|
|
1684
|
+
} {
|
|
1685
|
+
const firstAssistant: Message = {
|
|
1686
|
+
role: "assistant",
|
|
1687
|
+
content: [
|
|
1688
|
+
{ type: "tool_use", id: "tu-1", name: "some_tool", input: {} },
|
|
1689
|
+
],
|
|
1690
|
+
};
|
|
1691
|
+
const toolResult: Message = {
|
|
1692
|
+
role: "user",
|
|
1693
|
+
content: [{ type: "tool_result", tool_use_id: "tu-1", content: "ok" }],
|
|
1694
|
+
};
|
|
1695
|
+
const persistedTailCalls: Message[] = [];
|
|
1696
|
+
const baseline: Message[] = [
|
|
1697
|
+
{ role: "user", content: [{ type: "text", text: "hi" }] },
|
|
1698
|
+
];
|
|
1699
|
+
const history: Message[] = [...baseline];
|
|
1700
|
+
let processing = false;
|
|
1701
|
+
const wakeProducedOutputCalls: string[] = [];
|
|
1702
|
+
|
|
1703
|
+
const target: WakeTarget = {
|
|
1704
|
+
conversationId: "conv-suppress-surface",
|
|
1705
|
+
agentLoop: {
|
|
1706
|
+
run: async (_input, _onEvent, _signal, _requestId, onCheckpoint) => {
|
|
1707
|
+
const runHistory: Message[] = [..._input];
|
|
1708
|
+
runHistory.push(firstAssistant);
|
|
1709
|
+
runHistory.push(toolResult);
|
|
1710
|
+
await onCheckpoint!({
|
|
1711
|
+
turnIndex: 0,
|
|
1712
|
+
toolCount: 1,
|
|
1713
|
+
hasToolUse: true,
|
|
1714
|
+
history: runHistory,
|
|
1715
|
+
});
|
|
1716
|
+
return runHistory;
|
|
1717
|
+
},
|
|
1718
|
+
},
|
|
1719
|
+
getMessages: () => history,
|
|
1720
|
+
pushMessage: (msg) => {
|
|
1721
|
+
history.push(msg);
|
|
1722
|
+
},
|
|
1723
|
+
emitAgentEvent: () => {},
|
|
1724
|
+
isProcessing: () => processing,
|
|
1725
|
+
markProcessing: (on) => {
|
|
1726
|
+
processing = on;
|
|
1727
|
+
},
|
|
1728
|
+
persistTailMessage: async (msg) => {
|
|
1729
|
+
persistedTailCalls.push(msg);
|
|
1730
|
+
},
|
|
1731
|
+
onWakeProducedOutput: (_source, _hint, surfaceId) => {
|
|
1732
|
+
wakeProducedOutputCalls.push(surfaceId);
|
|
1733
|
+
},
|
|
1734
|
+
};
|
|
1735
|
+
return { target, persistedTailCalls, wakeProducedOutputCalls };
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
test(
|
|
1739
|
+
"default (suppressWakeSurface omitted) still injects the ui_surface " +
|
|
1740
|
+
"card and calls onWakeProducedOutput",
|
|
1741
|
+
async () => {
|
|
1742
|
+
const { target, persistedTailCalls, wakeProducedOutputCalls } =
|
|
1743
|
+
makeCheckpointTarget();
|
|
1744
|
+
|
|
1745
|
+
await wakeAgentForOpportunity(
|
|
1746
|
+
{
|
|
1747
|
+
conversationId: "conv-suppress-surface",
|
|
1748
|
+
hint: "do the thing",
|
|
1749
|
+
source: "memory_v2_consolidation",
|
|
1750
|
+
},
|
|
1751
|
+
{ resolveTarget: async () => target },
|
|
1752
|
+
);
|
|
1753
|
+
|
|
1754
|
+
// Existing behavior: card injected, broadcast fired exactly once.
|
|
1755
|
+
expect(wakeProducedOutputCalls).toHaveLength(1);
|
|
1756
|
+
const persistedFirst = persistedTailCalls[0];
|
|
1757
|
+
expect(persistedFirst).toBeDefined();
|
|
1758
|
+
const blocks = Array.isArray(persistedFirst!.content)
|
|
1759
|
+
? persistedFirst!.content
|
|
1760
|
+
: [];
|
|
1761
|
+
const uiBlock = blocks.find(
|
|
1762
|
+
(b: { type?: string }) => b.type === "ui_surface",
|
|
1763
|
+
);
|
|
1764
|
+
expect(uiBlock).toBeDefined();
|
|
1765
|
+
},
|
|
1766
|
+
);
|
|
1767
|
+
|
|
1768
|
+
test(
|
|
1769
|
+
"suppressWakeSurface: true produces output but skips the ui_surface " +
|
|
1770
|
+
"card injection and the onWakeProducedOutput broadcast",
|
|
1771
|
+
async () => {
|
|
1772
|
+
const { target, persistedTailCalls, wakeProducedOutputCalls } =
|
|
1773
|
+
makeCheckpointTarget();
|
|
1774
|
+
|
|
1775
|
+
await wakeAgentForOpportunity(
|
|
1776
|
+
{
|
|
1777
|
+
conversationId: "conv-suppress-surface",
|
|
1778
|
+
hint: "do the thing",
|
|
1779
|
+
source: "memory_v2_consolidation",
|
|
1780
|
+
suppressWakeSurface: true,
|
|
1781
|
+
},
|
|
1782
|
+
{ resolveTarget: async () => target },
|
|
1783
|
+
);
|
|
1784
|
+
|
|
1785
|
+
// Tail still persisted (wake produced real output).
|
|
1786
|
+
const persistedFirst = persistedTailCalls[0];
|
|
1787
|
+
expect(persistedFirst).toBeDefined();
|
|
1788
|
+
// First assistant tail message should NOT have a ui_surface block
|
|
1789
|
+
// prepended at the front.
|
|
1790
|
+
const blocks = Array.isArray(persistedFirst!.content)
|
|
1791
|
+
? persistedFirst!.content
|
|
1792
|
+
: [];
|
|
1793
|
+
const firstBlock = blocks[0] as { type?: string } | undefined;
|
|
1794
|
+
expect(firstBlock?.type).not.toBe("ui_surface");
|
|
1795
|
+
const uiBlock = blocks.find(
|
|
1796
|
+
(b: { type?: string }) => b.type === "ui_surface",
|
|
1797
|
+
);
|
|
1798
|
+
expect(uiBlock).toBeUndefined();
|
|
1799
|
+
// Live broadcast was suppressed.
|
|
1800
|
+
expect(wakeProducedOutputCalls).toHaveLength(0);
|
|
1801
|
+
},
|
|
1802
|
+
);
|
|
1803
|
+
});
|
|
1590
1804
|
});
|