benchflow 0.7.2.dev1986__py3-none-any.whl → 0.7.2.dev1994__py3-none-any.whl

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.
benchflow/cli/main.py CHANGED
@@ -1228,11 +1228,27 @@ def eval_view(
1228
1228
  ),
1229
1229
  ],
1230
1230
  port: Annotated[int, typer.Option(help="Server port")] = 8888,
1231
+ confirm: Annotated[
1232
+ bool,
1233
+ typer.Option(
1234
+ "--confirm",
1235
+ help=(
1236
+ "Show an Approve & submit / Not this one bar on the page; "
1237
+ "print DECISION: approved|rejected and exit 0 on approve, "
1238
+ "3 on reject."
1239
+ ),
1240
+ ),
1241
+ ] = False,
1231
1242
  ) -> None:
1232
1243
  """View a trial or session trajectory in the browser."""
1233
- from benchflow.trajectories.viewer import serve
1234
-
1235
- serve(str(rollout_dir), port)
1244
+ from benchflow.trajectories import viewer
1245
+
1246
+ decision = viewer.serve(str(rollout_dir), port, confirm=confirm)
1247
+ # Exit-code contract for --confirm: 0 approved, 3 rejected. 3 is chosen
1248
+ # so a rejection never collides with the CLI's existing error exits
1249
+ # (1 = errors, 2 = Typer usage errors).
1250
+ if decision == "rejected":
1251
+ raise typer.Exit(3)
1236
1252
 
1237
1253
 
1238
1254
  # ── Command-group wiring ──────────────────────────────────────────────
benchflow/cli/traj.py CHANGED
@@ -54,14 +54,26 @@ SKILL_RAW_URL = (
54
54
  "/.agents/skills/benchflow-traj-upload/SKILL.md"
55
55
  )
56
56
 
57
+ UPGRADE_COMMAND = "uv tool install --python 3.12 --upgrade --force benchflow"
58
+
59
+ # Three unwrapped lines separated by blank lines: each logical unit is one
60
+ # full physical line (no hard wraps mid-sentence), so the URL and the upgrade
61
+ # command stay selectable in agents and terminals.
57
62
  CONTRIBUTOR_PROMPT = (
58
- "Submit my best local Claude Code or Codex session to the BenchFlow eval "
59
- "prize. Read "
60
- f"{SKILL_RAW_URL} "
61
- "and follow it: find a session, open the viewer, upload only after I review it."
63
+ "Submit my relevant local Claude Code, Codex, OpenCode, or Cursor session "
64
+ "from the re:Agent e2e agentic science / ai4bio hackathon (last 72 hours, "
65
+ "on this laptop) to the BenchFlow eval prize."
66
+ "\n\n"
67
+ f"1. First make sure the latest benchflow CLI is installed: {UPGRADE_COMMAND}"
68
+ "\n\n"
69
+ f"2. Then read {SKILL_RAW_URL} "
70
+ "and follow it: find a session, open the viewer, and upload only after "
71
+ "I (the human) review it."
62
72
  )
63
73
 
64
- UPGRADE_COMMAND = "uv tool install --python 3.12 --upgrade --force benchflow"
74
+ CONTRIBUTOR_PROMPT_FRAMING = (
75
+ "Send this to your coding agent (it's a prompt for the agent, not steps for you):"
76
+ )
65
77
 
66
78
 
67
79
  def _fetch_latest_version() -> str | None:
@@ -166,14 +178,14 @@ def register_traj(app: typer.Typer) -> None:
166
178
  ] = False,
167
179
  prompt_only: Annotated[
168
180
  bool,
169
- typer.Option("--prompt", help="Print the copy-paste agent line and exit"),
181
+ typer.Option("--prompt", help="Print the copy-paste agent prompt and exit"),
170
182
  ] = False,
171
183
  list_sessions: Annotated[
172
184
  bool,
173
185
  typer.Option("--list", help="List recent local sessions and exit"),
174
186
  ] = False,
175
187
  ) -> None:
