@hackerrank/astra-cli 0.1.4 → 0.1.6

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 CHANGED
@@ -204,6 +204,17 @@ astra --resume <id> # resume (continue an interactive chat,
204
204
  # or inspect/continue an autonomous run)
205
205
  ```
206
206
 
207
+ Resume an interrupted benchmark without repeating its task, model, or output
208
+ arguments:
209
+
210
+ ```bash
211
+ astra bench --resume <session-id>
212
+ ```
213
+
214
+ It reopens the original candidate workspace and updates that same run's
215
+ trajectory, metrics row, and report telemetry. Explicit flags override the
216
+ saved setting when needed.
217
+
207
218
  Verified working models on the gateway include `claude-sonnet-5`, `claude-opus-5`,
208
219
  `gpt-5.6-sol`, `gpt-5.6-terra`, `gemini-3.7-flash`, `grok-4.6`, `kimi-k3`, and others.
209
220
  Some routes (e.g. `glm-5.2`) may return HTTP 402 when the underlying provider is out of
@@ -228,7 +239,7 @@ credits — that's a quota issue, not a bug.
228
239
  whatever runs already exist on disk, then exit
229
240
  -C, --cwd <path> Working directory for commands (default: cwd)
230
241
  -o, --output <path> Also write trajectory JSON here (autonomous mode)
231
- -s, --steps <n> Step limit (default: 40)
242
+ -s, --steps <n> Turn limit (unset = no limit)
232
243
  -w, --wall <seconds> Wall-clock limit (default: 0 = none)
233
244
  --timeout <seconds> Per-command timeout (default: 60)
234
245
  --max-output <n> Max chars of command output kept (default: 16000)
@@ -237,7 +248,7 @@ credits — that's a quota issue, not a bug.
237
248
  --resume <id> Resume a saved session
238
249
  --sessions List saved sessions and exit
239
250
  -y, --yolo Auto-run commands (always on in autonomous mode)
240
- -q, --quiet Do not stream steps (autonomous mode)
251
+ -q, --quiet Do not stream turns (autonomous mode)
241
252
  -h, --help Show help
242
253
  ```
