@vellumai/assistant 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -194,6 +194,19 @@ export const MemoryV2ConfigSchema = z
|
|
|
194
194
|
.describe(
|
|
195
195
|
"Hours between scheduled consolidation runs that synthesize buffered memories into concept pages",
|
|
196
196
|
),
|
|
197
|
+
consolidation_max_buffer_lines: z
|
|
198
|
+
.number({
|
|
199
|
+
error: "memory.v2.consolidation_max_buffer_lines must be a number",
|
|
200
|
+
})
|
|
201
|
+
.int("memory.v2.consolidation_max_buffer_lines must be an integer")
|
|
202
|
+
.positive(
|
|
203
|
+
"memory.v2.consolidation_max_buffer_lines must be a positive integer",
|
|
204
|
+
)
|
|
205
|
+
.nullable()
|
|
206
|
+
.default(100)
|
|
207
|
+
.describe(
|
|
208
|
+
"Size-based trigger for consolidation. When `memory/buffer.md` reaches this many non-empty lines, consolidation runs even if the time-based interval hasn't elapsed. Defaults to 100. Set to `null` to disable the size trigger and rely solely on `consolidation_interval_hours`.",
|
|
209
|
+
),
|
|
197
210
|
max_page_chars: z
|
|
198
211
|
.number({ error: "memory.v2.max_page_chars must be a number" })
|
|
199
212
|
.int("memory.v2.max_page_chars must be an integer")
|
|
@@ -260,9 +273,9 @@ export const MemoryV2ConfigSchema = z
|
|
|
260
273
|
.object({
|
|
261
274
|
enabled: z
|
|
262
275
|
.boolean()
|
|
263
|
-
.default(
|
|
276
|
+
.default(true)
|
|
264
277
|
.describe(
|
|
265
|
-
"Whether to use the LLM router as the per-turn page-selection mechanism in place of spreading activation.
|
|
278
|
+
"Whether to use the LLM router as the per-turn page-selection mechanism in place of spreading activation. Enabled by default.",
|
|
266
279
|
),
|
|
267
280
|
max_page_ids: z
|
|
268
281
|
.number()
|
|
@@ -282,10 +295,44 @@ export const MemoryV2ConfigSchema = z
|
|
|
282
295
|
.describe(
|
|
283
296
|
"Optional path to a file whose contents replace the bundled router prompt. Absolute paths are used as-is, a leading `~/` is expanded to the home directory, otherwise the path is resolved under the workspace root. The loaded contents may include `{{ASSISTANT_NAME}}`, `{{USER_NAME}}`, and `{{PAGE_INDEX}}`, which are substituted at runtime. If the file is missing, unreadable, or empty, the bundled prompt is used and a warning is logged.",
|
|
284
297
|
),
|
|
298
|
+
batch_size: z
|
|
299
|
+
.number()
|
|
300
|
+
.int()
|
|
301
|
+
.min(1)
|
|
302
|
+
.nullable()
|
|
303
|
+
.default(null)
|
|
304
|
+
.describe(
|
|
305
|
+
"Target batch size for parallel page-index routing. `null` (default) sends the entire page index in one call — identical to v3 behavior. When set, pages are split into `ceil(N / batch_size)` batches by stable FNV-1a hash on slug (so adding/removing a single page only invalidates one batch's KV cache), routed in parallel, and the selected slugs are unioned. A failure in one batch does not abort the turn as long as at least one batch succeeds.",
|
|
306
|
+
),
|
|
307
|
+
tier1_size: z
|
|
308
|
+
.number()
|
|
309
|
+
.int()
|
|
310
|
+
.min(1)
|
|
311
|
+
.nullable()
|
|
312
|
+
.default(null)
|
|
313
|
+
.describe(
|
|
314
|
+
"Pool size for the tier-1 'recently modified' batch. `null` (default) disables tier 1 entirely — all pages flow through tier 3 batching. When set, the top-N concept pages by file mtime become their own dedicated parallel batch with mtime-desc ordering; everything else is partitioned into tier 3 batches by `batch_size`. Synthetic entries (skills, CLI commands) have mtime=0 and naturally rank below real concept pages so they don't crowd tier 1.",
|
|
315
|
+
),
|
|
316
|
+
tier2_size: z
|
|
317
|
+
.number()
|
|
318
|
+
.int()
|
|
319
|
+
.min(1)
|
|
320
|
+
.nullable()
|
|
321
|
+
.default(null)
|
|
322
|
+
.describe(
|
|
323
|
+
"Pool size for the tier-2 'useful' batch. `null` (default) disables tier 2 — pages skip straight from tier 1 to tier 3. When set, the top-M pages by injection-frequency EMA (excluding tier 1) become their own parallel batch ordered by score desc. Pages with score 0 (never selected since EMA tracking began) are ineligible for tier 2 and stay in tier 3 regardless of `tier2_size`. Score is the time-decayed sum `Σ exp(-λ(now - tᵢ))` with 3-day half-life, computed on read from `memory_v2_injection_events`.",
|
|
324
|
+
),
|
|
325
|
+
})
|
|
326
|
+
.default({
|
|
327
|
+
enabled: true,
|
|
328
|
+
max_page_ids: 25,
|
|
329
|
+
router_prompt_path: null,
|
|
330
|
+
batch_size: null,
|
|
331
|
+
tier1_size: null,
|
|
332
|
+
tier2_size: null,
|
|
285
333
|
})
|
|
286
|
-
.default({ enabled: false, max_page_ids: 25, router_prompt_path: null })
|
|
287
334
|
.describe(
|
|
288
|
-
"LLM router configuration. When enabled, a single
|
|
335
|
+
"LLM router configuration. When enabled, a single router LLM call replaces spreading activation for per-turn page selection.",
|
|
289
336
|
),
|
|
290
337
|
})
|
|
291
338
|
.describe(
|
|
@@ -23,7 +23,9 @@ export const MemoryConfigSchema = z
|
|
|
23
23
|
enabled: z
|
|
24
24
|
.boolean({ error: "memory.enabled must be a boolean" })
|
|
25
25
|
.default(true)
|
|
26
|
-
.describe(
|
|
26
|
+
.describe(
|
|
27
|
+
"Whether the long-term memory system is enabled — gates background memory jobs, embedding generation, and `<memory>` block injection into user messages",
|
|
28
|
+
),
|
|
27
29
|
embeddings: MemoryEmbeddingsConfigSchema.default(
|
|
28
30
|
MemoryEmbeddingsConfigSchema.parse({}),
|
|
29
31
|
),
|
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
createConnection,
|
|
4
4
|
disableManagedConnectionsForByokHatch,
|
|
5
5
|
getConnection,
|
|
6
|
+
MANAGED_CONNECTION_NAMES,
|
|
7
|
+
PROVIDERS_REQUIRING_BASE_URL_AND_MODELS,
|
|
6
8
|
} from "../providers/inference/connections.js";
|
|
7
9
|
import { PROVIDER_CATALOG } from "../providers/model-catalog.js";
|
|
8
10
|
import { resolveModelIntent } from "../providers/model-intents.js";
|
|
@@ -17,10 +19,6 @@ import {
|
|
|
17
19
|
|
|
18
20
|
const log = getLogger("seed-inference-profiles");
|
|
19
21
|
|
|
20
|
-
const MANAGED_CONNECTION_NAME = "anthropic-managed";
|
|
21
|
-
const MANAGED_PROFILE_PROVIDER: NonNullable<ProfileEntry["provider"]> =
|
|
22
|
-
"anthropic";
|
|
23
|
-
|
|
24
22
|
/**
|
|
25
23
|
* Template for a daemon-managed inference profile. The profile's model is
|
|
26
24
|
* resolved at seed time from `PROVIDER_MODEL_INTENTS` so the catalog stays the
|
|
@@ -31,16 +29,20 @@ type ManagedProfileTemplate = Omit<
|
|
|
31
29
|
"provider" | "model" | "provider_connection"
|
|
32
30
|
> & {
|
|
33
31
|
intent: ModelIntent;
|
|
32
|
+
provider: NonNullable<ProfileEntry["provider"]>;
|
|
33
|
+
connectionName: string;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Managed
|
|
38
|
-
*
|
|
37
|
+
* Managed profiles. Overwritten on every daemon boot so Vellum can push
|
|
38
|
+
* model/config updates to customers in new releases. Platform overlays
|
|
39
39
|
* (`preserveProfileNames`) take precedence when present.
|
|
40
40
|
*/
|
|
41
41
|
const MANAGED_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
|
|
42
42
|
balanced: {
|
|
43
43
|
intent: "balanced",
|
|
44
|
+
provider: "anthropic",
|
|
45
|
+
connectionName: "anthropic-managed",
|
|
44
46
|
source: "managed",
|
|
45
47
|
label: "Balanced",
|
|
46
48
|
description: "Good balance of quality, cost, and speed",
|
|
@@ -51,6 +53,8 @@ const MANAGED_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
|
|
|
51
53
|
},
|
|
52
54
|
"quality-optimized": {
|
|
53
55
|
intent: "quality-optimized",
|
|
56
|
+
provider: "anthropic",
|
|
57
|
+
connectionName: "anthropic-managed",
|
|
54
58
|
source: "managed",
|
|
55
59
|
label: "Quality",
|
|
56
60
|
description: "Best results with the most capable model",
|
|
@@ -61,6 +65,8 @@ const MANAGED_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
|
|
|
61
65
|
},
|
|
62
66
|
"cost-optimized": {
|
|
63
67
|
intent: "latency-optimized",
|
|
68
|
+
provider: "anthropic",
|
|
69
|
+
connectionName: "anthropic-managed",
|
|
64
70
|
source: "managed",
|
|
65
71
|
label: "Speed",
|
|
66
72
|
description: "Fastest responses at lower cost",
|
|
@@ -74,11 +80,15 @@ const MANAGED_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
|
|
|
74
80
|
/**
|
|
75
81
|
* User profile templates. Materialized at hatch time for off-platform
|
|
76
82
|
* installations. Each points at the user's personal provider connection
|
|
77
|
-
* (backed by their API key in CES).
|
|
83
|
+
* (backed by their API key in CES). The `provider` and `connectionName`
|
|
84
|
+
* fields are placeholders — they are overridden at hatch time with the
|
|
85
|
+
* user's chosen provider and personal connection name.
|
|
78
86
|
*/
|
|
79
87
|
const USER_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
|
|
80
88
|
"custom-balanced": {
|
|
81
89
|
intent: "balanced",
|
|
90
|
+
provider: "anthropic",
|
|
91
|
+
connectionName: "",
|
|
82
92
|
source: "user",
|
|
83
93
|
label: "Balanced",
|
|
84
94
|
description: "Good balance of quality, cost, and speed",
|
|
@@ -89,6 +99,8 @@ const USER_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
|
|
|
89
99
|
},
|
|
90
100
|
"custom-quality-optimized": {
|
|
91
101
|
intent: "quality-optimized",
|
|
102
|
+
provider: "anthropic",
|
|
103
|
+
connectionName: "",
|
|
92
104
|
source: "user",
|
|
93
105
|
label: "Quality",
|
|
94
106
|
description: "Best results with the most capable model",
|
|
@@ -99,6 +111,8 @@ const USER_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
|
|
|
99
111
|
},
|
|
100
112
|
"custom-cost-optimized": {
|
|
101
113
|
intent: "latency-optimized",
|
|
114
|
+
provider: "anthropic",
|
|
115
|
+
connectionName: "",
|
|
102
116
|
source: "user",
|
|
103
117
|
label: "Speed",
|
|
104
118
|
description: "Fastest responses at lower cost",
|
|
@@ -164,11 +178,12 @@ export function seedInferenceProfiles(
|
|
|
164
178
|
// BYOK mode = off-platform installs. The user is bringing their own provider
|
|
165
179
|
// API key; managed profile labels get a " (Managed)" suffix to disambiguate
|
|
166
180
|
// from the personal "custom-*" profiles that share base labels. Managed
|
|
167
|
-
// profile + connection status is initially "disabled"
|
|
168
|
-
// offer an unusable platform-auth option on day one
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
181
|
+
// profile + connection status is initially "disabled" for true BYOK hatches
|
|
182
|
+
// so the picker doesn't offer an unusable platform-auth option on day one.
|
|
183
|
+
// When the hatch overlay explicitly selects a managed profile, the matching
|
|
184
|
+
// managed connection stays active so the first post-onboarding message can
|
|
185
|
+
// use the user's chosen managed route. Post-hatch user toggles survive every
|
|
186
|
+
// subsequent boot.
|
|
172
187
|
const isByokMode = !isPlatform;
|
|
173
188
|
|
|
174
189
|
// 1. Managed profiles. Off-platform: overwrite on every boot so Vellum can
|
|
@@ -198,10 +213,18 @@ export function seedInferenceProfiles(
|
|
|
198
213
|
// rewritten to the suffixed form. Any other previous label value
|
|
199
214
|
// (user-set custom string, explicit null, already-suffixed) is
|
|
200
215
|
// preserved as-is.
|
|
201
|
-
// • status: "disabled" on fresh materialization at hatch only —
|
|
202
|
-
// gated on (isHatch && !previous)
|
|
203
|
-
//
|
|
204
|
-
//
|
|
216
|
+
// • status: "disabled" on fresh materialization at BYOK hatch only —
|
|
217
|
+
// gated on (isHatch && !previous) and skipped for any managed
|
|
218
|
+
// connection explicitly selected by the hatch overlay. Post-hatch
|
|
219
|
+
// boots and existing installs are never auto-disabled. A user
|
|
220
|
+
// re-enable persists across boots via the key-presence preservation
|
|
221
|
+
// below.
|
|
222
|
+
const hatchSelectedManagedConnection = getHatchSelectedManagedConnection(
|
|
223
|
+
llm,
|
|
224
|
+
profiles,
|
|
225
|
+
options,
|
|
226
|
+
);
|
|
227
|
+
|
|
205
228
|
for (const [name, template] of Object.entries(MANAGED_PROFILE_TEMPLATES)) {
|
|
206
229
|
if (preservedProfileNames.has(name)) continue;
|
|
207
230
|
if (isPlatform && readObject(profiles[name]) !== null) continue;
|
|
@@ -212,10 +235,15 @@ export function seedInferenceProfiles(
|
|
|
212
235
|
: template;
|
|
213
236
|
const next = materializeProfile(
|
|
214
237
|
effectiveTemplate,
|
|
215
|
-
|
|
216
|
-
|
|
238
|
+
template.provider,
|
|
239
|
+
template.connectionName,
|
|
217
240
|
) as Record<string, unknown>;
|
|
218
|
-
if (
|
|
241
|
+
if (
|
|
242
|
+
isByokMode &&
|
|
243
|
+
options.isHatch &&
|
|
244
|
+
!previous &&
|
|
245
|
+
template.connectionName !== hatchSelectedManagedConnection
|
|
246
|
+
) {
|
|
219
247
|
next.status = "disabled";
|
|
220
248
|
}
|
|
221
249
|
if (previous) {
|
|
@@ -238,17 +266,24 @@ export function seedInferenceProfiles(
|
|
|
238
266
|
// 2. User profiles — only at hatch time for off-platform installations.
|
|
239
267
|
let userConnectionName: string | undefined;
|
|
240
268
|
if (options.isHatch && !isPlatform) {
|
|
241
|
-
// BYOK hatch: disable
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
269
|
+
// BYOK hatch: disable canonical managed connections so the picker doesn't
|
|
270
|
+
// surface unusable platform-auth options on day one. If the hatch overlay
|
|
271
|
+
// selected a managed profile, leave that connection active; the user has
|
|
272
|
+
// already chosen managed inference. Runs only here, only at hatch —
|
|
273
|
+
// `seedCanonicalConnections` leaves `status` alone on subsequent boots so
|
|
274
|
+
// a post-hatch user re-enable persists.
|
|
246
275
|
if (options.db) {
|
|
247
|
-
disableManagedConnectionsForByokHatch(options.db
|
|
276
|
+
disableManagedConnectionsForByokHatch(options.db, {
|
|
277
|
+
excludeConnection: hatchSelectedManagedConnection,
|
|
278
|
+
});
|
|
248
279
|
}
|
|
249
280
|
|
|
250
281
|
const hatchProvider = readString(readObject(llm.default)?.provider);
|
|
251
|
-
if (
|
|
282
|
+
if (
|
|
283
|
+
hatchProvider &&
|
|
284
|
+
hatchProvider !== "ollama" &&
|
|
285
|
+
!PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(hatchProvider)
|
|
286
|
+
) {
|
|
252
287
|
userConnectionName = `${hatchProvider}-personal`;
|
|
253
288
|
|
|
254
289
|
if (options.db) {
|
|
@@ -269,8 +304,7 @@ export function seedInferenceProfiles(
|
|
|
269
304
|
}
|
|
270
305
|
}
|
|
271
306
|
|
|
272
|
-
const provider =
|
|
273
|
-
hatchProvider as NonNullable<ProfileEntry["provider"]>;
|
|
307
|
+
const provider = hatchProvider as NonNullable<ProfileEntry["provider"]>;
|
|
274
308
|
for (const [name, template] of Object.entries(USER_PROFILE_TEMPLATES)) {
|
|
275
309
|
if (preservedProfileNames.has(name)) continue;
|
|
276
310
|
profiles[name] = materializeProfile(
|
|
@@ -342,7 +376,7 @@ function materializeProfile(
|
|
|
342
376
|
provider: NonNullable<ProfileEntry["provider"]>,
|
|
343
377
|
connectionName: string,
|
|
344
378
|
): ProfileEntry {
|
|
345
|
-
const { intent, ...rest } = template;
|
|
379
|
+
const { intent, provider: _p, connectionName: _c, ...rest } = template;
|
|
346
380
|
return {
|
|
347
381
|
...rest,
|
|
348
382
|
provider,
|
|
@@ -361,6 +395,42 @@ function readString(value: unknown): string | undefined {
|
|
|
361
395
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
362
396
|
}
|
|
363
397
|
|
|
398
|
+
function getHatchSelectedManagedConnection(
|
|
399
|
+
llm: Record<string, unknown>,
|
|
400
|
+
profiles: Record<string, Record<string, unknown>>,
|
|
401
|
+
options: SeedInferenceProfilesOptions,
|
|
402
|
+
): string | undefined {
|
|
403
|
+
if (!options.isHatch || options.preserveActiveProfile !== true) {
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const activeProfile = readString(llm.activeProfile);
|
|
408
|
+
if (!activeProfile) return undefined;
|
|
409
|
+
|
|
410
|
+
const activeProfileEntry = readObject(profiles[activeProfile]);
|
|
411
|
+
if (
|
|
412
|
+
activeProfileEntry &&
|
|
413
|
+
Object.prototype.hasOwnProperty.call(
|
|
414
|
+
activeProfileEntry,
|
|
415
|
+
"provider_connection",
|
|
416
|
+
)
|
|
417
|
+
) {
|
|
418
|
+
const explicitConnection = readString(
|
|
419
|
+
activeProfileEntry.provider_connection,
|
|
420
|
+
);
|
|
421
|
+
return explicitConnection &&
|
|
422
|
+
MANAGED_CONNECTION_NAMES.has(explicitConnection)
|
|
423
|
+
? explicitConnection
|
|
424
|
+
: undefined;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const templateConnection =
|
|
428
|
+
MANAGED_PROFILE_TEMPLATES[activeProfile]?.connectionName;
|
|
429
|
+
return templateConnection && MANAGED_CONNECTION_NAMES.has(templateConnection)
|
|
430
|
+
? templateConnection
|
|
431
|
+
: undefined;
|
|
432
|
+
}
|
|
433
|
+
|
|
364
434
|
/**
|
|
365
435
|
* Format the human-readable label seeded onto a personal provider connection
|
|
366
436
|
* at hatch time, e.g. `"Anthropic (Personal)"`. The display name is sourced
|
package/src/context/compactor.ts
CHANGED
|
@@ -155,6 +155,11 @@ export interface CompactionRunResult {
|
|
|
155
155
|
thresholdTokens: number;
|
|
156
156
|
compactedMessages: number;
|
|
157
157
|
compactedPersistedMessages: number;
|
|
158
|
+
/**
|
|
159
|
+
* Number of recent ("tail") messages preserved verbatim alongside the
|
|
160
|
+
* summary. Omitted on no-op / skipped results — defaults to 0 at render.
|
|
161
|
+
*/
|
|
162
|
+
preservedTailMessages?: number;
|
|
158
163
|
summaryCalls: number;
|
|
159
164
|
summaryInputTokens: number;
|
|
160
165
|
summaryOutputTokens: number;
|
|
@@ -327,7 +332,7 @@ export function renderImageManifest(entries: ManifestEntry[]): string {
|
|
|
327
332
|
* runtime emitted — typically
|
|
328
333
|
* `2026-04-02 (Thursday) 01:52:33 -05:00 (America/Chicago)`).
|
|
329
334
|
*/
|
|
330
|
-
function extractTurnContextTimestamp(message: Message): string | null {
|
|
335
|
+
export function extractTurnContextTimestamp(message: Message): string | null {
|
|
331
336
|
if (message.role !== "user") return null;
|
|
332
337
|
for (const block of message.content) {
|
|
333
338
|
if (block.type !== "text") continue;
|
|
@@ -439,6 +444,44 @@ function resolveTailStartIndex(
|
|
|
439
444
|
return null;
|
|
440
445
|
}
|
|
441
446
|
|
|
447
|
+
/**
|
|
448
|
+
* Walk a model-chosen tail index backward until it lands on a user message
|
|
449
|
+
* that does not contain client-side `tool_result` blocks. Prevents the
|
|
450
|
+
* orphan-`tool_result` failure where the matching assistant `tool_use` sits
|
|
451
|
+
* in the discarded prefix and Anthropic rejects the next call with
|
|
452
|
+
* `unexpected tool_use_id found in tool_result blocks`.
|
|
453
|
+
*
|
|
454
|
+
* Walking back (rather than forward) preserves the recent context the model
|
|
455
|
+
* deliberately chose to keep; the tail just expands by the few messages
|
|
456
|
+
* needed to re-anchor the orphaned `tool_result` against its `tool_use`.
|
|
457
|
+
*
|
|
458
|
+
* Returns 0 when the walk falls off the front — the caller treats this as
|
|
459
|
+
* "nothing to compact" via the existing `tailIndex === 0` branch.
|
|
460
|
+
*
|
|
461
|
+
* Only `type === "tool_result"` blocks count. Server-side tools
|
|
462
|
+
* (`server_tool_use` / `web_search_tool_result`) are self-paired inside an
|
|
463
|
+
* assistant message and never trigger an adjustment.
|
|
464
|
+
*/
|
|
465
|
+
export function adjustTailIndexForToolPairing(
|
|
466
|
+
messages: Message[],
|
|
467
|
+
tailIndex: number,
|
|
468
|
+
): number {
|
|
469
|
+
let k = tailIndex;
|
|
470
|
+
while (k > 0) {
|
|
471
|
+
const m = messages[k];
|
|
472
|
+
if (
|
|
473
|
+
m.role === "user" &&
|
|
474
|
+
// guard:allow-tool-result-only — server-side web_search_tool_result is
|
|
475
|
+
// self-paired inside its assistant message and never spans user turns.
|
|
476
|
+
!m.content.some((block) => block.type === "tool_result")
|
|
477
|
+
) {
|
|
478
|
+
return k;
|
|
479
|
+
}
|
|
480
|
+
k--;
|
|
481
|
+
}
|
|
482
|
+
return 0;
|
|
483
|
+
}
|
|
484
|
+
|
|
442
485
|
// ---------------------------------------------------------------------------
|
|
443
486
|
// Retained-image hydration
|
|
444
487
|
// ---------------------------------------------------------------------------
|
|
@@ -655,8 +698,12 @@ export async function runAssistantDrivenCompaction(
|
|
|
655
698
|
}
|
|
656
699
|
|
|
657
700
|
const timestamps = buildTimestampIndex(args.messages);
|
|
658
|
-
const
|
|
659
|
-
|
|
701
|
+
const resolvedTailIndex = resolveTailStartIndex(
|
|
702
|
+
args.messages,
|
|
703
|
+
timestamps,
|
|
704
|
+
parsed,
|
|
705
|
+
);
|
|
706
|
+
if (resolvedTailIndex == null) {
|
|
660
707
|
log.warn(
|
|
661
708
|
{
|
|
662
709
|
timestamp: parsed.tailStartTimestamp,
|
|
@@ -680,6 +727,22 @@ export async function runAssistantDrivenCompaction(
|
|
|
680
727
|
};
|
|
681
728
|
}
|
|
682
729
|
|
|
730
|
+
const tailIndex = adjustTailIndexForToolPairing(
|
|
731
|
+
args.messages,
|
|
732
|
+
resolvedTailIndex,
|
|
733
|
+
);
|
|
734
|
+
if (tailIndex !== resolvedTailIndex) {
|
|
735
|
+
log.info(
|
|
736
|
+
{
|
|
737
|
+
conversationId: args.conversationId,
|
|
738
|
+
originalTailIndex: resolvedTailIndex,
|
|
739
|
+
tailIndex,
|
|
740
|
+
walkedBy: resolvedTailIndex - tailIndex,
|
|
741
|
+
},
|
|
742
|
+
"Adjusted compaction tail backward to preserve tool_use/tool_result pairing",
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
|
|
683
746
|
if (tailIndex === 0) {
|
|
684
747
|
return {
|
|
685
748
|
...emptyResult(
|
|
@@ -762,6 +825,9 @@ export async function runAssistantDrivenCompaction(
|
|
|
762
825
|
compactedMessages: compactableMessages.length,
|
|
763
826
|
compactedPersistedMessages,
|
|
764
827
|
tailIndex,
|
|
828
|
+
...(tailIndex !== resolvedTailIndex
|
|
829
|
+
? { originalTailIndex: resolvedTailIndex }
|
|
830
|
+
: {}),
|
|
765
831
|
retainedImages: resolved.length,
|
|
766
832
|
summaryChars: summaryText.length,
|
|
767
833
|
},
|
|
@@ -780,6 +846,7 @@ export async function runAssistantDrivenCompaction(
|
|
|
780
846
|
thresholdTokens,
|
|
781
847
|
compactedMessages: compactableMessages.length,
|
|
782
848
|
compactedPersistedMessages,
|
|
849
|
+
preservedTailMessages: args.messages.length - tailIndex,
|
|
783
850
|
summaryCalls: 1,
|
|
784
851
|
summaryInputTokens: response.usage.inputTokens,
|
|
785
852
|
summaryOutputTokens: response.usage.outputTokens,
|
|
@@ -885,10 +952,12 @@ export async function runEmergencyCompaction(
|
|
|
885
952
|
|
|
886
953
|
const splitIndex = findLastToolPairStart(args.messages);
|
|
887
954
|
if (splitIndex == null || splitIndex === 0) {
|
|
888
|
-
log.info(
|
|
889
|
-
|
|
955
|
+
log.info("Emergency compaction: no tool pair found — falling through");
|
|
956
|
+
return emptyResult(
|
|
957
|
+
args,
|
|
958
|
+
thresholdTokens,
|
|
959
|
+
"no tool pair for emergency split",
|
|
890
960
|
);
|
|
891
|
-
return emptyResult(args, thresholdTokens, "no tool pair for emergency split");
|
|
892
961
|
}
|
|
893
962
|
|
|
894
963
|
const keptTail = stripInjectionsForCompaction(
|
|
@@ -904,8 +973,7 @@ export async function runEmergencyCompaction(
|
|
|
904
973
|
const prefixBudget = args.maxInputTokens - instructionBudget - outputBudget;
|
|
905
974
|
|
|
906
975
|
let prefixEstimate = estimatePromptTokens(prefix, args.systemPrompt, {
|
|
907
|
-
providerName:
|
|
908
|
-
args.provider.tokenEstimationProvider ?? args.provider.name,
|
|
976
|
+
providerName: args.provider.tokenEstimationProvider ?? args.provider.name,
|
|
909
977
|
});
|
|
910
978
|
|
|
911
979
|
if (prefixEstimate > prefixBudget && prefix.length > 1) {
|
|
@@ -920,10 +988,7 @@ export async function runEmergencyCompaction(
|
|
|
920
988
|
// Drop messages from the front until we fit. Keep at least the first
|
|
921
989
|
// message (may be an existing summary) and try to preserve recent context.
|
|
922
990
|
let dropCount = 0;
|
|
923
|
-
while (
|
|
924
|
-
prefixEstimate > prefixBudget &&
|
|
925
|
-
dropCount < prefix.length - 1
|
|
926
|
-
) {
|
|
991
|
+
while (prefixEstimate > prefixBudget && dropCount < prefix.length - 1) {
|
|
927
992
|
dropCount++;
|
|
928
993
|
const truncated = prefix.slice(dropCount);
|
|
929
994
|
prefixEstimate = estimatePromptTokens(truncated, args.systemPrompt, {
|
|
@@ -1015,7 +1080,8 @@ export async function runEmergencyCompaction(
|
|
|
1015
1080
|
compactedMessages: compactedCount,
|
|
1016
1081
|
keptTailMessages: keptTail.length,
|
|
1017
1082
|
summaryChars: summaryText.length,
|
|
1018
|
-
prefixTruncated:
|
|
1083
|
+
prefixTruncated:
|
|
1084
|
+
prefix[0]?.content?.[0]?.type === "text" &&
|
|
1019
1085
|
(prefix[0].content[0] as { text: string }).text.includes("truncated"),
|
|
1020
1086
|
},
|
|
1021
1087
|
"Applied emergency mid-turn compaction",
|
|
@@ -1030,6 +1096,7 @@ export async function runEmergencyCompaction(
|
|
|
1030
1096
|
thresholdTokens,
|
|
1031
1097
|
compactedMessages: compactedCount,
|
|
1032
1098
|
compactedPersistedMessages: Math.max(0, compactedCount - nonPersistedAway),
|
|
1099
|
+
preservedTailMessages: keptTail.length,
|
|
1033
1100
|
summaryCalls: 1,
|
|
1034
1101
|
summaryInputTokens: response.usage.inputTokens,
|
|
1035
1102
|
summaryOutputTokens: response.usage.outputTokens,
|
|
@@ -33,18 +33,42 @@ const OTHER_BLOCK_TOKENS = 16;
|
|
|
33
33
|
const SYSTEM_PROMPT_OVERHEAD_TOKENS = 8;
|
|
34
34
|
const GEMINI_INLINE_FILE_MIME_TYPES = new Set(["application/pdf"]);
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
// are resized. 1,200,000 / 750 = 1,600 tokens, matching the documented threshold.
|
|
42
|
-
// Reference table (max sizes that won't be resized):
|
|
36
|
+
// Dimension-based image token estimate, used as a universal default for every
|
|
37
|
+
// provider. The formula and constants below come from Anthropic's published
|
|
38
|
+
// vision spec — scale to a 1568x1568 bounding box, then charge
|
|
39
|
+
// ~(width * height) / 750 tokens, with a ~1.2-megapixel cap that lands at
|
|
40
|
+
// ~1,600 tokens per image. Reference table (max sizes that won't be resized):
|
|
43
41
|
// 1:1 → 1092x1092 (~1,590 tokens) 1:2 → 784x1568 (~1,639 tokens)
|
|
44
42
|
// See: https://platform.claude.com/docs/en/build-with-claude/vision#evaluate-image-size
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
//
|
|
44
|
+
// Other multimodal providers (OpenAI/GPT-4V tile pricing, Moonshot/Kimi,
|
|
45
|
+
// Gemini fixed-cost, OpenRouter pass-through) price differently in detail,
|
|
46
|
+
// but every published rate lands in the same hundreds-to-low-thousands range
|
|
47
|
+
// per image. Using this formula as the default gets compaction within ~2-3x
|
|
48
|
+
// of reality instead of the ~30-100x over-counting produced by treating the
|
|
49
|
+
// raw base64 payload as if it were text.
|
|
50
|
+
const IMAGE_MAX_DIMENSION = 1568;
|
|
51
|
+
const IMAGE_MAX_PIXELS = 1_200_000;
|
|
52
|
+
const IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
53
|
+
const IMAGE_MAX_TOKENS = 1_600;
|
|
54
|
+
|
|
55
|
+
// Gemini prices images differently: any side ≤384px counts as a single 258-token
|
|
56
|
+
// tile; anything larger is resized so the longest side is ≤3072px and then
|
|
57
|
+
// split into 768x768 tiles at 258 tokens each. A 4000x4000 image clamps to
|
|
58
|
+
// 3072x3072 → ceil(3072/768)^2 = 16 tiles = 4,128 tokens. Without the clamp
|
|
59
|
+
// we'd over-count it as 36 tiles (~9,288 tokens) and trigger spurious
|
|
60
|
+
// compaction. The clamped 16-tile, 4,128-token figure is also the per-image
|
|
61
|
+
// ceiling we fall back to when dimensions are unparseable (e.g. HEIC/HEIF
|
|
62
|
+
// from iOS attachments) — the generic 1,600 cap can under-count Gemini
|
|
63
|
+
// images by ~2.5x.
|
|
64
|
+
// See: https://ai.google.dev/gemini-api/docs/tokens#multimodal-tokens
|
|
65
|
+
const GEMINI_IMAGE_SMALL_THRESHOLD = 384;
|
|
66
|
+
const GEMINI_IMAGE_TILE_SIZE = 768;
|
|
67
|
+
const GEMINI_IMAGE_TOKENS_PER_TILE = 258;
|
|
68
|
+
const GEMINI_IMAGE_MAX_DIMENSION = 3072;
|
|
69
|
+
const GEMINI_IMAGE_MAX_TOKENS =
|
|
70
|
+
Math.ceil(GEMINI_IMAGE_MAX_DIMENSION / GEMINI_IMAGE_TILE_SIZE) ** 2 *
|
|
71
|
+
GEMINI_IMAGE_TOKENS_PER_TILE;
|
|
48
72
|
|
|
49
73
|
// Anthropic renders each PDF page as an image (~1,568 tokens at standard
|
|
50
74
|
// resolution) plus any extracted text. Typical PDF pages are 50-150 KB.
|
|
@@ -103,45 +127,62 @@ function estimateFileDataTokens(
|
|
|
103
127
|
return 0;
|
|
104
128
|
}
|
|
105
129
|
|
|
106
|
-
function
|
|
130
|
+
function estimateImageTokensByDimensions(
|
|
131
|
+
width: number,
|
|
132
|
+
height: number,
|
|
133
|
+
): number {
|
|
107
134
|
// Step 1: Scale to fit within 1568px bounding box
|
|
108
|
-
const dimScale = Math.min(
|
|
109
|
-
1,
|
|
110
|
-
ANTHROPIC_IMAGE_MAX_DIMENSION / Math.max(width, height),
|
|
111
|
-
);
|
|
135
|
+
const dimScale = Math.min(1, IMAGE_MAX_DIMENSION / Math.max(width, height));
|
|
112
136
|
let scaledWidth = Math.round(width * dimScale);
|
|
113
137
|
let scaledHeight = Math.round(height * dimScale);
|
|
114
138
|
|
|
115
139
|
// Step 2: Scale further if exceeds megapixel budget
|
|
116
140
|
const pixels = scaledWidth * scaledHeight;
|
|
117
|
-
if (pixels >
|
|
118
|
-
const mpScale = Math.sqrt(
|
|
141
|
+
if (pixels > IMAGE_MAX_PIXELS) {
|
|
142
|
+
const mpScale = Math.sqrt(IMAGE_MAX_PIXELS / pixels);
|
|
119
143
|
scaledWidth = Math.round(scaledWidth * mpScale);
|
|
120
144
|
scaledHeight = Math.round(scaledHeight * mpScale);
|
|
121
145
|
}
|
|
122
146
|
|
|
123
|
-
return Math.ceil(
|
|
124
|
-
|
|
125
|
-
|
|
147
|
+
return Math.ceil(scaledWidth * scaledHeight * IMAGE_TOKENS_PER_PIXEL);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function estimateGeminiImageTokens(width: number, height: number): number {
|
|
151
|
+
if (
|
|
152
|
+
width <= GEMINI_IMAGE_SMALL_THRESHOLD &&
|
|
153
|
+
height <= GEMINI_IMAGE_SMALL_THRESHOLD
|
|
154
|
+
) {
|
|
155
|
+
return GEMINI_IMAGE_TOKENS_PER_TILE;
|
|
156
|
+
}
|
|
157
|
+
// Gemini resizes images so the longest side is ≤3072px before tiling.
|
|
158
|
+
const clampedWidth = Math.min(width, GEMINI_IMAGE_MAX_DIMENSION);
|
|
159
|
+
const clampedHeight = Math.min(height, GEMINI_IMAGE_MAX_DIMENSION);
|
|
160
|
+
const tilesWide = Math.ceil(clampedWidth / GEMINI_IMAGE_TILE_SIZE);
|
|
161
|
+
const tilesHigh = Math.ceil(clampedHeight / GEMINI_IMAGE_TILE_SIZE);
|
|
162
|
+
return tilesWide * tilesHigh * GEMINI_IMAGE_TOKENS_PER_TILE;
|
|
126
163
|
}
|
|
127
164
|
|
|
128
165
|
function estimateImageTokens(
|
|
129
166
|
block: Extract<ContentBlock, { type: "image" }>,
|
|
130
167
|
options?: TokenEstimatorOptions,
|
|
131
168
|
): number {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
);
|
|
137
|
-
if (dims) {
|
|
138
|
-
return estimateAnthropicImageTokens(dims.width, dims.height);
|
|
169
|
+
const dims = parseImageDimensions(block.source.data, block.source.media_type);
|
|
170
|
+
if (dims) {
|
|
171
|
+
if (options?.providerName === "gemini") {
|
|
172
|
+
return estimateGeminiImageTokens(dims.width, dims.height);
|
|
139
173
|
}
|
|
140
|
-
|
|
141
|
-
|
|
174
|
+
return estimateImageTokensByDimensions(dims.width, dims.height);
|
|
175
|
+
}
|
|
176
|
+
// Dimensions unparseable (corrupt header, or formats parseImageDimensions
|
|
177
|
+
// doesn't recognize like HEIC/HEIF coming from iOS attachments). Fall back
|
|
178
|
+
// to the per-provider per-image ceiling rather than the raw base64 length,
|
|
179
|
+
// which over-counts by 30-100x. Gemini's tile pricing tops out well above
|
|
180
|
+
// the universal 1,600-token cap, so use its max-tile budget instead to
|
|
181
|
+
// avoid under-counting large iPhone screenshots.
|
|
182
|
+
if (options?.providerName === "gemini") {
|
|
183
|
+
return GEMINI_IMAGE_MAX_TOKENS;
|
|
142
184
|
}
|
|
143
|
-
|
|
144
|
-
return estimateTextTokens(block.source.data);
|
|
185
|
+
return IMAGE_MAX_TOKENS;
|
|
145
186
|
}
|
|
146
187
|
|
|
147
188
|
export function estimateContentBlockTokens(
|