176
- """Install the submit skill, or print the line to paste into an agent."""
188
+ """Install the submit skill, or print the prompt to send to an agent."""
177
189
  _maybe_print_update_hint()
178
190
  if prompt_only:
179
191
  _print_contributor_prompt()
@@ -194,7 +206,6 @@ def register_traj(app: typer.Typer) -> None:
194
206
  _run_npx_skill_install()
195
207
  else:
196
208
  _install_project_skill(Path.cwd())
197
- console.print("Paste this to your agent:")
198
209
  _print_contributor_prompt()
199
210
  if interactive and typer.confirm(
200
211
  "List recent sessions and open the viewer now?", default=False
@@ -625,7 +636,9 @@ def _print_dry_run(staged: StagedCapture) -> None:
625
636
 
626
637
 
627
638
  def _print_contributor_prompt() -> None:
628
- # One physical line so people can copy it. Rich wrapping would break that.
639
+ # Plain print: Rich wrapping would break the unbroken URL line and make
640
+ # the block awkward to copy.
641
+ print(CONTRIBUTOR_PROMPT_FRAMING)
629
642
  print(CONTRIBUTOR_PROMPT)
630
643
 
631
644
 
@@ -671,7 +684,7 @@ def _run_npx_skill_install() -> None:
671
684
  check=False,
672
685
  )
673
686
  if completed.returncode != 0:
674
- print_error("npx skills add failed; the copy-paste line below still works.")
687
+ print_error("npx skills add failed; the copy-paste prompt below still works.")
675
688
 
676
689
 
677
690
  def _print_session_hits() -> None:
@@ -16,9 +16,15 @@ _RESULT_PREVIEW = 300 # max chars for result summary
16
16
 
17
17
  # Shared stylesheet for all viewer pages, matching the www.benchflow.ai design
18
18
  # language (light monochrome: near-white page, white cards, near-black ink,
