@vellumai/assistant 0.11.8 → 0.11.9-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -2
- package/Dockerfile +8 -48
- package/docker-entrypoint.sh +5 -1
- package/docker-kata-apt-env.sh +3 -0
- package/docker-kata-apt-shims.sh +127 -0
- package/docker-kata-apt-wrapper.sh +45 -0
- package/docker-kata-chroot-exec.sh +35 -0
- package/docker-kata-pip.sh +8 -2
- package/docs/architecture/memory.md +8 -4
- package/docs/guardian-request-flow.md +18 -14
- package/docs/trusted-contact-access.md +1 -7
- package/node_modules/@vellumai/avatar-catalog/src/catalog.ts +7 -1
- package/node_modules/@vellumai/avatar-catalog/src/index.ts +1 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/platform-credential.ts +41 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/platform-credential.ts +41 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +0 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +110 -5
- package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +5 -33
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +3 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +100 -9
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -2
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +9 -0
- package/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/platform-credential.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/openapi.yaml +193 -4
- package/package.json +2 -2
- package/src/__tests__/access-request-card-view.test.ts +6 -5
- package/src/__tests__/access-request-seed-content-blocks.test.ts +5 -2
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +43 -82
- package/src/__tests__/always-loaded-tools-guard.test.ts +8 -2
- package/src/__tests__/attachment-stored-path-annotation.test.ts +80 -0
- package/src/__tests__/attachments-store.test.ts +27 -0
- package/src/__tests__/attachments.test.ts +43 -0
- package/src/__tests__/canned-reply-release.test.ts +48 -5
- package/src/__tests__/channel-delivery-store.test.ts +129 -18
- package/src/__tests__/channel-reply-delivery.test.ts +553 -93
- package/src/__tests__/channel-retry-sweep.test.ts +199 -0
- package/src/__tests__/client-os-metadata-persistence.test.ts +32 -3
- package/src/__tests__/conversation-error.test.ts +15 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +118 -0
- package/src/__tests__/conversation-pairing.test.ts +141 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +28 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +125 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +79 -0
- package/src/__tests__/conversation-store-ephemeral.test.ts +323 -10
- package/src/__tests__/conversation-sync-tags.test.ts +2 -37
- package/src/__tests__/credential-health-service.test.ts +98 -10
- package/src/__tests__/delete-propagation.test.ts +469 -0
- package/src/__tests__/dm-persistence.test.ts +16 -0
- package/src/__tests__/docker-kata-apt-shims.test.ts +135 -0
- package/src/__tests__/forbidden-legacy-symbols.test.ts +12 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +0 -22
- package/src/__tests__/guardian-gateway-sim.ts +0 -23
- package/src/__tests__/guardian-question-mode.test.ts +108 -0
- package/src/__tests__/guardian-reply-router-answer-mode.test.ts +0 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +26 -12
- package/src/__tests__/host-proxy-interface.test.ts +10 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +16 -0
- package/src/__tests__/injector-v3-suppression.test.ts +43 -9
- package/src/__tests__/list-messages-page-latest.test.ts +127 -0
- package/src/__tests__/list-messages-system-card.test.ts +103 -0
- package/src/__tests__/media-resolve-image-validation.test.ts +77 -0
- package/src/__tests__/notification-decision-fallback.test.ts +199 -77
- package/src/__tests__/notification-decision-strategy.test.ts +198 -213
- package/src/__tests__/notification-discord-adapter.test.ts +25 -0
- package/src/__tests__/notification-slack-adapter.test.ts +133 -0
- package/src/__tests__/notification-telegram-adapter.test.ts +36 -0
- package/src/__tests__/openai-provider.test.ts +5 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +85 -72
- package/src/__tests__/persist-user-message-set-processing-failure.test.ts +83 -65
- package/src/__tests__/platform-client-verify-credential.test.ts +100 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -1
- package/src/__tests__/process-message-display-content.test.ts +20 -9
- package/src/__tests__/processing-acquire-fenced-guard.test.ts +56 -0
- package/src/__tests__/provider-meta-persistence.test.ts +67 -0
- package/src/__tests__/reaction-persistence.test.ts +22 -198
- package/src/__tests__/run-conversation-turn-persistence.test.ts +5 -2
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +16 -0
- package/src/__tests__/skill-load-tool.test.ts +27 -0
- package/src/__tests__/skills.test.ts +34 -1
- package/src/__tests__/strip-memory-injections.test.ts +3 -4
- package/src/__tests__/terminal-tools.test.ts +9 -0
- package/src/__tests__/thread-backfill.test.ts +5 -3
- package/src/__tests__/unified-turn-context-location.test.ts +76 -0
- package/src/__tests__/voice-session-bridge.test.ts +18 -0
- package/src/__tests__/watch-retro-report-payload.test.ts +185 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +82 -0
- package/src/__tests__/workspace-migration-151-repair-renamed-fireworks-deepseek-pro-model-id.test.ts +235 -0
- package/src/__tests__/workspace-migration-152-repair-retired-fireworks-minimax-m2p7-model-id.test.ts +233 -0
- package/src/agent/attachments.ts +13 -2
- package/src/agent/loop.ts +3 -19
- package/src/api/README.md +9 -5
- package/src/api/index.ts +9 -8
- package/src/api/package.json +1 -0
- package/src/api/responses/conversation-message.ts +8 -0
- package/src/api/responses/home.ts +7 -18
- package/src/api/surfaces.ts +114 -7
- package/src/approvals/AGENTS.md +1 -1
- package/src/channels/__tests__/gateway-guardian-requests.test.ts +1 -23
- package/src/channels/__tests__/types.test.ts +22 -1
- package/src/channels/gateway-guardian-requests.ts +0 -22
- package/src/channels/types.ts +8 -6
- package/src/cli/__tests__/catalog-search-help.test.ts +12 -0
- package/src/cli/commands/channels/__tests__/channels.test.ts +26 -0
- package/src/cli/commands/channels/index.help.ts +15 -1
- package/src/cli/commands/channels/index.ts +6 -2
- package/src/cli/commands/db/__tests__/status.test.ts +22 -0
- package/src/cli/commands/db/index.help.ts +1 -1
- package/src/cli/commands/db/status.ts +172 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +29 -0
- package/src/cli/commands/platform/connect.ts +14 -5
- package/src/cli/commands/plugins.help.ts +6 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +0 -8
- package/src/cli/lib/__tests__/install-from-platform.test.ts +72 -0
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +33 -0
- package/src/cli/lib/bundled-marketplace.json +14 -0
- package/src/cli/lib/install-from-github.ts +9 -5
- package/src/cli/lib/install-from-platform.ts +12 -1
- package/src/config/__tests__/assistant-initiated-threads-gate.test.ts +61 -0
- package/src/config/assistant-initiated-threads-gate.ts +50 -0
- package/src/config/bundled-skills/acp/SKILL.md +10 -3
- package/src/config/bundled-skills/schedule/SKILL.md +25 -11
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +3 -1
- package/src/config/call-site-defaults.ts +4 -1
- package/src/config/feature-flag-registry.json +21 -4
- package/src/config/schemas/memory-v3.ts +4 -3
- package/src/context/strip-injections.ts +17 -56
- package/src/conversations/__tests__/message-consolidation.test.ts +39 -0
- package/src/conversations/message-consolidation.ts +4 -3
- package/src/credential-health/credential-health-service.ts +130 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +31 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +55 -59
- package/src/daemon/conversation-error.ts +2 -0
- package/src/daemon/conversation-messaging.ts +195 -47
- package/src/daemon/conversation-process.ts +20 -5
- package/src/daemon/conversation-runtime-assembly.ts +48 -39
- package/src/daemon/conversation-store.ts +159 -11
- package/src/daemon/conversation-tool-setup.ts +9 -2
- package/src/daemon/conversation.ts +290 -19
- package/src/daemon/dictation-text-processing.ts +2 -7
- package/src/daemon/handlers/config-channels.ts +33 -3
- package/src/daemon/handlers/shared.ts +9 -0
- package/src/daemon/port-oversized-content.test.ts +117 -0
- package/src/daemon/port-oversized-content.ts +110 -0
- package/src/daemon/process-message.ts +87 -16
- package/src/daemon/reaction-record.test.ts +23 -11
- package/src/daemon/reaction-record.ts +6 -9
- package/src/documents/document-store.ts +1 -5
- package/src/home/conversation-starter-validation.ts +1 -5
- package/src/live-voice/__tests__/live-voice-photo.test.ts +910 -50
- package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +31 -25
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +70 -1
- package/src/live-voice/live-voice-photo.ts +544 -61
- package/src/live-voice/live-voice-session.ts +6 -2
- package/src/live-voice/protocol.ts +20 -1
- package/src/messaging/provider-message-metadata.ts +40 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +10 -2
- package/src/messaging/providers/channel-transport.ts +28 -0
- package/src/messaging/providers/discord/send.ts +3 -2
- package/src/messaging/providers/slack/api.ts +11 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +133 -0
- package/src/messaging/providers/slack/message-metadata.ts +141 -1
- package/src/messaging/providers/slack/send.test.ts +58 -0
- package/src/messaging/providers/slack/send.ts +4 -4
- package/src/messaging/providers/slack/transport.ts +28 -2
- package/src/messaging/providers/telegram-bot/send.test.ts +159 -0
- package/src/messaging/providers/telegram-bot/send.ts +93 -0
- package/src/messaging/providers/telegram-bot/transport.ts +71 -0
- package/src/messaging/reaction-envelopes.test.ts +73 -0
- package/src/messaging/reaction-envelopes.ts +33 -5
- package/src/messaging/read-provider-metadata.ts +4 -3
- package/src/monitoring/recovery/__tests__/stranded-delivery-events.test.ts +208 -0
- package/src/monitoring/recovery/db.ts +35 -0
- package/src/monitoring/recovery/orphaned-channel-events.ts +3 -22
- package/src/monitoring/recovery/run-recovery.ts +6 -2
- package/src/monitoring/recovery/stale-processing.ts +3 -20
- package/src/monitoring/recovery/stranded-delivery-events.ts +68 -0
- package/src/notifications/AGENTS.md +2 -2
- package/src/notifications/README.md +2 -2
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +11 -9
- package/src/notifications/__tests__/broadcaster.test.ts +35 -4
- package/src/notifications/__tests__/notification-utils.test.ts +31 -0
- package/src/notifications/access-request-copy.ts +126 -114
- package/src/notifications/adapters/discord.ts +9 -5
- package/src/notifications/adapters/shared.ts +17 -4
- package/src/notifications/adapters/slack.ts +14 -4
- package/src/notifications/adapters/telegram.ts +8 -4
- package/src/notifications/approval-card-data.ts +5 -2
- package/src/notifications/assistant-reply-producer.ts +7 -7
- package/src/notifications/broadcaster.ts +57 -25
- package/src/notifications/conversation-pairing.ts +68 -2
- package/src/notifications/copy-composer.ts +13 -32
- package/src/notifications/decision-engine.ts +56 -237
- package/src/notifications/guardian-delivery-recorder.ts +5 -9
- package/src/notifications/guardian-feed-projection.ts +0 -15
- package/src/notifications/guardian-question-mode.ts +147 -6
- package/src/notifications/notification-utils.ts +117 -1
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -1
- package/src/persistence/conversation-crud.ts +93 -14
- package/src/persistence/conversation-queries.ts +144 -17
- package/src/persistence/conversation-types.ts +39 -5
- package/src/persistence/delivery-crud.ts +224 -28
- package/src/persistence/delivery-status.ts +21 -2
- package/src/persistence/migrations/374-channel-inbound-message-id-index.ts +26 -0
- package/src/persistence/migrations/375-create-channel-outbound-posts.ts +52 -0
- package/src/persistence/migrations/__tests__/375-create-channel-outbound-posts.test.ts +84 -0
- package/src/persistence/schema/conversations.ts +71 -2
- package/src/persistence/schema-contract.test.ts +78 -0
- package/src/persistence/schema-contract.ts +96 -0
- package/src/persistence/steps.ts +4 -0
- package/src/platform/client.ts +55 -0
- package/src/plugins/__tests__/mcp-servers.test.ts +46 -0
- package/src/plugins/defaults/injector-order.ts +2 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +2 -2
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +13 -15
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +10 -0
- package/src/plugins/defaults/memory/injectors.ts +1 -1
- package/src/plugins/defaults/memory/memory-marker.ts +17 -7
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +1 -5
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +54 -41
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +3 -3
- package/src/plugins/defaults/memory/v3/injector.ts +14 -15
- package/src/plugins/defaults/memory/v3/types.ts +15 -4
- package/src/plugins/defaults/turn-context/injectors.ts +3 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +24 -0
- package/src/plugins/mcp-servers.ts +14 -2
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +50 -0
- package/src/providers/__tests__/registry-native-web-search.test.ts +49 -2
- package/src/providers/anthropic/client.ts +21 -24
- package/src/providers/call-site-routing.ts +5 -4
- package/src/providers/connection-resolution.ts +29 -1
- package/src/providers/content-block-size.test.ts +82 -0
- package/src/providers/content-block-size.ts +99 -0
- package/src/providers/file-block-text.test.ts +64 -0
- package/src/providers/file-block-text.ts +28 -0
- package/src/providers/gemini/client.ts +12 -7
- package/src/providers/inference/auth.ts +6 -6
- package/src/providers/media-resolve.ts +14 -0
- package/src/providers/model-catalog.ts +31 -34
- package/src/providers/openai/chat-completions-provider.ts +9 -25
- package/src/providers/openai/responses-provider.ts +11 -18
- package/src/providers/registry.ts +10 -7
- package/src/providers/routing-identity.ts +2 -1
- package/src/providers/types.ts +1 -4
- package/src/providers/vellum-model-routing.ts +3 -2
- package/src/runtime/AGENTS.md +40 -15
- package/src/runtime/approval-message-composer.ts +1 -6
- package/src/runtime/assistant-event-hub.ts +2 -39
- package/src/runtime/channel-approval-types.ts +1 -5
- package/src/runtime/channel-reply-delivery.ts +182 -115
- package/src/runtime/{slack-reply-session.test.ts → channel-reply-session.test.ts} +294 -156
- package/src/runtime/{slack-reply-session.ts → channel-reply-session.ts} +107 -86
- package/src/runtime/channel-retry-sweep.ts +102 -1
- package/src/runtime/finalize-event-delivery.ts +4 -4
- package/src/runtime/guardian-action-message-composer.ts +1 -4
- package/src/runtime/guardian-reply-router.ts +4 -75
- package/src/runtime/http-router.ts +1 -5
- package/src/runtime/question-request-guardian-bridge.ts +2 -3
- package/src/runtime/routes/__tests__/conversation-list-assistant-section.test.ts +359 -0
- package/src/runtime/routes/__tests__/dictation-command-mode.test.ts +120 -0
- package/src/runtime/routes/__tests__/sight-frame-routes.test.ts +487 -0
- package/src/runtime/routes/acp-routes.ts +1 -1
- package/src/runtime/routes/canned-reply-release.ts +19 -9
- package/src/runtime/routes/channel-route-shared.ts +0 -39
- package/src/runtime/routes/channel-verification-routes.ts +4 -1
- package/src/runtime/routes/conversation-list-routes.ts +44 -3
- package/src/runtime/routes/conversation-management-routes.ts +2 -1
- package/src/runtime/routes/conversation-routes.ts +138 -59
- package/src/runtime/routes/diagnostics-routes.ts +111 -60
- package/src/runtime/routes/guardian-action-routes.ts +10 -14
- package/src/runtime/routes/guardian-approval-interception.ts +0 -5
- package/src/runtime/routes/inbound-message-handler.ts +170 -48
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +4 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +169 -8
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +82 -14
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +23 -48
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +454 -94
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +309 -102
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/platform-routes.ts +99 -10
- package/src/runtime/routes/sight-frame-routes.ts +136 -0
- package/src/runtime/sync/resource-sync-events.ts +14 -37
- package/src/runtime/sync/sync-publisher.test.ts +5 -4
- package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
- package/src/tools/client-os.ts +11 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/terminal/safe-env.ts +4 -0
- package/src/tools/tool-input-schemas.ts +2 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +4 -3
- package/src/tools/watch/watch-retro-report.ts +205 -0
- package/src/watch/__tests__/watch-retro.test.ts +268 -40
- package/src/watch/watch-retro.ts +190 -77
- package/src/workspace/migrations/151-repair-renamed-fireworks-deepseek-pro-model-id.ts +195 -0
- package/src/workspace/migrations/152-repair-retired-fireworks-minimax-m2p7-model-id.ts +198 -0
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +4 -0
- package/docker-kata-pip-chroot.sh +0 -22
- package/src/__tests__/slack-reaction-approvals.test.ts +0 -97
- package/src/__tests__/slack-reaction-guardian-approval.test.ts +0 -307
- package/src/api/events/conversation-list-invalidated.ts +0 -38
|
@@ -10,6 +10,12 @@ import {
|
|
|
10
10
|
} from "../../util/provider-error-patterns.js";
|
|
11
11
|
import { extractRetryAfterMs } from "../../util/retry.js";
|
|
12
12
|
import { stripOrphanedSurrogatesDeep } from "../../util/unicode.js";
|
|
13
|
+
import {
|
|
14
|
+
clampProviderString,
|
|
15
|
+
isDecodableTextMimeType,
|
|
16
|
+
keepFileAsWorkspaceRef,
|
|
17
|
+
} from "../content-block-size.js";
|
|
18
|
+
import { fileBlockToProviderText } from "../file-block-text.js";
|
|
13
19
|
import { base64Source, resolveMediaReferences } from "../media-resolve.js";
|
|
14
20
|
import {
|
|
15
21
|
couldBePlaceholderSentinelPrefix,
|
|
@@ -106,8 +112,7 @@ function readAnthropicErrorType(
|
|
|
106
112
|
error: InstanceType<typeof Anthropic.APIError>,
|
|
107
113
|
): string | undefined {
|
|
108
114
|
const body = error.error as
|
|
109
|
-
|
|
110
|
-
| undefined;
|
|
115
|
+
{ type?: string; error?: { type?: string; code?: string } } | undefined;
|
|
111
116
|
return body?.error?.type ?? body?.type;
|
|
112
117
|
}
|
|
113
118
|
|
|
@@ -127,8 +132,7 @@ function readAnthropicMessage(
|
|
|
127
132
|
error: InstanceType<typeof Anthropic.APIError>,
|
|
128
133
|
): string | undefined {
|
|
129
134
|
const body = error.error as
|
|
130
|
-
|
|
131
|
-
| undefined;
|
|
135
|
+
{ message?: string; error?: { message?: string } } | undefined;
|
|
132
136
|
const inner = body?.error?.message ?? body?.message;
|
|
133
137
|
return typeof inner === "string" && inner.length > 0 ? inner : undefined;
|
|
134
138
|
}
|
|
@@ -283,15 +287,6 @@ const ANTHROPIC_SUPPORTED_IMAGE_TYPES = new Set([
|
|
|
283
287
|
"image/webp",
|
|
284
288
|
]);
|
|
285
289
|
|
|
286
|
-
function isTextBasedMimeType(mediaType: string): boolean {
|
|
287
|
-
return (
|
|
288
|
-
mediaType.startsWith("text/") ||
|
|
289
|
-
mediaType === "application/json" ||
|
|
290
|
-
mediaType === "application/xml" ||
|
|
291
|
-
mediaType === "application/javascript"
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
290
|
/** Anthropic requires tool_use IDs to match ^[a-zA-Z0-9_-]+$ */
|
|
296
291
|
function sanitizeToolId(id: string): string {
|
|
297
292
|
if (!id) {
|
|
@@ -968,8 +963,7 @@ export class AnthropicProvider implements Provider {
|
|
|
968
963
|
const { tools, systemPrompt, config, onEvent, signal } = options ?? {};
|
|
969
964
|
const cacheTtl: "5m" | "1h" =
|
|
970
965
|
((config as Record<string, unknown> | undefined)?.cacheTtl as
|
|
971
|
-
| "
|
|
972
|
-
| "1h") ?? "1h";
|
|
966
|
+
"5m" | "1h") ?? "1h";
|
|
973
967
|
// Opt-out for callers (e.g. the memory router) that send a single
|
|
974
968
|
// user message per call with content that changes every time. The
|
|
975
969
|
// turn-start cache breakpoint below is only useful when the same
|
|
@@ -2148,9 +2142,10 @@ export class AnthropicProvider implements Provider {
|
|
|
2148
2142
|
const cacheControl = (
|
|
2149
2143
|
block as { cache_control?: Anthropic.CacheControlEphemeral }
|
|
2150
2144
|
).cache_control;
|
|
2145
|
+
const text = clampProviderString(block.text);
|
|
2151
2146
|
return cacheControl
|
|
2152
|
-
? { type: "text", text
|
|
2153
|
-
: { type: "text", text
|
|
2147
|
+
? { type: "text", text, cache_control: cacheControl }
|
|
2148
|
+
: { type: "text", text };
|
|
2154
2149
|
}
|
|
2155
2150
|
case "thinking":
|
|
2156
2151
|
if (!block.signature) {
|
|
@@ -2183,6 +2178,9 @@ export class AnthropicProvider implements Provider {
|
|
|
2183
2178
|
},
|
|
2184
2179
|
};
|
|
2185
2180
|
case "file": {
|
|
2181
|
+
if (keepFileAsWorkspaceRef(block.source)) {
|
|
2182
|
+
return { type: "text", text: fileBlockToProviderText(block) };
|
|
2183
|
+
}
|
|
2186
2184
|
const { media_type, data, filename } = base64Source(block.source);
|
|
2187
2185
|
if (media_type === "application/pdf") {
|
|
2188
2186
|
// Only valid base64 document source for Anthropic
|
|
@@ -2192,9 +2190,11 @@ export class AnthropicProvider implements Provider {
|
|
|
2192
2190
|
...(filename ? { title: filename } : {}),
|
|
2193
2191
|
} as unknown as Anthropic.ContentBlockParam;
|
|
2194
2192
|
}
|
|
2195
|
-
if (
|
|
2193
|
+
if (isDecodableTextMimeType(media_type)) {
|
|
2196
2194
|
// Decode base64 to UTF-8 text and send as PlainTextSource
|
|
2197
|
-
const decodedText =
|
|
2195
|
+
const decodedText = clampProviderString(
|
|
2196
|
+
Buffer.from(data, "base64").toString("utf-8"),
|
|
2197
|
+
);
|
|
2198
2198
|
return {
|
|
2199
2199
|
type: "document",
|
|
2200
2200
|
source: {
|
|
@@ -2205,14 +2205,11 @@ export class AnthropicProvider implements Provider {
|
|
|
2205
2205
|
...(filename ? { title: filename } : {}),
|
|
2206
2206
|
} as unknown as Anthropic.ContentBlockParam;
|
|
2207
2207
|
}
|
|
2208
|
-
// Binary non-text file:
|
|
2208
|
+
// Binary non-text file: name the workspace file, never dump bytes.
|
|
2209
2209
|
log.warn(
|
|
2210
2210
|
`Binary file type not natively supported by Anthropic: ${media_type}; falling back to text`,
|
|
2211
2211
|
);
|
|
2212
|
-
|
|
2213
|
-
? block.extracted_text
|
|
2214
|
-
: `[File: ${filename ?? "unknown"} (${media_type}) — binary file]`;
|
|
2215
|
-
return { type: "text", text: fallbackText };
|
|
2212
|
+
return { type: "text", text: fileBlockToProviderText(block) };
|
|
2216
2213
|
}
|
|
2217
2214
|
case "tool_use":
|
|
2218
2215
|
return {
|
|
@@ -358,17 +358,18 @@ export class CallSiteRoutingProvider implements Provider {
|
|
|
358
358
|
}
|
|
359
359
|
// Soft credential failure: the routed connection yielded no usable
|
|
360
360
|
// adapter and dispatch is landing on the default transport, which may
|
|
361
|
-
// be the platform-billed route
|
|
362
|
-
// observable.
|
|
361
|
+
// be the platform-billed route. Keep every such degradation
|
|
362
|
+
// observable. The reason is "no adapter", not a proven credential miss:
|
|
363
|
+
// adapter construction can also fail for dispatch bugs.
|
|
363
364
|
log.warn(
|
|
364
365
|
{
|
|
365
366
|
callSite,
|
|
366
367
|
connectionName,
|
|
367
368
|
provider: resolved.provider,
|
|
368
369
|
model: resolved.model,
|
|
369
|
-
reason: "
|
|
370
|
+
reason: "adapter_unavailable",
|
|
370
371
|
},
|
|
371
|
-
"Routed connection yielded no adapter
|
|
372
|
+
"Routed connection yielded no adapter: falling back to the default transport",
|
|
372
373
|
);
|
|
373
374
|
return this.defaultRoute(profileName);
|
|
374
375
|
}
|
|
@@ -387,9 +387,23 @@ export async function tryResolveProviderForConnectionName(
|
|
|
387
387
|
});
|
|
388
388
|
return attachProviderRoute(provider, connection);
|
|
389
389
|
} catch (err) {
|
|
390
|
+
if (err instanceof ConnectionResolutionError) {
|
|
391
|
+
throw err;
|
|
392
|
+
}
|
|
393
|
+
if (
|
|
394
|
+
err instanceof Error &&
|
|
395
|
+
/unresolved routing identity/.test(err.message)
|
|
396
|
+
) {
|
|
397
|
+
throw new ConnectionResolutionError(
|
|
398
|
+
connectionName,
|
|
399
|
+
"adapter_unavailable",
|
|
400
|
+
err.message,
|
|
401
|
+
{ cause: err, model },
|
|
402
|
+
);
|
|
403
|
+
}
|
|
390
404
|
log.warn(
|
|
391
405
|
{ err, connectionName },
|
|
392
|
-
"provider_connection auth resolution failed transiently
|
|
406
|
+
"provider_connection auth resolution failed transiently: returning null",
|
|
393
407
|
);
|
|
394
408
|
return null;
|
|
395
409
|
}
|
|
@@ -457,6 +471,20 @@ async function resolveThroughPlatform(
|
|
|
457
471
|
{ model, providerOverride: upstream },
|
|
458
472
|
);
|
|
459
473
|
} catch (err) {
|
|
474
|
+
if (err instanceof ConnectionResolutionError) {
|
|
475
|
+
throw err;
|
|
476
|
+
}
|
|
477
|
+
if (
|
|
478
|
+
err instanceof Error &&
|
|
479
|
+
/unresolved routing identity/.test(err.message)
|
|
480
|
+
) {
|
|
481
|
+
throw new ConnectionResolutionError(
|
|
482
|
+
VELLUM_MANAGED_CONNECTION_NAME,
|
|
483
|
+
"adapter_unavailable",
|
|
484
|
+
err.message,
|
|
485
|
+
{ cause: err, model },
|
|
486
|
+
);
|
|
487
|
+
}
|
|
460
488
|
log.warn({ err }, "Platform fallback auth resolution failed transiently");
|
|
461
489
|
return null;
|
|
462
490
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
clampProviderString,
|
|
5
|
+
exceedsProviderStringCap,
|
|
6
|
+
extractedTextForFileBlock,
|
|
7
|
+
isDecodableTextMimeType,
|
|
8
|
+
isVideoMimeType,
|
|
9
|
+
keepFileAsWorkspaceRef,
|
|
10
|
+
MAX_PROVIDER_STRING_BYTES,
|
|
11
|
+
PROVIDER_STRING_OMITTED_NOTE,
|
|
12
|
+
utf8ByteLength,
|
|
13
|
+
} from "./content-block-size.js";
|
|
14
|
+
|
|
15
|
+
describe("content-block-size", () => {
|
|
16
|
+
test("counts UTF-8 bytes, not JS string length", () => {
|
|
17
|
+
expect(utf8ByteLength("é")).toBe(2);
|
|
18
|
+
expect(exceedsProviderStringCap("é".repeat(4), 7)).toBe(true);
|
|
19
|
+
expect(exceedsProviderStringCap("é".repeat(3), 7)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("default cap is under OpenAI's 10 MiB per-part limit", () => {
|
|
23
|
+
expect(MAX_PROVIDER_STRING_BYTES).toBe(8_000_000);
|
|
24
|
+
expect(MAX_PROVIDER_STRING_BYTES).toBeLessThan(10_485_760);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("treats video MIME types, including parameters, as video", () => {
|
|
28
|
+
expect(isVideoMimeType("video/mp4")).toBe(true);
|
|
29
|
+
expect(isVideoMimeType("VIDEO/WEBM; codecs=vp9")).toBe(true);
|
|
30
|
+
expect(isVideoMimeType("application/pdf")).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("drops extracted_text for video and for over-cap extracts", () => {
|
|
34
|
+
expect(
|
|
35
|
+
extractedTextForFileBlock("video/mp4", "anything at all"),
|
|
36
|
+
).toBeUndefined();
|
|
37
|
+
expect(extractedTextForFileBlock("application/pdf", "hello")).toBe("hello");
|
|
38
|
+
expect(
|
|
39
|
+
extractedTextForFileBlock("text/plain", "abcdef", 4),
|
|
40
|
+
).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("clampProviderString replaces an over-cap string", () => {
|
|
44
|
+
expect(clampProviderString("ok")).toBe("ok");
|
|
45
|
+
expect(clampProviderString("abcdef", 4)).toBe(PROVIDER_STRING_OMITTED_NOTE);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("keepFileAsWorkspaceRef covers video and over-cap text refs", () => {
|
|
49
|
+
expect(isDecodableTextMimeType("text/plain")).toBe(true);
|
|
50
|
+
expect(isDecodableTextMimeType("application/json")).toBe(true);
|
|
51
|
+
expect(isDecodableTextMimeType("application/pdf")).toBe(false);
|
|
52
|
+
|
|
53
|
+
expect(
|
|
54
|
+
keepFileAsWorkspaceRef({
|
|
55
|
+
type: "workspace_ref",
|
|
56
|
+
media_type: "video/mp4",
|
|
57
|
+
sizeBytes: 12,
|
|
58
|
+
}),
|
|
59
|
+
).toBe(true);
|
|
60
|
+
expect(
|
|
61
|
+
keepFileAsWorkspaceRef({
|
|
62
|
+
type: "workspace_ref",
|
|
63
|
+
media_type: "text/plain",
|
|
64
|
+
sizeBytes: MAX_PROVIDER_STRING_BYTES + 1,
|
|
65
|
+
}),
|
|
66
|
+
).toBe(true);
|
|
67
|
+
expect(
|
|
68
|
+
keepFileAsWorkspaceRef({
|
|
69
|
+
type: "workspace_ref",
|
|
70
|
+
media_type: "text/plain",
|
|
71
|
+
sizeBytes: 20,
|
|
72
|
+
}),
|
|
73
|
+
).toBe(false);
|
|
74
|
+
expect(
|
|
75
|
+
keepFileAsWorkspaceRef({
|
|
76
|
+
type: "workspace_ref",
|
|
77
|
+
media_type: "application/pdf",
|
|
78
|
+
sizeBytes: MAX_PROVIDER_STRING_BYTES + 1,
|
|
79
|
+
}),
|
|
80
|
+
).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider per-string size cap and helpers for content that would be
|
|
3
|
+
* serialized as a single text part.
|
|
4
|
+
*
|
|
5
|
+
* OpenAI rejects a request when any `content[].text` / `input[].content[].text`
|
|
6
|
+
* exceeds 10_485_760 bytes (`string_above_max_length`). Stay under that so a
|
|
7
|
+
* single block cannot brick every later turn. Extra SQLite-only columns and
|
|
8
|
+
* attachment bytes are out of scope: this is the wire-string budget.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Bytes. Under OpenAI's 10 MiB per-part string limit (10_485_760). */
|
|
12
|
+
export const MAX_PROVIDER_STRING_BYTES = 8_000_000;
|
|
13
|
+
|
|
14
|
+
export function utf8ByteLength(text: string): number {
|
|
15
|
+
return Buffer.byteLength(text, "utf8");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function exceedsProviderStringCap(
|
|
19
|
+
text: string,
|
|
20
|
+
maxBytes: number = MAX_PROVIDER_STRING_BYTES,
|
|
21
|
+
): boolean {
|
|
22
|
+
return utf8ByteLength(text) > maxBytes;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isVideoMimeType(mimeType: string): boolean {
|
|
26
|
+
const normalised = mimeType.toLowerCase().trim().split(";")[0]?.trim() ?? "";
|
|
27
|
+
return normalised.startsWith("video/");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* MIME types a serializer would decode into a provider text/document string.
|
|
32
|
+
* Binary types (PDF, images, audio) stay out of this set: they are not the
|
|
33
|
+
* OpenAI `string_above_max_length` failure mode.
|
|
34
|
+
*/
|
|
35
|
+
export function isDecodableTextMimeType(mimeType: string): boolean {
|
|
36
|
+
const normalised = mimeType.toLowerCase().trim().split(";")[0]?.trim() ?? "";
|
|
37
|
+
return (
|
|
38
|
+
normalised.startsWith("text/") ||
|
|
39
|
+
normalised === "application/json" ||
|
|
40
|
+
normalised === "application/xml" ||
|
|
41
|
+
normalised === "application/javascript"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Video and over-cap text files stay on disk. Serializers name the workspace
|
|
47
|
+
* file; they must not load bytes or emit a decoded dump.
|
|
48
|
+
*/
|
|
49
|
+
export function keepFileAsWorkspaceRef(source: {
|
|
50
|
+
type: string;
|
|
51
|
+
media_type: string;
|
|
52
|
+
sizeBytes?: number;
|
|
53
|
+
}): boolean {
|
|
54
|
+
if (isVideoMimeType(source.media_type)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return (
|
|
58
|
+
source.type === "workspace_ref" &&
|
|
59
|
+
source.sizeBytes !== undefined &&
|
|
60
|
+
source.sizeBytes > MAX_PROVIDER_STRING_BYTES &&
|
|
61
|
+
isDecodableTextMimeType(source.media_type)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Drop `extracted_text` that must not ride a file block into the provider
|
|
67
|
+
* prompt: video is a workspace file, not a transcript dump, and any extract
|
|
68
|
+
* over the cap belongs in the attachment store rather than the request.
|
|
69
|
+
*/
|
|
70
|
+
export function extractedTextForFileBlock(
|
|
71
|
+
mimeType: string,
|
|
72
|
+
extractedText: string | undefined,
|
|
73
|
+
maxBytes: number = MAX_PROVIDER_STRING_BYTES,
|
|
74
|
+
): string | undefined {
|
|
75
|
+
if (extractedText === undefined || extractedText.length === 0) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
if (isVideoMimeType(mimeType)) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
if (exceedsProviderStringCap(extractedText, maxBytes)) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
return extractedText;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Fallback copy when a serializer still sees a string over the cap. */
|
|
88
|
+
export const PROVIDER_STRING_OMITTED_NOTE =
|
|
89
|
+
"Content exceeded the provider size limit and was omitted from this prompt.";
|
|
90
|
+
|
|
91
|
+
export function clampProviderString(
|
|
92
|
+
text: string,
|
|
93
|
+
maxBytes: number = MAX_PROVIDER_STRING_BYTES,
|
|
94
|
+
): string {
|
|
95
|
+
if (!exceedsProviderStringCap(text, maxBytes)) {
|
|
96
|
+
return text;
|
|
97
|
+
}
|
|
98
|
+
return PROVIDER_STRING_OMITTED_NOTE;
|
|
99
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { PROVIDER_STRING_OMITTED_NOTE } from "./content-block-size.js";
|
|
4
|
+
import { fileBlockToProviderText } from "./file-block-text.js";
|
|
5
|
+
import type { ContentBlock } from "./types.js";
|
|
6
|
+
|
|
7
|
+
function fileBlock(
|
|
8
|
+
mediaType: string,
|
|
9
|
+
extractedText?: string,
|
|
10
|
+
): Extract<ContentBlock, { type: "file" }> {
|
|
11
|
+
return {
|
|
12
|
+
type: "file",
|
|
13
|
+
source: {
|
|
14
|
+
type: "workspace_ref",
|
|
15
|
+
media_type: mediaType,
|
|
16
|
+
attachmentId: "att-1",
|
|
17
|
+
sizeBytes: 12,
|
|
18
|
+
filename: "clip.mp4",
|
|
19
|
+
},
|
|
20
|
+
...(extractedText !== undefined ? { extracted_text: extractedText } : {}),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe("fileBlockToProviderText", () => {
|
|
25
|
+
test("names a video without dumping extracted_text", () => {
|
|
26
|
+
const text = fileBlockToProviderText(
|
|
27
|
+
fileBlock("video/mp4", "a".repeat(1000)),
|
|
28
|
+
);
|
|
29
|
+
expect(text).toContain("video/mp4");
|
|
30
|
+
expect(text).toContain("workspace file");
|
|
31
|
+
expect(text).not.toContain("aaa");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("includes extracted_text for a non-video file under the cap", () => {
|
|
35
|
+
const text = fileBlockToProviderText({
|
|
36
|
+
type: "file",
|
|
37
|
+
source: {
|
|
38
|
+
type: "workspace_ref",
|
|
39
|
+
media_type: "application/pdf",
|
|
40
|
+
attachmentId: "att-2",
|
|
41
|
+
sizeBytes: 20,
|
|
42
|
+
filename: "notes.pdf",
|
|
43
|
+
},
|
|
44
|
+
extracted_text: "quarterly summary",
|
|
45
|
+
});
|
|
46
|
+
expect(text).toContain("quarterly summary");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("omits an over-cap extracted_text dump", () => {
|
|
50
|
+
const text = fileBlockToProviderText({
|
|
51
|
+
type: "file",
|
|
52
|
+
source: {
|
|
53
|
+
type: "workspace_ref",
|
|
54
|
+
media_type: "text/plain",
|
|
55
|
+
attachmentId: "att-3",
|
|
56
|
+
sizeBytes: 20,
|
|
57
|
+
filename: "huge.txt",
|
|
58
|
+
},
|
|
59
|
+
extracted_text: "x".repeat(8_000_001),
|
|
60
|
+
});
|
|
61
|
+
expect(text).toBe(PROVIDER_STRING_OMITTED_NOTE);
|
|
62
|
+
expect(text.length).toBeLessThan(200);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared file-block → provider text. OpenAI, Anthropic, and Gemini all fold
|
|
3
|
+
* non-native files into a text part. Video is never inlined: it stays a
|
|
4
|
+
* workspace file and the prompt only names it.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { escapeXmlAttr } from "../util/xml.js";
|
|
8
|
+
import { clampProviderString, isVideoMimeType } from "./content-block-size.js";
|
|
9
|
+
import type { ContentBlock } from "./types.js";
|
|
10
|
+
|
|
11
|
+
const VIDEO_FILE_NOTE =
|
|
12
|
+
"Video is stored as a workspace file. It is not inlined in this prompt.";
|
|
13
|
+
|
|
14
|
+
export function fileBlockToProviderText(
|
|
15
|
+
block: Extract<ContentBlock, { type: "file" }>,
|
|
16
|
+
): string {
|
|
17
|
+
const header = `<attached_file name="${escapeXmlAttr(
|
|
18
|
+
block.source.filename ?? "",
|
|
19
|
+
)}" type="${escapeXmlAttr(block.source.media_type)}" />`;
|
|
20
|
+
if (isVideoMimeType(block.source.media_type)) {
|
|
21
|
+
return `${header}\n${VIDEO_FILE_NOTE}`;
|
|
22
|
+
}
|
|
23
|
+
const extracted = block.extracted_text?.trim() ?? "";
|
|
24
|
+
if (extracted.length > 0) {
|
|
25
|
+
return clampProviderString(`${header}\n${extracted}`);
|
|
26
|
+
}
|
|
27
|
+
return `${header}\nNo extracted text available.`;
|
|
28
|
+
}
|
|
@@ -10,6 +10,11 @@ import { isAbortReason } from "../../util/abort-reasons.js";
|
|
|
10
10
|
import { ProviderError, type ProviderErrorReason } from "../../util/errors.js";
|
|
11
11
|
import { getLogger } from "../../util/logger.js";
|
|
12
12
|
import { DAILY_LIMIT_PATTERNS } from "../../util/provider-error-patterns.js";
|
|
13
|
+
import {
|
|
14
|
+
clampProviderString,
|
|
15
|
+
keepFileAsWorkspaceRef,
|
|
16
|
+
} from "../content-block-size.js";
|
|
17
|
+
import { fileBlockToProviderText } from "../file-block-text.js";
|
|
13
18
|
import { base64Source, resolveMediaReferences } from "../media-resolve.js";
|
|
14
19
|
import { PROVIDER_CATALOG } from "../model-catalog.js";
|
|
15
20
|
import { recordProviderRequestDiagnostics } from "../request-diagnostics.js";
|
|
@@ -429,8 +434,7 @@ export class GeminiProvider implements Provider {
|
|
|
429
434
|
const maxTokens = configObj?.max_tokens as number | undefined;
|
|
430
435
|
const modelOverride = configObj?.model as string | undefined;
|
|
431
436
|
const usageAttributionHeaders = configObj?.usageAttributionHeaders as
|
|
432
|
-
|
|
433
|
-
| undefined;
|
|
437
|
+
Record<string, string> | undefined;
|
|
434
438
|
const activeModel = modelOverride ?? this.model;
|
|
435
439
|
const thinkingConfig = geminiModelSupportsThinking(activeModel)
|
|
436
440
|
? buildThinkingConfig(
|
|
@@ -762,7 +766,7 @@ export class GeminiProvider implements Provider {
|
|
|
762
766
|
for (const block of blocks) {
|
|
763
767
|
switch (block.type) {
|
|
764
768
|
case "text":
|
|
765
|
-
parts.push({ text: block.text });
|
|
769
|
+
parts.push({ text: clampProviderString(block.text) });
|
|
766
770
|
break;
|
|
767
771
|
case "image": {
|
|
768
772
|
const imageSrc = base64Source(block.source);
|
|
@@ -775,6 +779,10 @@ export class GeminiProvider implements Provider {
|
|
|
775
779
|
break;
|
|
776
780
|
}
|
|
777
781
|
case "file": {
|
|
782
|
+
if (keepFileAsWorkspaceRef(block.source)) {
|
|
783
|
+
parts.push({ text: fileBlockToProviderText(block) });
|
|
784
|
+
break;
|
|
785
|
+
}
|
|
778
786
|
const fileSrc = base64Source(block.source);
|
|
779
787
|
if (this.supportsGeminiInlineFile(fileSrc.media_type)) {
|
|
780
788
|
// Normalize audio MIME onto Gemini's spelling (e.g. audio/mpeg →
|
|
@@ -797,10 +805,7 @@ export class GeminiProvider implements Provider {
|
|
|
797
805
|
});
|
|
798
806
|
}
|
|
799
807
|
} else {
|
|
800
|
-
|
|
801
|
-
? `[Attached file: ${fileSrc.filename} (${fileSrc.media_type})]\n${block.extracted_text}`
|
|
802
|
-
: `[Attached file: ${fileSrc.filename} (${fileSrc.media_type})]\nNo extracted text available.`;
|
|
803
|
-
parts.push({ text: fallback });
|
|
808
|
+
parts.push({ text: fileBlockToProviderText(block) });
|
|
804
809
|
}
|
|
805
810
|
break;
|
|
806
811
|
}
|
|
@@ -144,12 +144,12 @@ export type ConnectionProvider = string;
|
|
|
144
144
|
export const CHATGPT_SUBSCRIPTION_CONNECTION_NAME = "chatgpt-subscription";
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
* Provider values that
|
|
148
|
-
*
|
|
149
|
-
* chatgpt
|
|
150
|
-
*
|
|
151
|
-
* profiles carry no provider_connection;
|
|
152
|
-
* not stamp one.
|
|
147
|
+
* Provider values that name a routing identity. Dispatch translates them
|
|
148
|
+
* to a connection row and an upstream factory id via
|
|
149
|
+
* `resolveRoutingIdentity`. `chatgpt` has no adapter factory (upstream is
|
|
150
|
+
* always openai). `vellum` is also a catalog factory id when the derived
|
|
151
|
+
* upstream is vellum. Identity profiles carry no provider_connection;
|
|
152
|
+
* backfill and materialization must not stamp one.
|
|
153
153
|
*/
|
|
154
154
|
export const ROUTING_IDENTITY_PROVIDERS: ReadonlySet<string> = new Set([
|
|
155
155
|
"vellum",
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
sniffImageMimeType,
|
|
29
29
|
} from "../util/image-conversion.js";
|
|
30
30
|
import { getLogger } from "../util/logger.js";
|
|
31
|
+
import { keepFileAsWorkspaceRef } from "./content-block-size.js";
|
|
31
32
|
import {
|
|
32
33
|
attachmentIdFragment,
|
|
33
34
|
type Base64MediaSource,
|
|
@@ -265,6 +266,16 @@ async function resolveImageBlock(
|
|
|
265
266
|
}
|
|
266
267
|
|
|
267
268
|
function resolveFileBlock(block: FileContent): ContentBlock {
|
|
269
|
+
// Video and over-cap text stay a workspace file. Do not load bytes or
|
|
270
|
+
// carry extracted_text into the provider prompt: serializers name the
|
|
271
|
+
// file and stop there.
|
|
272
|
+
if (keepFileAsWorkspaceRef(block.source)) {
|
|
273
|
+
if (block.extracted_text === undefined) {
|
|
274
|
+
return block;
|
|
275
|
+
}
|
|
276
|
+
const { extracted_text: _extracted, ...rest } = block;
|
|
277
|
+
return rest;
|
|
278
|
+
}
|
|
268
279
|
if (block.source.type === "base64") {
|
|
269
280
|
return block;
|
|
270
281
|
}
|
|
@@ -356,6 +367,9 @@ function contentNeedsResolution(
|
|
|
356
367
|
: base64ImageNeedsRewrite(block.source, options);
|
|
357
368
|
}
|
|
358
369
|
if (block.type === "file") {
|
|
370
|
+
if (keepFileAsWorkspaceRef(block.source)) {
|
|
371
|
+
return block.extracted_text !== undefined;
|
|
372
|
+
}
|
|
359
373
|
return block.source.type === "workspace_ref";
|
|
360
374
|
}
|
|
361
375
|
if (block.type === "tool_result" && block.contentBlocks?.length) {
|
|
@@ -928,23 +928,6 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
928
928
|
cacheReadPer1mTokens: 0.16,
|
|
929
929
|
},
|
|
930
930
|
},
|
|
931
|
-
{
|
|
932
|
-
id: "accounts/fireworks/models/glm-5p2",
|
|
933
|
-
displayName: "GLM 5.2",
|
|
934
|
-
// Fireworks serves GLM 5.2 with a 1,040K input window.
|
|
935
|
-
contextWindowTokens: 1040000,
|
|
936
|
-
maxOutputTokens: 131072,
|
|
937
|
-
supportsThinking: true,
|
|
938
|
-
supportsCaching: true,
|
|
939
|
-
supportsVision: false,
|
|
940
|
-
supportsToolUse: true,
|
|
941
|
-
maxEffort: "max",
|
|
942
|
-
pricing: {
|
|
943
|
-
inputPer1mTokens: 1.4,
|
|
944
|
-
outputPer1mTokens: 4.4,
|
|
945
|
-
cacheReadPer1mTokens: 0.26,
|
|
946
|
-
},
|
|
947
|
-
},
|
|
948
931
|
{
|
|
949
932
|
id: "accounts/fireworks/models/glm-5p3",
|
|
950
933
|
displayName: "GLM 5.3",
|
|
@@ -984,6 +967,23 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
984
967
|
cacheReadPer1mTokens: 0.029,
|
|
985
968
|
},
|
|
986
969
|
},
|
|
970
|
+
{
|
|
971
|
+
id: "accounts/fireworks/models/glm-5p2",
|
|
972
|
+
displayName: "GLM 5.2",
|
|
973
|
+
// Fireworks serves GLM 5.2 with a 1,040K input window.
|
|
974
|
+
contextWindowTokens: 1040000,
|
|
975
|
+
maxOutputTokens: 131072,
|
|
976
|
+
supportsThinking: true,
|
|
977
|
+
supportsCaching: true,
|
|
978
|
+
supportsVision: false,
|
|
979
|
+
supportsToolUse: true,
|
|
980
|
+
maxEffort: "max",
|
|
981
|
+
pricing: {
|
|
982
|
+
inputPer1mTokens: 1.4,
|
|
983
|
+
outputPer1mTokens: 4.4,
|
|
984
|
+
cacheReadPer1mTokens: 0.26,
|
|
985
|
+
},
|
|
986
|
+
},
|
|
987
987
|
// Kimi K2.5 (accounts/fireworks/models/kimi-k2p5) is intentionally
|
|
988
988
|
// absent: Fireworks serves it on-demand/dedicated only, so serverless
|
|
989
989
|
// chat/completions calls 404 ("not found, inaccessible, and/or not
|
|
@@ -1006,28 +1006,25 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1006
1006
|
cacheReadPer1mTokens: 0.06,
|
|
1007
1007
|
},
|
|
1008
1008
|
},
|
|
1009
|
+
// MiniMax M2.7 (accounts/fireworks/models/minimax-m2p7) is
|
|
1010
|
+
// intentionally absent: Fireworks has no serverless deployment for
|
|
1011
|
+
// it (the model page claims serverless support, but the serving API
|
|
1012
|
+
// returns 404).
|
|
1009
1013
|
{
|
|
1010
|
-
id: "accounts/fireworks/models/
|
|
1011
|
-
displayName: "MiniMax M2.7",
|
|
1012
|
-
contextWindowTokens: 196608,
|
|
1013
|
-
maxOutputTokens: 25000,
|
|
1014
|
-
supportsThinking: false,
|
|
1015
|
-
supportsCaching: false,
|
|
1016
|
-
supportsVision: false,
|
|
1017
|
-
supportsToolUse: true,
|
|
1018
|
-
pricing: { inputPer1mTokens: 0.3, outputPer1mTokens: 1.2 },
|
|
1019
|
-
},
|
|
1020
|
-
{
|
|
1021
|
-
id: "accounts/fireworks/models/deepseek-v4-pro",
|
|
1014
|
+
id: "accounts/fireworks/models/deepseek-v4-pro-0813",
|
|
1022
1015
|
displayName: "DeepSeek V4 Pro",
|
|
1023
1016
|
contextWindowTokens: 1040000,
|
|
1024
1017
|
maxOutputTokens: 131072,
|
|
1025
1018
|
supportsThinking: true,
|
|
1026
|
-
supportsCaching:
|
|
1019
|
+
supportsCaching: true,
|
|
1027
1020
|
supportsVision: false,
|
|
1028
1021
|
supportsToolUse: true,
|
|
1029
1022
|
maxEffort: "max",
|
|
1030
|
-
pricing: {
|
|
1023
|
+
pricing: {
|
|
1024
|
+
inputPer1mTokens: 1.32,
|
|
1025
|
+
outputPer1mTokens: 3.96,
|
|
1026
|
+
cacheReadPer1mTokens: 0.044,
|
|
1027
|
+
},
|
|
1031
1028
|
},
|
|
1032
1029
|
{
|
|
1033
1030
|
id: "accounts/fireworks/models/deepseek-v4-flash-0731",
|
|
@@ -1834,7 +1831,7 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1834
1831
|
// Z.ai
|
|
1835
1832
|
{
|
|
1836
1833
|
id: "z-ai/glm-5.3",
|
|
1837
|
-
displayName: "GLM
|
|
1834
|
+
displayName: "GLM 5.3",
|
|
1838
1835
|
contextWindowTokens: 1048576,
|
|
1839
1836
|
maxOutputTokens: 131072,
|
|
1840
1837
|
supportsThinking: true,
|
|
@@ -1849,7 +1846,7 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1849
1846
|
},
|
|
1850
1847
|
{
|
|
1851
1848
|
id: "z-ai/glm-5.3-flash",
|
|
1852
|
-
displayName: "GLM
|
|
1849
|
+
displayName: "GLM 5.3 Flash",
|
|
1853
1850
|
contextWindowTokens: 1310720,
|
|
1854
1851
|
maxOutputTokens: 131072,
|
|
1855
1852
|
supportsThinking: true,
|
|
@@ -1864,7 +1861,7 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1864
1861
|
},
|
|
1865
1862
|
{
|
|
1866
1863
|
id: "z-ai/glm-5.2",
|
|
1867
|
-
displayName: "GLM
|
|
1864
|
+
displayName: "GLM 5.2",
|
|
1868
1865
|
contextWindowTokens: 1048576,
|
|
1869
1866
|
maxOutputTokens: 131072,
|
|
1870
1867
|
supportsThinking: true,
|