agenticreplay 0.1.3 → 0.1.4
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 +36 -912
- package/node_modules/@agenticreplay/adapters/dist/claude-code.d.ts.map +1 -1
- package/node_modules/@agenticreplay/adapters/dist/claude-code.js +7 -1
- package/node_modules/@agenticreplay/adapters/dist/claude-code.js.map +1 -1
- package/node_modules/@agenticreplay/adapters/dist/session.d.ts +2 -0
- package/node_modules/@agenticreplay/adapters/dist/session.d.ts.map +1 -1
- package/node_modules/@agenticreplay/adapters/dist/session.js +7 -1
- package/node_modules/@agenticreplay/adapters/dist/session.js.map +1 -1
- package/node_modules/@agenticreplay/adapters/package.json +6 -6
- package/node_modules/@agenticreplay/core/dist/index.d.ts +1 -0
- package/node_modules/@agenticreplay/core/dist/index.d.ts.map +1 -1
- package/node_modules/@agenticreplay/core/dist/index.js +1 -0
- package/node_modules/@agenticreplay/core/dist/index.js.map +1 -1
- package/node_modules/@agenticreplay/core/dist/paths.d.ts.map +1 -1
- package/node_modules/@agenticreplay/core/dist/paths.js +8 -4
- package/node_modules/@agenticreplay/core/dist/paths.js.map +1 -1
- package/node_modules/@agenticreplay/core/dist/run-names.d.ts +7 -0
- package/node_modules/@agenticreplay/core/dist/run-names.d.ts.map +1 -0
- package/node_modules/@agenticreplay/core/dist/run-names.js +79 -0
- package/node_modules/@agenticreplay/core/dist/run-names.js.map +1 -0
- package/node_modules/@agenticreplay/core/dist/writer.d.ts +5 -0
- package/node_modules/@agenticreplay/core/dist/writer.d.ts.map +1 -1
- package/node_modules/@agenticreplay/core/dist/writer.js +38 -6
- package/node_modules/@agenticreplay/core/dist/writer.js.map +1 -1
- package/node_modules/@agenticreplay/core/package.json +4 -3
- package/node_modules/@agenticreplay/fs-capture/package.json +2 -2
- package/node_modules/@agenticreplay/mcp-shim/package.json +1 -1
- package/node_modules/@agenticreplay/node-instrument/package.json +1 -1
- package/node_modules/@agenticreplay/plugin-api/dist/plugins.d.ts +1 -0
- package/node_modules/@agenticreplay/plugin-api/dist/plugins.d.ts.map +1 -1
- package/node_modules/@agenticreplay/plugin-api/package.json +1 -1
- package/node_modules/@agenticreplay/providers/package.json +3 -3
- package/node_modules/@agenticreplay/proxy/package.json +5 -5
- package/node_modules/@agenticreplay/schema/package.json +1 -1
- package/node_modules/@agenticreplay/shell-shim/dist/install.d.ts.map +1 -1
- package/node_modules/@agenticreplay/shell-shim/dist/install.js +5 -2
- package/node_modules/@agenticreplay/shell-shim/dist/install.js.map +1 -1
- package/node_modules/@agenticreplay/shell-shim/package.json +3 -3
- package/node_modules/@agenticreplay/viewer/package.json +2 -2
- package/node_modules/any-ascii/LICENSE +15 -0
- package/node_modules/any-ascii/README.md +20 -0
- package/node_modules/any-ascii/any-ascii.d.ts +4 -0
- package/node_modules/any-ascii/any-ascii.js +30 -0
- package/node_modules/any-ascii/block.js +645 -0
- package/node_modules/any-ascii/package.json +32 -0
- package/node_modules/any-ascii/test.js +83 -0
- package/package.json +12 -12
- package/packages/cli/dist/commands/record.d.ts.map +1 -1
- package/packages/cli/dist/commands/record.js +1 -0
- package/packages/cli/dist/commands/record.js.map +1 -1
- package/packages/cli/dist/commands/sync.d.ts.map +1 -1
- package/packages/cli/dist/commands/sync.js +17 -3
- package/packages/cli/dist/commands/sync.js.map +1 -1
- package/packages/cli/package.json +11 -11
package/README.md
CHANGED
|
@@ -1,941 +1,65 @@
|
|
|
1
1
|
# AgenticReplay
|
|
2
|
-
<sub>**English** · [简体中文](docs/i18n/README.zh-CN.md) · [日本語](docs/i18n/README.ja.md) · [한국어](docs/i18n/README.ko.md) · [Deutsch](docs/i18n/README.de.md) · [Français](docs/i18n/README.fr.md) · [Español](docs/i18n/README.es.md) · [العربية](docs/i18n/README.ar.md)</sub>
|
|
3
|
-
|
|
4
|
-
### Record model API traffic, inspect the evidence, and replay recorded responses offline.
|
|
5
|
-
|
|
6
|
-
AgenticReplay records supported clients at the HTTP proxy or process-local fetch boundary.
|
|
7
|
-
It preserves serialized request and response bodies after documented redaction, including
|
|
8
|
-
unknown provider fields and streaming content. A run with missing, interrupted, or over-limit
|
|
9
|
-
bodies is incomplete. Capture status, upstream outcome, and the agent's exit status are separate.
|
|
10
|
-
|
|
11
|
-
The default limit is **64 MiB per body**, configurable with `--max-body-bytes`; the buffering
|
|
12
|
-
budget is configurable with `--max-buffered-bytes`. Whole-body redaction runs before persistence.
|
|
13
|
-
The viewer exposes transformation details and complete redacted body downloads. Replay's
|
|
14
|
-
`canonical_exact`/`canonicalExact` score measures canonical request matching, not transport-byte equality.
|
|
15
|
-
|
|
16
|
-
Wrap the **host client** using Kiokuko (for example `agenticreplay record claude -- …`), not only
|
|
17
|
-
its `kiokuko mcp` child. Native Kiokuko DSH needs the companion recorder changes. See
|
|
18
|
-
[the acceptance status](docs/agenticreplay-acceptance.md) for verified paths and remaining gates.
|
|
19
|
-
Provider-hidden reasoning and SDK changes before the capture boundary are not observable.
|
|
20
|
-
|
|
21
|
-
[](LICENSE)
|
|
22
|
-
|
|
23
|
-
<sup>Real output from one session — a Claude Code run recorded, replayed against the recording, then
|
|
24
|
-
forked at checkpoint 4 onto two models and graded by `npx tsc --noEmit`. Nothing here is mocked up.</sup>
|
|
25
|
-
|
|
26
|
-
## Try it in three commands
|
|
27
|
-
|
|
28
|
-
The npm package `agenticreplay` installs the `agenticreplay` command. It wraps the client process
|
|
29
|
-
with temporary capture settings; it does not edit personal client configuration.
|
|
30
|
-
|
|
31
|
-
```console
|
|
32
|
-
npm i -g agenticreplay # the package is agenticreplay; the command it installs is agenticreplay
|
|
33
|
-
|
|
34
|
-
agenticreplay record claude # your agent, unmodified, doing whatever it does
|
|
35
|
-
agenticreplay replay last # the same run again — no network, no tokens, no charge
|
|
36
|
-
agenticreplay replay last --from 4 --model claude-haiku-4-5 --ui
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
The third line is the one people stay for: same files, same conversation prefix, different model
|
|
40
|
-
from step 4 onward. The model is the only variable, which is what makes the answer mean anything.
|
|
41
|
-
|
|
42
|
-
The three commands at the top need an agent installed, a key, a network and real tokens. If you
|
|
43
|
-
have none of those yet, one command brings its own:
|
|
44
|
-
|
|
45
|
-
```console
|
|
46
|
-
agenticreplay quickstart
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
It writes a small project with a genuine bug in it and a recording of an agent fixing that bug,
|
|
50
|
-
then replays the recording against the project with no model called: two failing tests before,
|
|
51
|
-
four passing after, three turns served from the trace and nothing spent. `--full` prints the whole
|
|
52
|
-
timeline and the replay as it happened.
|
|
53
|
-
|
|
54
|
-
## Read your agent's own system prompt
|
|
55
|
-
|
|
56
|
-
A proxy that sees the whole loop also sees the prompt the harness assembled before it sent
|
|
57
|
-
anything. One command captures it, scrubs the machine out of it, and files it by model:
|
|
58
|
-
|
|
59
|
-
```console
|
|
60
|
-
node capture/capture.mjs claude --model claude-opus-5
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Interactive prompts and `-p` prompts are not the same prompt, and neither is the same across
|
|
64
|
-
models. See [`capture/README.md`](capture/README.md) for the measured differences, the pitfalls,
|
|
65
|
-
and the sanitising rules.
|
|
66
|
-
|
|
67
|
-
## Why this exists
|
|
68
|
-
|
|
69
|
-
Agent debugging today is archaeology. You scroll a terminal, you re-run and get a different
|
|
70
|
-
failure, you add print statements to someone else's harness. The tools that exist are
|
|
71
|
-
observability tools: they tell you a run cost $4.12 and used 61k tokens, which is not the question
|
|
72
|
-
you have. The question you have is *why did it delete my migration file.*
|
|
73
|
-
|
|
74
|
-
AgenticReplay answers that by giving you the run back.
|
|
75
|
-
|
|
76
|
-
| | Observability tools | AgenticReplay |
|
|
77
|
-
|---|---|---|
|
|
78
|
-
| Tells you what a run cost | ✅ | ✅ |
|
|
79
|
-
| Tells you which tool call deleted the file | sometimes | ✅ |
|
|
80
|
-
| Runs the agent again and gets the same answer | ❌ | ✅ from the recording, byte-for-byte |
|
|
81
|
-
| Lets you change the model and re-run from step 4 | ❌ | ✅ |
|
|
82
|
-
| Needs you to modify your agent | usually an SDK wrapper | ❌ two env vars |
|
|
83
|
-
| Works after you close the terminal | ❌ | ✅ it is a file |
|
|
84
|
-
| Sees past the model API — shell exit codes, file writes | ❌ | ✅ every turn |
|
|
85
|
-
| Records an agent with no API endpoint to redirect | ❌ | ✅ opt-in `--tls-intercept` |
|
|
86
|
-
|
|
87
|
-
The last two rows are the ones an SDK wrapper structurally cannot reach. Capture happens *below*
|
|
88
|
-
the agent — at the process and socket boundary — so it does not matter whether the agent is
|
|
89
|
-
yours, whether you can edit it, or whether it even holds an API key: a Codex CLI signed in with
|
|
90
|
-
a ChatGPT subscription talks to its own backend over TLS, with a credential that only that backend
|
|
91
|
-
accepts, so it cannot be pointed elsewhere and stay the same session — and agenticreplay can still record
|
|
92
|
-
it. See
|
|
93
|
-
[when the harness will not be redirected](#when-the-harness-will-not-be-redirected).
|
|
94
|
-
|
|
95
|
-
## How it works
|
|
96
|
-
|
|
97
|
-
Model APIs are stateless, so on every turn an agent resends the entire conversation — including the
|
|
98
|
-
previous turn's tool results. **A proxy in front of the model therefore sees the whole loop**: each
|
|
99
|
-
request, each streamed response, every tool call the model emitted, and every tool result the
|
|
100
|
-
harness produced. That one property is what the tool is built on, and it is why **AgenticReplay does
|
|
101
|
-
not patch your agent** — it stands up a local proxy, sets two environment variables, and gets out of
|
|
102
|
-
the way.
|
|
103
|
-
|
|
104
|
-
**What "egress blocked" means, exactly.** On replay the proxy refuses to forward anything it cannot serve from the trace, so no model is called and no tokens are spent — that run prints `egress=blocked`. `--loose` lifts it deliberately: an unmatched request is then answered by the provider and recorded as a major divergence, and the same line reads `egress=live-on-unmatched`. A replay still *executes the recorded tool calls for real*, and a tool that opens its own socket — a shell command running `curl`, an MCP server fetching something — is outside the guarantee either way. By default it never reaches the proxy and goes to the network as usual. Under `--tls-intercept` it does reach the proxy, because that sets `HTTPS_PROXY` for the whole child: a host off the intercept list is tunnelled through untouched, though its hostname, port and byte counts still land in the trace, and a host on the list is refused there like any other unmatched call. Replay is not a sandbox; if you need one, run it inside one.
|
|
105
|
-
|
|
106
|
-
Three more layers catch what the protocol cannot see: an exit code, a real duration, which stream a
|
|
107
|
-
byte came out of, a file written without telling anyone. A fifth exists for the agents that read no
|
|
108
|
-
base-URL variable at all — see [which agents](#which-agents). A sixth reads the agent's own account
|
|
109
|
-
of its structure, for a harness that has one: which sub-agent ran, which handed off to which,
|
|
110
|
-
whether a guardrail tripped, which graph node produced what and which of them called no model at
|
|
111
|
-
all — none of which reaches the wire.
|
|
112
|
-
|
|
113
|
-
```mermaid
|
|
114
|
-
%%{init: {'theme':'neutral'}}%%
|
|
115
|
-
flowchart LR
|
|
116
|
-
A["<b>your agent</b><br/><i>unmodified</i>"]
|
|
117
|
-
|
|
118
|
-
subgraph agenticreplay["agenticreplay · six capture layers"]
|
|
119
|
-
direction TB
|
|
120
|
-
P["<b>proxy</b><br/>base-URL env var"]
|
|
121
|
-
SH["<b>PATH shim</b><br/>exit code · timing · streams"]
|
|
122
|
-
MC["<b>JSON-RPC tee</b><br/>MCP config rewrite"]
|
|
123
|
-
FS["<b>shadow git index</b><br/>workspace per turn"]
|
|
124
|
-
FH["<b>fetch hook</b><br/>for a hardcoded origin"]
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
A --> P & SH & MC & FS & FH
|
|
128
|
-
P -->|"forwarded, auth intact"| U["<b>the model API</b><br/><i>or your configured gateway</i>"]
|
|
129
|
-
agenticreplay ==> T[("<b>one trace</b><br/>.agenticreplay/runs/run_a1b2c3")]
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
They all land in the same timeline, ordered by when they actually happened rather than when agenticreplay
|
|
133
|
-
got around to reading them.
|
|
134
|
-
|
|
135
|
-
### Exact, fork and compare are one thing
|
|
136
|
-
|
|
137
|
-
They are not three subsystems. They are the same proxy with a **cursor** — the position in the
|
|
138
|
-
recorded stream where it stops answering from disk and starts answering from the network.
|
|
139
|
-
|
|
140
|
-
```mermaid
|
|
141
|
-
%%{init: {'theme':'neutral'}}%%
|
|
142
|
-
flowchart LR
|
|
143
|
-
subgraph disk["from disk · byte-for-byte · network blocked"]
|
|
144
|
-
direction LR
|
|
145
|
-
T1["turn 1"] --> T2["turn 2"] --> T3["turn 3"] --> T4["turn 4"]
|
|
146
|
-
end
|
|
147
|
-
T4 ==> CUR{{"<b>cursor</b>"}}
|
|
148
|
-
CUR ==> T5
|
|
149
|
-
subgraph net["from the network · any model you name"]
|
|
150
|
-
direction LR
|
|
151
|
-
T5["turn 5"] --> T6["turn 6"] --> T7["…"]
|
|
152
|
-
end
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
| command | where the cursor sits | what you get |
|
|
156
|
-
|---|---|---|
|
|
157
|
-
| `agenticreplay replay last` | at the end | the whole run again, **network blocked** — no tokens, no charge, no variance |
|
|
158
|
-
| `agenticreplay replay last --from 4 --model X` | at checkpoint 4 | turns up to 4 identical, then a different model takes over |
|
|
159
|
-
| `agenticreplay compare last --from 4 --models a,b` | at checkpoint 4, several times | one table, one variable — the model |
|
|
160
|
-
|
|
161
|
-
A **checkpoint** is not recorded; it is *derived* — any point where the conversation prefix is
|
|
162
|
-
complete and the workspace was snapshotted. Every fork therefore starts from a state that provably
|
|
163
|
-
existed.
|
|
164
|
-
|
|
165
|
-
## What a bug hunt actually looks like
|
|
166
|
-
|
|
167
|
-
Your agent was supposed to fix a failing auth test. It exited 0 and the test still fails. Start with
|
|
168
|
-
what it actually did:
|
|
169
|
-
|
|
170
|
-
```console
|
|
171
|
-
$ agenticreplay show last
|
|
172
|
-
run_6473f858b59e generic-openai@0.1.0 14 events exit 0
|
|
173
|
-
|
|
174
|
-
SEQ KIND WHAT DETAIL
|
|
175
|
-
0 RUN run started generic-openai
|
|
176
|
-
1 SNAP tree 919d32ba037537b43814c83779963b2cc3023db7 0 changed
|
|
177
|
-
2 MODEL claude-opus-5 1 messages
|
|
178
|
-
3 MODEL claude-opus-5 stop: tool_use · 100 in · 20 out
|
|
179
|
-
4 TOOL edit_file {"path":"auth.ts",…}
|
|
180
|
-
5 SNAP tree c6af62b75c0c8b8938bd6087328b5148f3dcd534 1 changed
|
|
181
|
-
6 FILE auth.ts modified +1 −3
|
|
182
|
-
7 TOOL edit_file ok
|
|
183
|
-
8 MODEL claude-opus-5 3 messages
|
|
184
|
-
9 MODEL claude-opus-5 stop: end_turn · 101 in · 5 out
|
|
185
|
-
10 SNAP tree c6af62b75c0c8b8938bd6087328b5148f3dcd534 0 changed
|
|
186
|
-
11 SHELL ["sh","-c","node --check nonexistent-file.ts"] /tmp/hunt
|
|
187
|
-
12 SHELL shell result exit 1 · 43ms
|
|
188
|
-
13 RUN run ended exit 0
|
|
189
|
-
|
|
190
|
-
info usage input=201 output=25 cost=$0.004890
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Three facts the model's own transcript could not have told you, and the run's exit code hid: the
|
|
194
|
-
file really changed (seq 6, `+1 −3`), the check the agent ran **failed** (seq 12, `exit 1`), and it
|
|
195
|
-
finished anyway. The run exited 0 because the *agent* exited 0.
|
|
196
|
-
|
|
197
|
-
That last fact is the one worth a command of its own. `agenticreplay show` gives you the order things
|
|
198
|
-
happened in; `agenticreplay graph` gives you what produced what:
|
|
199
|
-
|
|
200
|
-
```console
|
|
201
|
-
$ agenticreplay graph last
|
|
202
|
-
FROM TO KIND WHY
|
|
203
|
-
3 model.response 4 tool.call recorded tool_use block in the response
|
|
204
|
-
4 tool.call 6 fs.change inferred changed path appears in tool input, same or previous turn
|
|
205
|
-
4 tool.call 7 tool.result recorded tool result answers its call
|
|
206
|
-
7 tool.result 8 model.request recorded tool_result block in the request
|
|
207
|
-
11 shell.exec 12 shell.result recorded shell result answers its exec
|
|
208
|
-
|
|
209
|
-
1 inferred — derived from this trace, not recorded in it
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Two kinds of edge, and the difference matters. A **recorded** edge was written when the run
|
|
213
|
-
happened, because a `tool_use` block is physically inside the response that emitted it. An
|
|
214
|
-
**inferred** edge was worked out just now by the rule it names — a filesystem snapshot is taken
|
|
215
|
-
once per turn rather than once per tool call, so attributing a file change to a *particular* call
|
|
216
|
-
is a good guess and not a fact. Inferred edges are never written back into the trace, the same way
|
|
217
|
-
checkpoints are derived and never recorded, so a field a third-party reader trusts never contains
|
|
218
|
-
something agenticreplay made up.
|
|
219
|
-
|
|
220
|
-
`--graph-card` draws the whole run that way — time left to right, kind of thing top to bottom, with
|
|
221
|
-
the chain that produced the failure lit against everything else:
|
|
222
|
-
|
|
223
|
-

|
|
224
|
-
|
|
225
|
-
The shape is the point. A run is one motif repeated — request, response, call, effect, result — so
|
|
226
|
-
anything that breaks it is worth a look, and an event with **no edge leaving it** is an absence a
|
|
227
|
-
list cannot show at all.
|
|
228
|
-
|
|
229
|
-
`agenticreplay export last --card bug.svg` draws just that chain, which is the version that fits in an issue
|
|
230
|
-
or a message:
|
|
231
|
-
|
|
232
|
-

|
|
233
|
-
|
|
234
|
-
Nothing picked the subject by hand — `--to` was not passed. The card carries its own legend because
|
|
235
|
-
a dashed line travelling without its trace would otherwise launder a guess into a fact, and it
|
|
236
|
-
prints the command that reproduces it.
|
|
237
|
-
|
|
238
|
-
SVG renders in a GitHub issue and almost nowhere else that matters — X will not take it as an
|
|
239
|
-
upload, and Slack and Discord give it no preview — so name the file `.png` and you get one, or
|
|
240
|
-
`.gif` and the chain builds a hop at a time. That path needs a browser, and agenticreplay does not depend on
|
|
241
|
-
one: `docs/media/README.md` keeps the render toolchain out of `package.json` so nobody running
|
|
242
|
-
`npm ci` pays for a Chromium download, and a picture command is not a reason to reverse that. Ask
|
|
243
|
-
for a raster without it and agenticreplay says the one line that fixes it; `agenticreplay doctor` reports it either
|
|
244
|
-
way, and `.svg` never needs anything.
|
|
245
|
-
|
|
246
|
-
```console
|
|
247
|
-
agenticreplay export last --card bug.png # the chain, ready to post
|
|
248
|
-
agenticreplay export last --card bug.gif # the same chain, one hop per frame
|
|
249
|
-
npm i --no-save playwright-core pngjs gifenc # only needed for the two above
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
Now reproduce it as often as you like, for nothing:
|
|
253
|
-
|
|
254
|
-
```console
|
|
255
|
-
$ agenticreplay replay last
|
|
256
|
-
info replay.done reused=2/2 canonical_exact=2 divergences=0 unmatched=0 exit=0
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
No network, no tokens, no variance. Then ask the question you actually have — *would a different
|
|
260
|
-
model have got this right?*
|
|
261
|
-
|
|
262
|
-
```console
|
|
263
|
-
$ agenticreplay compare last --from 5 --models claude-opus-5,claude-haiku-4-5 --verify "npm test"
|
|
264
|
-
MODEL VERDICT TOKENS COST WALL RUN
|
|
265
|
-
claude-opus-5 pass 201/25 $0.004890 0.3s run_1457b35062ba
|
|
266
|
-
claude-haiku-4-5 pass 201/25 $0.000326 0.3s run_b8ee08479fb6
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
Both pass. One costs **15× less**. Same files, same conversation prefix, same checkpoint — the model
|
|
270
|
-
is the only thing that changed, which is the only reason that number means anything.
|
|
271
|
-
|
|
272
|
-
## The timeline
|
|
273
|
-
|
|
274
|
-
`agenticreplay replay last --ui` (or `agenticreplay ui`) opens the run as one self-contained HTML file — no server
|
|
275
|
-
to keep running, no network, nothing to install. Filter it, step it, or press space and watch the
|
|
276
|
-
run play back at the pace it actually happened.
|
|
277
|
-
|
|
278
|
-