19
- # pure-black accent, Satoshi/Google Sans Code with system-safe fallbacks, dark
20
- # code blocks). Kept inline so pages work fully offline with no external
21
- # requests; one constant so the three templates stop drifting apart.
19
+ # Satoshi/Google Sans Code with system-safe fallbacks). Kept inline so pages
20
+ # work fully offline with no external requests; one constant so the three
21
+ # templates stop drifting apart.
22
+ #
23
+ # The site itself is deliberately achromatic, so tool-call accents below are
24
+ # muted GitHub-label-style pastels (pale chip background + darker same-hue
25
+ # text + a soft left border strip) that read as annotations rather than
26
+ # fighting the monochrome base. The dark #141414 code treatment is reserved
27
+ # for terminal output of shell commands; everything else stays light.
22
28
  _VIEWER_CSS = """\
23
29
  * { margin: 0; padding: 0; box-sizing: border-box; }
24
30
  :root {
@@ -46,8 +52,10 @@ body { font-family: var(--font-sans); background: var(--background); color: var(
46
52
  .meta span { font-family: var(--font-mono); font-size: 11px; font-weight: 500; color: var(--ink-secondary); background: var(--secondary); padding: 3px 9px; border-radius: 4px; border: 1px solid var(--border); }
47
53
  .step { margin-bottom: 8px; padding: 12px 16px; border-radius: var(--radius); background: var(--card); border: 1px solid var(--border); box-shadow: 0 1px 2px rgba(10, 10, 10, 0.04); }
48
54
  .step.prompt { background: var(--secondary); border-color: var(--rule-strong); margin-bottom: 14px; }
49
- .step.output { background: var(--code-bg); border-color: var(--code-bg); padding: 10px 16px; }
50
- .step.output pre { color: var(--code-ink); font-family: var(--font-mono); font-size: 12px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; }
55
+ .step.output { background: var(--card); border-color: var(--border); padding: 10px 16px; }
56
+ .step.output pre { color: var(--ink-secondary); font-family: var(--font-mono); font-size: 12px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; }
57
+ .step.output.term { background: var(--code-bg); border-color: var(--code-bg); }
58
+ .step.output.term pre { color: var(--code-ink); }
51
59
  .step.result { background: var(--ink); border-color: var(--ink); margin-top: 14px; }
52
60
  .step.result .msg { color: var(--background); }
53
61
  .step-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
@@ -59,12 +67,70 @@ body { font-family: var(--font-sans); background: var(--background); color: var(
59
67
  .msg { font-size: 14px; line-height: 1.65; white-space: pre-wrap; word-break: break-word; }
60
68
  .thinking { font-size: 13px; color: var(--muted); font-style: italic; margin-bottom: 8px; padding: 8px 12px; background: var(--secondary); border-radius: 4px; border-left: 2px solid var(--rule-strong); white-space: pre-wrap; word-break: break-word; }
61
69
  .tool { margin-bottom: 6px; }
62
- .tool-name { display: inline-flex; align-items: center; font-family: var(--font-mono); font-size: 11px; font-weight: 600; color: var(--ink); background: var(--secondary); border: 1px solid var(--border); padding: 2px 9px; border-radius: 4px; }
63
- .tool-args { margin-top: 6px; font-family: var(--font-mono); font-size: 12px; line-height: 1.7; color: var(--code-ink); background: var(--code-bg); padding: 10px 12px; border-radius: 6px; white-space: pre-wrap; word-break: break-word; }
70
+ .tool-name { display: inline-flex; align-items: center; font-family: var(--font-mono); font-size: 11px; font-weight: 600; color: var(--acc-ink, var(--ink)); background: var(--acc-bg, var(--secondary)); border: 1px solid var(--acc-line, var(--border)); padding: 2px 9px; border-radius: 4px; }
71
+ .tool-args { margin-top: 6px; font-family: var(--font-mono); font-size: 12px; line-height: 1.7; color: var(--ink-secondary); background: var(--secondary); border: 1px solid var(--border); padding: 10px 12px; border-radius: 6px; white-space: pre-wrap; word-break: break-word; }
72
+ .step.tool-step { border-left: 3px solid var(--acc-strip, var(--rule-strong)); }
73
+ .acc-bash { --acc-bg: #f7efda; --acc-line: #ecdcb2; --acc-ink: #8a5a12; --acc-strip: #dcb45e; }
74
+ .acc-edit { --acc-bg: #e8f0fa; --acc-line: #d0dff1; --acc-ink: #1d4e89; --acc-strip: #7fa8d8; }
75
+ .acc-read { --acc-bg: #e5f2ec; --acc-line: #cbe3d7; --acc-ink: #1a6b52; --acc-strip: #74bda0; }
76
+ .acc-agent { --acc-bg: #efeaf8; --acc-line: #ded3ef; --acc-ink: #5b3e96; --acc-strip: #a78fd6; }
77
+ .acc-web { --acc-bg: #e3f1f6; --acc-line: #c8e2ea; --acc-ink: #176478; --acc-strip: #6fb6ca; }
78
+ .acc-other { --acc-bg: var(--secondary); --acc-line: var(--border); --acc-ink: var(--ink-secondary); --acc-strip: var(--rule-strong); }
64
79
  .metrics { font-family: var(--font-mono); font-size: 11px; color: var(--faint); margin-top: 4px; }
65
80
  .turn-divider { border-top: 1px solid var(--border); margin: 24px 0; }
66
81
  """
67
82
 
