@yansigit/opencodex 2.31.0
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/AGENTS_INSTALL.md +109 -0
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +587 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
- package/gui/dist/assets/index-CGoDO3uO.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +108 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +1327 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +121 -0
- package/src/adapters/client-fingerprint.ts +65 -0
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +288 -0
- package/src/adapters/cursor/discovery.ts +333 -0
- package/src/adapters/cursor/effort-map.ts +151 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +269 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1653 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +76 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +547 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +663 -0
- package/src/adapters/cursor/protobuf-events.ts +1381 -0
- package/src/adapters/cursor/protobuf-request.ts +1032 -0
- package/src/adapters/cursor/request-builder.ts +461 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +735 -0
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +79 -0
- package/src/adapters/cursor/types.ts +79 -0
- package/src/adapters/cursor.ts +322 -0
- package/src/adapters/google-antigravity-hosts.ts +48 -0
- package/src/adapters/google-antigravity-replay.ts +827 -0
- package/src/adapters/google-antigravity-tools.ts +105 -0
- package/src/adapters/google-antigravity-wire.ts +141 -0
- package/src/adapters/google-errors.ts +92 -0
- package/src/adapters/google-http.ts +460 -0
- package/src/adapters/google-tool-schema.ts +238 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +1377 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +112 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1936 -0
- package/src/adapters/mimo-free.ts +280 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1980 -0
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1911 -0
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +154 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +1986 -0
- package/src/chat/inbound.ts +319 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +266 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +205 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +615 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +107 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +578 -0
- package/src/claude/model-info.ts +174 -0
- package/src/claude/outbound.ts +926 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +302 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +737 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +299 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +290 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +213 -0
- package/src/cli/claude.ts +355 -0
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +47 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +127 -0
- package/src/cli/config-command.ts +209 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/dispatch.ts +585 -0
- package/src/cli/doctor.ts +1202 -0
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +213 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +973 -0
- package/src/cli/init.ts +211 -0
- package/src/cli/integrations.ts +260 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +245 -0
- package/src/cli/models.ts +422 -0
- package/src/cli/observe.ts +206 -0
- package/src/cli/opencode.ts +588 -0
- package/src/cli/provider-replit.ts +232 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +492 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +422 -0
- package/src/cli/replit-gateway-key-input.ts +138 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +328 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +199 -0
- package/src/cli/v2.ts +268 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1704 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +47 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +544 -0
- package/src/codex/account-usability.ts +43 -0
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/agent-roles-sync.ts +225 -0
- package/src/codex/agent-roles.ts +238 -0
- package/src/codex/app-server-processes.ts +1143 -0
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +2147 -0
- package/src/codex/auth-collision.ts +109 -0
- package/src/codex/auth-context.ts +665 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +436 -0
- package/src/codex/catalog/bundled.ts +549 -0
- package/src/codex/catalog/effort.ts +446 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +664 -0
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +650 -0
- package/src/codex/catalog/provider-fetch.ts +2064 -0
- package/src/codex/catalog/sync.ts +1883 -0
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +14 -0
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1566 -0
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +108 -0
- package/src/codex/history-provider.ts +979 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1733 -0
- package/src/codex/injected-marker.ts +106 -0
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +225 -0
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +56 -0
- package/src/codex/main-account.ts +68 -0
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +273 -0
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +144 -0
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +425 -0
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +573 -0
- package/src/codex/refresh.ts +62 -0
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +1888 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +2170 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +784 -0
- package/src/codex/sync.ts +319 -0
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +298 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +160 -0
- package/src/combos/index.ts +45 -0
- package/src/combos/request.ts +94 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +398 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +4041 -0
- package/src/fork/register.ts +3 -0
- package/src/generated/compatibility-version.json +3116 -0
- package/src/generated/model-metadata.ts +106 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +530 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +955 -0
- package/src/images/plan.ts +143 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +195 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +346 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +130 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +364 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +380 -0
- package/src/lib/errors.ts +406 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +270 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-outbound.ts +210 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +521 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +61 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +119 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +86 -0
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +400 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +392 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-atomic-replace.ts +156 -0
- package/src/lib/windows-elevation.ts +773 -0
- package/src/lib/windows-secret-acl.ts +854 -0
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +403 -0
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +188 -0
- package/src/oauth/antigravity-routing.ts +151 -0
- package/src/oauth/callback-server.ts +300 -0
- package/src/oauth/chatgpt.ts +161 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +252 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +262 -0
- package/src/oauth/health.ts +407 -0
- package/src/oauth/index.ts +1504 -0
- package/src/oauth/key-providers.ts +124 -0
- package/src/oauth/kimi.ts +227 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +130 -0
- package/src/oauth/log.ts +50 -0
- package/src/oauth/login-cli.ts +223 -0
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +728 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +695 -0
- package/src/providers/antigravity-quota.ts +216 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +74 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +82 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +586 -0
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +449 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +243 -0
- package/src/providers/openai-tier-startup.ts +56 -0
- package/src/providers/openai-tiers.ts +423 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +185 -0
- package/src/providers/quota.ts +2345 -0
- package/src/providers/registry.ts +2918 -0
- package/src/providers/replit/constants.ts +27 -0
- package/src/providers/replit/derive.ts +85 -0
- package/src/providers/replit/headers.ts +28 -0
- package/src/providers/replit/origin.ts +55 -0
- package/src/providers/replit/pair-install-response.ts +72 -0
- package/src/providers/replit/probe.ts +199 -0
- package/src/providers/replit/setup.ts +350 -0
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +103 -0
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +148 -0
- package/src/reasoning-effort.ts +183 -0
- package/src/responses/compaction.ts +142 -0
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +838 -0
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +165 -0
- package/src/responses/spill-store.ts +459 -0
- package/src/responses/state.ts +1433 -0
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +761 -0
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +53 -0
- package/src/server/auth-cors.ts +751 -0
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +442 -0
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +1030 -0
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +190 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +568 -0
- package/src/server/index.ts +1813 -0
- package/src/server/lifecycle.ts +498 -0
- package/src/server/live.ts +717 -0
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +1433 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +193 -0
- package/src/server/management/body.ts +41 -0
- package/src/server/management/combo-routes.ts +263 -0
- package/src/server/management/config-routes.ts +835 -0
- package/src/server/management/context.ts +113 -0
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +586 -0
- package/src/server/management/model-routes.ts +560 -0
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +637 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +1033 -0
- package/src/server/management/replit-provider-routes.ts +86 -0
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +385 -0
- package/src/server/management/shared.ts +286 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +435 -0
- package/src/server/management/system-routes.ts +194 -0
- package/src/server/management/usage-summary-cache.ts +94 -0
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +314 -0
- package/src/server/management-auth.ts +482 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +328 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1209 -0
- package/src/server/request-decompress.ts +239 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1259 -0
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +551 -0
- package/src/server/responses/compact.ts +771 -0
- package/src/server/responses/core.ts +5389 -0
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +331 -0
- package/src/server/responses/fetch-helpers.ts +232 -0
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +251 -0
- package/src/server/responses/upstream-error.ts +53 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +25 -0
- package/src/server/search.ts +201 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +315 -0
- package/src/server/startup-health-cache.ts +131 -0
- package/src/server/system-env.ts +484 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +472 -0
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +3575 -0
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +757 -0
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +876 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +106 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +415 -0
- package/src/update/job.ts +1887 -0
- package/src/update/notify.ts +263 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +625 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +416 -0
- package/src/usage/log.ts +1223 -0
- package/src/usage/summary.ts +753 -0
- package/src/usage/totals.ts +14 -0
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +189 -0
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +131 -0
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +681 -0
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +195 -0
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +113 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +331 -0
- package/src/web-search/loop.ts +896 -0
- package/src/web-search/parse.ts +315 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/synthetic-tool.ts +47 -0
- package/src/web-search/xai-executor.ts +219 -0
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { atomicWriteFileAsync, getConfigDir, type AtomicWriteAsyncTestSeam } from "../config";
|
|
5
|
+
import { enforceAppOwnedMemoryBudget } from "../lib/app-owned-memory";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Google-family thoughtSignature reasoning-replay cache.
|
|
9
|
+
*
|
|
10
|
+
* Gemini-3 interleaved thinking is stateless upstream: each model content part carries a
|
|
11
|
+
* `thoughtSignature` that MUST be echoed back on the matching part in the next request, or the
|
|
12
|
+
* upstream rejects the turn (HTTP 400). We observe signatures on the response stream, cache them
|
|
13
|
+
* per `model + session`, and re-inject them into the outgoing `request.contents` on the next turn.
|
|
14
|
+
*
|
|
15
|
+
* Mirrors CLIProxyAPI `internal/runtime/executor/antigravity_reasoning_replay.go` and is also used
|
|
16
|
+
* by Vertex with a transport/project/location-prefixed model identity. Gemini-only;
|
|
17
|
+
* Claude-on-Antigravity uses inline signature sanitization instead (see google-antigravity-wire).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface ReplayCall {
|
|
21
|
+
signature: string;
|
|
22
|
+
sizeBytes: number;
|
|
23
|
+
touchedAtMs: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ReplayEntry {
|
|
27
|
+
/** thoughtSignature keyed by functionCall identity (name + canonical args). */
|
|
28
|
+
byCall: Map<string, ReplayCall>;
|
|
29
|
+
bytes: number;
|
|
30
|
+
expiresAtMs: number;
|
|
31
|
+
oldestAtMs: number | null;
|
|
32
|
+
/** Most recent observe/apply activity; orders sessions under the snapshot cap. */
|
|
33
|
+
lastActiveAtMs: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const MIN_SIGNATURE_LEN = 16;
|
|
37
|
+
const REPLAY_TTL_MS = 60 * 60 * 1000; // 1h
|
|
38
|
+
export const ANTIGRAVITY_REPLAY_MAX_ENTRIES = 10_240;
|
|
39
|
+
const REPLAY_EVICT_BATCH = 128;
|
|
40
|
+
const REPLAY_MAX_CALLS_PER_SESSION = 256;
|
|
41
|
+
export const ANTIGRAVITY_REPLAY_MAX_BYTES_PER_SESSION = 2 * 1024 * 1024;
|
|
42
|
+
export const ANTIGRAVITY_REPLAY_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
43
|
+
const REPLAY_MAX_SIGNATURE_BYTES = 64 * 1024;
|
|
44
|
+
/** Fixed 64-hex outer key length, counted once per session entry. */
|
|
45
|
+
const REPLAY_SESSION_KEY_BYTES = 64;
|
|
46
|
+
const REPLAY_SNAPSHOT_FILE = "antigravity-replay.json";
|
|
47
|
+
const REPLAY_SNAPSHOT_VERSION = 1;
|
|
48
|
+
const REPLAY_SNAPSHOT_DEBOUNCE_MS = 2_000;
|
|
49
|
+
/** Upper bound on flush retries: mutations arriving faster than writes complete
|
|
50
|
+
* must not let shutdown hang; the last completed write is already on disk. */
|
|
51
|
+
const REPLAY_FLUSH_MAX_ATTEMPTS = 8;
|
|
52
|
+
/** Write bound for the durable snapshot (mirrors the responses-state cap). */
|
|
53
|
+
const REPLAY_SNAPSHOT_MAX_BYTES = 24 * 1024 * 1024;
|
|
54
|
+
/** Refuse-to-parse ceiling for an existing snapshot file. */
|
|
55
|
+
const REPLAY_SNAPSHOT_REFUSE_BYTES = 32 * 1024 * 1024;
|
|
56
|
+
|
|
57
|
+
interface ReplayLimits {
|
|
58
|
+
maxCallsPerSession: number;
|
|
59
|
+
maxBytesPerSession: number;
|
|
60
|
+
maxSignatureBytes: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const DEFAULT_REPLAY_LIMITS: ReplayLimits = {
|
|
64
|
+
maxCallsPerSession: REPLAY_MAX_CALLS_PER_SESSION,
|
|
65
|
+
maxBytesPerSession: ANTIGRAVITY_REPLAY_MAX_BYTES_PER_SESSION,
|
|
66
|
+
maxSignatureBytes: REPLAY_MAX_SIGNATURE_BYTES,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const replayCache = new Map<string, ReplayEntry>();
|
|
70
|
+
const utf8 = new TextEncoder();
|
|
71
|
+
let replayLimits = { ...DEFAULT_REPLAY_LIMITS };
|
|
72
|
+
let replayBytes = 0;
|
|
73
|
+
let replayOldestSessionKey: string | undefined;
|
|
74
|
+
let replayOldestAt: number | null = null;
|
|
75
|
+
let replaySnapshotLoaded = false;
|
|
76
|
+
let replaySnapshotPersistTimer: ReturnType<typeof setTimeout> | null = null;
|
|
77
|
+
let replaySnapshotPersistGate: Promise<void> = Promise.resolve();
|
|
78
|
+
/** Mutation generation: bumped on every cache change that needs persisting. */
|
|
79
|
+
let replayMutationGeneration = 0;
|
|
80
|
+
/** Generation of the data the last successful snapshot write actually captured. */
|
|
81
|
+
let replayWrittenGeneration = 0;
|
|
82
|
+
let replaySnapshotWriteSeam: AtomicWriteAsyncTestSeam | undefined;
|
|
83
|
+
let replaySnapshotLoadDiscarded = false;
|
|
84
|
+
let replaySnapshotMaxBytes = REPLAY_SNAPSHOT_MAX_BYTES;
|
|
85
|
+
|
|
86
|
+
function replaySnapshotPath(): string {
|
|
87
|
+
return join(getConfigDir(), REPLAY_SNAPSHOT_FILE);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function loadReplaySnapshotEntry(key: string, value: unknown): void {
|
|
91
|
+
if (!/^[0-9a-f]{64}$/.test(key)) return;
|
|
92
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
93
|
+
const rec = value as { byCall?: unknown; expiresAtMs?: unknown; lastActiveAtMs?: unknown };
|
|
94
|
+
if (typeof rec.expiresAtMs !== "number" || !Number.isFinite(rec.expiresAtMs)) return;
|
|
95
|
+
// Expired sessions are dropped at load; a stale snapshot is self-healing.
|
|
96
|
+
if (rec.expiresAtMs <= Date.now()) {
|
|
97
|
+
replaySnapshotLoadDiscarded = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!Array.isArray(rec.byCall)) return;
|
|
101
|
+
const byCall = new Map<string, ReplayCall>();
|
|
102
|
+
let bytes = REPLAY_SESSION_KEY_BYTES;
|
|
103
|
+
for (const pair of rec.byCall) {
|
|
104
|
+
if (!Array.isArray(pair) || pair.length !== 2 || typeof pair[0] !== "string") continue;
|
|
105
|
+
if (!/^[0-9a-f]{64}$/.test(pair[0])) continue;
|
|
106
|
+
const call = pair[1] as { signature?: unknown; touchedAtMs?: unknown } | null;
|
|
107
|
+
if (!call || typeof call !== "object" || Array.isArray(call)) continue;
|
|
108
|
+
if (typeof call.signature !== "string" || call.signature.length < MIN_SIGNATURE_LEN) continue;
|
|
109
|
+
if (typeof call.touchedAtMs !== "number" || !Number.isFinite(call.touchedAtMs)) continue;
|
|
110
|
+
// Never trust a serialized sizeBytes: a forged snapshot could claim a tiny
|
|
111
|
+
// size for a huge signature and bypass every byte cap. Recompute from the
|
|
112
|
+
// signature itself, exactly like the live write path.
|
|
113
|
+
const signatureBytes = utf8.encode(call.signature).byteLength;
|
|
114
|
+
if (signatureBytes > replayLimits.maxSignatureBytes) continue;
|
|
115
|
+
const callBytes = utf8.encode(pair[0]).byteLength + signatureBytes;
|
|
116
|
+
if (callBytes > replayLimits.maxBytesPerSession) continue;
|
|
117
|
+
// A duplicated call key would overstate entry.bytes (the map keeps only the
|
|
118
|
+
// last value) and could evict valid sessions; keep the first occurrence.
|
|
119
|
+
if (byCall.has(pair[0])) {
|
|
120
|
+
replaySnapshotLoadDiscarded = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
byCall.set(pair[0], {
|
|
124
|
+
signature: call.signature,
|
|
125
|
+
sizeBytes: callBytes,
|
|
126
|
+
touchedAtMs: call.touchedAtMs,
|
|
127
|
+
});
|
|
128
|
+
bytes += callBytes;
|
|
129
|
+
}
|
|
130
|
+
if (byCall.size === 0) {
|
|
131
|
+
replaySnapshotLoadDiscarded = true;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const lastActiveAtMs = typeof rec.lastActiveAtMs === "number" && Number.isFinite(rec.lastActiveAtMs)
|
|
135
|
+
? rec.lastActiveAtMs
|
|
136
|
+
: rec.expiresAtMs;
|
|
137
|
+
const entry: ReplayEntry = { byCall, bytes, expiresAtMs: rec.expiresAtMs, oldestAtMs: null, lastActiveAtMs };
|
|
138
|
+
const loadedCallCount = entry.byCall.size;
|
|
139
|
+
// Account BEFORE trimming: evictInnerCalls decrements the global byte count
|
|
140
|
+
// through deleteReplayCall, so the entry must already be on the books.
|
|
141
|
+
replayCache.set(key, entry);
|
|
142
|
+
replayBytes += entry.bytes;
|
|
143
|
+
// Same per-session caps as live writes, in case limits changed across versions.
|
|
144
|
+
evictInnerCalls(entry);
|
|
145
|
+
if (entry.byCall.size < loadedCallCount) replaySnapshotLoadDiscarded = true;
|
|
146
|
+
if (entry.byCall.size === 0) {
|
|
147
|
+
deleteReplaySession(key);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
refreshReplaySessionCandidate(key, entry);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Lazy load of the durable snapshot on first cache access, so signatures observed
|
|
155
|
+
* before a proxy restart are available again once the session id re-derives to the
|
|
156
|
+
* same key (the session id is anchored on the first user message text). Load is
|
|
157
|
+
* best-effort: missing, corrupt, or oversized files start the cache empty.
|
|
158
|
+
*/
|
|
159
|
+
function ensureReplaySnapshotLoaded(): void {
|
|
160
|
+
if (replaySnapshotLoaded) return;
|
|
161
|
+
replaySnapshotLoaded = true;
|
|
162
|
+
replaySnapshotLoadDiscarded = false;
|
|
163
|
+
try {
|
|
164
|
+
const path = replaySnapshotPath();
|
|
165
|
+
if (!existsSync(path)) return;
|
|
166
|
+
const stat = statSync(path);
|
|
167
|
+
// Bound the read BEFORE parse: the 24 MiB write cap constrains snapshots this
|
|
168
|
+
// process wrote, not a pre-existing oversized file.
|
|
169
|
+
if (!stat.isFile() || stat.size > REPLAY_SNAPSHOT_REFUSE_BYTES) return;
|
|
170
|
+
const raw = JSON.parse(readFileSync(path, "utf8")) as { version?: unknown; sessions?: unknown };
|
|
171
|
+
if (raw.version !== REPLAY_SNAPSHOT_VERSION || !Array.isArray(raw.sessions)) return;
|
|
172
|
+
for (const entry of raw.sessions) {
|
|
173
|
+
if (!Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== "string") continue;
|
|
174
|
+
loadReplaySnapshotEntry(entry[0], entry[1]);
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
// Missing/corrupt snapshot: start empty.
|
|
178
|
+
}
|
|
179
|
+
const sessionsAfterLoad = replayCache.size;
|
|
180
|
+
// Load-time admission must respect the same global caps as live writes.
|
|
181
|
+
evictIfNeeded();
|
|
182
|
+
if (replayCache.size < sessionsAfterLoad) replaySnapshotLoadDiscarded = true;
|
|
183
|
+
// Persist the cleaned state once: expired/over-limit sessions must not stay
|
|
184
|
+
// on disk and be re-parsed (and re-dropped) after every restart.
|
|
185
|
+
if (replaySnapshotLoadDiscarded) markReplayDirty();
|
|
186
|
+
enforceAppOwnedMemoryBudget();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function markReplayDirty(): void {
|
|
190
|
+
replayMutationGeneration += 1;
|
|
191
|
+
if (replaySnapshotPersistTimer) return;
|
|
192
|
+
replaySnapshotPersistTimer = setTimeout(() => {
|
|
193
|
+
void persistReplaySnapshotNow().catch(() => {
|
|
194
|
+
// Redacted static message only: never echo the underlying error, which
|
|
195
|
+
// can carry paths or other environment details.
|
|
196
|
+
console.warn("[antigravity] replay snapshot persist failed; cached signatures will not survive a restart");
|
|
197
|
+
});
|
|
198
|
+
}, REPLAY_SNAPSHOT_DEBOUNCE_MS);
|
|
199
|
+
(replaySnapshotPersistTimer as { unref?: () => void }).unref?.();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function persistReplaySnapshotNow(): Promise<void> {
|
|
203
|
+
if (replaySnapshotPersistTimer) {
|
|
204
|
+
clearTimeout(replaySnapshotPersistTimer);
|
|
205
|
+
replaySnapshotPersistTimer = null;
|
|
206
|
+
}
|
|
207
|
+
// Serialize writers so a flush and a debounced write cannot race on temps/ACL.
|
|
208
|
+
const previous = replaySnapshotPersistGate;
|
|
209
|
+
let release!: () => void;
|
|
210
|
+
replaySnapshotPersistGate = new Promise<void>(resolve => { release = resolve; });
|
|
211
|
+
await previous;
|
|
212
|
+
const writeGeneration = replayMutationGeneration;
|
|
213
|
+
try {
|
|
214
|
+
const sessions: Array<[string, unknown]> = [];
|
|
215
|
+
// Account for the bytes actually written, not just the entries: the document
|
|
216
|
+
// is `{"version":N,"sessions":[...]}`, so the framing and the comma between
|
|
217
|
+
// entries count against the cap too. Summing per-entry sizes alone let the
|
|
218
|
+
// written file exceed replaySnapshotMaxBytes by a margin that grew with every
|
|
219
|
+
// additional session.
|
|
220
|
+
const prefix = `{"version":${JSON.stringify(REPLAY_SNAPSHOT_VERSION)},"sessions":[`;
|
|
221
|
+
const suffix = "]}";
|
|
222
|
+
const serialized: string[] = [];
|
|
223
|
+
let total = Buffer.byteLength(prefix, "utf8") + Buffer.byteLength(suffix, "utf8");
|
|
224
|
+
// Most-recently-active first so the sessions that survive the snapshot
|
|
225
|
+
// byte cap are the ones actually in use (Map order is insertion order).
|
|
226
|
+
for (const [key, entry] of [...replayCache].sort((a, b) => b[1].lastActiveAtMs - a[1].lastActiveAtMs)) {
|
|
227
|
+
const byCall = [...entry.byCall].map(([callKey, call]) => [
|
|
228
|
+
callKey,
|
|
229
|
+
{ signature: call.signature, touchedAtMs: call.touchedAtMs },
|
|
230
|
+
]);
|
|
231
|
+
const persistEntry: [string, unknown] = [key, {
|
|
232
|
+
byCall,
|
|
233
|
+
expiresAtMs: entry.expiresAtMs,
|
|
234
|
+
lastActiveAtMs: entry.lastActiveAtMs,
|
|
235
|
+
}];
|
|
236
|
+
const encoded = JSON.stringify(persistEntry);
|
|
237
|
+
const size = Buffer.byteLength(encoded, "utf8") + (serialized.length > 0 ? 1 : 0);
|
|
238
|
+
if (total + size > replaySnapshotMaxBytes) break;
|
|
239
|
+
total += size;
|
|
240
|
+
serialized.push(encoded);
|
|
241
|
+
sessions.push(persistEntry);
|
|
242
|
+
}
|
|
243
|
+
serialized.reverse();
|
|
244
|
+
const document = `${prefix}${serialized.join(",")}${suffix}`;
|
|
245
|
+
// Defensive: the admitted entries are what we serialize, so this can only
|
|
246
|
+
// trip if the accounting above and the payload below ever drift apart.
|
|
247
|
+
if (Buffer.byteLength(document, "utf8") > replaySnapshotMaxBytes) {
|
|
248
|
+
throw new Error("Antigravity replay snapshot exceeded its configured byte cap.");
|
|
249
|
+
}
|
|
250
|
+
mkdirSync(dirname(replaySnapshotPath()), { recursive: true, mode: 0o700 });
|
|
251
|
+
try { chmodSync(dirname(replaySnapshotPath()), 0o700); } catch { /* best-effort (e.g. Windows) */ }
|
|
252
|
+
await atomicWriteFileAsync(
|
|
253
|
+
replaySnapshotPath(),
|
|
254
|
+
document,
|
|
255
|
+
undefined,
|
|
256
|
+
replaySnapshotWriteSeam,
|
|
257
|
+
);
|
|
258
|
+
replayWrittenGeneration = writeGeneration;
|
|
259
|
+
} finally {
|
|
260
|
+
release();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Flush any pending debounced snapshot write (graceful shutdown / deterministic tests). */
|
|
265
|
+
export async function flushAntigravityReplay(): Promise<void> {
|
|
266
|
+
// Persist until the durable generation catches up with the latest mutation:
|
|
267
|
+
// a change that lands while a writer is in flight must not be lost when
|
|
268
|
+
// shutdown exits right after the first write completes.
|
|
269
|
+
for (let attempt = 0; attempt < REPLAY_FLUSH_MAX_ATTEMPTS; attempt += 1) {
|
|
270
|
+
if (replaySnapshotPersistTimer || replayMutationGeneration > replayWrittenGeneration) {
|
|
271
|
+
await persistReplaySnapshotNow();
|
|
272
|
+
}
|
|
273
|
+
// No pending timer: still await any in-flight write so shutdown does not race it.
|
|
274
|
+
await replaySnapshotPersistGate;
|
|
275
|
+
if (replayMutationGeneration === replayWrittenGeneration && replaySnapshotPersistTimer === null) return;
|
|
276
|
+
}
|
|
277
|
+
// Budget exhausted without convergence. Resolving here would tell
|
|
278
|
+
// drainAndShutdown() the snapshot is durable while the latest thought
|
|
279
|
+
// signature may never have reached disk, so shutdown diagnostics would claim
|
|
280
|
+
// a durability we cannot demonstrate. Reject instead, with fixed text: the
|
|
281
|
+
// shutdown path logs this message, so it must not carry session, model, or
|
|
282
|
+
// signature detail.
|
|
283
|
+
throw new Error("Antigravity replay snapshot flush did not converge.");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Fixed-size identity for a (model, sessionId) pair: SHA-256 over
|
|
288
|
+
* length-prefixed UTF-16 code units fed incrementally (no separator ambiguity
|
|
289
|
+
* — `("a\0b","c")` and `("a","b\0c")` derive different keys — and no raw
|
|
290
|
+
* model/session strings retained as Map keys, which the byte caps never
|
|
291
|
+
* counted).
|
|
292
|
+
*/
|
|
293
|
+
/**
|
|
294
|
+
* Injective string feed for key derivation: length-prefixed in CODE UNITS,
|
|
295
|
+
* then each code unit as two little-endian bytes. TextEncoder/UTF-8 would
|
|
296
|
+
* fold lone surrogates into U+FFFD, colliding distinct strings (e.g.
|
|
297
|
+
* "�" and "�") into the same key.
|
|
298
|
+
*/
|
|
299
|
+
function updateHashWithString(hash: ReturnType<typeof createHash>, value: string): void {
|
|
300
|
+
hash.update(String(value.length));
|
|
301
|
+
hash.update("\0");
|
|
302
|
+
const buf = Buffer.allocUnsafe(8192);
|
|
303
|
+
let offset = 0;
|
|
304
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
305
|
+
buf.writeUInt16LE(value.charCodeAt(index), offset);
|
|
306
|
+
offset += 2;
|
|
307
|
+
if (offset === buf.length) {
|
|
308
|
+
hash.update(buf);
|
|
309
|
+
offset = 0;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (offset > 0) hash.update(buf.subarray(0, offset));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function replayKey(model: string, sessionId: string): string {
|
|
316
|
+
const hash = createHash("sha256");
|
|
317
|
+
updateHashWithString(hash, model);
|
|
318
|
+
updateHashWithString(hash, sessionId);
|
|
319
|
+
return hash.digest("hex");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Canonical output exceeding this budget is rejected DURING the walk — the
|
|
323
|
+
* pre-fix path materialized an unbounded canonical string before admission. */
|
|
324
|
+
const REPLAY_MAX_CANONICAL_ARGS_BYTES = 64 * 1024;
|
|
325
|
+
const CANONICAL_OVERFLOW = Symbol("canonical-overflow");
|
|
326
|
+
|
|
327
|
+
/** Byte-identical output to the old recursive canonicalJson, written incrementally. */
|
|
328
|
+
/**
|
|
329
|
+
* Key-count pre-check: every object key costs at least 4 canonical bytes
|
|
330
|
+
* (two quotes, colon, separator), so a wider object ALWAYS overflows the
|
|
331
|
+
* canonical budget — skip the sort and the walk. Object.keys allocation
|
|
332
|
+
* itself is linear and irreducible in JS (there is no streaming key API),
|
|
333
|
+
* but it is transient and never sorted or walked past this bound.
|
|
334
|
+
*/
|
|
335
|
+
const CANONICAL_MAX_KEYS_PER_OBJECT = REPLAY_MAX_CANONICAL_ARGS_BYTES / 4;
|
|
336
|
+
|
|
337
|
+
/** Test-only scan instrumentation: proves overflow aborts the walk near the
|
|
338
|
+
* cap instead of scanning/materializing the whole input. */
|
|
339
|
+
let canonicalScanUnitsForTests = 0;
|
|
340
|
+
export function canonicalScanUnitsForTestsValue(): number {
|
|
341
|
+
return canonicalScanUnitsForTests;
|
|
342
|
+
}
|
|
343
|
+
export function resetCanonicalScanUnitsForTests(): void {
|
|
344
|
+
canonicalScanUnitsForTests = 0;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const MAX_CANONICAL_DEPTH = 128;
|
|
348
|
+
|
|
349
|
+
function writeCanonicalJson(value: unknown, sink: (chunk: string) => void, depth = 0): void {
|
|
350
|
+
canonicalScanUnitsForTests += 1;
|
|
351
|
+
// Depth overflow is the same class as byte overflow: skip replay for this
|
|
352
|
+
// call instead of exhausting the stack on a pathological argument shape.
|
|
353
|
+
if (depth > MAX_CANONICAL_DEPTH) throw CANONICAL_OVERFLOW;
|
|
354
|
+
if (typeof value === "string") {
|
|
355
|
+
writeJsonStringEscaped(value, sink);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (value === null || typeof value !== "object") {
|
|
359
|
+
sink(JSON.stringify(value) ?? "null");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (Array.isArray(value)) {
|
|
363
|
+
sink("[");
|
|
364
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
365
|
+
if (index > 0) sink(",");
|
|
366
|
+
// Array.prototype.map parity: holes produce NOTHING between the commas
|
|
367
|
+
// (old output `[1,,3]`), while an explicit undefined element is "null".
|
|
368
|
+
if (index in value) writeCanonicalJson(value[index], sink, depth + 1);
|
|
369
|
+
}
|
|
370
|
+
sink("]");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const keys = Object.keys(value as Record<string, unknown>);
|
|
374
|
+
if (keys.length > CANONICAL_MAX_KEYS_PER_OBJECT) throw CANONICAL_OVERFLOW;
|
|
375
|
+
keys.sort();
|
|
376
|
+
sink("{");
|
|
377
|
+
keys.forEach((k, index) => {
|
|
378
|
+
if (index > 0) sink(",");
|
|
379
|
+
writeJsonStringEscaped(k, sink);
|
|
380
|
+
sink(":");
|
|
381
|
+
writeCanonicalJson((value as Record<string, unknown>)[k], sink, depth + 1);
|
|
382
|
+
});
|
|
383
|
+
sink("}");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* JSON.stringify string escaping, streamed in small chunks so the budget can
|
|
388
|
+
* reject mid-string — calling JSON.stringify on a multi-MiB primitive would
|
|
389
|
+
* materialize its full escaped form before the sink could refuse it.
|
|
390
|
+
* Semantics mirror JSON.stringify for strings exactly (ES2019 JSON
|
|
391
|
+
* superset): quotes/backslash and control characters are escaped, LONE
|
|
392
|
+
* surrogates become \uXXXX, and valid surrogate pairs pass through raw.
|
|
393
|
+
*/
|
|
394
|
+
function writeJsonStringEscaped(value: string, sink: (chunk: string) => void): void {
|
|
395
|
+
sink('"');
|
|
396
|
+
let buffer = "";
|
|
397
|
+
for (const cp of value) {
|
|
398
|
+
canonicalScanUnitsForTests += 1;
|
|
399
|
+
const code = cp.codePointAt(0)!;
|
|
400
|
+
let escaped: string;
|
|
401
|
+
if (cp === '"') escaped = '\\"';
|
|
402
|
+
else if (cp === "\\") escaped = "\\\\";
|
|
403
|
+
else if (cp === "\b") escaped = "\\b";
|
|
404
|
+
else if (cp === "\f") escaped = "\\f";
|
|
405
|
+
else if (cp === "\n") escaped = "\\n";
|
|
406
|
+
else if (cp === "\r") escaped = "\\r";
|
|
407
|
+
else if (cp === "\t") escaped = "\\t";
|
|
408
|
+
else if (code < 0x20) escaped = `\\u${code.toString(16).padStart(4, "0")}`;
|
|
409
|
+
// Lone surrogates: JSON.stringify emits \uXXXX (a raw one would decode
|
|
410
|
+
// back as U+FFFD and collide with real U+FFFD content).
|
|
411
|
+
else if (code >= 0xd800 && code <= 0xdfff) escaped = `\\u${code.toString(16).padStart(4, "0")}`;
|
|
412
|
+
else escaped = cp;
|
|
413
|
+
buffer += escaped;
|
|
414
|
+
if (buffer.length >= 4096) {
|
|
415
|
+
sink(buffer);
|
|
416
|
+
buffer = "";
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (buffer.length > 0) sink(buffer);
|
|
420
|
+
sink('"');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** Bounded canonicalization: null on overflow (skip replay for that call). */
|
|
424
|
+
function canonicalJsonBounded(value: unknown, maxBytes: number): string | null {
|
|
425
|
+
let written = 0;
|
|
426
|
+
const parts: string[] = [];
|
|
427
|
+
const sink = (chunk: string) => {
|
|
428
|
+
written += utf8.encode(chunk).byteLength;
|
|
429
|
+
if (written > maxBytes) throw CANONICAL_OVERFLOW;
|
|
430
|
+
parts.push(chunk);
|
|
431
|
+
};
|
|
432
|
+
try {
|
|
433
|
+
writeCanonicalJson(value, sink);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
if (error === CANONICAL_OVERFLOW) return null;
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
438
|
+
return parts.join("");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Stable identity for a functionCall part: fixed-size SHA-256 over
|
|
443
|
+
* length-prefixed name + canonical args. Overflow during canonicalization
|
|
444
|
+
* skips replay for that call (never materializes an unbounded string); other
|
|
445
|
+
* canonicalization failures keep the old name-only fallback semantics.
|
|
446
|
+
*/
|
|
447
|
+
function functionCallKey(name: unknown, args: unknown): string | undefined {
|
|
448
|
+
if (typeof name !== "string" || name.length === 0) return undefined;
|
|
449
|
+
let canonical: string | null;
|
|
450
|
+
try {
|
|
451
|
+
canonical = canonicalJsonBounded(args ?? {}, REPLAY_MAX_CANONICAL_ARGS_BYTES);
|
|
452
|
+
} catch {
|
|
453
|
+
canonical = "";
|
|
454
|
+
}
|
|
455
|
+
if (canonical === null) return undefined;
|
|
456
|
+
const hash = createHash("sha256");
|
|
457
|
+
updateHashWithString(hash, name);
|
|
458
|
+
updateHashWithString(hash, canonical);
|
|
459
|
+
return hash.digest("hex");
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/** Test-only key-derivation seam: the fixed-key regression cannot go red
|
|
463
|
+
* through snapshot.bytes (that metric never counted raw outer keys). */
|
|
464
|
+
export function antigravityReplayKeyForTests(model: string, sessionId: string): string {
|
|
465
|
+
return replayKey(model, sessionId);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export function antigravityFunctionCallKeyForTests(name: unknown, args: unknown): string | undefined {
|
|
469
|
+
return functionCallKey(name, args);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** Test-only bounded-canonicalization seam: proves mid-walk rejection without
|
|
473
|
+
* materializing the escaped form (allocation guard). */
|
|
474
|
+
export function antigravityCanonicalJsonBoundedForTests(value: unknown, maxBytes: number): string | null {
|
|
475
|
+
return canonicalJsonBounded(value, maxBytes);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** Test-only: the ACTUAL internal session keys, so tests can prove raw
|
|
479
|
+
* model/session strings are never retained as Map keys. */
|
|
480
|
+
export function antigravityReplaySessionKeysForTests(): string[] {
|
|
481
|
+
return [...replayCache.keys()];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function extractSignature(part: Record<string, unknown>): string | undefined {
|
|
485
|
+
const direct = part.thoughtSignature ?? part.thought_signature;
|
|
486
|
+
if (typeof direct === "string" && direct.length >= MIN_SIGNATURE_LEN) return direct;
|
|
487
|
+
const extra = part.extra_content as { google?: { thought_signature?: unknown } } | undefined;
|
|
488
|
+
const nested = extra?.google?.thought_signature;
|
|
489
|
+
if (typeof nested === "string" && nested.length >= MIN_SIGNATURE_LEN) return nested;
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function deleteReplaySession(key: string): number {
|
|
494
|
+
const entry = replayCache.get(key);
|
|
495
|
+
if (!entry) return 0;
|
|
496
|
+
replayCache.delete(key);
|
|
497
|
+
replayBytes -= entry.bytes;
|
|
498
|
+
if (replayOldestSessionKey === key) recomputeReplayOldestCandidate();
|
|
499
|
+
return entry.bytes;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function recomputeReplayOldestCandidate(): void {
|
|
503
|
+
replayOldestSessionKey = undefined;
|
|
504
|
+
replayOldestAt = null;
|
|
505
|
+
for (const [key, entry] of replayCache) {
|
|
506
|
+
if (entry.oldestAtMs === null || (replayOldestAt !== null && entry.oldestAtMs >= replayOldestAt)) continue;
|
|
507
|
+
replayOldestSessionKey = key;
|
|
508
|
+
replayOldestAt = entry.oldestAtMs;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function refreshReplaySessionCandidate(key: string, entry: ReplayEntry): void {
|
|
513
|
+
entry.oldestAtMs = entry.byCall.values().next().value?.touchedAtMs ?? null;
|
|
514
|
+
if (replayOldestSessionKey === key) {
|
|
515
|
+
recomputeReplayOldestCandidate();
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (entry.oldestAtMs !== null && (replayOldestAt === null || entry.oldestAtMs < replayOldestAt)) {
|
|
519
|
+
replayOldestSessionKey = key;
|
|
520
|
+
replayOldestAt = entry.oldestAtMs;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function deleteExpiredReplaySessions(now: number): void {
|
|
525
|
+
let deleted = false;
|
|
526
|
+
for (const [key, entry] of replayCache) {
|
|
527
|
+
if (entry.expiresAtMs > now) continue;
|
|
528
|
+
deleteReplaySession(key);
|
|
529
|
+
deleted = true;
|
|
530
|
+
}
|
|
531
|
+
// Expiry is a durable mutation too: rewrite the snapshot so opaque thought
|
|
532
|
+
// signatures do not remain at rest after their in-memory TTL has elapsed.
|
|
533
|
+
if (deleted) markReplayDirty();
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* The lazy per-call expiry scan is O(sessions); at the 10,240-session cap
|
|
538
|
+
* every observe/apply would rescan the whole map — O(n²) under load. The 60s
|
|
539
|
+
* state-store sweeper is already the periodic expiry authority, so lazy scans
|
|
540
|
+
* are throttled to at most one per interval (expired entries may linger a few
|
|
541
|
+
* extra seconds; TTL is fuzzy at that scale by design).
|
|
542
|
+
*/
|
|
543
|
+
const LAZY_SWEEP_INTERVAL_MS = 30_000;
|
|
544
|
+
let lastLazySweepAt = Number.NEGATIVE_INFINITY;
|
|
545
|
+
|
|
546
|
+
function deleteExpiredReplaySessionsThrottled(now: number): void {
|
|
547
|
+
if (now - lastLazySweepAt < LAZY_SWEEP_INTERVAL_MS) return;
|
|
548
|
+
lastLazySweepAt = now;
|
|
549
|
+
deleteExpiredReplaySessions(now);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export function sweepExpiredAntigravityReplay(now = Date.now()): number {
|
|
553
|
+
const before = replayCache.size;
|
|
554
|
+
deleteExpiredReplaySessions(now);
|
|
555
|
+
return before - replayCache.size;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function deleteReplayCall(entry: ReplayEntry, callKey: string): number {
|
|
559
|
+
const call = entry.byCall.get(callKey);
|
|
560
|
+
if (!call) return 0;
|
|
561
|
+
entry.byCall.delete(callKey);
|
|
562
|
+
entry.bytes -= call.sizeBytes;
|
|
563
|
+
replayBytes -= call.sizeBytes;
|
|
564
|
+
return call.sizeBytes;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function evictInnerCalls(entry: ReplayEntry): void {
|
|
568
|
+
while (
|
|
569
|
+
entry.byCall.size > replayLimits.maxCallsPerSession
|
|
570
|
+
|| entry.bytes > replayLimits.maxBytesPerSession
|
|
571
|
+
) {
|
|
572
|
+
const oldest = entry.byCall.keys().next().value;
|
|
573
|
+
if (oldest === undefined) break;
|
|
574
|
+
deleteReplayCall(entry, oldest);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function evictIfNeeded(): void {
|
|
579
|
+
if (replayCache.size > ANTIGRAVITY_REPLAY_MAX_ENTRIES) {
|
|
580
|
+
const oldest = [...replayCache.entries()]
|
|
581
|
+
.sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs)
|
|
582
|
+
.slice(0, REPLAY_EVICT_BATCH);
|
|
583
|
+
for (const [key] of oldest) deleteReplaySession(key);
|
|
584
|
+
}
|
|
585
|
+
while (replayBytes > ANTIGRAVITY_REPLAY_MAX_TOTAL_BYTES) {
|
|
586
|
+
const oldestKey = [...replayCache.entries()]
|
|
587
|
+
.sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs)[0]?.[0];
|
|
588
|
+
if (oldestKey === undefined) return;
|
|
589
|
+
deleteReplaySession(oldestKey);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/** Gemini/Flash/Agent use the replay cache; Claude does not (inline sanitization instead). */
|
|
594
|
+
export function antigravityUsesReplayCache(model: string): boolean {
|
|
595
|
+
return !/claude/i.test(model);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Observe a parsed CCA chunk's `candidates[0].content.parts` and record thought signatures keyed by
|
|
600
|
+
* the functionCall identity (name + args). Accumulates across the whole session so a sequential
|
|
601
|
+
* multi-step tool loop keeps EVERY prior call's signature, not just the latest part-index slot.
|
|
602
|
+
* A signature on a standalone thought part applies to the functionCall parts that follow it in
|
|
603
|
+
* the same array AND to later arrays of the same turn: streaming splits a thought part and its
|
|
604
|
+
* calls across SSE chunks, so `carriedThoughtSig` threads the still-unpaired signature from the
|
|
605
|
+
* previous chunk and the return value hands the remainder to the next one (#897, #2125). A call's
|
|
606
|
+
* own signature always takes precedence over a carried one.
|
|
607
|
+
* `parts` is the already-unwrapped `response.candidates[0].content.parts`.
|
|
608
|
+
*/
|
|
609
|
+
export function observeAntigravityReplay(
|
|
610
|
+
model: string,
|
|
611
|
+
sessionId: string,
|
|
612
|
+
parts: unknown[],
|
|
613
|
+
carriedThoughtSig?: string,
|
|
614
|
+
): string | undefined {
|
|
615
|
+
if (!antigravityUsesReplayCache(model) || !Array.isArray(parts) || parts.length === 0) return carriedThoughtSig;
|
|
616
|
+
ensureReplaySnapshotLoaded();
|
|
617
|
+
const now = Date.now();
|
|
618
|
+
deleteExpiredReplaySessionsThrottled(now);
|
|
619
|
+
const key = replayKey(model, sessionId);
|
|
620
|
+
const existing = replayCache.get(key);
|
|
621
|
+
const entry = existing ?? {
|
|
622
|
+
byCall: new Map<string, ReplayCall>(),
|
|
623
|
+
bytes: REPLAY_SESSION_KEY_BYTES,
|
|
624
|
+
expiresAtMs: 0,
|
|
625
|
+
oldestAtMs: null,
|
|
626
|
+
lastActiveAtMs: 0,
|
|
627
|
+
};
|
|
628
|
+
let inserted = false;
|
|
629
|
+
let pendingThoughtSig: string | undefined = carriedThoughtSig;
|
|
630
|
+
for (const raw of parts) {
|
|
631
|
+
if (!raw || typeof raw !== "object") continue;
|
|
632
|
+
const part = raw as Record<string, unknown>;
|
|
633
|
+
const sig = extractSignature(part);
|
|
634
|
+
const fc = part.functionCall as { name?: unknown; args?: unknown } | undefined;
|
|
635
|
+
if (!fc) {
|
|
636
|
+
// A signature on a standalone thought part pairs with the NEXT functionCall in this
|
|
637
|
+
// array: thought parts are stripped from replayed history, so the call part is the only
|
|
638
|
+
// carrier that survives (#897). Non-thought parts keep their own signature in history.
|
|
639
|
+
if (sig && part.thought === true) pendingThoughtSig = sig;
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
const callSig = sig ?? pendingThoughtSig; // a signature on the call part itself wins
|
|
643
|
+
if (!callSig) continue;
|
|
644
|
+
const ck = functionCallKey(fc.name, fc.args);
|
|
645
|
+
if (!ck) continue; // only function-call signatures are replayable by identity
|
|
646
|
+
const signatureBytes = utf8.encode(callSig).byteLength;
|
|
647
|
+
const sizeBytes = utf8.encode(ck).byteLength + signatureBytes;
|
|
648
|
+
if (signatureBytes > replayLimits.maxSignatureBytes || sizeBytes > replayLimits.maxBytesPerSession) continue;
|
|
649
|
+
deleteReplayCall(entry, ck);
|
|
650
|
+
entry.byCall.set(ck, { signature: callSig, sizeBytes, touchedAtMs: now });
|
|
651
|
+
entry.bytes += sizeBytes;
|
|
652
|
+
replayBytes += sizeBytes;
|
|
653
|
+
inserted = true;
|
|
654
|
+
}
|
|
655
|
+
if (!inserted) return pendingThoughtSig;
|
|
656
|
+
// Charge the fixed outer key only when the session is actually stored.
|
|
657
|
+
if (!existing) replayBytes += REPLAY_SESSION_KEY_BYTES;
|
|
658
|
+
evictInnerCalls(entry);
|
|
659
|
+
if (entry.byCall.size === 0) {
|
|
660
|
+
// The fixed session overhead can exceed the per-session cap on its own
|
|
661
|
+
// (test-sized limits): an entry holding zero calls is unusable — drop it
|
|
662
|
+
// instead of retaining an unevictable shell.
|
|
663
|
+
if (existing) {
|
|
664
|
+
deleteReplaySession(key);
|
|
665
|
+
markReplayDirty();
|
|
666
|
+
} else {
|
|
667
|
+
replayBytes -= REPLAY_SESSION_KEY_BYTES;
|
|
668
|
+
}
|
|
669
|
+
return pendingThoughtSig;
|
|
670
|
+
}
|
|
671
|
+
entry.expiresAtMs = now + REPLAY_TTL_MS;
|
|
672
|
+
entry.lastActiveAtMs = now;
|
|
673
|
+
replayCache.set(key, entry);
|
|
674
|
+
refreshReplaySessionCandidate(key, entry);
|
|
675
|
+
evictIfNeeded();
|
|
676
|
+
enforceAppOwnedMemoryBudget();
|
|
677
|
+
markReplayDirty();
|
|
678
|
+
return pendingThoughtSig;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Re-inject cached thought signatures into the outgoing `request.contents`, matched by functionCall
|
|
683
|
+
* identity across ALL model turns (not just the last one). Only fills a functionCall part that
|
|
684
|
+
* lacks a real signature. Returns the same array reference (mutated in place).
|
|
685
|
+
*/
|
|
686
|
+
export function applyAntigravityReplay(model: string, sessionId: string, contents: unknown[]): unknown[] {
|
|
687
|
+
if (!antigravityUsesReplayCache(model) || !Array.isArray(contents)) return contents;
|
|
688
|
+
ensureReplaySnapshotLoaded();
|
|
689
|
+
const now = Date.now();
|
|
690
|
+
deleteExpiredReplaySessionsThrottled(now);
|
|
691
|
+
const entry = replayCache.get(replayKey(model, sessionId));
|
|
692
|
+
if (!entry) {
|
|
693
|
+
return contents;
|
|
694
|
+
}
|
|
695
|
+
let touched = false;
|
|
696
|
+
for (const c of contents as { role?: string; parts?: unknown[] }[]) {
|
|
697
|
+
if (!c || typeof c !== "object" || c.role !== "model" || !Array.isArray(c.parts)) continue;
|
|
698
|
+
for (const raw of c.parts) {
|
|
699
|
+
if (!raw || typeof raw !== "object") continue;
|
|
700
|
+
const part = raw as Record<string, unknown>;
|
|
701
|
+
const fc = part.functionCall as { name?: unknown; args?: unknown } | undefined;
|
|
702
|
+
if (!fc) continue;
|
|
703
|
+
if (part.thoughtSignature !== undefined || part.thought_signature !== undefined) continue;
|
|
704
|
+
const ck = functionCallKey(fc.name, fc.args);
|
|
705
|
+
let call = ck ? entry.byCall.get(ck) : undefined;
|
|
706
|
+
let matchedKey = ck;
|
|
707
|
+
if (!call && typeof fc.name === "string" && typeof fc.args === "object" && fc.args !== null) {
|
|
708
|
+
// Freeform / custom tool replay unwrap:
|
|
709
|
+
// The client replays custom_tool_call with arguments: { input: "..." }.
|
|
710
|
+
// Upstream was invoked with args: { input: "..." } or raw string or parsed JSON.
|
|
711
|
+
const argsObj = fc.args as Record<string, unknown>;
|
|
712
|
+
if (typeof argsObj.input === "string") {
|
|
713
|
+
const trimmedInput = argsObj.input.trim();
|
|
714
|
+
if (
|
|
715
|
+
trimmedInput.length <= REPLAY_MAX_CANONICAL_ARGS_BYTES
|
|
716
|
+
&& utf8.encode(trimmedInput).byteLength <= REPLAY_MAX_CANONICAL_ARGS_BYTES
|
|
717
|
+
) {
|
|
718
|
+
try {
|
|
719
|
+
const parsedInput = JSON.parse(trimmedInput);
|
|
720
|
+
if (parsedInput && typeof parsedInput === "object") {
|
|
721
|
+
const altKey = functionCallKey(fc.name, parsedInput);
|
|
722
|
+
if (altKey && entry.byCall.has(altKey)) {
|
|
723
|
+
call = entry.byCall.get(altKey);
|
|
724
|
+
matchedKey = altKey;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
} catch {
|
|
728
|
+
// not JSON, keep default
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (call && matchedKey) {
|
|
734
|
+
part.thoughtSignature = call.signature;
|
|
735
|
+
entry.byCall.delete(matchedKey);
|
|
736
|
+
entry.byCall.set(matchedKey, { ...call, touchedAtMs: now });
|
|
737
|
+
touched = true;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (touched) {
|
|
742
|
+
entry.lastActiveAtMs = now;
|
|
743
|
+
refreshReplaySessionCandidate(replayKey(model, sessionId), entry);
|
|
744
|
+
markReplayDirty();
|
|
745
|
+
}
|
|
746
|
+
return contents;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/** Drop the cache entry when upstream rejects a signature (clear-on-invalid). */
|
|
750
|
+
export function clearAntigravityReplay(model: string, sessionId: string): void {
|
|
751
|
+
ensureReplaySnapshotLoaded();
|
|
752
|
+
if (deleteReplaySession(replayKey(model, sessionId)) > 0) markReplayDirty();
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
export function antigravityReplayMetrics(): {
|
|
756
|
+
sessions: number;
|
|
757
|
+
calls: number;
|
|
758
|
+
totalBytes: number;
|
|
759
|
+
largestSessionBytes: number;
|
|
760
|
+
} {
|
|
761
|
+
ensureReplaySnapshotLoaded();
|
|
762
|
+
let calls = 0;
|
|
763
|
+
let largestSessionBytes = 0;
|
|
764
|
+
for (const entry of replayCache.values()) {
|
|
765
|
+
calls += entry.byCall.size;
|
|
766
|
+
largestSessionBytes = Math.max(largestSessionBytes, entry.bytes);
|
|
767
|
+
}
|
|
768
|
+
return { sessions: replayCache.size, calls, totalBytes: replayBytes, largestSessionBytes };
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
export function antigravityReplayRetainedStoreSnapshot(): {
|
|
772
|
+
count: number;
|
|
773
|
+
bytes: number;
|
|
774
|
+
evictableBytes: number;
|
|
775
|
+
pinnedBytes: number;
|
|
776
|
+
oldestAt: number | null;
|
|
777
|
+
} {
|
|
778
|
+
ensureReplaySnapshotLoaded();
|
|
779
|
+
return {
|
|
780
|
+
count: replayCache.size,
|
|
781
|
+
bytes: replayBytes,
|
|
782
|
+
evictableBytes: replayBytes,
|
|
783
|
+
pinnedBytes: 0,
|
|
784
|
+
oldestAt: replayOldestAt,
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
export function evictOldestAntigravityReplayForBudget(): number {
|
|
789
|
+
ensureReplaySnapshotLoaded();
|
|
790
|
+
const removed = replayOldestSessionKey === undefined ? 0 : deleteReplaySession(replayOldestSessionKey);
|
|
791
|
+
if (removed > 0) markReplayDirty();
|
|
792
|
+
return removed;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
export function setAntigravityReplayLimitsForTests(limits?: Partial<ReplayLimits>): void {
|
|
796
|
+
__resetAntigravityReplayCache();
|
|
797
|
+
replayLimits = limits ? { ...DEFAULT_REPLAY_LIMITS, ...limits } : { ...DEFAULT_REPLAY_LIMITS };
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/** Test-only write seam (mirrors AtomicWriteAsyncTestSeam usage elsewhere). */
|
|
801
|
+
export function setAntigravityReplayWriteSeamForTests(seam: AtomicWriteAsyncTestSeam | undefined): void {
|
|
802
|
+
replaySnapshotWriteSeam = seam;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/** Test-only snapshot byte cap (mirrors the limits seam). */
|
|
806
|
+
export function setAntigravityReplaySnapshotMaxBytesForTests(maxBytes: number): void {
|
|
807
|
+
replaySnapshotMaxBytes = maxBytes;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** Test seam. */
|
|
811
|
+
export function __resetAntigravityReplayCache(): void {
|
|
812
|
+
if (replaySnapshotPersistTimer) {
|
|
813
|
+
clearTimeout(replaySnapshotPersistTimer);
|
|
814
|
+
replaySnapshotPersistTimer = null;
|
|
815
|
+
}
|
|
816
|
+
replaySnapshotLoaded = false;
|
|
817
|
+
lastLazySweepAt = Number.NEGATIVE_INFINITY;
|
|
818
|
+
replayCache.clear();
|
|
819
|
+
replayBytes = 0;
|
|
820
|
+
replayOldestSessionKey = undefined;
|
|
821
|
+
replayOldestAt = null;
|
|
822
|
+
replayMutationGeneration = 0;
|
|
823
|
+
replayWrittenGeneration = 0;
|
|
824
|
+
replaySnapshotWriteSeam = undefined;
|
|
825
|
+
replaySnapshotLoadDiscarded = false;
|
|
826
|
+
replaySnapshotMaxBytes = REPLAY_SNAPSHOT_MAX_BYTES;
|
|
827
|
+
}
|