@alumbwe/anvil 1.0.20 → 1.0.21
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 +6 -2
- 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,351 @@
|
|
|
1
|
+
# Testing Patterns Guide
|
|
2
|
+
|
|
3
|
+
This guide documents best practices for writing tests in the Anvil codebase, based on lessons learned from buffbench runs and production issues.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Mock Cleanup](#mock-cleanup)
|
|
8
|
+
2. [Type-Safe Mocks](#type-safe-mocks)
|
|
9
|
+
3. [Assertion Best Practices](#assertion-best-practices)
|
|
10
|
+
4. [Test Isolation](#test-isolation)
|
|
11
|
+
5. [Common Patterns](#common-patterns)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Mock Cleanup
|
|
16
|
+
|
|
17
|
+
### ❌ DON'T: Use `afterAll` for mock restoration
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// BAD: Mocks leak between tests
|
|
21
|
+
afterAll(() => {
|
|
22
|
+
mockSpy.mockRestore()
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### ✅ DO: Use `afterEach` for mock restoration
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// GOOD: Each test starts with clean state
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
mockSpy.mockRestore()
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Why**: `afterAll` runs only once after all tests complete. If one test modifies mock behavior, subsequent tests inherit that state, causing flaky tests and hard-to-debug failures.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Type-Safe Mocks
|
|
40
|
+
|
|
41
|
+
### ❌ DON'T: Use `as any` casts for mocks
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// BAD: Type safety lost, bugs hide
|
|
45
|
+
spyOn(db, 'insert').mockReturnValue({
|
|
46
|
+
values: mock(() => Promise.resolve({ id: 'test-id' })),
|
|
47
|
+
} as any)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### ✅ DO: Use typed mock factories
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// GOOD: Type-safe, reusable, documented
|
|
54
|
+
import { setupDbSpies } from '@anvil/common/testing/mocks'
|
|
55
|
+
|
|
56
|
+
const dbSpies = setupDbSpies(db, { defaultInsertId: 'test-id' })
|
|
57
|
+
// dbSpies.insert is properly typed
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Available Mock Factories
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import {
|
|
64
|
+
// Logger mocks
|
|
65
|
+
createMockLogger,
|
|
66
|
+
createMockLoggerWithCapture,
|
|
67
|
+
|
|
68
|
+
// Analytics mocks
|
|
69
|
+
createMockAnalytics,
|
|
70
|
+
setupAnalyticsMocks,
|
|
71
|
+
|
|
72
|
+
// Database mocks
|
|
73
|
+
setupDbSpies,
|
|
74
|
+
createMockDbOperations,
|
|
75
|
+
|
|
76
|
+
// Crypto mocks
|
|
77
|
+
setupCryptoMocks,
|
|
78
|
+
createMockUuid,
|
|
79
|
+
|
|
80
|
+
// Stream mocks
|
|
81
|
+
createToolCallChunk,
|
|
82
|
+
createMockStream,
|
|
83
|
+
} from '@anvil/common/testing/mocks'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Assertion Best Practices
|
|
89
|
+
|
|
90
|
+
### ❌ DON'T: Assert on raw strings with formatting
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// BAD: Brittle to whitespace/format changes
|
|
94
|
+
expect(JSON.stringify(result)).toContain('"role":"assistant"')
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### ✅ DO: Parse JSON and assert on structured fields
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// GOOD: Robust to formatting changes
|
|
101
|
+
const parsed = JSON.parse(result)
|
|
102
|
+
expect(parsed.role).toBe('assistant')
|
|
103
|
+
expect(parsed.content).toHaveLength(1)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### ❌ DON'T: Use substring checks for role validation
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// BAD: False positives possible
|
|
110
|
+
expect(serializedHistory).toContain('assistant')
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### ✅ DO: Check exact field values
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// GOOD: Precise and reliable
|
|
117
|
+
expect(messages.some((m) => m.role === 'assistant')).toBe(true)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Test Isolation
|
|
123
|
+
|
|
124
|
+
### ❌ DON'T: Share mutable state between tests
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// BAD: Tests affect each other
|
|
128
|
+
let sharedState = { count: 0 }
|
|
129
|
+
|
|
130
|
+
it('test 1', () => {
|
|
131
|
+
sharedState.count++
|
|
132
|
+
expect(sharedState.count).toBe(1)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('test 2', () => {
|
|
136
|
+
// Fails if test 1 runs first!
|
|
137
|
+
expect(sharedState.count).toBe(0)
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### ✅ DO: Reset state in `beforeEach`
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// GOOD: Each test has fresh state
|
|
145
|
+
let state: { count: number }
|
|
146
|
+
|
|
147
|
+
beforeEach(() => {
|
|
148
|
+
state = { count: 0 }
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('test 1', () => {
|
|
152
|
+
state.count++
|
|
153
|
+
expect(state.count).toBe(1)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('test 2', () => {
|
|
157
|
+
expect(state.count).toBe(0) // Works!
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Common Patterns
|
|
164
|
+
|
|
165
|
+
### Testing with Mock Logger
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { createMockLoggerWithCapture } from '@anvil/common/testing/mocks'
|
|
169
|
+
|
|
170
|
+
describe('myFunction', () => {
|
|
171
|
+
it('logs errors appropriately', async () => {
|
|
172
|
+
const { logger, getByLevel } = createMockLoggerWithCapture()
|
|
173
|
+
|
|
174
|
+
await myFunction({ logger })
|
|
175
|
+
|
|
176
|
+
const errors = getByLevel('error')
|
|
177
|
+
expect(errors).toHaveLength(0) // No errors logged
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Testing with Mock Analytics
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { setupAnalyticsMocks } from '@anvil/common/testing/mocks'
|
|
186
|
+
import * as analytics from '@anvil/common/analytics'
|
|
187
|
+
|
|
188
|
+
describe('tracking', () => {
|
|
189
|
+
let analyticsSpy: AnalyticsSpies
|
|
190
|
+
|
|
191
|
+
beforeEach(() => {
|
|
192
|
+
analyticsSpy = setupAnalyticsMocks(analytics)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
afterEach(() => {
|
|
196
|
+
analyticsSpy.restore()
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('tracks the event', async () => {
|
|
200
|
+
await doSomething()
|
|
201
|
+
expect(analyticsSpy.trackEvent).toHaveBeenCalledWith('something_done', {
|
|
202
|
+
prop: 'value',
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Testing with Deterministic UUIDs
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { setupCryptoMocks } from '@anvil/common/testing/mocks'
|
|
212
|
+
|
|
213
|
+
describe('ID generation', () => {
|
|
214
|
+
let cryptoSpies: CryptoMockSpies
|
|
215
|
+
|
|
216
|
+
beforeEach(() => {
|
|
217
|
+
cryptoSpies = setupCryptoMocks({ prefix: 'test', sequential: true })
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
afterEach(() => {
|
|
221
|
+
cryptoSpies.restore()
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('creates items with sequential IDs', async () => {
|
|
225
|
+
const item1 = await createItem()
|
|
226
|
+
const item2 = await createItem()
|
|
227
|
+
|
|
228
|
+
expect(item1.id).toBe('test-0000-0000-0000-000000000000')
|
|
229
|
+
expect(item2.id).toBe('test-0000-0000-0000-000000000001')
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Testing LLM Streams
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import {
|
|
238
|
+
createMockStream,
|
|
239
|
+
createTextChunk,
|
|
240
|
+
createToolCallChunk,
|
|
241
|
+
collectStreamChunks,
|
|
242
|
+
} from '@anvil/common/testing/mocks'
|
|
243
|
+
|
|
244
|
+
describe('stream processing', () => {
|
|
245
|
+
it('handles tool calls', async () => {
|
|
246
|
+
const stream = createMockStream([
|
|
247
|
+
createTextChunk('Analyzing...'),
|
|
248
|
+
createToolCallChunk('read_files', { paths: ['test.ts'] }),
|
|
249
|
+
createTextChunk('Done!'),
|
|
250
|
+
createToolCallChunk('end_turn', {}),
|
|
251
|
+
])
|
|
252
|
+
|
|
253
|
+
const { chunks } = await collectStreamChunks(stream)
|
|
254
|
+
|
|
255
|
+
const toolCalls = chunks.filter((c) => c.type === 'tool-call')
|
|
256
|
+
expect(toolCalls).toHaveLength(2)
|
|
257
|
+
expect(toolCalls[0].toolName).toBe('read_files')
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Testing Database Operations
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { setupDbSpies } from '@anvil/common/testing/mocks'
|
|
266
|
+
import db from '@anvil/internal/db'
|
|
267
|
+
|
|
268
|
+
describe('data layer', () => {
|
|
269
|
+
let dbSpies: DbSpies
|
|
270
|
+
|
|
271
|
+
beforeEach(() => {
|
|
272
|
+
dbSpies = setupDbSpies(db, { defaultInsertId: 'new-record-id' })
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
afterEach(() => {
|
|
276
|
+
dbSpies.restore()
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('inserts a new record', async () => {
|
|
280
|
+
const result = await createRecord({ name: 'Test' })
|
|
281
|
+
|
|
282
|
+
expect(dbSpies.insert).toHaveBeenCalled()
|
|
283
|
+
expect(result.id).toBe('new-record-id')
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Additional Lessons from Buffbench
|
|
291
|
+
|
|
292
|
+
### Cross-Browser Styles
|
|
293
|
+
|
|
294
|
+
When adding custom scrollbar styles, always include Firefox support:
|
|
295
|
+
|
|
296
|
+
```css
|
|
297
|
+
/* WebKit (Chrome, Safari, Edge) */
|
|
298
|
+
::-webkit-scrollbar {
|
|
299
|
+
width: 6px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Firefox */
|
|
303
|
+
scrollbar-width: thin;
|
|
304
|
+
scrollbar-color: hsl(var(--border) / 0.6) transparent;
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Duplicate Code Detection
|
|
308
|
+
|
|
309
|
+
Before adding utility functions, search for existing implementations:
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
# Search for similar functions
|
|
313
|
+
rg "filterOutSystemRole\|filterSystem" --type ts
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Shared Mock File Context
|
|
317
|
+
|
|
318
|
+
Don't duplicate mock file context creators. Use the shared one:
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import { mockFileContext } from '@anvil/common/testing/fixtures/agent-runtime'
|
|
322
|
+
|
|
323
|
+
// Don't create a new one in each test file
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Error Path Coverage
|
|
327
|
+
|
|
328
|
+
Always add tests for error scenarios:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
it('handles API errors gracefully', async () => {
|
|
332
|
+
mockApi.mockRejectedValueOnce(new Error('Network error'))
|
|
333
|
+
|
|
334
|
+
const result = await fetchData()
|
|
335
|
+
|
|
336
|
+
expect(result.error).toBe('Network error')
|
|
337
|
+
})
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Migration Checklist
|
|
343
|
+
|
|
344
|
+
When updating tests to use these patterns:
|
|
345
|
+
|
|
346
|
+
1. [ ] Replace `as any` casts with typed mock factories
|
|
347
|
+
2. [ ] Move mock restoration from `afterAll` to `afterEach`
|
|
348
|
+
3. [ ] Replace string assertions with structured assertions
|
|
349
|
+
4. [ ] Use shared fixtures instead of duplicating mock data
|
|
350
|
+
5. [ ] Add error path coverage if missing
|
|
351
|
+
6. [ ] Ensure deterministic IDs with `setupCryptoMocks`
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One rule, checked the same way on every surface: **if a picker can OFFER a
|
|
3
|
+
* anvil model row, every gate that row then passes through must accept it.**
|
|
4
|
+
*
|
|
5
|
+
* Anvil model ids fan out across several hand-kept lists — the per-tier
|
|
6
|
+
* picker catalogs, the session-admission sets, the free-mode agent allowlist,
|
|
7
|
+
* and each surface's own request validator. They are separate on purpose (a
|
|
8
|
+
* model can be retired from a picker while live sessions keep running on it),
|
|
9
|
+
* so nothing stops one from moving without the others. When they drift in the
|
|
10
|
+
* offer→gate direction the user sees a row they cannot use: Anvil Desktop
|
|
11
|
+
* shipped the earned GLM 5.2 row while its `/api/thread/:id/agent` route still
|
|
12
|
+
* validated against `ANVIL_MODELS` (which excludes GLM by design), so every
|
|
13
|
+
* user who earned the reward got 400 "invalid model" when they picked it.
|
|
14
|
+
*
|
|
15
|
+
* Drift the OTHER way is fine and deliberate — an id that is acceptable but not
|
|
16
|
+
* offered is exactly how a staged retirement works — so this only ever checks
|
|
17
|
+
* offered ⊆ accepted.
|
|
18
|
+
*
|
|
19
|
+
* Each surface owns its offer set (they live in the desktop/cli/web packages,
|
|
20
|
+
* which `common` cannot import), so each calls `anvilOfferViolations` from
|
|
21
|
+
* its own test with its own rows. Keeping the CHECKS here is what makes a new
|
|
22
|
+
* gate apply to every surface at once.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
FREE_MODE_AGENT_MODELS,
|
|
27
|
+
ANVIL_ROOT_AGENT_IDS,
|
|
28
|
+
} from '../constants/free-agents'
|
|
29
|
+
import {
|
|
30
|
+
getAnvilModel,
|
|
31
|
+
getAnvilWebModel,
|
|
32
|
+
isAnvilSessionModelAllowedForAccessTier,
|
|
33
|
+
isAnvilSessionModelId,
|
|
34
|
+
resolveAnvilSessionModelForAccessTier,
|
|
35
|
+
} from '../constants/anvil-models'
|
|
36
|
+
|
|
37
|
+
import type { AnvilAccessTier } from '../constants/anvil-models'
|
|
38
|
+
|
|
39
|
+
/** Which catalog the surface reads a row's name/tagline/badges out of. A
|
|
40
|
+
* surface that renders through the wrong one shows a fallback model's label on
|
|
41
|
+
* the row (both getters degrade to DeepSeek Flash rather than throwing). */
|
|
42
|
+
export type AnvilCatalogSource = 'supported' | 'web'
|
|
43
|
+
|
|
44
|
+
export interface AnvilOfferSurface {
|
|
45
|
+
/** Names the surface in failure messages, e.g. 'anvil-desktop picker (full)'. */
|
|
46
|
+
surface: string
|
|
47
|
+
/** The tier whose rows `offered` describes — gates are tier-sensitive. */
|
|
48
|
+
accessTier: AnvilAccessTier
|
|
49
|
+
/** Every model id this surface can put in front of a user on that tier,
|
|
50
|
+
* INCLUDING earned/unlocked rows like the GLM referral reward. */
|
|
51
|
+
offered: readonly string[]
|
|
52
|
+
/** The surface's own request validator, if it has one (the desktop route's
|
|
53
|
+
* `isModelForHarness`, the web picker's selectable filter, …). */
|
|
54
|
+
accepts?: (model: string) => boolean
|
|
55
|
+
/** The free-mode root agent(s) this surface's turns run under. One id, or
|
|
56
|
+
* several when the surface has per-execution-mode roots. */
|
|
57
|
+
rootAgentIdFor?: (model: string) => string | readonly string[]
|
|
58
|
+
/** Catalog the surface renders from. Omit to skip the label check. */
|
|
59
|
+
catalog?: AnvilCatalogSource
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function rootAgentIds(
|
|
63
|
+
surface: AnvilOfferSurface,
|
|
64
|
+
model: string,
|
|
65
|
+
): readonly string[] {
|
|
66
|
+
if (!surface.rootAgentIdFor) return []
|
|
67
|
+
const ids = surface.rootAgentIdFor(model)
|
|
68
|
+
return typeof ids === 'string' ? [ids] : ids
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Every way `model` is offerable-but-unusable on this surface, as sentences a
|
|
73
|
+
* failing test can print verbatim. Empty array means the row is good.
|
|
74
|
+
*/
|
|
75
|
+
function violationsForModel(
|
|
76
|
+
surface: AnvilOfferSurface,
|
|
77
|
+
model: string,
|
|
78
|
+
): string[] {
|
|
79
|
+
const where = `${surface.surface}: ${model}`
|
|
80
|
+
const out: string[] = []
|
|
81
|
+
|
|
82
|
+
// 1. the session layer has to know the id at all, on any tier
|
|
83
|
+
if (!isAnvilSessionModelId(model)) {
|
|
84
|
+
// everything below is derived from the same catalogs, so one unknown id
|
|
85
|
+
// would otherwise produce five near-identical lines
|
|
86
|
+
return [`${where} is offered but is not a anvil session model id`]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 2. …and admit it for THIS tier, or admission answers model_not_allowed
|
|
90
|
+
if (!isAnvilSessionModelAllowedForAccessTier(model, surface.accessTier)) {
|
|
91
|
+
out.push(
|
|
92
|
+
`${where} is offered to the ${surface.accessTier} tier, which session admission does not allow it on`,
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 3. a resolver that silently swaps the pick is as bad as a refusal: the user
|
|
97
|
+
// picks GLM, the turn runs on DeepSeek, and nothing says why
|
|
98
|
+
const resolved = resolveAnvilSessionModelForAccessTier(
|
|
99
|
+
model,
|
|
100
|
+
surface.accessTier,
|
|
101
|
+
)
|
|
102
|
+
if (resolved !== model) {
|
|
103
|
+
out.push(
|
|
104
|
+
`${where} is offered but resolveAnvilSessionModelForAccessTier coerces it to ${resolved}`,
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 4. the surface's own validator — the check the GLM 400 needed
|
|
109
|
+
if (surface.accepts && !surface.accepts(model)) {
|
|
110
|
+
out.push(`${where} is offered but the surface's own validator rejects it`)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 5. free mode charges/403s per (agent, model) pair, so the root this
|
|
114
|
+
// surface runs turns under must allow the model, and must itself be a
|
|
115
|
+
// registered root or its subagents 403 with free_mode_invalid_agent_hierarchy
|
|
116
|
+
const roots = rootAgentIds(surface, model)
|
|
117
|
+
if (surface.rootAgentIdFor && roots.length === 0) {
|
|
118
|
+
// a resolver that answers "no root" for an offered model is a wiring error, and
|
|
119
|
+
// silently running no root checks is exactly how this whole class of bug hides
|
|
120
|
+
out.push(`${where} is offered but resolves to no free-mode root agent`)
|
|
121
|
+
}
|
|
122
|
+
for (const rootId of roots) {
|
|
123
|
+
const allowed = FREE_MODE_AGENT_MODELS[rootId]
|
|
124
|
+
if (!allowed) {
|
|
125
|
+
out.push(
|
|
126
|
+
`${where} runs under free-mode root ${rootId}, which is not in FREE_MODE_AGENT_MODELS`,
|
|
127
|
+
)
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
if (!allowed.has(model)) {
|
|
131
|
+
out.push(
|
|
132
|
+
`${where} runs under free-mode root ${rootId}, whose allowlist does not include it (free_mode_invalid_agent_model)`,
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
if (!ANVIL_ROOT_AGENT_IDS.some((id) => id === rootId)) {
|
|
136
|
+
out.push(
|
|
137
|
+
`${where} runs under free-mode root ${rootId}, which is missing from ANVIL_ROOT_AGENT_IDS (its subagents would 403)`,
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 6. the row has to render as itself rather than as the fallback's label
|
|
143
|
+
if (surface.catalog) {
|
|
144
|
+
const row =
|
|
145
|
+
surface.catalog === 'web'
|
|
146
|
+
? getAnvilWebModel(model)
|
|
147
|
+
: getAnvilModel(model)
|
|
148
|
+
if (row.id !== model) {
|
|
149
|
+
out.push(
|
|
150
|
+
`${where} is offered but the ${surface.catalog} catalog has no row for it, so it renders as ${row.id}`,
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return out
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Every offer→gate violation on a surface. Assert `toEqual([])`: the strings
|
|
159
|
+
* are the failure message. */
|
|
160
|
+
export function anvilOfferViolations(
|
|
161
|
+
surface: AnvilOfferSurface,
|
|
162
|
+
): string[] {
|
|
163
|
+
// an empty offer set passes every check below while meaning the test wired
|
|
164
|
+
// itself to the wrong list, which is the one failure mode a green run hides
|
|
165
|
+
if (surface.offered.length === 0) {
|
|
166
|
+
return [`${surface.surface}: offers no models at all — check the test wiring`]
|
|
167
|
+
}
|
|
168
|
+
return surface.offered.flatMap((model) => violationsForModel(surface, model))
|
|
169
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared error helper utilities for tests.
|
|
3
|
+
* These provide typed ways to create errors with additional properties
|
|
4
|
+
* like `code` (for Node.js filesystem errors) and `constraint` (for Postgres errors).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface NodeError extends Error {
|
|
8
|
+
code?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PostgresError extends Error {
|
|
12
|
+
code: string
|
|
13
|
+
constraint?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const createNodeError = (message: string, code: string): NodeError => {
|
|
17
|
+
const error: NodeError = new Error(message)
|
|
18
|
+
error.code = code
|
|
19
|
+
return error
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const createPostgresError = (
|
|
23
|
+
message: string,
|
|
24
|
+
code: string,
|
|
25
|
+
constraint?: string,
|
|
26
|
+
): PostgresError => {
|
|
27
|
+
const error = new Error(message) as PostgresError
|
|
28
|
+
error.code = code
|
|
29
|
+
if (constraint !== undefined) {
|
|
30
|
+
error.constraint = constraint
|
|
31
|
+
}
|
|
32
|
+
return error
|
|
33
|
+
}
|