83
+ # Sticky bottom confirmation bar injected only in --confirm mode. Styling
84
+ # reuses the page's CSS variables (white surface, top border, ink text, pill
85
+ # buttons) so it reads as part of the same www.benchflow.ai design language.
86
+ # The <style> block rides along with the snippet so plain (non-confirm) pages
87
+ # carry zero confirm markup or CSS.
88
+ _CONFIRM_BAR_HTML = """\
89
+ <style>
90
+ body { padding-bottom: 104px; }
91
+ .confirm-bar { position: fixed; bottom: 0; left: 0; right: 0; background: var(--card); border-top: 1px solid var(--border); box-shadow: 0 -1px 3px rgba(10, 10, 10, 0.05); z-index: 10; }
92
+ .confirm-inner { max-width: 960px; margin: 0 auto; padding: 14px 20px; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
93
+ .confirm-question { font-size: 14px; font-weight: 500; color: var(--ink); }
94
+ .confirm-actions { display: flex; gap: 8px; flex: none; }
95
+ .confirm-btn { font-family: var(--font-sans); font-size: 13px; font-weight: 600; letter-spacing: -0.01em; padding: 8px 18px; border-radius: 9999px; cursor: pointer; transition: background 0.15s ease; }
96
+ .confirm-btn.approve { background: var(--ink); color: var(--background); border: 1px solid var(--ink); }
97
+ .confirm-btn.approve:hover { background: #262626; }
98
+ .confirm-btn.reject { background: var(--card); color: var(--ink); border: 1px solid var(--rule-strong); }
99
+ .confirm-btn.reject:hover { background: var(--secondary); }
100
+ </style>
101
+ <div class="confirm-bar">
102
+ <div class="confirm-inner" id="confirm-inner">
103
+ <span class="confirm-question">Submit this trajectory to the BenchFlow eval prize?</span>
104
+ <div class="confirm-actions">
105
+ <button class="confirm-btn approve" onclick="__benchDecide('approve')">Approve &amp; submit</button>
106
+ <button class="confirm-btn reject" onclick="__benchDecide('reject')">Not this one</button>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ <script>
111
+ async function __benchDecide(choice) {
112
+ try {
113
+ await fetch("/decision", { method: "POST", body: choice });
114
+ } catch (err) {
115
+ // The server exits right after answering; a dropped socket is still a
116
+ // delivered decision, so fall through to the confirmation note.
117
+ }
118
+ document.getElementById("confirm-inner").innerHTML =
119
+ choice === "approve"
120
+ ? '<span class="confirm-question">Approved — go back to your agent.</span>'
121
+ : '<span class="confirm-question">Rejected — tell your agent which session instead.</span>';
122
+ }
123
+ </script>
124
+ """
125
+
126
+
127
+ def _inject_confirm_bar(page: str) -> str:
128
+ """Append the confirm bar just before </body> (or at the end as fallback)."""
129
+ if "</body>" in page:
130
+ return page.replace("</body>", f"{_CONFIRM_BAR_HTML}</body>", 1)
131
+ return page + _CONFIRM_BAR_HTML
132
+
133
+
68
134
  # Small BenchFlow wordmark header (inline SVG logo from the site's icon set —
69
135
  # no external requests) shown above the page title on every viewer page.
70
136
  _WORDMARK_HTML = (
@@ -87,6 +153,28 @@ _WORDMARK_HTML = (
87
153
  )
88
154
 
89
155
 
156
+ # Tool kind → accent class. Substring matching so it covers Claude Code tool
157
+ # names (Bash, Write, Edit, Read, Agent, WebSearch, ...), ACP kinds (execute,
158
+ # edit, read, search, fetch, ...), and Codex function names (shell, ...).
159
+ # Order matters: earlier entries win (e.g. "websearch" hits web before read).
160
+ _TOOL_ACCENTS: tuple[tuple[str, tuple[str, ...]], ...] = (
161
+ ("acc-web", ("web", "search", "fetch", "grep", "glob", "browser")),
162
+ ("acc-bash", ("bash", "shell", "exec", "terminal", "command")),
163
+ ("acc-edit", ("write", "edit", "patch", "delete", "move", "notebook")),
164
+ ("acc-read", ("read", "cat", "view", "ls", "list")),
165
+ ("acc-agent", ("agent", "task", "skill", "oracle")),
166
+ )
167
+
168
+
169
+ def _tool_accent_class(name: str) -> str:
170
+ """Map a tool name/kind to its accent CSS class (presentation only)."""
171
+ lowered = name.lower()
172
+ for css_class, needles in _TOOL_ACCENTS:
173
+ if any(needle in lowered for needle in needles):
174
+ return css_class
175
+ return "acc-other"
176
+
177
+
90
178
  def render_turn(events: list[dict], turn_number: int, prompt: str = "") -> str:
91
179
  """Render one turn's events as HTML blocks."""
92
180
  blocks = []
@@ -103,6 +191,9 @@ def render_turn(events: list[dict], turn_number: int, prompt: str = "") -> str:
103
191
  # Group: thinking → text → tool_use → tool_result → thinking → ...
104
192
  pending_thinking = ""
105
193
  pending_text = ""
194
+ # tool_use id → accent class, so each tool_result can match its call's
195
+ # accent and shell results alone keep the dark terminal treatment.
196
+ accent_by_tool_id: dict[str, str] = {}
106
197
 
107
198
  for event in events:
108
199
  etype = event.get("type", "")
@@ -160,7 +251,13 @@ def render_turn(events: list[dict], turn_number: int, prompt: str = "") -> str:
160
251
  f"</div>"
161
252
  )
