agenticreplay 0.1.0 → 0.1.2
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/README.md +14 -13
- package/bin/agenticreplay.js +2 -0
- package/node_modules/@agenticreplay/adapters/dist/claude-code.d.ts +37 -0
- package/node_modules/@agenticreplay/adapters/dist/claude-code.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/claude-code.js +103 -0
- package/node_modules/@agenticreplay/adapters/dist/claude-code.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/codex.d.ts +36 -0
- package/node_modules/@agenticreplay/adapters/dist/codex.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/codex.js +130 -0
- package/node_modules/@agenticreplay/adapters/dist/codex.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/contract.d.ts +65 -0
- package/node_modules/@agenticreplay/adapters/dist/contract.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/contract.js +460 -0
- package/node_modules/@agenticreplay/adapters/dist/contract.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/cursor.d.ts +40 -0
- package/node_modules/@agenticreplay/adapters/dist/cursor.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/cursor.js +71 -0
- package/node_modules/@agenticreplay/adapters/dist/cursor.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/detect.d.ts +47 -0
- package/node_modules/@agenticreplay/adapters/dist/detect.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/detect.js +123 -0
- package/node_modules/@agenticreplay/adapters/dist/detect.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/env.d.ts +60 -0
- package/node_modules/@agenticreplay/adapters/dist/env.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/env.js +99 -0
- package/node_modules/@agenticreplay/adapters/dist/env.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/exec.d.ts +25 -0
- package/node_modules/@agenticreplay/adapters/dist/exec.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/exec.js +51 -0
- package/node_modules/@agenticreplay/adapters/dist/exec.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/generic-openai.d.ts +8 -0
- package/node_modules/@agenticreplay/adapters/dist/generic-openai.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/generic-openai.js +31 -0
- package/node_modules/@agenticreplay/adapters/dist/generic-openai.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/goose.d.ts +39 -0
- package/node_modules/@agenticreplay/adapters/dist/goose.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/goose.js +81 -0
- package/node_modules/@agenticreplay/adapters/dist/goose.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/grok.d.ts +23 -0
- package/node_modules/@agenticreplay/adapters/dist/grok.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/grok.js +50 -0
- package/node_modules/@agenticreplay/adapters/dist/grok.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/hermes.d.ts +70 -0
- package/node_modules/@agenticreplay/adapters/dist/hermes.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/hermes.js +88 -0
- package/node_modules/@agenticreplay/adapters/dist/hermes.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/index.d.ts +25 -0
- package/node_modules/@agenticreplay/adapters/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/index.js +25 -0
- package/node_modules/@agenticreplay/adapters/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/indexrag.d.ts +27 -0
- package/node_modules/@agenticreplay/adapters/dist/indexrag.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/indexrag.js +236 -0
- package/node_modules/@agenticreplay/adapters/dist/indexrag.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/kilo.d.ts +21 -0
- package/node_modules/@agenticreplay/adapters/dist/kilo.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/kilo.js +35 -0
- package/node_modules/@agenticreplay/adapters/dist/kilo.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/mcode.d.ts +81 -0
- package/node_modules/@agenticreplay/adapters/dist/mcode.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/mcode.js +368 -0
- package/node_modules/@agenticreplay/adapters/dist/mcode.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/mcp-config.d.ts +17 -0
- package/node_modules/@agenticreplay/adapters/dist/mcp-config.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/mcp-config.js +62 -0
- package/node_modules/@agenticreplay/adapters/dist/mcp-config.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/mimo.d.ts +26 -0
- package/node_modules/@agenticreplay/adapters/dist/mimo.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/mimo.js +40 -0
- package/node_modules/@agenticreplay/adapters/dist/mimo.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/node.d.ts +19 -0
- package/node_modules/@agenticreplay/adapters/dist/node.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/node.js +59 -0
- package/node_modules/@agenticreplay/adapters/dist/node.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/openclaw.d.ts +19 -0
- package/node_modules/@agenticreplay/adapters/dist/openclaw.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/openclaw.js +43 -0
- package/node_modules/@agenticreplay/adapters/dist/openclaw.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-api-bases.d.ts +2 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-api-bases.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-api-bases.js +201 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-api-bases.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-capture.d.ts +26 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-capture.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-capture.js +132 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-capture.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-catalog.d.ts +5 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-catalog.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-catalog.js +43 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-catalog.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-config.d.ts +40 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-config.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-config.js +185 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode-config.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode.d.ts +10 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode.js +243 -0
- package/node_modules/@agenticreplay/adapters/dist/opencode.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/qwen.d.ts +51 -0
- package/node_modules/@agenticreplay/adapters/dist/qwen.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/qwen.js +105 -0
- package/node_modules/@agenticreplay/adapters/dist/qwen.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/registry.d.ts +19 -0
- package/node_modules/@agenticreplay/adapters/dist/registry.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/registry.js +110 -0
- package/node_modules/@agenticreplay/adapters/dist/registry.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/scaffold.d.ts +26 -0
- package/node_modules/@agenticreplay/adapters/dist/scaffold.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/scaffold.js +125 -0
- package/node_modules/@agenticreplay/adapters/dist/scaffold.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/session.d.ts +47 -0
- package/node_modules/@agenticreplay/adapters/dist/session.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/session.js +112 -0
- package/node_modules/@agenticreplay/adapters/dist/session.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/zcode.d.ts +60 -0
- package/node_modules/@agenticreplay/adapters/dist/zcode.d.ts.map +1 -0
- package/node_modules/@agenticreplay/adapters/dist/zcode.js +424 -0
- package/node_modules/@agenticreplay/adapters/dist/zcode.js.map +1 -0
- package/node_modules/@agenticreplay/adapters/package.json +29 -0
- package/node_modules/@agenticreplay/core/dist/blobs.d.ts +21 -0
- package/node_modules/@agenticreplay/core/dist/blobs.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/blobs.js +102 -0
- package/node_modules/@agenticreplay/core/dist/blobs.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/fetch.d.ts +38 -0
- package/node_modules/@agenticreplay/core/dist/fetch.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/fetch.js +47 -0
- package/node_modules/@agenticreplay/core/dist/fetch.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/graph.d.ts +106 -0
- package/node_modules/@agenticreplay/core/dist/graph.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/graph.js +394 -0
- package/node_modules/@agenticreplay/core/dist/graph.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/index.d.ts +9 -0
- package/node_modules/@agenticreplay/core/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/index.js +9 -0
- package/node_modules/@agenticreplay/core/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/paths.d.ts +45 -0
- package/node_modules/@agenticreplay/core/dist/paths.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/paths.js +149 -0
- package/node_modules/@agenticreplay/core/dist/paths.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/private-files.d.ts +37 -0
- package/node_modules/@agenticreplay/core/dist/private-files.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/private-files.js +353 -0
- package/node_modules/@agenticreplay/core/dist/private-files.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/reader.d.ts +45 -0
- package/node_modules/@agenticreplay/core/dist/reader.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/reader.js +152 -0
- package/node_modules/@agenticreplay/core/dist/reader.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/redaction.d.ts +69 -0
- package/node_modules/@agenticreplay/core/dist/redaction.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/redaction.js +768 -0
- package/node_modules/@agenticreplay/core/dist/redaction.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/writer.d.ts +75 -0
- package/node_modules/@agenticreplay/core/dist/writer.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/writer.js +368 -0
- package/node_modules/@agenticreplay/core/dist/writer.js.map +1 -0
- package/node_modules/@agenticreplay/core/package.json +26 -0
- package/node_modules/@agenticreplay/fs-capture/dist/capture.d.ts +84 -0
- package/node_modules/@agenticreplay/fs-capture/dist/capture.d.ts.map +1 -0
- package/node_modules/@agenticreplay/fs-capture/dist/capture.js +147 -0
- package/node_modules/@agenticreplay/fs-capture/dist/capture.js.map +1 -0
- package/node_modules/@agenticreplay/fs-capture/dist/git.d.ts +25 -0
- package/node_modules/@agenticreplay/fs-capture/dist/git.d.ts.map +1 -0
- package/node_modules/@agenticreplay/fs-capture/dist/git.js +108 -0
- package/node_modules/@agenticreplay/fs-capture/dist/git.js.map +1 -0
- package/node_modules/@agenticreplay/fs-capture/dist/index.d.ts +4 -0
- package/node_modules/@agenticreplay/fs-capture/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/fs-capture/dist/index.js +4 -0
- package/node_modules/@agenticreplay/fs-capture/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/fs-capture/dist/shadow.d.ts +271 -0
- package/node_modules/@agenticreplay/fs-capture/dist/shadow.d.ts.map +1 -0
- package/node_modules/@agenticreplay/fs-capture/dist/shadow.js +668 -0
- package/node_modules/@agenticreplay/fs-capture/dist/shadow.js.map +1 -0
- package/node_modules/@agenticreplay/fs-capture/package.json +25 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/cli.d.ts +100 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/cli.d.ts.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/cli.js +334 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/cli.js.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/framing.d.ts +41 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/framing.d.ts.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/framing.js +94 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/framing.js.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/index.d.ts +5 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/index.js +5 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/mock.d.ts +70 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/mock.d.ts.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/mock.js +138 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/mock.js.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/shim.d.ts +24 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/shim.d.ts.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/shim.js +105 -0
- package/node_modules/@agenticreplay/mcp-shim/dist/shim.js.map +1 -0
- package/node_modules/@agenticreplay/mcp-shim/package.json +26 -0
- package/node_modules/@agenticreplay/node-instrument/dist/capture.d.ts +58 -0
- package/node_modules/@agenticreplay/node-instrument/dist/capture.d.ts.map +1 -0
- package/node_modules/@agenticreplay/node-instrument/dist/capture.js +404 -0
- package/node_modules/@agenticreplay/node-instrument/dist/capture.js.map +1 -0
- package/node_modules/@agenticreplay/node-instrument/dist/index.d.ts +4 -0
- package/node_modules/@agenticreplay/node-instrument/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/node-instrument/dist/index.js +4 -0
- package/node_modules/@agenticreplay/node-instrument/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/node-instrument/dist/install.d.ts +47 -0
- package/node_modules/@agenticreplay/node-instrument/dist/install.d.ts.map +1 -0
- package/node_modules/@agenticreplay/node-instrument/dist/install.js +174 -0
- package/node_modules/@agenticreplay/node-instrument/dist/install.js.map +1 -0
- package/node_modules/@agenticreplay/node-instrument/dist/rewrite.d.ts +33 -0
- package/node_modules/@agenticreplay/node-instrument/dist/rewrite.d.ts.map +1 -0
- package/node_modules/@agenticreplay/node-instrument/dist/rewrite.js +61 -0
- package/node_modules/@agenticreplay/node-instrument/dist/rewrite.js.map +1 -0
- package/node_modules/@agenticreplay/node-instrument/package.json +23 -0
- package/node_modules/@agenticreplay/plugin-api/dist/canonical.d.ts +87 -0
- package/node_modules/@agenticreplay/plugin-api/dist/canonical.d.ts.map +1 -0
- package/node_modules/@agenticreplay/plugin-api/dist/canonical.js +10 -0
- package/node_modules/@agenticreplay/plugin-api/dist/canonical.js.map +1 -0
- package/node_modules/@agenticreplay/plugin-api/dist/index.d.ts +3 -0
- package/node_modules/@agenticreplay/plugin-api/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/plugin-api/dist/index.js +3 -0
- package/node_modules/@agenticreplay/plugin-api/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/plugin-api/dist/plugins.d.ts +258 -0
- package/node_modules/@agenticreplay/plugin-api/dist/plugins.d.ts.map +1 -0
- package/node_modules/@agenticreplay/plugin-api/dist/plugins.js +2 -0
- package/node_modules/@agenticreplay/plugin-api/dist/plugins.js.map +1 -0
- package/node_modules/@agenticreplay/plugin-api/package.json +22 -0
- package/node_modules/@agenticreplay/providers/dist/index.d.ts +34 -0
- package/node_modules/@agenticreplay/providers/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/index.js +34 -0
- package/node_modules/@agenticreplay/providers/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/pricing.d.ts +44 -0
- package/node_modules/@agenticreplay/providers/dist/pricing.d.ts.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/pricing.js +126 -0
- package/node_modules/@agenticreplay/providers/dist/pricing.js.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/anthropic.d.ts +66 -0
- package/node_modules/@agenticreplay/providers/dist/translate/anthropic.d.ts.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/anthropic.js +569 -0
- package/node_modules/@agenticreplay/providers/dist/translate/anthropic.js.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/openai-responses.d.ts +51 -0
- package/node_modules/@agenticreplay/providers/dist/translate/openai-responses.d.ts.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/openai-responses.js +574 -0
- package/node_modules/@agenticreplay/providers/dist/translate/openai-responses.js.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/openai.d.ts +60 -0
- package/node_modules/@agenticreplay/providers/dist/translate/openai.d.ts.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/openai.js +577 -0
- package/node_modules/@agenticreplay/providers/dist/translate/openai.js.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/sse.d.ts +29 -0
- package/node_modules/@agenticreplay/providers/dist/translate/sse.d.ts.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/sse.js +97 -0
- package/node_modules/@agenticreplay/providers/dist/translate/sse.js.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/util.d.ts +34 -0
- package/node_modules/@agenticreplay/providers/dist/translate/util.d.ts.map +1 -0
- package/node_modules/@agenticreplay/providers/dist/translate/util.js +106 -0
- package/node_modules/@agenticreplay/providers/dist/translate/util.js.map +1 -0
- package/node_modules/@agenticreplay/providers/package.json +26 -0
- package/node_modules/@agenticreplay/proxy/dist/ca.d.ts +45 -0
- package/node_modules/@agenticreplay/proxy/dist/ca.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/ca.js +264 -0
- package/node_modules/@agenticreplay/proxy/dist/ca.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/der.d.ts +51 -0
- package/node_modules/@agenticreplay/proxy/dist/der.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/der.js +144 -0
- package/node_modules/@agenticreplay/proxy/dist/der.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/dialects.d.ts +81 -0
- package/node_modules/@agenticreplay/proxy/dist/dialects.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/dialects.js +329 -0
- package/node_modules/@agenticreplay/proxy/dist/dialects.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/forward.d.ts +34 -0
- package/node_modules/@agenticreplay/proxy/dist/forward.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/forward.js +44 -0
- package/node_modules/@agenticreplay/proxy/dist/forward.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/index.d.ts +9 -0
- package/node_modules/@agenticreplay/proxy/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/index.js +9 -0
- package/node_modules/@agenticreplay/proxy/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/intercept.d.ts +234 -0
- package/node_modules/@agenticreplay/proxy/dist/intercept.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/intercept.js +1258 -0
- package/node_modules/@agenticreplay/proxy/dist/intercept.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/matching.d.ts +109 -0
- package/node_modules/@agenticreplay/proxy/dist/matching.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/matching.js +928 -0
- package/node_modules/@agenticreplay/proxy/dist/matching.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/retrieval.d.ts +93 -0
- package/node_modules/@agenticreplay/proxy/dist/retrieval.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/retrieval.js +233 -0
- package/node_modules/@agenticreplay/proxy/dist/retrieval.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/server.d.ts +327 -0
- package/node_modules/@agenticreplay/proxy/dist/server.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/server.js +2291 -0
- package/node_modules/@agenticreplay/proxy/dist/server.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/tls-hosts.d.ts +54 -0
- package/node_modules/@agenticreplay/proxy/dist/tls-hosts.d.ts.map +1 -0
- package/node_modules/@agenticreplay/proxy/dist/tls-hosts.js +178 -0
- package/node_modules/@agenticreplay/proxy/dist/tls-hosts.js.map +1 -0
- package/node_modules/@agenticreplay/proxy/package.json +28 -0
- package/node_modules/@agenticreplay/schema/dist/constants.d.ts +30 -0
- package/node_modules/@agenticreplay/schema/dist/constants.d.ts.map +1 -0
- package/node_modules/@agenticreplay/schema/dist/constants.js +66 -0
- package/node_modules/@agenticreplay/schema/dist/constants.js.map +1 -0
- package/node_modules/@agenticreplay/schema/dist/index.d.ts +4 -0
- package/node_modules/@agenticreplay/schema/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/schema/dist/index.js +4 -0
- package/node_modules/@agenticreplay/schema/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/schema/dist/types.d.ts +97 -0
- package/node_modules/@agenticreplay/schema/dist/types.d.ts.map +1 -0
- package/node_modules/@agenticreplay/schema/dist/types.js +7 -0
- package/node_modules/@agenticreplay/schema/dist/types.js.map +1 -0
- package/node_modules/@agenticreplay/schema/dist/validate.d.ts +13 -0
- package/node_modules/@agenticreplay/schema/dist/validate.d.ts.map +1 -0
- package/node_modules/@agenticreplay/schema/dist/validate.js +44 -0
- package/node_modules/@agenticreplay/schema/dist/validate.js.map +1 -0
- package/node_modules/@agenticreplay/schema/package.json +28 -0
- package/node_modules/@agenticreplay/shell-shim/dist/index.d.ts +4 -0
- package/node_modules/@agenticreplay/shell-shim/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/index.js +4 -0
- package/node_modules/@agenticreplay/shell-shim/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/install.d.ts +75 -0
- package/node_modules/@agenticreplay/shell-shim/dist/install.d.ts.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/install.js +353 -0
- package/node_modules/@agenticreplay/shell-shim/dist/install.js.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/resolve.d.ts +2 -0
- package/node_modules/@agenticreplay/shell-shim/dist/resolve.d.ts.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/resolve.js +97 -0
- package/node_modules/@agenticreplay/shell-shim/dist/resolve.js.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/runner-bin.d.ts +3 -0
- package/node_modules/@agenticreplay/shell-shim/dist/runner-bin.d.ts.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/runner-bin.js +4 -0
- package/node_modules/@agenticreplay/shell-shim/dist/runner-bin.js.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/runner.d.ts +45 -0
- package/node_modules/@agenticreplay/shell-shim/dist/runner.d.ts.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/dist/runner.js +105 -0
- package/node_modules/@agenticreplay/shell-shim/dist/runner.js.map +1 -0
- package/node_modules/@agenticreplay/shell-shim/package.json +26 -0
- package/node_modules/@agenticreplay/viewer/dist/bundle.d.ts +11 -0
- package/node_modules/@agenticreplay/viewer/dist/bundle.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/bundle.js +36 -0
- package/node_modules/@agenticreplay/viewer/dist/bundle.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/client/main.d.ts +12 -0
- package/node_modules/@agenticreplay/viewer/dist/client/main.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/client/main.js +249 -0
- package/node_modules/@agenticreplay/viewer/dist/client/main.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/credit.d.ts +22 -0
- package/node_modules/@agenticreplay/viewer/dist/credit.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/credit.js +22 -0
- package/node_modules/@agenticreplay/viewer/dist/credit.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/css.d.ts +11 -0
- package/node_modules/@agenticreplay/viewer/dist/css.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/css.js +410 -0
- package/node_modules/@agenticreplay/viewer/dist/css.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/export.d.ts +21 -0
- package/node_modules/@agenticreplay/viewer/dist/export.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/export.js +22 -0
- package/node_modules/@agenticreplay/viewer/dist/export.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/html.d.ts +32 -0
- package/node_modules/@agenticreplay/viewer/dist/html.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/html.js +252 -0
- package/node_modules/@agenticreplay/viewer/dist/html.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/index.d.ts +16 -0
- package/node_modules/@agenticreplay/viewer/dist/index.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/index.js +16 -0
- package/node_modules/@agenticreplay/viewer/dist/index.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/render.d.ts +126 -0
- package/node_modules/@agenticreplay/viewer/dist/render.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/render.js +699 -0
- package/node_modules/@agenticreplay/viewer/dist/render.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/serve.d.ts +21 -0
- package/node_modules/@agenticreplay/viewer/dist/serve.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/serve.js +122 -0
- package/node_modules/@agenticreplay/viewer/dist/serve.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/trace-dir.d.ts +28 -0
- package/node_modules/@agenticreplay/viewer/dist/trace-dir.d.ts.map +1 -0
- package/node_modules/@agenticreplay/viewer/dist/trace-dir.js +120 -0
- package/node_modules/@agenticreplay/viewer/dist/trace-dir.js.map +1 -0
- package/node_modules/@agenticreplay/viewer/package.json +25 -0
- package/node_modules/ajv/.runkit_example.js +23 -0
- package/node_modules/ajv/LICENSE +22 -0
- package/node_modules/ajv/README.md +207 -0
- package/node_modules/ajv/dist/2019.d.ts +19 -0
- package/node_modules/ajv/dist/2019.js +61 -0
- package/node_modules/ajv/dist/2019.js.map +1 -0
- package/node_modules/ajv/dist/2020.d.ts +19 -0
- package/node_modules/ajv/dist/2020.js +55 -0
- package/node_modules/ajv/dist/2020.js.map +1 -0
- package/node_modules/ajv/dist/ajv.d.ts +18 -0
- package/node_modules/ajv/dist/ajv.js +50 -0
- package/node_modules/ajv/dist/ajv.js.map +1 -0
- package/node_modules/ajv/dist/compile/codegen/code.d.ts +40 -0
- package/node_modules/ajv/dist/compile/codegen/code.js +156 -0
- package/node_modules/ajv/dist/compile/codegen/code.js.map +1 -0
- package/node_modules/ajv/dist/compile/codegen/index.d.ts +79 -0
- package/node_modules/ajv/dist/compile/codegen/index.js +697 -0
- package/node_modules/ajv/dist/compile/codegen/index.js.map +1 -0
- package/node_modules/ajv/dist/compile/codegen/scope.d.ts +79 -0
- package/node_modules/ajv/dist/compile/codegen/scope.js +143 -0
- package/node_modules/ajv/dist/compile/codegen/scope.js.map +1 -0
- package/node_modules/ajv/dist/compile/errors.d.ts +13 -0
- package/node_modules/ajv/dist/compile/errors.js +123 -0
- package/node_modules/ajv/dist/compile/errors.js.map +1 -0
- package/node_modules/ajv/dist/compile/index.d.ts +80 -0
- package/node_modules/ajv/dist/compile/index.js +242 -0
- package/node_modules/ajv/dist/compile/index.js.map +1 -0
- package/node_modules/ajv/dist/compile/jtd/parse.d.ts +4 -0
- package/node_modules/ajv/dist/compile/jtd/parse.js +350 -0
- package/node_modules/ajv/dist/compile/jtd/parse.js.map +1 -0
- package/node_modules/ajv/dist/compile/jtd/serialize.d.ts +4 -0
- package/node_modules/ajv/dist/compile/jtd/serialize.js +236 -0
- package/node_modules/ajv/dist/compile/jtd/serialize.js.map +1 -0
- package/node_modules/ajv/dist/compile/jtd/types.d.ts +6 -0
- package/node_modules/ajv/dist/compile/jtd/types.js +14 -0
- package/node_modules/ajv/dist/compile/jtd/types.js.map +1 -0
- package/node_modules/ajv/dist/compile/names.d.ts +20 -0
- package/node_modules/ajv/dist/compile/names.js +28 -0
- package/node_modules/ajv/dist/compile/names.js.map +1 -0
- package/node_modules/ajv/dist/compile/ref_error.d.ts +6 -0
- package/node_modules/ajv/dist/compile/ref_error.js +12 -0
- package/node_modules/ajv/dist/compile/ref_error.js.map +1 -0
- package/node_modules/ajv/dist/compile/resolve.d.ts +12 -0
- package/node_modules/ajv/dist/compile/resolve.js +155 -0
- package/node_modules/ajv/dist/compile/resolve.js.map +1 -0
- package/node_modules/ajv/dist/compile/rules.d.ts +28 -0
- package/node_modules/ajv/dist/compile/rules.js +26 -0
- package/node_modules/ajv/dist/compile/rules.js.map +1 -0
- package/node_modules/ajv/dist/compile/util.d.ts +40 -0
- package/node_modules/ajv/dist/compile/util.js +178 -0
- package/node_modules/ajv/dist/compile/util.js.map +1 -0
- package/node_modules/ajv/dist/compile/validate/applicability.d.ts +6 -0
- package/node_modules/ajv/dist/compile/validate/applicability.js +19 -0
- package/node_modules/ajv/dist/compile/validate/applicability.js.map +1 -0
- package/node_modules/ajv/dist/compile/validate/boolSchema.d.ts +4 -0
- package/node_modules/ajv/dist/compile/validate/boolSchema.js +50 -0
- package/node_modules/ajv/dist/compile/validate/boolSchema.js.map +1 -0
- package/node_modules/ajv/dist/compile/validate/dataType.d.ts +17 -0
- package/node_modules/ajv/dist/compile/validate/dataType.js +203 -0
- package/node_modules/ajv/dist/compile/validate/dataType.js.map +1 -0
- package/node_modules/ajv/dist/compile/validate/defaults.d.ts +2 -0
- package/node_modules/ajv/dist/compile/validate/defaults.js +35 -0
- package/node_modules/ajv/dist/compile/validate/defaults.js.map +1 -0
- package/node_modules/ajv/dist/compile/validate/index.d.ts +42 -0
- package/node_modules/ajv/dist/compile/validate/index.js +520 -0
- package/node_modules/ajv/dist/compile/validate/index.js.map +1 -0
- package/node_modules/ajv/dist/compile/validate/keyword.d.ts +8 -0
- package/node_modules/ajv/dist/compile/validate/keyword.js +124 -0
- package/node_modules/ajv/dist/compile/validate/keyword.js.map +1 -0
- package/node_modules/ajv/dist/compile/validate/subschema.d.ts +47 -0
- package/node_modules/ajv/dist/compile/validate/subschema.js +81 -0
- package/node_modules/ajv/dist/compile/validate/subschema.js.map +1 -0
- package/node_modules/ajv/dist/core.d.ts +174 -0
- package/node_modules/ajv/dist/core.js +618 -0
- package/node_modules/ajv/dist/core.js.map +1 -0
- package/node_modules/ajv/dist/jtd.d.ts +47 -0
- package/node_modules/ajv/dist/jtd.js +72 -0
- package/node_modules/ajv/dist/jtd.js.map +1 -0
- package/node_modules/ajv/dist/refs/data.json +13 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/index.d.ts +2 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/index.js +28 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/index.js.map +1 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/meta/applicator.json +53 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/meta/content.json +17 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/meta/core.json +57 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/meta/format.json +14 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/meta/meta-data.json +37 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/meta/validation.json +90 -0
- package/node_modules/ajv/dist/refs/json-schema-2019-09/schema.json +39 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/index.d.ts +2 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/index.js +30 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/index.js.map +1 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/meta/applicator.json +48 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/meta/content.json +17 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/meta/core.json +51 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/meta/format-annotation.json +14 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/meta/meta-data.json +37 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/meta/unevaluated.json +15 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/meta/validation.json +90 -0
- package/node_modules/ajv/dist/refs/json-schema-2020-12/schema.json +55 -0
- package/node_modules/ajv/dist/refs/json-schema-draft-06.json +137 -0
- package/node_modules/ajv/dist/refs/json-schema-draft-07.json +151 -0
- package/node_modules/ajv/dist/refs/json-schema-secure.json +88 -0
- package/node_modules/ajv/dist/refs/jtd-schema.d.ts +3 -0
- package/node_modules/ajv/dist/refs/jtd-schema.js +118 -0
- package/node_modules/ajv/dist/refs/jtd-schema.js.map +1 -0
- package/node_modules/ajv/dist/runtime/equal.d.ts +6 -0
- package/node_modules/ajv/dist/runtime/equal.js +7 -0
- package/node_modules/ajv/dist/runtime/equal.js.map +1 -0
- package/node_modules/ajv/dist/runtime/parseJson.d.ts +18 -0
- package/node_modules/ajv/dist/runtime/parseJson.js +185 -0
- package/node_modules/ajv/dist/runtime/parseJson.js.map +1 -0
- package/node_modules/ajv/dist/runtime/quote.d.ts +5 -0
- package/node_modules/ajv/dist/runtime/quote.js +30 -0
- package/node_modules/ajv/dist/runtime/quote.js.map +1 -0
- package/node_modules/ajv/dist/runtime/re2.d.ts +6 -0
- package/node_modules/ajv/dist/runtime/re2.js +6 -0
- package/node_modules/ajv/dist/runtime/re2.js.map +1 -0
- package/node_modules/ajv/dist/runtime/timestamp.d.ts +5 -0
- package/node_modules/ajv/dist/runtime/timestamp.js +42 -0
- package/node_modules/ajv/dist/runtime/timestamp.js.map +1 -0
- package/node_modules/ajv/dist/runtime/ucs2length.d.ts +5 -0
- package/node_modules/ajv/dist/runtime/ucs2length.js +24 -0
- package/node_modules/ajv/dist/runtime/ucs2length.js.map +1 -0
- package/node_modules/ajv/dist/runtime/uri.d.ts +6 -0
- package/node_modules/ajv/dist/runtime/uri.js +6 -0
- package/node_modules/ajv/dist/runtime/uri.js.map +1 -0
- package/node_modules/ajv/dist/runtime/validation_error.d.ts +7 -0
- package/node_modules/ajv/dist/runtime/validation_error.js +11 -0
- package/node_modules/ajv/dist/runtime/validation_error.js.map +1 -0
- package/node_modules/ajv/dist/standalone/index.d.ts +6 -0
- package/node_modules/ajv/dist/standalone/index.js +90 -0
- package/node_modules/ajv/dist/standalone/index.js.map +1 -0
- package/node_modules/ajv/dist/standalone/instance.d.ts +12 -0
- package/node_modules/ajv/dist/standalone/instance.js +35 -0
- package/node_modules/ajv/dist/standalone/instance.js.map +1 -0
- package/node_modules/ajv/dist/types/index.d.ts +183 -0
- package/node_modules/ajv/dist/types/index.js +3 -0
- package/node_modules/ajv/dist/types/index.js.map +1 -0
- package/node_modules/ajv/dist/types/json-schema.d.ts +125 -0
- package/node_modules/ajv/dist/types/json-schema.js +3 -0
- package/node_modules/ajv/dist/types/json-schema.js.map +1 -0
- package/node_modules/ajv/dist/types/jtd-schema.d.ts +174 -0
- package/node_modules/ajv/dist/types/jtd-schema.js +3 -0
- package/node_modules/ajv/dist/types/jtd-schema.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/additionalItems.d.ts +8 -0
- package/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js +49 -0
- package/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js +106 -0
- package/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/allOf.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/applicator/allOf.js +23 -0
- package/node_modules/ajv/dist/vocabularies/applicator/allOf.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/anyOf.d.ts +4 -0
- package/node_modules/ajv/dist/vocabularies/applicator/anyOf.js +12 -0
- package/node_modules/ajv/dist/vocabularies/applicator/anyOf.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/contains.d.ts +7 -0
- package/node_modules/ajv/dist/vocabularies/applicator/contains.js +95 -0
- package/node_modules/ajv/dist/vocabularies/applicator/contains.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/dependencies.d.ts +21 -0
- package/node_modules/ajv/dist/vocabularies/applicator/dependencies.js +85 -0
- package/node_modules/ajv/dist/vocabularies/applicator/dependencies.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.js +11 -0
- package/node_modules/ajv/dist/vocabularies/applicator/dependentSchemas.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/if.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/applicator/if.js +66 -0
- package/node_modules/ajv/dist/vocabularies/applicator/if.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/index.d.ts +13 -0
- package/node_modules/ajv/dist/vocabularies/applicator/index.js +44 -0
- package/node_modules/ajv/dist/vocabularies/applicator/index.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/items.d.ts +5 -0
- package/node_modules/ajv/dist/vocabularies/applicator/items.js +52 -0
- package/node_modules/ajv/dist/vocabularies/applicator/items.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/items2020.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/applicator/items2020.js +30 -0
- package/node_modules/ajv/dist/vocabularies/applicator/items2020.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/not.d.ts +4 -0
- package/node_modules/ajv/dist/vocabularies/applicator/not.js +26 -0
- package/node_modules/ajv/dist/vocabularies/applicator/not.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/oneOf.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/applicator/oneOf.js +60 -0
- package/node_modules/ajv/dist/vocabularies/applicator/oneOf.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/patternProperties.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js +75 -0
- package/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/prefixItems.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js +12 -0
- package/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/properties.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/applicator/properties.js +54 -0
- package/node_modules/ajv/dist/vocabularies/applicator/properties.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/propertyNames.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js +38 -0
- package/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/applicator/thenElse.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/applicator/thenElse.js +13 -0
- package/node_modules/ajv/dist/vocabularies/applicator/thenElse.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/code.d.ts +17 -0
- package/node_modules/ajv/dist/vocabularies/code.js +131 -0
- package/node_modules/ajv/dist/vocabularies/code.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/core/id.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/core/id.js +10 -0
- package/node_modules/ajv/dist/vocabularies/core/id.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/core/index.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/core/index.js +16 -0
- package/node_modules/ajv/dist/vocabularies/core/index.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/core/ref.d.ts +8 -0
- package/node_modules/ajv/dist/vocabularies/core/ref.js +122 -0
- package/node_modules/ajv/dist/vocabularies/core/ref.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/discriminator/index.d.ts +5 -0
- package/node_modules/ajv/dist/vocabularies/discriminator/index.js +104 -0
- package/node_modules/ajv/dist/vocabularies/discriminator/index.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/discriminator/types.d.ts +10 -0
- package/node_modules/ajv/dist/vocabularies/discriminator/types.js +9 -0
- package/node_modules/ajv/dist/vocabularies/discriminator/types.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/draft2020.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/draft2020.js +23 -0
- package/node_modules/ajv/dist/vocabularies/draft2020.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/draft7.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/draft7.js +17 -0
- package/node_modules/ajv/dist/vocabularies/draft7.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.d.ts +5 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.js +30 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/dynamicAnchor.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.d.ts +5 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.js +51 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/dynamicRef.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/index.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/index.js +9 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/index.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.js +16 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/recursiveAnchor.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.js +10 -0
- package/node_modules/ajv/dist/vocabularies/dynamic/recursiveRef.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/errors.d.ts +9 -0
- package/node_modules/ajv/dist/vocabularies/errors.js +3 -0
- package/node_modules/ajv/dist/vocabularies/errors.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/format/format.d.ts +8 -0
- package/node_modules/ajv/dist/vocabularies/format/format.js +92 -0
- package/node_modules/ajv/dist/vocabularies/format/format.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/format/index.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/format/index.js +6 -0
- package/node_modules/ajv/dist/vocabularies/format/index.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/discriminator.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/jtd/discriminator.js +71 -0
- package/node_modules/ajv/dist/vocabularies/jtd/discriminator.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/elements.d.ts +5 -0
- package/node_modules/ajv/dist/vocabularies/jtd/elements.js +24 -0
- package/node_modules/ajv/dist/vocabularies/jtd/elements.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/enum.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/jtd/enum.js +43 -0
- package/node_modules/ajv/dist/vocabularies/jtd/enum.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/error.d.ts +9 -0
- package/node_modules/ajv/dist/vocabularies/jtd/error.js +20 -0
- package/node_modules/ajv/dist/vocabularies/jtd/error.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/index.d.ts +10 -0
- package/node_modules/ajv/dist/vocabularies/jtd/index.js +29 -0
- package/node_modules/ajv/dist/vocabularies/jtd/index.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/metadata.d.ts +5 -0
- package/node_modules/ajv/dist/vocabularies/jtd/metadata.js +25 -0
- package/node_modules/ajv/dist/vocabularies/jtd/metadata.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/nullable.d.ts +4 -0
- package/node_modules/ajv/dist/vocabularies/jtd/nullable.js +22 -0
- package/node_modules/ajv/dist/vocabularies/jtd/nullable.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.js +15 -0
- package/node_modules/ajv/dist/vocabularies/jtd/optionalProperties.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/properties.d.ts +22 -0
- package/node_modules/ajv/dist/vocabularies/jtd/properties.js +149 -0
- package/node_modules/ajv/dist/vocabularies/jtd/properties.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/ref.d.ts +4 -0
- package/node_modules/ajv/dist/vocabularies/jtd/ref.js +67 -0
- package/node_modules/ajv/dist/vocabularies/jtd/ref.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/type.d.ts +10 -0
- package/node_modules/ajv/dist/vocabularies/jtd/type.js +69 -0
- package/node_modules/ajv/dist/vocabularies/jtd/type.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/union.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/jtd/union.js +12 -0
- package/node_modules/ajv/dist/vocabularies/jtd/union.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/jtd/values.d.ts +5 -0
- package/node_modules/ajv/dist/vocabularies/jtd/values.js +51 -0
- package/node_modules/ajv/dist/vocabularies/jtd/values.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/metadata.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/metadata.js +18 -0
- package/node_modules/ajv/dist/vocabularies/metadata.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/next.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/next.js +8 -0
- package/node_modules/ajv/dist/vocabularies/next.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/index.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/index.js +7 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/index.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js +40 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js +65 -0
- package/node_modules/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/const.d.ts +6 -0
- package/node_modules/ajv/dist/vocabularies/validation/const.js +25 -0
- package/node_modules/ajv/dist/vocabularies/validation/const.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/dependentRequired.d.ts +5 -0
- package/node_modules/ajv/dist/vocabularies/validation/dependentRequired.js +12 -0
- package/node_modules/ajv/dist/vocabularies/validation/dependentRequired.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/enum.d.ts +8 -0
- package/node_modules/ajv/dist/vocabularies/validation/enum.js +48 -0
- package/node_modules/ajv/dist/vocabularies/validation/enum.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/index.d.ts +16 -0
- package/node_modules/ajv/dist/vocabularies/validation/index.js +33 -0
- package/node_modules/ajv/dist/vocabularies/validation/index.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitContains.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitContains.js +15 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitContains.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitItems.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitItems.js +24 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitItems.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitLength.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitLength.js +27 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitLength.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitNumber.d.ts +11 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitNumber.js +27 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitNumber.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitProperties.d.ts +3 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitProperties.js +24 -0
- package/node_modules/ajv/dist/vocabularies/validation/limitProperties.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/multipleOf.d.ts +8 -0
- package/node_modules/ajv/dist/vocabularies/validation/multipleOf.js +26 -0
- package/node_modules/ajv/dist/vocabularies/validation/multipleOf.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/pattern.d.ts +8 -0
- package/node_modules/ajv/dist/vocabularies/validation/pattern.js +33 -0
- package/node_modules/ajv/dist/vocabularies/validation/pattern.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/required.d.ts +8 -0
- package/node_modules/ajv/dist/vocabularies/validation/required.js +79 -0
- package/node_modules/ajv/dist/vocabularies/validation/required.js.map +1 -0
- package/node_modules/ajv/dist/vocabularies/validation/uniqueItems.d.ts +9 -0
- package/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js +64 -0
- package/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js.map +1 -0
- package/node_modules/ajv/lib/2019.ts +81 -0
- package/node_modules/ajv/lib/2020.ts +75 -0
- package/node_modules/ajv/lib/ajv.ts +70 -0
- package/node_modules/ajv/lib/compile/codegen/code.ts +169 -0
- package/node_modules/ajv/lib/compile/codegen/index.ts +852 -0
- package/node_modules/ajv/lib/compile/codegen/scope.ts +215 -0
- package/node_modules/ajv/lib/compile/errors.ts +184 -0
- package/node_modules/ajv/lib/compile/index.ts +324 -0
- package/node_modules/ajv/lib/compile/jtd/parse.ts +411 -0
- package/node_modules/ajv/lib/compile/jtd/serialize.ts +277 -0
- package/node_modules/ajv/lib/compile/jtd/types.ts +16 -0
- package/node_modules/ajv/lib/compile/names.ts +27 -0
- package/node_modules/ajv/lib/compile/ref_error.ts +13 -0
- package/node_modules/ajv/lib/compile/resolve.ts +149 -0
- package/node_modules/ajv/lib/compile/rules.ts +50 -0
- package/node_modules/ajv/lib/compile/util.ts +213 -0
- package/node_modules/ajv/lib/compile/validate/applicability.ts +22 -0
- package/node_modules/ajv/lib/compile/validate/boolSchema.ts +47 -0
- package/node_modules/ajv/lib/compile/validate/dataType.ts +230 -0
- package/node_modules/ajv/lib/compile/validate/defaults.ts +32 -0
- package/node_modules/ajv/lib/compile/validate/index.ts +582 -0
- package/node_modules/ajv/lib/compile/validate/keyword.ts +171 -0
- package/node_modules/ajv/lib/compile/validate/subschema.ts +135 -0
- package/node_modules/ajv/lib/core.ts +892 -0
- package/node_modules/ajv/lib/jtd.ts +132 -0
- package/node_modules/ajv/lib/refs/data.json +13 -0
- package/node_modules/ajv/lib/refs/json-schema-2019-09/index.ts +28 -0
- package/node_modules/ajv/lib/refs/json-schema-2019-09/meta/applicator.json +53 -0
- package/node_modules/ajv/lib/refs/json-schema-2019-09/meta/content.json +17 -0
- package/node_modules/ajv/lib/refs/json-schema-2019-09/meta/core.json +57 -0
- package/node_modules/ajv/lib/refs/json-schema-2019-09/meta/format.json +14 -0
- package/node_modules/ajv/lib/refs/json-schema-2019-09/meta/meta-data.json +37 -0
- package/node_modules/ajv/lib/refs/json-schema-2019-09/meta/validation.json +90 -0
- package/node_modules/ajv/lib/refs/json-schema-2019-09/schema.json +39 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/index.ts +30 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/meta/applicator.json +48 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/meta/content.json +17 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/meta/core.json +51 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/meta/format-annotation.json +14 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/meta/meta-data.json +37 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/meta/unevaluated.json +15 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/meta/validation.json +90 -0
- package/node_modules/ajv/lib/refs/json-schema-2020-12/schema.json +55 -0
- package/node_modules/ajv/lib/refs/json-schema-draft-06.json +137 -0
- package/node_modules/ajv/lib/refs/json-schema-draft-07.json +151 -0
- package/node_modules/ajv/lib/refs/json-schema-secure.json +88 -0
- package/node_modules/ajv/lib/refs/jtd-schema.ts +130 -0
- package/node_modules/ajv/lib/runtime/equal.ts +7 -0
- package/node_modules/ajv/lib/runtime/parseJson.ts +177 -0
- package/node_modules/ajv/lib/runtime/quote.ts +31 -0
- package/node_modules/ajv/lib/runtime/re2.ts +6 -0
- package/node_modules/ajv/lib/runtime/timestamp.ts +46 -0
- package/node_modules/ajv/lib/runtime/ucs2length.ts +20 -0
- package/node_modules/ajv/lib/runtime/uri.ts +6 -0
- package/node_modules/ajv/lib/runtime/validation_error.ts +13 -0
- package/node_modules/ajv/lib/standalone/index.ts +100 -0
- package/node_modules/ajv/lib/standalone/instance.ts +36 -0
- package/node_modules/ajv/lib/types/index.ts +244 -0
- package/node_modules/ajv/lib/types/json-schema.ts +187 -0
- package/node_modules/ajv/lib/types/jtd-schema.ts +273 -0
- package/node_modules/ajv/lib/vocabularies/applicator/additionalItems.ts +56 -0
- package/node_modules/ajv/lib/vocabularies/applicator/additionalProperties.ts +118 -0
- package/node_modules/ajv/lib/vocabularies/applicator/allOf.ts +22 -0
- package/node_modules/ajv/lib/vocabularies/applicator/anyOf.ts +14 -0
- package/node_modules/ajv/lib/vocabularies/applicator/contains.ts +109 -0
- package/node_modules/ajv/lib/vocabularies/applicator/dependencies.ts +112 -0
- package/node_modules/ajv/lib/vocabularies/applicator/dependentSchemas.ts +11 -0
- package/node_modules/ajv/lib/vocabularies/applicator/if.ts +80 -0
- package/node_modules/ajv/lib/vocabularies/applicator/index.ts +53 -0
- package/node_modules/ajv/lib/vocabularies/applicator/items.ts +59 -0
- package/node_modules/ajv/lib/vocabularies/applicator/items2020.ts +36 -0
- package/node_modules/ajv/lib/vocabularies/applicator/not.ts +38 -0
- package/node_modules/ajv/lib/vocabularies/applicator/oneOf.ts +82 -0
- package/node_modules/ajv/lib/vocabularies/applicator/patternProperties.ts +91 -0
- package/node_modules/ajv/lib/vocabularies/applicator/prefixItems.ts +12 -0
- package/node_modules/ajv/lib/vocabularies/applicator/properties.ts +57 -0
- package/node_modules/ajv/lib/vocabularies/applicator/propertyNames.ts +50 -0
- package/node_modules/ajv/lib/vocabularies/applicator/thenElse.ts +13 -0
- package/node_modules/ajv/lib/vocabularies/code.ts +168 -0
- package/node_modules/ajv/lib/vocabularies/core/id.ts +10 -0
- package/node_modules/ajv/lib/vocabularies/core/index.ts +16 -0
- package/node_modules/ajv/lib/vocabularies/core/ref.ts +129 -0
- package/node_modules/ajv/lib/vocabularies/discriminator/index.ts +113 -0
- package/node_modules/ajv/lib/vocabularies/discriminator/types.ts +12 -0
- package/node_modules/ajv/lib/vocabularies/draft2020.ts +23 -0
- package/node_modules/ajv/lib/vocabularies/draft7.ts +17 -0
- package/node_modules/ajv/lib/vocabularies/dynamic/dynamicAnchor.ts +31 -0
- package/node_modules/ajv/lib/vocabularies/dynamic/dynamicRef.ts +51 -0
- package/node_modules/ajv/lib/vocabularies/dynamic/index.ts +9 -0
- package/node_modules/ajv/lib/vocabularies/dynamic/recursiveAnchor.ts +14 -0
- package/node_modules/ajv/lib/vocabularies/dynamic/recursiveRef.ts +10 -0
- package/node_modules/ajv/lib/vocabularies/errors.ts +18 -0
- package/node_modules/ajv/lib/vocabularies/format/format.ts +120 -0
- package/node_modules/ajv/lib/vocabularies/format/index.ts +6 -0
- package/node_modules/ajv/lib/vocabularies/jtd/discriminator.ts +89 -0
- package/node_modules/ajv/lib/vocabularies/jtd/elements.ts +32 -0
- package/node_modules/ajv/lib/vocabularies/jtd/enum.ts +45 -0
- package/node_modules/ajv/lib/vocabularies/jtd/error.ts +23 -0
- package/node_modules/ajv/lib/vocabularies/jtd/index.ts +37 -0
- package/node_modules/ajv/lib/vocabularies/jtd/metadata.ts +24 -0
- package/node_modules/ajv/lib/vocabularies/jtd/nullable.ts +21 -0
- package/node_modules/ajv/lib/vocabularies/jtd/optionalProperties.ts +15 -0
- package/node_modules/ajv/lib/vocabularies/jtd/properties.ts +184 -0
- package/node_modules/ajv/lib/vocabularies/jtd/ref.ts +76 -0
- package/node_modules/ajv/lib/vocabularies/jtd/type.ts +75 -0
- package/node_modules/ajv/lib/vocabularies/jtd/union.ts +12 -0
- package/node_modules/ajv/lib/vocabularies/jtd/values.ts +58 -0
- package/node_modules/ajv/lib/vocabularies/metadata.ts +17 -0
- package/node_modules/ajv/lib/vocabularies/next.ts +8 -0
- package/node_modules/ajv/lib/vocabularies/unevaluated/index.ts +7 -0
- package/node_modules/ajv/lib/vocabularies/unevaluated/unevaluatedItems.ts +47 -0
- package/node_modules/ajv/lib/vocabularies/unevaluated/unevaluatedProperties.ts +85 -0
- package/node_modules/ajv/lib/vocabularies/validation/const.ts +28 -0
- package/node_modules/ajv/lib/vocabularies/validation/dependentRequired.ts +23 -0
- package/node_modules/ajv/lib/vocabularies/validation/enum.ts +54 -0
- package/node_modules/ajv/lib/vocabularies/validation/index.ts +49 -0
- package/node_modules/ajv/lib/vocabularies/validation/limitContains.ts +16 -0
- package/node_modules/ajv/lib/vocabularies/validation/limitItems.ts +26 -0
- package/node_modules/ajv/lib/vocabularies/validation/limitLength.ts +30 -0
- package/node_modules/ajv/lib/vocabularies/validation/limitNumber.ts +42 -0
- package/node_modules/ajv/lib/vocabularies/validation/limitProperties.ts +26 -0
- package/node_modules/ajv/lib/vocabularies/validation/multipleOf.ts +34 -0
- package/node_modules/ajv/lib/vocabularies/validation/pattern.ts +39 -0
- package/node_modules/ajv/lib/vocabularies/validation/required.ts +98 -0
- package/node_modules/ajv/lib/vocabularies/validation/uniqueItems.ts +79 -0
- package/node_modules/ajv/package.json +126 -0
- package/node_modules/ajv-formats/LICENSE +21 -0
- package/node_modules/ajv-formats/README.md +125 -0
- package/node_modules/ajv-formats/dist/formats.d.ts +9 -0
- package/node_modules/ajv-formats/dist/formats.js +208 -0
- package/node_modules/ajv-formats/dist/formats.js.map +1 -0
- package/node_modules/ajv-formats/dist/index.d.ts +15 -0
- package/node_modules/ajv-formats/dist/index.js +37 -0
- package/node_modules/ajv-formats/dist/index.js.map +1 -0
- package/node_modules/ajv-formats/dist/limit.d.ts +10 -0
- package/node_modules/ajv-formats/dist/limit.js +69 -0
- package/node_modules/ajv-formats/dist/limit.js.map +1 -0
- package/node_modules/ajv-formats/package.json +74 -0
- package/node_modules/ajv-formats/src/formats.ts +269 -0
- package/node_modules/ajv-formats/src/index.ts +62 -0
- package/node_modules/ajv-formats/src/limit.ts +99 -0
- package/node_modules/fast-deep-equal/LICENSE +21 -0
- package/node_modules/fast-deep-equal/README.md +96 -0
- package/node_modules/fast-deep-equal/es6/index.d.ts +2 -0
- package/node_modules/fast-deep-equal/es6/index.js +72 -0
- package/node_modules/fast-deep-equal/es6/react.d.ts +2 -0
- package/node_modules/fast-deep-equal/es6/react.js +79 -0
- package/node_modules/fast-deep-equal/index.d.ts +4 -0
- package/node_modules/fast-deep-equal/index.js +46 -0
- package/node_modules/fast-deep-equal/package.json +61 -0
- package/node_modules/fast-deep-equal/react.d.ts +2 -0
- package/node_modules/fast-deep-equal/react.js +53 -0
- package/node_modules/fast-uri/.gitattributes +2 -0
- package/node_modules/fast-uri/.github/dependabot.yml +13 -0
- package/node_modules/fast-uri/.github/workflows/ci.yml +106 -0
- package/node_modules/fast-uri/.github/workflows/lock-threads.yml +19 -0
- package/node_modules/fast-uri/.github/workflows/package-manager-ci.yml +24 -0
- package/node_modules/fast-uri/LICENSE +30 -0
- package/node_modules/fast-uri/README.md +152 -0
- package/node_modules/fast-uri/benchmark/benchmark.mjs +159 -0
- package/node_modules/fast-uri/benchmark/equal.mjs +51 -0
- package/node_modules/fast-uri/benchmark/non-simple-domain.mjs +22 -0
- package/node_modules/fast-uri/benchmark/package.json +17 -0
- package/node_modules/fast-uri/benchmark/string-array-to-hex-stripped.mjs +24 -0
- package/node_modules/fast-uri/benchmark/ws-is-secure.mjs +65 -0
- package/node_modules/fast-uri/eslint.config.js +6 -0
- package/node_modules/fast-uri/index.js +631 -0
- package/node_modules/fast-uri/lib/schemes.js +272 -0
- package/node_modules/fast-uri/lib/utils.js +747 -0
- package/node_modules/fast-uri/package.json +68 -0
- package/node_modules/fast-uri/test/ajv.test.js +43 -0
- package/node_modules/fast-uri/test/component-safe-serialization.test.js +163 -0
- package/node_modules/fast-uri/test/equal.test.js +145 -0
- package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +503 -0
- package/node_modules/fast-uri/test/fixtures/uri-js-serialize.json +120 -0
- package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
- package/node_modules/fast-uri/test/ipv6-validation.test.js +124 -0
- package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
- package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
- package/node_modules/fast-uri/test/parse.test.js +327 -0
- package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
- package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
- package/node_modules/fast-uri/test/resolve.test.js +87 -0
- package/node_modules/fast-uri/test/rfc-3986.test.js +90 -0
- package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
- package/node_modules/fast-uri/test/security-normalization.test.js +178 -0
- package/node_modules/fast-uri/test/security.test.js +431 -0
- package/node_modules/fast-uri/test/serialize.test.js +151 -0
- package/node_modules/fast-uri/test/uri-js-compatibility.test.js +33 -0
- package/node_modules/fast-uri/test/uri-js.test.js +912 -0
- package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
- package/node_modules/fast-uri/test/util.test.js +38 -0
- package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
- package/node_modules/fast-uri/tsconfig.json +9 -0
- package/node_modules/fast-uri/types/index.d.ts +60 -0
- package/node_modules/fast-uri/types/index.test-d.ts +17 -0
- package/node_modules/json-schema-traverse/.eslintrc.yml +27 -0
- package/node_modules/json-schema-traverse/.github/FUNDING.yml +2 -0
- package/node_modules/json-schema-traverse/.github/workflows/build.yml +28 -0
- package/node_modules/json-schema-traverse/.github/workflows/publish.yml +27 -0
- package/node_modules/json-schema-traverse/LICENSE +21 -0
- package/node_modules/json-schema-traverse/README.md +95 -0
- package/node_modules/json-schema-traverse/index.d.ts +40 -0
- package/node_modules/json-schema-traverse/index.js +93 -0
- package/node_modules/json-schema-traverse/package.json +43 -0
- package/node_modules/json-schema-traverse/spec/.eslintrc.yml +6 -0
- package/node_modules/json-schema-traverse/spec/fixtures/schema.js +125 -0
- package/node_modules/json-schema-traverse/spec/index.spec.js +171 -0
- package/node_modules/require-from-string/index.js +34 -0
- package/node_modules/require-from-string/license +21 -0
- package/node_modules/require-from-string/package.json +28 -0
- package/node_modules/require-from-string/readme.md +56 -0
- package/package.json +49 -2
- package/packages/cli/dist/agent-spans.d.ts +238 -0
- package/packages/cli/dist/agent-spans.d.ts.map +1 -0
- package/packages/cli/dist/agent-spans.js +677 -0
- package/packages/cli/dist/agent-spans.js.map +1 -0
- package/packages/cli/dist/api.d.ts +127 -0
- package/packages/cli/dist/api.d.ts.map +1 -0
- package/packages/cli/dist/api.js +159 -0
- package/packages/cli/dist/api.js.map +1 -0
- package/packages/cli/dist/archive.d.ts +24 -0
- package/packages/cli/dist/archive.d.ts.map +1 -0
- package/packages/cli/dist/archive.js +230 -0
- package/packages/cli/dist/archive.js.map +1 -0
- package/packages/cli/dist/args.d.ts +57 -0
- package/packages/cli/dist/args.d.ts.map +1 -0
- package/packages/cli/dist/args.js +180 -0
- package/packages/cli/dist/args.js.map +1 -0
- package/packages/cli/dist/attach.d.ts +48 -0
- package/packages/cli/dist/attach.d.ts.map +1 -0
- package/packages/cli/dist/attach.js +153 -0
- package/packages/cli/dist/attach.js.map +1 -0
- package/packages/cli/dist/capture-limits.d.ts +7 -0
- package/packages/cli/dist/capture-limits.d.ts.map +1 -0
- package/packages/cli/dist/capture-limits.js +13 -0
- package/packages/cli/dist/capture-limits.js.map +1 -0
- package/packages/cli/dist/cli.d.ts +3 -0
- package/packages/cli/dist/cli.d.ts.map +1 -0
- package/packages/cli/dist/cli.js +4 -0
- package/packages/cli/dist/cli.js.map +1 -0
- package/packages/cli/dist/commands/attach.d.ts +49 -0
- package/packages/cli/dist/commands/attach.d.ts.map +1 -0
- package/packages/cli/dist/commands/attach.js +350 -0
- package/packages/cli/dist/commands/attach.js.map +1 -0
- package/packages/cli/dist/commands/compare.d.ts +27 -0
- package/packages/cli/dist/commands/compare.d.ts.map +1 -0
- package/packages/cli/dist/commands/compare.js +187 -0
- package/packages/cli/dist/commands/compare.js.map +1 -0
- package/packages/cli/dist/commands/doctor.d.ts +24 -0
- package/packages/cli/dist/commands/doctor.d.ts.map +1 -0
- package/packages/cli/dist/commands/doctor.js +402 -0
- package/packages/cli/dist/commands/doctor.js.map +1 -0
- package/packages/cli/dist/commands/gc.d.ts +24 -0
- package/packages/cli/dist/commands/gc.d.ts.map +1 -0
- package/packages/cli/dist/commands/gc.js +326 -0
- package/packages/cli/dist/commands/gc.js.map +1 -0
- package/packages/cli/dist/commands/inspect.d.ts +45 -0
- package/packages/cli/dist/commands/inspect.d.ts.map +1 -0
- package/packages/cli/dist/commands/inspect.js +375 -0
- package/packages/cli/dist/commands/inspect.js.map +1 -0
- package/packages/cli/dist/commands/quickstart.d.ts +48 -0
- package/packages/cli/dist/commands/quickstart.d.ts.map +1 -0
- package/packages/cli/dist/commands/quickstart.js +413 -0
- package/packages/cli/dist/commands/quickstart.js.map +1 -0
- package/packages/cli/dist/commands/record.d.ts +31 -0
- package/packages/cli/dist/commands/record.d.ts.map +1 -0
- package/packages/cli/dist/commands/record.js +958 -0
- package/packages/cli/dist/commands/record.js.map +1 -0
- package/packages/cli/dist/commands/replay.d.ts +184 -0
- package/packages/cli/dist/commands/replay.d.ts.map +1 -0
- package/packages/cli/dist/commands/replay.js +2014 -0
- package/packages/cli/dist/commands/replay.js.map +1 -0
- package/packages/cli/dist/commands/scrub.d.ts +34 -0
- package/packages/cli/dist/commands/scrub.d.ts.map +1 -0
- package/packages/cli/dist/commands/scrub.js +765 -0
- package/packages/cli/dist/commands/scrub.js.map +1 -0
- package/packages/cli/dist/commands/setup.d.ts +23 -0
- package/packages/cli/dist/commands/setup.d.ts.map +1 -0
- package/packages/cli/dist/commands/setup.js +239 -0
- package/packages/cli/dist/commands/setup.js.map +1 -0
- package/packages/cli/dist/commands/sync.d.ts +236 -0
- package/packages/cli/dist/commands/sync.d.ts.map +1 -0
- package/packages/cli/dist/commands/sync.js +1270 -0
- package/packages/cli/dist/commands/sync.js.map +1 -0
- package/packages/cli/dist/config.d.ts +69 -0
- package/packages/cli/dist/config.d.ts.map +1 -0
- package/packages/cli/dist/config.js +144 -0
- package/packages/cli/dist/config.js.map +1 -0
- package/packages/cli/dist/exchange-events.d.ts +80 -0
- package/packages/cli/dist/exchange-events.d.ts.map +1 -0
- package/packages/cli/dist/exchange-events.js +296 -0
- package/packages/cli/dist/exchange-events.js.map +1 -0
- package/packages/cli/dist/flags.d.ts +41 -0
- package/packages/cli/dist/flags.d.ts.map +1 -0
- package/packages/cli/dist/flags.js +292 -0
- package/packages/cli/dist/flags.js.map +1 -0
- package/packages/cli/dist/fs-events.d.ts +19 -0
- package/packages/cli/dist/fs-events.d.ts.map +1 -0
- package/packages/cli/dist/fs-events.js +89 -0
- package/packages/cli/dist/fs-events.js.map +1 -0
- package/packages/cli/dist/index.d.ts +18 -0
- package/packages/cli/dist/index.d.ts.map +1 -0
- package/packages/cli/dist/index.js +18 -0
- package/packages/cli/dist/index.js.map +1 -0
- package/packages/cli/dist/main.d.ts +2 -0
- package/packages/cli/dist/main.d.ts.map +1 -0
- package/packages/cli/dist/main.js +404 -0
- package/packages/cli/dist/main.js.map +1 -0
- package/packages/cli/dist/mcp-server.d.ts +44 -0
- package/packages/cli/dist/mcp-server.d.ts.map +1 -0
- package/packages/cli/dist/mcp-server.js +250 -0
- package/packages/cli/dist/mcp-server.js.map +1 -0
- package/packages/cli/dist/mcp.d.ts +126 -0
- package/packages/cli/dist/mcp.d.ts.map +1 -0
- package/packages/cli/dist/mcp.js +290 -0
- package/packages/cli/dist/mcp.js.map +1 -0
- package/packages/cli/dist/out.d.ts +107 -0
- package/packages/cli/dist/out.d.ts.map +1 -0
- package/packages/cli/dist/out.js +280 -0
- package/packages/cli/dist/out.js.map +1 -0
- package/packages/cli/dist/rasterize.d.ts +62 -0
- package/packages/cli/dist/rasterize.d.ts.map +1 -0
- package/packages/cli/dist/rasterize.js +154 -0
- package/packages/cli/dist/rasterize.js.map +1 -0
- package/packages/cli/dist/serial.d.ts +19 -0
- package/packages/cli/dist/serial.d.ts.map +1 -0
- package/packages/cli/dist/serial.js +34 -0
- package/packages/cli/dist/serial.js.map +1 -0
- package/packages/cli/dist/share-card.d.ts +73 -0
- package/packages/cli/dist/share-card.d.ts.map +1 -0
- package/packages/cli/dist/share-card.js +413 -0
- package/packages/cli/dist/share-card.js.map +1 -0
- package/packages/cli/dist/snapshot.d.ts +39 -0
- package/packages/cli/dist/snapshot.d.ts.map +1 -0
- package/packages/cli/dist/snapshot.js +43 -0
- package/packages/cli/dist/snapshot.js.map +1 -0
- package/packages/cli/dist/tls-capture.d.ts +135 -0
- package/packages/cli/dist/tls-capture.d.ts.map +1 -0
- package/packages/cli/dist/tls-capture.js +586 -0
- package/packages/cli/dist/tls-capture.js.map +1 -0
- package/packages/cli/dist/upstream.d.ts +29 -0
- package/packages/cli/dist/upstream.d.ts.map +1 -0
- package/packages/cli/dist/upstream.js +41 -0
- package/packages/cli/dist/upstream.js.map +1 -0
- package/packages/cli/dist/version.d.ts +2 -0
- package/packages/cli/dist/version.d.ts.map +1 -0
- package/packages/cli/dist/version.js +18 -0
- package/packages/cli/dist/version.js.map +1 -0
- package/packages/cli/package.json +11 -11
- package/.dockerignore +0 -11
- package/.gitattributes +0 -19
- package/.github/workflows/ci.yml +0 -146
- package/.grok-plugin/plugin.json +0 -12
- package/.mcp.json +0 -8
- package/.prettierignore +0 -4
- package/.prettierrc.json +0 -1
- package/CONTRIBUTING.md +0 -106
- package/Dockerfile +0 -30
- package/MAINTAINERS.md +0 -24
- package/RELEASING.md +0 -183
- package/SECURITY.md +0 -149
- package/capture/CAPTURE-RUNBOOK.md +0 -271
- package/capture/CURSOR-HTTP2.md +0 -226
- package/capture/README.md +0 -349
- package/capture/capture-run.gif +0 -0
- package/capture/capture.mjs +0 -1883
- package/capture/fable-capture.gif +0 -0
- package/capture/fable-capture.mp4 +0 -0
- package/capture/muse-shim.mjs +0 -114
- package/capture/render-run.mjs +0 -125
- package/capture/render.mjs +0 -228
- package/companion-kiokuko-dsh.patch +0 -7806
- package/docs/agenticreplay-acceptance.md +0 -135
- package/docs/architecture.md +0 -160
- package/docs/chain-card.png +0 -0
- package/docs/compare-card.png +0 -0
- package/docs/demo-cli.gif +0 -0
- package/docs/demo-viewer.gif +0 -0
- package/docs/fidelity.md +0 -16
- package/docs/good-first-issues.md +0 -78
- package/docs/graph-card.png +0 -0
- package/docs/how-to/compare-models-on-the-same-failure.md +0 -58
- package/docs/how-to/debug-a-failing-agent-run.md +0 -127
- package/docs/how-to/why-did-my-agent-delete-my-file.md +0 -92
- package/docs/i18n/README.ar.md +0 -597
- package/docs/i18n/README.de.md +0 -648
- package/docs/i18n/README.es.md +0 -642
- package/docs/i18n/README.fr.md +0 -649
- package/docs/i18n/README.ja.md +0 -608
- package/docs/i18n/README.ko.md +0 -598
- package/docs/i18n/README.zh-CN.md +0 -574
- package/docs/integrations.md +0 -628
- package/docs/launch-path.md +0 -215
- package/docs/media/README.md +0 -101
- package/docs/media/sync-transcript.txt +0 -18
- package/docs/media/transcript.txt +0 -29
- package/docs/opencode-capture.md +0 -137
- package/docs/plugins.md +0 -250
- package/docs/proposals/action-graph-and-reasoning.md +0 -354
- package/docs/sync-card.png +0 -0
- package/docs/validation.md +0 -52
- package/docs/viewer-timeline.png +0 -0
- package/examples/traces/README.md +0 -50
- package/examples/traces/run_9f2c14a03b71/blobs/1b/1b694f32b8c582ace6b35ad64050e532fb3ee3ecc1a2763a82cf42ad0f2a3dd0 +0 -10
- package/examples/traces/run_9f2c14a03b71/blobs/51/517b5b8ec6c3b13121dbaec61784e2a024247c7c4efd9ba068b582ee57bd2764 +0 -4
- package/examples/traces/run_9f2c14a03b71/blobs/5b/5bba1bd069013d86363f69c00c1ae77c84c4e113aeeb7431c8141af4fed76860 +0 -7
- package/examples/traces/run_9f2c14a03b71/blobs/84/84247e7b385fd5f99a6728a509838e52ef963af92f44ef6eccbed6910de1438d +0 -10
- package/examples/traces/run_9f2c14a03b71/blobs/ac/ac9b4be9bdf7fe63c7b5135e2d14c4c0745a48771e733804869790590455f8e8 +0 -7
- package/examples/traces/run_9f2c14a03b71/events.jsonl +0 -31
- package/examples/traces/run_9f2c14a03b71/manifest.json +0 -46
- package/examples/traces/run_9f2c14a03b71/redactions.json +0 -17
- package/llms.txt +0 -26
- package/packages/adapters/fixtures/harness/claude-code.json +0 -12
- package/packages/adapters/fixtures/harness/codex.json +0 -10
- package/packages/adapters/fixtures/harness/cursor.json +0 -10
- package/packages/adapters/fixtures/harness/exec.json +0 -10
- package/packages/adapters/fixtures/harness/generic-openai.json +0 -14
- package/packages/adapters/fixtures/harness/goose.json +0 -14
- package/packages/adapters/fixtures/harness/grok.json +0 -18
- package/packages/adapters/fixtures/harness/hermes.json +0 -10
- package/packages/adapters/fixtures/harness/indexrag.json +0 -21
- package/packages/adapters/fixtures/harness/kilo.json +0 -10
- package/packages/adapters/fixtures/harness/mcode.json +0 -10
- package/packages/adapters/fixtures/harness/mimo.json +0 -10
- package/packages/adapters/fixtures/harness/node.json +0 -22
- package/packages/adapters/fixtures/harness/openclaw.json +0 -21
- package/packages/adapters/fixtures/harness/opencode.json +0 -20
- package/packages/adapters/fixtures/harness/qwen.json +0 -20
- package/packages/adapters/fixtures/harness/zcode.json +0 -10
- package/packages/adapters/package.json +0 -29
- package/packages/adapters/src/claude-code.ts +0 -109
- package/packages/adapters/src/codex.ts +0 -135
- package/packages/adapters/src/contract.ts +0 -531
- package/packages/adapters/src/cursor.ts +0 -76
- package/packages/adapters/src/detect.ts +0 -132
- package/packages/adapters/src/env.ts +0 -118
- package/packages/adapters/src/exec.ts +0 -59
- package/packages/adapters/src/generic-openai.ts +0 -36
- package/packages/adapters/src/goose.ts +0 -87
- package/packages/adapters/src/grok.ts +0 -60
- package/packages/adapters/src/hermes.ts +0 -92
- package/packages/adapters/src/index.ts +0 -24
- package/packages/adapters/src/indexrag.ts +0 -254
- package/packages/adapters/src/kilo.ts +0 -39
- package/packages/adapters/src/mcode.ts +0 -392
- package/packages/adapters/src/mcp-config.ts +0 -84
- package/packages/adapters/src/mimo.ts +0 -44
- package/packages/adapters/src/node.ts +0 -71
- package/packages/adapters/src/openclaw.ts +0 -50
- package/packages/adapters/src/opencode-api-bases.ts +0 -200
- package/packages/adapters/src/opencode-capture.ts +0 -164
- package/packages/adapters/src/opencode-catalog.ts +0 -44
- package/packages/adapters/src/opencode-config.ts +0 -209
- package/packages/adapters/src/opencode.ts +0 -257
- package/packages/adapters/src/qwen.ts +0 -113
- package/packages/adapters/src/registry.ts +0 -121
- package/packages/adapters/src/scaffold.ts +0 -173
- package/packages/adapters/src/session.ts +0 -138
- package/packages/adapters/src/zcode.ts +0 -440
- package/packages/adapters/test/adapters.test.ts +0 -666
- package/packages/adapters/test/contract.test.ts +0 -601
- package/packages/adapters/test/exec-adapter.test.ts +0 -114
- package/packages/adapters/test/harness-versions.test.ts +0 -220
- package/packages/adapters/test/indexrag.test.ts +0 -192
- package/packages/adapters/test/mcode.test.ts +0 -678
- package/packages/adapters/test/mcp-config.test.ts +0 -118
- package/packages/adapters/test/named-agents.test.ts +0 -262
- package/packages/adapters/test/node-adapter.test.ts +0 -98
- package/packages/adapters/test/opencode-capture.test.ts +0 -137
- package/packages/adapters/test/opencode-catalog.test.ts +0 -178
- package/packages/adapters/test/opencode-config.test.ts +0 -63
- package/packages/adapters/test/scaffold.test.ts +0 -156
- package/packages/adapters/test/session.test.ts +0 -368
- package/packages/adapters/test/zcode.test.ts +0 -497
- package/packages/adapters/tsconfig.json +0 -16
- package/packages/cli/README.md +0 -92
- package/packages/cli/src/agent-spans.ts +0 -752
- package/packages/cli/src/api.ts +0 -275
- package/packages/cli/src/archive.ts +0 -264
- package/packages/cli/src/args.ts +0 -207
- package/packages/cli/src/attach.ts +0 -178
- package/packages/cli/src/capture-limits.ts +0 -21
- package/packages/cli/src/cli.ts +0 -4
- package/packages/cli/src/commands/attach.ts +0 -441
- package/packages/cli/src/commands/compare.ts +0 -218
- package/packages/cli/src/commands/doctor.ts +0 -447
- package/packages/cli/src/commands/gc.ts +0 -372
- package/packages/cli/src/commands/inspect.ts +0 -482
- package/packages/cli/src/commands/quickstart.ts +0 -479
- package/packages/cli/src/commands/record.ts +0 -1069
- package/packages/cli/src/commands/replay.ts +0 -2289
- package/packages/cli/src/commands/scrub.ts +0 -865
- package/packages/cli/src/commands/setup.ts +0 -294
- package/packages/cli/src/commands/sync.ts +0 -1429
- package/packages/cli/src/config.ts +0 -182
- package/packages/cli/src/exchange-events.ts +0 -369
- package/packages/cli/src/flags.ts +0 -316
- package/packages/cli/src/fs-events.ts +0 -103
- package/packages/cli/src/index.ts +0 -17
- package/packages/cli/src/main.ts +0 -419
- package/packages/cli/src/mcp-server.ts +0 -314
- package/packages/cli/src/mcp.ts +0 -350
- package/packages/cli/src/out.ts +0 -350
- package/packages/cli/src/rasterize.ts +0 -205
- package/packages/cli/src/serial.ts +0 -38
- package/packages/cli/src/share-card.ts +0 -514
- package/packages/cli/src/snapshot.ts +0 -80
- package/packages/cli/src/tls-capture.ts +0 -688
- package/packages/cli/src/upstream.ts +0 -75
- package/packages/cli/src/version.ts +0 -21
- package/packages/cli/test/agent-spans-drain.test.ts +0 -157
- package/packages/cli/test/agent-spans-race.test.ts +0 -97
- package/packages/cli/test/agent-spans.test.ts +0 -1032
- package/packages/cli/test/api.test.ts +0 -178
- package/packages/cli/test/archive.test.ts +0 -143
- package/packages/cli/test/args.test.ts +0 -164
- package/packages/cli/test/attach-command.test.ts +0 -356
- package/packages/cli/test/attach.test.ts +0 -198
- package/packages/cli/test/capture-oracle.test.ts +0 -160
- package/packages/cli/test/capture-warning.test.ts +0 -215
- package/packages/cli/test/chain-card.test.ts +0 -190
- package/packages/cli/test/claude-capture.test.ts +0 -242
- package/packages/cli/test/compare.test.ts +0 -329
- package/packages/cli/test/config-permission-failure.test.ts +0 -41
- package/packages/cli/test/config.test.ts +0 -239
- package/packages/cli/test/declared-inside-replay.test.ts +0 -170
- package/packages/cli/test/doctor.test.ts +0 -276
- package/packages/cli/test/e2e.test.ts +0 -1230
- package/packages/cli/test/exchange-events.test.ts +0 -345
- package/packages/cli/test/failure-lines.test.ts +0 -47
- package/packages/cli/test/fixtures/deaf-agent.mjs +0 -11
- package/packages/cli/test/fixtures/embedding-agent.mjs +0 -32
- package/packages/cli/test/fixtures/fake-agent.mjs +0 -114
- package/packages/cli/test/fixtures/fake-model.mjs +0 -105
- package/packages/cli/test/fixtures/gateway-agent.mjs +0 -20
- package/packages/cli/test/fixtures/ide-parent.mjs +0 -34
- package/packages/cli/test/fixtures/mcp-agent.mjs +0 -64
- package/packages/cli/test/fixtures/proxy-call.mjs +0 -105
- package/packages/cli/test/fixtures/responses-agent.mjs +0 -82
- package/packages/cli/test/fixtures/responses-model.mjs +0 -94
- package/packages/cli/test/fixtures/tls-agent.mjs +0 -35
- package/packages/cli/test/fixtures/vercel-agent.mjs +0 -81
- package/packages/cli/test/flag-values.test.ts +0 -162
- package/packages/cli/test/flags.test.ts +0 -313
- package/packages/cli/test/fork-provenance.test.ts +0 -224
- package/packages/cli/test/fork-tls.test.ts +0 -190
- package/packages/cli/test/gateway-e2e.test.ts +0 -110
- package/packages/cli/test/gc.test.ts +0 -531
- package/packages/cli/test/generated-esm.test.ts +0 -87
- package/packages/cli/test/graph-card.test.ts +0 -249
- package/packages/cli/test/graph-command.test.ts +0 -258
- package/packages/cli/test/grok-bot-e2e.test.ts +0 -349
- package/packages/cli/test/inspect.test.ts +0 -412
- package/packages/cli/test/intercept-failures.test.ts +0 -74
- package/packages/cli/test/json-only.test.ts +0 -88
- package/packages/cli/test/json-output.test.ts +0 -221
- package/packages/cli/test/list-gateway.test.ts +0 -770
- package/packages/cli/test/mcp-capture.test.ts +0 -498
- package/packages/cli/test/mcp-server.test.ts +0 -315
- package/packages/cli/test/nested-repo-replay.test.ts +0 -972
- package/packages/cli/test/no-credential-in-attrs.test.ts +0 -255
- package/packages/cli/test/opencode-capture.test.ts +0 -422
- package/packages/cli/test/opencode-catalog-capture.test.ts +0 -175
- package/packages/cli/test/out.test.ts +0 -491
- package/packages/cli/test/pinned-fetch.test.ts +0 -154
- package/packages/cli/test/prompt-scrub.test.ts +0 -132
- package/packages/cli/test/publish-order.test.ts +0 -87
- package/packages/cli/test/quickstart.test.ts +0 -579
- package/packages/cli/test/rasterize.test.ts +0 -86
- package/packages/cli/test/record-detection.test.ts +0 -156
- package/packages/cli/test/record-teardown.test.ts +0 -203
- package/packages/cli/test/replay-putback.test.ts +0 -242
- package/packages/cli/test/replay-spill.test.ts +0 -116
- package/packages/cli/test/replay-teardown.test.ts +0 -143
- package/packages/cli/test/replay-trace.test.ts +0 -199
- package/packages/cli/test/replay-transport.test.ts +0 -113
- package/packages/cli/test/reset-roots.test.ts +0 -67
- package/packages/cli/test/responses-e2e.test.ts +0 -119
- package/packages/cli/test/retrieval-context.test.ts +0 -168
- package/packages/cli/test/review-regressions.test.ts +0 -168
- package/packages/cli/test/route-decision.test.ts +0 -134
- package/packages/cli/test/run-dir-sidecars.test.ts +0 -779
- package/packages/cli/test/scrub.test.ts +0 -936
- package/packages/cli/test/serial.test.ts +0 -65
- package/packages/cli/test/setup.test.ts +0 -535
- package/packages/cli/test/share-card.test.ts +0 -135
- package/packages/cli/test/shell-capture.test.ts +0 -184
- package/packages/cli/test/shell-frames-drain.test.ts +0 -91
- package/packages/cli/test/snapshot-retry.test.ts +0 -54
- package/packages/cli/test/support-claims.test.ts +0 -127
- package/packages/cli/test/sync-credentials.test.ts +0 -177
- package/packages/cli/test/sync.test.ts +0 -1709
- package/packages/cli/test/tls-intercept.test.ts +0 -548
- package/packages/cli/test/upstream-visible.test.ts +0 -314
- package/packages/cli/test/vercel-sdk-e2e.test.ts +0 -105
- package/packages/cli/test/version.test.ts +0 -20
- package/packages/cli/tsconfig.json +0 -40
- package/packages/core/package.json +0 -26
- package/packages/core/src/blobs.ts +0 -109
- package/packages/core/src/fetch.ts +0 -48
- package/packages/core/src/graph.ts +0 -450
- package/packages/core/src/index.ts +0 -8
- package/packages/core/src/paths.ts +0 -180
- package/packages/core/src/private-files.ts +0 -394
- package/packages/core/src/reader.ts +0 -175
- package/packages/core/src/redaction.ts +0 -826
- package/packages/core/src/writer.ts +0 -467
- package/packages/core/test/blobs.test.ts +0 -170
- package/packages/core/test/capture-contract.test.ts +0 -179
- package/packages/core/test/data-uri-redaction.test.ts +0 -758
- package/packages/core/test/graph.test.ts +0 -520
- package/packages/core/test/integration.test.ts +0 -148
- package/packages/core/test/paths-permission-failure.test.ts +0 -53
- package/packages/core/test/paths.test.ts +0 -339
- package/packages/core/test/private-files-owner.test.ts +0 -77
- package/packages/core/test/private-files-scratch-race.test.ts +0 -62
- package/packages/core/test/private-files.test.ts +0 -251
- package/packages/core/test/reader.test.ts +0 -271
- package/packages/core/test/redaction.test.ts +0 -525
- package/packages/core/test/writer.test.ts +0 -531
- package/packages/core/tsconfig.json +0 -6
- package/packages/fs-capture/package.json +0 -25
- package/packages/fs-capture/src/capture.ts +0 -193
- package/packages/fs-capture/src/git.ts +0 -131
- package/packages/fs-capture/src/index.ts +0 -3
- package/packages/fs-capture/src/shadow.ts +0 -751
- package/packages/fs-capture/test/capture.test.ts +0 -241
- package/packages/fs-capture/test/git.test.ts +0 -118
- package/packages/fs-capture/test/helpers.ts +0 -28
- package/packages/fs-capture/test/shadow.test.ts +0 -1058
- package/packages/fs-capture/tsconfig.json +0 -6
- package/packages/mcp-shim/package.json +0 -26
- package/packages/mcp-shim/src/cli.ts +0 -372
- package/packages/mcp-shim/src/framing.ts +0 -106
- package/packages/mcp-shim/src/index.ts +0 -4
- package/packages/mcp-shim/src/mock.ts +0 -194
- package/packages/mcp-shim/src/shim.ts +0 -126
- package/packages/mcp-shim/test/cli.test.ts +0 -489
- package/packages/mcp-shim/test/framing.test.ts +0 -143
- package/packages/mcp-shim/test/mock.test.ts +0 -200
- package/packages/mcp-shim/test/shim.test.ts +0 -287
- package/packages/mcp-shim/tsconfig.json +0 -5
- package/packages/node-instrument/package.json +0 -23
- package/packages/node-instrument/src/capture.ts +0 -505
- package/packages/node-instrument/src/index.ts +0 -3
- package/packages/node-instrument/src/install.ts +0 -186
- package/packages/node-instrument/src/rewrite.ts +0 -66
- package/packages/node-instrument/test/capture.test.ts +0 -423
- package/packages/node-instrument/test/install.test.ts +0 -427
- package/packages/node-instrument/test/rewrite.test.ts +0 -85
- package/packages/node-instrument/tsconfig.json +0 -9
- package/packages/plugin-api/package.json +0 -22
- package/packages/plugin-api/src/canonical.ts +0 -73
- package/packages/plugin-api/src/index.ts +0 -2
- package/packages/plugin-api/src/plugins.ts +0 -265
- package/packages/plugin-api/test/canonical.test.ts +0 -58
- package/packages/plugin-api/tsconfig.json +0 -5
- package/packages/providers/package.json +0 -26
- package/packages/providers/src/index.ts +0 -34
- package/packages/providers/src/pricing.ts +0 -140
- package/packages/providers/src/translate/anthropic.ts +0 -620
- package/packages/providers/src/translate/openai-responses.ts +0 -638
- package/packages/providers/src/translate/openai.ts +0 -630
- package/packages/providers/src/translate/sse.ts +0 -99
- package/packages/providers/src/translate/util.ts +0 -111
- package/packages/providers/test/anthropic-translate.test.ts +0 -687
- package/packages/providers/test/openai-responses-translate.test.ts +0 -377
- package/packages/providers/test/openai-translate.test.ts +0 -884
- package/packages/providers/test/pricing.test.ts +0 -171
- package/packages/providers/tsconfig.json +0 -6
- package/packages/proxy/package.json +0 -28
- package/packages/proxy/src/ca.ts +0 -376
- package/packages/proxy/src/der.ts +0 -152
- package/packages/proxy/src/dialects.ts +0 -413
- package/packages/proxy/src/forward.ts +0 -64
- package/packages/proxy/src/index.ts +0 -8
- package/packages/proxy/src/intercept.ts +0 -1554
- package/packages/proxy/src/matching.ts +0 -1056
- package/packages/proxy/src/retrieval.ts +0 -290
- package/packages/proxy/src/server.ts +0 -2875
- package/packages/proxy/src/tls-hosts.ts +0 -205
- package/packages/proxy/test/beta-headers.test.ts +0 -39
- package/packages/proxy/test/ca.test.ts +0 -146
- package/packages/proxy/test/forward.test.ts +0 -306
- package/packages/proxy/test/hop-by-hop.test.ts +0 -118
- package/packages/proxy/test/image-matching.test.ts +0 -129
- package/packages/proxy/test/indexing-concurrency.test.ts +0 -224
- package/packages/proxy/test/lookahead.test.ts +0 -160
- package/packages/proxy/test/malformed-body.test.ts +0 -73
- package/packages/proxy/test/matching.test.ts +0 -822
- package/packages/proxy/test/passthrough.test.ts +0 -599
- package/packages/proxy/test/recorded-upstream.test.ts +0 -253
- package/packages/proxy/test/responses-dialect.test.ts +0 -239
- package/packages/proxy/test/retrieval.test.ts +0 -589
- package/packages/proxy/test/server.test.ts +0 -1281
- package/packages/proxy/test/tls-hosts.test.ts +0 -190
- package/packages/proxy/test/tls-intercept.test.ts +0 -1769
- package/packages/proxy/tsconfig.json +0 -22
- package/packages/schema/package.json +0 -28
- package/packages/schema/src/constants.ts +0 -73
- package/packages/schema/src/index.ts +0 -3
- package/packages/schema/src/types.ts +0 -103
- package/packages/schema/src/validate.ts +0 -60
- package/packages/schema/test/schema-parity.test.ts +0 -116
- package/packages/schema/tsconfig.json +0 -5
- package/packages/shell-shim/package.json +0 -26
- package/packages/shell-shim/src/index.ts +0 -3
- package/packages/shell-shim/src/install.ts +0 -406
- package/packages/shell-shim/src/resolve.ts +0 -98
- package/packages/shell-shim/src/runner-bin.ts +0 -4
- package/packages/shell-shim/src/runner.ts +0 -149
- package/packages/shell-shim/test/resolve.test.ts +0 -125
- package/packages/shell-shim/test/shim.test.ts +0 -548
- package/packages/shell-shim/test/transport-race.test.ts +0 -109
- package/packages/shell-shim/tsconfig.json +0 -13
- package/packages/viewer/package.json +0 -25
- package/packages/viewer/src/bundle.ts +0 -36
- package/packages/viewer/src/client/main.ts +0 -248
- package/packages/viewer/src/credit.ts +0 -24
- package/packages/viewer/src/css.ts +0 -409
- package/packages/viewer/src/export.ts +0 -38
- package/packages/viewer/src/html.ts +0 -356
- package/packages/viewer/src/index.ts +0 -16
- package/packages/viewer/src/render.ts +0 -791
- package/packages/viewer/src/serve.ts +0 -150
- package/packages/viewer/src/trace-dir.ts +0 -140
- package/packages/viewer/test/bundle.test.ts +0 -47
- package/packages/viewer/test/client.test.ts +0 -378
- package/packages/viewer/test/export.test.ts +0 -131
- package/packages/viewer/test/fixtures.ts +0 -69
- package/packages/viewer/test/html.test.ts +0 -453
- package/packages/viewer/test/playback.test.ts +0 -54
- package/packages/viewer/test/render.test.ts +0 -714
- package/packages/viewer/test/serve.test.ts +0 -125
- package/packages/viewer/tsconfig.json +0 -6
- package/prompt/AIDER/deepseek-v4-1-flash-system-prompt.md +0 -29
- package/prompt/CLAUDECODE/claude-fable-5-1-print-system-prompt.md +0 -127
- package/prompt/CLAUDECODE/claude-fable-5-1-system-prompt.md +0 -169
- package/prompt/CLAUDECODE/claude-opus-4-8-system-prompt.md +0 -131
- package/prompt/CLAUDECODE/claude-opus-5-system-prompt.md +0 -149
- package/prompt/CLINE/openai-gpt-4o-mini-system-prompt.md +0 -37
- package/prompt/CODEX/gpt-5-6-luna-system-prompt.md +0 -183
- package/prompt/CODEX/gpt-5-6-sol-system-prompt.md +0 -218
- package/prompt/CODEX/gpt-6-1-sol-system-prompt.md +0 -242
- package/prompt/CODEX/gpt-6-astra-system-prompt.md +0 -170
- package/prompt/CODEX/gpt-6-luna-system-prompt.md +0 -223
- package/prompt/CODEX/gpt-6-sol-system-prompt.md +0 -226
- package/prompt/CRUSH/deepseek-v4-1-flash-system-prompt.md +0 -472
- package/prompt/CURSOR/grok-4-5-high-system-prompt.md +0 -40
- package/prompt/DSH/deepseek-flash-system-prompt.md +0 -31
- package/prompt/GEMINICLI/gemini-3-5-flash-system-prompt.md +0 -168
- package/prompt/GOOSE/deepseek-v4-1-flash-system-prompt.md +0 -180
- package/prompt/HACKERAI/openai-gpt-4o-mini-system-prompt.md +0 -70
- package/prompt/HERMES/nemotron-3-5-lightning-free-system-prompt.md +0 -111
- package/prompt/KILOCODE/kilo-auto-free-system-prompt.md +0 -129
- package/prompt/MCODE/deepseek-deepseek-v4-flash-free-coding-system-prompt.md +0 -128
- package/prompt/MCODE/deepseek-deepseek-v4-flash-free-tui-system-prompt.md +0 -119
- package/prompt/MCODE/deepseek-deepseek-v4-flash-free-work-system-prompt.md +0 -140
- package/prompt/MIMOCODE/mimo-v2-5-pro-system-prompt.md +0 -411
- package/prompt/MIMOCODE/mimo-v2-5-system-prompt.md +0 -411
- package/prompt/MUSECODE/muse-spark-1-3-system-prompt.md +0 -78
- package/prompt/OPENCLAW/gpt-5-6-sol-system-prompt.md +0 -333
- package/prompt/OPENCODE/big-pickle-system-prompt.md +0 -114
- package/prompt/OPENCODE/gpt-5-6-sol-system-prompt.md +0 -126
- package/prompt/OPENCODE/ling-3-0-flash-fin-free-system-prompt.md +0 -114
- package/prompt/OPENCODE/mimo-v2-5-free-system-prompt.md +0 -114
- package/prompt/OPENCODE/muse-spark-1-2-contributor-free-system-prompt.md +0 -84
- package/prompt/OPENCODE/nemotron-3-5-lightning-free-system-prompt.md +0 -114
- package/prompt/OPENCODE/nemotron-3-ultra-free-system-prompt.md +0 -114
- package/prompt/PI/openai-gpt-4o-mini-system-prompt.md +0 -31
- package/prompt/QWENCODE/gpt-5-6-sol-system-prompt.md +0 -290
- package/prompt/ZCODE/glm-4-6-system-prompt.md +0 -91
- package/python/README.md +0 -132
- package/python/agenticreplay_trace/__init__.py +0 -58
- package/python/agenticreplay_trace/graph.py +0 -137
- package/python/agenticreplay_trace/models.py +0 -491
- package/python/agenticreplay_trace/py.typed +0 -0
- package/python/agenticreplay_trace/reader.py +0 -295
- package/python/conftest.py +0 -12
- package/python/examples/analyze.py +0 -114
- package/python/pyproject.toml +0 -45
- package/python/tests/conftest.py +0 -102
- package/python/tests/test_capture_contract.py +0 -24
- package/python/tests/test_conformance.py +0 -235
- package/python/tests/test_examples.py +0 -60
- package/python/tests/test_graph.py +0 -247
- package/python/tests/test_models.py +0 -342
- package/python/tests/test_reader.py +0 -423
- package/python-langgraph/README.md +0 -159
- package/python-langgraph/agenticreplay_langgraph/__init__.py +0 -21
- package/python-langgraph/agenticreplay_langgraph/handler.py +0 -317
- package/python-langgraph/agenticreplay_langgraph/hook.py +0 -168
- package/python-langgraph/pyproject.toml +0 -35
- package/python-langgraph/tests/test_handler.py +0 -370
- package/python-langgraph/tests/test_integration.py +0 -484
- package/python-openai-agents/README.md +0 -145
- package/python-openai-agents/agenticreplay_openai_agents/__init__.py +0 -11
- package/python-openai-agents/agenticreplay_openai_agents/processor.py +0 -331
- package/python-openai-agents/pyproject.toml +0 -34
- package/python-openai-agents/tests/test_processor.py +0 -457
- package/scripts/check-neutrality.mjs +0 -62
- package/scripts/codex-client-acceptance.mjs +0 -133
- package/scripts/conformance.mjs +0 -245
- package/scripts/fidelity.mjs +0 -170
- package/scripts/hermes-client-acceptance.mjs +0 -158
- package/scripts/host-client-acceptance.mjs +0 -732
- package/scripts/installed-smoke.mjs +0 -316
- package/scripts/publish-order.mjs +0 -96
- package/scripts/regenerate-quickstart.mjs +0 -79
- package/scripts/render-cards.mjs +0 -178
- package/scripts/render-demo.mjs +0 -139
- package/scripts/render-sync-card.mjs +0 -96
- package/scripts/set-version.mjs +0 -152
- package/scripts/set-version.test.ts +0 -220
- package/scripts/sync-server-json.mjs +0 -13
- package/scripts/update-opencode-api-bases.mjs +0 -29
- package/server.json +0 -28
- package/skills/agenticreplay-replay/SKILL.md +0 -211
- package/spec/agenticreplay-trace-v0.md +0 -223
- package/test/integrations/README.md +0 -133
- package/test/integrations/agents/ai_sdk_openai.mjs +0 -24
- package/test/integrations/agents/anthropic_sdk.py +0 -10
- package/test/integrations/agents/browser_use_agent.py +0 -24
- package/test/integrations/agents/crewai_agent.py +0 -44
- package/test/integrations/agents/crewai_model_names.py +0 -72
- package/test/integrations/agents/hardcoded_origin.mjs +0 -13
- package/test/integrations/agents/haystack_pipeline.py +0 -28
- package/test/integrations/agents/haystack_rag.py +0 -35
- package/test/integrations/agents/langgraph_agent.py +0 -54
- package/test/integrations/agents/langgraph_nodes.py +0 -62
- package/test/integrations/agents/litellm_agent.py +0 -11
- package/test/integrations/agents/llama_index_agent.py +0 -27
- package/test/integrations/agents/llama_index_model_names.py +0 -67
- package/test/integrations/agents/mastra_agent.mjs +0 -20
- package/test/integrations/agents/mcp_agent.py +0 -44
- package/test/integrations/agents/mcp_server.py +0 -20
- package/test/integrations/agents/openai_agents_handoff.py +0 -52
- package/test/integrations/agents/openai_agents_sdk.py +0 -29
- package/test/integrations/agents/openai_async.py +0 -17
- package/test/integrations/agents/openai_sdk.py +0 -10
- package/test/integrations/agents/openhands_agent.py +0 -34
- package/test/integrations/agents/rag_pipeline.py +0 -79
- package/test/integrations/agents/vision_agent.py +0 -63
- package/test/integrations/run.mjs +0 -719
- package/test/integrations/stub-origin.mjs +0 -229
- package/tsconfig.base.json +0 -20
- package/tsconfig.json +0 -41
- package/vitest.config.ts +0 -39
- package/vitest.setup.ts +0 -25
- /package/{packages → node_modules/@agenticreplay}/schema/schema/event.schema.json +0 -0
- /package/{packages → node_modules/@agenticreplay}/schema/schema/manifest.schema.json +0 -0
|
@@ -1,2289 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { mkdtemp, realpath, rm } from 'node:fs/promises';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import {
|
|
5
|
-
constants as fsConstants,
|
|
6
|
-
copyFileSync,
|
|
7
|
-
createReadStream,
|
|
8
|
-
linkSync,
|
|
9
|
-
lstatSync,
|
|
10
|
-
readlinkSync,
|
|
11
|
-
renameSync,
|
|
12
|
-
rmdirSync,
|
|
13
|
-
statSync,
|
|
14
|
-
symlinkSync,
|
|
15
|
-
unlinkSync,
|
|
16
|
-
} from 'node:fs';
|
|
17
|
-
import { createHash, randomBytes } from 'node:crypto';
|
|
18
|
-
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
19
|
-
import {
|
|
20
|
-
enclosingDelegation,
|
|
21
|
-
TraceReader,
|
|
22
|
-
deriveCheckpoints,
|
|
23
|
-
resolveRunSelector,
|
|
24
|
-
ensureRunsDir,
|
|
25
|
-
snapToCheckpoint,
|
|
26
|
-
TraceWriter,
|
|
27
|
-
} from '@agenticreplay/core';
|
|
28
|
-
import { FsCapture } from '@agenticreplay/fs-capture';
|
|
29
|
-
import {
|
|
30
|
-
createProxy,
|
|
31
|
-
defaultDialects,
|
|
32
|
-
type RecordedExchange,
|
|
33
|
-
type RecordedRetrieval,
|
|
34
|
-
type RunCa,
|
|
35
|
-
} from '@agenticreplay/proxy';
|
|
36
|
-
import { defaultAdapters, resolveLaunch } from '@agenticreplay/adapters';
|
|
37
|
-
import { serveViewer } from '@agenticreplay/viewer';
|
|
38
|
-
import { isBlobRef, type TraceEvent } from '@agenticreplay/schema';
|
|
39
|
-
import type { HarnessArtifacts } from '@agenticreplay/plugin-api';
|
|
40
|
-
import { ExchangeEventDeriver, appendDerivedEvents } from '../exchange-events.js';
|
|
41
|
-
import { cdCommand, type Output } from '../out.js';
|
|
42
|
-
import type { ParsedArgs } from '../args.js';
|
|
43
|
-
import { SerialQueue } from '../serial.js';
|
|
44
|
-
import { appendSnapshot } from '../fs-events.js';
|
|
45
|
-
import { drainMcpFrames, mcpForReplay, pointAtMcpConfig } from '../mcp.js';
|
|
46
|
-
import { planTlsCapture, recordedTlsHosts, setupTlsCapture, trustRunCa } from '../tls-capture.js';
|
|
47
|
-
import { upstreamPlan, type UpstreamPlan } from '../upstream.js';
|
|
48
|
-
import { AGENTICREPLAY_VERSION } from '../version.js';
|
|
49
|
-
|
|
50
|
-
export interface ReplayResult {
|
|
51
|
-
/** The run that was *read*: the recording being replayed or forked. */
|
|
52
|
-
runId: string;
|
|
53
|
-
mode: 'exact' | 'fork';
|
|
54
|
-
canonicalExact: number;
|
|
55
|
-
divergences: number;
|
|
56
|
-
/**
|
|
57
|
-
* Requests the recording could not serve.
|
|
58
|
-
*
|
|
59
|
-
* Printed in `replay.done` since the beginning and never returned, so every caller that was not
|
|
60
|
-
* a terminal had to scrape it back out of the log line — while `exitCode` folds it into a single
|
|
61
|
-
* bit. It is the number that says whether a replay actually reproduced the run.
|
|
62
|
-
*/
|
|
63
|
-
unmatched: number;
|
|
64
|
-
liveCalls: number;
|
|
65
|
-
exitCode: number;
|
|
66
|
-
/**
|
|
67
|
-
* The run this invocation *wrote*, whichever mode it ran in — a fork's continuation, or an exact
|
|
68
|
-
* replay's record of its own findings. Absent only when `--no-trace` turned the latter off.
|
|
69
|
-
*
|
|
70
|
-
* `forkRunId` is kept as the fork-only name because callers use it to mean "there is a fork
|
|
71
|
-
* here": `--ui` opens it in preference to the parent, and `compare` reads token usage out of it.
|
|
72
|
-
* Neither is true of an exact replay's trace, which holds no exchanges at all.
|
|
73
|
-
*/
|
|
74
|
-
traceRunId?: string;
|
|
75
|
-
forkRunId?: string;
|
|
76
|
-
/** Scratch worktree a fork ran in, so a verify command can be run against its result. */
|
|
77
|
-
worktree?: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Rebuild the recorded exchanges from a trace.
|
|
82
|
-
*
|
|
83
|
-
* Eligible redacted transport bodies come back verbatim from storage. Canonical request matching
|
|
84
|
-
* is a separate measure; a provider-translated origin response is not the client's response.
|
|
85
|
-
*/
|
|
86
|
-
export async function loadExchanges(reader: TraceReader): Promise<RecordedExchange[]> {
|
|
87
|
-
const events = await reader.events();
|
|
88
|
-
const requests = events.filter((e) => e.type === 'net.request' && e.attrs?.phase === 'body');
|
|
89
|
-
const responses = new Map<string, TraceEvent>();
|
|
90
|
-
for (const event of events) {
|
|
91
|
-
if (event.type === 'net.response' && typeof event.attrs?.request_id === 'string') {
|
|
92
|
-
responses.set(event.attrs.request_id, event);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
const dialects = defaultDialects();
|
|
96
|
-
const exchanges: RecordedExchange[] = [];
|
|
97
|
-
for (const req of requests) {
|
|
98
|
-
const id = req.attrs?.request_id;
|
|
99
|
-
if (typeof id !== 'string') continue;
|
|
100
|
-
const res = responses.get(id);
|
|
101
|
-
if (
|
|
102
|
-
!res ||
|
|
103
|
-
!res.causes?.includes(req.seq) ||
|
|
104
|
-
res.attrs?.response_complete !== true ||
|
|
105
|
-
req.attrs?.capture_incomplete ||
|
|
106
|
-
req.attrs?.truncated ||
|
|
107
|
-
res.attrs?.capture_incomplete ||
|
|
108
|
-
res.attrs?.truncated ||
|
|
109
|
-
res.attrs?.abandoned ||
|
|
110
|
-
req.attrs?.replay_eligible === false ||
|
|
111
|
-
res.transformations?.some((change) => change.rule === 'provider-protocol-translation') ||
|
|
112
|
-
req.payload === undefined ||
|
|
113
|
-
res.payload === undefined
|
|
114
|
-
)
|
|
115
|
-
continue;
|
|
116
|
-
const dialectId = req.attrs?.model_dialect;
|
|
117
|
-
const dialect = dialects.find((d) => d.id === dialectId);
|
|
118
|
-
if (!dialect) continue;
|
|
119
|
-
const rawRequest = await rawBodyOf(reader, req);
|
|
120
|
-
const rawResponse = await rawBodyOf(reader, res);
|
|
121
|
-
const headers = res.attrs?.headers as Record<string, unknown> | undefined;
|
|
122
|
-
const streamed =
|
|
123
|
-
String(headers?.['content-type'] ?? '').includes('text/event-stream') ||
|
|
124
|
-
rawResponse.trimStart().startsWith('data:') ||
|
|
125
|
-
rawResponse.trimStart().startsWith('event:');
|
|
126
|
-
exchanges.push({
|
|
127
|
-
seq: req.seq,
|
|
128
|
-
dialect: dialect.id,
|
|
129
|
-
path: String(req.attrs?.path ?? '/'),
|
|
130
|
-
rawRequest,
|
|
131
|
-
rawResponse,
|
|
132
|
-
status: Number(res.attrs?.status ?? 0),
|
|
133
|
-
streamed,
|
|
134
|
-
canonicalRequest: dialect.toCanonicalRequest(JSON.parse(rawRequest)),
|
|
135
|
-
durationMs: Number(res.attrs?.duration_ms ?? 0),
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
return exchanges;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Rebuild the recording's retrieval calls — embeddings, rerank — from its `net.*` pairs.
|
|
143
|
-
*
|
|
144
|
-
* They live there rather than in `model.request` because that is what they are: HTTP the proxy
|
|
145
|
-
* saw and no dialect claimed. What sets them apart from the rest of that traffic is the two
|
|
146
|
-
* attributes `RetrievalRule` put on them. `replay_key` is what a later run looks the answer up
|
|
147
|
-
* by, and its presence is the whole test for "this pair can be served again" — a pair without one
|
|
148
|
-
* is the opaque traffic `net.*` has always held, and is skipped here rather than guessed at.
|
|
149
|
-
*
|
|
150
|
-
* Pairing is by `causes`, not by position. A run interleaves retrieval with model calls and the
|
|
151
|
-
* responses arrive out of order under a worker pool, so the nth response is not the answer to the
|
|
152
|
-
* nth request; the writer already records which request each response answered.
|
|
153
|
-
*/
|
|
154
|
-
export async function loadRetrievals(reader: TraceReader): Promise<RecordedRetrieval[]> {
|
|
155
|
-
const events = await reader.events();
|
|
156
|
-
const responses = new Map<number, TraceEvent>();
|
|
157
|
-
for (const event of events) {
|
|
158
|
-
if (event.type !== 'net.response') continue;
|
|
159
|
-
for (const cause of event.causes ?? []) responses.set(cause, event);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const out: RecordedRetrieval[] = [];
|
|
163
|
-
for (const request of events) {
|
|
164
|
-
if (request.type !== 'net.request') continue;
|
|
165
|
-
const key = request.attrs?.['replay_key'];
|
|
166
|
-
if (typeof key !== 'string' || key === '') continue;
|
|
167
|
-
const response = responses.get(request.seq);
|
|
168
|
-
if (
|
|
169
|
-
request.attrs?.['truncated'] ||
|
|
170
|
-
response?.attrs?.['truncated'] ||
|
|
171
|
-
request.attrs?.['capture_incomplete'] ||
|
|
172
|
-
response?.attrs?.['capture_incomplete'] ||
|
|
173
|
-
response?.attrs?.['abandoned'] ||
|
|
174
|
-
response === undefined
|
|
175
|
-
)
|
|
176
|
-
continue;
|
|
177
|
-
const headers = response?.attrs?.['headers'];
|
|
178
|
-
const contentType =
|
|
179
|
-
headers !== null && typeof headers === 'object'
|
|
180
|
-
? ((headers as Record<string, unknown>)['content-type'] ?? 'application/json')
|
|
181
|
-
: 'application/json';
|
|
182
|
-
const stored = response?.attrs?.['stored'];
|
|
183
|
-
const digest = response?.attrs?.['response_sha256'];
|
|
184
|
-
const batchKey = request.attrs?.['batch_key'];
|
|
185
|
-
out.push({
|
|
186
|
-
seq: request.seq,
|
|
187
|
-
rule: String(request.attrs?.['rule'] ?? 'unknown'),
|
|
188
|
-
key,
|
|
189
|
-
path: String(request.attrs?.['path'] ?? ''),
|
|
190
|
-
status: Number(response?.attrs?.['status'] ?? 200),
|
|
191
|
-
contentType: typeof contentType === 'string' ? contentType : 'application/json',
|
|
192
|
-
// Needed only to reorder a batch, which cannot be done without knowing which text each
|
|
193
|
-
// recorded vector answered. A recording made before that existed has none, and simply
|
|
194
|
-
// cannot be reordered.
|
|
195
|
-
rawRequest: await rawBodyOf(reader, request),
|
|
196
|
-
// A digest-only recording kept no body. Empty here is what the proxy reads as "agenticreplay has
|
|
197
|
-
// proof of this answer and not the answer", and it says so rather than serving nothing.
|
|
198
|
-
rawResponse:
|
|
199
|
-
stored === 'digest' || response === undefined ? '' : await rawBodyOf(reader, response),
|
|
200
|
-
...(typeof digest === 'string' ? { digest } : {}),
|
|
201
|
-
...(typeof batchKey === 'string' ? { batchKey } : {}),
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
return out;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Read a recorded body back as the exact bytes that were sent.
|
|
209
|
-
*
|
|
210
|
-
* The subtlety is the spill boundary. The writer stores `JSON.stringify(payload)` in a blob once a
|
|
211
|
-
* payload crosses `INLINE_PAYLOAD_LIMIT`, and every wire body is a *string* — so the same body is
|
|
212
|
-
* kept as itself while it fits inline and as a quoted, backslash-escaped JSON string literal once
|
|
213
|
-
* it spills. Reading the blob bytes straight back therefore returns an escaped copy: canonicalizing
|
|
214
|
-
* it yields `model: ''` and `messages: []`, every request falls to rung 4, and replay against any
|
|
215
|
-
* real harness matches nothing at all. Undoing the writer's encoding is what makes the two forms
|
|
216
|
-
* identical again.
|
|
217
|
-
*
|
|
218
|
-
* Not `resolvePayload`, which would hand back a parsed object for a body that was recorded as one;
|
|
219
|
-
* exact replay means the agent gets the same bytes, so anything that is not a JSON string is
|
|
220
|
-
* returned verbatim.
|
|
221
|
-
*/
|
|
222
|
-
async function rawBodyOf(reader: TraceReader, event: TraceEvent): Promise<string> {
|
|
223
|
-
const payload = event.payload;
|
|
224
|
-
if (isBlobRef(payload)) {
|
|
225
|
-
const text = new TextDecoder().decode(await reader.blob(payload));
|
|
226
|
-
try {
|
|
227
|
-
const decoded: unknown = JSON.parse(text);
|
|
228
|
-
return typeof decoded === 'string' ? decoded : text;
|
|
229
|
-
} catch {
|
|
230
|
-
// Not JSON at all — a blob written by something else, or a future media type. The bytes are
|
|
231
|
-
// still the best answer available.
|
|
232
|
-
return text;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
if (typeof payload === 'string') return payload;
|
|
236
|
-
return JSON.stringify(payload ?? {});
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* The arguments that will make the agent ask again.
|
|
241
|
-
*
|
|
242
|
-
* A run recorded as `agenticreplay record claude -- -p "..."` carries its prompt in argv, and replaying it
|
|
243
|
-
* is simply a matter of passing it back. A run driven by hand carries nothing: the prompt went
|
|
244
|
-
* into a terminal, and argv is just `["claude"]`. Replaying that launched an agent with no reason
|
|
245
|
-
* to call anything, so the recorded exchanges were never requested and the replay came back empty.
|
|
246
|
-
*
|
|
247
|
-
* The prompts recorded from the harness's own transcript are what closes that gap. Only the first
|
|
248
|
-
* turn is driven — the harnesses take one prompt per non-interactive invocation — so a multi-turn
|
|
249
|
-
* conversation says how much of itself it is reproducing instead of quietly reproducing one turn
|
|
250
|
-
* and calling it a replay.
|
|
251
|
-
*/
|
|
252
|
-
function driveArgs(
|
|
253
|
-
adapter: {
|
|
254
|
-
driveArgs?(prompts: string[], recorded: string[]): string[] | undefined;
|
|
255
|
-
replayArgs?(recorded: string[]): string[] | undefined;
|
|
256
|
-
artifacts?: HarnessArtifacts;
|
|
257
|
-
},
|
|
258
|
-
ctx: { manifest: { argv: string[] }; prompts: string[] },
|
|
259
|
-
out: Output,
|
|
260
|
-
args?: ParsedArgs,
|
|
261
|
-
): string[] {
|
|
262
|
-
const argv = chosenArgs(adapter, ctx, out);
|
|
263
|
-
return args?.bool('serialize') ? serialized(adapter.artifacts, argv, out) : argv;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Run the harness one request at a time, for an operator who asked for determinism explicitly.
|
|
268
|
-
*
|
|
269
|
-
* Never on by default, and the reason is the whole point of a replay: clamping concurrency
|
|
270
|
-
* changes the run being reproduced. A pipeline with ten workers and one with one worker do the
|
|
271
|
-
* same work in a different order, and a replay that quietly ran the second is not a replay of the
|
|
272
|
-
* first. What it *is* good for is the second question someone asks of a flaky pipeline — "is this
|
|
273
|
-
* a concurrency problem?" — which is worth one flag and worth being asked for.
|
|
274
|
-
*
|
|
275
|
-
* The adapter declares which flag its harness spells this with; agenticreplay does not guess. An adapter
|
|
276
|
-
* that declares none says so rather than silently doing nothing.
|
|
277
|
-
*/
|
|
278
|
-
function serialized(
|
|
279
|
-
artifacts: HarnessArtifacts | undefined,
|
|
280
|
-
argv: string[],
|
|
281
|
-
out: Output,
|
|
282
|
-
): string[] {
|
|
283
|
-
const clamp = artifacts?.concurrencyFlag;
|
|
284
|
-
if (clamp === undefined) {
|
|
285
|
-
out.warn('replay.serialize_unsupported', {
|
|
286
|
-
why: 'this adapter declares no concurrency flag, so agenticreplay has nothing to clamp',
|
|
287
|
-
effect: 'the replay runs exactly as recorded',
|
|
288
|
-
});
|
|
289
|
-
return argv;
|
|
290
|
-
}
|
|
291
|
-
const at = argv.indexOf(clamp.flag);
|
|
292
|
-
const next = [...argv];
|
|
293
|
-
if (at === -1) next.push(clamp.flag, clamp.serialValue);
|
|
294
|
-
else next[at + 1] = clamp.serialValue;
|
|
295
|
-
out.info('replay.serialized', {
|
|
296
|
-
flag: clamp.flag,
|
|
297
|
-
value: clamp.serialValue,
|
|
298
|
-
note: 'this replays a different run from the one recorded, on purpose',
|
|
299
|
-
});
|
|
300
|
-
return next;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/** The argv a replay would run with, before `--serialize` has a say. */
|
|
304
|
-
function chosenArgs(
|
|
305
|
-
adapter: {
|
|
306
|
-
driveArgs?(prompts: string[], recorded: string[]): string[] | undefined;
|
|
307
|
-
replayArgs?(recorded: string[]): string[] | undefined;
|
|
308
|
-
},
|
|
309
|
-
ctx: { manifest: { argv: string[] }; prompts: string[] },
|
|
310
|
-
out: Output,
|
|
311
|
-
): string[] {
|
|
312
|
-
const recorded = ctx.manifest.argv.slice(1);
|
|
313
|
-
const prompts = ctx.prompts;
|
|
314
|
-
// Before the transcript is consulted, because a harness that keeps none never reached the hook
|
|
315
|
-
// below at all: the early return for "no prompts" fired first, so an adapter for a pipeline —
|
|
316
|
-
// whose argv is the entire instruction and whose prompts exist nowhere — had no way to say
|
|
317
|
-
// anything about its own replay.
|
|
318
|
-
const rewritten = adapter.replayArgs?.(recorded);
|
|
319
|
-
if (rewritten !== undefined) return rewritten;
|
|
320
|
-
if (prompts.length === 0) return recorded;
|
|
321
|
-
|
|
322
|
-
// Whether argv already drives the run, decided by comparing it against the prompts the harness
|
|
323
|
-
// recorded rather than by looking for a flag. `codex exec` with the prompt on stdin has a
|
|
324
|
-
// non-empty argv that carries no prompt at all, so "argv is non-empty" answers the wrong
|
|
325
|
-
// question; "argv contains what the person actually asked" answers the right one.
|
|
326
|
-
if (prompts.some((prompt) => recorded.includes(prompt))) return recorded;
|
|
327
|
-
|
|
328
|
-
const driven = adapter.driveArgs?.(prompts, recorded);
|
|
329
|
-
if (driven === undefined) return recorded;
|
|
330
|
-
|
|
331
|
-
// Said before the run, not after, because it reframes every divergence that follows. A harness
|
|
332
|
-
// driven without a terminal does not send byte-identical requests to the ones it sent with one —
|
|
333
|
-
// Claude Code splices an extra reminder turn into an interactive conversation, and the calls it
|
|
334
|
-
// makes for itself, like naming the session, have no counterpart at all. Those show up as
|
|
335
|
-
// unmatched, and without this line they read as a corrupt trace rather than as the cost of
|
|
336
|
-
// reproducing a conversation nobody recorded the keystrokes of.
|
|
337
|
-
out.info('replay.driven', {
|
|
338
|
-
source: 'harness transcript',
|
|
339
|
-
turns_recorded: prompts.length,
|
|
340
|
-
turns_driven: 1,
|
|
341
|
-
note:
|
|
342
|
-
prompts.length > 1
|
|
343
|
-
? 'the harness takes one prompt per non-interactive run; later turns are not re-asked'
|
|
344
|
-
: 'requests the harness made for itself may not recur',
|
|
345
|
-
});
|
|
346
|
-
return driven;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Prompts off a `session.snapshot`.
|
|
351
|
-
*
|
|
352
|
-
* The transcript rides in the same payload, so this spills to a blob for any real session — which
|
|
353
|
-
* is why it takes the reader rather than reading the event alone.
|
|
354
|
-
*/
|
|
355
|
-
async function readPrompts(reader: TraceReader, events: TraceEvent[]): Promise<string[]> {
|
|
356
|
-
const event = events.find((e) => e.type === 'session.snapshot');
|
|
357
|
-
if (event === undefined) return [];
|
|
358
|
-
let payload: unknown = event.payload;
|
|
359
|
-
try {
|
|
360
|
-
if (isBlobRef(payload)) {
|
|
361
|
-
payload = JSON.parse(new TextDecoder().decode(await reader.blob(payload)));
|
|
362
|
-
}
|
|
363
|
-
if (typeof payload === 'string') payload = JSON.parse(payload);
|
|
364
|
-
} catch {
|
|
365
|
-
return [];
|
|
366
|
-
}
|
|
367
|
-
if (payload === null || typeof payload !== 'object') return [];
|
|
368
|
-
const prompts = (payload as { prompts?: unknown }).prompts;
|
|
369
|
-
return Array.isArray(prompts) ? prompts.filter((p): p is string => typeof p === 'string') : [];
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
export async function replayCommand(
|
|
373
|
-
args: ParsedArgs,
|
|
374
|
-
out: Output,
|
|
375
|
-
cwd = process.cwd(),
|
|
376
|
-
): Promise<ReplayResult> {
|
|
377
|
-
const selector = args.positionals[0] ?? 'last';
|
|
378
|
-
const runDir = (await resolveRunSelector(cwd, selector)).dir;
|
|
379
|
-
const reader = await TraceReader.open(runDir);
|
|
380
|
-
if (reader.manifest().capture?.status === 'incomplete') {
|
|
381
|
-
out.warn('replay.source_incomplete', {
|
|
382
|
-
issues: reader.manifest().capture!.issues.join(','),
|
|
383
|
-
detail: 'only complete transport pairs are eligible; missing responses cannot be served',
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
const manifest = reader.manifest();
|
|
387
|
-
const events = await reader.events();
|
|
388
|
-
|
|
389
|
-
const integrity = await reader.verifyIntegrity();
|
|
390
|
-
// Two different facts, and saying the wrong one costs trust in the right one. A recorder that was
|
|
391
|
-
// killed leaves a run with no digest at all, and telling that person their trace "changed since
|
|
392
|
-
// the run ended" is an accusation about a file nothing touched.
|
|
393
|
-
if (integrity.state === 'mismatch') {
|
|
394
|
-
out.warn('trace.integrity', {
|
|
395
|
-
expected: integrity.expected,
|
|
396
|
-
actual: integrity.actual,
|
|
397
|
-
note: 'events.jsonl changed since the run ended',
|
|
398
|
-
});
|
|
399
|
-
} else if (integrity.state === 'unsealed') {
|
|
400
|
-
out.warn('trace.unsealed', {
|
|
401
|
-
actual: integrity.actual,
|
|
402
|
-
note: 'the recorder never sealed this run, so there is nothing to verify it against',
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
const exchanges = await loadExchanges(reader);
|
|
407
|
-
const retrievals = await loadRetrievals(reader);
|
|
408
|
-
const prompts = await readPrompts(reader, events);
|
|
409
|
-
const from = args.num('from');
|
|
410
|
-
const model = args.str('model');
|
|
411
|
-
const isFork = from !== undefined || model !== undefined;
|
|
412
|
-
|
|
413
|
-
const result = isFork
|
|
414
|
-
? await replayFork(args, out, {
|
|
415
|
-
manifest,
|
|
416
|
-
events,
|
|
417
|
-
exchanges,
|
|
418
|
-
retrievals,
|
|
419
|
-
runDir,
|
|
420
|
-
cwd,
|
|
421
|
-
prompts,
|
|
422
|
-
from,
|
|
423
|
-
model,
|
|
424
|
-
})
|
|
425
|
-
: await replayExact(args, out, {
|
|
426
|
-
manifest,
|
|
427
|
-
events,
|
|
428
|
-
exchanges,
|
|
429
|
-
retrievals,
|
|
430
|
-
runDir,
|
|
431
|
-
cwd,
|
|
432
|
-
prompts,
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
if (args.bool('ui')) {
|
|
436
|
-
// Show the run you just produced: after a fork that is the child, not the parent, because
|
|
437
|
-
// the child is the one carrying the outcome you asked the question about.
|
|
438
|
-
const target = result.forkRunId
|
|
439
|
-
? (await resolveRunSelector(cwd, result.forkRunId)).dir
|
|
440
|
-
: runDir;
|
|
441
|
-
await openViewer(target, args, out);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
return result;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
async function openViewer(runDir: string, args: ParsedArgs, out: Output): Promise<void> {
|
|
448
|
-
const server = await serveViewer({ runDir, port: args.num('port') ?? 0 });
|
|
449
|
-
out.phase('viewer', { url: server.url });
|
|
450
|
-
out.plain(' ctrl-c to stop');
|
|
451
|
-
await new Promise<void>((resolve) => {
|
|
452
|
-
process.once('SIGINT', () => void server.close().then(resolve));
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
interface Ctx {
|
|
457
|
-
manifest: ReturnType<TraceReader['manifest']>;
|
|
458
|
-
events: TraceEvent[];
|
|
459
|
-
exchanges: RecordedExchange[];
|
|
460
|
-
/** Embedding and rerank calls the recording can answer without a dialect. */
|
|
461
|
-
retrievals: RecordedRetrieval[];
|
|
462
|
-
runDir: string;
|
|
463
|
-
cwd: string;
|
|
464
|
-
/** The user's turns, recovered from the harness transcript. Empty when there was none. */
|
|
465
|
-
prompts: string[];
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Exact replay. Every response comes from the trace and egress is blocked — if the agent reaches
|
|
470
|
-
* for the network here, that is a bug we want to fail on, not paper over.
|
|
471
|
-
*
|
|
472
|
-
* It also restores the filesystem the run started from, and that is not a nicety. A harness reads
|
|
473
|
-
* files into the conversation, so the bytes on disk end up inside the recorded request; the run
|
|
474
|
-
* then edits those same files. Replaying in the directory you recorded in re-reads what the
|
|
475
|
-
* recording itself changed, the trailing message differs, and the replay halts at rung 4 —
|
|
476
|
-
* correctly, but uselessly, because nothing about the recording was wrong. Restoring first is
|
|
477
|
-
* what makes "exact" mean anything, and it has the second virtue of leaving your checkout alone
|
|
478
|
-
* rather than letting the agent edit it a second time.
|
|
479
|
-
*/
|
|
480
|
-
async function replayExact(args: ParsedArgs, out: Output, ctx: Ctx): Promise<ReplayResult> {
|
|
481
|
-
// Before `replayWorkspace`, because these refuse and `replayWorkspace` replaces the working
|
|
482
|
-
// tree. Resolved after it, a refusal threw with the checkout holding the recorded run's files
|
|
483
|
-
// and the operator's own tree left behind in an `agenticreplay-safety-*` scratch whose path nothing
|
|
484
|
-
// prints — the outcome the release exists to prevent, escaping it because it only ran at the
|
|
485
|
-
// child's launch. An invocation that cannot work should decide so before it touches anything,
|
|
486
|
-
// which is the rule `attach` already follows where it resolves the advertised URL before making
|
|
487
|
-
// a run directory.
|
|
488
|
-
//
|
|
489
|
-
// Two refusals, not one. `upstreamPlan` rejects an upstream that is not an origin;
|
|
490
|
-
// `planTlsCapture` rejects a `--tls-hosts` list that names `*` or contradicts itself, and an
|
|
491
|
-
// `AGENTICREPLAY_TLS_UPSTREAM_CA` that cannot be read. It is the refusing half of `setupTlsCapture` on
|
|
492
|
-
// its own — it mints nothing, so the certificate authority is still created down where the run
|
|
493
|
-
// is, after the workspace exists.
|
|
494
|
-
const plan = await upstreamPlan(args);
|
|
495
|
-
// A run recorded through interception is reproduced through it, without the operator having to
|
|
496
|
-
// remember which hosts they named. See `setupTlsCapture`.
|
|
497
|
-
const interceptedHosts = recordedTlsHosts(ctx.events);
|
|
498
|
-
await planTlsCapture(args, interceptedHosts);
|
|
499
|
-
|
|
500
|
-
// Armed on the line after the call, which is only sound because `replayWorkspace` owns its own
|
|
501
|
-
// destructive step: it either returns with the tree replaced and a `release` that undoes that,
|
|
502
|
-
// or it throws having already put the tree back. Arming here without that guarantee would have
|
|
503
|
-
// read as covering the restore while missing it entirely — the restore is inside the call, so a
|
|
504
|
-
// `materialize` that failed part-way rejected before `workspace` was ever assigned.
|
|
505
|
-
const workspace = await replayWorkspace(args, out, ctx);
|
|
506
|
-
try {
|
|
507
|
-
return await replayRestored(args, out, ctx, { plan, interceptedHosts, workspace });
|
|
508
|
-
} finally {
|
|
509
|
-
// The outermost of three, and the one that needs no list to be right. Hoisting a refusal is
|
|
510
|
-
// only ever as complete as the list of refusals somebody thought of, and that list has been
|
|
511
|
-
// wrong twice; `replayRestored` has its own `finally` for the span it owns; this covers what
|
|
512
|
-
// is left, which is `openReplayTrace` throwing before that span begins. `release` is
|
|
513
|
-
// idempotent, so the ordinary path still puts the tree back where it always did — before the
|
|
514
|
-
// run reports itself done — and this is a no-op behind it.
|
|
515
|
-
await workspace.release();
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* The replay itself, once the filesystem it needs is in place.
|
|
521
|
-
*
|
|
522
|
-
* Split from `replayExact` for one reason: everything in here happens with the recording's files
|
|
523
|
-
* over the operator's checkout, so it has to run inside something that puts them back.
|
|
524
|
-
*/
|
|
525
|
-
async function replayRestored(
|
|
526
|
-
args: ParsedArgs,
|
|
527
|
-
out: Output,
|
|
528
|
-
ctx: Ctx,
|
|
529
|
-
prepared: {
|
|
530
|
-
plan: UpstreamPlan;
|
|
531
|
-
interceptedHosts: readonly string[] | undefined;
|
|
532
|
-
workspace: Workspace;
|
|
533
|
-
},
|
|
534
|
-
): Promise<ReplayResult> {
|
|
535
|
-
const { plan, interceptedHosts, workspace } = prepared;
|
|
536
|
-
const divergences: { level: string; detail: string; seq: number }[] = [];
|
|
537
|
-
const unmatched: { seq: number; reason: string }[] = [];
|
|
538
|
-
const trace = await openReplayTrace(args, ctx, workspace.dir);
|
|
539
|
-
// Serial for the same reason the recorder's is: the callbacks fire from the proxy's request
|
|
540
|
-
// handler, and two overlapping appends would interleave lines in events.jsonl.
|
|
541
|
-
const writes = new SerialQueue();
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* The two things a failed replay would otherwise leave running or on disk.
|
|
545
|
-
*
|
|
546
|
-
* Filled as each is built, because what needs cleaning up depends on how far the run got:
|
|
547
|
-
* `createProxy` throwing has a key to remove and no socket, and everything after it has both.
|
|
548
|
-
*/
|
|
549
|
-
let mintedCa: RunCa | undefined;
|
|
550
|
-
let openProxy: { close: () => Promise<void> } | undefined;
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Give up on a replay that has already built something, then re-throw. `agenticreplay record`'s
|
|
554
|
-
* `abandon` is the same function for the same two reasons.
|
|
555
|
-
*
|
|
556
|
-
* A listening proxy keeps Node's event loop alive, so a throw printed the error and then hung
|
|
557
|
-
* forever — `agenticreplay replay` was the one command of the three that had to be killed. And an
|
|
558
|
-
* unsealed trace has no `ended_at` or `integrity`, so `verifyIntegrity` reports the run as
|
|
559
|
-
* *tampered* rather than as unfinished.
|
|
560
|
-
*
|
|
561
|
-
* Sealing rather than discarding, for the reason the fork gives: a run that failed to launch is
|
|
562
|
-
* still a run someone will want to read. The CA is not disposed here because the `finally`
|
|
563
|
-
* below does it on both paths.
|
|
564
|
-
*/
|
|
565
|
-
async function abandon(err: unknown): Promise<never> {
|
|
566
|
-
await openProxy?.close().catch(() => undefined);
|
|
567
|
-
await writes.drain().catch(() => undefined);
|
|
568
|
-
if (trace) {
|
|
569
|
-
await trace
|
|
570
|
-
.append({ type: 'run.end', actor: 'agenticreplay', turn: 0, attrs: { error: String(err) } })
|
|
571
|
-
.catch(() => undefined);
|
|
572
|
-
await trace.close().catch(() => undefined);
|
|
573
|
-
}
|
|
574
|
-
throw err;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/*
|
|
578
|
-
* Everything from here to the child's exit mints a key and then opens a socket, so everything
|
|
579
|
-
* from here to the child's exit has to come back through `abandon` — the rule `agenticreplay record`
|
|
580
|
-
* states in the same words, having learned it the same way.
|
|
581
|
-
*
|
|
582
|
-
* The guard used to start at the spawn, which covered the failure it was written for and missed
|
|
583
|
-
* the four that happen first. `RunCa.create` writes `tls/ca.key`; `createProxy` binds; then
|
|
584
|
-
* `mcpForReplay` rewrites a config and `adapter.prepare` runs a harness's own validation, which
|
|
585
|
-
* is where `agenticreplay replay` on a trace naming an adapter this build does not have ended up — the
|
|
586
|
-
* right error message, printed, and then a process that never exited with a private key left
|
|
587
|
-
* behind in the run directory.
|
|
588
|
-
*/
|
|
589
|
-
try {
|
|
590
|
-
// A subscription-backed harness does not use the ordinary base URL, so exact replay needs the
|
|
591
|
-
// same per-run CA and HTTPS proxy as recording. The proxy's TLS hook then answers Codex's
|
|
592
|
-
// model request from the trace before it can open an origin connection.
|
|
593
|
-
// The hosts were resolved before the workspace was restored, where anything refusable about
|
|
594
|
-
// them was refused; this is the half that mints.
|
|
595
|
-
const tls = await setupTlsCapture({
|
|
596
|
-
args,
|
|
597
|
-
out,
|
|
598
|
-
...(interceptedHosts ? { recordedHosts: interceptedHosts } : {}),
|
|
599
|
-
writer: trace,
|
|
600
|
-
runDir: ctx.runDir,
|
|
601
|
-
writes,
|
|
602
|
-
turn: () => 0,
|
|
603
|
-
});
|
|
604
|
-
// Handed to `abandon` the moment it exists, not once the run is under way: `createProxy` on
|
|
605
|
-
// the next line is one of the throws this key has to survive being disposed by.
|
|
606
|
-
mintedCa = tls.ca;
|
|
607
|
-
|
|
608
|
-
const proxy = await createProxy({
|
|
609
|
-
mode: 'replay',
|
|
610
|
-
exchanges: ctx.exchanges,
|
|
611
|
-
retrievals: ctx.retrievals,
|
|
612
|
-
loose: args.bool('loose'),
|
|
613
|
-
upstream: plan.upstream,
|
|
614
|
-
upstreamHeaders: plan.headers,
|
|
615
|
-
upstreamHeadersOrigin: plan.headersOrigin,
|
|
616
|
-
...tls.proxyOptions,
|
|
617
|
-
onDivergence: (d) => {
|
|
618
|
-
divergences.push(d);
|
|
619
|
-
if (!trace) return;
|
|
620
|
-
writes.push(async () => {
|
|
621
|
-
await trace.append({
|
|
622
|
-
type: 'divergence',
|
|
623
|
-
actor: 'agenticreplay',
|
|
624
|
-
// Turn 0 for everything in this trace, and deliberately not a running count. An exact
|
|
625
|
-
// match produces no callback, so a counter here would number the third divergence as
|
|
626
|
-
// turn 1 and claim a position in the conversation that it does not have. `source_seq`
|
|
627
|
-
// is the honest coordinate: it points straight at the parent's own event.
|
|
628
|
-
turn: 0,
|
|
629
|
-
attrs: { level: d.level, rung: d.rung, detail: d.detail, source_seq: d.seq },
|
|
630
|
-
});
|
|
631
|
-
});
|
|
632
|
-
},
|
|
633
|
-
// Printed as it happens rather than tallied at the end. A halted replay stops the agent, so
|
|
634
|
-
// the count in `replay.done` arrives after the operator has already seen the run die — and a
|
|
635
|
-
// bare `unmatched=1` with no reason is indistinguishable from a bug in agenticreplay itself.
|
|
636
|
-
//
|
|
637
|
-
// The two directories are on the line because a distance in the hundreds of thousands is true
|
|
638
|
-
// and unactionable. Harnesses put absolute paths in their tool calls, so a replay running
|
|
639
|
-
// anywhere but the recording's own directory gets a permission refusal where the recording has
|
|
640
|
-
// file contents — by far the most common cause of a halt, and invisible from the number alone.
|
|
641
|
-
onUnmatched: (u) => {
|
|
642
|
-
unmatched.push(u);
|
|
643
|
-
// A halt inside a delegation is not a corrupt trace and not a bug, and looks like both. The
|
|
644
|
-
// harness writes the delegate's prompt itself, fresh on every run, so the request really is
|
|
645
|
-
// a different question — which is exactly what the matcher refuses to serve from a
|
|
646
|
-
// recording. Without this the operator sees `distance 54` and goes looking for the fault.
|
|
647
|
-
const delegation = enclosingDelegation(ctx.events, u.seq);
|
|
648
|
-
const next =
|
|
649
|
-
workspace.dir === ctx.manifest.cwd
|
|
650
|
-
? 'agenticreplay replay <run> --loose'
|
|
651
|
-
: `${cdCommand(ctx.manifest.cwd)} && agenticreplay replay <run> --in-place # or --loose to continue live`;
|
|
652
|
-
out.warn('replay.unmatched', {
|
|
653
|
-
seq: u.seq,
|
|
654
|
-
index: u.index,
|
|
655
|
-
reason: u.reason,
|
|
656
|
-
...(delegation === undefined
|
|
657
|
-
? {}
|
|
658
|
-
: {
|
|
659
|
-
inside: `${delegation.subagent} delegated at seq ${delegation.seq}`,
|
|
660
|
-
why: 'the harness writes a delegate prompt of its own each run, so this request is a different question rather than a drifted one',
|
|
661
|
-
}),
|
|
662
|
-
recorded_in: ctx.manifest.cwd,
|
|
663
|
-
replayed_in: workspace.dir,
|
|
664
|
-
next,
|
|
665
|
-
});
|
|
666
|
-
// Again as a line of its own. The field above is for whatever reads the record; on a terminal
|
|
667
|
-
// it is shown JSON-escaped — every backslash doubled, every quote escaped — which is correct
|
|
668
|
-
// for a key=value line and useless to paste, and a Windows path is nothing but backslashes.
|
|
669
|
-
out.plain(` next: ${next}`);
|
|
670
|
-
if (!trace) return;
|
|
671
|
-
// `error`, not `divergence`: nothing was served and the run is over, so calling it an
|
|
672
|
-
// inexact match would put a rung on a ladder the request never climbed. It is also the one
|
|
673
|
-
// finding here that exists nowhere else — a matched exchange is already in the parent, but
|
|
674
|
-
// "the agent asked for something this recording cannot answer" is new, and until now it
|
|
675
|
-
// lived only in the operator's scrollback.
|
|
676
|
-
writes.push(async () => {
|
|
677
|
-
await trace.append({
|
|
678
|
-
type: 'error',
|
|
679
|
-
actor: 'agenticreplay',
|
|
680
|
-
turn: 0,
|
|
681
|
-
attrs: {
|
|
682
|
-
rule: 'replay_unmatched',
|
|
683
|
-
rung: 4,
|
|
684
|
-
reason: u.reason,
|
|
685
|
-
index: u.index,
|
|
686
|
-
source_seq: u.seq,
|
|
687
|
-
recorded_in: ctx.manifest.cwd,
|
|
688
|
-
replayed_in: workspace.dir,
|
|
689
|
-
},
|
|
690
|
-
});
|
|
691
|
-
});
|
|
692
|
-
},
|
|
693
|
-
});
|
|
694
|
-
// Listening from here on, so from here on a throw that does not close it is a process that
|
|
695
|
-
// does not exit.
|
|
696
|
-
openProxy = proxy;
|
|
697
|
-
|
|
698
|
-
await trace?.append({
|
|
699
|
-
type: 'run.start',
|
|
700
|
-
actor: 'agenticreplay',
|
|
701
|
-
turn: 0,
|
|
702
|
-
attrs: {
|
|
703
|
-
adapter: ctx.manifest.adapter.id,
|
|
704
|
-
cwd: workspace.dir,
|
|
705
|
-
proxy: proxy.url,
|
|
706
|
-
mode: 'replay',
|
|
707
|
-
// Also on the manifest, which is what an out-of-process reader sees first. Here as well
|
|
708
|
-
// because a trace read on its own should say what it is a replay of.
|
|
709
|
-
parent_run: ctx.manifest.run_id,
|
|
710
|
-
exchanges: ctx.exchanges.length,
|
|
711
|
-
},
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
out.phase('replaying', {
|
|
715
|
-
run: ctx.manifest.run_id,
|
|
716
|
-
exchanges: ctx.exchanges.length,
|
|
717
|
-
// Read from the flag, because `--loose` is exactly the run where it is not blocked. It was a
|
|
718
|
-
// literal, so `agenticreplay replay <run> --loose` announced `egress=blocked` and then answered the
|
|
719
|
-
// first unmatched request from the provider — the one line a reader checks to know whether a
|
|
720
|
-
// run can spend money, saying the opposite of what the run was about to do. Worse than an
|
|
721
|
-
// inaccuracy, because `replay halted` recommends `--loose` by name: the reader is following
|
|
722
|
-
// agenticreplay's own advice when the label stops being true.
|
|
723
|
-
egress: args.bool('loose') ? 'live-on-unmatched' : 'blocked',
|
|
724
|
-
proxy: proxy.url,
|
|
725
|
-
cwd: workspace.dir,
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
// The agent runs live for everything that is not a model call, MCP included. Without a config it
|
|
729
|
-
// talks to servers agenticreplay cannot see — or, for a harness that requires the variable, does not start
|
|
730
|
-
// at all, which is how a replay of a working recording exits non-zero for a reason that has
|
|
731
|
-
// nothing to do with the recording.
|
|
732
|
-
const mcp =
|
|
733
|
-
trace === undefined
|
|
734
|
-
? undefined
|
|
735
|
-
: await mcpForReplay(
|
|
736
|
-
args,
|
|
737
|
-
ctx.events,
|
|
738
|
-
trace,
|
|
739
|
-
out,
|
|
740
|
-
join(ctx.runDir, 'mcp-frames.jsonl'),
|
|
741
|
-
ctx.runDir,
|
|
742
|
-
);
|
|
743
|
-
|
|
744
|
-
const adapter = defaultAdapters().get(ctx.manifest.adapter.id);
|
|
745
|
-
const launch = await adapter.prepare({
|
|
746
|
-
runId: ctx.manifest.run_id,
|
|
747
|
-
cwd: workspace.dir,
|
|
748
|
-
proxyUrl: proxy.url,
|
|
749
|
-
runDir: ctx.runDir,
|
|
750
|
-
userArgs: driveArgs(adapter, ctx, out, args),
|
|
751
|
-
env: process.env,
|
|
752
|
-
});
|
|
753
|
-
// Replaying must not introduce a catalog request before the capture plugin is installed.
|
|
754
|
-
if (adapter.id === 'opencode') launch.env.OPENCODE_DISABLE_MODELS_FETCH = '1';
|
|
755
|
-
if (proxy.tls) await trustRunCa(trace, proxy.tls, proxy.url, launch.env, out);
|
|
756
|
-
if (mcp) {
|
|
757
|
-
pointAtMcpConfig(launch.env, mcp.configPath);
|
|
758
|
-
// Same as record: the path has to reach the harness the way the harness actually reads it, or
|
|
759
|
-
// a replay re-instruments a config nothing opens and every recorded MCP call goes unmatched.
|
|
760
|
-
const mcpArgs = adapter.mcpConfigArgs?.(mcp.configPath);
|
|
761
|
-
if (mcpArgs !== undefined) launch.args = [...mcpArgs, ...launch.args];
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
const exitCode = await runChild(
|
|
765
|
-
launch.command,
|
|
766
|
-
launch.args,
|
|
767
|
-
{ ...process.env, ...launch.env },
|
|
768
|
-
workspace.dir,
|
|
769
|
-
agentStdoutFor(args),
|
|
770
|
-
);
|
|
771
|
-
|
|
772
|
-
await writes.drain();
|
|
773
|
-
const stats = proxy.stats();
|
|
774
|
-
await proxy.close();
|
|
775
|
-
|
|
776
|
-
for (const d of divergences) {
|
|
777
|
-
out.warn('divergence', { seq: d.seq, level: d.level, detail: d.detail });
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// A halted replay is a failed replay even if the harness chose to exit 0 on the error. The exit
|
|
781
|
-
// code is what a script reads, so it has to reflect what happened rather than what the agent
|
|
782
|
-
// decided to do about it — and the trace records the same verdict for the same reason.
|
|
783
|
-
const verdict = exitCode === 0 && unmatched.length > 0 ? 1 : exitCode;
|
|
784
|
-
|
|
785
|
-
if (trace) {
|
|
786
|
-
// What the replay discovered rather than repeated: these calls really happened just now, and
|
|
787
|
-
// exist nowhere in the parent.
|
|
788
|
-
if (mcp) await drainMcpFrames(mcp, trace, () => 0, 0);
|
|
789
|
-
await trace.append({
|
|
790
|
-
type: 'run.end',
|
|
791
|
-
actor: 'agenticreplay',
|
|
792
|
-
turn: 0,
|
|
793
|
-
attrs: {
|
|
794
|
-
exit_code: verdict,
|
|
795
|
-
agent_exit_code: exitCode,
|
|
796
|
-
matched: stats.matchedExact,
|
|
797
|
-
divergences: stats.divergences,
|
|
798
|
-
unmatched: stats.unmatched,
|
|
799
|
-
},
|
|
800
|
-
});
|
|
801
|
-
await trace.close(verdict);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
/**
|
|
805
|
-
* Retrieval fidelity, on its own axis and only when the run has one.
|
|
806
|
-
*
|
|
807
|
-
* Never folded into `exact`: that number is about a matching ladder these calls do not climb,
|
|
808
|
-
* and mixing them would make a recording look more faithfully reproduced the more embeddings
|
|
809
|
-
* it happened to make. Omitted entirely when neither the recording nor the replay had any, so
|
|
810
|
-
* an ordinary run's verdict line keeps the shape it has always had.
|
|
811
|
-
*/
|
|
812
|
-
const retrieval =
|
|
813
|
-
stats.retrievalTotal > 0 || stats.retrievalServed > 0 || stats.retrievalLive > 0
|
|
814
|
-
? { retrieval: `${stats.retrievalServed}/${stats.retrievalTotal}` }
|
|
815
|
-
: {};
|
|
816
|
-
|
|
817
|
-
out.phase('replay.done', {
|
|
818
|
-
// `matched=1 total=13` was the old shape, and on a healthy replay of a real harness it read as
|
|
819
|
-
// a failure: rung 1 is only reachable when nothing in the request was redacted, so a run whose
|
|
820
|
-
// every request was served from disk still reported one match. What someone wants to know here
|
|
821
|
-
// is how much of the recording was reused, and how much of that reuse was exact.
|
|
822
|
-
reused: `${stats.matchedExact + stats.matchedInexact}/${ctx.exchanges.length}`,
|
|
823
|
-
canonical_exact: stats.matchedExact,
|
|
824
|
-
divergences: stats.divergences,
|
|
825
|
-
unmatched: stats.unmatched,
|
|
826
|
-
// Only when it happened. An exchange served from a position other than the cursor is not a
|
|
827
|
-
// fidelity loss — it was byte-identical, or it carried a divergence of its own and is
|
|
828
|
-
// counted there — but it is worth saying, because it is the difference between a recording
|
|
829
|
-
// that reproduced and one that merely happened to line up.
|
|
830
|
-
...(stats.reordered > 0 ? { reordered: stats.reordered } : {}),
|
|
831
|
-
...retrieval,
|
|
832
|
-
exit: exitCode,
|
|
833
|
-
// Omitted entirely under --no-trace: `Output` drops undefined fields, so the line stays the
|
|
834
|
-
// shape it has always been for anyone who opted out.
|
|
835
|
-
trace: trace?.runId,
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
return {
|
|
839
|
-
runId: ctx.manifest.run_id,
|
|
840
|
-
mode: 'exact',
|
|
841
|
-
...(trace === undefined ? {} : { traceRunId: trace.runId }),
|
|
842
|
-
canonicalExact: stats.matchedExact,
|
|
843
|
-
divergences: stats.divergences,
|
|
844
|
-
unmatched: stats.unmatched,
|
|
845
|
-
liveCalls: stats.liveCalls,
|
|
846
|
-
exitCode: verdict,
|
|
847
|
-
};
|
|
848
|
-
} catch (err) {
|
|
849
|
-
// `return await`, not the bare `return abandon(err)` that `agenticreplay record` can afford. The
|
|
850
|
-
// difference is the `finally` below: a promise returned out of a `catch` is not adopted by
|
|
851
|
-
// the caller until the `finally` has finished, and this one awaits real I/O, so the
|
|
852
|
-
// rejection sits with no handler attached for as long as that takes. Node reports it as an
|
|
853
|
-
// unhandled rejection and kills the process on the spot — the operator got a raw stack trace
|
|
854
|
-
// where `agenticreplay replay` had always printed `error replay.failed`, and `main`'s catch never ran
|
|
855
|
-
// at all. Awaiting here turns it back into a throw *inside* the catch, before the finally,
|
|
856
|
-
// which is what every frame above expects.
|
|
857
|
-
return await abandon(err);
|
|
858
|
-
} finally {
|
|
859
|
-
// Both paths, which is why these two are here and the proxy is not: a replay that ran
|
|
860
|
-
// closes its own proxy after reading `stats()`, and one that did not comes through
|
|
861
|
-
// `abandon`. These do not divide that way. The whole justification for restoring over
|
|
862
|
-
// the working tree is that it is put back, and `tls-capture.ts` asks the caller to own
|
|
863
|
-
// the run CA on every exit path — `replayCommand` had no equivalent of the `minted`
|
|
864
|
-
// wrapper `recordCommand` and `attachCommand` use, so a key outlived every failure
|
|
865
|
-
// between the mint and the launch.
|
|
866
|
-
await workspace.release();
|
|
867
|
-
await mintedCa?.dispose();
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* The run an exact replay writes about itself, or nothing under `--no-trace`.
|
|
873
|
-
*
|
|
874
|
-
* Spec §4 says every inexact match is an event in the trace, and until now the exact path had no
|
|
875
|
-
* trace to put one in: divergences were printed and discarded with the scrollback. They cannot go
|
|
876
|
-
* into the run being replayed — it is append-only and its manifest carries a digest over
|
|
877
|
-
* events.jsonl, so a single line appended would invalidate every verification anyone had done of
|
|
878
|
-
* it — so the replay becomes a run of its own, pointing back at its subject through `parent_run`.
|
|
879
|
-
* That one field is what `agenticreplay list`, `agenticreplay show` and `agenticreplay gc` already read, which is why it is
|
|
880
|
-
* on the manifest and not only in an event: gc uses it to refuse to delete a run something else
|
|
881
|
-
* still points at.
|
|
882
|
-
*
|
|
883
|
-
* There is no `fork_point`. An exact replay does not branch anywhere, and a fabricated checkpoint
|
|
884
|
-
* would be a number `agenticreplay list` prints as though someone had chosen it.
|
|
885
|
-
*
|
|
886
|
-
* What it deliberately does NOT record is the exchanges it served. Every one of them was read out
|
|
887
|
-
* of the parent's own events.jsonl and handed back byte for byte, so copying them here would
|
|
888
|
-
* duplicate the trace's single largest cost — the conversation bodies, blobs and all — to store a
|
|
889
|
-
* second copy that is identical by construction, and would make `agenticreplay gc` report a store twice the
|
|
890
|
-
* size for one recording's worth of content. An unmatched request is the opposite case: it is
|
|
891
|
-
* something the agent asked that the recording never contained, so it exists nowhere else and is
|
|
892
|
-
* written as an `error` event above. The rule is that this trace holds what replaying *discovered*,
|
|
893
|
-
* and points at the parent for what replaying merely repeated.
|
|
894
|
-
*/
|
|
895
|
-
async function openReplayTrace(
|
|
896
|
-
args: ParsedArgs,
|
|
897
|
-
ctx: Ctx,
|
|
898
|
-
cwd: string,
|
|
899
|
-
): Promise<TraceWriter | undefined> {
|
|
900
|
-
if (!args.bool('trace', true)) return undefined;
|
|
901
|
-
const dir = await ensureRunsDir(ctx.cwd);
|
|
902
|
-
return TraceWriter.create(dir, {
|
|
903
|
-
adapter: ctx.manifest.adapter,
|
|
904
|
-
argv: ctx.manifest.argv,
|
|
905
|
-
// Where the replay actually ran, which is not always where the recording did: `--worktree`
|
|
906
|
-
// puts it in a scratch copy, and `agenticreplay gc` reads exactly this to decide whether the directory
|
|
907
|
-
// is one of ours to reclaim.
|
|
908
|
-
cwd,
|
|
909
|
-
agenticreplayVersion: AGENTICREPLAY_VERSION,
|
|
910
|
-
parentRun: ctx.manifest.run_id,
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
/**
|
|
915
|
-
* Fork replay. Replay deterministically to a checkpoint, materialize the filesystem as it was at
|
|
916
|
-
* that moment, then let the agent continue live — optionally on a different model.
|
|
917
|
-
*/
|
|
918
|
-
async function replayFork(
|
|
919
|
-
args: ParsedArgs,
|
|
920
|
-
out: Output,
|
|
921
|
-
ctx: Ctx & { cwd: string; from?: number; model?: string },
|
|
922
|
-
): Promise<ReplayResult> {
|
|
923
|
-
const checkpoints = deriveCheckpoints(ctx.events);
|
|
924
|
-
if (checkpoints.length === 0) {
|
|
925
|
-
throw new Error(
|
|
926
|
-
'this run has no checkpoints, so there is nothing to fork from\n' +
|
|
927
|
-
' checkpoints need a filesystem snapshot; was the run recorded with --no-fs?',
|
|
928
|
-
);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
const target = ctx.from ?? checkpoints[checkpoints.length - 1]!.seq;
|
|
932
|
-
const { checkpoint, snapped } = snapToCheckpoint(checkpoints, target);
|
|
933
|
-
if (snapped) {
|
|
934
|
-
// Saying so matters: silently forking from different state than the user asked for is the
|
|
935
|
-
// worst failure mode this tool has.
|
|
936
|
-
out.warn('fork.snapped', {
|
|
937
|
-
requested: target,
|
|
938
|
-
using: checkpoint.seq,
|
|
939
|
-
why: 'nearest preceding checkpoint',
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// Exchanges are indexed by position, so the fork point in exchange terms is how many model
|
|
944
|
-
// requests happened at or before the checkpoint.
|
|
945
|
-
const forkAt = ctx.exchanges.filter((e) => e.seq <= checkpoint.seq).length;
|
|
946
|
-
|
|
947
|
-
// Before the worktree and the fork's own run directory, because both of these refuse:
|
|
948
|
-
// `upstreamPlan` an upstream that is not an origin, `planTlsCapture` a `--tls-hosts` list that
|
|
949
|
-
// names `*` or contradicts itself and an `AGENTICREPLAY_TLS_UPSTREAM_CA` that cannot be read. Resolved
|
|
950
|
-
// after them, a typo in any of them left a restored temp tree in `$TMPDIR` that `agenticreplay gc` will
|
|
951
|
-
// not reclaim — no manifest points at it — and an empty fork in `agenticreplay list`, reading
|
|
952
|
-
// `FROM <parent>@<n>` as though it had run.
|
|
953
|
-
const plan = await upstreamPlan(args);
|
|
954
|
-
// A fork continues a recorded conversation, so it is intercepted on the same terms the
|
|
955
|
-
// recording was; see `setupTlsCapture` below, which this is the refusing half of.
|
|
956
|
-
const forkInterceptedHosts = recordedTlsHosts(ctx.events);
|
|
957
|
-
await planTlsCapture(args, forkInterceptedHosts);
|
|
958
|
-
|
|
959
|
-
// Before the worktree too, for the same reason as the two above: this refuses, and refusing
|
|
960
|
-
// after `mkdtemp` left an empty `agenticreplay-<seq>-*` directory in `$TMPDIR` on every attempt — no
|
|
961
|
-
// manifest points at it, so `agenticreplay gc` never reclaims it, and nothing printed where it was.
|
|
962
|
-
let restoreFrom: { fs: FsCapture; tree: string } | undefined;
|
|
963
|
-
if (checkpoint.fsTree && args.bool('fs', true)) {
|
|
964
|
-
// Restore from the ORIGINAL run's shadow store: that is the only place the tree object
|
|
965
|
-
// exists. Pointing a fresh store at the tree id fails to unpack it, which is exactly the
|
|
966
|
-
// silent-wrong-state failure the checkpoint machinery is meant to prevent.
|
|
967
|
-
const fs = await FsCapture.start({ runDir: ctx.runDir, cwd: ctx.cwd });
|
|
968
|
-
// A RUN CAN BE WITHOUT THE STORE ITS OWN EVENTS POINT AT — see `NO_SNAPSHOT_WHY`.
|
|
969
|
-
//
|
|
970
|
-
// The line above guards against reading the WRONG store. It cannot guard against there being
|
|
971
|
-
// no store at all, and `materialize` then died inside git — "failed to unpack tree object
|
|
972
|
-
// <40 hex>" — which names neither the cause nor anything to do about it, on the exact path the
|
|
973
|
-
// gateway console tells people to take (`agenticreplay pull <run>` then `agenticreplay compare last --from N`).
|
|
974
|
-
//
|
|
975
|
-
// Refusing rather than forking anyway: an empty worktree is not the state the checkpoint
|
|
976
|
-
// describes, and a fork from the wrong state is the failure this machinery exists to prevent.
|
|
977
|
-
// `--no-fs` is the way to say "the conversation is what I am forking", and it now leaves the
|
|
978
|
-
// workspace alone on both ends — this restore and the fork's own capture below.
|
|
979
|
-
if (!(await fs.hasTree(checkpoint.fsTree))) {
|
|
980
|
-
// The detail goes in the warning, not the thrown message: `agenticreplay compare` renders each leg's
|
|
981
|
-
// error in one cell of a table, so anything past the first line is lost exactly where this
|
|
982
|
-
// is most likely to be read.
|
|
983
|
-
out.warn('fork.no_snapshot', {
|
|
984
|
-
seq: checkpoint.seq,
|
|
985
|
-
tree: checkpoint.fsTree,
|
|
986
|
-
store: join(ctx.runDir, 'fs'),
|
|
987
|
-
why: NO_SNAPSHOT_WHY,
|
|
988
|
-
recorded_in: ctx.manifest.cwd,
|
|
989
|
-
next: `agenticreplay ${args.command} ${ctx.manifest.run_id} --from ${checkpoint.seq} --no-fs # fork the conversation, without the workspace`,
|
|
990
|
-
});
|
|
991
|
-
throw new Error(
|
|
992
|
-
`no workspace snapshot for the checkpoint at seq ${checkpoint.seq} — see fork.no_snapshot; --no-fs forks without it`,
|
|
993
|
-
);
|
|
994
|
-
}
|
|
995
|
-
await keepNested(out, fs, checkpoint.fsTree, 'a fresh worktree');
|
|
996
|
-
restoreFrom = { fs, tree: checkpoint.fsTree };
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
const worktree = await mkdtemp(join(tmpdir(), `agenticreplay-${checkpoint.seq}-`));
|
|
1000
|
-
if (restoreFrom !== undefined) {
|
|
1001
|
-
await restoreFrom.fs.restore(restoreFrom.tree, worktree, { allowIncomplete: true });
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
const dir = await ensureRunsDir(ctx.cwd);
|
|
1005
|
-
const writer = await TraceWriter.create(dir, {
|
|
1006
|
-
adapter: ctx.manifest.adapter,
|
|
1007
|
-
argv: ctx.manifest.argv,
|
|
1008
|
-
cwd: worktree,
|
|
1009
|
-
agenticreplayVersion: AGENTICREPLAY_VERSION,
|
|
1010
|
-
// Also in the manifest, not only the fork event: the manifest is what an out-of-process
|
|
1011
|
-
// reader sees first, and `agenticreplay gc` uses it to decide a parent run may not be deleted.
|
|
1012
|
-
parentRun: ctx.manifest.run_id,
|
|
1013
|
-
forkPoint: checkpoint.seq,
|
|
1014
|
-
...(ctx.model === undefined ? {} : { forkModel: ctx.model }),
|
|
1015
|
-
});
|
|
1016
|
-
|
|
1017
|
-
const deriver = new ExchangeEventDeriver();
|
|
1018
|
-
let turn = 0;
|
|
1019
|
-
// When each turn began, so an MCP frame drained after the agent exits is attributed to the turn
|
|
1020
|
-
// it happened during rather than to whichever turn happened to be last.
|
|
1021
|
-
const turnStartedAt: { turn: number; at: number }[] = [];
|
|
1022
|
-
const turnAtFork = (at: number): number => {
|
|
1023
|
-
let found = 0;
|
|
1024
|
-
for (const mark of turnStartedAt) {
|
|
1025
|
-
if (mark.at > at) break;
|
|
1026
|
-
found = mark.turn;
|
|
1027
|
-
}
|
|
1028
|
-
return found;
|
|
1029
|
-
};
|
|
1030
|
-
const writes = new SerialQueue();
|
|
1031
|
-
|
|
1032
|
-
/**
|
|
1033
|
-
* The fork's own filesystem capture, over its worktree.
|
|
1034
|
-
*
|
|
1035
|
-
* A fork had none, so its trace carried no `fs.snapshot` — and a checkpoint is derived from a
|
|
1036
|
-
* snapshot (spec §3), so a fork had no checkpoints and could not itself be forked. `agenticreplay compare
|
|
1037
|
-
* last` immediately after a fork failed with "this run has no checkpoints", because `last` had
|
|
1038
|
-
* resolved to the fork. The tool is pitched on iterative exploration; a branch you cannot branch
|
|
1039
|
-
* again is a dead end one step in.
|
|
1040
|
-
*
|
|
1041
|
-
* Same posture as recording: a capture layer that will not start degrades the trace and never
|
|
1042
|
-
* stops the run.
|
|
1043
|
-
*/
|
|
1044
|
-
let forkFs: FsCapture | undefined;
|
|
1045
|
-
if (args.bool('fs', true)) {
|
|
1046
|
-
try {
|
|
1047
|
-
// Same widening as the recording's: a fork of a pipeline produces the same kind of
|
|
1048
|
-
// artifact, and a fork whose snapshots omit it is a fork that cannot itself be replayed.
|
|
1049
|
-
const forkArtifacts = defaultAdapters().get(ctx.manifest.adapter.id).artifacts?.capture;
|
|
1050
|
-
forkFs = await FsCapture.start({
|
|
1051
|
-
runDir: writer.runDir,
|
|
1052
|
-
cwd: worktree,
|
|
1053
|
-
...(forkArtifacts === undefined ? {} : { forced: forkArtifacts }),
|
|
1054
|
-
});
|
|
1055
|
-
} catch (err) {
|
|
1056
|
-
out.warn('fs.unavailable', { reason: String(err) });
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
// A fork runs a real agent live, so it has exactly the same blind spot `agenticreplay record` does: a
|
|
1061
|
-
// harness that talks to its own backend over TLS reads no base-URL variable and is invisible
|
|
1062
|
-
// without interception. The flag was parsed here and silently discarded, which is the worse
|
|
1063
|
-
// half — the operator believes they captured that traffic.
|
|
1064
|
-
// Same for a fork: it continues a recorded conversation, so its prefix is replayed and the
|
|
1065
|
-
// requests carrying it arrive by the same intercepted transport they were recorded on.
|
|
1066
|
-
|
|
1067
|
-
/** Filled as each is built; see the exact path's pair, which this mirrors. */
|
|
1068
|
-
let mintedCa: RunCa | undefined;
|
|
1069
|
-
let openProxy: { close: () => Promise<void> } | undefined;
|
|
1070
|
-
|
|
1071
|
-
/**
|
|
1072
|
-
* The same two failures `agenticreplay record` had. A listening proxy keeps Node's event loop alive, so
|
|
1073
|
-
* a throw printed the error and then hung; and a fork that mints a certificate authority must
|
|
1074
|
-
* not leave the private key on disk when it dies. The trace is sealed either way, because a
|
|
1075
|
-
* fork that failed to launch is still a fork someone will want to read.
|
|
1076
|
-
*
|
|
1077
|
-
* This used to guard the spawn alone, which is one throw out of five: `RunCa.create` writes the
|
|
1078
|
-
* key, `mcpForReplay` rewrites a config, `createProxy` binds, and `adapter.prepare` runs a
|
|
1079
|
-
* harness's own validation — all of them before the child, all of them leaving the key, the
|
|
1080
|
-
* socket, or both.
|
|
1081
|
-
*/
|
|
1082
|
-
async function abandon(err: unknown): Promise<never> {
|
|
1083
|
-
await openProxy?.close().catch(() => undefined);
|
|
1084
|
-
await writes.drain().catch(() => undefined);
|
|
1085
|
-
await mintedCa?.dispose().catch(() => undefined);
|
|
1086
|
-
await writer
|
|
1087
|
-
.append({ type: 'run.end', actor: 'agenticreplay', turn, attrs: { error: String(err) } })
|
|
1088
|
-
.catch(() => undefined);
|
|
1089
|
-
await writer.close().catch(() => undefined);
|
|
1090
|
-
throw err;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
const tls = await setupTlsCapture({
|
|
1094
|
-
args,
|
|
1095
|
-
out,
|
|
1096
|
-
...(forkInterceptedHosts ? { recordedHosts: forkInterceptedHosts } : {}),
|
|
1097
|
-
writer,
|
|
1098
|
-
writes,
|
|
1099
|
-
turn: () => turn,
|
|
1100
|
-
}).catch(abandon);
|
|
1101
|
-
mintedCa = tls.ca;
|
|
1102
|
-
|
|
1103
|
-
// A fork continues the run live past the checkpoint, so its MCP traffic is new and belongs in the
|
|
1104
|
-
// fork's own trace. Without this the layer simply stopped at the fork point.
|
|
1105
|
-
const mcp = await mcpForReplay(args, ctx.events, writer, out, undefined, ctx.runDir).catch(
|
|
1106
|
-
abandon,
|
|
1107
|
-
);
|
|
1108
|
-
|
|
1109
|
-
const proxy = await createProxy({
|
|
1110
|
-
mode: 'hybrid',
|
|
1111
|
-
forkAt,
|
|
1112
|
-
forkModel: ctx.model,
|
|
1113
|
-
exchanges: ctx.exchanges,
|
|
1114
|
-
// Past the cursor too: a fork changes which model answers, not what a paragraph embeds to.
|
|
1115
|
-
retrievals: ctx.retrievals,
|
|
1116
|
-
upstream: plan.upstream,
|
|
1117
|
-
upstreamHeaders: plan.headers,
|
|
1118
|
-
upstreamHeadersOrigin: plan.headersOrigin,
|
|
1119
|
-
...tls.proxyOptions,
|
|
1120
|
-
onExchange: (exchange) => {
|
|
1121
|
-
writes.push(async () => {
|
|
1122
|
-
turn += 1;
|
|
1123
|
-
turnStartedAt.push({ turn, at: Date.now() });
|
|
1124
|
-
await appendDerivedEvents(writer, deriver, exchange, turn);
|
|
1125
|
-
if (forkFs) await appendSnapshot(forkFs, writer, out, turn);
|
|
1126
|
-
});
|
|
1127
|
-
},
|
|
1128
|
-
onRoute: (decision) => {
|
|
1129
|
-
writes.push(async () => {
|
|
1130
|
-
await writer.append({
|
|
1131
|
-
type: 'route.decision',
|
|
1132
|
-
// On a fork the gateway is agenticreplay: it substituted the model and picked what serves it.
|
|
1133
|
-
actor: 'gateway',
|
|
1134
|
-
turn,
|
|
1135
|
-
attrs: { ...decision },
|
|
1136
|
-
});
|
|
1137
|
-
});
|
|
1138
|
-
},
|
|
1139
|
-
onDivergence: (d) => {
|
|
1140
|
-
writes.push(async () => {
|
|
1141
|
-
await writer.append({
|
|
1142
|
-
type: 'divergence',
|
|
1143
|
-
actor: 'agenticreplay',
|
|
1144
|
-
turn,
|
|
1145
|
-
attrs: { level: d.level, rung: d.rung, detail: d.detail, source_seq: d.seq },
|
|
1146
|
-
});
|
|
1147
|
-
});
|
|
1148
|
-
},
|
|
1149
|
-
});
|
|
1150
|
-
// Listening from here on — see the exact path.
|
|
1151
|
-
openProxy = proxy;
|
|
1152
|
-
|
|
1153
|
-
// A fork is a run, and a run opens with run.start (spec §2.3). It opened on `fork` instead, so
|
|
1154
|
-
// the gateway refused every fork on push — "0 run.start and 1 run.end events" — which meant the
|
|
1155
|
-
// output of `agenticreplay compare`, the thing the tool is pitched on, could not be shared with anyone.
|
|
1156
|
-
// `fork` keeps everything that makes it a fork; it just is not the first line any more.
|
|
1157
|
-
await writer.append({
|
|
1158
|
-
type: 'run.start',
|
|
1159
|
-
actor: 'agenticreplay',
|
|
1160
|
-
turn: 0,
|
|
1161
|
-
attrs: {
|
|
1162
|
-
adapter: ctx.manifest.adapter.id,
|
|
1163
|
-
cwd: worktree,
|
|
1164
|
-
proxy: proxy.url,
|
|
1165
|
-
forked_from: ctx.manifest.run_id,
|
|
1166
|
-
},
|
|
1167
|
-
});
|
|
1168
|
-
|
|
1169
|
-
await writer.append({
|
|
1170
|
-
type: 'fork',
|
|
1171
|
-
actor: 'agenticreplay',
|
|
1172
|
-
turn: 0,
|
|
1173
|
-
attrs: {
|
|
1174
|
-
parent_run: ctx.manifest.run_id,
|
|
1175
|
-
fork_point: checkpoint.seq,
|
|
1176
|
-
model: ctx.model ?? ctx.manifest.adapter.id,
|
|
1177
|
-
worktree,
|
|
1178
|
-
},
|
|
1179
|
-
});
|
|
1180
|
-
|
|
1181
|
-
// The state the fork starts from, so its first checkpoint is the checkpoint it branched at
|
|
1182
|
-
// rather than whatever the first live turn happened to leave behind.
|
|
1183
|
-
if (forkFs) await appendSnapshot(forkFs, writer, out, 0, { initial: true });
|
|
1184
|
-
|
|
1185
|
-
out.phase('forked', {
|
|
1186
|
-
from: ctx.manifest.run_id,
|
|
1187
|
-
at: checkpoint.seq,
|
|
1188
|
-
run: writer.runId,
|
|
1189
|
-
model: ctx.model ?? '(unchanged)',
|
|
1190
|
-
worktree,
|
|
1191
|
-
});
|
|
1192
|
-
|
|
1193
|
-
// `defaultAdapters().get` refuses a manifest naming an adapter this build does not have, which
|
|
1194
|
-
// is what replaying a newer agenticreplay's recording looks like from here, and `prepare` is where a
|
|
1195
|
-
// harness validates its own invocation. Both are throws with the proxy already listening.
|
|
1196
|
-
const launch = await (async () => {
|
|
1197
|
-
const adapter = defaultAdapters().get(ctx.manifest.adapter.id);
|
|
1198
|
-
const prepared = await adapter.prepare({
|
|
1199
|
-
runId: writer.runId,
|
|
1200
|
-
cwd: worktree,
|
|
1201
|
-
proxyUrl: proxy.url,
|
|
1202
|
-
runDir: writer.runDir,
|
|
1203
|
-
userArgs: driveArgs(adapter, ctx, out, args),
|
|
1204
|
-
env: process.env,
|
|
1205
|
-
});
|
|
1206
|
-
|
|
1207
|
-
if (proxy.tls) {
|
|
1208
|
-
await trustRunCa(writer, proxy.tls, proxy.url, prepared.env, out);
|
|
1209
|
-
}
|
|
1210
|
-
if (mcp) {
|
|
1211
|
-
pointAtMcpConfig(prepared.env, mcp.configPath);
|
|
1212
|
-
// Same as record: the path has to reach the harness the way the harness actually reads it,
|
|
1213
|
-
// or a replay re-instruments a config nothing opens and every recorded MCP call goes
|
|
1214
|
-
// unmatched.
|
|
1215
|
-
const mcpArgs = adapter.mcpConfigArgs?.(mcp.configPath);
|
|
1216
|
-
if (mcpArgs !== undefined) prepared.args = [...mcpArgs, ...prepared.args];
|
|
1217
|
-
}
|
|
1218
|
-
return prepared;
|
|
1219
|
-
})().catch(abandon);
|
|
1220
|
-
|
|
1221
|
-
const exitCode = await runChild(
|
|
1222
|
-
launch.command,
|
|
1223
|
-
launch.args,
|
|
1224
|
-
{ ...process.env, ...launch.env },
|
|
1225
|
-
worktree,
|
|
1226
|
-
agentStdoutFor(args),
|
|
1227
|
-
).catch(abandon);
|
|
1228
|
-
await writes.drain();
|
|
1229
|
-
if (mcp) await drainMcpFrames(mcp, writer, turnAtFork, turn);
|
|
1230
|
-
|
|
1231
|
-
const stats = proxy.stats();
|
|
1232
|
-
await writer.append({
|
|
1233
|
-
type: 'run.end',
|
|
1234
|
-
actor: 'agenticreplay',
|
|
1235
|
-
turn,
|
|
1236
|
-
attrs: { exit_code: exitCode },
|
|
1237
|
-
});
|
|
1238
|
-
const manifest = await writer.close(exitCode);
|
|
1239
|
-
await proxy.close();
|
|
1240
|
-
await tls.ca?.dispose();
|
|
1241
|
-
|
|
1242
|
-
out.phase('fork.done', {
|
|
1243
|
-
run: writer.runId,
|
|
1244
|
-
replayed: forkAt,
|
|
1245
|
-
live: stats.liveCalls,
|
|
1246
|
-
divergences: stats.divergences,
|
|
1247
|
-
// A fork keeps replaying retrieval past its own cursor, so this says how much of the parent's
|
|
1248
|
-
// index work it reused rather than paid for again. `live` counts chat turns; these are not.
|
|
1249
|
-
...(stats.retrievalTotal > 0 || stats.retrievalLive > 0
|
|
1250
|
-
? { retrieval: `${stats.retrievalServed}/${stats.retrievalTotal}` }
|
|
1251
|
-
: {}),
|
|
1252
|
-
events: manifest.counts?.events ?? writer.seq,
|
|
1253
|
-
exit: exitCode,
|
|
1254
|
-
});
|
|
1255
|
-
|
|
1256
|
-
return {
|
|
1257
|
-
runId: ctx.manifest.run_id,
|
|
1258
|
-
traceRunId: writer.runId,
|
|
1259
|
-
forkRunId: writer.runId,
|
|
1260
|
-
worktree,
|
|
1261
|
-
mode: 'fork',
|
|
1262
|
-
canonicalExact: stats.matchedExact,
|
|
1263
|
-
divergences: stats.divergences,
|
|
1264
|
-
unmatched: stats.unmatched,
|
|
1265
|
-
liveCalls: stats.liveCalls,
|
|
1266
|
-
exitCode,
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
/** Where an exact replay runs, and how to put the directory back afterwards. */
|
|
1271
|
-
interface Workspace {
|
|
1272
|
-
dir: string;
|
|
1273
|
-
/**
|
|
1274
|
-
* Put the working tree back. Idempotent, and called from two places for that reason: once at
|
|
1275
|
-
* the child's launch, where it belongs on the ordinary path, and once from a `finally` around
|
|
1276
|
-
* the whole restored span, which is what makes the guarantee hold for a throw that never
|
|
1277
|
-
* reaches the launch at all.
|
|
1278
|
-
*/
|
|
1279
|
-
release: () => Promise<void>;
|
|
1280
|
-
/**
|
|
1281
|
-
* Whether this directory holds the recording's state and will be put back afterwards.
|
|
1282
|
-
*
|
|
1283
|
-
* False for `--in-place`, for a run with no snapshot to restore, and for a replay invoked from
|
|
1284
|
-
* somewhere other than the directory the recording was made in. It gates the one thing a replay
|
|
1285
|
-
* does that is not reversible on its own: deleting an adapter's declared artifacts. AgenticReplay may
|
|
1286
|
-
* delete only what it took a copy of first — anywhere else that would be destroying the
|
|
1287
|
-
* operator's index to reproduce someone else's.
|
|
1288
|
-
*/
|
|
1289
|
-
restored: boolean;
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
const noRelease = async (): Promise<void> => {};
|
|
1293
|
-
|
|
1294
|
-
/**
|
|
1295
|
-
* Whether two paths name the same directory — asked of the filesystem, not of the strings.
|
|
1296
|
-
*
|
|
1297
|
-
* Windows gives a process its working directory as it was spelled, so one folder arrives as
|
|
1298
|
-
* `C:\proj` from PowerShell and `c:\proj` from cmd or an editor's terminal, or through a junction
|
|
1299
|
-
* under another name altogether. Compared as strings, the directory a run was recorded in read as
|
|
1300
|
-
* somewhere else, and that is not a cosmetic miss: "elsewhere" skips the safety copy, so the
|
|
1301
|
-
* replay ran over the working tree and kept what it wrote there. Measured from cmd: the recorded
|
|
1302
|
-
* edit replaced uncommitted work in the very directory the run was made in.
|
|
1303
|
-
*
|
|
1304
|
-
* `realpath` answers with the name on disk — drive letter, case and links resolved. A path that
|
|
1305
|
-
* does not exist here, such as a colleague's, keeps the comparison it always had.
|
|
1306
|
-
*/
|
|
1307
|
-
async function sameDirectory(a: string, b: string): Promise<boolean> {
|
|
1308
|
-
const onDisk = (p: string): Promise<string> => realpath(p).catch(() => resolve(p));
|
|
1309
|
-
return (await onDisk(a)) === (await onDisk(b));
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
/**
|
|
1313
|
-
* Prepare the filesystem an exact replay needs.
|
|
1314
|
-
*
|
|
1315
|
-
* Two facts decide this, and they pull against each other. A harness reads files into the
|
|
1316
|
-
* conversation, so the bytes on disk are inside the recorded request and replaying against a
|
|
1317
|
-
* directory the recording itself edited produces a different request. And a harness writes
|
|
1318
|
-
* *absolute* paths into its tool calls, so replaying a copy of that directory somewhere else makes
|
|
1319
|
-
* the agent read outside its working directory — where it gets a permission refusal in place of
|
|
1320
|
-
* the file, which diverges just as badly. Measured on a real Claude Code run: a scratch copy
|
|
1321
|
-
* halted at the first tool result; the same trace restored at its own path replayed all six
|
|
1322
|
-
* exchanges with nothing unmatched.
|
|
1323
|
-
*
|
|
1324
|
-
* So the default restores the recorded state *over the working tree*, at the path the run was
|
|
1325
|
-
* recorded in. That is only defensible because it is reversible: the current tree is snapshotted
|
|
1326
|
-
* into a scratch store first and put back in a `finally`, so a replay is observationally a no-op
|
|
1327
|
-
* on your checkout, and the snapshot id is printed before anything is touched in case the process
|
|
1328
|
-
* is killed in between.
|
|
1329
|
-
*
|
|
1330
|
-
* Three ways out. `--worktree` replays in a scratch copy and never touches your files, at the cost
|
|
1331
|
-
* of the divergence above. `--in-place` uses the tree exactly as it stands, restoring nothing.
|
|
1332
|
-
* And a replay invoked from somewhere other than the directory the run was recorded in never
|
|
1333
|
-
* restores, because writing a recorded tree over an unrelated directory is not a thing to do by
|
|
1334
|
-
* default.
|
|
1335
|
-
*/
|
|
1336
|
-
async function replayWorkspace(args: ParsedArgs, out: Output, ctx: Ctx): Promise<Workspace> {
|
|
1337
|
-
const artifacts = adapterArtifacts(ctx);
|
|
1338
|
-
// `--worktree` IS A PROMISE — "never touches your files" — so once it is asked for, nothing
|
|
1339
|
-
// below may fall back to the operator's directory. Two branches did, both ahead of the one that
|
|
1340
|
-
// reads it: `--in-place` given beside it won without a word, and a run with no snapshot took the
|
|
1341
|
-
// in-place warning and ran the agent over the working tree anyway. Measured on a run recorded
|
|
1342
|
-
// with --no-fs: `agenticreplay replay last --worktree` wrote the recorded edit over uncommitted work.
|
|
1343
|
-
const wantsWorktree = args.bool('worktree');
|
|
1344
|
-
if (wantsWorktree && args.bool('in-place')) {
|
|
1345
|
-
throw new Error(
|
|
1346
|
-
'--worktree and --in-place cannot both apply: one replays in a scratch copy and never ' +
|
|
1347
|
-
'touches your files, the other replays in them. Give one.',
|
|
1348
|
-
);
|
|
1349
|
-
}
|
|
1350
|
-
if (args.bool('in-place')) {
|
|
1351
|
-
warnArtifactsKept(artifacts, out);
|
|
1352
|
-
return { dir: ctx.cwd, release: noRelease, restored: false };
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
const initial = deriveCheckpoints(ctx.events).find((c) => c.fsTree !== undefined);
|
|
1356
|
-
if (!initial?.fsTree) {
|
|
1357
|
-
// Refused rather than run in an empty directory: an empty directory is not a copy of anything
|
|
1358
|
-
// the run started from, and a replay that halts at the first file it reads is no way to learn
|
|
1359
|
-
// that. `--in-place` is the honest version of what this used to do, and says what it does.
|
|
1360
|
-
if (wantsWorktree) {
|
|
1361
|
-
out.warn('replay.no_snapshot', {
|
|
1362
|
-
why: 'this run was recorded without filesystem capture, so it holds no workspace to copy',
|
|
1363
|
-
next: `agenticreplay replay ${ctx.manifest.run_id} --in-place # against this directory as it stands`,
|
|
1364
|
-
});
|
|
1365
|
-
throw new Error(
|
|
1366
|
-
'no workspace snapshot to copy — see replay.no_snapshot; --in-place replays without it',
|
|
1367
|
-
);
|
|
1368
|
-
}
|
|
1369
|
-
out.warn('replay.in-place', {
|
|
1370
|
-
why: 'this run has no filesystem snapshot to restore',
|
|
1371
|
-
note: 'recorded with --no-fs; a file the run read may since have changed',
|
|
1372
|
-
});
|
|
1373
|
-
warnArtifactsKept(artifacts, out);
|
|
1374
|
-
return { dir: ctx.cwd, release: noRelease, restored: false };
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
const recorded = await FsCapture.start({ runDir: ctx.runDir, cwd: ctx.cwd });
|
|
1378
|
-
|
|
1379
|
-
if (args.bool('worktree')) {
|
|
1380
|
-
// Both before `mkdtemp`, because either can refuse, and a refusal after it left an empty
|
|
1381
|
-
// `agenticreplay-replay-*` directory in `$TMPDIR` on every attempt: nothing had printed its path yet,
|
|
1382
|
-
// and `agenticreplay gc` never reclaims an exact replay's worktree.
|
|
1383
|
-
await assertSnapshotHeld(out, ctx, recorded, initial.fsTree);
|
|
1384
|
-
await keepNested(out, recorded, initial.fsTree, 'a fresh worktree');
|
|
1385
|
-
// A fresh directory, so there is nothing for a reset to remove: the restore below is the only
|
|
1386
|
-
// thing that puts anything here, and what it puts here is the recording's starting state.
|
|
1387
|
-
const worktree = await mkdtemp(join(tmpdir(), `agenticreplay-replay-${ctx.manifest.run_id}-`));
|
|
1388
|
-
await recorded.restore(initial.fsTree, worktree, { allowIncomplete: true });
|
|
1389
|
-
return { dir: worktree, release: noRelease, restored: true };
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
if (!(await sameDirectory(ctx.cwd, ctx.manifest.cwd))) {
|
|
1393
|
-
out.warn('replay.elsewhere', {
|
|
1394
|
-
recorded_in: ctx.manifest.cwd,
|
|
1395
|
-
running_in: ctx.cwd,
|
|
1396
|
-
note: 'not restoring over a directory the run was not recorded in; use --worktree for a copy',
|
|
1397
|
-
});
|
|
1398
|
-
warnArtifactsKept(artifacts, out);
|
|
1399
|
-
return { dir: ctx.cwd, release: noRelease, restored: false };
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
// BEFORE THE COPY, NOT INSIDE THE TRY BELOW — both of these.
|
|
1403
|
-
//
|
|
1404
|
-
// They refuse without writing a byte, so they have to run while there is still nothing to undo.
|
|
1405
|
-
// Refusing after the safety copy existed meant the catch called `release`, whose `materialize`
|
|
1406
|
-
// refused for the same reason the guard just did — the operator's own tree holds the same
|
|
1407
|
-
// nested repositories — so a replay that had touched nothing printed "your files are in that
|
|
1408
|
-
// store and were not put back" and left a copy of the whole workspace in a temp directory.
|
|
1409
|
-
await assertSnapshotHeld(out, ctx, recorded, initial.fsTree);
|
|
1410
|
-
const keptNested = await assertRestorable(recorded, initial.fsTree, ctx.cwd, artifacts);
|
|
1411
|
-
|
|
1412
|
-
// A store of its own, under the OS temp dir: the safety snapshot is scratch, and writing it into
|
|
1413
|
-
// the trace's shadow store would leave an object in a recorded run that nothing references.
|
|
1414
|
-
const scratch = await mkdtemp(join(tmpdir(), 'agenticreplay-safety-'));
|
|
1415
|
-
// With the adapter's declared artifacts, for the reason the safety snapshot exists at all: the
|
|
1416
|
-
// replay is about to delete those paths (see `resetArtifacts`), and a snapshot that skipped
|
|
1417
|
-
// them because the workspace ignores them would make that deletion permanent. AgenticReplay may delete
|
|
1418
|
-
// only what it has a copy of.
|
|
1419
|
-
const forced = [
|
|
1420
|
-
...new Set([...(artifacts?.capture ?? []), ...(artifacts?.resetBeforeReplay ?? [])]),
|
|
1421
|
-
];
|
|
1422
|
-
const safety = await FsCapture.start({
|
|
1423
|
-
runDir: scratch,
|
|
1424
|
-
cwd: ctx.cwd,
|
|
1425
|
-
...(forced.length === 0 ? {} : { forced }),
|
|
1426
|
-
});
|
|
1427
|
-
const before = await safety.snapshotTurn(0);
|
|
1428
|
-
|
|
1429
|
-
// BOTH TREES, because the gap belongs to whichever of them holds a gitlink.
|
|
1430
|
-
//
|
|
1431
|
-
// `keptNested` is what the *recording* cannot write. What `release` below actually skips is
|
|
1432
|
-
// every gitlink in the *copy of the operator's own tree*, and those are not the same set: a
|
|
1433
|
-
// repository cloned into the workspace since the recording was made is in the second and not
|
|
1434
|
-
// the first. Reporting only the first said nothing about it and then promised, unqualified,
|
|
1435
|
-
// that the files come back — while the replayed agent's writes inside it stayed.
|
|
1436
|
-
const keptHere = await safety.gitlinks(before.tree);
|
|
1437
|
-
// And the ones no snapshot could hold at all. A nested repository with no commit is not a
|
|
1438
|
-
// gitlink — git has no id to record it by — so neither `gitlinks` call can see it, and before
|
|
1439
|
-
// this the safety snapshot did not merely miss it: it threw, and `agenticreplay replay` exited on git's
|
|
1440
|
-
// own sentence about a commit checked out with an `agenticreplay-safety-*` directory left behind. It is
|
|
1441
|
-
// skipped now, which makes it exactly the case these lines are about: agenticreplay holds no copy of
|
|
1442
|
-
// what is inside it.
|
|
1443
|
-
//
|
|
1444
|
-
// And the repositories around a declared artifact path, which the forced add set aside for the
|
|
1445
|
-
// same reason: git stages nothing inside another repository, so neither snapshot holds them.
|
|
1446
|
-
const kept = [
|
|
1447
|
-
...new Set([
|
|
1448
|
-
...keptNested,
|
|
1449
|
-
...keptHere,
|
|
1450
|
-
...safety.uncommittedNested,
|
|
1451
|
-
...safety.forcedInsideNested.map((entry) => entry.repository),
|
|
1452
|
-
]),
|
|
1453
|
-
].sort();
|
|
1454
|
-
|
|
1455
|
-
// Not "they are left exactly as they are", which is only true of the restore. The replay runs
|
|
1456
|
-
// the recorded agent live in this directory, and agenticreplay does not intercept what it does — so a
|
|
1457
|
-
// recorded build step, generator or `rm` reaches inside these repositories like any other
|
|
1458
|
-
// path, and neither snapshot holds anything of theirs. Said before the agent starts.
|
|
1459
|
-
keepNestedNote(out, kept, {
|
|
1460
|
-
effect:
|
|
1461
|
-
'agenticreplay holds no copy of what is inside them, so whatever this replay does there stays',
|
|
1462
|
-
next: 'replay with --worktree to run the agent somewhere agenticreplay can put back',
|
|
1463
|
-
});
|
|
1464
|
-
|
|
1465
|
-
out.info('replay.restored', {
|
|
1466
|
-
to: initial.fsTree,
|
|
1467
|
-
your_tree: before.tree,
|
|
1468
|
-
// The unqualified sentence is the one the operator acts on, so where it is not true of the
|
|
1469
|
-
// whole tree it does not get to be said of the whole tree.
|
|
1470
|
-
note:
|
|
1471
|
-
kept.length === 0
|
|
1472
|
-
? 'your files are restored when the replay ends'
|
|
1473
|
-
: `your files are restored when the replay ends, except inside ${kept.join(', ')}`,
|
|
1474
|
-
});
|
|
1475
|
-
|
|
1476
|
-
// Two callers, on purpose — see `Workspace.release`. Restoring twice would be wrong rather than
|
|
1477
|
-
// merely wasteful: the second pass would write the pre-replay tree back over whatever the
|
|
1478
|
-
// caller has done since, and the `rm` would take the scratch with it.
|
|
1479
|
-
let released = false;
|
|
1480
|
-
// Filled in below, before anything is written — see `assertOverwritable`.
|
|
1481
|
-
let introduced: Introduced = { files: [], dirs: [], walked: new Map() };
|
|
1482
|
-
const release = async (): Promise<void> => {
|
|
1483
|
-
if (released) return;
|
|
1484
|
-
released = true;
|
|
1485
|
-
try {
|
|
1486
|
-
// `allowIncomplete`, always — and the gap it tolerates is real, not notional. The nested
|
|
1487
|
-
// repositories this copy holds only as gitlinks are the one part of the tree the replay
|
|
1488
|
-
// can change and this cannot put back, which is why `replay.nested_kept` says so before
|
|
1489
|
-
// the agent starts rather than leaving it to be discovered here.
|
|
1490
|
-
//
|
|
1491
|
-
// Refusing at this point recovers none of that and abandons everything the copy *does*
|
|
1492
|
-
// hold: it left the working tree in the replay's state and the operator's own files in a
|
|
1493
|
-
// scratch directory, which is the one outcome a safety copy exists to prevent.
|
|
1494
|
-
await safety.restore(before.tree, ctx.cwd, { allowIncomplete: true });
|
|
1495
|
-
} catch (err) {
|
|
1496
|
-
// Say where the copy is before rethrowing. This is the one failure after which the scratch
|
|
1497
|
-
// store holds the only copy of the operator's working tree, and until now the path it is at
|
|
1498
|
-
// was printed nowhere — the directory was kept, deliberately, and nothing said so.
|
|
1499
|
-
out.warn('replay.restore_failed', {
|
|
1500
|
-
your_tree: before.tree,
|
|
1501
|
-
copy_at: scratch,
|
|
1502
|
-
note: 'your files are in that store and were not put back; it is not deleted',
|
|
1503
|
-
});
|
|
1504
|
-
throw err;
|
|
1505
|
-
}
|
|
1506
|
-
// What the recording wrote where there was nothing, taken away again — after the put-back, and
|
|
1507
|
-
// only once it has succeeded: the copy never held these, so the put-back alone left them.
|
|
1508
|
-
await removeIntroduced(ctx.cwd, introduced, out);
|
|
1509
|
-
// Only after the restore succeeded. This store holds the only copy of your working tree as
|
|
1510
|
-
// it was before the replay overwrote it, so removing it on the failure path would delete the
|
|
1511
|
-
// thing the failure means you still need — better a directory to clean up by hand than the
|
|
1512
|
-
// one that had your uncommitted work in it.
|
|
1513
|
-
//
|
|
1514
|
-
// Left behind on every run until now: a whole workspace per `agenticreplay replay`, in a directory
|
|
1515
|
-
// `agenticreplay gc` deliberately will not touch because it only reclaims scratch worktrees belonging
|
|
1516
|
-
// to forks. Owning its lifetime here is the fix; teaching gc to delete unknown temp
|
|
1517
|
-
// directories is how gc ends up removing someone's work.
|
|
1518
|
-
await rm(scratch, { recursive: true, force: true });
|
|
1519
|
-
};
|
|
1520
|
-
|
|
1521
|
-
// The destructive step, inside the thing that undoes it.
|
|
1522
|
-
//
|
|
1523
|
-
// A caller cannot guard this one: it happens here, and the `release` that undoes it does not
|
|
1524
|
-
// exist outside this function until this function returns. So a `materialize` that failed
|
|
1525
|
-
// part-way — it is `read-tree` + `checkout-index -a -f` with no rollback of its own, and a
|
|
1526
|
-
// path conflict, EACCES or ENOSPC stops it mid-tree — left the operator holding a mixture of
|
|
1527
|
-
// their files and the recording's, with the only copy of the original in a scratch directory
|
|
1528
|
-
// whose path nothing prints, and no release attempted anywhere.
|
|
1529
|
-
//
|
|
1530
|
-
// With this, the function has one postcondition either way: it returns with the tree replaced
|
|
1531
|
-
// and a `release` that puts it back, or it throws with the tree as it found it. That is what
|
|
1532
|
-
// lets the caller arm its own guard on the line after the call rather than before it.
|
|
1533
|
-
try {
|
|
1534
|
-
// `assertRestorable` ran before the copy above — it is the half that can refuse without
|
|
1535
|
-
// having done anything. This is the other direction: whatever the reset is about to delete
|
|
1536
|
-
// has to be in the copy taken a moment ago, not merely in the recording. It needs the copy,
|
|
1537
|
-
// so it runs here, and its failure is what `release` is for.
|
|
1538
|
-
await assertResettable(safety, ctx.cwd, artifacts);
|
|
1539
|
-
// And the same rule for what the restore overwrites rather than deletes, and for what the
|
|
1540
|
-
// recorded agent writes after it — every tree the run recorded, not only the first. Before
|
|
1541
|
-
// the reset, which is the first thing here that changes the working tree.
|
|
1542
|
-
const trees = deriveCheckpoints(ctx.events).flatMap((c) => (c.fsTree ? [c.fsTree] : []));
|
|
1543
|
-
introduced = await assertOverwritable(recorded, trees, safety, before.tree, ctx.cwd);
|
|
1544
|
-
|
|
1545
|
-
// Before the restore, not after — and that ordering is the whole definition of the reset.
|
|
1546
|
-
// `materialize` writes the tree's files and leaves anything else where it is, so a cache the
|
|
1547
|
-
// recording never had would survive it and the harness would resume from work the recording
|
|
1548
|
-
// did not do. Clearing first and restoring second leaves these paths holding *exactly* what
|
|
1549
|
-
// they held when the recording started, which is the only state the recording can answer for
|
|
1550
|
-
// — including the case where that state was a populated cache, as it is for a recording of
|
|
1551
|
-
// one mid-pipeline stage. Doing it the other way round deleted that stage's own input.
|
|
1552
|
-
await resetArtifacts(ctx.cwd, artifacts, out);
|
|
1553
|
-
// `keptNested`, not `kept`: this writes the *recording's* tree, so the only gaps it can meet
|
|
1554
|
-
// are that tree's own gitlinks. A repository the operator added since is not in it to skip.
|
|
1555
|
-
//
|
|
1556
|
-
// Tolerated only because the guard above has already decided these repositories are not in
|
|
1557
|
-
// anything this replay deletes; where they were, it threw instead of reaching here.
|
|
1558
|
-
await recorded.restore(initial.fsTree, ctx.cwd, {
|
|
1559
|
-
allowIncomplete: keptNested.length > 0,
|
|
1560
|
-
});
|
|
1561
|
-
} catch (err) {
|
|
1562
|
-
// The original failure is the one to report. `release` can fail for its own reasons, and
|
|
1563
|
-
// letting that replace this one told the operator about the cleanup and never about the cause.
|
|
1564
|
-
try {
|
|
1565
|
-
await release();
|
|
1566
|
-
} catch {
|
|
1567
|
-
// `release` has already warned, with the path to the copy it kept.
|
|
1568
|
-
}
|
|
1569
|
-
throw err;
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
return { dir: ctx.cwd, release, restored: true };
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
/**
|
|
1576
|
-
* Why a run's events can name workspace snapshots its own store does not hold.
|
|
1577
|
-
*
|
|
1578
|
-
* Two ways, and nothing left on disk says which: `agenticreplay pull` of a run pushed without `--fs` — the
|
|
1579
|
-
* default, because push will not ship what scrub cannot reach — and `agenticreplay scrub --drop-fs`, which
|
|
1580
|
-
* deletes the store and keeps the `fs.snapshot` events that name it. This used to name only the
|
|
1581
|
-
* first, and as though it were unconditional ("a gateway archive … never the filesystem
|
|
1582
|
-
* snapshots"), which blamed a gateway for a run that had never been near one and hid the way to
|
|
1583
|
-
* get the snapshots: a copy pushed with `--fs` restores and replays exactly.
|
|
1584
|
-
*/
|
|
1585
|
-
const NO_SNAPSHOT_WHY =
|
|
1586
|
-
"the run's workspace snapshots are not in its store: a pulled run carries them only if it was pushed with --fs, and agenticreplay scrub --drop-fs deletes them";
|
|
1587
|
-
|
|
1588
|
-
/**
|
|
1589
|
-
* Refuse a restore the run's own store cannot serve, before anything is written.
|
|
1590
|
-
*
|
|
1591
|
-
* Asked up front for the reason {@link assertRestorable} is: otherwise the restore dies inside
|
|
1592
|
-
* git, "failed to unpack tree object <40 hex>", which is true and names neither the cause nor
|
|
1593
|
-
* anything to do about it — the one diagnosis the fork path already gave (`fork.no_snapshot`).
|
|
1594
|
-
*
|
|
1595
|
-
* Refused, not degraded to `--in-place` the way a run recorded with `--no-fs` is. Both callers
|
|
1596
|
-
* were asked for the recorded tree — a scratch copy of it, or it put over the checkout and taken
|
|
1597
|
-
* back afterwards — and running the agent over the tree as it stands instead keeps whatever the
|
|
1598
|
-
* replay writes, which neither of those does. `--in-place` is one flag away, and says so.
|
|
1599
|
-
*/
|
|
1600
|
-
async function assertSnapshotHeld(
|
|
1601
|
-
out: Output,
|
|
1602
|
-
ctx: Ctx,
|
|
1603
|
-
capture: FsCapture,
|
|
1604
|
-
tree: string,
|
|
1605
|
-
): Promise<void> {
|
|
1606
|
-
if (await capture.hasTree(tree)) return;
|
|
1607
|
-
out.warn('replay.no_snapshot', {
|
|
1608
|
-
tree,
|
|
1609
|
-
store: join(ctx.runDir, 'fs'),
|
|
1610
|
-
why: NO_SNAPSHOT_WHY,
|
|
1611
|
-
next: `agenticreplay replay ${ctx.manifest.run_id} --in-place # against this directory as it stands`,
|
|
1612
|
-
});
|
|
1613
|
-
throw new Error(
|
|
1614
|
-
'no workspace snapshot to restore — see replay.no_snapshot; --in-place replays without it',
|
|
1615
|
-
);
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
/**
|
|
1619
|
-
* Refuse before deleting, rather than from inside the restore that was meant to undo the deletion.
|
|
1620
|
-
*
|
|
1621
|
-
* Asked of the tree rather than attempted on it: this runs while the operator's artifacts are
|
|
1622
|
-
* still on disk, so it has to be free of side effects.
|
|
1623
|
-
*
|
|
1624
|
-
* **Only the repositories this replay would destroy.** A snapshot records a nested repository as a
|
|
1625
|
-
* gitlink and never holds its contents, so a restore cannot put one back — and where the replay
|
|
1626
|
-
* deletes the path first, that is the whole of the harm the guard exists for: the reset empties
|
|
1627
|
-
* `cache/`, the restore cannot refill it, and a corpus is gone. Where the replay deletes nothing,
|
|
1628
|
-
* there is nothing to put back: `materialize` writes the tree's files and leaves everything else
|
|
1629
|
-
* alone, so the nested repository is still sitting there when the restore finishes, untouched.
|
|
1630
|
-
*
|
|
1631
|
-
* Refusing that case turned every recording made in a workspace with a submodule or a vendored
|
|
1632
|
-
* checkout into one that could not be replayed at all — and `claude`, like every adapter but
|
|
1633
|
-
* `indexrag`, declares no reset paths, so that is every such recording it makes. Issue #103.
|
|
1634
|
-
*
|
|
1635
|
-
* Returns the ones being left as they are, so the caller can say so and can tell `materialize`
|
|
1636
|
-
* that their absence is expected rather than a gap it should refuse over.
|
|
1637
|
-
*
|
|
1638
|
-
* Exported for the test that pins which of the two it does, for the same reason {@link resetRoots}
|
|
1639
|
-
* is: deciding wrongly here costs the operator either a corpus or every replay in the workspace.
|
|
1640
|
-
*/
|
|
1641
|
-
export async function assertRestorable(
|
|
1642
|
-
capture: FsCapture,
|
|
1643
|
-
tree: string,
|
|
1644
|
-
dir: string,
|
|
1645
|
-
artifacts: HarnessArtifacts | undefined,
|
|
1646
|
-
): Promise<string[]> {
|
|
1647
|
-
const gitlinks = await capture.gitlinks(tree);
|
|
1648
|
-
if (gitlinks.length === 0) return [];
|
|
1649
|
-
const roots = resetRoots(dir, artifacts);
|
|
1650
|
-
// Containment in BOTH directions, because the reset is destructive either way — see
|
|
1651
|
-
// `reachedByReset`, which `assertResettable` asks the same question of for a repository with no
|
|
1652
|
-
// commit, since neither this guard nor `uncaptured` can see one.
|
|
1653
|
-
const doomed = gitlinks.filter((path) => reachedByReset(path, roots));
|
|
1654
|
-
if (doomed.length === 0) return gitlinks;
|
|
1655
|
-
throw new Error(
|
|
1656
|
-
`this replay would delete ${roots.join(', ')} to put the harness back where the recording ` +
|
|
1657
|
-
`started, and cannot put ${doomed.join(', ')} back: the snapshot holds those as embedded ` +
|
|
1658
|
-
'git repositories, whose contents it never captured — whether the reset removes one ' +
|
|
1659
|
-
'outright or reaches inside it. Nothing has been deleted. Move them ' +
|
|
1660
|
-
'outside the adapter’s reset paths, or replay with --in-place to leave the working tree ' +
|
|
1661
|
-
'alone.',
|
|
1662
|
-
);
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
/**
|
|
1666
|
-
* Compare paths the way the filesystem underneath them does.
|
|
1667
|
-
*
|
|
1668
|
-
* `rm -rf <dir>/Cache` removes `cache` on Windows, and git treats the two as one path there,
|
|
1669
|
-
* so a case-sensitive prefix test calls a corpus safe and then deletes it. Folded on the
|
|
1670
|
-
* platforms whose filesystems are case-insensitive by default; erring that way costs at worst
|
|
1671
|
-
* a replay refused on a case-sensitive APFS volume, and erring the other way costs the corpus.
|
|
1672
|
-
*/
|
|
1673
|
-
function fold(path: string): string {
|
|
1674
|
-
return process.platform === 'win32' || process.platform === 'darwin' ? path.toLowerCase() : path;
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
/**
|
|
1678
|
-
* Whether resetting `roots` reaches `path`, in either direction.
|
|
1679
|
-
*
|
|
1680
|
-
* `path` inside a root is the obvious half: the thing sits in a directory being deleted.
|
|
1681
|
-
* A root inside `path` is the half that reads backwards and matters just as much — a multi-segment
|
|
1682
|
-
* declaration such as `resetBeforeReplay: ['data/cache']` with a nested repository at `data`
|
|
1683
|
-
* deletes a path *inside* the operator's own repository.
|
|
1684
|
-
*
|
|
1685
|
-
* Whole segments, so `data2` is not read as being inside `data`; folded where the filesystem folds,
|
|
1686
|
-
* so `Data` is not read as being somewhere else. Shared by the two guards that have to agree about
|
|
1687
|
-
* it — {@link assertRestorable} for a repository the snapshot holds as a gitlink, and
|
|
1688
|
-
* {@link assertResettable} for one it could not hold at all.
|
|
1689
|
-
*/
|
|
1690
|
-
function reachedByReset(path: string, roots: readonly string[]): boolean {
|
|
1691
|
-
const one = fold(path);
|
|
1692
|
-
return roots.some((root) => {
|
|
1693
|
-
const other = fold(root);
|
|
1694
|
-
return one === other || one.startsWith(`${other}/`) || other.startsWith(`${one}/`);
|
|
1695
|
-
});
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
/**
|
|
1699
|
-
* Say which nested repositories a restore cannot write, and what that costs here.
|
|
1700
|
-
*
|
|
1701
|
-
* `effect` differs by caller because the consequence does: a fork writes into a directory of its
|
|
1702
|
-
* own, where the repository is simply missing and the operator's copy is never in reach. An
|
|
1703
|
-
* in-place replay runs the agent over the operator's own tree, where it is very much in reach and
|
|
1704
|
-
* agenticreplay cannot put it back.
|
|
1705
|
-
*/
|
|
1706
|
-
function keepNestedNote(
|
|
1707
|
-
out: Output,
|
|
1708
|
-
paths: readonly string[],
|
|
1709
|
-
fields: { effect: string; next?: string },
|
|
1710
|
-
): void {
|
|
1711
|
-
if (paths.length === 0) return;
|
|
1712
|
-
out.warn('replay.nested_kept', {
|
|
1713
|
-
paths: paths.join(','),
|
|
1714
|
-
why: 'these are git repositories of their own, and the snapshot never held their contents — recorded as a reference, or, with no commit to reference, not at all',
|
|
1715
|
-
...fields,
|
|
1716
|
-
});
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
/** The same note, for the restores that write into a directory of their own. */
|
|
1720
|
-
async function keepNested(
|
|
1721
|
-
out: Output,
|
|
1722
|
-
capture: FsCapture,
|
|
1723
|
-
tree: string,
|
|
1724
|
-
where: string,
|
|
1725
|
-
): Promise<void> {
|
|
1726
|
-
keepNestedNote(out, await capture.gitlinks(tree), {
|
|
1727
|
-
effect: `they are absent from ${where}`,
|
|
1728
|
-
});
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
|
-
/** What the adapter that made this recording says about its own artifacts, if anything. */
|
|
1732
|
-
function adapterArtifacts(ctx: {
|
|
1733
|
-
manifest: { adapter: { id: string } };
|
|
1734
|
-
}): HarnessArtifacts | undefined {
|
|
1735
|
-
try {
|
|
1736
|
-
return defaultAdapters().get(ctx.manifest.adapter.id).artifacts;
|
|
1737
|
-
} catch {
|
|
1738
|
-
// A trace naming an adapter this build does not have. The replay fails on that later, with a
|
|
1739
|
-
// better message than one from here; nothing about the workspace should depend on it.
|
|
1740
|
-
return undefined;
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
/**
|
|
1745
|
-
* Delete the artifacts the harness resumes from, so the replay starts where the recording did.
|
|
1746
|
-
*
|
|
1747
|
-
* A pipeline picks up where it left off: it reads its own cache, sees which items are finished
|
|
1748
|
-
* and does the rest. Replayed on top of the recording's *completed* cache it does nothing — no
|
|
1749
|
-
* request is made, nothing is matched, and the replay reports having reproduced a run it never
|
|
1750
|
-
* ran. `reused=0/6 exit=0` is what that looks like, and it reads as a broken recording.
|
|
1751
|
-
*
|
|
1752
|
-
* Whole paths, never "the parts that look invalid". IndexRAG's resume skips by `chunk_id`
|
|
1753
|
-
* including entries whose recorded outcome was a failure, so keeping what looks valid promotes
|
|
1754
|
-
* the previous run's failures to completed work — a subtler wrong answer than the one being
|
|
1755
|
-
* fixed. The workspace is restored afterwards, which is what makes deleting acceptable at all,
|
|
1756
|
-
* and `workspace.restored` is the gate: agenticreplay deletes only where it took a copy first.
|
|
1757
|
-
*/
|
|
1758
|
-
async function resetArtifacts(
|
|
1759
|
-
dir: string,
|
|
1760
|
-
artifacts: HarnessArtifacts | undefined,
|
|
1761
|
-
out: Output,
|
|
1762
|
-
): Promise<void> {
|
|
1763
|
-
const removed: string[] = [];
|
|
1764
|
-
for (const root of resetRoots(dir, artifacts)) {
|
|
1765
|
-
await rm(resolve(dir, root), { recursive: true, force: true });
|
|
1766
|
-
removed.push(root);
|
|
1767
|
-
}
|
|
1768
|
-
if (removed.length > 0) {
|
|
1769
|
-
out.info('replay.artifacts_reset', {
|
|
1770
|
-
paths: [...new Set(removed)].join(','),
|
|
1771
|
-
why: 'the harness resumes from these, so they are put back to the recording’s starting state',
|
|
1772
|
-
note: 'your files are restored when the replay ends',
|
|
1773
|
-
});
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
/**
|
|
1778
|
-
* The paths {@link resetArtifacts} will actually remove.
|
|
1779
|
-
*
|
|
1780
|
-
* The declared path itself, with a trailing glob stripped — `cache/**` is a declaration about
|
|
1781
|
-
* `cache`, and a glob suffix is the only part of a pattern this can act on without a glob library.
|
|
1782
|
-
*
|
|
1783
|
-
* Not the pattern's *first segment*, which is what this took before. Nothing constrains a
|
|
1784
|
-
* declaration to one segment — `resetBeforeReplay` is `readonly string[]` and the plugin docs call
|
|
1785
|
-
* it "whole paths" — so an adapter declaring `data/cache` had `rm -rf <cwd>/data` run against the
|
|
1786
|
-
* operator's working directory. That deletion is wider than the copy meant to reverse it: only
|
|
1787
|
-
* `data/cache` is force-staged, and the rest of `data/` is in the safety tree only if the workspace
|
|
1788
|
-
* does not ignore it — and a workspace that ignores its pipeline's product directory is the entire
|
|
1789
|
-
* reason `artifacts.capture` exists. `data/raw-inputs/` would have been deleted permanently, under
|
|
1790
|
-
* a message saying the files come back when the replay ends.
|
|
1791
|
-
*
|
|
1792
|
-
* Shared with the check that runs before the removal, so the two cannot disagree about what is
|
|
1793
|
-
* about to be deleted — a guard looking at a different set from the `rm` would be worse than none.
|
|
1794
|
-
*/
|
|
1795
|
-
export function resetRoots(dir: string, artifacts: HarnessArtifacts | undefined): string[] {
|
|
1796
|
-
const roots: string[] = [];
|
|
1797
|
-
for (const pattern of artifacts?.resetBeforeReplay ?? []) {
|
|
1798
|
-
const declared = pattern.replace(/[\\/]+\*{1,2}$/, '').replace(/[\\/]+$/, '');
|
|
1799
|
-
if (declared === '' || declared === '.' || declared === '..' || declared.includes('*'))
|
|
1800
|
-
continue;
|
|
1801
|
-
// Never outside the workspace, whatever an adapter declares.
|
|
1802
|
-
const resolved = resolve(dir, declared);
|
|
1803
|
-
if (!isInsideDir(dir, resolved)) continue;
|
|
1804
|
-
// Through `resolve` and back, so what leaves here is the path the deletion will act on
|
|
1805
|
-
// rather than the spelling the adapter happened to use. `./cache`, `data//cache` and
|
|
1806
|
-
// `tools/../cache` all reach `rm` as one path and have to leave here as one too: the
|
|
1807
|
-
// guard below compares these strings, and a guard looking at a different set from the
|
|
1808
|
-
// `rm` is worse than none. Forward slashes, since this is also a `git ls-files` pathspec.
|
|
1809
|
-
const root = relative(dir, resolved).split(/[\\/]/).join('/');
|
|
1810
|
-
if (root === '') continue;
|
|
1811
|
-
roots.push(root);
|
|
1812
|
-
}
|
|
1813
|
-
return [...new Set(roots)];
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
|
-
/** What a restore will write where the working tree holds nothing, for the put-back to remove. */
|
|
1817
|
-
export interface Introduced {
|
|
1818
|
-
/** Each with the ids of every version of it the recording holds, to know it again by. */
|
|
1819
|
-
files: { path: string; oids: ReadonlySet<string> }[];
|
|
1820
|
-
/** Directories it has to create for them, deepest first, so each is empty when its turn comes. */
|
|
1821
|
-
dirs: string[];
|
|
1822
|
-
/**
|
|
1823
|
-
* The links on the way to these files that git walked into as directories when it took the copy
|
|
1824
|
-
* — a junction, on Windows — and that were links then, each with where it pointed. The removal
|
|
1825
|
-
* may pass through real directories and through these, while each still points there, and
|
|
1826
|
-
* through nothing else.
|
|
1827
|
-
*/
|
|
1828
|
-
walked: ReadonlyMap<string, string>;
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
/**
|
|
1832
|
-
* Refuse the reset unless the safety copy holds everything the reset is about to delete.
|
|
1833
|
-
*
|
|
1834
|
-
* `assertRestorable` asks whether the *recording's* tree can be written back. This asks the other
|
|
1835
|
-
* half, and it is the half that owns the operator's files: `resetArtifacts` deletes whole
|
|
1836
|
-
* directories, and the only thing that can put back what was in them and not in the recording is
|
|
1837
|
-
* the safety snapshot taken moments earlier. That snapshot is not unconditionally complete. The
|
|
1838
|
-
* sensitive pathspecs are applied to its forced add as well — rightly; they are what stops an
|
|
1839
|
-
* adapter sweeping a credential into a trace — and a nested repository inside a declared path is
|
|
1840
|
-
* dropped because no snapshot can hold one. Both are deliberate, and both mean the copy is missing
|
|
1841
|
-
* something the `rm` would take: an `.env` the operator keeps beside a cache, a prebuilt store
|
|
1842
|
-
* cloned into `vector_store/`. Neither need appear in the recording's tree at all, since the
|
|
1843
|
-
* operator may have put it there afterwards, so the other guard passes and the loss is permanent
|
|
1844
|
-
* and silent.
|
|
1845
|
-
*
|
|
1846
|
-
* Refusing rather than warning, because there is no second chance after the `rm`, and the rule this
|
|
1847
|
-
* replay is operating under says so in as many words: agenticreplay deletes only what it took a copy of
|
|
1848
|
-
* first. In the ordinary run nothing under these paths is excluded and this asks git one question
|
|
1849
|
-
* and moves on.
|
|
1850
|
-
*/
|
|
1851
|
-
export async function assertResettable(
|
|
1852
|
-
safety: FsCapture,
|
|
1853
|
-
dir: string,
|
|
1854
|
-
artifacts: HarnessArtifacts | undefined,
|
|
1855
|
-
): Promise<void> {
|
|
1856
|
-
const roots = resetRoots(dir, artifacts);
|
|
1857
|
-
if (roots.length === 0) return;
|
|
1858
|
-
|
|
1859
|
-
// THE SAME RULE, FOR THE NESTED REPOSITORIES NO SNAPSHOT NAMES AS GITLINKS.
|
|
1860
|
-
//
|
|
1861
|
-
// `assertRestorable` asks this of the recording's gitlinks, before anything is copied. Two kinds
|
|
1862
|
-
// of repository are in no tree, so that guard cannot see them — and the `uncaptured` question
|
|
1863
|
-
// below cannot either: `ls-files --others -- data/cache` does not descend into an embedded
|
|
1864
|
-
// repository, so a reset root INSIDE one comes back empty (measured: empty for `data/cache`,
|
|
1865
|
-
// `data/` for `data`). So the direction the other guard needs `root.startsWith(path)` for is the
|
|
1866
|
-
// one that gets through here, for both:
|
|
1867
|
-
//
|
|
1868
|
-
// - one with no commit, which git cannot record at all (`uncommittedNested`)
|
|
1869
|
-
// - one around a declared path, which the forced add sets aside rather than hand to git
|
|
1870
|
-
// (`forcedInsideNested`). Measured before it did: with `data/` ignored, `add -f -- data/cache`
|
|
1871
|
-
// exited 0 having staged nothing, and the reset then deleted the operator's `data/cache`
|
|
1872
|
-
// under exit 0 and "your files are restored when the replay ends" — committed or not.
|
|
1873
|
-
//
|
|
1874
|
-
// It has to be asked HERE because this is the first thing holding a snapshot of the operator's
|
|
1875
|
-
// own tree, which is the only place either kind is ever named.
|
|
1876
|
-
const unheld = [
|
|
1877
|
-
...safety.uncommittedNested,
|
|
1878
|
-
...safety.forcedInsideNested.map((entry) => entry.repository),
|
|
1879
|
-
];
|
|
1880
|
-
const reached = [...new Set(unheld)].filter((path) => reachedByReset(path, roots)).sort();
|
|
1881
|
-
if (reached.length > 0) {
|
|
1882
|
-
// Not "commit inside them", which this said before and which never helped: a committed
|
|
1883
|
-
// repository is a gitlink, whose contents no snapshot holds either, so the same reset is
|
|
1884
|
-
// refused the same way.
|
|
1885
|
-
throw new Error(
|
|
1886
|
-
`this replay would delete ${roots.join(', ')} to put the harness back where the recording ` +
|
|
1887
|
-
`started, and cannot put ${reached.join(', ')} back: those are git repositories of their ` +
|
|
1888
|
-
'own, and no snapshot holds a byte of what is inside them — whether the reset removes one ' +
|
|
1889
|
-
'outright or reaches inside it. Nothing has been deleted. Move those repositories clear of ' +
|
|
1890
|
-
'the adapter’s reset paths, or replay with --in-place to leave the working tree alone.',
|
|
1891
|
-
);
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
const missing = await safety.uncaptured(roots);
|
|
1895
|
-
if (missing.length === 0) return;
|
|
1896
|
-
const shown = missing.slice(0, 5).join(', ');
|
|
1897
|
-
const rest = missing.length > 5 ? `, and ${missing.length - 5} more` : '';
|
|
1898
|
-
throw new Error(
|
|
1899
|
-
`this replay would delete ${roots.join(', ')} to put the harness back where the recording ` +
|
|
1900
|
-
`started, and the copy agenticreplay took first does not hold ${shown}${rest} — a nested git ` +
|
|
1901
|
-
'repository, or a path the sensitive-file rules keep out of every snapshot. AgenticReplay deletes ' +
|
|
1902
|
-
'only what it has a copy of, so nothing has been deleted. Move those out of the reset ' +
|
|
1903
|
-
'paths, or replay with --in-place to leave the working tree alone.',
|
|
1904
|
-
);
|
|
1905
|
-
}
|
|
1906
|
-
|
|
1907
|
-
/**
|
|
1908
|
-
* Refuse the restore unless the safety copy holds every file it is about to overwrite — and name
|
|
1909
|
-
* the files it will write where the operator has none. Exported for the test that pins what the
|
|
1910
|
-
* removal may walk through.
|
|
1911
|
-
*
|
|
1912
|
-
* `materialize` writes each file the recorded tree holds, whatever is at that path. The copy it is
|
|
1913
|
-
* undone from is an ordinary snapshot, which honours the workspace's `.gitignore`, so a path the
|
|
1914
|
-
* recording tracked and the workspace ignores now was overwritten with the recorded bytes, and the
|
|
1915
|
-
* put-back had nothing to put back. Measured: a `settings.json` moved into `.gitignore` after the
|
|
1916
|
-
* recording, holding the operator's local settings, came out of `agenticreplay replay` holding the
|
|
1917
|
-
* recording's, under "your files are restored when the replay ends" — and neither git nor agenticreplay
|
|
1918
|
-
* held the original anywhere. The rule `assertResettable` enforces for a delete holds for an
|
|
1919
|
-
* overwrite: agenticreplay destroys only what it took a copy of first.
|
|
1920
|
-
*
|
|
1921
|
-
* The same listing settles the other half of that sentence. A file the recorded tree holds and the
|
|
1922
|
-
* working tree does not — deleted since, say — is written by the restore, and the put-back, whose
|
|
1923
|
-
* copy never had it, left it there: a replay brought back a config the operator had removed and
|
|
1924
|
-
* committed the removal of. Those are returned for the put-back to take away again. AgenticReplay put them
|
|
1925
|
-
* there, and nothing of the operator's was at those paths when it did.
|
|
1926
|
-
*
|
|
1927
|
-
* EVERY TREE THE RUN RECORDED, not only the one the restore writes. An exact replay makes the
|
|
1928
|
-
* recorded agent do what it did, so the files it wrote are in the later checkpoints: one it
|
|
1929
|
-
* created came back with the replay and stayed after the put-back, like the restore's own, and
|
|
1930
|
-
* one it overwrote that the copy does not hold is lost to the agent exactly as it would be to the
|
|
1931
|
-
* restore. Both are answered the same way.
|
|
1932
|
-
*
|
|
1933
|
-
* Asked before `resetArtifacts`: the reset deletes what the copy holds, and an artifact would look
|
|
1934
|
-
* absent after it. Artifacts are forced into the copy, so neither list can name one.
|
|
1935
|
-
*/
|
|
1936
|
-
export async function assertOverwritable(
|
|
1937
|
-
recorded: FsCapture,
|
|
1938
|
-
trees: readonly string[],
|
|
1939
|
-
safety: FsCapture,
|
|
1940
|
-
held: string,
|
|
1941
|
-
dir: string,
|
|
1942
|
-
): Promise<Introduced> {
|
|
1943
|
-
const copied = new Set((await safety.files(held)).map((file) => fold(file.path)));
|
|
1944
|
-
// What is on disk at a path, asked once per path. A path this process cannot even look at counts
|
|
1945
|
-
// as something there: nothing here may conclude "safe to overwrite" from an error it cannot read.
|
|
1946
|
-
const seen = new Map<string, 'dir' | 'other' | 'none'>();
|
|
1947
|
-
const at = (path: string): 'dir' | 'other' | 'none' => {
|
|
1948
|
-
let kind = seen.get(path);
|
|
1949
|
-
if (kind === undefined) {
|
|
1950
|
-
try {
|
|
1951
|
-
kind = lstatSync(join(dir, path)).isDirectory() ? 'dir' : 'other';
|
|
1952
|
-
} catch (err) {
|
|
1953
|
-
const code = (err as NodeJS.ErrnoException).code;
|
|
1954
|
-
kind = code === 'ENOENT' || code === 'ENOTDIR' ? 'none' : 'other';
|
|
1955
|
-
}
|
|
1956
|
-
seen.set(path, kind);
|
|
1957
|
-
}
|
|
1958
|
-
return kind;
|
|
1959
|
-
};
|
|
1960
|
-
const absent = (path: string): boolean => at(path) === 'none';
|
|
1961
|
-
// Every directory git walked into when it took the copy. A link among them — a junction, on
|
|
1962
|
-
// Windows, whose contents the copy holds as ordinary files — is a directory as far as the
|
|
1963
|
-
// restore and the put-back are concerned, and is treated as one here.
|
|
1964
|
-
const intoCopy = new Set<string>();
|
|
1965
|
-
for (const held of copied) {
|
|
1966
|
-
const parts = held.split('/');
|
|
1967
|
-
for (let i = 1; i < parts.length; i += 1) intoCopy.add(parts.slice(0, i).join('/'));
|
|
1968
|
-
}
|
|
1969
|
-
// What the restore would destroy to write `path`, asked the way it writes: every directory on
|
|
1970
|
-
// the way first, then the path itself. A file or a link standing where the recording has a
|
|
1971
|
-
// directory is destroyed to make the directory — and the error from asking for the path under it
|
|
1972
|
-
// does not say so, since POSIX answers ENOTDIR there and Windows answers ENOENT, exactly as for a
|
|
1973
|
-
// path under nothing at all. Undefined when nothing is in the way.
|
|
1974
|
-
const inTheWay = (path: string): string | undefined => {
|
|
1975
|
-
const parts = path.split('/');
|
|
1976
|
-
for (let i = 1; i < parts.length; i += 1) {
|
|
1977
|
-
const prefix = parts.slice(0, i).join('/');
|
|
1978
|
-
const kind = at(prefix);
|
|
1979
|
-
if (kind === 'none') return undefined;
|
|
1980
|
-
if (kind === 'other' && !intoCopy.has(fold(prefix))) return prefix;
|
|
1981
|
-
}
|
|
1982
|
-
return absent(path) ? undefined : path;
|
|
1983
|
-
};
|
|
1984
|
-
const overwritten = new Set<string>();
|
|
1985
|
-
const files: Introduced['files'] = [];
|
|
1986
|
-
const recordedFiles = new Map<string, Set<string>>();
|
|
1987
|
-
for (const tree of new Set(trees)) {
|
|
1988
|
-
for (const { path, oid } of await recorded.files(tree)) {
|
|
1989
|
-
const oids = recordedFiles.get(path) ?? new Set<string>();
|
|
1990
|
-
oids.add(oid);
|
|
1991
|
-
recordedFiles.set(path, oids);
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
for (const [path, oids] of recordedFiles) {
|
|
1995
|
-
if (copied.has(fold(path))) continue;
|
|
1996
|
-
const blocker = inTheWay(path);
|
|
1997
|
-
if (blocker === undefined) files.push({ path, oids });
|
|
1998
|
-
// Held: the put-back writes it back, clearing the directory the restore made in its place.
|
|
1999
|
-
else if (blocker !== path && copied.has(fold(blocker))) continue;
|
|
2000
|
-
else overwritten.add(blocker);
|
|
2001
|
-
}
|
|
2002
|
-
if (overwritten.size > 0) {
|
|
2003
|
-
const listed = [...overwritten].sort();
|
|
2004
|
-
const shown = listed.slice(0, 5).join(', ');
|
|
2005
|
-
const rest = listed.length > 5 ? `, and ${listed.length - 5} more` : '';
|
|
2006
|
-
throw new Error(
|
|
2007
|
-
`this replay would overwrite ${shown}${rest}: your tree has them and the copy agenticreplay took ` +
|
|
2008
|
-
'first does not — the workspace ignores them, or they sit in a nested repository — so ' +
|
|
2009
|
-
'nothing could put them back. Nothing has been changed. Move them aside, or replay with ' +
|
|
2010
|
-
'--worktree to leave the working tree alone.',
|
|
2011
|
-
);
|
|
2012
|
-
}
|
|
2013
|
-
const parents = new Set<string>();
|
|
2014
|
-
for (const file of files) {
|
|
2015
|
-
const parts = file.path.split('/');
|
|
2016
|
-
for (let i = 1; i < parts.length; i += 1) parents.add(parts.slice(0, i).join('/'));
|
|
2017
|
-
}
|
|
2018
|
-
const dirs: string[] = [];
|
|
2019
|
-
for (const parent of parents) if (absent(parent)) dirs.push(parent);
|
|
2020
|
-
dirs.sort((a, b) => b.split('/').length - a.split('/').length);
|
|
2021
|
-
// What the removal may pass through besides real directories: the links on the way to these
|
|
2022
|
-
// files that git walked into — asked now, while the tree is still the operator's. Not every
|
|
2023
|
-
// directory git walked into: a real one there now could be a link by the time the removal runs,
|
|
2024
|
-
// and a path that was a directory when the copy was taken is no licence to follow a link there.
|
|
2025
|
-
// Where each pointed, too: a link re-pointed by the time the removal runs is another link, and
|
|
2026
|
-
// following it could leave the workspace as surely as following a new one.
|
|
2027
|
-
const walked = new Map<string, string>();
|
|
2028
|
-
for (const parent of parents) {
|
|
2029
|
-
if (!intoCopy.has(fold(parent))) continue;
|
|
2030
|
-
try {
|
|
2031
|
-
const at = join(dir, parent);
|
|
2032
|
-
if (lstatSync(at).isSymbolicLink()) walked.set(fold(parent), readlinkSync(at));
|
|
2033
|
-
} catch {
|
|
2034
|
-
// Gone, or not ours to look at: nothing to walk through.
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
return { files, dirs, walked };
|
|
2038
|
-
}
|
|
2039
|
-
|
|
2040
|
-
/**
|
|
2041
|
-
* Take away what the recording put where the operator had nothing — and only that.
|
|
2042
|
-
*
|
|
2043
|
-
* Asked of each file as it stands now, not as it stood when the list was made. The replayed agent
|
|
2044
|
-
* has run since, for minutes with a real harness, in a tree nothing else was told to keep out of:
|
|
2045
|
-
* the operator's own new file, a watcher's output, or a replay that went its own way can be at one
|
|
2046
|
-
* of these paths by now, and these are exactly the paths no copy holds. So a file goes only while
|
|
2047
|
-
* its bytes are a version the recording holds for that path — what the restore wrote, or what the
|
|
2048
|
-
* recorded agent wrote after it. Anything else is left where it is, and named.
|
|
2049
|
-
*
|
|
2050
|
-
* `rmdir` takes a directory only while it is empty, so one that something else wrote into stays,
|
|
2051
|
-
* with what it wrote. Exported for the test that pins which files it will not take; `judging` is
|
|
2052
|
-
* that test's way in to the moment a file is being judged, and nothing in agenticreplay passes one.
|
|
2053
|
-
*/
|
|
2054
|
-
export async function removeIntroduced(
|
|
2055
|
-
dir: string,
|
|
2056
|
-
introduced: Introduced,
|
|
2057
|
-
out: Output,
|
|
2058
|
-
judging?: (path: string) => void | Promise<void>,
|
|
2059
|
-
): Promise<void> {
|
|
2060
|
-
const kept: string[] = [];
|
|
2061
|
-
const setAside: string[] = [];
|
|
2062
|
-
for (const file of introduced.files) {
|
|
2063
|
-
if (!onlyThroughDirectories(dir, file.path, introduced.walked)) continue;
|
|
2064
|
-
const suffix = `.agenticreplay-${randomBytes(4).toString('hex')}`;
|
|
2065
|
-
const outcome = await takeIfRecorded(join(dir, file.path), suffix, file.oids, judging);
|
|
2066
|
-
if (outcome === 'kept') kept.push(file.path);
|
|
2067
|
-
else if (outcome === 'set-aside') setAside.push(`${file.path}${suffix}`);
|
|
2068
|
-
}
|
|
2069
|
-
for (const made of introduced.dirs) {
|
|
2070
|
-
if (!onlyThroughDirectories(dir, made, introduced.walked)) continue;
|
|
2071
|
-
try {
|
|
2072
|
-
rmdirSync(join(dir, made));
|
|
2073
|
-
} catch {
|
|
2074
|
-
// Not empty: something wrote into it that is not agenticreplay's to take.
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2077
|
-
if (kept.length > 0 || setAside.length > 0) {
|
|
2078
|
-
out.warn('replay.left_in_place', {
|
|
2079
|
-
paths: kept.length > 0 ? kept.sort().join(',') : undefined,
|
|
2080
|
-
set_aside: setAside.length > 0 ? setAside.sort().join(',') : undefined,
|
|
2081
|
-
why: 'agenticreplay takes away only what it can show it put there, and could not show it of these — written since by something else, or in use where they could not be moved',
|
|
2082
|
-
next: 'delete them yourself if they are not yours; set_aside names ones whose path was taken while they were being judged',
|
|
2083
|
-
});
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
|
|
2087
|
-
/**
|
|
2088
|
-
* Remove the file at `target` if, and only if, it holds one of `oids` — judged on the very file that
|
|
2089
|
-
* goes. Hashing a path and then removing the path looks it up twice, and a writer that replaces
|
|
2090
|
-
* the file in between would have its bytes removed unjudged. Renamed aside first, the file is
|
|
2091
|
-
* pinned: the rename is atomic, and what is hashed is what is removed. A file written at the path
|
|
2092
|
-
* a moment later is a new file there, which this never touches.
|
|
2093
|
-
*
|
|
2094
|
-
* One that does not match goes back where it was — without replacing anything that has taken the
|
|
2095
|
-
* path meanwhile. Then it stays beside it under the name it was set aside as, and is named.
|
|
2096
|
-
*/
|
|
2097
|
-
async function takeIfRecorded(
|
|
2098
|
-
target: string,
|
|
2099
|
-
suffix: string,
|
|
2100
|
-
oids: ReadonlySet<string>,
|
|
2101
|
-
judging?: (path: string) => void | Promise<void>,
|
|
2102
|
-
): Promise<'absent' | 'taken' | 'kept' | 'set-aside'> {
|
|
2103
|
-
try {
|
|
2104
|
-
if (lstatSync(target).isDirectory()) return 'kept';
|
|
2105
|
-
} catch {
|
|
2106
|
-
return 'absent';
|
|
2107
|
-
}
|
|
2108
|
-
const aside = `${target}${suffix}`;
|
|
2109
|
-
try {
|
|
2110
|
-
renameSync(target, aside);
|
|
2111
|
-
} catch (err) {
|
|
2112
|
-
// Gone in the meantime, or held open where the platform will not move it: not taken either way.
|
|
2113
|
-
return (err as NodeJS.ErrnoException).code === 'ENOENT' ? 'absent' : 'kept';
|
|
2114
|
-
}
|
|
2115
|
-
await judging?.(target);
|
|
2116
|
-
// A file that cannot even be read is not one agenticreplay can show it wrote — and throwing here would
|
|
2117
|
-
// leave it under the name it was set aside as. Back where it was, like any other.
|
|
2118
|
-
const now = await blobId(aside, oids).catch(() => undefined);
|
|
2119
|
-
if (now !== undefined && oids.has(now)) {
|
|
2120
|
-
try {
|
|
2121
|
-
unlinkSync(aside);
|
|
2122
|
-
return 'taken';
|
|
2123
|
-
} catch {
|
|
2124
|
-
// Could not remove it: put it back rather than leave it under a name nobody knows.
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
return putBack(aside, target) ? 'kept' : 'set-aside';
|
|
2128
|
-
}
|
|
2129
|
-
|
|
2130
|
-
/**
|
|
2131
|
-
* Move `aside` back to `target` unless something is at `target` now — and decide that atomically.
|
|
2132
|
-
* Checking the path and then renaming onto it is a race the rename loses: on POSIX `rename`
|
|
2133
|
-
* replaces whatever arrived in between, which is the very write this exists to keep. So every way
|
|
2134
|
-
* back is a primitive that creates only if absent, and fails on EEXIST: a hard link for a file; a
|
|
2135
|
-
* fresh `symlink` for a link, which `link` would follow on some platforms (macOS); and, where the
|
|
2136
|
-
* filesystem has no hard links, a copy opened with O_EXCL.
|
|
2137
|
-
*/
|
|
2138
|
-
function putBack(aside: string, target: string): boolean {
|
|
2139
|
-
try {
|
|
2140
|
-
if (lstatSync(aside).isSymbolicLink()) {
|
|
2141
|
-
const to = readlinkSync(aside);
|
|
2142
|
-
const junction = process.platform === 'win32' && isDirectory(aside);
|
|
2143
|
-
symlinkSync(to, target, junction ? 'junction' : undefined);
|
|
2144
|
-
} else {
|
|
2145
|
-
try {
|
|
2146
|
-
linkSync(aside, target);
|
|
2147
|
-
} catch (err) {
|
|
2148
|
-
if ((err as NodeJS.ErrnoException).code === 'EEXIST') return false;
|
|
2149
|
-
copyFileSync(aside, target, fsConstants.COPYFILE_EXCL);
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
2152
|
-
} catch {
|
|
2153
|
-
// EEXIST among them: something has the path now, and this file stays beside it, named.
|
|
2154
|
-
return false;
|
|
2155
|
-
}
|
|
2156
|
-
try {
|
|
2157
|
-
unlinkSync(aside);
|
|
2158
|
-
} catch {
|
|
2159
|
-
// Two names for one file: the one the operator knows is back, which is what matters.
|
|
2160
|
-
}
|
|
2161
|
-
return true;
|
|
2162
|
-
}
|
|
2163
|
-
|
|
2164
|
-
/** Whether `path` leads to a directory, following links; false when it leads nowhere. */
|
|
2165
|
-
function isDirectory(path: string): boolean {
|
|
2166
|
-
try {
|
|
2167
|
-
return statSync(path).isDirectory();
|
|
2168
|
-
} catch {
|
|
2169
|
-
return false;
|
|
2170
|
-
}
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
/**
|
|
2174
|
-
* The git blob id of what stands at `path` now — the id `ls-tree` reports for the same bytes, in
|
|
2175
|
-
* the object format `like` is in. Undefined when nothing is there; something that is neither a
|
|
2176
|
-
* file nor a link gets an id no blob has, so it is never taken for one.
|
|
2177
|
-
*/
|
|
2178
|
-
async function blobId(path: string, like: ReadonlySet<string>): Promise<string | undefined> {
|
|
2179
|
-
let st: ReturnType<typeof lstatSync>;
|
|
2180
|
-
try {
|
|
2181
|
-
st = lstatSync(path);
|
|
2182
|
-
} catch {
|
|
2183
|
-
return undefined;
|
|
2184
|
-
}
|
|
2185
|
-
const sample = like.values().next().value;
|
|
2186
|
-
const hash = createHash(sample !== undefined && sample.length === 64 ? 'sha256' : 'sha1');
|
|
2187
|
-
if (st.isSymbolicLink()) {
|
|
2188
|
-
const target = readlinkSync(path, { encoding: 'buffer' });
|
|
2189
|
-
hash.update(`blob ${target.length}\0`).update(target);
|
|
2190
|
-
} else if (st.isFile()) {
|
|
2191
|
-
hash.update(`blob ${st.size}\0`);
|
|
2192
|
-
for await (const chunk of createReadStream(path)) hash.update(chunk as Buffer);
|
|
2193
|
-
} else {
|
|
2194
|
-
return 'not a file';
|
|
2195
|
-
}
|
|
2196
|
-
return hash.digest('hex');
|
|
2197
|
-
}
|
|
2198
|
-
|
|
2199
|
-
/**
|
|
2200
|
-
* Whether removing `file` under `dir` stays inside the tree: every directory on the way a real one,
|
|
2201
|
-
* or a link git walked into when it took the copy that still points where it pointed then. Asked
|
|
2202
|
-
* at removal time, because a link can have appeared on the way since, or been re-pointed — the
|
|
2203
|
-
* replayed agent may have done either — and `rm` follows it wherever it points now.
|
|
2204
|
-
*/
|
|
2205
|
-
function onlyThroughDirectories(
|
|
2206
|
-
dir: string,
|
|
2207
|
-
file: string,
|
|
2208
|
-
walked: ReadonlyMap<string, string>,
|
|
2209
|
-
): boolean {
|
|
2210
|
-
const parts = file.split('/');
|
|
2211
|
-
for (let i = 1; i < parts.length; i += 1) {
|
|
2212
|
-
const prefix = parts.slice(0, i).join('/');
|
|
2213
|
-
const at = join(dir, prefix);
|
|
2214
|
-
try {
|
|
2215
|
-
const st = lstatSync(at);
|
|
2216
|
-
if (st.isDirectory()) continue;
|
|
2217
|
-
const then = walked.get(fold(prefix));
|
|
2218
|
-
if (!st.isSymbolicLink() || then === undefined || readlinkSync(at) !== then) return false;
|
|
2219
|
-
} catch {
|
|
2220
|
-
return false;
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
return true;
|
|
2224
|
-
}
|
|
2225
|
-
|
|
2226
|
-
/** The `--in-place` half of the same rule: say what was not done, rather than doing it unsafely. */
|
|
2227
|
-
function warnArtifactsKept(artifacts: HarnessArtifacts | undefined, out: Output): void {
|
|
2228
|
-
const paths = artifacts?.resetBeforeReplay ?? [];
|
|
2229
|
-
if (paths.length === 0) return;
|
|
2230
|
-
out.warn('replay.artifacts_kept', {
|
|
2231
|
-
paths: paths.join(','),
|
|
2232
|
-
why: 'this replay is not restoring the working tree, so agenticreplay has no copy to put back',
|
|
2233
|
-
effect:
|
|
2234
|
-
'the harness may resume from artifacts the recording had not produced yet and ask nothing',
|
|
2235
|
-
next: 'replay without --in-place, or clear them yourself',
|
|
2236
|
-
});
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
|
-
/** Whether `target` is `dir` or sits under it, by the platform's own path rules. */
|
|
2240
|
-
function isInsideDir(dir: string, target: string): boolean {
|
|
2241
|
-
const rel = relative(resolve(dir), target);
|
|
2242
|
-
return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
/** Where the replayed agent's own stdout should go, given the flags. */
|
|
2246
|
-
function agentStdoutFor(args: ParsedArgs): 'inherit' | 'stderr' | 'ignore' {
|
|
2247
|
-
if (args.bool('quiet')) return 'ignore';
|
|
2248
|
-
return args.bool('json') ? 'stderr' : 'inherit';
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
/**
|
|
2252
|
-
* Launch the agent for a replay or a fork.
|
|
2253
|
-
*
|
|
2254
|
-
* Where the replayed agent's own stdout goes:
|
|
2255
|
-
*
|
|
2256
|
-
* inherit the default — you asked to watch the run happen again, so you watch it
|
|
2257
|
-
* stderr `--json`: agenticreplay's stdout is the result document, so the agent's output moves aside
|
|
2258
|
-
* rather than landing in the middle of it
|
|
2259
|
-
* ignore `--quiet`: the run is being replayed for its verdict, not for its narration
|
|
2260
|
-
*
|
|
2261
|
-
* stdin and stderr stay inherited in every case, so a harness that prompts still can and a
|
|
2262
|
-
* harness that fails still says why.
|
|
2263
|
-
*/
|
|
2264
|
-
async function runChild(
|
|
2265
|
-
command: string,
|
|
2266
|
-
argv: string[],
|
|
2267
|
-
env: NodeJS.ProcessEnv,
|
|
2268
|
-
cwd = process.cwd(),
|
|
2269
|
-
agentStdout: 'inherit' | 'stderr' | 'ignore' = 'inherit',
|
|
2270
|
-
): Promise<number> {
|
|
2271
|
-
// Same resolution as record and as detection; see resolveLaunch.
|
|
2272
|
-
const target = await resolveLaunch(command, argv);
|
|
2273
|
-
return new Promise((resolve, reject) => {
|
|
2274
|
-
const child = spawn(target.file, target.args, {
|
|
2275
|
-
env,
|
|
2276
|
-
cwd,
|
|
2277
|
-
shell: target.shell,
|
|
2278
|
-
stdio:
|
|
2279
|
-
agentStdout === 'inherit'
|
|
2280
|
-
? 'inherit'
|
|
2281
|
-
: ['inherit', agentStdout === 'ignore' ? 'ignore' : 'pipe', 'inherit'],
|
|
2282
|
-
});
|
|
2283
|
-
if (agentStdout === 'stderr') child.stdout?.pipe(process.stderr);
|
|
2284
|
-
child.on('error', (err) =>
|
|
2285
|
-
reject(new Error(`could not launch "${command}": ${String(err)}\n is it on your PATH?`)),
|
|
2286
|
-
);
|
|
2287
|
-
child.on('close', (code) => resolve(code ?? 0));
|
|
2288
|
-
});
|
|
2289
|
-
}
|