orcareplay 0.2.2 → 0.2.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.
Files changed (68) hide show
  1. package/dist/args.d.ts +33 -0
  2. package/dist/args.d.ts.map +1 -1
  3. package/dist/args.js +41 -5
  4. package/dist/args.js.map +1 -1
  5. package/dist/commands/attach.js +15 -4
  6. package/dist/commands/attach.js.map +1 -1
  7. package/dist/commands/inspect.d.ts +11 -0
  8. package/dist/commands/inspect.d.ts.map +1 -1
  9. package/dist/commands/inspect.js +43 -0
  10. package/dist/commands/inspect.js.map +1 -1
  11. package/dist/commands/quickstart.d.ts +48 -0
  12. package/dist/commands/quickstart.d.ts.map +1 -0
  13. package/dist/commands/quickstart.js +373 -0
  14. package/dist/commands/quickstart.js.map +1 -0
  15. package/dist/commands/record.d.ts +13 -0
  16. package/dist/commands/record.d.ts.map +1 -1
  17. package/dist/commands/record.js +119 -8
  18. package/dist/commands/record.js.map +1 -1
  19. package/dist/commands/replay.d.ts.map +1 -1
  20. package/dist/commands/replay.js +439 -283
  21. package/dist/commands/replay.js.map +1 -1
  22. package/dist/commands/scrub.d.ts.map +1 -1
  23. package/dist/commands/scrub.js +182 -47
  24. package/dist/commands/scrub.js.map +1 -1
  25. package/dist/commands/setup.d.ts.map +1 -1
  26. package/dist/commands/setup.js +67 -4
  27. package/dist/commands/setup.js.map +1 -1
  28. package/dist/exchange-events.d.ts.map +1 -1
  29. package/dist/exchange-events.js +23 -0
  30. package/dist/exchange-events.js.map +1 -1
  31. package/dist/flags.d.ts.map +1 -1
  32. package/dist/flags.js +68 -2
  33. package/dist/flags.js.map +1 -1
  34. package/dist/main.d.ts.map +1 -1
  35. package/dist/main.js +32 -0
  36. package/dist/main.js.map +1 -1
  37. package/dist/tls-capture.d.ts +10 -2
  38. package/dist/tls-capture.d.ts.map +1 -1
  39. package/dist/tls-capture.js +41 -3
  40. package/dist/tls-capture.js.map +1 -1
  41. package/dist/upstream.d.ts.map +1 -1
  42. package/dist/upstream.js +17 -0
  43. package/dist/upstream.js.map +1 -1
  44. package/package.json +13 -12
  45. package/quickstart/project/agent.mjs +128 -0
  46. package/quickstart/project/package.json +8 -0
  47. package/quickstart/project/src/schedule.js +41 -0
  48. package/quickstart/project/test/schedule.test.js +25 -0
  49. package/quickstart/trace/blobs/f7/f7c1592306093f65f441d2afd5f496b7635b7e18f5c15f55b11f71f0b552a118 +1 -0
  50. package/quickstart/trace/events.jsonl +19 -0
  51. package/quickstart/trace/fs/HEAD +1 -0
  52. package/quickstart/trace/fs/config +11 -0
  53. package/quickstart/trace/fs/description +1 -0
  54. package/quickstart/trace/fs/index +0 -0
  55. package/quickstart/trace/fs/info/exclude +11 -0
  56. package/quickstart/trace/fs/objects/1b/1f956f306fe2c66611dd9e280e7fd2dce1ecda +0 -0
  57. package/quickstart/trace/fs/objects/1c/c77eaef0608e088283cb62876b80be9473edce +0 -0
  58. package/quickstart/trace/fs/objects/20/0c1f7a4be93b037ff27cea57853a738fcbf744 +0 -0
  59. package/quickstart/trace/fs/objects/20/28a902b70fff80fe02a8aaaf30005fd91bafb7 +0 -0
  60. package/quickstart/trace/fs/objects/34/522daa46d0e3f8db09ec7d874fa6f05cbcdabf +0 -0
  61. package/quickstart/trace/fs/objects/48/a7bdafdaae1c53a354c7bd06c740719e1cb255 +0 -0
  62. package/quickstart/trace/fs/objects/80/13b71377c59b728861dd8eec39a919387a4b81 +0 -0
  63. package/quickstart/trace/fs/objects/9f/db6ace4a4410e535a2aef50e653e46a4c3694a +0 -0
  64. package/quickstart/trace/fs/objects/a4/371e26fb48bece62891a54e4374895fba69f0d +0 -0
  65. package/quickstart/trace/fs/objects/d3/3088476a7db5aef2dfd6c839d16d2b2ccaca40 +0 -0
  66. package/quickstart/trace/manifest.json +43 -0
  67. package/quickstart/trace/redactions.json +4 -0
  68. package/quickstart/trace/shell-frames.jsonl +0 -0