162
253
 
163
- blocks.append(f'<div class="step agent">{"".join(parts)}</div>')
254
+ accent = _tool_accent_class(block.get("name", ""))
255
+ if block.get("id"):
256
+ accent_by_tool_id[str(block["id"])] = accent
257
+ blocks.append(
258
+ f'<div class="step agent tool-step {accent}">'
259
+ f"{''.join(parts)}</div>"
260
+ )
164
261
 
165
262
  elif etype == "user":
166
263
  content = event.get("message", {}).get("content", "")
@@ -181,8 +278,13 @@ def render_turn(events: list[dict], turn_number: int, prompt: str = "") -> str:
181
278
  display = "[binary content]"
182
279
  else:
183
280
  display = html.escape(raw[:400])
281
+ accent = accent_by_tool_id.get(
282
+ str(block.get("tool_use_id", "")), "acc-other"
283
+ )
284
+ term = " term" if accent == "acc-bash" else ""
184
285
  blocks.append(
185
- f'<div class="step output"><pre>{display}</pre></div>'
286
+ f'<div class="step output tool-step {accent}{term}">'
287
+ f"<pre>{display}</pre></div>"
186
288
  )
187
289
  elif block.get("type") == "text" and block.get("text"):
188
290
  texts.append(str(block["text"]))