|
|
279
|
-
|
|
280
|
-
Every layer lands in the same timeline, so you can read the run as one story rather than four:
|
|
281
|
-
the model turns and their token counts, each tool call with its arguments and result, the shell
|
|
282
|
-
commands with their exit codes and timing, and the filesystem changes with the tree they produced.
|
|
283
|
-
|
|
284
|
-
`agenticreplay export last -o bug.html` writes exactly that page to a single file you can attach to an
|
|
285
|
-
issue. It carries no external reference of any kind — CI asserts that — so it renders from a
|
|
286
|
-
download folder, on a plane, in five years.
|
|
287
|
-
|
|
288
|
-
## Same task, different model
|
|
289
|
-
|
|
290
|
-
`agenticreplay compare` forks one recorded run onto several models from the same checkpoint, with the same
|
|
291
|
-
files and the same conversation prefix, and grades each one with a command you choose. The model
|
|
292
|
-
is the only variable, which is what makes the answer mean anything.
|
|
293
|
-
|
|
294
|
-

|
|
295
|
-
|
|
296
|
-
```console
|
|
297
|
-
agenticreplay compare last --from 4 \
|
|
298
|
-
--models claude-sonnet-5,claude-haiku-4-5 \
|
|
299
|
-
--verify "npm test" \
|
|
300
|
-
--share verdict.svg # the card above, ready to paste into an issue
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### Pointing it at several models
|
|
304
|
-
|
|
305
|
-
Comparing models means reaching several providers, and doing that by hand means knowing that
|
|
306
|
-
`--upstream-anthropic` and `--upstream-openai` exist, that one gateway can serve both wire formats,
|
|
307
|
-
and where the key goes. All of that is real and none of it is discoverable, so there is a command
|
|
308
|
-
that asks instead:
|
|
309
|
-
|
|
310
|
-
```console
|
|
311
|
-
$ agenticreplay setup
|
|
312
|
-
Gateway URL (required): https://gateway.example
|
|
313
|
-
API key (stored 0600; leave blank for none):
|
|
314
|
-
info config.saved path=~/.config/agenticreplay/config.json mode=0600 gateway=https://gateway.example auth=stored
|
|
315
|
-
|
|
316
|
-
6 models available:
|
|
317
|
-
anthropic/claude-opus-5
|
|
318
|
-
anthropic/claude-haiku-4-5
|
|
319
|
-
openai/gpt-5.2
|
|
320
|
-
...
|
|
321
|
-
|
|
322
|
-
$ agenticreplay models
|
|
323
|
-
MODEL $/MTOK IN $/MTOK OUT
|
|
324
|
-
anthropic/claude-opus-5 15 75
|
|
325
|
-
anthropic/claude-haiku-4-5 1 5
|
|
326
|
-
openai/gpt-5.2 1.25 10
|
|
327
|
-
some-local-model — —
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
`agenticreplay setup` asks the gateway what it actually serves rather than just writing the file, so a wrong
|
|
331
|
-
URL or a dead key is an answer now instead of a 401 in the middle of a comparison. It also stores the
|
|
332
|
-
models you picked, so after that `agenticreplay compare last --verify "npm test"` needs no model list and no
|
|
333
|
-
upstream flags at all. `agenticreplay models` prices what it recognises and shows a
|
|
334
|
-
dash for what it does not, because inventing a number for an unknown model is how a comparison
|
|
335
|
-
table ends up quoting a cost that was never real.
|
|
336
|
-
|
|
337
|
-
A gateway must be configured explicitly. Use an endpoint you control or a provider you choose
|
|
338
|
-
that supports the required API dialects. There is no default gateway.
|
|
339
|
-
|
|
340
|
-
Without a configured gateway, `agenticreplay record` retains the agent's selected provider and
|
|
341
|
-
credentials. Non-interactive setup requires `agenticreplay setup --gateway <url> --key-env <VAR>`;
|
|
342
|
-
`--key <key>` stores the credential in the private configuration file instead.
|
|
343
|
-
|
|
344
|
-
The key never reaches a trace. It is attached to the outbound request only, while what gets
|
|
345
|
-
recorded is built from the *incoming* request with auth stripped — so it is invisible to the
|
|
346
|
-
recording by construction, not by a rule someone has to remember. It is withheld entirely if a flag
|
|
347
|
-
sends that traffic somewhere other than the gateway that issued it.
|
|
348
|
-
|
|
349
|
-
## The other half of the run: `agenticreplay push` and `agenticreplay pull`
|
|
350
|
-
|
|
351
|
-
A compatible gateway can store and exchange AgenticReplay archives. Configure its URL explicitly;
|
|
352
|
-
legacy OrcaReplay archives are rejected by this clean-break release.
|
|
353
|
-
|
|
354
|
-
```console
|
|
355
|
-
$ agenticreplay list --remote # what the gateway is holding
|
|
356
|
-
$ agenticreplay push [run] # this machine -> the gateway
|
|
357
|
-
$ agenticreplay pull <run> # the gateway -> this machine
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-

|
|
361
|
-
|
|
362
|
-
<sub>Real output. The card is generated by `scripts/render-sync-card.mjs` from
|
|
363
|
-
`docs/media/sync-transcript.txt`, a captured round trip — `agenticreplay record`, then push, then pull into a
|
|
364
|
-
*different* workspace, then `agenticreplay show` reading the pulled copy. No line is written by hand. The
|
|
365
|
-
gateway there is a local stub speaking the same two endpoints, which is why its address is a
|
|
366
|
-
loopback one.</sub>
|
|
367
|
-
|
|
368
|
-
`push` defaults to the last run, like every other run-taking command. `pull` will not: "last" means
|
|
369
|
-
nothing on a machine that has not seen the run yet, so it takes an id.
|
|
370
|
-
|
|
371
|
-
Which is what `agenticreplay list --remote` is for. An id you do not have is not a default you can supply,
|
|
372
|
-
and the only place to read one used to be the console in a browser — so the command that needs an
|
|
373
|
-
id lived on a tab the terminal could not open. `--remote` asks the gateway the same question
|
|
374
|
-
`agenticreplay list` asks this directory, over the GET on the path push already POSTs to:
|
|
375
|
-
|
|
376
|
-
```console
|
|
377
|
-
$ agenticreplay list --remote --limit 3
|
|
378
|
-
RUN STARTED SOURCE APP TURNS TOOLS LAYERS MODELS OUTCOME
|
|
379
|
-
run_3d9e61a07b52 2026-09-21 09:36 upload generic-openai 1 0 model,fs m exit 0
|
|
380
|
-
run_8c14f2e9a07d3b6150e2c49a 2026-09-20 04:21 gateway Unknown 1 0 model,route deepseek/deepseek-v4-flash-free end_turn
|
|
381
|
-
run_f07a5c2e91d4468bb3e0a17c 2026-09-20 02:02 gateway curl 1 0 model,route deepseek/deepseek-v4.1-flash error
|
|
382
|
-
|
|
383
|
-
agenticreplay pull <run> # fetch one into this machine’s store
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
**LAYERS is the column to read.** It is what the two halves of this page are about: `model` and
|
|
387
|
-
`route` come from the gateway, which sees the routing decision no laptop can; `fs`, `shell`, `mcp`
|
|
388
|
-
and `net` come from a run recorded here. It says what is in a run before you spend a download on
|
|
389
|
-
it. SOURCE sits beside it because where a run came from is a different question from what it holds.
|
|
390
|
-
|
|
391
|
-
STARTED is the run's first event, not the moment the gateway stored it — for a pushed run those
|
|
392
|
-
differ by however long you waited before pushing. OUTCOME is the gateway's word for a run it
|
|
393
|
-
recorded (`end_turn`, `error`) and the exit for one pushed from here. Anything the gateway does not
|
|
394
|
-
report reads `—` rather than blank; an older deployment sends no timestamp at all, and STARTED is
|
|
395
|
-
left unset rather than dated 1970. `--source gateway` narrows to what the gateway recorded;
|
|
396
|
-
`--source upload` to what was pushed to it — those two, and a typo is refused rather than answered
|
|
397
|
-
with an empty list; without the flag you get both. `--limit` sets how many (20 by default). `--gateway`
|
|
398
|
-
stays what it is for push and pull — an optional override of *which* host — rather than doubling
|
|
399
|
-
as the switch, so a destination you have already named needs no url typed at it. All three belong
|
|
400
|
-
to `--remote`: given without it they are refused, not ignored, because `agenticreplay list --gateway <url>`
|
|
401
|
-
answering with this directory's runs would read as that host's answer.
|
|
402
|
-
|
|
403
|
-
`--json` asks the same question: `agenticreplay list --remote --json` emits the gateway's runs as one
|
|
404
|
-
document on stdout, which is where the id in `agenticreplay pull <run>` wants to come from when nobody
|
|
405
|
-
is reading a table.
|
|
406
|
-
|
|
407
|
-
### What it needs
|
|
408
|
-
|
|
409
|
-
A key with the `replay` scope, from **Console → API Keys → replay**. That scope can read and push
|
|
410
|
-
Sessions runs and cannot relay, so it is not a model-traffic credential wearing another hat — a
|
|
411
|
-
replay key is refused on the relay routes outright. Uploads are also off per workspace by default
|
|
412
|
-
and turned on under **Settings → request logs**; a gateway that is only recording, not accepting,
|
|
413
|
-
is the normal state.
|
|
414
|
-
|
|
415
|
-
```console
|
|
416
|
-
$ export AGENTICREPLAY_GATEWAY_URL=https://gateway.example # your explicitly selected gateway
|
|
417
|
-
$ read -rs AGENTICREPLAY_GATEWAY_KEY && export AGENTICREPLAY_GATEWAY_KEY # not typed on the command line
|
|
418
|
-
$ agenticreplay push last
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
Both exports persist for the shell session, which is the point of the rule above: a later
|
|
422
|
-
`agenticreplay push --gateway <somewhere-else>` in that same shell is refused rather than quietly handed the
|
|
423
|
-
key you exported for your own gateway.
|
|
424
|
-
|
|
425
|
-
### What it will not do
|
|
426
|
-
|
|
427
|
-
- **Explicit destination required.** Name the gateway with `--gateway`,
|
|
428
|
-
`AGENTICREPLAY_GATEWAY_URL`, or `agenticreplay setup --gateway <url>`. A trace can contain source,
|
|
429
|
-
shell output and filesystem snapshots. `push.packed` reports its file count and size before upload.
|
|
430
|
-
|
|
431
|
-
- **Never the workspace snapshots, unless you ask.** `agenticreplay scrub` rewrites `events.jsonl` and the
|
|
432
|
-
blobs, but it cannot rewrite the shadow git store under `fs/` — its objects are deflated and
|
|
433
|
-
addressed by the hash of their contents, so it searches them, tells you what is still in there,
|
|
434
|
-
and offers `--drop-fs`. Push therefore leaves `fs/` at home by default: a scrub that reports
|
|
435
|
-
"objects still hold the material" must not be followed by a push that ships them anyway. `--fs`
|
|
436
|
-
sends them for the case that needs them — a teammate reproducing against the tree — and says so
|
|
437
|
-
on the way out, because that is the one part of the payload the scan on either end cannot read
|
|
438
|
-
(the objects are compressed, so a plaintext scan sees nothing).
|
|
439
|
-
- **Never a key to a host it was not set up for.** Every credential has a home — the stored key's
|
|
440
|
-
is `agenticreplay setup`'s gateway, an exported key's is `AGENTICREPLAY_GATEWAY_URL`, and a key exported
|
|
441
|
-
with no URL beside it has no home at all — and it is attached only when the destination matches
|
|
442
|
-
that home. `--gateway` changes where the run goes, not what the key was
|
|
443
|
-
issued for, so pointing it elsewhere refuses the push rather than authenticating it with someone
|
|
444
|
-
else's credential. The one case left through is a key whose only destination is the one this
|
|
445
|
-
invocation names: nothing earlier associated it with a host, which is the CI shape.
|
|
446
|
-
- **Never anonymously.** None of the three is attempted without a key. An unauthenticated request
|
|
447
|
-
is exactly what a misconfigured public endpoint answers, and a `200` is a poor way to learn
|
|
448
|
-
either that your run went somewhere with no owner, or that what a listing showed you was never
|
|
449
|
-
scoped to you.
|
|
450
|
-
- **Never a partial replace.** `pull --force` writes the new copy beside the old one and swaps by
|
|
451
|
-
rename, so a crash leaves a complete run either way, and the next pull finishes or rolls back
|
|
452
|
-
whatever the last one left behind. `--force` asks to replace a recording, which is not a licence
|
|
453
|
-
to destroy it and then fail.
|
|
454
2
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
It re-computes the integrity root over `events.jsonl` and refuses a push whose root does not match
|
|
458
|
-
its manifest — which is why push sends the bytes already on disk and converts nothing. It also
|
|
459
|
-
scans the archive for plaintext secrets and **refuses** it on a hit. That refusal is the point of
|
|
460
|
-
`--force`, and why `--force` is opt-in: the default is what stops a key reaching a shared server.
|
|
461
|
-
|
|
462
|
-
Redaction itself is not re-run here. `TraceWriter` puts every event through *spill, redact,
|
|
463
|
-
validate, append* on its way to disk precisely so no caller can bypass it, so the bytes push reads
|
|
464
|
-
are already redacted; re-scrubbing them would rewrite the bytes the integrity root covers, which is
|
|
465
|
-
the one thing that makes an archive unpushable. The gateway's scan is a second, independent line,
|
|
466
|
-
not a repeat of the first.
|
|
467
|
-
|
|
468
|
-
## Which agents
|
|
469
|
-
|
|
470
|
-
Two things decide whether a harness can be recorded: whether it can be pointed at the proxy, and
|
|
471
|
-
whether agenticreplay understands the wire format it speaks once it arrives.
|
|
472
|
-
|
|
473
|
-
| Agent | How it is captured | State |
|
|
474
|
-
|---|---|---|
|
|
475
|
-
| **Claude Code** | `ANTHROPIC_BASE_URL` | works — validated against a real bug fix, [in detail](docs/validation.md) |
|
|
476
|
-
| **Codex CLI** (API key) | Invocation-scoped selected-provider URL override → Responses API | adapter implemented; current installed-client acceptance remains blocked — see [status](docs/agenticreplay-acceptance.md) |
|
|
477
|
-
| **Codex CLI** (ChatGPT login) | `--tls-intercept` → Responses API | protocol fixtures pass; current authenticated-client acceptance remains unverified |
|
|
478
|
-
| **OpenAI Agents SDK** | `OPENAI_BASE_URL` → Responses API | works — the SDK itself, on the Responses API it defaults to, records and replays at `canonical_exact=1`, [in CI](test/integrations/); [its own tracing is a second egress](docs/integrations.md#openai-agents-sdk) |
|
|
479
|
-
| **Vercel AI SDK** | `OPENAI_BASE_URL` via `@ai-sdk/openai` (since 2.0.41) — `agenticreplay record generic-openai --`; fetch hook (`agenticreplay record node --`) when the origin is compiled in | works — env-aware SDK clients via `generic-openai` at `canonical_exact=1`, [in CI](test/integrations/); an agent posting to an origin compiled into its source records and replays at `canonical_exact=1` |
|
|
480
|
-
| **grok-cli** (and its Telegram bot) | `agenticreplay record grok` — `GROK_BASE_URL`, plus the hook for its sub-agents | works |
|
|
481
|
-
| **OpenClaw** | `agenticreplay record openclaw` — the hook for the gateway, inherited variables for the agents it spawns | works |
|
|
482
|
-
| **opencode** | `agenticreplay record opencode` | adapter shipped, both origins redirected |
|
|
483
|
-
| **goose** (Block) | `agenticreplay record goose` — `OPENAI_HOST` **and** `OPENAI_BASE_URL`, `ANTHROPIC_HOST` → Responses API | works — driven end to end against goose 1.49.0, [what is different about it](#the-harness-that-reads-different-variables) |
|
|
484
|
-
| **LangGraph / LangChain** | `OPENAI_BASE_URL`, `ANTHROPIC_BASE_URL` | works — a two-node graph, streaming and with a tool, records and replays at `canonical_exact=2` and forks live, [in CI](test/integrations/); `pip install agenticreplay-langgraph` [also records the graph](python-langgraph/README.md) — which node ran, in which superstep, including the ones that call no model |
|
|
485
|
-
| **Haystack** | `agenticreplay record generic-openai -- python your_pipeline.py` — its `OpenAIChatGenerator` leaves `base_url` to the OpenAI SDK, which reads `OPENAI_BASE_URL` | works — a `Pipeline` with `ChatPromptBuilder` and `OpenAIChatGenerator` records and replays at `canonical_exact=1`, [in CI](test/integrations/) |
|
|
486
|
-
| **OpenHands** | `agenticreplay record generic-openai -- python your_agent.py` — its SDK wraps LiteLLM and reads `OPENAI_API_BASE` | works — the SDK's own LLM layer records and replays at `canonical_exact=1`, [in CI](test/integrations/) |
|
|
487
|
-
| **CrewAI** | `agenticreplay record generic-openai -- python your_crew.py` — since 1.x its own provider, reading `OPENAI_API_BASE` and `OPENAI_BASE_URL` | works — a real `Agent`, `Task` and `Crew` records and replays at `canonical_exact=1`, [in CI](test/integrations/); [what 1.x changed](docs/integrations.md#crewai) |
|
|
488
|
-
| **Aider** | `agenticreplay record generic-openai -- python your_agent.py` — routes through LiteLLM, which reads `OPENAI_API_BASE` | works — the LiteLLM layer records and replays at `canonical_exact=1`, [in CI](test/integrations/) |
|
|
489
|
-
| **browser-use** | `agenticreplay record generic-openai -- python your_task.py` — its `ChatOpenAI` passes an unset `base_url` straight through | works — records and replays at `canonical_exact=1`, [in CI](test/integrations/); LLM layer only, [the browser is not driven](docs/integrations.md#browser-use) |
|
|
490
|
-
| **Hermes** (Nous Research) | `agenticreplay record hermes --tls-intercept -- …` | transport adapter implemented; current installed-client acceptance remains unverified |
|
|
491
|
-
| **IndexRAG** | `agenticreplay record indexrag -- --data-dir dataset/<name>/documents` — the whole pipeline as one run: `OPENAI_BASE_URL` for chat, `INDEXRAG_EMBEDDING_BASE_URL` for a second origin | works — driven end to end against the real pipeline: indexing, embeddings and answering replay offline at `canonical_exact=11/11 retrieval=3/3`, and a fork keeps the recorded index, [what is different about a pipeline](docs/integrations.md#rag-and-retrieval-frameworks) |
|
|
492
|
-
| **a RAG pipeline** (LlamaIndex, GraphRAG, LightRAG) | `agenticreplay record generic-openai -- <cmd>` — embeddings and rerank replay without a dialect | works — a concurrent index build plus an embedding batch records and replays at `canonical_exact=5 retrieval=2`, [in CI](test/integrations/) |
|
|
493
|
-
| **Codex-in-the-IDE** | `agenticreplay record exec --tls-intercept -- code .` | works — the extension spawns the agent, and it inherits the capture |
|
|
494
|
-
| **a bot with a hardcoded origin** | `agenticreplay record exec --tls-intercept -- <cmd>` | works — a Grok bot posting to a URL in its own source, [in detail](#an-agent-that-reads-nothing-at-all) |
|
|
495
|
-
| **an agent in a sandbox or on another machine** | `agenticreplay attach` | works — agenticreplay is reachable and prints what to export, [in detail](#an-agent-that-is-not-on-this-machine) |
|
|
496
|
-
| **anything else** | `agenticreplay record generic-openai -- <cmd>` | works if it reads a base-URL variable; `agenticreplay record node -- <cmd>` if it does not |
|
|
497
|
-
|
|
498
|
-
Claude Code, Hermes and goose have been driven end to end against the real harness, and each of
|
|
499
|
-
them broke something. Claude Code [broke four things](docs/validation.md); Hermes found a streaming
|
|
500
|
-
exchange the proxy was dropping entirely; goose broke two more, and neither was in the adapter —
|
|
501
|
-
one was the replay matcher, one was a run reporting success over a trace of nothing but errors. The
|
|
502
|
-
rest are held to the adapter contract and to fixtures that record the exact variables each one
|
|
503
|
-
sets, so a harness that renames the variable it reads turns a check red instead of producing an
|
|
504
|
-
empty trace.
|
|
505
|
-
|
|
506
|
-
### The harness that reads different variables
|
|
507
|
-
|
|
508
|
-
Every other OpenAI-shaped client in this table reads `OPENAI_BASE_URL`. goose reads it too, but it
|
|
509
|
-
reads `OPENAI_HOST` **first** — and for Anthropic it reads `ANTHROPIC_HOST` and nothing else.
|
|
510
|
-
|
|
511
|
-
That combination is worse than it sounds, because both halves fail silently:
|
|
512
|
-
|
|
513
|
-
- Setting only `OPENAI_BASE_URL` works right up until the user already has `OPENAI_HOST` exported
|
|
514
|
-
for something else. Then their value wins, the run goes to their origin, and agenticreplay prints a clean
|
|
515
|
-
recording over an empty trace.
|
|
516
|
-
- `ANTHROPIC_BASE_URL` — the variable `generic-openai` sets, and the one the rest of the ecosystem
|
|
517
|
-
reads — does nothing at all here. A sink addressed only by it receives no request.
|
|
518
|
-
|
|
519
|
-
So the adapter sets `OPENAI_HOST`, `OPENAI_BASE_URL` and `ANTHROPIC_HOST`, all at the same proxy,
|
|
520
|
-
and the fixture pins all three. The traffic that arrives is the Responses API — `POST /v1/responses`
|
|
521
|
-
plus one `GET /v1/models` on start-up — which the proxy's `openai-responses` dialect already claims.
|
|
522
|
-
|
|
523
|
-
```console
|
|
524
|
-
GOOSE_PROVIDER=openai GOOSE_MODEL=<model> agenticreplay record goose -- run -t "fix the failing test"
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
`GOOSE_PROVIDER` and `GOOSE_MODEL` are passed through, never invented: goose has no default for a
|
|
528
|
-
custom endpoint, and choosing one for you would launch a different agent than `goose` does.
|
|
529
|
-
|
|
530
|
-
Two limits worth knowing before you rely on it. goose runs its shell without going through agenticreplay's
|
|
531
|
-
PATH shim, so `shell` tool calls are in the trace with their output but without the real exit code,
|
|
532
|
-
duration or stdout/stderr split — record with `--no-shell` to claim nothing rather than that. And
|
|
533
|
-
goose asks a second model for a session title *while the conversation is already under way*, so
|
|
534
|
-
that call and the first real turn race; the replay matcher handles them arriving in either order,
|
|
535
|
-
which it did not before goose was the first harness to do this.
|
|
536
|
-
|
|
537
|
-
### A gateway that launches the coding agent
|
|
538
|
-
|
|
539
|
-
OpenClaw does not do the coding: it runs Claude Code, Codex or opencode as child processes and
|
|
540
|
-
drives them from a chat app, so one run carries two kinds of model traffic. The gateway's own calls
|
|
541
|
-
are caught by the fetch hook. The coding agent's calls are caught by the ordinary variables — not
|
|
542
|
-
because OpenClaw reads them, but because **a child process inherits its parent's environment**, so
|
|
543
|
-
the Claude Code it spawns sees `ANTHROPIC_BASE_URL` exactly as it would if you had run it yourself.
|
|
544
|
-
|
|
545
|
-
```console
|
|
546
|
-
agenticreplay record openclaw
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
That inheritance is a property of the operating system rather than of agenticreplay, which is the kind of
|
|
550
|
-
thing that stays obviously true right up until some layer in between sanitises the environment. So
|
|
551
|
-
it has a test: a gateway fixture that makes no model call of its own, spawns an agent that does, and
|
|
552
|
-
is recorded and replayed offline through the grandchild's traffic.
|
|
553
|
-
|
|
554
|
-
### An agent that reads nothing at all
|
|
555
|
-
|
|
556
|
-
A bot with `https://api.x.ai/v1` typed into its source reads no variable, so nothing can be
|
|
557
|
-
redirected. It is also, by some distance, the most common shape of agent people write. Capture it
|
|
558
|
-
below the agent instead, at the socket:
|
|
559
|
-
|
|
560
|
-
```console
|
|
561
|
-
agenticreplay record exec --tls-intercept -- python bot.py
|
|
562
|
-
agenticreplay record exec --tls-intercept -- ./my-agent --task "fix the test"
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
`exec` (or `any`) launches your command and sets nothing — no origin and no credential — so the bot
|
|
566
|
-
still talks to whoever it was already talking to, and agenticreplay reads the conversation on the way past.
|
|
567
|
-
`api.x.ai` is on the default decrypt list, so a Grok bot needs no host named.
|
|
568
|
-
|
|
569
|
-
The same command reaches an agent agenticreplay did not launch, as long as something agenticreplay *did* launch is
|
|
570
|
-
its ancestor. That is the shape of Codex-in-the-IDE: the extension spawns the agent as a child, so
|
|
571
|
-
recording the editor captures the agent underneath it.
|
|
572
|
-
|
|
573
|
-
```console
|
|
574
|
-
agenticreplay record exec --tls-intercept -- code .
|
|
575
|
-
agenticreplay record exec --tls-intercept -- cursor .
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
Replay needs no flags repeated. A run recorded through interception writes down which hosts it
|
|
579
|
-
decrypted, and `agenticreplay replay` reads that back and re-establishes the same policy — `--no-tls-intercept`
|
|
580
|
-
if you would rather it did not.
|
|
581
|
-
|
|
582
|
-
To decrypt an endpoint the default list does not cover, add it rather than replacing the list:
|
|
583
|
-
|
|
584
|
-
```console
|
|
585
|
-
agenticreplay record exec --tls-intercept --tls-hosts '+my-gateway.internal' -- ./my-agent
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
A plain `--tls-hosts a,b` still means "decrypt exactly these", as it always has. Mixing the two
|
|
589
|
-
forms is refused rather than guessed at.
|
|
590
|
-
|
|
591
|
-
### An agent that is not on this machine
|
|
592
|
-
|
|
593
|
-
An agent in a dev container, on a VPS, or in someone's CI cannot be launched by agenticreplay, so there is
|
|
594
|
-
no environment for it to build. `agenticreplay attach` turns it around: agenticreplay holds the proxy open, prints
|
|
595
|
-
the block to paste on the far side, and records whatever arrives until you stop it.
|
|
596
|
-
|
|
597
|
-
```console
|
|
598
|
-
agenticreplay attach --for claude
|
|
599
|
-
agenticreplay attach --for claude --bind 0.0.0.0 --advertise host.docker.internal
|
|
600
|
-
agenticreplay attach --tls-intercept # for an agent over there that reads no variable either
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
```
|
|
604
|
-
info attached run=run_e3d64e15ee8f proxy=http://127.0.0.1:33437 for=claude-code
|
|
605
|
-
|
|
606
|
-
# in the sandbox, before starting your agent:
|
|
607
|
-
export ANTHROPIC_BASE_URL='http://127.0.0.1:33437'
|
|
608
|
-
|
|
609
|
-
Recording. Press ctrl-C when the agent is done.
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
The variables come from the same adapters `agenticreplay record` uses, so a sandbox recording cannot drift
|
|
613
|
-
from a local one. `--advertise` is required when you bind a wildcard: `0.0.0.0` is a statement about
|
|
614
|
-
listening, not an address anything can dial, and agenticreplay refuses to print a URL that cannot work.
|
|
615
|
-
|
|
616
|
-
Replaying such a run has the same problem in reverse — the agent may not exist on this machine — so
|
|
617
|
-
replay attaches too:
|
|
618
|
-
|
|
619
|
-
```console
|
|
620
|
-
agenticreplay attach --replay <run>
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
It serves the recording with egress blocked and takes the harness from the recording itself. Point
|
|
624
|
-
the same remote agent at it and the run happens again, offline, with no model called.
|
|
625
|
-
|
|
626
|
-
### A base-URL variable agenticreplay has never heard of
|
|
627
|
-
|
|
628
|
-
Enumerating them is hopeless. Hermes overrides per provider, and its own `.env.example` carries
|
|
629
|
-
`NOVITA_BASE_URL`, `GLM_BASE_URL`, `KIMI_BASE_URL`, `MINIMAX_BASE_URL`, `HF_BASE_URL`,
|
|
630
|
-
`NEBIUS_BASE_URL` and a dozen more. A list baked into agenticreplay would be stale the week after it was
|
|
631
|
-
written, so name the variable instead:
|
|
632
|
-
|
|
633
|
-
```console
|
|
634
|
-
AGENTICREPLAY_BASE_URL_VARS='CUSTOM_API_BASE_URL' agenticreplay record generic-openai -- my-agent
|
|
635
|
-
AGENTICREPLAY_BASE_URL_VARS='GLM_BASE_URL,KIMI_BASE_URL' agenticreplay record generic-openai -- my-agent
|
|
636
|
-
AGENTICREPLAY_BASE_URL_VARS='SOMETHING_BASE_URL=/' agenticreplay record node -- node agent.mjs
|
|
637
|
-
```
|
|
638
|
-
|
|
639
|
-
Each name is pointed at the proxy with `/v1` appended, which is what an OpenAI-compatible override
|
|
640
|
-
wants; `=<path>` overrides that, and `=/` gives the bare origin.
|
|
641
|
-
|
|
642
|
-
If you record an agent this way and it works, an adapter is about twenty lines —
|
|
643
|
-
[docs/plugins.md](docs/plugins.md). If it does not, the trace is the most useful thing you can send:
|
|
644
|
-
`agenticreplay export last -o run.html`.
|
|
645
|
-
|
|
646
|
-
## When the harness will not be redirected
|
|
647
|
-
|
|
648
|
-
Base-URL injection captures every harness that reads a base-URL variable, and the fetch hook covers
|
|
649
|
-
the Node ones that do not. A Codex CLI signed in with a ChatGPT subscription is neither — but not
|
|
650
|
-
for the reason this section used to give.
|
|
651
|
-
|
|
652
|
-
It *does* read a provider base URL, and an explicit one overrides the ChatGPT backend; both paths
|
|
653
|
-
even speak the same wire protocol. What is bound to that backend is the **credential**: a signed-in
|
|
654
|
-
subscription carries a plan token the ChatGPT backend accepts and `api.openai.com` does not. Point
|
|
655
|
-
the URL somewhere else and Codex asks for an API key instead — you are no longer recording the
|
|
656
|
-
subscription session, you are recording a different one. Codex is Rust, so there is no `fetch` of
|
|
657
|
-
ours to reach either.
|
|
658
|
-
|
|
659
|
-
`--tls-intercept` is the answer to that, and it is deliberately a separate decision you have to
|
|
660
|
-
make, because it mints a certificate authority.
|
|
661
|
-
|
|
662
|
-
```console
|
|
663
|
-
agenticreplay record codex --tls-intercept
|
|
664
|
-
agenticreplay record codex --tls-intercept --tls-hosts 'api.openai.com,*.chatgpt.com'
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
The CA is unique to the run, trusted only by the agent agenticreplay launches — through that child's own
|
|
668
|
-
environment, never a system or browser trust store — and deleted when the run ends. AgenticReplay will not
|
|
669
|
-
offer to install it anywhere. Hosts outside the allowlist are tunnelled unread and recorded as an
|
|
670
|
-
address and a byte count, with no path and no body, because agenticreplay never held the plaintext. Asking
|
|
671
|
-
to intercept everything is refused rather than honoured.
|
|
672
|
-
|
|
673
|
-
What comes back through it is not a log line. An intercepted request is parsed by the same wire
|
|
674
|
-
dialects as any other, so it lands in the trace as an ordinary exchange — replayable offline and
|
|
675
|
-
forkable to a different model, on a run that never had an API key of yours in it.
|
|
676
|
-
|
|
677
|
-
It works on `agenticreplay replay --model`, `agenticreplay fork` and `agenticreplay compare` too, which launch a live agent for
|
|
678
|
-
the same reason.
|
|
679
|
-
|
|
680
|
-
## For an agent, a script, or CI
|
|
681
|
-
|
|
682
|
-
A trace is a file, which is the one thing an observability dashboard cannot be — so the most useful
|
|
683
|
-
question about a failed run is one an *agent* can ask: *replay my last run and tell me what
|
|
684
|
-
diverged.* Every command answers as data, and agenticreplay serves itself over MCP.
|
|
685
|
-
|
|
686
|
-
```console
|
|
687
|
-
$ agenticreplay replay last --json
|
|
688
|
-
{"runId":"run_a278eea7b535","mode":"exact","traceRunId":"run_687e3f84b208","matchedExact":2,"divergences":0,"unmatched":0,"liveCalls":0,"exitCode":0}
|
|
689
|
-
|
|
690
|
-
$ agenticreplay show last --json | jq '.events[] | select(.kind == "TOOL")'
|
|
691
|
-
$ agenticreplay checkpoints last --json | jq '.[-1].seq'
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
One JSON document on stdout, diagnostics on stderr — including the recorded agent's own output, so
|
|
695
|
-
the document stays parseable while a run is talking. Failures answer in JSON too, with a non-zero
|
|
696
|
-
exit. `--json` covers `list`, `show`, `events`, `checkpoints`, `graph`, `record`, `replay`,
|
|
697
|
-
`compare` and `doctor`.
|
|
698
|
-
|
|
699
|
-
**As tools.** `agenticreplay mcp` serves the trace store to an agent over stdio:
|
|
700
|
-
|
|
701
|
-
```json
|
|
702
|
-
{ "mcpServers": { "agenticreplay": { "command": "agenticreplay", "args": ["mcp"] } } }
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
`agenticreplay_list_runs`, `agenticreplay_show_run`, `agenticreplay_checkpoints`, `agenticreplay_graph`, `agenticreplay_replay` and
|
|
706
|
-
`agenticreplay_compare`. Replay is free and offline; `agenticreplay_compare` says in its own description that it
|
|
707
|
-
spends real tokens, because a model choosing a tool reads that string and nothing else — and
|
|
708
|
-
`agenticreplay_graph` spends its description saying what `recorded` and `inferred` mean, for the same
|
|
709
|
-
reason.
|
|
710
|
-
|
|
711
|
-
**From code**, if you would rather not shell out:
|
|
712
|
-
|
|
713
|
-
```ts
|
|
714
|
-
import { AgenticReplay } from 'agenticreplay';
|
|
715
|
-
|
|
716
|
-
const agenticreplay = new AgenticReplay({ cwd: process.cwd() });
|
|
717
|
-
const { unmatched, divergences } = await agenticreplay.replay('last');
|
|
718
|
-
const timeline = await agenticreplay.show('last');
|
|
719
|
-
```
|
|
720
|
-
|
|
721
|
-
It never writes to your stdout and never calls `process.exit` — both asserted, because a library
|
|
722
|
-
that does either cannot be built on.
|
|
723
|
-
|
|
724
|
-
## Status
|
|
725
|
-
|
|
726
|
-
Early. `v0` is the walking skeleton of the three commands above. Everything below is exercised by
|
|
727
|
-
The test suite, the trace-format conformance check and a plugin-API neutrality check run on Node 24 and 26.
|
|
728
|
-
|
|
729
|
-
| Capability | State |
|
|
730
|
-
|---|---|
|
|
731
|
-
| Trace format v0 + JSON Schema | working |
|
|
732
|
-
| Anthropic / OpenAI-compatible model capture | working |
|
|
733
|
-
| OpenAI Responses API capture | working — the format the OpenAI Agents SDK and the Codex CLI default to. Records, replays offline and forks; a fork stays on the wire format the agent speaks |
|
|
734
|
-
| Agents that read no base-URL variable | working — `agenticreplay record node -- <cmd>` writes a preload into the run directory and redirects `globalThis.fetch` for an allowlist of provider hosts. Node and Bun both, since Bun ignores `--require` in `NODE_OPTIONS`. Use this when the origin is compiled into source; ordinary `@ai-sdk/openai` clients that honour `OPENAI_BASE_URL` go through `generic-openai` instead |
|
|
735
|
-
| A call agenticreplay cannot read | working — forwarded rather than refused, and recorded as `net.request` / `net.response`: evidence, not a replayable turn. A recording that captured nothing warns instead of exiting clean |
|
|
736
|
-
| Machine-readable output (`--json`) | working — one JSON document on stdout, diagnostics on stderr, failures as JSON |
|
|
737
|
-
| Causal graph (`agenticreplay graph`) | working — what caused what, as a table or as JSON. Every edge says whether the trace recorded it or agenticreplay derived it just now, and names the rule either way. `--to N` narrows to the chain that produced one event |
|
|
738
|
-
| Shareable cards | working — `agenticreplay export --card` draws one causal chain, `--graph-card` draws the whole run with that chain lit, and `compare --share` draws the verdict table. `.svg` always; `.png` and `.gif` when the optional render toolchain is installed, which `agenticreplay doctor` reports and `npm ci` never pulls in |
|
|
739
|
-
| First run without an agent (`agenticreplay quickstart`) | working — the package carries a real recording and the project it was made against, and replays one over the other offline, so the first look costs no key, no network and no tokens |
|
|
740
|
-
| MCP server (`agenticreplay mcp`) | working — six tools over stdio, so an agent can read, explain and replay its own runs |
|
|
741
|
-
| Programmatic API (`AgenticReplay`) | working — the commands render what it returns, so the terminal is a view of one source of truth |
|
|
742
|
-
| Replaying a session you typed into | working, and approximate — [what that means](#replaying-a-session-you-typed-into) |
|
|
743
|
-
| Exact replay with divergence reporting | working — restores the recorded filesystem over your working tree, then puts it back; `--worktree` for a scratch copy, `--in-place` to restore nothing. Writes a run of its own recording what the replay *discovered* — divergences, unmatched requests — and points at the parent for what it merely repeated; `--no-trace` to skip |
|
|
744
|
-
| Fork replay from a checkpoint | working — a fork records its own filesystem snapshots, so it is a run you can fork again |
|
|
745
|
-
| Compare across models | working — `agenticreplay setup` stores an explicitly selected gateway, key and model list, so `agenticreplay compare` needs no flags |
|
|
746
|
-
| Filesystem snapshots and diffs | working |
|
|
747
|
-
| Single-file HTML export | working |
|
|
748
|
-
| MCP call recording | working — opt in with `--mcp-config <path>`. Replay and fork re-instrument from the config the recording used, so the layer does not stop at the fork point |
|
|
749
|
-
| Post-hoc scrubbing (`agenticreplay scrub`) | working |
|
|
750
|
-
| Shell capture (`PATH` shim) | working — exit codes, duration and the stdout/stderr split. `--no-shell` to skip |
|
|
751
|
-
| Non-model network capture | working — opt in with `--tls-intercept`; mints a per-run CA the launched agent alone trusts, decrypts an allowlist of hosts, tunnels the rest unread, and deletes the key when the run ends |
|
|
752
|
-
| Codex subscription model capture/replay | working — recognizes the `/backend-api/codex/responses` HTTPS fallback, decodes zstd request bodies for matching, and serves the recorded SSE response without opening the origin during replay |
|
|
753
|
-
| Validated against a real agent | Claude Code, recording a real fix to a real bug: recorded, replayed offline end to end, forked from a checkpoint and exported. It broke four things no fixture could have produced, all since fixed — [what a real agent found](docs/validation.md) |
|
|
754
|
-
| Subscription-auth harnesses | Claude Code works. A Codex CLI signed in with a ChatGPT subscription carries a plan credential only its own backend accepts, so redirecting the URL would record a different session rather than that one: it needs `--tls-intercept`. With an API key it needs nothing special |
|
|
755
|
-
|
|
756
|
-
## Replaying a session you typed into
|
|
757
|
-
|
|
758
|
-
A run started as `agenticreplay record claude` and driven by hand has its prompts nowhere on the wire. AgenticReplay
|
|
759
|
-
recovers them from the harness's own transcript and replays the session by handing them back
|
|
760
|
-
without a terminal — which reproduces the conversation, and does not reproduce the run byte for
|
|
761
|
-
byte. Two differences are inherent rather than defects, and agenticreplay names both rather than papering
|
|
762
|
-
over them:
|
|
3
|
+
<sub>**English** · [简体中文](docs/i18n/README.zh-CN.md) · [日本語](docs/i18n/README.ja.md) · [한국어](docs/i18n/README.ko.md) · [Deutsch](docs/i18n/README.de.md) · [Français](docs/i18n/README.fr.md) · [Español](docs/i18n/README.es.md) · [العربية](docs/i18n/README.ar.md)</sub>
|
|
763
4
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
interactive recording is a complete replay of what you asked, not a partial one.
|
|
768
|
-
- **A terminal session is offered tools a replay cannot have.** `AskUserQuestion`, `EnterPlanMode`,
|
|
769
|
-
`ExitPlanMode`, `EndConversation` all need a person in front of them, so they are absent when the
|
|
770
|
-
same agent is driven without one. Their schemas are large, so a request carrying them can differ
|
|
771
|
-
from the recorded one by enough to halt; the halt says which tools are missing and why.
|
|
5
|
+
AgenticReplay logs AI API traffic to help debug and improve AI harnesses. It captures requests
|
|
6
|
+
and responses from supported clients, including reasoning/thinking content or summaries returned
|
|
7
|
+
by the API.
|
|
772
8
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
exactly. Reading a run is unaffected either way: `agenticreplay show` and the viewer are complete for both.
|
|
9
|
+
Compare the trace with tool results, captured shell commands and file changes to find where a run
|
|
10
|
+
went wrong.
|
|
776
11
|
|
|
777
12
|
## Install
|
|
778
13
|
|
|
779
|
-
|
|
780
|
-
npm i -g agenticreplay
|
|
781
|
-
agenticreplay doctor # checks node, git, and which agents it can find
|
|
782
|
-
```
|
|
783
|
-
|
|
784
|
-
Node 24 or newer. No native dependencies, so there is nothing to compile and nothing fetched at
|
|
785
|
-
install time beyond the tarballs themselves.
|
|
786
|
-
|
|
787
|
-
The CLI package bundles its runtime dependencies, including the internal workspace libraries.
|
|
788
|
-
|
|
789
|
-
**From source**, to work on it or to run an unreleased commit:
|
|
790
|
-
|
|
791
|
-
```console
|
|
792
|
-
git clone https://github.com/askdkc/AgenticReplay && cd AgenticReplay
|
|
793
|
-
npm ci && npm run build
|
|
794
|
-
npm install -g . # puts `agenticreplay` on PATH
|
|
795
|
-
```
|
|
796
|
-
|
|
797
|
-
To release from the repository root:
|
|
14
|
+
Requires Node.js 24+, Git, and a configured AI harness.
|
|
798
15
|
|
|
799
16
|
```console
|
|
17
|
+
git clone https://github.com/askdkc/AgenticReplay.git
|
|
18
|
+
cd AgenticReplay
|
|
800
19
|
npm ci
|
|
801
|
-
npm
|
|
20
|
+
npm run build
|
|
21
|
+
npm install -g .
|
|
22
|
+
agenticreplay doctor
|
|
802
23
|
```
|
|
803
24
|
|
|
804
|
-
|
|
805
|
-
the binary, quickstart assets, and runtime dependencies before uploading them. To create a local
|
|
806
|
-
archive without uploading, run `npm pack`; it writes `agenticreplay-<version>.tgz`. Adding
|
|
807
|
-
`--dry-run` only previews the archive. `node scripts/installed-smoke.mjs` verifies one actual root
|
|
808
|
-
archive in a clean offline installation.
|
|
809
|
-
|
|
810
|
-
**Node 24+ to run it** (the CLI's `engines` says `>=24.0.0`). Development and CI use Node 24 or
|
|
811
|
-
26. No account, no signup, no API key changes.
|
|
812
|
-
|
|
813
|
-
On Windows, shell capture writes `.cmd` shims in front of `sh.exe` and `bash.exe` (Git for Windows
|
|
814
|
-
provides both), and Windows runs a `.cmd` only through a shell. So it captures a shell started
|
|
815
|
-
through cmd.exe or PowerShell, and not one an agent starts itself: a Node harness's
|
|
816
|
-
`child_process.spawn('bash')` goes straight to the real `bash.exe`, and Claude Code finds its shell
|
|
817
|
-
without consulting `PATH` at all. `agenticreplay doctor` says which of the two holds on the machine, and
|
|
818
|
-
`agenticreplay record` warns `shell.ineffective` on any run whose tool calls ran commands the shim never
|
|
819
|
-
saw — record those with `--no-shell`. If no POSIX shell is on `PATH`, doctor warns and
|
|
820
|
-
`--no-shell` is the way to record.
|
|
821
|
-
|
|
822
|
-
## Where your runs are kept
|
|
823
|
-
|
|
824
|
-
Everything lands in **`.agenticreplay/runs/` inside the project you recorded in** — per-project, never a
|
|
825
|
-
global store, so a run travels with the checkout it belongs to. One run directory is one
|
|
826
|
-
self-describing thing:
|
|
827
|
-
|
|
828
|
-
```
|
|
829
|
-
.agenticreplay/
|
|
830
|
-
.gitignore # just `*` — the store excludes itself, so a trace cannot be committed by accident
|
|
831
|
-
runs/run_d0a2ee7ce615/
|
|
832
|
-
manifest.json # who, when, which adapter, the git commit, counts, integrity digest
|
|
833
|
-
events.jsonl # the timeline, one JSON object per line, append-only
|
|
834
|
-
blobs/ # content-addressed payloads over 4 KB, deduplicated
|
|
835
|
-
fs/ # shadow git index: the workspace at every turn
|
|
836
|
-
shims/ # the PATH shims this run used; written by agenticreplay, read by nothing
|
|
837
|
-
py/ # bootstrap for the agent-structure layer, when the agent is Python
|
|
838
|
-
redactions.json # what was removed, by rule and count — never by value
|
|
839
|
-
```
|
|
25
|
+
## Use
|
|
840
26
|
|
|
841
|
-
|
|
27
|
+
Run these commands in the project you want to investigate:
|
|
842
28
|
|
|
843
29
|
```console
|
|
844
|
-
agenticreplay
|
|
845
|
-
agenticreplay
|
|
846
|
-
agenticreplay
|
|
847
|
-
agenticreplay replay last # `last` = newest recording (it skips replay traces)
|
|
848
|
-
agenticreplay replay run_d0a2ee7ce615 # or name one outright
|
|
849
|
-
agenticreplay gc --older-than 7d --dry-run # what would be reclaimed, before anything is
|
|
30
|
+
agenticreplay record claude -- -p "Investigate the failing test"
|
|
31
|
+
agenticreplay show last
|
|
32
|
+
agenticreplay export last -o run.html
|
|
850
33
|
```
|
|
851
34
|
|
|
852
|
-
|
|
853
|
-
`.agenticreplay/runs/`
|
|
854
|
-
|
|
855
|
-
answer, holding nothing.
|
|
35
|
+
Record the harness process that sends the API requests, such as Claude Code. Logs are stored in
|
|
36
|
+
`.agenticreplay/runs/` in that project; `last` selects the newest recording. Open `run.html` in a
|
|
37
|
+
browser to inspect the trace.
|
|
856
38
|
|
|
857
|
-
|
|
39
|
+
Capture directories use `YYYYMMDDHHmmss-agent-title-id` (UTC). Names contain only lowercase
|
|
40
|
+
ASCII letters, digits and hyphens: at most 255 characters, with titles capped at 80.
|
|
41
|
+
Titles come from the session or, if unavailable, the first user prompt; they are redacted and
|
|
42
|
+
transliterated to ASCII. The final name is applied when recording closes. Existing `run_<id>`
|
|
43
|
+
directories and commands using a run ID remain supported.
|
|
858
44
|
|
|
859
|
-
|
|
860
|
-
redacted in the write path: environment capture is deny-by-default, auth headers are never written,
|
|
861
|
-
and known key shapes plus high-entropy strings are replaced with stable placeholders.
|
|
862
|
-
|
|
863
|
-
Redaction is best-effort mitigation, not a guarantee. **Treat a trace as sensitive** — roughly as
|
|
864
|
-
sensitive as a shell history plus a heap dump.
|
|
45
|
+
For a custom harness that reads `OPENAI_BASE_URL`, replace `node agent.js` with its launch command:
|
|
865
46
|
|
|
866
47
|
```console
|
|
867
|
-
agenticreplay
|
|
868
|
-
agenticreplay scrub last --match my-hostname # remove something after the fact
|
|
48
|
+
agenticreplay record generic-openai -- node agent.js
|
|
869
49
|
```
|
|
870
50
|
|
|
871
|
-
|
|
872
|
-
detectors, refreshes the integrity digest, and leaves binary blobs byte-identical.
|
|
873
|
-
|
|
874
|
-
It cannot rewrite the filesystem snapshots. Git objects are addressed by the hash of their own
|
|
875
|
-
contents, so editing one changes its id, which forces every tree naming it to be rewritten and
|
|
876
|
-
every event naming those trees after that — a history rewrite whose failure mode is a run that no
|
|
877
|
-
longer restores. So scrub *searches* the snapshot store and tells you when your string is still in
|
|
878
|
-
there, rather than reporting a clean trace it could not clean. `--drop-fs` deletes the store
|
|
879
|
-
outright, at the cost of being able to fork the run.
|
|
880
|
-
|
|
881
|
-
## What is open, and what is not
|
|
882
|
-
|
|
883
|
-
Always open, under Apache-2.0: the trace format, the core, the CLI, the viewer, the adapters, and
|
|
884
|
-
the provider interface.
|
|
885
|
-
|
|
886
|
-
AgenticReplay is a fork of [OrcaReplay](https://github.com/Continuum-AI-Corp/OrcaReplay).
|
|
887
|
-
Original copyright notices and the Apache-2.0 license are retained. This fork uses its own package
|
|
888
|
-
names, configuration and trace format, and does not promote or select a gateway by default.
|
|
889
|
-
|
|
890
|
-
Provider plugins use the public `Provider` interface in `@agenticreplay/plugin-api`.
|
|
891
|
-
No vendor
|
|
892
|
-
plugin exists yet, so the CI job that enforces this (`scripts/check-neutrality.mjs`) says so and
|
|
893
|
-
passes as a no-op; it starts building against the published package rather than workspace source the
|
|
894
|
-
moment one lands. If a plugin ever needs a capability, that capability goes into the public
|
|
895
|
-
interface first, with a second implementation showing it is not shaped around one vendor.
|
|
896
|
-
|
|
897
|
-
## Documentation
|
|
898
|
-
|
|
899
|
-
**Start here if you have a problem right now:**
|
|
51
|
+
For Codex with a ChatGPT subscription, use TLS interception:
|
|
900
52
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
**Reference:**
|
|
906
|
-
|
|
907
|
-
- [`spec/agenticreplay-trace-v0.md`](spec/agenticreplay-trace-v0.md) — the normative trace format
|
|
908
|
-
- [`docs/integrations.md`](docs/integrations.md) — recording a framework, one command each, every number asserted in CI
|
|
909
|
-
- [`docs/architecture.md`](docs/architecture.md) — how capture, replay and fork actually work
|
|
910
|
-
- [`docs/validation.md`](docs/validation.md) — what broke the first time this met a real agent
|
|
911
|
-
- [`docs/launch-path.md`](docs/launch-path.md) — what is built, what is not, and what is next
|
|
912
|
-
- [`docs/plugins.md`](docs/plugins.md) — writing an adapter or a provider
|
|
913
|
-
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — five-minute dev loop
|
|
914
|
-
- [Good first issues](docs/good-first-issues.md) — twelve of them, with the file to start in
|
|
915
|
-
|
|
916
|
-
## Help wanted
|
|
917
|
-
|
|
918
|
-
The format is v0 and the walking skeleton works, which is the interesting point in a project's life:
|
|
919
|
-
the decisions are still cheap to change and there is a lot of obvious work with the file to start in
|
|
920
|
-
already written down.
|
|
53
|
+
```console
|
|
54
|
+
agenticreplay record exec --tls-intercept -- codex
|
|
55
|
+
```
|
|
921
56
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
about twenty lines — [docs/plugins.md](docs/plugins.md). If it does not, `node` may already cover
|
|
925
|
-
it; a recording that comes back empty from a harness not listed [above](#which-agents) is worth
|
|
926
|
-
an issue either way.
|
|
927
|
-
- **Add a framework to the integration checks.** `test/integrations/` records a real framework
|
|
928
|
-
against a stub origin, kills the origin and replays it. Eight are covered; AutoGen, LlamaIndex,
|
|
929
|
-
Pydantic AI and smolagents are not. A new one is a script and a row.
|
|
930
|
-
- **Reimplement the reader.** The spec is CC BY 4.0 on purpose. There is already a Python reader;
|
|
931
|
-
Go and Rust are open.
|
|
932
|
-
- **Break the replay.** The matching ladder is the heart of this and the fastest way to improve it
|
|
933
|
-
is a real recording it gets wrong. Open an issue with `agenticreplay export last -o bug.html` attached — it
|
|
934
|
-
is one self-contained file, and `agenticreplay scrub` is there for anything you need out of it first.
|
|
57
|
+
Recording runs the harness and may incur API charges. Redaction is best-effort: traces may still
|
|
58
|
+
contain sensitive data. Review them before sharing.
|
|
935
59
|
|
|
936
|
-
|
|
60
|
+
See [integrations](docs/integrations.md) for other harnesses and
|
|
61
|
+
[debugging a failing run](docs/how-to/debug-a-failing-agent-run.md) for an example.
|
|
937
62
|
|
|
938
63
|
## License
|
|
939
64
|
|
|
940
|
-
Apache-2.0
|
|
941
|
-
|
|
65
|
+
Apache-2.0. Trace specification: CC BY 4.0.
|