@alumbwe/anvil 1.0.20 → 1.0.22
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/package.json +74 -70
- package/src/agents/bundled-agents.generated.ts +4 -4
- package/src/chat.tsx +1703 -1698
- package/src/cli-args.ts +134 -134
- package/src/components/status-bar.tsx +331 -313
- package/src/hooks/helpers/send-message.ts +663 -654
- package/src/hooks/use-send-message.ts +806 -773
- package/src/index.tsx +473 -473
- package/src/state/chat-store.ts +553 -544
- package/src/utils/create-run-config.ts +121 -121
- package/src/utils/format-compact-number.ts +37 -0
- package/vendor/@anvil/common/package.json +27 -0
- package/vendor/@anvil/common/src/__tests__/agent-validation.test.ts +846 -0
- package/vendor/@anvil/common/src/__tests__/anvil-model-availability.test.ts +101 -0
- package/vendor/@anvil/common/src/__tests__/anvil-models.test.ts +1408 -0
- package/vendor/@anvil/common/src/__tests__/anvil-public-data-use-copy.test.ts +258 -0
- package/vendor/@anvil/common/src/__tests__/anvil-referral-tiers.test.ts +48 -0
- package/vendor/@anvil/common/src/__tests__/anvil-spend-ceilings.test.ts +282 -0
- package/vendor/@anvil/common/src/__tests__/anvil-trust.test.ts +526 -0
- package/vendor/@anvil/common/src/__tests__/deepseek-direct.test.ts +68 -0
- package/vendor/@anvil/common/src/__tests__/disposable-email.test.ts +132 -0
- package/vendor/@anvil/common/src/__tests__/dynamic-agent-template-schema.test.ts +422 -0
- package/vendor/@anvil/common/src/__tests__/env-ci.test.ts +167 -0
- package/vendor/@anvil/common/src/__tests__/env-process.test.ts +145 -0
- package/vendor/@anvil/common/src/__tests__/foreign-client-shipped-agents.test.ts +234 -0
- package/vendor/@anvil/common/src/__tests__/foreign-client-signals.test.ts +379 -0
- package/vendor/@anvil/common/src/__tests__/free-agents.test.ts +807 -0
- package/vendor/@anvil/common/src/__tests__/gravity-capi.test.ts +154 -0
- package/vendor/@anvil/common/src/__tests__/handlesteps-parsing.test.ts +246 -0
- package/vendor/@anvil/common/src/__tests__/kimi-k3-god-only.test.ts +77 -0
- package/vendor/@anvil/common/src/__tests__/model-config.test.ts +89 -0
- package/vendor/@anvil/common/src/__tests__/project-file-tree.test.ts +298 -0
- package/vendor/@anvil/common/src/__tests__/provisioned-model-tiers.test.ts +116 -0
- package/vendor/@anvil/common/src/__tests__/reasoning-effort.test.ts +167 -0
- package/vendor/@anvil/common/src/__tests__/reddit-capi.test.ts +132 -0
- package/vendor/@anvil/common/src/__tests__/response-ad-positions.test.ts +72 -0
- package/vendor/@anvil/common/src/__tests__/user-state.test.ts +30 -0
- package/vendor/@anvil/common/src/actions.ts +215 -0
- package/vendor/@anvil/common/src/analytics-core.ts +69 -0
- package/vendor/@anvil/common/src/analytics.ts +93 -0
- package/vendor/@anvil/common/src/api-keys/constants.ts +26 -0
- package/vendor/@anvil/common/src/browser-actions.ts +413 -0
- package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding-gate.test.ts +63 -0
- package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding.test.ts +246 -0
- package/vendor/@anvil/common/src/constants/__tests__/cf-worker-signals.test.ts +106 -0
- package/vendor/@anvil/common/src/constants/agents.ts +99 -0
- package/vendor/@anvil/common/src/constants/analytics-events.ts +435 -0
- package/vendor/@anvil/common/src/constants/anthropic.ts +73 -0
- package/vendor/@anvil/common/src/constants/anvil-data-use.ts +81 -0
- package/vendor/@anvil/common/src/constants/anvil-errors.ts +7 -0
- package/vendor/@anvil/common/src/constants/anvil-gemini-thinker.ts +21 -0
- package/vendor/@anvil/common/src/constants/anvil-model-ids.ts +11 -0
- package/vendor/@anvil/common/src/constants/anvil-models.ts +2130 -0
- package/vendor/@anvil/common/src/constants/anvil-onboarding-gate.ts +55 -0
- package/vendor/@anvil/common/src/constants/anvil-onboarding.ts +307 -0
- package/vendor/@anvil/common/src/constants/anvil-referral-tiers.ts +87 -0
- package/vendor/@anvil/common/src/constants/anvil-signup-block.ts +68 -0
- package/vendor/@anvil/common/src/constants/anvil-spend-ceilings.ts +450 -0
- package/vendor/@anvil/common/src/constants/anvil-trust.ts +976 -0
- package/vendor/@anvil/common/src/constants/auth.ts +18 -0
- package/vendor/@anvil/common/src/constants/byok.ts +2 -0
- package/vendor/@anvil/common/src/constants/cf-worker-signals.ts +132 -0
- package/vendor/@anvil/common/src/constants/composio.ts +34 -0
- package/vendor/@anvil/common/src/constants/deepseek-direct.ts +55 -0
- package/vendor/@anvil/common/src/constants/feedback.ts +13 -0
- package/vendor/@anvil/common/src/constants/foreign-client-signals.ts +272 -0
- package/vendor/@anvil/common/src/constants/free-agents.ts +777 -0
- package/vendor/@anvil/common/src/constants/gemini.ts +15 -0
- package/vendor/@anvil/common/src/constants/grant-priorities.ts +13 -0
- package/vendor/@anvil/common/src/constants/hosts.ts +6 -0
- package/vendor/@anvil/common/src/constants/images.ts +51 -0
- package/vendor/@anvil/common/src/constants/index.ts +7 -0
- package/vendor/@anvil/common/src/constants/knowledge.ts +35 -0
- package/vendor/@anvil/common/src/constants/limits.ts +23 -0
- package/vendor/@anvil/common/src/constants/model-config.ts +277 -0
- package/vendor/@anvil/common/src/constants/openrouter-attribution.ts +8 -0
- package/vendor/@anvil/common/src/constants/paths.ts +69 -0
- package/vendor/@anvil/common/src/constants/provider-routes.ts +317 -0
- package/vendor/@anvil/common/src/constants/reasoning-effort.ts +79 -0
- package/vendor/@anvil/common/src/constants/skills.ts +60 -0
- package/vendor/@anvil/common/src/constants/subscription-plans.ts +49 -0
- package/vendor/@anvil/common/src/constants/ui.ts +25 -0
- package/vendor/@anvil/common/src/env-ci.ts +36 -0
- package/vendor/@anvil/common/src/env-process.ts +95 -0
- package/vendor/@anvil/common/src/env-schema.ts +95 -0
- package/vendor/@anvil/common/src/env.ts +24 -0
- package/vendor/@anvil/common/src/gravity-capi.ts +207 -0
- package/vendor/@anvil/common/src/mcp/client.ts +233 -0
- package/vendor/@anvil/common/src/old-constants.ts +10 -0
- package/vendor/@anvil/common/src/project-file-tree.ts +355 -0
- package/vendor/@anvil/common/src/reddit-capi.ts +254 -0
- package/vendor/@anvil/common/src/schemas/feedback.ts +52 -0
- package/vendor/@anvil/common/src/schemas/logs.ts +66 -0
- package/vendor/@anvil/common/src/templates/agent-validation.ts +392 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/LICENSE +202 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/README.md +294 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/01-basic-diff-reviewer.ts +17 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/02-intermediate-git-committer.ts +78 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/03-advanced-file-explorer.ts +73 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/my-custom-agent.ts +40 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/package.json +6 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/skills/README.md +65 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/skills/example-skill/SKILL.md +29 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/types/agent-definition.ts +497 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/types/tools.ts +444 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/types/util-types.ts +178 -0
- package/vendor/@anvil/common/src/testing/TESTING_PATTERNS.md +351 -0
- package/vendor/@anvil/common/src/testing/anvil-offer-invariants.ts +169 -0
- package/vendor/@anvil/common/src/testing/errors.ts +33 -0
- package/vendor/@anvil/common/src/testing/fixtures/agent-runtime.ts +334 -0
- package/vendor/@anvil/common/src/testing/impl/agent-runtime.ts +6 -0
- package/vendor/@anvil/common/src/testing/index.ts +84 -0
- package/vendor/@anvil/common/src/testing/mock-modules.ts +53 -0
- package/vendor/@anvil/common/src/testing/mock-types.ts +123 -0
- package/vendor/@anvil/common/src/testing/mocks/analytics.ts +261 -0
- package/vendor/@anvil/common/src/testing/mocks/child-process.ts +93 -0
- package/vendor/@anvil/common/src/testing/mocks/crypto.ts +218 -0
- package/vendor/@anvil/common/src/testing/mocks/database.ts +337 -0
- package/vendor/@anvil/common/src/testing/mocks/fetch.ts +219 -0
- package/vendor/@anvil/common/src/testing/mocks/filesystem.ts +166 -0
- package/vendor/@anvil/common/src/testing/mocks/index.ts +101 -0
- package/vendor/@anvil/common/src/testing/mocks/logger.ts +135 -0
- package/vendor/@anvil/common/src/testing/mocks/stream.ts +313 -0
- package/vendor/@anvil/common/src/testing/mocks/timers.ts +132 -0
- package/vendor/@anvil/common/src/testing/mocks/tree-sitter.ts +127 -0
- package/vendor/@anvil/common/src/testing/setup.ts +282 -0
- package/vendor/@anvil/common/src/testing-env-ci.ts +15 -0
- package/vendor/@anvil/common/src/testing-env-process.ts +78 -0
- package/vendor/@anvil/common/src/tools/__tests__/compile-tool-definitions.test.ts +34 -0
- package/vendor/@anvil/common/src/tools/compile-tool-definitions.ts +157 -0
- package/vendor/@anvil/common/src/tools/constants.ts +117 -0
- package/vendor/@anvil/common/src/tools/list.ts +190 -0
- package/vendor/@anvil/common/src/tools/params/__tests__/coerce-to-array.test.ts +213 -0
- package/vendor/@anvil/common/src/tools/params/__tests__/read-docs.test.ts +47 -0
- package/vendor/@anvil/common/src/tools/params/__tests__/render-ui.test.ts +65 -0
- package/vendor/@anvil/common/src/tools/params/tool/__tests__/run-terminal-command-timeout.test.ts +49 -0
- package/vendor/@anvil/common/src/tools/params/tool/add-message.ts +39 -0
- package/vendor/@anvil/common/src/tools/params/tool/add-subgoal.ts +57 -0
- package/vendor/@anvil/common/src/tools/params/tool/apply-patch.ts +110 -0
- package/vendor/@anvil/common/src/tools/params/tool/ask-user.ts +181 -0
- package/vendor/@anvil/common/src/tools/params/tool/browser-logs.ts +85 -0
- package/vendor/@anvil/common/src/tools/params/tool/cloud-plan-ready.ts +94 -0
- package/vendor/@anvil/common/src/tools/params/tool/code-search.ts +159 -0
- package/vendor/@anvil/common/src/tools/params/tool/composio.ts +131 -0
- package/vendor/@anvil/common/src/tools/params/tool/create-plan.ts +80 -0
- package/vendor/@anvil/common/src/tools/params/tool/end-turn.ts +57 -0
- package/vendor/@anvil/common/src/tools/params/tool/find-files.ts +60 -0
- package/vendor/@anvil/common/src/tools/params/tool/glob.ts +80 -0
- package/vendor/@anvil/common/src/tools/params/tool/gravity-index.ts +93 -0
- package/vendor/@anvil/common/src/tools/params/tool/list-directory.ts +58 -0
- package/vendor/@anvil/common/src/tools/params/tool/lookup-agent-info.ts +37 -0
- package/vendor/@anvil/common/src/tools/params/tool/propose-str-replace.ts +103 -0
- package/vendor/@anvil/common/src/tools/params/tool/propose-write-file.ts +71 -0
- package/vendor/@anvil/common/src/tools/params/tool/read-docs.ts +90 -0
- package/vendor/@anvil/common/src/tools/params/tool/read-files.ts +109 -0
- package/vendor/@anvil/common/src/tools/params/tool/read-subtree.ts +79 -0
- package/vendor/@anvil/common/src/tools/params/tool/read-url.ts +81 -0
- package/vendor/@anvil/common/src/tools/params/tool/render-ui.ts +168 -0
- package/vendor/@anvil/common/src/tools/params/tool/run-file-change-hooks.ts +57 -0
- package/vendor/@anvil/common/src/tools/params/tool/run-terminal-command.ts +211 -0
- package/vendor/@anvil/common/src/tools/params/tool/screenshot.ts +97 -0
- package/vendor/@anvil/common/src/tools/params/tool/set-messages.ts +44 -0
- package/vendor/@anvil/common/src/tools/params/tool/set-output.ts +61 -0
- package/vendor/@anvil/common/src/tools/params/tool/skill.ts +59 -0
- package/vendor/@anvil/common/src/tools/params/tool/spawn-agent-inline.ts +56 -0
- package/vendor/@anvil/common/src/tools/params/tool/spawn-agents.ts +154 -0
- package/vendor/@anvil/common/src/tools/params/tool/str-replace.ts +107 -0
- package/vendor/@anvil/common/src/tools/params/tool/suggest-followups.ts +97 -0
- package/vendor/@anvil/common/src/tools/params/tool/task-completed.ts +61 -0
- package/vendor/@anvil/common/src/tools/params/tool/think-deeply.ts +56 -0
- package/vendor/@anvil/common/src/tools/params/tool/update-subgoal.ts +89 -0
- package/vendor/@anvil/common/src/tools/params/tool/vision-analyze.ts +82 -0
- package/vendor/@anvil/common/src/tools/params/tool/web-search.ts +73 -0
- package/vendor/@anvil/common/src/tools/params/tool/write-file.ts +71 -0
- package/vendor/@anvil/common/src/tools/params/tool/write-todos.ts +67 -0
- package/vendor/@anvil/common/src/tools/params/utils.ts +150 -0
- package/vendor/@anvil/common/src/tools/utils.ts +24 -0
- package/vendor/@anvil/common/src/types/__tests__/dynamic-agent-template.test.ts +20 -0
- package/vendor/@anvil/common/src/types/agent-template.ts +215 -0
- package/vendor/@anvil/common/src/types/anvil-session.ts +535 -0
- package/vendor/@anvil/common/src/types/anvil-streak.ts +6 -0
- package/vendor/@anvil/common/src/types/anvil-usage.ts +64 -0
- package/vendor/@anvil/common/src/types/api/agents/publish.ts +61 -0
- package/vendor/@anvil/common/src/types/bun-test.d.ts +5 -0
- package/vendor/@anvil/common/src/types/contracts/agent-runtime.ts +75 -0
- package/vendor/@anvil/common/src/types/contracts/analytics.ts +9 -0
- package/vendor/@anvil/common/src/types/contracts/bigquery.ts +55 -0
- package/vendor/@anvil/common/src/types/contracts/billing.ts +46 -0
- package/vendor/@anvil/common/src/types/contracts/client.ts +53 -0
- package/vendor/@anvil/common/src/types/contracts/database.ts +112 -0
- package/vendor/@anvil/common/src/types/contracts/env.ts +203 -0
- package/vendor/@anvil/common/src/types/contracts/llm.ts +191 -0
- package/vendor/@anvil/common/src/types/contracts/logger.ts +14 -0
- package/vendor/@anvil/common/src/types/contracts/logs.ts +34 -0
- package/vendor/@anvil/common/src/types/contracts/trace.ts +21 -0
- package/vendor/@anvil/common/src/types/dynamic-agent-template.ts +328 -0
- package/vendor/@anvil/common/src/types/filesystem.ts +10 -0
- package/vendor/@anvil/common/src/types/function-params.ts +33 -0
- package/vendor/@anvil/common/src/types/grant.ts +20 -0
- package/vendor/@anvil/common/src/types/gravity-index.ts +170 -0
- package/vendor/@anvil/common/src/types/json.ts +29 -0
- package/vendor/@anvil/common/src/types/mcp.ts +24 -0
- package/vendor/@anvil/common/src/types/messages/anvil-message.ts +58 -0
- package/vendor/@anvil/common/src/types/messages/content-part.ts +59 -0
- package/vendor/@anvil/common/src/types/messages/data-content.ts +14 -0
- package/vendor/@anvil/common/src/types/messages/provider-metadata.ts +13 -0
- package/vendor/@anvil/common/src/types/organization.ts +118 -0
- package/vendor/@anvil/common/src/types/print-mode.ts +121 -0
- package/vendor/@anvil/common/src/types/publisher.ts +67 -0
- package/vendor/@anvil/common/src/types/session-state.ts +149 -0
- package/vendor/@anvil/common/src/types/skill.ts +86 -0
- package/vendor/@anvil/common/src/types/source.ts +11 -0
- package/vendor/@anvil/common/src/types/spawn.ts +13 -0
- package/vendor/@anvil/common/src/types/subscription.ts +67 -0
- package/vendor/@anvil/common/src/types/usage.ts +16 -0
- package/vendor/@anvil/common/src/types/util.ts +3 -0
- package/vendor/@anvil/common/src/util/__tests__/ad-user-agent.test.ts +26 -0
- package/vendor/@anvil/common/src/util/__tests__/analytics-dispatcher.test.ts +122 -0
- package/vendor/@anvil/common/src/util/__tests__/analytics-log.test.ts +102 -0
- package/vendor/@anvil/common/src/util/__tests__/analytics-sampling.test.ts +143 -0
- package/vendor/@anvil/common/src/util/__tests__/anvil-streak.test.ts +202 -0
- package/vendor/@anvil/common/src/util/__tests__/anvil-usage-summary.test.ts +193 -0
- package/vendor/@anvil/common/src/util/__tests__/axiom-only-log.test.ts +140 -0
- package/vendor/@anvil/common/src/util/__tests__/client-user-agent.test.ts +148 -0
- package/vendor/@anvil/common/src/util/__tests__/engagement-tracker.test.ts +115 -0
- package/vendor/@anvil/common/src/util/__tests__/env-file-path.test.ts +57 -0
- package/vendor/@anvil/common/src/util/__tests__/error-abort.test.ts +774 -0
- package/vendor/@anvil/common/src/util/__tests__/error-api-details.test.ts +206 -0
- package/vendor/@anvil/common/src/util/__tests__/file-read-limits.test.ts +173 -0
- package/vendor/@anvil/common/src/util/__tests__/format-code-search.test.ts +60 -0
- package/vendor/@anvil/common/src/util/__tests__/log-mirror.test.ts +31 -0
- package/vendor/@anvil/common/src/util/__tests__/messages.test.ts +1252 -0
- package/vendor/@anvil/common/src/util/__tests__/partial-json-delta.test.ts +505 -0
- package/vendor/@anvil/common/src/util/__tests__/path.test.ts +22 -0
- package/vendor/@anvil/common/src/util/__tests__/project-ignore.test.ts +90 -0
- package/vendor/@anvil/common/src/util/__tests__/promise.test.ts +325 -0
- package/vendor/@anvil/common/src/util/__tests__/rate-limit.test.ts +28 -0
- package/vendor/@anvil/common/src/util/__tests__/reddit-anvil-retention.test.ts +130 -0
- package/vendor/@anvil/common/src/util/__tests__/saxy.test.ts +1008 -0
- package/vendor/@anvil/common/src/util/__tests__/split-data.test.ts +289 -0
- package/vendor/@anvil/common/src/util/__tests__/string.test.ts +239 -0
- package/vendor/@anvil/common/src/util/__tests__/thread-title.test.ts +70 -0
- package/vendor/@anvil/common/src/util/__tests__/tool-result-media-order.test.ts +108 -0
- package/vendor/@anvil/common/src/util/__tests__/ttft-histogram.test.ts +180 -0
- package/vendor/@anvil/common/src/util/__tests__/with-timeout.test.ts +24 -0
- package/vendor/@anvil/common/src/util/__tests__/zoned-time.test.ts +88 -0
- package/vendor/@anvil/common/src/util/ad-user-agent.ts +25 -0
- package/vendor/@anvil/common/src/util/agent-file-utils.ts +110 -0
- package/vendor/@anvil/common/src/util/agent-id-parsing.ts +136 -0
- package/vendor/@anvil/common/src/util/agent-name-normalization.ts +38 -0
- package/vendor/@anvil/common/src/util/agent-name-resolver.ts +86 -0
- package/vendor/@anvil/common/src/util/analytics-dispatcher.ts +82 -0
- package/vendor/@anvil/common/src/util/analytics-log.ts +78 -0
- package/vendor/@anvil/common/src/util/analytics-sampling.ts +255 -0
- package/vendor/@anvil/common/src/util/anvil-model-availability.ts +102 -0
- package/vendor/@anvil/common/src/util/anvil-privacy.ts +82 -0
- package/vendor/@anvil/common/src/util/anvil-streak-line.ts +103 -0
- package/vendor/@anvil/common/src/util/anvil-streak.ts +156 -0
- package/vendor/@anvil/common/src/util/anvil-usage-summary.ts +125 -0
- package/vendor/@anvil/common/src/util/array.ts +31 -0
- package/vendor/@anvil/common/src/util/axiom-only-log.ts +124 -0
- package/vendor/@anvil/common/src/util/cache-debug.ts +171 -0
- package/vendor/@anvil/common/src/util/client-user-agent.ts +114 -0
- package/vendor/@anvil/common/src/util/credentials.ts +26 -0
- package/vendor/@anvil/common/src/util/currency.ts +25 -0
- package/vendor/@anvil/common/src/util/dates.ts +81 -0
- package/vendor/@anvil/common/src/util/disposable-email.ts +277 -0
- package/vendor/@anvil/common/src/util/engagement-tracker.ts +129 -0
- package/vendor/@anvil/common/src/util/env-file-path.ts +40 -0
- package/vendor/@anvil/common/src/util/error.ts +562 -0
- package/vendor/@anvil/common/src/util/file-read-limits.ts +236 -0
- package/vendor/@anvil/common/src/util/file.ts +339 -0
- package/vendor/@anvil/common/src/util/format-code-search.ts +115 -0
- package/vendor/@anvil/common/src/util/lazy-response-ads.ts +92 -0
- package/vendor/@anvil/common/src/util/log-data.ts +57 -0
- package/vendor/@anvil/common/src/util/log-ingest.ts +58 -0
- package/vendor/@anvil/common/src/util/log-mirror.ts +34 -0
- package/vendor/@anvil/common/src/util/lru-cache.ts +67 -0
- package/vendor/@anvil/common/src/util/messages.ts +685 -0
- package/vendor/@anvil/common/src/util/min-heap.ts +87 -0
- package/vendor/@anvil/common/src/util/model-utils.ts +4 -0
- package/vendor/@anvil/common/src/util/object.ts +128 -0
- package/vendor/@anvil/common/src/util/partial-json-delta.ts +89 -0
- package/vendor/@anvil/common/src/util/path.ts +15 -0
- package/vendor/@anvil/common/src/util/project-ignore.ts +119 -0
- package/vendor/@anvil/common/src/util/promise.ts +73 -0
- package/vendor/@anvil/common/src/util/random.ts +15 -0
- package/vendor/@anvil/common/src/util/rate-limit.ts +56 -0
- package/vendor/@anvil/common/src/util/reddit-anvil-retention.ts +60 -0
- package/vendor/@anvil/common/src/util/reddit-capi-events.ts +36 -0
- package/vendor/@anvil/common/src/util/response-ad-positions.ts +54 -0
- package/vendor/@anvil/common/src/util/saxy.ts +741 -0
- package/vendor/@anvil/common/src/util/skills.test.ts +44 -0
- package/vendor/@anvil/common/src/util/skills.ts +36 -0
- package/vendor/@anvil/common/src/util/split-data.ts +259 -0
- package/vendor/@anvil/common/src/util/stop-sequence.ts +61 -0
- package/vendor/@anvil/common/src/util/string.ts +426 -0
- package/vendor/@anvil/common/src/util/system-info.ts +53 -0
- package/vendor/@anvil/common/src/util/thread-title.ts +46 -0
- package/vendor/@anvil/common/src/util/ttft-histogram.ts +73 -0
- package/vendor/@anvil/common/src/util/xml-parser.ts +27 -0
- package/vendor/@anvil/common/src/util/xml.ts +17 -0
- package/vendor/@anvil/common/src/util/zod-schema.ts +25 -0
- package/vendor/@anvil/common/src/util/zoned-time.ts +136 -0
- package/vendor/@anvil/common/src/utils/ask-user-bridge.ts +44 -0
- package/vendor/@anvil/sdk/dist/index.cjs +52297 -0
- package/vendor/@anvil/sdk/dist/index.cjs.map +440 -0
- package/vendor/@anvil/sdk/dist/index.d.ts +4277 -0
- package/vendor/@anvil/sdk/dist/index.mjs +52290 -0
- package/vendor/@anvil/sdk/dist/index.mjs.map +440 -0
- package/vendor/@anvil/sdk/dist/vendor/ai.cjs +30886 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/arm64-darwin/rg +0 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/arm64-linux/rg +0 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-darwin/rg +0 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-linux/rg +0 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-win32/rg.exe +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-c-sharp.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-cpp.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-go.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-java.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-javascript.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-python.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-ruby.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-rust.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-tsx.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-typescript.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter.wasm +0 -0
- package/vendor/@anvil/sdk/package.json +21 -0
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
import { modelMessageSchema } from 'ai'
|
|
2
|
+
import { cloneDeep, has, isEqual } from 'lodash'
|
|
3
|
+
|
|
4
|
+
import type { Logger } from '../types/contracts/logger'
|
|
5
|
+
import type { JSONValue } from '../types/json'
|
|
6
|
+
import type {
|
|
7
|
+
AssistantMessage,
|
|
8
|
+
AuxiliaryMessageData,
|
|
9
|
+
Message,
|
|
10
|
+
SystemMessage,
|
|
11
|
+
ToolMessage,
|
|
12
|
+
UserMessage,
|
|
13
|
+
} from '../types/messages/anvil-message'
|
|
14
|
+
import type {
|
|
15
|
+
ToolCallPart,
|
|
16
|
+
ToolResultOutput,
|
|
17
|
+
} from '../types/messages/content-part'
|
|
18
|
+
import type { ProviderMetadata } from '../types/messages/provider-metadata'
|
|
19
|
+
import type {
|
|
20
|
+
AssistantModelMessage,
|
|
21
|
+
ModelMessage,
|
|
22
|
+
SystemModelMessage,
|
|
23
|
+
ToolModelMessage,
|
|
24
|
+
UserModelMessage,
|
|
25
|
+
} from 'ai'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
export function toContentString(msg: ModelMessage): string {
|
|
29
|
+
const { content } = msg
|
|
30
|
+
if (typeof content === 'string') return content
|
|
31
|
+
return content
|
|
32
|
+
.map((item) =>
|
|
33
|
+
item && 'text' in item && typeof item.text === 'string' ? item.text : '',
|
|
34
|
+
)
|
|
35
|
+
.join('\n')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function withCacheControl<T extends object>(
|
|
39
|
+
obj: T & { providerOptions?: ProviderMetadata },
|
|
40
|
+
): T & { providerOptions: ProviderMetadata } {
|
|
41
|
+
const wrapper = cloneDeep(obj) as T & {
|
|
42
|
+
providerOptions: ProviderMetadata
|
|
43
|
+
}
|
|
44
|
+
if (!wrapper.providerOptions) {
|
|
45
|
+
wrapper.providerOptions = {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* 'anvil' provider name is not compatible with providerMetadata for
|
|
49
|
+
* messages, so we need to use 'openaiCompatible' instead.
|
|
50
|
+
* https://github.com/vercel/ai/blob/8e4fdac31b4f8c6a8d07a606a8833e74adf99470/packages/openai-compatible/src/chat/convert-to-openai-compatible-chat-messages.ts#L9
|
|
51
|
+
*/
|
|
52
|
+
for (const provider of [
|
|
53
|
+
'anthropic',
|
|
54
|
+
'openrouter',
|
|
55
|
+
'openaiCompatible',
|
|
56
|
+
] as const) {
|
|
57
|
+
if (!wrapper.providerOptions[provider]) {
|
|
58
|
+
wrapper.providerOptions[provider] = {}
|
|
59
|
+
}
|
|
60
|
+
wrapper.providerOptions[provider].cache_control = { type: 'ephemeral' }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return wrapper
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function withoutCacheControl<T extends object>(
|
|
67
|
+
obj: T & { providerOptions?: ProviderMetadata },
|
|
68
|
+
): T & { providerOptions?: ProviderMetadata } {
|
|
69
|
+
const wrapper = cloneDeep(obj) as T & {
|
|
70
|
+
providerOptions?: ProviderMetadata
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const provider of [
|
|
74
|
+
'anthropic',
|
|
75
|
+
'openrouter',
|
|
76
|
+
'openaiCompatible',
|
|
77
|
+
] as const) {
|
|
78
|
+
if (has(wrapper.providerOptions?.[provider]?.cache_control, 'type')) {
|
|
79
|
+
delete wrapper.providerOptions?.[provider]?.cache_control?.type
|
|
80
|
+
}
|
|
81
|
+
if (
|
|
82
|
+
Object.keys(wrapper.providerOptions?.[provider]?.cache_control ?? {})
|
|
83
|
+
.length === 0
|
|
84
|
+
) {
|
|
85
|
+
delete wrapper.providerOptions?.[provider]?.cache_control
|
|
86
|
+
}
|
|
87
|
+
if (Object.keys(wrapper.providerOptions?.[provider] ?? {}).length === 0) {
|
|
88
|
+
delete wrapper.providerOptions?.[provider]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (Object.keys(wrapper.providerOptions ?? {}).length === 0) {
|
|
93
|
+
delete wrapper.providerOptions
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return wrapper
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type NonStringContent<T extends { content: any }> = Omit<T, 'content'> & {
|
|
100
|
+
content: Exclude<T['content'], string>
|
|
101
|
+
}
|
|
102
|
+
type ModelMessageWithAuxiliaryData = (
|
|
103
|
+
| SystemModelMessage
|
|
104
|
+
| NonStringContent<UserModelMessage>
|
|
105
|
+
| NonStringContent<AssistantModelMessage>
|
|
106
|
+
| ToolModelMessage
|
|
107
|
+
) &
|
|
108
|
+
AuxiliaryMessageData
|
|
109
|
+
|
|
110
|
+
function assistantToAnvilMessage(
|
|
111
|
+
message: Omit<AssistantMessage, 'content'> & {
|
|
112
|
+
content: Exclude<AssistantMessage['content'], string>[number]
|
|
113
|
+
},
|
|
114
|
+
): AssistantMessage {
|
|
115
|
+
// if (message.content.type === 'tool-call') {
|
|
116
|
+
// return cloneDeep({
|
|
117
|
+
// ...message,
|
|
118
|
+
// content: [
|
|
119
|
+
// {
|
|
120
|
+
// type: 'text',
|
|
121
|
+
// text: getToolCallString(
|
|
122
|
+
// message.content.toolName,
|
|
123
|
+
// message.content.input,
|
|
124
|
+
// false,
|
|
125
|
+
// ),
|
|
126
|
+
// },
|
|
127
|
+
// ],
|
|
128
|
+
// })
|
|
129
|
+
// }
|
|
130
|
+
return cloneDeep({ ...message, content: [message.content] })
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** A tool message answering `message`'s call, carrying `output`. */
|
|
134
|
+
function toolResultMessage(
|
|
135
|
+
message: ToolMessage,
|
|
136
|
+
output: Extract<ToolResultOutput, { type: 'json' }>,
|
|
137
|
+
): ModelMessageWithAuxiliaryData {
|
|
138
|
+
return cloneDeep<ToolModelMessage>({
|
|
139
|
+
...message,
|
|
140
|
+
role: 'tool',
|
|
141
|
+
content: [{ ...message, output, type: 'tool-result' }],
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const EMPTY_TOOL_OUTPUT = { type: 'json', value: '' } as const
|
|
146
|
+
|
|
147
|
+
function convertToolResultMessage(
|
|
148
|
+
message: ToolMessage,
|
|
149
|
+
): ModelMessageWithAuxiliaryData[] {
|
|
150
|
+
if (message.content.length === 0) {
|
|
151
|
+
return [toolResultMessage(message, EMPTY_TOOL_OUTPUT)]
|
|
152
|
+
}
|
|
153
|
+
// A `media` part becomes a *user* message, because providers do not accept
|
|
154
|
+
// images inside a tool message. Emit every tool message first regardless of
|
|
155
|
+
// the part order, so the results still immediately follow the assistant's
|
|
156
|
+
// tool_calls — otherwise a tool that returns [media, json] (which is the
|
|
157
|
+
// order preview_screenshot uses, so the model sees pixels before the note)
|
|
158
|
+
// puts a user message between the call and its result. Providers reject that
|
|
159
|
+
// outright ("tool call result does not follow tool call"), and the server's
|
|
160
|
+
// repair drops the call and the result as an unpaired pair.
|
|
161
|
+
const toolMessages: ModelMessageWithAuxiliaryData[] = []
|
|
162
|
+
const mediaMessages: ModelMessageWithAuxiliaryData[] = []
|
|
163
|
+
for (const c of message.content) {
|
|
164
|
+
if (c.type === 'json') {
|
|
165
|
+
toolMessages.push(toolResultMessage(message, c))
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
if (c.type === 'media') {
|
|
169
|
+
mediaMessages.push(
|
|
170
|
+
cloneDeep<UserMessage>({
|
|
171
|
+
...message,
|
|
172
|
+
role: 'user',
|
|
173
|
+
content: [{ type: 'file', data: c.data, mediaType: c.mediaType }],
|
|
174
|
+
}),
|
|
175
|
+
)
|
|
176
|
+
continue
|
|
177
|
+
}
|
|
178
|
+
c satisfies never
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Invalid tool output type: ${(c as { type: unknown }).type}`,
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Media-only output (see `mediaToolResult`) would otherwise emit a user
|
|
185
|
+
// message and no tool message at all, leaving the call permanently
|
|
186
|
+
// unanswered — the same unpaired-tool-call failure, just reached a different
|
|
187
|
+
// way. Answer it with the empty result the no-content branch already uses.
|
|
188
|
+
if (toolMessages.length === 0) {
|
|
189
|
+
toolMessages.push(toolResultMessage(message, EMPTY_TOOL_OUTPUT))
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return [...toolMessages, ...mediaMessages]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function convertToolMessage(message: Message): ModelMessageWithAuxiliaryData[] {
|
|
196
|
+
if (message.role === 'system') {
|
|
197
|
+
return [
|
|
198
|
+
{
|
|
199
|
+
...message,
|
|
200
|
+
content: message.content.map(({ text }) => text).join('\n\n'),
|
|
201
|
+
},
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
if (message.role === 'user') {
|
|
205
|
+
return [cloneDeep(message)]
|
|
206
|
+
}
|
|
207
|
+
if (message.role === 'assistant') {
|
|
208
|
+
if (typeof message.content === 'string') {
|
|
209
|
+
return [
|
|
210
|
+
cloneDeep({
|
|
211
|
+
...message,
|
|
212
|
+
content: [{ type: 'text' as const, text: message.content }],
|
|
213
|
+
}),
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
return message.content.map((c) => {
|
|
217
|
+
return assistantToAnvilMessage({
|
|
218
|
+
...message,
|
|
219
|
+
content: c,
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
if (message.role === 'tool') {
|
|
224
|
+
return convertToolResultMessage(message)
|
|
225
|
+
}
|
|
226
|
+
message satisfies never
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Invalid message role: ${(message as { role: unknown }).role}`,
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function convertToolMessages(
|
|
233
|
+
messages: Message[],
|
|
234
|
+
): ModelMessageWithAuxiliaryData[] {
|
|
235
|
+
const withoutToolMessages: ModelMessageWithAuxiliaryData[] = []
|
|
236
|
+
// Media parts ride *user* messages (see convertToolResultMessage), and the AI
|
|
237
|
+
// SDK throws at a user/system boundary when any tool call issued so far is
|
|
238
|
+
// still unanswered. Emitting media inline therefore splits a batch of
|
|
239
|
+
// parallel calls -- the sibling whose result is not reached yet is still
|
|
240
|
+
// pending, so validation fails client-side before the request is ever sent,
|
|
241
|
+
// permanently wedging the thread that replays that history. Buffer media
|
|
242
|
+
// until every call is answered, then flush: waiting on the answers not on
|
|
243
|
+
// the next non-tool message keeps the image as close to its own result as is
|
|
244
|
+
// safe, even when something is interleaved between the results.
|
|
245
|
+
// (convertToolResultMessage already does this within one tool message.)
|
|
246
|
+
const unanswered = new Set<string>()
|
|
247
|
+
let pendingMedia: ModelMessageWithAuxiliaryData[] = []
|
|
248
|
+
const flushMedia = () => {
|
|
249
|
+
if (unanswered.size > 0 || pendingMedia.length === 0) return
|
|
250
|
+
withoutToolMessages.push(...pendingMedia)
|
|
251
|
+
pendingMedia = []
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
for (const message of messages) {
|
|
255
|
+
const converted = convertToolMessage(message)
|
|
256
|
+
if (message.role !== 'tool') {
|
|
257
|
+
flushMedia()
|
|
258
|
+
withoutToolMessages.push(...converted)
|
|
259
|
+
for (const part of converted) {
|
|
260
|
+
if (part.role !== 'assistant') continue
|
|
261
|
+
for (const content of part.content) {
|
|
262
|
+
// Mirrors the SDK: a provider-executed call needs no local result, so
|
|
263
|
+
// tracking one would defer every later image forever.
|
|
264
|
+
if (content.type !== 'tool-call') continue
|
|
265
|
+
if (content.providerExecuted !== true) {
|
|
266
|
+
unanswered.add(content.toolCallId)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
continue
|
|
271
|
+
}
|
|
272
|
+
for (const part of converted) {
|
|
273
|
+
if (part.role !== 'tool') {
|
|
274
|
+
pendingMedia.push(part)
|
|
275
|
+
continue
|
|
276
|
+
}
|
|
277
|
+
withoutToolMessages.push(part)
|
|
278
|
+
for (const content of part.content) {
|
|
279
|
+
if (content.type === 'tool-result') {
|
|
280
|
+
unanswered.delete(content.toolCallId)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
flushMedia()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Anything still buffered had no clean boundary; dropUnansweredToolCalls has
|
|
288
|
+
// already removed calls that never get answered, so this is just a backstop.
|
|
289
|
+
withoutToolMessages.push(...pendingMedia)
|
|
290
|
+
return withoutToolMessages
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Recursively replace any lone (unpaired) UTF-16 surrogate with U+FFFD in every
|
|
295
|
+
* string reachable from `value`, mutating objects/arrays in place.
|
|
296
|
+
*
|
|
297
|
+
* Why this exists: unsafe truncation (e.g. slicing a file read or terminal
|
|
298
|
+
* output in the middle of an emoji / astral-plane character) can leave a lone
|
|
299
|
+
* surrogate in message content. JS's `JSON.stringify` is "well-formed" and emits
|
|
300
|
+
* it as a syntactically-valid `\uXXXX` escape, and JS's `JSON.parse` is lenient
|
|
301
|
+
* and accepts it, so the corruption slips through every client-side check. But
|
|
302
|
+
* strict server-side parsers — notably Rust's serde_json, used by
|
|
303
|
+
* OpenAI/OpenRouter/Anthropic — reject the whole request body with
|
|
304
|
+
* "unexpected end of hex escape". Once such content lands in the message
|
|
305
|
+
* history, EVERY subsequent provider request fails fatally and the agent stops,
|
|
306
|
+
* even though nothing is wrong with the current turn's tool call.
|
|
307
|
+
*
|
|
308
|
+
* Sanitizing here, at the single chokepoint where all messages are converted to
|
|
309
|
+
* provider format, guarantees a single bad character can never poison the
|
|
310
|
+
* conversation regardless of which tool produced it. It is a no-op on
|
|
311
|
+
* already-valid strings, so valid emoji, base64, etc. are untouched.
|
|
312
|
+
*
|
|
313
|
+
* (Equivalent to `String.prototype.toWellFormed()`, implemented as a regex so we
|
|
314
|
+
* don't need to widen the project's TS lib to ES2024.)
|
|
315
|
+
*/
|
|
316
|
+
const LONE_SURROGATE_REGEX =
|
|
317
|
+
/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g
|
|
318
|
+
|
|
319
|
+
function toWellFormedString(str: string): string {
|
|
320
|
+
return str.replace(LONE_SURROGATE_REGEX, '�')
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function wellFormStringsInPlace(value: unknown): void {
|
|
324
|
+
// Arrays and plain objects are both handled here: Object.keys enumerates array
|
|
325
|
+
// indices too, and indexing by string key mutates the element in place.
|
|
326
|
+
if (!value || typeof value !== 'object') return
|
|
327
|
+
const obj = value as Record<string, unknown>
|
|
328
|
+
for (const key of Object.keys(obj)) {
|
|
329
|
+
const item = obj[key]
|
|
330
|
+
if (typeof item === 'string') {
|
|
331
|
+
obj[key] = toWellFormedString(item)
|
|
332
|
+
} else {
|
|
333
|
+
wellFormStringsInPlace(item)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function convertCbToModelMessages({
|
|
339
|
+
messages,
|
|
340
|
+
includeCacheControl = true,
|
|
341
|
+
logger,
|
|
342
|
+
}: {
|
|
343
|
+
messages: Message[]
|
|
344
|
+
includeCacheControl?: boolean
|
|
345
|
+
logger?: Logger
|
|
346
|
+
}): ModelMessage[] {
|
|
347
|
+
// AI SDK validates tool-call/result pairing before making a request. Repair
|
|
348
|
+
// at this lowest shared boundary as well as in the runtime step builder so
|
|
349
|
+
// auxiliary calls and restored SDK state cannot poison a thread permanently.
|
|
350
|
+
const sendableMessages = dropUnansweredToolCalls(messages)
|
|
351
|
+
const toolMessagesConverted: ModelMessageWithAuxiliaryData[] =
|
|
352
|
+
convertToolMessages(sendableMessages)
|
|
353
|
+
|
|
354
|
+
const aggregated: ModelMessageWithAuxiliaryData[] = []
|
|
355
|
+
for (const message of toolMessagesConverted) {
|
|
356
|
+
if (aggregated.length === 0) {
|
|
357
|
+
aggregated.push(message)
|
|
358
|
+
continue
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const lastMessage = aggregated[aggregated.length - 1]
|
|
362
|
+
if (
|
|
363
|
+
lastMessage.timeToLive !== message.timeToLive ||
|
|
364
|
+
!isEqual(lastMessage.providerOptions, message.providerOptions) ||
|
|
365
|
+
!isEqual(lastMessage.tags, message.tags)
|
|
366
|
+
) {
|
|
367
|
+
aggregated.push(message)
|
|
368
|
+
continue
|
|
369
|
+
}
|
|
370
|
+
if (lastMessage.role === 'system' && message.role === 'system') {
|
|
371
|
+
lastMessage.content += '\n\n' + message.content
|
|
372
|
+
continue
|
|
373
|
+
}
|
|
374
|
+
if (lastMessage.role === 'user' && message.role === 'user') {
|
|
375
|
+
lastMessage.content.push(...message.content)
|
|
376
|
+
continue
|
|
377
|
+
}
|
|
378
|
+
if (lastMessage.role === 'assistant' && message.role === 'assistant') {
|
|
379
|
+
lastMessage.content.push(...message.content)
|
|
380
|
+
continue
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
aggregated.push(message)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Neutralize any lone UTF-16 surrogates before the messages reach the provider.
|
|
387
|
+
// These are mutated in place; every aggregated message is a fresh clone (see
|
|
388
|
+
// convertToolMessage), so the caller's message history is unaffected.
|
|
389
|
+
for (const message of aggregated) {
|
|
390
|
+
if (typeof message.content === 'string') {
|
|
391
|
+
message.content = toWellFormedString(message.content)
|
|
392
|
+
} else {
|
|
393
|
+
wellFormStringsInPlace(message.content)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (!includeCacheControl) {
|
|
398
|
+
return aggregated
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Add cache control to specific messages (max of 4 can be marked for caching!):
|
|
402
|
+
// - The message right before the three tagged messages
|
|
403
|
+
// - Last message
|
|
404
|
+
for (const tag of [
|
|
405
|
+
'LAST_ASSISTANT_MESSAGE',
|
|
406
|
+
'USER_PROMPT',
|
|
407
|
+
'STEP_PROMPT',
|
|
408
|
+
undefined, // Last message
|
|
409
|
+
] as const) {
|
|
410
|
+
let index =
|
|
411
|
+
tag === 'LAST_ASSISTANT_MESSAGE'
|
|
412
|
+
? aggregated.findLastIndex((m) => m.role === 'assistant')
|
|
413
|
+
: tag
|
|
414
|
+
? aggregated.findLastIndex((m) => m.tags?.includes(tag))
|
|
415
|
+
: aggregated.length
|
|
416
|
+
if (index <= 0) {
|
|
417
|
+
continue
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Iterate to find the last "valid" message that we can cache control
|
|
421
|
+
let prevMessage: (typeof aggregated)[number]
|
|
422
|
+
let contentBlock: (typeof prevMessage)['content']
|
|
423
|
+
addCacheControlLoop: while (true) {
|
|
424
|
+
index--
|
|
425
|
+
|
|
426
|
+
// No message found
|
|
427
|
+
if (index < 0) {
|
|
428
|
+
break
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
prevMessage = aggregated[index]
|
|
432
|
+
contentBlock = prevMessage.content
|
|
433
|
+
|
|
434
|
+
if (typeof contentBlock === 'string') {
|
|
435
|
+
// This must be a system message
|
|
436
|
+
aggregated[index] = withCacheControl(aggregated[index])
|
|
437
|
+
break
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Iterate to find the last valid content part (not a very short string)
|
|
441
|
+
let lastContentIndex = contentBlock.length
|
|
442
|
+
let lastContentPart: (typeof contentBlock)[number]
|
|
443
|
+
while (true) {
|
|
444
|
+
lastContentIndex--
|
|
445
|
+
lastContentPart = contentBlock[lastContentIndex]
|
|
446
|
+
|
|
447
|
+
if (lastContentIndex < 0) {
|
|
448
|
+
// Continue searching in next message
|
|
449
|
+
break
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (lastContentPart.type !== 'text') {
|
|
453
|
+
contentBlock[lastContentIndex] = withCacheControl(
|
|
454
|
+
contentBlock[lastContentIndex],
|
|
455
|
+
)
|
|
456
|
+
break addCacheControlLoop
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
prevMessage.content = [
|
|
460
|
+
...contentBlock.slice(0, lastContentIndex),
|
|
461
|
+
withCacheControl(lastContentPart),
|
|
462
|
+
...contentBlock.slice(lastContentIndex + 1),
|
|
463
|
+
] as typeof contentBlock
|
|
464
|
+
|
|
465
|
+
break addCacheControlLoop
|
|
466
|
+
}
|
|
467
|
+
break
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Validate each message against the AI SDK schema
|
|
472
|
+
for (let i = 0; i < aggregated.length; i++) {
|
|
473
|
+
const message = aggregated[i]
|
|
474
|
+
const result = modelMessageSchema.safeParse(message)
|
|
475
|
+
if (!result.success) {
|
|
476
|
+
if (logger) {
|
|
477
|
+
logger.error(
|
|
478
|
+
{ message, aggregated, error: result.error },
|
|
479
|
+
`convertCbToModelMessages: Message at index ${i} failed schema validation.`,
|
|
480
|
+
)
|
|
481
|
+
}
|
|
482
|
+
throw new Error(
|
|
483
|
+
`convertCbToModelMessages: Message at index ${i} failed schema validation.\n` +
|
|
484
|
+
`Role: ${message.role}\n` +
|
|
485
|
+
`Message:\n${result.error.message}`,
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return aggregated
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Drop local tool calls that were not answered before the next user/system
|
|
495
|
+
* boundary or the end of history.
|
|
496
|
+
*
|
|
497
|
+
* AI SDK validates that boundary before making a request. A result later in
|
|
498
|
+
* history cannot cross a user/system boundary to answer an interrupted call.
|
|
499
|
+
* Provider-executed calls need no local answer.
|
|
500
|
+
*
|
|
501
|
+
* Tool messages themselves are preserved: the SDK intentionally supports
|
|
502
|
+
* freestanding results from user-run commands between prompts. The web API's
|
|
503
|
+
* wire-format sanitizer handles provider-specific orphan and duplicate rules.
|
|
504
|
+
*/
|
|
505
|
+
export function dropUnansweredToolCalls(messages: Message[]): Message[] {
|
|
506
|
+
const pending = new Map<string, ToolCallPart[]>()
|
|
507
|
+
const unanswered = new Set<ToolCallPart>()
|
|
508
|
+
const flushPending = () => {
|
|
509
|
+
for (const calls of pending.values()) {
|
|
510
|
+
for (const call of calls) unanswered.add(call)
|
|
511
|
+
}
|
|
512
|
+
pending.clear()
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Mirror AI SDK's validator: assistant calls add pending ids, tool messages
|
|
516
|
+
// resolve them, and a user/system boundary (or end of history) requires the
|
|
517
|
+
// pending set to be empty.
|
|
518
|
+
for (const message of messages) {
|
|
519
|
+
if (message.role === 'assistant' && Array.isArray(message.content)) {
|
|
520
|
+
for (const part of message.content) {
|
|
521
|
+
if (part.type !== 'tool-call' || part.providerExecuted === true) continue
|
|
522
|
+
const calls = pending.get(part.toolCallId)
|
|
523
|
+
if (calls) calls.push(part)
|
|
524
|
+
else pending.set(part.toolCallId, [part])
|
|
525
|
+
}
|
|
526
|
+
} else if (message.role === 'tool') {
|
|
527
|
+
pending.delete(message.toolCallId)
|
|
528
|
+
} else if (message.role === 'user' || message.role === 'system') {
|
|
529
|
+
flushPending()
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
flushPending()
|
|
533
|
+
|
|
534
|
+
if (unanswered.size === 0) return messages
|
|
535
|
+
|
|
536
|
+
return messages.flatMap((message) => {
|
|
537
|
+
if (message.role !== 'assistant' || !Array.isArray(message.content)) {
|
|
538
|
+
return [message]
|
|
539
|
+
}
|
|
540
|
+
const content = message.content.filter(
|
|
541
|
+
(part) => part.type !== 'tool-call' || !unanswered.has(part),
|
|
542
|
+
)
|
|
543
|
+
return content.length > 0 ? [{ ...message, content }] : []
|
|
544
|
+
})
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// type NoContent<T> = T & { content?: never }
|
|
548
|
+
export type SystemContent =
|
|
549
|
+
| string
|
|
550
|
+
| SystemMessage['content'][number]
|
|
551
|
+
| SystemMessage['content']
|
|
552
|
+
export function systemContent(
|
|
553
|
+
content: SystemContent,
|
|
554
|
+
): SystemMessage['content'] {
|
|
555
|
+
if (typeof content === 'string') {
|
|
556
|
+
return [{ type: 'text', text: content }]
|
|
557
|
+
}
|
|
558
|
+
if (Array.isArray(content)) {
|
|
559
|
+
return content
|
|
560
|
+
}
|
|
561
|
+
return [content]
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export function systemMessage(
|
|
565
|
+
params:
|
|
566
|
+
| SystemContent
|
|
567
|
+
| ({
|
|
568
|
+
content: SystemContent
|
|
569
|
+
} & Omit<SystemMessage, 'role' | 'content'>),
|
|
570
|
+
): SystemMessage {
|
|
571
|
+
if (typeof params === 'object' && 'content' in params) {
|
|
572
|
+
return {
|
|
573
|
+
...params,
|
|
574
|
+
role: 'system',
|
|
575
|
+
content: systemContent(params.content),
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
role: 'system',
|
|
580
|
+
content: systemContent(params),
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export type UserContent =
|
|
585
|
+
| string
|
|
586
|
+
| UserMessage['content'][number]
|
|
587
|
+
| UserMessage['content']
|
|
588
|
+
export function userContent(content: UserContent): UserMessage['content'] {
|
|
589
|
+
if (typeof content === 'string') {
|
|
590
|
+
return [{ type: 'text', text: content }]
|
|
591
|
+
}
|
|
592
|
+
if (Array.isArray(content)) {
|
|
593
|
+
return content
|
|
594
|
+
}
|
|
595
|
+
return [content]
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export function userMessage(
|
|
599
|
+
params:
|
|
600
|
+
| UserContent
|
|
601
|
+
| ({
|
|
602
|
+
content: UserContent
|
|
603
|
+
} & Omit<UserMessage, 'role' | 'content'>),
|
|
604
|
+
): UserMessage {
|
|
605
|
+
if (typeof params === 'object' && 'content' in params) {
|
|
606
|
+
return {
|
|
607
|
+
...params,
|
|
608
|
+
role: 'user',
|
|
609
|
+
content: userContent(params.content),
|
|
610
|
+
sentAt: Date.now(),
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
role: 'user',
|
|
615
|
+
content: userContent(params),
|
|
616
|
+
sentAt: Date.now(),
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export type AssistantContent =
|
|
621
|
+
| string
|
|
622
|
+
| AssistantMessage['content'][number]
|
|
623
|
+
| AssistantMessage['content']
|
|
624
|
+
export function assistantContent(
|
|
625
|
+
content: AssistantContent,
|
|
626
|
+
): AssistantMessage['content'] {
|
|
627
|
+
if (typeof content === 'string') {
|
|
628
|
+
return [{ type: 'text', text: content }]
|
|
629
|
+
}
|
|
630
|
+
if (Array.isArray(content)) {
|
|
631
|
+
return content
|
|
632
|
+
}
|
|
633
|
+
return [content]
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
export function assistantMessage(
|
|
637
|
+
params:
|
|
638
|
+
| AssistantContent
|
|
639
|
+
| ({
|
|
640
|
+
content: AssistantContent
|
|
641
|
+
} & Omit<AssistantMessage, 'role' | 'content'>),
|
|
642
|
+
): AssistantMessage {
|
|
643
|
+
if (typeof params === 'object' && 'content' in params) {
|
|
644
|
+
return {
|
|
645
|
+
...params,
|
|
646
|
+
role: 'assistant',
|
|
647
|
+
content: assistantContent(params.content),
|
|
648
|
+
sentAt: Date.now(),
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
role: 'assistant',
|
|
653
|
+
content: assistantContent(params),
|
|
654
|
+
sentAt: Date.now(),
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
export function jsonToolResult<T extends JSONValue>(
|
|
659
|
+
value: T,
|
|
660
|
+
): [
|
|
661
|
+
Extract<ToolResultOutput, { type: 'json' }> & {
|
|
662
|
+
value: T
|
|
663
|
+
},
|
|
664
|
+
] {
|
|
665
|
+
return [
|
|
666
|
+
{
|
|
667
|
+
type: 'json',
|
|
668
|
+
value,
|
|
669
|
+
},
|
|
670
|
+
]
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
export function mediaToolResult(params: {
|
|
674
|
+
data: string
|
|
675
|
+
mediaType: string
|
|
676
|
+
}): [Extract<ToolResultOutput, { type: 'media' }>] {
|
|
677
|
+
const { data, mediaType } = params
|
|
678
|
+
return [
|
|
679
|
+
{
|
|
680
|
+
type: 'media',
|
|
681
|
+
data,
|
|
682
|
+
mediaType,
|
|
683
|
+
},
|
|
684
|
+
]
|
|
685
|
+
}
|