@@ -408,8 +510,13 @@ def _render_acp_events(
408
510
  kind = html.escape(event.get("kind", ""))
409
511
  event_title = html.escape(event.get("title", ""))
410
512
  status = event.get("status", "")
513
+ # ACP kinds are coarse ("other" covers Skill/Task/...); when the
514
+ # kind doesn't map, fall back to the human title for the accent.
515
+ accent = _tool_accent_class(event.get("kind", ""))
516
+ if accent == "acc-other":
517
+ accent = _tool_accent_class(event.get("title", ""))
411
518
  blocks.append(
412
- f'<div class="step agent">'
519
+ f'<div class="step agent tool-step {accent}">'
413
520
  f'<div class="tool"><span class="tool-name">{kind}</span> {event_title}</div>'
414
521
  f'<div class="metrics">{status}</div>'
415
522
  f"</div>"
@@ -578,9 +685,21 @@ def _stream_json_page(title: str, events: list[dict], turn_blocks: list[str]) ->
578
685
 
579
686
 
580
687
  def serve(
581
- rollout_path: str, port: int = 8888, prompts: list[str] | None = None
582
- ) -> None:
583
- """Serve a trial directory or a session JSONL file as a web page."""
688
+ rollout_path: str,
689
+ port: int = 8888,
690
+ prompts: list[str] | None = None,
691
+ confirm: bool = False,
692
+ ) -> str | None:
693
+ """Serve a trial directory or a session JSONL file as a web page.
694
+
695
+ With ``confirm=True`` the page carries an Approve/Reject bar posting to
696
+ ``/decision``; the first valid decision shuts the server down and is
697
+ returned as ``"approved"`` or ``"rejected"`` after printing a
698
+ machine-readable ``DECISION: <value>`` line to stdout. Without it the
699
+ server runs until Ctrl+C and the return value is ``None`` — exactly the
700
+ pre-confirm behavior (no bar, no POST endpoint).
701
+ """
702
+ import threading
584
703
  from http.server import HTTPServer, SimpleHTTPRequestHandler
585
704
 
586
705
  path = Path(rollout_path)
@@ -600,11 +719,20 @@ def serve(
600
719
  print(f"No trajectories found in {path}")
601
720
  sys.exit(1)
602
721
  if write_sidecar:
722
+ # The sidecar stays the plain page: the confirm bar is a one-shot
723
+ # interaction against this live server, not part of the artifact.
603
724
  (path / "trajectory.html").write_text(html_content)
725
+ if confirm:
726
+ html_content = _inject_confirm_bar(html_content)
604
727
 
605
728
  print(f"Trajectory viewer: http://localhost:{port}")
606
729
  print(f"Trial: {path}")
607
- print("Press Ctrl+C to stop\n")
730
+ if confirm:
731
+ print("Waiting for Approve / Not this one in the browser (Ctrl+C to stop)\n")
732
+ else:
733
+ print("Press Ctrl+C to stop\n")
734
+
735
+ decision: str | None = None
608
736
 
609
737
  class Handler(SimpleHTTPRequestHandler):
610
738
  def do_GET(self):
@@ -616,11 +744,44 @@ def serve(
616
744
  def log_message(self, format, *args):
617
745
  pass
618
746
 
619
- server = HTTPServer(("localhost", port), Handler)
747
+ handler_cls: type[SimpleHTTPRequestHandler] = Handler
748
+
749
+ if confirm:
750
+
751
+ class ConfirmHandler(Handler):
752
+ def do_POST(self):
753
+ nonlocal decision
754
+ if self.path != "/decision":
755
+ self.send_error(404)
756
+ return
757
+ length = int(self.headers.get("Content-Length") or 0)
758
+ body = self.rfile.read(length).decode("utf-8", "replace").strip()
759
+ if body not in ("approve", "reject"):
760
+ self.send_error(400, "Body must be 'approve' or 'reject'")
761
+ return
762
+ decision = "approved" if body == "approve" else "rejected"
763
+ self.send_response(200)
764
+ self.send_header("Content-Type", "text/plain; charset=utf-8")
765
+ self.end_headers()
766
+ self.wfile.write(decision.encode())
767
+ # shutdown() blocks until serve_forever returns, and this
768
+ # handler runs inside that loop — stop from a helper thread.
769
+ threading.Thread(target=server.shutdown, daemon=True).start()
770
+
771
+ handler_cls = ConfirmHandler
772
+
773
+ server = HTTPServer(("localhost", port), handler_cls)
620
774
  try:
621
775
  server.serve_forever()
622
776
  except KeyboardInterrupt:
623
777
  print("\nStopped.")
778
+ return None
779
+ finally:
780
+ server.server_close()
781
+
782
+ if decision is not None:
783
+ print(f"DECISION: {decision}", flush=True)
784
+ return decision
624
785
 
625
786
 
626
787
  if __name__ == "__main__":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: benchflow
3
- Version: 0.7.2.dev1986
3
+ Version: 0.7.2.dev1994
4
4
  Summary: Multi-turn agent benchmarking with ACP — run any agent, any model, any provider.
5
5
  Project-URL: Homepage, https://github.com/benchflow-ai/benchflow
6
6
  Project-URL: Repository, https://github.com/benchflow-ai/benchflow
@@ -89,20 +89,23 @@ BenchFlow is a universal environment framework: it runs AI agents against task e
89
89
 
90
90
  ## Quick start: 1. Submit a trajectory
91
91
 
92
- Proud of a Claude Code or Codex session? Copy this line into Claude Code,
93
- Codex, Cursor, or any coding agent. That is the $2,000 eval prize path. No
94
- BenchFlow account, API key, or Azure login.
92
+ **Send these to your coding agent.** Copy
93
+ the block below and paste it as a message to Claude Code, Codex, OpenCode, or
94
+ Cursor. That is the $2,000 eval prize path. No BenchFlow account, API key, or
95
+ Azure login.
95
96
 
96
- ```
97
- Submit my best local Claude Code or Codex session to the BenchFlow eval prize. Read https://raw.githubusercontent.com/benchflow-ai/benchflow/main/.agents/skills/benchflow-traj-upload/SKILL.md and follow it: find a session, open the viewer, upload only after I review it.
98
- ```
97
+ > Submit my relevant local Claude Code, Codex, OpenCode, or Cursor session from the re:Agent e2e agentic science / ai4bio hackathon (last 72 hours, on this laptop) to the BenchFlow eval prize.
98
+ >
99
+ > 1. First make sure the latest benchflow CLI is installed: uv tool install --python 3.12 --upgrade --force benchflow
100
+ >
101
+ > 2. Then read https://raw.githubusercontent.com/benchflow-ai/benchflow/main/.agents/skills/benchflow-traj-upload/SKILL.md and follow it: find a session, open the viewer, and upload only after I (the human) review it.
99
102
 
100
103
  The agent finds sessions on this machine, opens the viewer, and uploads after
101
104
  you like what you see.
102
105
 
103
- Be on the latest BenchFlow first — `uv tool install --python 3.12 --upgrade
104
- --force benchflow`. The `bench traj` commands print a one-line upgrade hint
105
- when a newer release is available.
106
+ The prompt has the agent install the latest BenchFlow first (`uv tool
107
+ install --python 3.12 --upgrade --force benchflow`). The `bench traj` commands
108
+ also print a one-line upgrade hint when a newer release is available.
106
109
 
107
110
  Prefer the terminal instead? The guided upload inspects before anything leaves
108
111
  your machine — it renders a redacted trajectory report (step counts, masked
@@ -128,7 +131,7 @@ bench traj setup
128
131
  ```
129
132
 
130
133
  `npx skills add` asks which agents to install for. `bench traj setup` copies
131
- the skill into this project and prints the same paste line. See the
134
+ the skill into this project and prints the same agent prompt. See the
132
135
  [upload skill](./.agents/skills/benchflow-traj-upload/SKILL.md).
133
136
 
134
137
  ## Quick start: 2. Run with a ChatGPT or Claude subscription
@@ -108,7 +108,7 @@ benchflow/cli/environment.py,sha256=q3G8G2i0rs_wqGIWzULnnc6Nt8TlJLO12zuYlro-5-A,
108
108
  benchflow/cli/eval_artifacts.py,sha256=4CbS8aIaynCk2PUeWA4Qo31F8SH7_rxtwB7po8_O3v4,11275
109
109
  benchflow/cli/eval_lift.py,sha256=0Tyn9ZVIzAIeZ9vm0T7onBLre48gliKf0K8NrD7m1j0,3072
110
110
  benchflow/cli/hub.py,sha256=uX5O-q_9CMRb9VlW06HkA5EbKUa6xORnNNTNmMM2H_0,7029
111
- benchflow/cli/main.py,sha256=nSVz2r1Iq0_i7Mu4Mqx1-ok3jy_7C2k74nblROKe1UA,47711
111
+ benchflow/cli/main.py,sha256=gjsxz90nwHaYq-SMpfzFG-zSbAzvxW6GZAy6JCbiWe4,48319
112
112
  benchflow/cli/monitor.py,sha256=-0C4OzZqgUj7Lvi5obJVsK-M81OZwaZNEpis71Kqyj4,3427
113
113
  benchflow/cli/review.py,sha256=ysR6No9oFu0TgcbucNG9YmXY5FkMUHobdr6F4rHQmQw,7892
114
114
  benchflow/cli/sandbox.py,sha256=QrLASbsfzPGVosW_s5XxE3_mcXqTzbU2ZnnC9MADpXM,9849
@@ -116,7 +116,7 @@ benchflow/cli/skills.py,sha256=2Apv1iqvmstWEhY6O-PRdxKc8RYwR6f7MvbYfl2dWm0,7497
116
116
  benchflow/cli/tasks.py,sha256=b1XP1EZ79ZtK_1shF65qZyv27EcKKFZYV35M4DzxYKI,18534
117
117
  benchflow/cli/trace_import.py,sha256=NHDieFdiKQ-2Sr1-GF8Z5EDcbGsz2qq_kFE0rXacC2o,14705
118
118
  benchflow/cli/train.py,sha256=MIP_JxptitH348bWWURpcWt-TmDAw1s8d-RUU7jBaIg,23635
119
- benchflow/cli/traj.py,sha256=2QlXCLNywIKsm8uQVsQ1TAEnaafV6b0dfLx37O2wEsU,23691
119
+ benchflow/cli/traj.py,sha256=tX4mtAXbEwEhbwegUTHl_IBIFoxKtVlrwrlv8MPf764,24290
120
120
  benchflow/continue_run/__init__.py,sha256=dWidng7kJphVUtbP1a1Ma0Lyjxj5yLgQZIApaKCJUew,1479
121
121
  benchflow/continue_run/batch.py,sha256=8BNSInyp_Ol6Fcrg4ezBJdi3cBS12nC8mf963_x4fIg,4146
122
122
  benchflow/continue_run/orchestrator.py,sha256=wYoseBNE_rZ0EjNwWDyqfave54xNRe43sCd_O4VJjyk,27676
@@ -291,9 +291,9 @@ benchflow/trajectories/sessions.py,sha256=u_6x1WyT8YtKUVOb6b3myTCHhqn58a8NHCLAvQ
291
291
  benchflow/trajectories/tree.py,sha256=c0jyoP9OurDQq-zRtPc66edzWUvLGwQQz0jgtyqSw2U,5576
292
292
  benchflow/trajectories/trl_sft_tokenization.py,sha256=-Ef0bP9cuzfzOz5nIv2mEF3taQIEw68h6SM1YO-Mwn4,8643
293
293
  benchflow/trajectories/types.py,sha256=aSJclec3dOCUlfFNDL8GfzvkAcBo5r-dvlHE0MnGdMo,27554
294
- benchflow/trajectories/viewer.py,sha256=59wr8pcWWsCYzuI_sFzTSFJFHeXzH6Yklrh8UsF1ksg,26561
295
- benchflow-0.7.2.dev1986.dist-info/METADATA,sha256=D7gO4mBmAwaKA5_bFHpuy2Hu2_vfMEBQLdPfVoCyFEk,15261
296
- benchflow-0.7.2.dev1986.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
297
- benchflow-0.7.2.dev1986.dist-info/entry_points.txt,sha256=qPMBVtH4qz3P4nMALEAdTbvpxvsVch9RhqshZKQn1dE,84
298
- benchflow-0.7.2.dev1986.dist-info/licenses/LICENSE,sha256=ohnbHkuBylrTtOvaZJk9Hz7dRwmaEEGAb5ZcBV4Xuhw,10779
299
- benchflow-0.7.2.dev1986.dist-info/RECORD,,
294
+ benchflow/trajectories/viewer.py,sha256=4V7HMjJtUt8_HE1dStZwzF9W_3yefHNas7R3Y1iZmvY,34760
295
+ benchflow-0.7.2.dev1994.dist-info/METADATA,sha256=BZC_LqHIgXKHtV4fMAqIDeaodQRbu2iDK2awvDuJ3fI,15565
296
+ benchflow-0.7.2.dev1994.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
297
+ benchflow-0.7.2.dev1994.dist-info/entry_points.txt,sha256=qPMBVtH4qz3P4nMALEAdTbvpxvsVch9RhqshZKQn1dE,84
298
+ benchflow-0.7.2.dev1994.dist-info/licenses/LICENSE,sha256=ohnbHkuBylrTtOvaZJk9Hz7dRwmaEEGAb5ZcBV4Xuhw,10779
299
+ benchflow-0.7.2.dev1994.dist-info/RECORD,,