@@ -0,0 +1,373 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { cp, mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { promisify } from 'node:util';
6
+ import { parseArgs } from '../args.js';
7
+ import { Output } from '../out.js';
8
+ import { replayCommand } from './replay.js';
9
+ import { showCommand } from './inspect.js';
10
+ /**
11
+ * The first two minutes, with nothing installed and nothing spent.
12
+ *
13
+ * Everything orca does well is visual — a timeline, a causal chain, a comparison — and none of it
14
+ * can be seen until a run exists. Producing one costs an agent, a key, a network and real tokens,
15
+ * so the shortest honest path from `npm i` to "oh, I see what this is" ran through four things a
16
+ * person has to arrange first. Most of them do not.
17
+ *
18
+ * So the package carries a run. A real one: a small agent, a real model, a genuine bug in
19
+ * timezone arithmetic that it genuinely fixes. `quickstart` puts the project and the recording
20
+ * somewhere, then replays the recording against the project with the network off — which is the
21
+ * product's own guarantee doing the work of its own demonstration.
22
+ *
23
+ * The agent it was recorded from is a Node script that ships with it, which is the reason replay
24
+ * needs nothing installed: orca already requires the runtime that agent runs on.
25
+ *
26
+ * The default output fits on one screen, and that is the whole design of it. The first version
27
+ * printed the full timeline and the replayed agent's own chatter — sixty-eight lines, with the
28
+ * failing test count and the passing one fifty lines apart. The comparison is the point, and a
29
+ * comparison you have to scroll between is not one. `--full` still prints everything.
30
+ */
31
+ /** Where the shipped asset lives, relative to the built `dist/commands/`. */
32
+ const ASSET = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'quickstart');
33
+ const DEFAULT_DIR = 'orca-quickstart';
34
+ const exec = promisify(execFile);
35
+ async function events() {
36
+ const jsonl = await readFile(join(ASSET, 'trace', 'events.jsonl'), 'utf8');
37
+ return jsonl
38
+ .split('\n')
39
+ .filter((l) => l.trim())
40
+ .map((l) => JSON.parse(l));
41
+ }
42
+ /** Read the run id out of the shipped trace, so nothing has to hardcode it. */
43
+ async function shippedRunId() {
44
+ const manifest = JSON.parse(await readFile(join(ASSET, 'trace', 'manifest.json'), 'utf8'));
45
+ const id = manifest.run_id;
46
+ if (id === undefined)
47
+ throw new Error('the shipped quickstart trace has no run_id');
48
+ return id;
49
+ }
50
+ /**
51
+ * Whether it is safe to write a project into this path.
52
+ *
53
+ * Only a missing path counts as free. Swallowing every error meant a path that exists and is not a
54
+ * directory read as empty, so `orca quickstart --dir notes.txt` skipped the sentence that explains
55
+ * the problem and surfaced `ENOTDIR: not a directory, mkdir` from three lines further on.
56
+ */
57
+ async function isEmptyish(dir) {
58
+ try {
59
+ return (await readdir(dir)).length === 0;
60
+ }
61
+ catch (err) {
62
+ return err.code === 'ENOENT';
63
+ }
64
+ }
65
+ /**
66
+ * Tell the trace where it now lives.
67
+ *
68
+ * The shipped manifest names a neutral directory, because a trace records the absolute path it was
69
+ * captured in and publishing one on npm would publish that. But `replay` compares that path against
70
+ * the directory it is running in, and on a mismatch it declines to restore the recorded filesystem
71
+ * — correctly, since restoring a stranger's tree over yours is not a thing to do on a guess.
72
+ *
73
+ * Left alone, that lands on the first person who follows the tour. `orca quickstart` replays with
74
+ * `--in-place` and is unaffected, but the obvious next thing to type is the command from the top of
75
+ * the README, and `orca replay last` in the quickstart directory reported `exact=1` with two
76
+ * divergences and a warning — the flagship guarantee failing on the second look, against a tree the
77
+ * first replay had already changed.
78
+ *
79
+ * So the path is rewritten as the trace is placed. The asset on npm stays neutral, and the copy on
80
+ * disk is honest about where it is. Safe to edit because the integrity digest covers
81
+ * `events.jsonl`, and the replay matcher reads request bodies — neither reads this field.
82
+ */
83
+ async function adoptTrace(runDir, target) {
84
+ const path = join(runDir, 'manifest.json');
85
+ const manifest = JSON.parse(await readFile(path, 'utf8'));
86
+ manifest['cwd'] = target;
87
+ await writeFile(path, `${JSON.stringify(manifest, null, 2)}
88
+ `, 'utf8');
89
+ }
90
+ /**
91
+ * The project's own test suite, summarised.
92
+ *
93
+ * Run before and after, because the arc is the demo: two tests fail, a recording of an agent
94
+ * fixing them is replayed with the network off, and the same two pass. Telling someone to go and
95
+ * check afterwards does not work — by then the replay has already written the fix, so the
96
+ * instruction to "see two failures" would show them four passes.
97
+ */
98
+ async function testSummary(dir) {
99
+ let tap = '';
100
+ try {
101
+ // The reporter is named, and the environment that could name a second one is taken away.
102
+ //
103
+ // The counts are read out of TAP, and node picks its reporter from the ambient default and
104
+ // from `NODE_OPTIONS`. A developer with `--test-reporter=spec` set — an ordinary thing to have
105
+ // — got `? passing, ? failing` for both halves of the before-and-after, from a command that
106
+ // still reported success. Naming `tap` on the command line does not fix that on its own:
107
+ // reporters accumulate, and two of them with one destination makes node refuse to start at
108
+ // all. So the variable goes, which is right regardless — this is orca running a fixed test
109
+ // file of its own to read two numbers out of it, not the user running their suite.
110
+ ({ stdout: tap } = await exec(process.execPath, ['--test', '--test-reporter=tap', 'test/schedule.test.js'], { cwd: dir, env: withoutNodeOptions(process.env) }));
111
+ }
112
+ catch (err) {
113
+ tap = err.stdout ?? '';
114
+ }
115
+ const pass = /^# pass (\d+)/m.exec(tap)?.[1] ?? '?';
116
+ const fail = /^# fail (\d+)/m.exec(tap)?.[1] ?? '?';
117
+ const failing = tap
118
+ .split('\n')
119
+ .filter((l) => l.startsWith('not ok'))
120
+ .map((l) => l.replace(/^not ok \d+ - /, ''));
121
+ return { line: `${pass} passing, ${fail} failing`, failing };
122
+ }
123
+ /** A copy of the environment with `NODE_OPTIONS` removed. */
124
+ function withoutNodeOptions(env) {
125
+ const copy = { ...env };
126
+ delete copy['NODE_OPTIONS'];
127
+ return copy;
128
+ }
129
+ /** What the recording did, in three lines rather than nineteen. */
130
+ export function recordingSummary(all) {
131
+ const turns = all.filter((e) => e.type === 'model.response');
132
+ const model = String(turns[0]?.attrs?.['model'] ?? 'a model').replace(/-\d{4}-\d{2}-\d{2}$/, '');
133
+ const tokensIn = turns.reduce((n, e) => n + Number(e.attrs?.['input_tokens'] ?? 0), 0);
134
+ const tokensOut = turns.reduce((n, e) => n + Number(e.attrs?.['output_tokens'] ?? 0), 0);
135
+ const read = new Set();
136
+ for (const e of all) {
137
+ if (e.type !== 'tool.call')
138
+ continue;
139
+ const input = e.attrs?.['input'];
140
+ if (e.attrs?.['name'] === 'read_file' && input?.path)
141
+ read.add(input.path);
142
+ }
143
+ // Straight off each event. Reading the path out and then looking the event back up gives two
144
+ // changes to one file the *first* one's counts twice, which is a summary that quietly lies.
145
+ const wrote = all
146
+ .filter((e) => e.type === 'fs.change')
147
+ .map((e) => {
148
+ const a = e.attrs ?? {};
149
+ return `${String(a['path'])} (+${a['insertions']} −${a['deletions']})`;
150
+ });
151
+ const lines = [
152
+ `${all.length} events · ${turns.length} model turns · ${model} · ${tokensIn.toLocaleString()} in · ${tokensOut.toLocaleString()} out`,
153
+ ];
154
+ if (read.size > 0)
155
+ lines.push(`read ${[...read].join(', ')}`);
156
+ if (wrote.length > 0)
157
+ lines.push(`wrote ${wrote.join(', ')}`);
158
+ return lines;
159
+ }
160
+ /** Files the recording changed on disk. */
161
+ function writtenPaths(all) {
162
+ return all
163
+ .filter((e) => e.type === 'fs.change')
164
+ .map((e) => String(e.attrs?.['path']))
165
+ .filter((p) => p !== 'undefined');
166
+ }
167
+ /**
168
+ * What the model said, from the recording rather than from the replayed process.
169
+ *
170
+ * Trimmed to its first sentences and labelled. Unlabelled, eleven lines of an assistant explaining
171
+ * daylight saving read as orca talking — and the recorded reply ends with "If you want, I can also
172
+ * explain the DST edge case in more detail", which in an offline replay invites the obvious
173
+ * question of who it is addressed to.
174
+ */
175
+ export function modelSaid(all, lines) {
176
+ const alreadySaid = writtenPaths(all);
177
+ for (const e of [...all].reverse()) {
178
+ if (e.type !== 'model.response' || !e.payload)
179
+ continue;
180
+ try {
181
+ const body = JSON.parse(e.payload);
182
+ const text = body.choices?.[0]?.message?.content;
183
+ if (!text)
184
+ continue;
185
+ const kept = text
186
+ .split('\n')
187
+ .map((l) => l.trim())
188
+ .filter(Boolean)
189
+ // Two lines of excerpt is no place to repeat what the summary three lines above already
190
+ // said. A section header on its own carries nothing, and "Fixed src/schedule.js." is the
191
+ // `wrote src/schedule.js` line again — the excerpt is here for the why, not the what.
192
+ .filter((l) => lines > 4 || (!l.endsWith(':') && !restatesTheSummary(l, alreadySaid)))
193
+ .map((l) => (lines > 4 ? l : plain(l)));
194
+ return kept.slice(0, lines);
195
+ }
196
+ catch {
197
+ return [];
198
+ }
199
+ }
200
+ return [];
201
+ }
202
+ /**
203
+ * Whether a line only names a file the summary has already reported as written.
204
+ *
205
+ * Short and path-shaped: "Fixed `src/schedule.js`." carries nothing the `wrote` line above did not,
206
+ * and length is what separates it from a sentence that happens to mention the same file while
207
+ * saying something about it.
208
+ */
209
+ function restatesTheSummary(line, written) {
210
+ const flat = plain(line);
211
+ return flat.length < 60 && written.some((p) => flat.includes(p));
212
+ }
213
+ /**
214
+ * The excerpt as it is printed: a label and its lines, or nothing at all.
215
+ *
216
+ * Nothing at all is a real outcome, not a defensive branch. The lines are filtered — section
217
+ * headers dropped, restatements of the file the summary already reported dropped — so a reply made
218
+ * entirely of those leaves the label standing over empty space, which reads as output that went
219
+ * missing rather than as output that was never worth printing.
220
+ */
221
+ export function excerptBlock(all, lines) {
222
+ const said = modelSaid(all, lines);
223
+ if (said.length === 0)
224
+ return [];
225
+ return [
226
+ ' the model, out of the recording:',
227
+ ...said.map((l) => ` ${l}`),
228
+ ];
229
+ }
230
+ /** Markdown emphasis and bullet syntax removed, clipped to something a terminal can hold. */
231
+ function plain(line) {
232
+ const flat = line
233
+ .replace(/^[-*]\s+/, '')
234
+ .replace(/\*\*(.+?)\*\*/g, '$1')
235
+ .replace(/`(.+?)`/g, '$1');
236
+ return flat.length > 84 ? `${flat.slice(0, 83)}…` : flat;
237
+ }
238
+ /**
239
+ * Whether the replay demonstrated the thing, rather than merely failing to fail.
240
+ *
241
+ * `turns > 0` is the clause that is easy to leave out and is the whole point. Without it an empty
242
+ * recording makes `0 === 0` true, and the command congratulates itself on a run that never
243
+ * happened — which is what it did, over a project whose tests were still red, until a directory
244
+ * with a space in its name stopped the agent from starting at all.
245
+ */
246
+ export function replayDemonstrated(turns, served, unmatched) {
247
+ return turns > 0 && served === turns && unmatched === 0;
248
+ }
249
+ export async function quickstartCommand(args, out, cwd = process.cwd()) {
250
+ const target = resolve(cwd, args.str('dir') ?? DEFAULT_DIR);
251
+ const full = args.bool('full');
252
+ const runId = await shippedRunId();
253
+ // Refusing rather than merging: this writes a source tree and a trace, and quietly mixing those
254
+ // into a directory someone already has work in is the kind of help nobody asks for twice.
255
+ if (!(await isEmptyish(target))) {
256
+ throw new Error(`${target} already exists and is not an empty directory\n` +
257
+ ' pick somewhere else: orca quickstart --dir <path>');
258
+ }
259
+ await mkdir(join(target, '.orca', 'runs'), { recursive: true });
260
+ await cp(join(ASSET, 'project'), target, { recursive: true });
261
+ const runDir = join(target, '.orca', 'runs', runId);
262
+ await cp(join(ASSET, 'trace'), runDir, { recursive: true });
263
+ await adoptTrace(runDir, target);
264
+ const all = await events();
265
+ const before = await testSummary(target);
266
+ out.info('quickstart.ready', { dir: target, run: runId });
267
+ out.plain('');
268
+ out.plain(' A reminder scheduler with a bug in it, and a recording of an agent fixing it.');
269
+ out.plain(' Nothing here needs a key, a network, or an agent installed.');
270
+ out.plain('');
271
+ out.plain(` Before ${before.line}`);
272
+ for (const f of before.failing)
273
+ out.plain(` ✗ ${f}`);
274
+ out.plain('');
275
+ const summary = recordingSummary(all);
276
+ out.plain(` Recorded ${summary[0]}`);
277
+ for (const l of summary.slice(1))
278
+ out.plain(` ${l}`);
279
+ out.plain('');
280
+ const replayed = await runReplay(target, runId, out, full);
281
+ // The denominator is the recording's, not the replay's. `matchedExact + unmatched` is the number
282
+ // of turns the replay was asked about, which is zero when the agent never started — and `0/0`
283
+ // reads like a pass. Counting the model turns in the trace makes that case print `0/3`.
284
+ const turns = all.filter((e) => e.type === 'model.response').length;
285
+ const ok = replayDemonstrated(turns, replayed.matchedExact, replayed.unmatched);
286
+ if (full)
287
+ out.plain('');
288
+ out.plain(` Replayed ${replayed.matchedExact}/${turns} turns from the trace · ` +
289
+ `${replayed.divergences} divergences · ${replayed.liveCalls} live calls · network blocked`);
290
+ out.plain('');
291
+ const excerpt = excerptBlock(all, full ? 40 : 2);
292
+ if (excerpt.length > 0) {
293
+ for (const line of excerpt)
294
+ out.plain(line);
295
+ out.plain('');
296
+ }
297
+ const after = await testSummary(target);
298
+ out.plain(` After ${after.line}`);
299
+ out.plain('');
300
+ // Said rather than hidden, and reflected in the exit code: a quickstart that quietly reports a
301
+ // success it did not have teaches the opposite of the guarantee it exists to demonstrate.
302
+ if (ok) {
303
+ out.plain(' Nothing above talked to a model. The fix came out of the recording.');
304
+ }
305
+ else if (replayed.matchedExact === 0) {
306
+ out.plain(` The replay served none of the ${turns} recorded turns — the agent did not run.`);
307
+ out.plain(' Re-run with --full to see what it printed.');
308
+ }
309
+ else {
310
+ out.plain(` ${replayed.unmatched} turn(s) could not be served from the recording.`);
311
+ }
312
+ out.plain('');
313
+ out.plain(` cd ${target}`);
314
+ out.plain('');
315
+ // `orca` is on PATH only after a global install. Saying so here rather than letting someone
316
+ // meet `command not found` as the next thing that happens to them.
317
+ out.plain(' See it orca ui # the timeline, in a browser');
318
+ out.plain(' Dig in orca show · orca graph · orca export -o run.html');
319
+ out.plain(' Your own orca record claude -- -p "..."');
320
+ out.plain('');
321
+ out.plain(' Installed locally rather than with -g? Put npx in front: npx orca ui');
322
+ if (!full)
323
+ out.plain(' --full for the whole timeline and the replay as it happened.');
324
+ return {
325
+ dir: target,
326
+ runId,
327
+ unmatched: replayed.unmatched,
328
+ turns,
329
+ served: replayed.matchedExact,
330
+ ok,
331
+ };
332
+ }
333
+ /**
334
+ * Replay the shipped run, printing everything or nothing.
335
+ *
336
+ * `--full` calls the command in process, so the timeline and the replayed agent's own output land
337
+ * on the terminal exactly as they would from `orca replay`.
338
+ *
339
+ * The default passes `--json`, which is what `replay` already keys its quiet mode on: the agent is
340
+ * spawned with inherited stdio, so under anything else its output reaches the terminal without
341
+ * passing through something orca could filter. The result document goes to a discarded `Output`,
342
+ * and this command prints the two numbers worth reading instead.
343
+ *
344
+ * Not a child process. Spawning `../cli.js` resolves from `dist` and not from source, which is a
345
+ * split between how the tests run this and how a user does — and a path that only works in one of
346
+ * those is the shape of bug this project keeps finding in other people's harnesses.
347
+ */
348
+ async function runReplay(target, runId, out, full) {
349
+ if (full) {
350
+ out.plain('── the recording ──────────────────────────────────');
351
+ out.plain('');
352
+ await showCommand(parseArgs(['show', runId]), out, target);
353
+ out.plain('');
354
+ out.plain('── replaying it, offline ──────────────────────────');
355
+ out.plain('');
356
+ const result = await replayCommand(parseArgs(['replay', runId, '--in-place']), out, target);
357
+ return {
358
+ matchedExact: result.matchedExact,
359
+ unmatched: result.unmatched,
360
+ divergences: result.divergences,
361
+ liveCalls: result.liveCalls,
362
+ };
363
+ }
364
+ const quiet = new Output({ write: () => { }, color: false, ci: true });
365
+ const result = await replayCommand(parseArgs(['replay', runId, '--in-place', '--json', '--quiet']), quiet, target);
366
+ return {
367
+ matchedExact: result.matchedExact,
368
+ unmatched: result.unmatched,
369
+ divergences: result.divergences,
370
+ liveCalls: result.liveCalls,
371
+ };
372
+ }
373
+ //# sourceMappingURL=quickstart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quickstart.js","sourceRoot":"","sources":["../../src/commands/quickstart.ts"],"names":[],"mappings":"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"}
@@ -14,4 +14,17 @@ export interface RecordResult {
14
14
  exitCode: number;
15
15
  }