243
254
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hackerrank/astra-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Minimal zero-dependency AI coding agent for the HackerRank AI Gateway.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agent.js CHANGED
@@ -38,7 +38,7 @@ export class Agent {
38
38
  this.model = model;
39
39
  this.env = env;
40
40
  this.mode = opts.mode === "interactive" ? "interactive" : "autonomous";
41
- this.stepLimit = opts.stepLimit ?? 40;
41
+ this.stepLimit = opts.stepLimit ?? 0;
42
42
  this.wallTimeLimitSeconds = opts.wallTimeLimitSeconds ?? 0;
43
43
  this.maxConsecutiveFormatErrors = opts.maxConsecutiveFormatErrors ?? 3;
44
44
  this.outputPath = opts.outputPath ?? null;
@@ -64,6 +64,7 @@ export class Agent {
64
64
  this.sessionId = opts.sessionId ?? null;
65
65
  this.sessionCreated = opts.sessionCreated ?? new Date().toISOString();
66
66
  this.title = opts.title ?? "";
67
+ this.bench = opts.bench ?? null;
67
68
  this._saveSession = opts.saveSession ?? null; // (id, doc) => void
68
69
  }
69
70
 
@@ -240,6 +241,7 @@ export class Agent {
240
241
  id: this.sessionId ?? undefined,
241
242
  created: this.sessionCreated,
242
243
  title: this.title || undefined,
244
+ bench: this.bench ?? undefined,
243
245
  info: {
244
246
  mode: this.mode,
245
247
  task: this.task || undefined,
@@ -248,10 +250,20 @@ export class Agent {
248
250
  n_steps: this.nSteps,
249
251
  model: this.model.model,
250
252
  n_calls: this.model.nCalls,
253
+ n_retries: this.model.nRetries,
254
+ counters: {
255
+ commands: this.nCommands,
256
+ failed_commands: this.nFailedCommands,
257
+ format_errors: this.nFormatErrors,
258
+ declined: this.nDeclined,
259
+ },
251
260
  elapsed_seconds: Math.round((Date.now() - this.startTime) / 1000),
252
261
  tokens: {
253
262
  prompt: this.model.totalPromptTokens,
254
263
  completion: this.model.totalCompletionTokens,
264
+ cached: this.model.totalCachedTokens,
265
+ cache_write: this.model.totalCacheWriteTokens,
266
+ reasoning: this.model.totalReasoningTokens,
255
267
  total: this.model.totalPromptTokens + this.model.totalCompletionTokens,
256
268
  last_context: this.lastContextTokens,
257
269
  },
@@ -282,8 +294,25 @@ export class Agent {
282
294
  this.task = data?.info?.task ?? this.task;
283
295
  this.mode = data?.info?.mode === "interactive" ? "interactive" : this.mode;
284
296
  this.nSteps = data?.info?.n_steps ?? this.messages.filter((m) => m.role === "assistant").length;
297
+ this.nCommands = data?.info?.counters?.commands ?? 0;
298
+ this.nFailedCommands = data?.info?.counters?.failed_commands ?? 0;
299
+ this.nFormatErrors = data?.info?.counters?.format_errors ?? 0;
300
+ this.nDeclined = data?.info?.counters?.declined ?? 0;
285
301
  this.lastContextTokens = data?.info?.tokens?.last_context ?? 0;
302
+ this.model.nCalls = data?.info?.n_calls ?? this.model.nCalls;
303
+ this.model.nRetries = data?.info?.n_retries ?? this.model.nRetries;
304
+ this.model.totalPromptTokens = data?.info?.tokens?.prompt ?? this.model.totalPromptTokens;
305
+ this.model.totalCompletionTokens = data?.info?.tokens?.completion ?? this.model.totalCompletionTokens;
306
+ this.model.totalCachedTokens = data?.info?.tokens?.cached ?? this.model.totalCachedTokens;
307
+ this.model.totalCacheWriteTokens = data?.info?.tokens?.cache_write ?? this.model.totalCacheWriteTokens;
308
+ this.model.totalReasoningTokens = data?.info?.tokens?.reasoning ?? this.model.totalReasoningTokens;
309
+ this.model.totalCostUsd = data?.info?.cost?.usd ?? this.model.totalCostUsd;
310
+ this.model.reportedCostUsd = data?.info?.cost?.reported_usd ?? this.model.reportedCostUsd;
311
+ this.model.estimatedCostUsd = data?.info?.cost?.estimated_usd ?? this.model.estimatedCostUsd;
312
+ this.model.costSource = data?.info?.cost?.source ?? this.model.costSource;
313
+ this.startTime = Date.now() - Number(data?.info?.elapsed_seconds || 0) * 1000;
286
314
  this.exitStatus = data?.info?.exit_status || null;
315
+ this.bench = data?.bench ?? this.bench;
287
316
  if (data?.id) this.sessionId = data.id;
288
317
  if (data?.created) this.sessionCreated = data.created;
289
318
  if (data?.title) this.title = data.title;
package/src/bench.js CHANGED
@@ -26,6 +26,7 @@ import { Agent } from "./agent.js";
26
26
 
27
27
  /** CSV columns, in order, for the metrics table. */
28
28
  export const METRIC_COLUMNS = [
29
+ "run_path",
29
30
  "timestamp",
30
31
  "model",
31
32
  "reasoning",
@@ -211,8 +212,9 @@ export function metricsHeader() {
211
212
  * the top-level rolled-up index at <root>/metrics.csv.
212
213
  */
213
214
  export function writeMetrics({ runDir, root, metrics }) {
215
+ const rowMetrics = { ...metrics, run_path: path.relative(benchRoot(root), runDir) };
214
216
  const header = metricsHeader();
215
- const row = metricsRow(metrics);
217
+ const row = metricsRow(rowMetrics);
216
218
 
217
219
  // Per-run file.
218
220
  fs.writeFileSync(path.join(runDir, "metrics.csv"), header + "\n" + row + "\n");
@@ -222,7 +224,14 @@ export function writeMetrics({ runDir, root, metrics }) {
222
224
  if (!fs.existsSync(indexPath)) {
223
225
  fs.writeFileSync(indexPath, header + "\n" + row + "\n");
224
226
  } else {
225
- fs.appendFileSync(indexPath, row + "\n");
227
+ const lines = fs.readFileSync(indexPath, "utf8").trimEnd().split("\n");
228
+ const runPathIndex = lines[0].split(",").indexOf("run_path");
229
+ const existing = runPathIndex < 0
230
+ ? -1
231
+ : lines.findIndex((line, index) => index > 0 && line.split(",")[runPathIndex] === rowMetrics.run_path);
232
+ if (existing >= 0) lines[existing] = row;
233
+ else lines.push(row);
234
+ fs.writeFileSync(indexPath, lines.join("\n") + "\n");
226
235
  }
227
236
  return { runMetrics: path.join(runDir, "metrics.csv"), index: indexPath };
228
237
  }
@@ -282,7 +291,7 @@ export async function runCell({
282
291
  taskFile,
283
292
  projectMetadata,
284
293
  root,
285
- steps = 40,
294
+ steps = 0,
286
295
  wall = 0,
287
296
  timeout = 60,
288
297
  maxOutputChars = 16000,
package/src/cli.js CHANGED
@@ -17,6 +17,7 @@
17
17
  * astra -m <model> -f path/to/instruction.md
18
18
  * astra -m <model> -p tasks/dummy-slugify bench run from a task directory
19
19
  * astra bench --project ./task project-configured bench run
20
+ * astra bench --resume <session-id> continue the same bench attempt
20
21
  * astra --resume <session-id> resume a saved session
21
22
  * astra --sessions list saved sessions
22
23
  *
@@ -45,7 +46,7 @@
45
46
  * --bench-root <dir> Root folder for bench runs (default: ./bench)
46
47
  * -C, --cwd <path> Working directory for commands (default: cwd)
47
48
  * -o, --output <path> Also write trajectory JSON here (bench mode)
48
- * -s, --steps <n> Step limit (default: 40)
49
+ * -s, --steps <n> Turn limit (unset = no limit)
49
50
  * -w, --wall <seconds> Wall-clock limit (default: 0 = none)
50
51
  * --timeout <seconds> Per-command timeout (default: 60)
51
52
  * --max-output <n> Max chars of command output kept (default: 16000)
@@ -55,7 +56,7 @@
55
56
  * --sessions List saved sessions and exit
56
57
  * -y, --yolo Auto-run commands without confirmation
57
58
  * (always on in bench mode)
58
- * -q, --quiet Do not stream steps (bench mode)
59
+ * -q, --quiet Do not stream turns (bench mode)
59
60
  * -h, --help Show this help
60
61
  *
61
62
  * API key resolution (first hit wins):
@@ -113,7 +114,7 @@ function reasoningKwargs(level) {
113
114
  }
114
115
 
115
116
  function parseArgs(argv) {
116
- const args = { steps: 40, wall: 0, timeout: 60, quiet: false, "max-output": 16000, _provided: new Set() };
117
+ const args = { steps: 0, wall: 0, timeout: 60, quiet: false, "max-output": 16000, _provided: new Set() };
117
118
  const alias = {
118
119
  "-m": "model", "--model": "model",
119
120
  "-t": "task", "--task": "task",
@@ -198,6 +199,19 @@ async function main() {
198
199
  // Bench mode uses the autonomous engine; agent mode uses the interactive one.
199
200
  let task = args.task;
200
201
  if (args["task-file"]) task = fs.readFileSync(args["task-file"], "utf8");
202
+ let resumeBench = resumeDoc?.bench ?? null;
203
+ if (args.bench && args.resume && !resumeBench) {
204
+ console.error("\x1b[31m[astra] request_error: this session is not a bench run.\x1b[0m");
205
+ process.exit(2);
206
+ }
207
+ if (resumeBench) {
208
+ if (!args._provided.has("task") && !args._provided.has("task-file")) task = resumeBench.task;
209
+ if (!args._provided.has("path")) args.path = resumeBench.taskPath;
210
+ if (!args._provided.has("bench-root")) args["bench-root"] = resumeBench.root;
211
+ if (!args._provided.has("steps")) args.steps = resumeBench.steps;
212
+ if (!args._provided.has("wall")) args.wall = resumeBench.wall;
213
+ if (!args._provided.has("timeout")) args.timeout = resumeBench.timeout;
214
+ }
201
215
  let project = null;
202
216
  if (args.project) {
203
217
  try {
@@ -261,7 +275,7 @@ async function main() {
261
275
  // 5. hard fallback: glm-5.2 with medium reasoning
262
276
  // The chosen values are cached so the next `astra` needs no flags.
263
277
  let modelId = args.model || resumeDoc?.info?.model;
264
- let reasoning = args.reasoning || "";
278
+ let reasoning = args.reasoning || resumeBench?.reasoning || "";
265
279
 
266
280
  if (mode === "autonomous") {
267
281
  if (project && !modelId) modelId = project.bench.models.join(",");
@@ -330,7 +344,21 @@ async function main() {
330
344
  // workspace under bench/<model-name-reasoning>/run-NN/. The agent's commands
331
345
  // run inside that workspace and metrics are recorded when it finishes.
332
346
  let benchRun = null;
333
- if (mode === "autonomous" && !resumeDoc) {
347
+ if (mode === "autonomous" && resumeBench) {
348
+ if (!fs.existsSync(resumeBench.dir) || !fs.existsSync(resumeBench.workspace)) {
349
+ console.error("\x1b[31m[astra] request_error: saved bench workspace is missing.\x1b[0m");
350
+ process.exit(2);
351
+ }
352
+ benchRun = {
353
+ dir: resumeBench.dir,
354
+ workspace: resumeBench.workspace,
355
+ root: resumeBench.root,
356
+ slug: path.basename(path.dirname(resumeBench.dir)),
357
+ runId: path.basename(resumeBench.dir),
358
+ };
359
+ resumeBench.segments = Array.isArray(resumeBench.segments) ? resumeBench.segments : [];
360
+ resumeBench.segments.push({ started_at: new Date().toISOString(), resumed: true });
361
+ } else if (mode === "autonomous" && !resumeDoc) {
334
362
  benchRun = allocateRun({ model: modelId, reasoning, root: args["bench-root"] });
335
363
  const seeded = seedWorkspace(benchRun.workspace, {
336
364
  taskPath: args.path,
@@ -341,9 +369,22 @@ async function main() {
341
369
  // Persist the task text alongside the run for reproducibility.
342
370
  fs.writeFileSync(path.join(benchRun.dir, "task.md"), task || "");
343
371
  writeProjectMetadata(benchRun.dir, project?.metadata);
372
+ resumeBench = {
373
+ dir: benchRun.dir,
374
+ workspace: benchRun.workspace,
375
+ root: benchRun.root,
376
+ task,
377
+ taskPath: args.path,
378
+ model: modelId,
379
+ reasoning,
380
+ steps: Number(args.steps),
381
+ wall: Number(args.wall),
382
+ timeout: Number(args.timeout),
383
+ segments: [{ started_at: new Date().toISOString(), resumed: false }],
384
+ };
344
385
  }
345
386
 
346
- const cmdCwd = benchRun ? benchRun.workspace : cwd;
387
+ const cmdCwd = benchRun?.workspace || cwd;
347
388
  const env = new LocalEnvironment({
348
389
  cwd: cmdCwd,
349
390
  timeout: Number(args.timeout),
@@ -362,12 +403,23 @@ async function main() {
362
403
  : null,
363
404
  sessionId,
364
405
  saveSession,
406
+ bench: resumeBench,
365
407
  onEvent: quiet || mode === "interactive" ? () => {} : (msg) => printEvent(msg),
366
408
  onStep: quiet || mode === "interactive" ? () => {} : (s) => printStep(s),
367
409
  });
368
410
 
369
411
  if (resumeDoc) {
370
412
  agent.restore(resumeDoc);
413
+ if (resumeBench) {
414
+ agent.exitStatus = null;
415
+ if (!args._provided.has("steps") && Number(resumeBench.steps) > 0) {
416
+ agent.stepLimit = agent.nSteps + Number(resumeBench.steps);
417
+ }
418
+ if (!args._provided.has("wall") && Number(resumeBench.wall) > 0) {
419
+ agent.wallTimeLimitSeconds =
420
+ Math.ceil((Date.now() - agent.startTime) / 1000) + Number(resumeBench.wall);
421
+ }
422
+ }
371
423
  console.error(`\x1b[2m[astra] resumed ${sessionId} (${agent.messages.length} messages)\x1b[0m`);
372
424
  }
373
425
 
@@ -419,20 +471,51 @@ async function main() {
419
471
  if (benchRun) {
420
472
  console.error(
421
473
  `\x1b[2m[astra] bench · ${benchRun.slug}/${benchRun.runId} ` +
422
- `· model=${modelId}${reasoning ? ` reasoning=${reasoning}` : ""} steps<=${args.steps}\x1b[0m`
474
+ `· model=${modelId}${reasoning ? ` reasoning=${reasoning}` : ""}` +
475
+ `${Number(args.steps) > 0 ? ` turns<=${args.steps}` : ""}\x1b[0m`
423
476
  );
424
477
  console.error(`\x1b[2m[astra] workspace -> ${benchRun.workspace}\x1b[0m`);
478
+ console.error(`\x1b[2m[astra] session -> ${sessionId}\x1b[0m`);
425
479
  } else {
426
- console.error(`\x1b[2m[astra] bench · model=${modelId} cwd=${cmdCwd} steps<=${args.steps}\x1b[0m`);
480
+ console.error(
481
+ `\x1b[2m[astra] bench · model=${modelId} cwd=${cmdCwd}` +
482
+ `${Number(args.steps) > 0 ? ` turns<=${args.steps}` : ""}\x1b[0m`
483
+ );
427
484
  }
428
485
  }
429
486
 
430
- const result =
431
- resumeDoc && !task ? await continueAutonomous(agent) : await agent.run(task);
487
+ let result;
488
+ try {
489
+ result = resumeDoc ? await continueAutonomous(agent) : await agent.run(task);
490
+ } catch (error) {
491
+ agent.exitStatus = "Error";
492
+ const segment = agent.bench?.segments?.at(-1);
493
+ if (segment) {
494
+ segment.finished_at = new Date().toISOString();
495
+ segment.exit_status = "Error";
496
+ segment.error = String(error?.message || error);
497
+ }
498
+ agent.save();
499
+ if (benchRun) {
500
+ writeMetrics({
501
+ runDir: benchRun.dir,
502
+ root: benchRun.root,
503
+ metrics: collectMetrics({ agent, model, reasoning }),
504
+ });
505
+ try { refreshReport(benchRun.root); } catch {}
506
+ }
507
+ throw error;
508
+ }
432
509
  agent.save();
433
510
 
434
511
  // Record benchmark metrics (per-run CSV + rolled-up index).
435
512
  if (benchRun) {
513
+ const segment = agent.bench?.segments?.at(-1);
514
+ if (segment) {
515
+ segment.finished_at = new Date().toISOString();
516
+ segment.exit_status = result.exit_status;
517
+ }
518
+ agent.save();
436
519
  const metrics = collectMetrics({ agent, model, reasoning });
437
520
  const paths = writeMetrics({ runDir: benchRun.dir, root: benchRun.root, metrics });
438
521
  if (!quiet) {
@@ -451,7 +534,7 @@ async function main() {
451
534
  const pt = model.totalPromptTokens;
452
535
  const ct = model.totalCompletionTokens;
453
536
  console.error(
454
- `\n\x1b[1m[astra] exit=${result.exit_status} steps=${agent.nSteps} calls=${model.nCalls}\x1b[0m`
537
+ `\n\x1b[1m[astra] exit=${result.exit_status} turns=${agent.nSteps}\x1b[0m`
455
538
  );
456
539
  console.error(
457
540
  `\x1b[1m[astra] tokens: prompt=${fmt(pt)} completion=${fmt(ct)} total=${fmt(pt + ct)} ` +
@@ -576,7 +659,7 @@ async function runBenchMatrix({ args, apiKey, models, reasonings, repeat, task,
576
659
  const status = r.error ? `model_error: ${r.error.split("\n")[0]}` : r.status;
577
660
  console.error(
578
661
  ` ${mark} ${String(r.model).padEnd(20)} ${String(r.reasoning).padEnd(8)} ` +
579
- `${String(status).padEnd(16)} ${String(r.steps).padStart(3)} steps · ` +
662
+ `${String(status).padEnd(16)} ${String(r.steps).padStart(3)} turns · ` +
580
663
  `${fmt(r.total_tokens)} tok · ${fmtUsd(r.cost_usd)}` +
581
664
  `${r.cost_source === "estimated" ? "~" : ""}`
582
665
  );
@@ -620,7 +703,7 @@ function printLeaderboard(board) {
620
703
  board.sort((a, b) => b.completion_rate - a.completion_rate || a.avg_steps - b.avg_steps);
621
704
  console.error(
622
705
  `\n\x1b[1m${"model".padEnd(20)} ${"reason".padEnd(8)} ${"complete".padEnd(8)} ` +
623
- `${"steps".padStart(6)} ${"tokens".padStart(9)} ${"cost".padStart(9)} source\x1b[0m`
706
+ `${"turns".padStart(6)} ${"tokens".padStart(9)} ${"cost".padStart(9)} source\x1b[0m`
624
707
  );
625
708
  for (const g of board) {
626
709
  const completed = `${g.completed}/${g.runs}`;
@@ -665,23 +748,22 @@ function printSessions() {
665
748
 
666
749
  function printEvent(msg) {
667
750
  // Bench mode keeps output compact: skip the verbose system/user/assistant
668
- // message bodies (per-step progress is shown by printStep). Only surface a
751
+ // message bodies (per-turn progress is shown by printStep). Only surface a
669
752
  // non-empty terminal exit event so the run's submission is still visible.
670
753
  if (msg.role !== "exit" || !msg.content?.trim()) return;
671
754
  console.error(`\x1b[35m--- SUBMISSION ---\x1b[0m\n${msg.content}\n`);
672
755
  }
673
756
 
674
- /** Compact per-step status line with exact token usage from the API. */
757
+ /** Compact per-turn status line with exact token usage from the API. */
675
758
  function printStep(s) {
676
- const limit = s.stepLimit > 0 ? `/${s.stepLimit}` : "";
677
759
  const cached = s.usage.cached_tokens ? ` (cached ${fmt(s.usage.cached_tokens)})` : "";
678
760
  const cost = s.costUsd != null
679
- ? ` · ${fmtUsd(s.costUsd)}${s.costKind === "estimated" ? "~" : ""}`
761
+ ? ` ${fmtUsd(s.costUsd)}${s.costKind === "estimated" ? "~" : ""}`
680
762
  : "";
681
763
  console.error(
682
- `\x1b[36m[astra] step ${s.step}${limit} · call ${s.nCalls} · ` +
683
- `↑${fmt(s.usage.prompt_tokens)} ↓${fmt(s.usage.completion_tokens)} tok · ` +
684
- `ctx ${fmt(s.contextTokens)}${cached}${cost} · ${s.elapsedSeconds.toFixed(1)}s\x1b[0m`
764
+ `\x1b[36m[astra] turn ${s.step} ` +
765
+ `↑${fmt(s.usage.prompt_tokens)} ↓${fmt(s.usage.completion_tokens)} tok ` +
766
+ `ctx ${fmt(s.contextTokens)}${cached}${cost} ${s.elapsedSeconds.toFixed(1)}s\x1b[0m`
685
767
  );
686
768
  }
687
769
 
package/src/project.js CHANGED
@@ -14,7 +14,7 @@ const DEFAULT_BENCH = {
14
14
  models: [],
15
15
  reasoning: [""],
16
16
  repeat: 1,
17
- steps: 40,
17
+ steps: 0,
18
18
  wall_seconds: 0,
19
19
  command_timeout_seconds: 60,
20
20
  };
@@ -169,7 +169,7 @@ export function loadProject(projectPath) {
169
169
  models: stringArray(bench.models, "bench.models", DEFAULT_BENCH.models),
170
170
  reasoning: stringArray(bench.reasoning, "bench.reasoning", DEFAULT_BENCH.reasoning),
171
171
  repeat: positiveInteger(bench.repeat, "bench.repeat", DEFAULT_BENCH.repeat),
172
- steps: positiveInteger(bench.steps, "bench.steps", DEFAULT_BENCH.steps),
172
+ steps: positiveInteger(bench.steps, "bench.steps", DEFAULT_BENCH.steps, { allowZero: true }),
173
173
  wall: positiveInteger(bench.wall_seconds, "bench.wall_seconds", DEFAULT_BENCH.wall_seconds, { allowZero: true }),
174
174
  timeout: positiveInteger(bench.command_timeout_seconds, "bench.command_timeout_seconds", DEFAULT_BENCH.command_timeout_seconds),
175
175
  },
package/src/report.js CHANGED
@@ -296,6 +296,7 @@ function readRunDir({ rootDir, slug, runId, dir }) {
296
296
  tokens,
297
297
  cost_usd: costSource ? num(metricsRow?.cost_usd ?? traj?.info?.cost?.usd) : null,
298
298
  cost_source: costSource || "",
299
+ resume_segments: Array.isArray(traj?.bench?.segments) ? traj.bench.segments : [],
299
300
  timeline,
300
301
  paths: {
301
302
  dir: path.relative(rootDir, dir),