@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,236 @@
|
|
|
1
|
+
import { FILE_READ_STATUS } from '../constants/paths'
|
|
2
|
+
|
|
3
|
+
import type { FileReadWindow } from '../types/contracts/client'
|
|
4
|
+
|
|
5
|
+
/** Maximum source characters returned by one read_files tool call. */
|
|
6
|
+
export const MAX_READ_FILES_CHARS = 100_000
|
|
7
|
+
|
|
8
|
+
/** Maximum estimated tokens returned by one read_files tool call. */
|
|
9
|
+
export const MAX_READ_FILES_TOKENS = 20_000
|
|
10
|
+
|
|
11
|
+
/** Maximum lines returned per file in one read_files tool call. */
|
|
12
|
+
export const MAX_READ_FILE_LINES = 2_000
|
|
13
|
+
|
|
14
|
+
/** Maximum characters returned per file in one read_files tool call. */
|
|
15
|
+
export const MAX_READ_FILE_CHARS = 50_000
|
|
16
|
+
|
|
17
|
+
export function windowFileRead(
|
|
18
|
+
content: string,
|
|
19
|
+
offset?: number,
|
|
20
|
+
limit?: number,
|
|
21
|
+
): string {
|
|
22
|
+
const lines = content.split('\n')
|
|
23
|
+
if (lines.length > 1 && lines[lines.length - 1] === '') lines.pop()
|
|
24
|
+
const totalLines = lines.length
|
|
25
|
+
const start = Math.max(1, Math.floor(offset ?? 1))
|
|
26
|
+
const maxLines = Math.min(
|
|
27
|
+
Math.max(1, Math.floor(limit ?? MAX_READ_FILE_LINES)),
|
|
28
|
+
MAX_READ_FILE_LINES,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if (start > totalLines) {
|
|
32
|
+
return `[read_files: ${totalLines} lines total; offset ${start} is beyond the end of the file.]`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let end = Math.min(totalLines, start - 1 + maxLines)
|
|
36
|
+
let selected = lines.slice(start - 1, end).join('\n')
|
|
37
|
+
let charCapped = false
|
|
38
|
+
if (selected.length > MAX_READ_FILE_CHARS) {
|
|
39
|
+
charCapped = true
|
|
40
|
+
let chars = 0
|
|
41
|
+
let count = 0
|
|
42
|
+
for (let i = start - 1; i < end; i++) {
|
|
43
|
+
const lineLength = lines[i].length + (count > 0 ? 1 : 0)
|
|
44
|
+
if (chars + lineLength > MAX_READ_FILE_CHARS && count > 0) break
|
|
45
|
+
chars += lineLength
|
|
46
|
+
count++
|
|
47
|
+
}
|
|
48
|
+
end = start - 1 + count
|
|
49
|
+
selected = lines.slice(start - 1, end).join('\n')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (start === 1 && end === totalLines) {
|
|
53
|
+
return content
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const charCapNote = charCapped
|
|
57
|
+
? ` The window was shortened to stay under ${MAX_READ_FILE_CHARS.toLocaleString()} characters.`
|
|
58
|
+
: ''
|
|
59
|
+
const continueNote =
|
|
60
|
+
end < totalLines
|
|
61
|
+
? ` Use code_search to locate the part you need and read a window around it, or call read_files again with offset=${end + 1} to continue.`
|
|
62
|
+
: ''
|
|
63
|
+
return `${selected}\n\n[read_files: showing lines ${start}-${end} of ${totalLines}.${charCapNote}${continueNote}]`
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type FileReadWindows = Record<string, FileReadWindow[]>
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Read the line windows a `windowedFileReads` agent forwarded with a read_files
|
|
70
|
+
* call, if any. Absent for every other agent, and that absence is what keeps
|
|
71
|
+
* the legacy whole-file behavior.
|
|
72
|
+
*
|
|
73
|
+
* Hosted surfaces (Anvil Web/Cloud on Daytona, and the browser runtime) run
|
|
74
|
+
* read_files through their own override rather than the SDK's local reader, so
|
|
75
|
+
* each has to pick the windows out of the tool input itself.
|
|
76
|
+
*/
|
|
77
|
+
export function fileReadWindowsOf(input: unknown): FileReadWindows | undefined {
|
|
78
|
+
const windows =
|
|
79
|
+
input && typeof input === 'object'
|
|
80
|
+
? (input as { fileWindows?: unknown }).fileWindows
|
|
81
|
+
: undefined
|
|
82
|
+
return windows && typeof windows === 'object' && !Array.isArray(windows)
|
|
83
|
+
? (windows as FileReadWindows)
|
|
84
|
+
: undefined
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Apply one file's windows to its content.
|
|
89
|
+
*
|
|
90
|
+
* Call this BEFORE the read budget (`createFileReadLimiter`), the order the
|
|
91
|
+
* SDK's local reader uses. The other way round, an offset past the budget's
|
|
92
|
+
* truncation point answers "beyond the end of the file" for a file that has
|
|
93
|
+
* those lines.
|
|
94
|
+
*
|
|
95
|
+
* A path with no explicit window still goes through `windowFileRead`, which is
|
|
96
|
+
* where the per-file 2,000-line cap comes from — that cap, more than the
|
|
97
|
+
* explicit offsets, is what a windowed agent saves on a large file.
|
|
98
|
+
*/
|
|
99
|
+
export function applyFileReadWindows(
|
|
100
|
+
content: string,
|
|
101
|
+
windows: FileReadWindow[] | undefined,
|
|
102
|
+
): string {
|
|
103
|
+
// `Array.isArray`, not a truthiness check on `.length`: callers look these up
|
|
104
|
+
// by a model-supplied path in a map that lost its null prototype crossing the
|
|
105
|
+
// wire as JSON, so `windows` can be `Function.prototype` for a file named
|
|
106
|
+
// `constructor` — truthy, with a `.length` of 1 and no `.map`.
|
|
107
|
+
const list = Array.isArray(windows) && windows.length > 0 ? windows : [{}]
|
|
108
|
+
return list
|
|
109
|
+
.map((window) => windowFileRead(content, window?.offset, window?.limit))
|
|
110
|
+
.join('\n\n')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Small chunks avoid pathological BPE runtimes on repetitive Unicode. */
|
|
114
|
+
const TOKEN_CHUNK_CHARS = 1_024
|
|
115
|
+
|
|
116
|
+
type LimitedFileRead = {
|
|
117
|
+
content: string
|
|
118
|
+
includedChars: number
|
|
119
|
+
includedTokens: number
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
type FileReadLimiterOptions = {
|
|
123
|
+
/** A conservative local token estimator, including any desired safety factor. */
|
|
124
|
+
countTokens?: (text: string) => number
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function avoidSplittingSurrogatePair(text: string, end: number): number {
|
|
128
|
+
if (
|
|
129
|
+
end > 0 &&
|
|
130
|
+
end < text.length &&
|
|
131
|
+
text.charCodeAt(end - 1) >= 0xd800 &&
|
|
132
|
+
text.charCodeAt(end - 1) <= 0xdbff
|
|
133
|
+
) {
|
|
134
|
+
return end - 1
|
|
135
|
+
}
|
|
136
|
+
return end
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function limitContentByTokens(
|
|
140
|
+
content: string,
|
|
141
|
+
tokenBudget: number,
|
|
142
|
+
countTokens: (text: string) => number,
|
|
143
|
+
): { chars: number; tokens: number; truncated: boolean } {
|
|
144
|
+
let chars = 0
|
|
145
|
+
let tokens = 0
|
|
146
|
+
|
|
147
|
+
while (chars < content.length) {
|
|
148
|
+
let chunkEnd = Math.min(chars + TOKEN_CHUNK_CHARS, content.length)
|
|
149
|
+
chunkEnd = avoidSplittingSurrogatePair(content, chunkEnd)
|
|
150
|
+
if (chunkEnd === chars) chunkEnd++
|
|
151
|
+
|
|
152
|
+
const chunk = content.slice(chars, chunkEnd)
|
|
153
|
+
const chunkTokens = countTokens(chunk)
|
|
154
|
+
if (tokens + chunkTokens <= tokenBudget) {
|
|
155
|
+
chars = chunkEnd
|
|
156
|
+
tokens += chunkTokens
|
|
157
|
+
continue
|
|
158
|
+
}
|
|
159
|
+
// Keep only complete, independently verified chunks. This can leave less
|
|
160
|
+
// than one chunk of budget unused, but avoids costly exact-prefix fitting.
|
|
161
|
+
return { chars, tokens, truncated: true }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return { chars, tokens, truncated: false }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function limitFileReadContent(
|
|
168
|
+
content: string,
|
|
169
|
+
remainingChars: number,
|
|
170
|
+
remainingTokens: number,
|
|
171
|
+
countTokens?: (text: string) => number,
|
|
172
|
+
): LimitedFileRead {
|
|
173
|
+
const charLimit = Math.min(content.length, Math.max(0, remainingChars))
|
|
174
|
+
const safeCharLimit = avoidSplittingSurrogatePair(content, charLimit)
|
|
175
|
+
const charLimitedContent = content.slice(0, safeCharLimit)
|
|
176
|
+
const tokenBudget = Math.max(0, remainingTokens)
|
|
177
|
+
const tokenLimit = countTokens
|
|
178
|
+
? limitContentByTokens(charLimitedContent, tokenBudget, countTokens)
|
|
179
|
+
: {
|
|
180
|
+
chars: charLimitedContent.length,
|
|
181
|
+
tokens: 0,
|
|
182
|
+
truncated: false,
|
|
183
|
+
}
|
|
184
|
+
const includedChars = tokenLimit.chars
|
|
185
|
+
const includedTokens = tokenLimit.tokens
|
|
186
|
+
|
|
187
|
+
if (includedChars === content.length) {
|
|
188
|
+
return { content, includedChars, includedTokens }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let notice: string
|
|
192
|
+
if (tokenLimit.truncated) {
|
|
193
|
+
const hitAggregateLimit = remainingTokens < MAX_READ_FILES_TOKENS
|
|
194
|
+
notice = hitAggregateLimit
|
|
195
|
+
? `${FILE_READ_STATUS.TOO_LARGE}: The combined read_files output exceeded the ${MAX_READ_FILES_TOKENS.toLocaleString()} estimated-token limit. This file was truncated after ${includedTokens.toLocaleString()} estimated tokens. Read it separately or use code_search for the relevant section.`
|
|
196
|
+
: `${FILE_READ_STATUS.TOO_LARGE}: This file exceeded the ${MAX_READ_FILES_TOKENS.toLocaleString()} estimated-token per-file limit. It was truncated after ${includedTokens.toLocaleString()} estimated tokens. Use code_search or a more targeted read for the relevant section.`
|
|
197
|
+
} else {
|
|
198
|
+
const hitAggregateLimit = remainingChars < MAX_READ_FILES_CHARS
|
|
199
|
+
notice = hitAggregateLimit
|
|
200
|
+
? `${FILE_READ_STATUS.TOO_LARGE}: The combined read_files output exceeded the ${MAX_READ_FILES_CHARS.toLocaleString()} character hard limit. This file was truncated after ${includedChars.toLocaleString()} characters. Read it separately or use code_search for the relevant section.`
|
|
201
|
+
: `${FILE_READ_STATUS.TOO_LARGE}: This file is ${content.length.toLocaleString()} characters, exceeding the ${MAX_READ_FILES_CHARS.toLocaleString()} character hard limit. The content above has been truncated. Use code_search or a more targeted read for the relevant section.`
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
content:
|
|
206
|
+
includedChars === 0
|
|
207
|
+
? notice
|
|
208
|
+
: `${content.slice(0, includedChars)}\n\n${notice}`,
|
|
209
|
+
includedChars,
|
|
210
|
+
includedTokens,
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Creates an ordered limiter for one read_files invocation. Status/error
|
|
216
|
+
* messages should bypass this limiter so they do not consume the content
|
|
217
|
+
* budget.
|
|
218
|
+
*/
|
|
219
|
+
export function createFileReadLimiter(options: FileReadLimiterOptions = {}) {
|
|
220
|
+
let remainingChars = MAX_READ_FILES_CHARS
|
|
221
|
+
let remainingTokens = MAX_READ_FILES_TOKENS
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
limit(content: string): string {
|
|
225
|
+
const limited = limitFileReadContent(
|
|
226
|
+
content,
|
|
227
|
+
remainingChars,
|
|
228
|
+
remainingTokens,
|
|
229
|
+
options.countTokens,
|
|
230
|
+
)
|
|
231
|
+
remainingChars -= limited.includedChars
|
|
232
|
+
remainingTokens -= limited.includedTokens
|
|
233
|
+
return limited.content
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import * as os from 'os'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
|
|
4
|
+
import { z } from 'zod/v4'
|
|
5
|
+
|
|
6
|
+
import type { AnvilFileSystem } from '../types/filesystem'
|
|
7
|
+
import type { SkillsMap } from '../types/skill'
|
|
8
|
+
|
|
9
|
+
export const FileTreeNodeSchema: z.ZodType<FileTreeNode> = z.object({
|
|
10
|
+
name: z.string(),
|
|
11
|
+
type: z.enum(['file', 'directory']),
|
|
12
|
+
children: z.lazy(() => z.array(FileTreeNodeSchema).optional()),
|
|
13
|
+
filePath: z.string(),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export interface FileTreeNode {
|
|
17
|
+
name: string
|
|
18
|
+
type: 'file' | 'directory'
|
|
19
|
+
filePath: string
|
|
20
|
+
lastReadTime?: number
|
|
21
|
+
children?: FileTreeNode[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DirectoryNode extends FileTreeNode {
|
|
25
|
+
type: 'directory'
|
|
26
|
+
children: FileTreeNode[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface FileNode extends FileTreeNode {
|
|
30
|
+
type: 'file'
|
|
31
|
+
lastReadTime: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const FileVersionSchema = z.object({
|
|
35
|
+
path: z.string(),
|
|
36
|
+
content: z.string(),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export type FileVersion = z.infer<typeof FileVersionSchema>
|
|
40
|
+
|
|
41
|
+
export const customToolDefinitionsSchema = z
|
|
42
|
+
.record(
|
|
43
|
+
z.string(),
|
|
44
|
+
z.object({
|
|
45
|
+
// inputSchema can be a Zod schema (from MCP tools) or a JSON Schema object
|
|
46
|
+
// (from SDK custom tools that have been serialized). The agent-runtime
|
|
47
|
+
// converts JSON schemas to Zod using ensureZodSchema() before use.
|
|
48
|
+
inputSchema: z.custom<z.ZodType | Record<string, unknown>>(),
|
|
49
|
+
endsAgentStep: z.boolean().optional().default(false),
|
|
50
|
+
description: z.string().optional(),
|
|
51
|
+
exampleInputs: z.record(z.string(), z.any()).array().optional(),
|
|
52
|
+
}),
|
|
53
|
+
)
|
|
54
|
+
.default(() => ({}))
|
|
55
|
+
export type CustomToolDefinitions = NonNullable<
|
|
56
|
+
z.input<typeof customToolDefinitionsSchema>
|
|
57
|
+
>
|
|
58
|
+
|
|
59
|
+
export const ProjectFileContextSchema = z.object({
|
|
60
|
+
projectRoot: z.string(),
|
|
61
|
+
cwd: z.string(),
|
|
62
|
+
fileTree: z.array(z.custom<FileTreeNode>()),
|
|
63
|
+
fileTokenScores: z.record(z.string(), z.record(z.string(), z.number())),
|
|
64
|
+
tokenCallers: z
|
|
65
|
+
.record(z.string(), z.record(z.string(), z.array(z.string())))
|
|
66
|
+
.optional(),
|
|
67
|
+
knowledgeFiles: z.record(z.string(), z.string()),
|
|
68
|
+
userKnowledgeFiles: z.record(z.string(), z.string()).optional(),
|
|
69
|
+
agentTemplates: z.record(z.string(), z.any()).default(() => ({})),
|
|
70
|
+
customToolDefinitions: customToolDefinitionsSchema,
|
|
71
|
+
skills: z.record(z.string(), z.any()).optional(),
|
|
72
|
+
gitChanges: z.object({
|
|
73
|
+
status: z.string(),
|
|
74
|
+
diff: z.string(),
|
|
75
|
+
diffCached: z.string(),
|
|
76
|
+
lastCommitMessages: z.string(),
|
|
77
|
+
}),
|
|
78
|
+
changesSinceLastChat: z.record(z.string(), z.string()),
|
|
79
|
+
shellConfigFiles: z.record(z.string(), z.string()),
|
|
80
|
+
systemInfo: z.object({
|
|
81
|
+
platform: z.string(),
|
|
82
|
+
shell: z.string(),
|
|
83
|
+
nodeVersion: z.string(),
|
|
84
|
+
arch: z.string(),
|
|
85
|
+
homedir: z.string(),
|
|
86
|
+
cpus: z.number(),
|
|
87
|
+
chromeAvailable: z.boolean(),
|
|
88
|
+
}),
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
export type ProjectFileContext = {
|
|
92
|
+
projectRoot: string
|
|
93
|
+
cwd: string
|
|
94
|
+
fileTree: FileTreeNode[]
|
|
95
|
+
fileTokenScores: Record<string, Record<string, number>>
|
|
96
|
+
tokenCallers?: Record<string, Record<string, string[]>>
|
|
97
|
+
knowledgeFiles: Record<string, string>
|
|
98
|
+
userKnowledgeFiles?: Record<string, string>
|
|
99
|
+
agentTemplates: Record<string, any>
|
|
100
|
+
customToolDefinitions: CustomToolDefinitions
|
|
101
|
+
skills?: SkillsMap
|
|
102
|
+
gitChanges: {
|
|
103
|
+
status: string
|
|
104
|
+
diff: string
|
|
105
|
+
diffCached: string
|
|
106
|
+
lastCommitMessages: string
|
|
107
|
+
}
|
|
108
|
+
changesSinceLastChat: Record<string, string>
|
|
109
|
+
shellConfigFiles: Record<string, string>
|
|
110
|
+
systemInfo: {
|
|
111
|
+
platform: string
|
|
112
|
+
shell: string
|
|
113
|
+
nodeVersion: string
|
|
114
|
+
arch: string
|
|
115
|
+
homedir: string
|
|
116
|
+
cpus: number
|
|
117
|
+
chromeAvailable: boolean
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const fileRegex =
|
|
122
|
+
/<write_file>\s*<path>([^<]+)<\/path>\s*<content>([\s\S]*?)<\/content>\s*<\/write_file>/g
|
|
123
|
+
export const fileWithNoPathRegex = /<write_file>([\s\S]*?)<\/write_file>/g
|
|
124
|
+
|
|
125
|
+
export const parseFileBlocks = (fileBlocks: string) => {
|
|
126
|
+
let fileMatch
|
|
127
|
+
const files: Record<string, string> = {}
|
|
128
|
+
while ((fileMatch = fileRegex.exec(fileBlocks)) !== null) {
|
|
129
|
+
const [, filePath, fileContent] = fileMatch
|
|
130
|
+
files[filePath] = fileContent.startsWith('\n')
|
|
131
|
+
? fileContent.slice(1)
|
|
132
|
+
: fileContent
|
|
133
|
+
}
|
|
134
|
+
return files
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export const getStubProjectFileContext = (): ProjectFileContext => ({
|
|
138
|
+
projectRoot: '',
|
|
139
|
+
cwd: '',
|
|
140
|
+
fileTree: [],
|
|
141
|
+
fileTokenScores: {},
|
|
142
|
+
knowledgeFiles: {},
|
|
143
|
+
userKnowledgeFiles: {},
|
|
144
|
+
agentTemplates: {},
|
|
145
|
+
customToolDefinitions: {},
|
|
146
|
+
skills: {},
|
|
147
|
+
gitChanges: {
|
|
148
|
+
status: '',
|
|
149
|
+
diff: '',
|
|
150
|
+
diffCached: '',
|
|
151
|
+
lastCommitMessages: '',
|
|
152
|
+
},
|
|
153
|
+
changesSinceLastChat: {},
|
|
154
|
+
shellConfigFiles: {},
|
|
155
|
+
systemInfo: {
|
|
156
|
+
platform: '',
|
|
157
|
+
shell: '',
|
|
158
|
+
nodeVersion: '',
|
|
159
|
+
arch: '',
|
|
160
|
+
homedir: '',
|
|
161
|
+
cpus: 0,
|
|
162
|
+
chromeAvailable: false,
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
export const createMarkdownFileBlock = (filePath: string, content: string) => {
|
|
167
|
+
return `\`\`\`${filePath}\n${content}\n\`\`\``
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const parseMarkdownCodeBlock = (content: string) => {
|
|
171
|
+
const match = content.match(/^```(?:[a-zA-Z]+)?\n([\s\S]*)\n```$/)
|
|
172
|
+
if (match) {
|
|
173
|
+
return match[1] + '\n'
|
|
174
|
+
}
|
|
175
|
+
return content
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const createSearchReplaceBlock = (search: string, replace: string) => {
|
|
179
|
+
return `<<<<<<< SEARCH\n${search}\n=======\n${replace}\n>>>>>>> REPLACE`
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function printFileTree(
|
|
183
|
+
nodes: FileTreeNode[],
|
|
184
|
+
depth: number = 0,
|
|
185
|
+
): string {
|
|
186
|
+
let result = ''
|
|
187
|
+
const indentation = ' '.repeat(depth)
|
|
188
|
+
for (const node of nodes) {
|
|
189
|
+
result += `${indentation}${node.name}${node.type === 'directory' ? '/' : ''}\n`
|
|
190
|
+
if (node.type === 'directory' && node.children) {
|
|
191
|
+
result += printFileTree(node.children, depth + 1)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return result
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function printFileTreeWithTokens(
|
|
198
|
+
nodes: FileTreeNode[],
|
|
199
|
+
fileTokenScores: Record<string, Record<string, number>>,
|
|
200
|
+
path: string[] = [],
|
|
201
|
+
): string {
|
|
202
|
+
let result = ''
|
|
203
|
+
const depth = path.length
|
|
204
|
+
const indentToken = ' '
|
|
205
|
+
const indentation = indentToken.repeat(depth)
|
|
206
|
+
const indentationWithFile = indentToken.repeat(depth + 1)
|
|
207
|
+
for (const node of nodes) {
|
|
208
|
+
if (
|
|
209
|
+
node.type === 'directory' &&
|
|
210
|
+
(!node.children || node.children.length === 0)
|
|
211
|
+
) {
|
|
212
|
+
// Skip empty directories
|
|
213
|
+
continue
|
|
214
|
+
}
|
|
215
|
+
result += `${indentation}${node.name}${node.type === 'directory' ? '/' : ''}`
|
|
216
|
+
path.push(node.name)
|
|
217
|
+
const filePath = path.join('/')
|
|
218
|
+
const tokenScores = fileTokenScores[filePath]
|
|
219
|
+
if (node.type === 'file' && tokenScores) {
|
|
220
|
+
const tokens = Object.keys(tokenScores)
|
|
221
|
+
if (tokens.length > 0) {
|
|
222
|
+
result += `\n${indentationWithFile}${tokens.join(' ')}`
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
result += '\n'
|
|
226
|
+
if (node.type === 'directory' && node.children) {
|
|
227
|
+
result += printFileTreeWithTokens(node.children, fileTokenScores, path)
|
|
228
|
+
}
|
|
229
|
+
path.pop()
|
|
230
|
+
}
|
|
231
|
+
return result
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Ensures the given file contents ends with a newline character.
|
|
236
|
+
* @param contents - The file contents
|
|
237
|
+
* @returns the file contents with a newline character.
|
|
238
|
+
*/
|
|
239
|
+
export const ensureEndsWithNewline = (
|
|
240
|
+
contents: string | null,
|
|
241
|
+
): string | null => {
|
|
242
|
+
if (contents === null || contents === '') {
|
|
243
|
+
// Leave empty file as is
|
|
244
|
+
return contents
|
|
245
|
+
}
|
|
246
|
+
if (contents.endsWith('\n')) {
|
|
247
|
+
return contents
|
|
248
|
+
}
|
|
249
|
+
return contents + '\n'
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Node-compatible file existence check.
|
|
254
|
+
* Uses fs.stat instead of Bun-specific fs.exists.
|
|
255
|
+
*/
|
|
256
|
+
export async function fileExists(params: {
|
|
257
|
+
filePath: string
|
|
258
|
+
fs: AnvilFileSystem
|
|
259
|
+
}): Promise<boolean> {
|
|
260
|
+
const { filePath, fs } = params
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
await fs.stat(filePath)
|
|
264
|
+
return true
|
|
265
|
+
} catch {
|
|
266
|
+
return false
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export const ensureDirectoryExists = async (params: {
|
|
271
|
+
baseDir: string
|
|
272
|
+
fs: AnvilFileSystem
|
|
273
|
+
}) => {
|
|
274
|
+
const { baseDir, fs } = params
|
|
275
|
+
|
|
276
|
+
const baseDirExists = await fileExists({ filePath: baseDir, fs })
|
|
277
|
+
if (!baseDirExists) {
|
|
278
|
+
await fs.mkdir(baseDir, { recursive: true })
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Removes markdown code block syntax if present, including any language tag
|
|
284
|
+
*/
|
|
285
|
+
export const cleanMarkdownCodeBlock = (content: string): string => {
|
|
286
|
+
const cleanResponse = content.match(/^```(?:[a-zA-Z]+)?\n([\s\S]*)\n```$/)
|
|
287
|
+
? content.replace(/^```(?:[a-zA-Z]+)?\n/, '').replace(/\n```$/, '')
|
|
288
|
+
: content
|
|
289
|
+
return cleanResponse
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function isValidFilePath(path: string) {
|
|
293
|
+
if (!path) return false
|
|
294
|
+
|
|
295
|
+
// Check for whitespace
|
|
296
|
+
if (/\s/.test(path)) return false
|
|
297
|
+
|
|
298
|
+
// Check for invalid characters
|
|
299
|
+
const invalidChars = /[<>:"|?*\x00-\x1F]/g
|
|
300
|
+
if (invalidChars.test(path)) return false
|
|
301
|
+
|
|
302
|
+
return true
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export async function isDir(params: {
|
|
306
|
+
path: string
|
|
307
|
+
fs: AnvilFileSystem
|
|
308
|
+
}): Promise<boolean> {
|
|
309
|
+
const { path, fs } = params
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const stats = await fs.stat(path)
|
|
313
|
+
return stats.isDirectory()
|
|
314
|
+
} catch {
|
|
315
|
+
return false
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Returns true if the `toPath` is a subdirectory of `fromPath`.
|
|
321
|
+
*/
|
|
322
|
+
export function isSubdir(fromPath: string, toPath: string) {
|
|
323
|
+
const resolvedFrom = path.resolve(fromPath)
|
|
324
|
+
const resolvedTo = path.resolve(toPath)
|
|
325
|
+
|
|
326
|
+
if (process.platform === 'win32') {
|
|
327
|
+
const fromDrive = path.parse(resolvedFrom).root.toLowerCase()
|
|
328
|
+
const toDrive = path.parse(resolvedTo).root.toLowerCase()
|
|
329
|
+
if (fromDrive !== toDrive) {
|
|
330
|
+
return false
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return !path.relative(resolvedFrom, resolvedTo).startsWith('..')
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function isValidProjectRoot(dir: string): boolean {
|
|
338
|
+
return !isSubdir(dir, os.homedir())
|
|
339
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats code search output to group matches by file.
|
|
3
|
+
*
|
|
4
|
+
* Input format: ./file.ts:line:content
|
|
5
|
+
* Output format:
|
|
6
|
+
* Found 3 matches
|
|
7
|
+
* ./file.ts:
|
|
8
|
+
* Line 1: content
|
|
9
|
+
* Line 2: another line content
|
|
10
|
+
* Line 3: yet another line content
|
|
11
|
+
*
|
|
12
|
+
* (double newline between distinct files)
|
|
13
|
+
*
|
|
14
|
+
* @param stdout The raw stdout from ripgrep
|
|
15
|
+
* @param options.matchCount The number of actual matches, excluding context lines
|
|
16
|
+
* @returns Formatted output with matches grouped by file
|
|
17
|
+
*/
|
|
18
|
+
export function formatCodeSearchOutput(
|
|
19
|
+
stdout: string,
|
|
20
|
+
options: { matchCount?: number } = {},
|
|
21
|
+
): string {
|
|
22
|
+
if (!stdout) {
|
|
23
|
+
return 'Found 0 matches'
|
|
24
|
+
}
|
|
25
|
+
const lines = stdout.split('\n')
|
|
26
|
+
const formatted: string[] = [
|
|
27
|
+
`Found ${options.matchCount ?? countFormattedMatches(lines)} matches`,
|
|
28
|
+
]
|
|
29
|
+
let currentFile: string | null = null
|
|
30
|
+
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
if (!line.trim()) {
|
|
33
|
+
formatted.push(line)
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Skip separator lines between result groups
|
|
38
|
+
if (line === '--') {
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Ripgrep output format:
|
|
43
|
+
// - Match lines: filename:line_number:content
|
|
44
|
+
// - Context lines (with -A/-B/-C flags): filename-line_number-content
|
|
45
|
+
|
|
46
|
+
// Use regex to find the pattern: separator + digits + separator
|
|
47
|
+
// This handles filenames with hyphens/colons by matching the line number pattern
|
|
48
|
+
const parsedLine = parseRipgrepLine(line)
|
|
49
|
+
|
|
50
|
+
if (!parsedLine) {
|
|
51
|
+
formatted.push(line)
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
const { filePath, lineNumber, content } = parsedLine
|
|
55
|
+
|
|
56
|
+
// Check if this is a new file (file paths don't start with whitespace)
|
|
57
|
+
if (filePath && !filePath.startsWith(' ') && !filePath.startsWith('\t')) {
|
|
58
|
+
if (filePath !== currentFile) {
|
|
59
|
+
// New file - add double newline before it (except for the first file)
|
|
60
|
+
if (currentFile !== null) {
|
|
61
|
+
formatted.push('')
|
|
62
|
+
}
|
|
63
|
+
currentFile = filePath
|
|
64
|
+
// Show file path with colon on its own line
|
|
65
|
+
formatted.push(filePath + ':')
|
|
66
|
+
formatted.push(` Line ${lineNumber}: ${content}`)
|
|
67
|
+
} else {
|
|
68
|
+
formatted.push(` Line ${lineNumber}: ${content}`)
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// Line doesn't match expected format, keep as-is
|
|
72
|
+
formatted.push(line)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return formatted.join('\n')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function parseRipgrepLine(line: string): {
|
|
80
|
+
filePath: string
|
|
81
|
+
lineNumber: string
|
|
82
|
+
content: string
|
|
83
|
+
isContext: boolean
|
|
84
|
+
} | null {
|
|
85
|
+
// Try match line pattern: filename:digits:content
|
|
86
|
+
const matchLineMatch = line.match(/(.*?):(\d+):(.*)$/)
|
|
87
|
+
if (matchLineMatch) {
|
|
88
|
+
return {
|
|
89
|
+
filePath: matchLineMatch[1],
|
|
90
|
+
lineNumber: matchLineMatch[2],
|
|
91
|
+
content: matchLineMatch[3],
|
|
92
|
+
isContext: false,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Try context line pattern: filename-digits-content
|
|
97
|
+
const contextLineMatch = line.match(/(.*?)-(\d+)-(.*)$/)
|
|
98
|
+
if (contextLineMatch) {
|
|
99
|
+
return {
|
|
100
|
+
filePath: contextLineMatch[1],
|
|
101
|
+
lineNumber: contextLineMatch[2],
|
|
102
|
+
content: contextLineMatch[3],
|
|
103
|
+
isContext: true,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function countFormattedMatches(lines: string[]): number {
|
|
111
|
+
return lines.filter((line) => {
|
|
112
|
+
const parsedLine = parseRipgrepLine(line)
|
|
113
|
+
return parsedLine && !parsedLine.isContext
|
|
114
|
+
}).length
|
|
115
|
+
}
|