16
16
  export declare function recordCommand(args: ParsedArgs, out: Output, cwd?: string): Promise<RecordResult>;
17
+ /**
18
+ * The origins a configured upstream map actually names, deduplicated and safe to print.
19
+ *
20
+ * `resolveUpstream` writes one entry per dialect and `openai` and `openai-responses` always share
21
+ * a value — so the raw map renders a single gateway three times. What a reader wants is the set of
22
+ * places traffic can go, which is usually one.
23
+ *
24
+ * Through `recordableOrigin` for the same reason the trace goes through it, and against the same
25
+ * sentence: `orca setup --gateway` accepts a URL carrying the key — `https://user:pw@gw.example`,
26
+ * `https://gw.example?key=…` — and `config.ts` says of that file that "nothing ever prints it
27
+ * back". A line on the terminal is printing it back, and it is a line that ends up in CI logs.
28
+ */
29
+ export declare function distinctOrigins(upstream: Record<string, string> | undefined): string[];
17
30
  //# sourceMappingURL=record.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/commands/record.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK7C,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AA0BD,wBAAsB,aAAa,CACjC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,SAAgB,GAClB,OAAO,CAAC,YAAY,CAAC,CAUvB"}
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/commands/record.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK7C,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AA0BD,wBAAsB,aAAa,CACjC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,SAAgB,GAClB,OAAO,CAAC,YAAY,CAAC,CAUvB;AA+mBD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAAG,MAAM,EAAE,CActF"}
@@ -3,14 +3,14 @@ import { readFile } from 'node:fs/promises';
3
3
  import { delimiter, resolve } from 'node:path';
