@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,505 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getPartialJsonDelta,
|
|
5
|
+
parsePartialJsonObjectSingle,
|
|
6
|
+
} from '../partial-json-delta'
|
|
7
|
+
|
|
8
|
+
describe('parsePartialJsonObjectSingle', () => {
|
|
9
|
+
describe('complete valid JSON', () => {
|
|
10
|
+
it('should parse complete valid JSON', () => {
|
|
11
|
+
const input = '{"name": "test", "value": 42}'
|
|
12
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
13
|
+
expect(result).toEqual({
|
|
14
|
+
lastParamComplete: true,
|
|
15
|
+
params: { name: 'test', value: 42 },
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('should parse empty object', () => {
|
|
20
|
+
const input = '{}'
|
|
21
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
22
|
+
expect(result).toEqual({ lastParamComplete: true, params: {} })
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should parse nested objects', () => {
|
|
26
|
+
const input = '{"user": {"name": "John", "age": 30}}'
|
|
27
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
28
|
+
expect(result).toEqual({
|
|
29
|
+
lastParamComplete: true,
|
|
30
|
+
params: { user: { name: 'John', age: 30 } },
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should parse arrays', () => {
|
|
35
|
+
const input = '{"items": [1, 2, 3]}'
|
|
36
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
37
|
+
expect(result).toEqual({
|
|
38
|
+
lastParamComplete: true,
|
|
39
|
+
params: { items: [1, 2, 3] },
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe('incomplete JSON - missing closing brace', () => {
|
|
45
|
+
it('should parse object missing final closing brace', () => {
|
|
46
|
+
const input = '{"name": "test", "value": 42'
|
|
47
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
48
|
+
expect(result).toEqual({
|
|
49
|
+
lastParamComplete: true,
|
|
50
|
+
params: { name: 'test' },
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should parse nested object missing final closing brace', () => {
|
|
55
|
+
const input = '{"user": {"name": "John", "age": 30}'
|
|
56
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
57
|
+
expect(result).toEqual({
|
|
58
|
+
lastParamComplete: true,
|
|
59
|
+
params: { user: { name: 'John', age: 30 } },
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should parse object with incomplete string value', () => {
|
|
64
|
+
const input = '{"name": "test", "incomplete": "partial'
|
|
65
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
66
|
+
expect(result).toEqual({
|
|
67
|
+
lastParamComplete: false,
|
|
68
|
+
params: { name: 'test', incomplete: 'partial' },
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('incomplete JSON - trailing comma handling', () => {
|
|
74
|
+
it('should handle trailing comma by removing last property', () => {
|
|
75
|
+
const input = '{"name": "test", "value": 42, "incomplete":'
|
|
76
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
77
|
+
expect(result).toEqual({
|
|
78
|
+
lastParamComplete: true,
|
|
79
|
+
params: { name: 'test', value: 42 },
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should handle multiple trailing commas', () => {
|
|
84
|
+
const input = '{"a": 1, "b": 2, "c": 3, "d":'
|
|
85
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
86
|
+
expect(result).toEqual({
|
|
87
|
+
lastParamComplete: true,
|
|
88
|
+
params: { a: 1, b: 2, c: 3 },
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should handle nested object with trailing comma', () => {
|
|
93
|
+
const input = '{"user": {"name": "John", "age": 30}, "incomplete":'
|
|
94
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
95
|
+
expect(result).toEqual({
|
|
96
|
+
lastParamComplete: true,
|
|
97
|
+
params: { user: { name: 'John', age: 30 } },
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should handle array with trailing comma', () => {
|
|
102
|
+
const input = '{"items": [1, 2, 3], "incomplete":'
|
|
103
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
104
|
+
expect(result).toEqual({
|
|
105
|
+
lastParamComplete: true,
|
|
106
|
+
params: { items: [1, 2, 3] },
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe('comma search optimization', () => {
|
|
112
|
+
it('should efficiently find last valid comma in deeply nested incomplete JSON', () => {
|
|
113
|
+
// This tests the O(n) backward comma search optimization
|
|
114
|
+
const input = '{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "incomplete":'
|
|
115
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
116
|
+
expect(result).toEqual({
|
|
117
|
+
lastParamComplete: true,
|
|
118
|
+
params: { a: 1, b: 2, c: 3, d: 4, e: 5 },
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('should handle comma inside string value when searching backwards', () => {
|
|
123
|
+
// Comma inside a string should not be treated as a separator
|
|
124
|
+
const input = '{"message": "Hello, world", "incomplete":'
|
|
125
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
126
|
+
expect(result).toEqual({
|
|
127
|
+
lastParamComplete: true,
|
|
128
|
+
params: { message: 'Hello, world' },
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should find valid comma after skipping invalid parse attempts', () => {
|
|
133
|
+
// Multiple commas, need to find the right one
|
|
134
|
+
const input = '{"x": [1, 2, 3], "y": {"a": 1, "b": 2}, "z":'
|
|
135
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
136
|
+
expect(result).toEqual({
|
|
137
|
+
lastParamComplete: true,
|
|
138
|
+
params: { x: [1, 2, 3], y: { a: 1, b: 2 } },
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe('edge cases', () => {
|
|
144
|
+
it('should return empty object for empty string', () => {
|
|
145
|
+
const input = ''
|
|
146
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
147
|
+
expect(result).toEqual({ lastParamComplete: true, params: {} })
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('should return empty object for invalid JSON', () => {
|
|
151
|
+
const input = 'not json at all'
|
|
152
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
153
|
+
expect(result).toEqual({ lastParamComplete: true, params: {} })
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('should return empty object for malformed JSON', () => {
|
|
157
|
+
const input = '{"name": test}'
|
|
158
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
159
|
+
expect(result).toEqual({ lastParamComplete: true, params: {} })
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should handle JSON with only opening brace', () => {
|
|
163
|
+
const input = '{'
|
|
164
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
165
|
+
expect(result).toEqual({ lastParamComplete: true, params: {} })
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('should handle JSON with whitespace', () => {
|
|
169
|
+
const input = ' {"name": "test"} '
|
|
170
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
171
|
+
expect(result).toEqual({
|
|
172
|
+
lastParamComplete: true,
|
|
173
|
+
params: { name: 'test' },
|
|
174
|
+
})
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('should handle complex nested incomplete JSON', () => {
|
|
178
|
+
const input =
|
|
179
|
+
'{"data": {"users": [{"name": "John"}, {"name": "Jane"}], "count": 2}, "meta":'
|
|
180
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
181
|
+
expect(result).toEqual({
|
|
182
|
+
lastParamComplete: true,
|
|
183
|
+
params: {
|
|
184
|
+
data: {
|
|
185
|
+
users: [{ name: 'John' }, { name: 'Jane' }],
|
|
186
|
+
count: 2,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
describe('real-world streaming scenarios', () => {
|
|
194
|
+
it('should handle partial JSON from streaming response', () => {
|
|
195
|
+
const input =
|
|
196
|
+
'{"status": "processing", "progress": 0.5, "message": "Working on'
|
|
197
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
198
|
+
expect(result).toEqual({
|
|
199
|
+
lastParamComplete: false,
|
|
200
|
+
params: {
|
|
201
|
+
status: 'processing',
|
|
202
|
+
progress: 0.5,
|
|
203
|
+
message: 'Working on',
|
|
204
|
+
},
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('should handle JSON with boolean and null values', () => {
|
|
209
|
+
const input =
|
|
210
|
+
'{"active": true, "deleted": false, "metadata": null, "incomplete":'
|
|
211
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
212
|
+
expect(result).toEqual({
|
|
213
|
+
lastParamComplete: true,
|
|
214
|
+
params: { active: true, deleted: false, metadata: null },
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('should handle JSON with numbers', () => {
|
|
219
|
+
const input =
|
|
220
|
+
'{"integer": 42, "float": 3.14, "negative": -10, "incomplete":'
|
|
221
|
+
const result = parsePartialJsonObjectSingle(input)
|
|
222
|
+
expect(result).toEqual({
|
|
223
|
+
lastParamComplete: true,
|
|
224
|
+
params: { integer: 42, float: 3.14, negative: -10 },
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
describe('getPartialJsonDelta', () => {
|
|
231
|
+
describe('input validation', () => {
|
|
232
|
+
it('should throw error when content does not start with previous', () => {
|
|
233
|
+
const content = '{"name": "test"}'
|
|
234
|
+
const previous = '{"other": "value"}'
|
|
235
|
+
|
|
236
|
+
expect(() => getPartialJsonDelta(content, previous)).toThrow(
|
|
237
|
+
'Content must be previous content plus new content',
|
|
238
|
+
)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('should work when content starts with previous', () => {
|
|
242
|
+
const content = '{"name": "test", "value": 42}'
|
|
243
|
+
const previous = '{"name": "test"'
|
|
244
|
+
|
|
245
|
+
expect(() => getPartialJsonDelta(content, previous)).not.toThrow()
|
|
246
|
+
})
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
describe('basic delta detection from streaming JSON', () => {
|
|
250
|
+
it('should detect new properties added to empty object', () => {
|
|
251
|
+
const content = '{ "name": "test", "value": 42 }'
|
|
252
|
+
const previous = '{'
|
|
253
|
+
const result = getPartialJsonDelta(content, previous)
|
|
254
|
+
|
|
255
|
+
expect(result.delta).toEqual({ name: 'test', value: 42 })
|
|
256
|
+
expect(result.result).toEqual({ name: 'test', value: 42 })
|
|
257
|
+
expect(result.lastParam.key).toBe('value')
|
|
258
|
+
expect(result.lastParam.complete).toBe(true)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('should detect completion of partial string value', () => {
|
|
262
|
+
const content = '{"name": "updated"}'
|
|
263
|
+
const previous = '{"name": "upda'
|
|
264
|
+
const result = getPartialJsonDelta(content, previous)
|
|
265
|
+
|
|
266
|
+
expect(result.delta).toEqual({ name: 'ted' })
|
|
267
|
+
expect(result.result).toEqual({ name: 'updated' })
|
|
268
|
+
expect(result.lastParam.key).toBe('name')
|
|
269
|
+
expect(result.lastParam.complete).toBe(true)
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it('should return empty delta when no changes detected', () => {
|
|
273
|
+
const content = '{"name": "test", "value": 42}'
|
|
274
|
+
const previous = '{"name": "test", "value": 42'
|
|
275
|
+
const result = getPartialJsonDelta(content, previous)
|
|
276
|
+
|
|
277
|
+
expect(result.delta).toEqual({ value: 42 })
|
|
278
|
+
expect(result.result).toEqual({ name: 'test', value: 42 })
|
|
279
|
+
expect(result.lastParam.key).toBe('value')
|
|
280
|
+
expect(result.lastParam.complete).toBe(true)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
it('should detect new property being added', () => {
|
|
284
|
+
const content = '{"name": "test", "value": 100}'
|
|
285
|
+
const previous = '{"name": "test", "value": '
|
|
286
|
+
const result = getPartialJsonDelta(content, previous)
|
|
287
|
+
|
|
288
|
+
expect(result.delta).toEqual({ value: 100 })
|
|
289
|
+
expect(result.result).toEqual({ name: 'test', value: 100 })
|
|
290
|
+
expect(result.lastParam.key).toBe('value')
|
|
291
|
+
expect(result.lastParam.complete).toBe(true)
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
describe('string delta handling', () => {
|
|
296
|
+
it('should return string slice for partial string updates', () => {
|
|
297
|
+
const content = '{"message": "Hello World"}'
|
|
298
|
+
const previous = '{"message": "Hello'
|
|
299
|
+
const result = getPartialJsonDelta(content, previous)
|
|
300
|
+
|
|
301
|
+
expect(result.delta).toEqual({ message: ' World' })
|
|
302
|
+
expect(result.result).toEqual({ message: 'Hello World' })
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('should handle empty string to non-empty string', () => {
|
|
306
|
+
const content = '{"message": "Hello"}'
|
|
307
|
+
const previous = '{"message": "'
|
|
308
|
+
const result = getPartialJsonDelta(content, previous)
|
|
309
|
+
|
|
310
|
+
expect(result.delta).toEqual({ message: 'Hello' })
|
|
311
|
+
expect(result.result).toEqual({ message: 'Hello' })
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('should handle new string property', () => {
|
|
315
|
+
const content = '{"message": "Hello"}'
|
|
316
|
+
const previous = '{'
|
|
317
|
+
const result = getPartialJsonDelta(content, previous)
|
|
318
|
+
|
|
319
|
+
expect(result.delta).toEqual({ message: 'Hello' })
|
|
320
|
+
expect(result.result).toEqual({ message: 'Hello' })
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('should handle multi-line string streaming', () => {
|
|
324
|
+
const content = '{"text": "Line 1\\nLine 2\\nLine 3"}'
|
|
325
|
+
const previous = '{"text": "Line 1\\nLine'
|
|
326
|
+
const result = getPartialJsonDelta(content, previous)
|
|
327
|
+
|
|
328
|
+
expect(result.delta).toEqual({ text: ' 2\nLine 3' })
|
|
329
|
+
expect(result.result).toEqual({ text: 'Line 1\nLine 2\nLine 3' })
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
describe('non-string value changes', () => {
|
|
334
|
+
it('should return full value for non-string changes', () => {
|
|
335
|
+
const content = '{"count": 42}'
|
|
336
|
+
const previous = '{"count": '
|
|
337
|
+
const result = getPartialJsonDelta(content, previous)
|
|
338
|
+
|
|
339
|
+
expect(result.delta).toEqual({ count: 42 })
|
|
340
|
+
expect(result.result).toEqual({ count: 42 })
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
it('should handle boolean values', () => {
|
|
344
|
+
const content = '{"active": true}'
|
|
345
|
+
const previous = '{"active": '
|
|
346
|
+
const result = getPartialJsonDelta(content, previous)
|
|
347
|
+
|
|
348
|
+
expect(result.delta).toEqual({ active: true })
|
|
349
|
+
expect(result.result).toEqual({ active: true })
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('should handle null values', () => {
|
|
353
|
+
const content = '{"data": null}'
|
|
354
|
+
const previous = '{"data": '
|
|
355
|
+
const result = getPartialJsonDelta(content, previous)
|
|
356
|
+
|
|
357
|
+
expect(result.delta).toEqual({ data: null })
|
|
358
|
+
expect(result.result).toEqual({ data: null })
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
it('should handle array values', () => {
|
|
362
|
+
const content = '{"items": [1, 2, 3]}'
|
|
363
|
+
const previous = '{"items": '
|
|
364
|
+
const result = getPartialJsonDelta(content, previous)
|
|
365
|
+
|
|
366
|
+
expect(result.delta).toEqual({ items: [1, 2, 3] })
|
|
367
|
+
expect(result.result).toEqual({ items: [1, 2, 3] })
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('should handle nested object values', () => {
|
|
371
|
+
const content = '{"user": {"name": "John", "age": 30}}'
|
|
372
|
+
const previous = '{"user": '
|
|
373
|
+
const result = getPartialJsonDelta(content, previous)
|
|
374
|
+
|
|
375
|
+
expect(result.delta).toEqual({ user: { name: 'John', age: 30 } })
|
|
376
|
+
expect(result.result).toEqual({ user: { name: 'John', age: 30 } })
|
|
377
|
+
})
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
describe('realistic streaming scenarios', () => {
|
|
381
|
+
it('should handle progressive JSON building', () => {
|
|
382
|
+
// Simulate streaming JSON construction
|
|
383
|
+
let previous = '{'
|
|
384
|
+
let content = '{"status": "processing"'
|
|
385
|
+
let result = getPartialJsonDelta(content, previous)
|
|
386
|
+
|
|
387
|
+
expect(result.delta).toEqual({ status: 'processing' })
|
|
388
|
+
expect(result.result).toEqual({ status: 'processing' })
|
|
389
|
+
|
|
390
|
+
// Add more content
|
|
391
|
+
previous = content
|
|
392
|
+
content = '{"status": "processing", "progress": 0.5'
|
|
393
|
+
result = getPartialJsonDelta(content, previous)
|
|
394
|
+
|
|
395
|
+
expect(result.delta).toEqual({})
|
|
396
|
+
expect(result.result).toEqual({ status: 'processing' })
|
|
397
|
+
|
|
398
|
+
// Complete the JSON
|
|
399
|
+
previous = content
|
|
400
|
+
content =
|
|
401
|
+
'{"status": "processing", "progress": 0.5, "message": "Almost done"}'
|
|
402
|
+
result = getPartialJsonDelta(content, previous)
|
|
403
|
+
|
|
404
|
+
expect(result.delta).toEqual({ message: 'Almost done', progress: 0.5 })
|
|
405
|
+
expect(result.result).toEqual({
|
|
406
|
+
status: 'processing',
|
|
407
|
+
progress: 0.5,
|
|
408
|
+
message: 'Almost done',
|
|
409
|
+
})
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
it('should handle streaming text completion', () => {
|
|
413
|
+
const previous = '{"response": "The quick brown'
|
|
414
|
+
const content =
|
|
415
|
+
'{"response": "The quick brown fox jumps over the lazy dog"}'
|
|
416
|
+
const result = getPartialJsonDelta(content, previous)
|
|
417
|
+
|
|
418
|
+
expect(result.delta).toEqual({ response: ' fox jumps over the lazy dog' })
|
|
419
|
+
expect(result.result).toEqual({
|
|
420
|
+
response: 'The quick brown fox jumps over the lazy dog',
|
|
421
|
+
})
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
it('should handle incomplete JSON with trailing comma', () => {
|
|
425
|
+
const previous = '{"name": "test", "value": 42,'
|
|
426
|
+
const content = '{"name": "test", "value": 42, "status": "complete"}'
|
|
427
|
+
const result = getPartialJsonDelta(content, previous)
|
|
428
|
+
|
|
429
|
+
expect(result.delta).toEqual({ status: 'complete' })
|
|
430
|
+
expect(result.result).toEqual({
|
|
431
|
+
name: 'test',
|
|
432
|
+
value: 42,
|
|
433
|
+
status: 'complete',
|
|
434
|
+
})
|
|
435
|
+
})
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
describe('lastParam tracking', () => {
|
|
439
|
+
it('should track last parameter key and completion status', () => {
|
|
440
|
+
const content = '{"name": "test", "value": 42}'
|
|
441
|
+
const previous = '{'
|
|
442
|
+
const result = getPartialJsonDelta(content, previous)
|
|
443
|
+
|
|
444
|
+
expect(result.lastParam.key).toBe('value')
|
|
445
|
+
expect(result.lastParam.complete).toBe(true)
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
it('should indicate incomplete last parameter', () => {
|
|
449
|
+
const content = '{"name": "test", "message": "partial'
|
|
450
|
+
const previous = '{'
|
|
451
|
+
const result = getPartialJsonDelta(content, previous)
|
|
452
|
+
|
|
453
|
+
expect(result.lastParam.key).toBe('message')
|
|
454
|
+
expect(result.lastParam.complete).toBe(false)
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
it('should handle undefined key for empty object', () => {
|
|
458
|
+
const content = '{}'
|
|
459
|
+
const previous = '{'
|
|
460
|
+
const result = getPartialJsonDelta(content, previous)
|
|
461
|
+
|
|
462
|
+
expect(result.lastParam.key).toBeUndefined()
|
|
463
|
+
expect(result.lastParam.complete).toBe(false)
|
|
464
|
+
})
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
describe('edge cases', () => {
|
|
468
|
+
it('should handle empty previous string', () => {
|
|
469
|
+
const content = '{"name": "test"}'
|
|
470
|
+
const previous = ''
|
|
471
|
+
|
|
472
|
+
expect(() => getPartialJsonDelta(content, previous)).not.toThrow()
|
|
473
|
+
const result = getPartialJsonDelta(content, previous)
|
|
474
|
+
expect(result.delta).toEqual({ name: 'test' })
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
it('should handle identical content and previous', () => {
|
|
478
|
+
const content = '{"name": "test"}'
|
|
479
|
+
const previous = '{"name": "test"}'
|
|
480
|
+
const result = getPartialJsonDelta(content, previous)
|
|
481
|
+
|
|
482
|
+
expect(result.delta).toEqual({})
|
|
483
|
+
expect(result.result).toEqual({ name: 'test' })
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
it('should handle malformed JSON gracefully', () => {
|
|
487
|
+
const content = '{"name": test}' // Invalid JSON
|
|
488
|
+
const previous = '{'
|
|
489
|
+
const result = getPartialJsonDelta(content, previous)
|
|
490
|
+
|
|
491
|
+
// Should return empty objects when JSON parsing fails
|
|
492
|
+
expect(result.delta).toEqual({})
|
|
493
|
+
expect(result.result).toEqual({})
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
it('should handle escaped quotes in strings', () => {
|
|
497
|
+
const content = '{"message": "He said \\"Hello\\" to me"}'
|
|
498
|
+
const previous = '{"message": "He said \\"Hello'
|
|
499
|
+
const result = getPartialJsonDelta(content, previous)
|
|
500
|
+
|
|
501
|
+
expect(result.delta).toEqual({ message: '" to me' })
|
|
502
|
+
expect(result.result).toEqual({ message: 'He said "Hello" to me' })
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
|
|
3
|
+
import { describe, expect, test } from 'bun:test'
|
|
4
|
+
|
|
5
|
+
import { isPathInside } from '../path'
|
|
6
|
+
|
|
7
|
+
describe('isPathInside', () => {
|
|
8
|
+
test('accepts the root itself and a child when root ends in a separator', () => {
|
|
9
|
+
const root = path.parse(process.cwd()).root
|
|
10
|
+
|
|
11
|
+
expect(isPathInside(root, root)).toBe(true)
|
|
12
|
+
expect(isPathInside(root, path.join(root, 'project.txt'))).toBe(true)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('rejects parents, sibling prefixes, and other filesystem roots', () => {
|
|
16
|
+
const root = path.resolve('workspace', 'project')
|
|
17
|
+
|
|
18
|
+
expect(isPathInside(root, path.dirname(root))).toBe(false)
|
|
19
|
+
expect(isPathInside(root, `${root}-private`)).toBe(false)
|
|
20
|
+
expect(isPathInside(root, path.parse(root).root)).toBe(false)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createProjectIgnoreFileReader,
|
|
5
|
+
isProjectFileIgnored,
|
|
6
|
+
} from '../project-ignore'
|
|
7
|
+
|
|
8
|
+
function reader(files: Record<string, string>) {
|
|
9
|
+
return async (filePath: string) => files[filePath] ?? null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('isProjectFileIgnored', () => {
|
|
13
|
+
test('applies root and nested ignore rules with negation', async () => {
|
|
14
|
+
const readIgnoreFile = reader({
|
|
15
|
+
'.gitignore': 'private/\n*.sample\n',
|
|
16
|
+
'config/.gitignore': '*.template\n!.env.template\n',
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
expect(
|
|
20
|
+
await isProjectFileIgnored({
|
|
21
|
+
filePath: 'private/.env.example',
|
|
22
|
+
readIgnoreFile,
|
|
23
|
+
}),
|
|
24
|
+
).toBe(true)
|
|
25
|
+
expect(
|
|
26
|
+
await isProjectFileIgnored({
|
|
27
|
+
filePath: 'config/.env.sample',
|
|
28
|
+
readIgnoreFile,
|
|
29
|
+
}),
|
|
30
|
+
).toBe(true)
|
|
31
|
+
expect(
|
|
32
|
+
await isProjectFileIgnored({
|
|
33
|
+
filePath: 'config/.env.template',
|
|
34
|
+
readIgnoreFile,
|
|
35
|
+
}),
|
|
36
|
+
).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('reads Anvil ignore files and rejects paths escaping the project', async () => {
|
|
40
|
+
const readIgnoreFile = reader({ '.anvilignore': '.env.example\n' })
|
|
41
|
+
|
|
42
|
+
expect(
|
|
43
|
+
await isProjectFileIgnored({
|
|
44
|
+
filePath: '.env.example',
|
|
45
|
+
readIgnoreFile,
|
|
46
|
+
}),
|
|
47
|
+
).toBe(true)
|
|
48
|
+
expect(
|
|
49
|
+
await isProjectFileIgnored({
|
|
50
|
+
filePath: '../.env.example',
|
|
51
|
+
readIgnoreFile,
|
|
52
|
+
}),
|
|
53
|
+
).toBe(true)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
describe('createProjectIgnoreFileReader', () => {
|
|
58
|
+
test('lists each directory once and reads only files that exist', async () => {
|
|
59
|
+
const listed: string[] = []
|
|
60
|
+
const read: string[] = []
|
|
61
|
+
const readIgnoreFile = createProjectIgnoreFileReader({
|
|
62
|
+
listDirectory: async (directory) => {
|
|
63
|
+
listed.push(directory)
|
|
64
|
+
return directory === '' ? ['.gitignore'] : []
|
|
65
|
+
},
|
|
66
|
+
readFile: async (filePath) => {
|
|
67
|
+
read.push(filePath)
|
|
68
|
+
return '*.secret\n'
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
expect(await readIgnoreFile('.gitignore')).toBe('*.secret\n')
|
|
73
|
+
expect(await readIgnoreFile('.anvilignore')).toBeNull()
|
|
74
|
+
expect(listed).toEqual([''])
|
|
75
|
+
expect(read).toEqual(['.gitignore'])
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('propagates errors when a listed ignore file is unreadable', async () => {
|
|
79
|
+
const readIgnoreFile = createProjectIgnoreFileReader({
|
|
80
|
+
listDirectory: async () => ['.gitignore'],
|
|
81
|
+
readFile: async () => {
|
|
82
|
+
throw new Error('permission denied')
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
await expect(readIgnoreFile('.gitignore')).rejects.toThrow(
|
|
87
|
+
'permission denied',
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
})
|