4
4
  import { TraceWriter, ensureRunsDir } from '@orcareplay/core';
5
5
  import { FsCapture } from '@orcareplay/fs-capture';
6
- import { createProxy, RunCa } from '@orcareplay/proxy';
6
+ import { createProxy, unusableOrigin, recordableOrigin, RunCa, } from '@orcareplay/proxy';
7
7
  import { captureSession, defaultAdapters, resolveLaunch, snapshotDir } from '@orcareplay/adapters';
8
8
  import { ExchangeEventDeriver, appendDerivedEvents } from '../exchange-events.js';
9
9
  import { installShellShim, readShellFrames } from '@orcareplay/shell-shim';
10
10
  import { drainMcpFrames, pointAtMcpConfig, setupMcpCapture } from '../mcp.js';
11
11
  import { SerialQueue } from '../serial.js';
12
12
  import { appendSnapshot } from '../fs-events.js';
13
- import { persistNetExchange, setupTlsCapture, trustRunCa } from '../tls-capture.js';
13
+ import { persistNetExchange, planTlsCapture, setupTlsCapture, trustRunCa } from '../tls-capture.js';
14
14
  import { upstreamPlan } from '../upstream.js';
15
15
  import { ORCA_VERSION } from '../version.js';
16
16
  /**
@@ -56,6 +56,23 @@ async function runRecording(args, out, cwd, minted) {
56
56
  if (agentName) {
57
57
  adapter = registry.get(agentName);
58
58
  }
59
+ else if (args.passthrough.length > 0) {
60
+ // A command after `--` is not a hint about the environment; it is the answer. Detection is
61
+ // machine-wide by construction — every adapter's is `detectAgent(binaries, homePaths)`, which
62
+ // asks whether a binary is on PATH or a directory exists under `$HOME`, and neither is a fact
63
+ // about this directory. So on a machine with Claude Code installed,
64
+ // `orca record -- python my_graph.py` detected `claude-code` and launched *that*, handing it
65
+ // `python my_graph.py` as arguments. The run recorded, so the failure looked like a bad key
66
+ // rather than like orca having started the wrong program.
67
+ //
68
+ // `generic-openai` rather than `exec`: both decline to guess what the command is, but only one
69
+ // of them redirects anything. `exec` points the agent nowhere and warns that interception is
70
+ // required — correct for a Go binary with its origin compiled in, and wrong as a default,
71
+ // because most things run this way read a base-URL variable and would have been captured by
72
+ // setting the three that `generic-openai` sets. Sending everyone to `--tls-intercept` to record
73
+ // a Python script is a worse answer than the bug this replaces.
74
+ adapter = registry.get('generic-openai');
75
+ }
59
76
  else {
60
77
  adapter = await registry.detect(cwd);
61
78
  if (!adapter) {
@@ -65,6 +82,15 @@ async function runRecording(args, out, cwd, minted) {
65
82
  }
66
83
  out.info('adapter.detected', { id: adapter.id });
67
84
  }
85
+ // Before the run directory exists, because both of these refuse: `upstreamPlan` an upstream that
86
+ // is not an origin, `planTlsCapture` a `--tls-hosts` list that names `*` or contradicts itself
87
+ // and an `ORCA_TLS_UPSTREAM_CA` that cannot be read. Resolved after it, a typo in any of them
88
+ // left an empty run behind for `orca list` to show. Neither depends on more than the arguments,
89
+ // the environment and a file the run was going to read anyway, so there is no reason for either
90
+ // to run later — and `planTlsCapture` is the refusing half of `setupTlsCapture` alone, so the
91
+ // certificate authority is still minted below, once the run has a directory to mint it into.
92
+ const plan = await upstreamPlan(args);
93
+ await planTlsCapture(args);
68
94
  const dir = await ensureRunsDir(cwd);
69
95
  const writer = await TraceWriter.create(dir, {
70
96
  adapter: { id: adapter.id, version: ORCA_VERSION, harness_version: adapter.harnessVersions },
@@ -131,7 +157,6 @@ async function runRecording(args, out, cwd, minted) {
131
157
  // Serial, not parallel: each persist snapshots the workspace, and two overlapping `git add`
132
158
  // calls collide on index.lock. It also keeps seq in the order exchanges actually happened.
133
159
  const writes = new SerialQueue();
134
- const plan = await upstreamPlan(args);
135
160
  /**
136
161
  * TLS interception, off unless the flag is present.
137
162
  *
@@ -172,6 +197,17 @@ async function runRecording(args, out, cwd, minted) {
172
197
  let commandToolCalls = 0;
173
198
  /** Commands the shim actually saw. Zero against a non-zero `commandToolCalls` is the warning. */
174
199
  let shellFrames = 0;
200
+ /**
201
+ * Exchanges the upstream answered with an error status.
202
+ *
203
+ * Counted because a run in which every model call failed is indistinguishable, from the summary
204
+ * line alone, from one that worked: the harness retries, gives up, exits 0, and orca prints
205
+ * `recorded … exit=0` over a trace whose every answer is a 503. That happened against a gateway
206
+ * whose credentials had lapsed, and nothing in the output said so.
207
+ */
208
+ let erroredExchanges = 0;
209
+ /** The status those errors carried, so the warning can name it rather than describe it. */
210
+ const errorStatuses = new Map();
175
211
  const proxy = await createProxy({
176
212
  mode: 'record',
177
213
  upstream: plan.upstream,
@@ -179,6 +215,10 @@ async function runRecording(args, out, cwd, minted) {
179
215
  upstreamHeadersOrigin: plan.headersOrigin,
180
216
  onExchange: (exchange) => {
181
217
  modelExchanges += 1;
218
+ if (exchange.status >= 400) {
219
+ erroredExchanges += 1;
220
+ errorStatuses.set(exchange.status, (errorStatuses.get(exchange.status) ?? 0) + 1);
221
+ }
182
222
  writes.push(() => persist(exchange));
183
223
  },
184
224
  // A POST on a path no dialect claims is forwarded rather than refused, and lands in the trace
@@ -290,6 +330,17 @@ async function runRecording(args, out, cwd, minted) {
290
330
  run: writer.runId,
291
331
  adapter: adapter.id,
292
332
  proxy: proxy.url,
333
+ // `proxy` is orca's own address, so the line said where the agent would call and never where
334
+ // the call would go on to. A gateway left behind in `~/.orca/config.json` redirects every run
335
+ // on the machine, and the first sign of it used to be an answer from a model nobody asked for.
336
+ //
337
+ // Only when something was configured. Left alone, each dialect has its own vendor default and
338
+ // there is no single value to print — and `upstream=default` on every ordinary run is noise
339
+ // that would teach people to stop reading the line. The trace records the origin either way,
340
+ // per exchange, because that is where it is actually decided.
341
+ ...(distinctOrigins(plan.upstream).length > 0
342
+ ? { upstream: distinctOrigins(plan.upstream).join(',') }
343
+ : {}),
293
344
  fs: fs ? 'on' : 'off',
294
345
  shell: shell ? 'on' : 'off',
295
346
  });
@@ -430,16 +481,49 @@ async function runRecording(args, out, cwd, minted) {
430
481
  * the adapters; this guards the run, which is where a user actually meets it.
431
482
  */
432
483
  if (modelExchanges === 0) {
484
+ const baseUrls = Object.keys(launch.env)
485
+ .filter((name) => /(?:_BASE_URL|_API_BASE)$/.test(name))
486
+ .sort();
487
+ // Which cause is worth naming depends on how this adapter captures at all, and the line
488
+ // already carries the evidence: `set=none` means there was never a variable to be ignored.
489
+ // Printing "it may not read a base-URL variable" next to `set=none` contradicts the run's own
490
+ // output and sends the reader after a route that was never the route -- which is how the
491
+ // capture bug fixed earlier in this branch stayed hidden through a dozen rounds of debugging.
492
+ const cause = baseUrls.length > 0
493
+ ? 'the agent never called the proxy — it may not read a base-URL variable'
494
+ : tls.ca
495
+ ? 'this adapter captures at the transport, and nothing orca decrypted looked like a model call'
496
+ : 'this adapter captures at the transport, and interception was not on';
433
497
  out.warn('capture.empty', {
434
498
  exchanges: 0,
435
- cause: 'the agent never called the proxy — it may not read a base-URL variable',
436
- set: Object.keys(launch.env)
437
- .filter((name) => /(?:_BASE_URL|_API_BASE)$/.test(name))
438
- .sort()
439
- .join(',') || 'none',
499
+ cause,
500
+ set: baseUrls.join(',') || 'none',
440
501
  next: 'orca doctor',
441
502
  });
442
503
  }
504
+ /**
505
+ * Every model call came back an error.
506
+ *
507
+ * `capture.empty` covers the run the proxy never saw. This covers the run it saw all of, where
508
+ * every answer was a refusal: an expired gateway credential, a model the account cannot reach, a
509
+ * provider outage. The harness retries, exhausts its attempts, prints its own error and exits 0,
510
+ * so the summary reads exactly like a successful recording — `recorded events=27 exit=0` — over
511
+ * a trace that contains no model output at all. Replaying it faithfully reproduces the failures.
512
+ *
513
+ * Only when *all* of them failed. A run that retried once and then worked is a run that worked,
514
+ * and warning about it would train people to ignore the line that matters.
515
+ */
516
+ if (modelExchanges > 0 && erroredExchanges === modelExchanges) {
517
+ const commonest = [...errorStatuses.entries()].sort((a, b) => b[1] - a[1])[0];
518
+ out.warn('capture.errors', {
519
+ exchanges: modelExchanges,
520
+ errors: erroredExchanges,
521
+ status: commonest ? commonest[0] : 'unknown',
522
+ cause: 'every model call the proxy forwarded came back an error',
523
+ effect: 'the trace holds the failures, not any model output — a replay reproduces those',
524
+ next: 'orca show to read the errors, then check the upstream credential or model id',
525
+ });
526
+ }
443
527
  /**
444
528
  * Shell capture that was on and recorded nothing.
445
529
  *
@@ -535,4 +619,31 @@ async function runChild(command, argv, env, cwd, quietStdout = false) {
535
619
  });
536
620
  });
537
621
  }
622
+ /**
623
+ * The origins a configured upstream map actually names, deduplicated and safe to print.
624
+ *
625
+ * `resolveUpstream` writes one entry per dialect and `openai` and `openai-responses` always share
626
+ * a value — so the raw map renders a single gateway three times. What a reader wants is the set of
627
+ * places traffic can go, which is usually one.
628
+ *
629
+ * Through `recordableOrigin` for the same reason the trace goes through it, and against the same
630
+ * sentence: `orca setup --gateway` accepts a URL carrying the key — `https://user:pw@gw.example`,
631
+ * `https://gw.example?key=…` — and `config.ts` says of that file that "nothing ever prints it
632
+ * back". A line on the terminal is printing it back, and it is a line that ends up in CI logs.
633
+ */
634
+ export function distinctOrigins(upstream) {
635
+ const safe = Object.values(upstream ?? {})
636
+ // Dropped only where sanitising cannot work, so a gateway configured with its key in the URL
637
+ // still says *where* the traffic went. `recordableOrigin` removes userinfo and query, which is
638
+ // enough for every `http(s)://` value — but a scheme-less URL parses with its username as the
639
+ // protocol, so it comes back as `myuser://PASSWORD@gw.example/v1`, credential intact, and a
640
+ // filter keeping everything that is not `undefined` kept it. There is no sanitising that: with
641
+ // no scheme there is no telling which half of `a:b` was meant as the host. `upstreamPlan`
642
+ // refuses these before a run starts; this is here because the function is exported and one
643
+ // line from a terminal.
644
+ .filter((origin) => unusableOrigin(origin) === undefined)
645
+ .map(recordableOrigin)
646
+ .filter((origin) => origin !== undefined);
647
+ return [...new Set(safe)];
648
+ }
538
649
  //# sourceMappingURL=record.js.map