Terminal Recorder¶
The terminal recorder captures an already-running tmux-backed agent session for later parser and lifecycle testing. The maintained developer wrapper lives under tools/terminal_record and delegates to houmao.terminal_record.
For maintainer-oriented design notes and change guidance, see the Terminal Recorder Developer Guide.
Use the repo-managed Python environment when invoking it:
pixi run python -m tools.terminal_record start --mode active --target-session HOUMAO-gpu --tool codex
Recorder analysis accepts --observed-version <version> so replay selects the same bounded detector profile as the live session. Omitting it selects the conservative fallback profile.
Use a high-rate source capture for canonical labeling, then derive deterministic lower-cadence streams without losing source traceability:
pixi run python -m houmao.terminal_record derive-stream \
--run-root <recording> \
--target-sample-interval-seconds 0.5 \
--sampling-mode jitter \
--seed 17 \
--output-path <recording>/pane_snapshots_2hz-jitter.ndjson
--sampling-mode supports canonical regular, jitter, drop, and burst schedules. The older internal spellings jittered, gapped, and bursty remain accepted for existing replay plans. Every derived row records source_sample_id and source_elapsed_seconds. Qualification derives 10 Hz, 5 Hz, and 2 Hz fixed streams plus seeded irregular variants from the frozen 20 Hz source without modifying that source. Validation maps each retained row back to its audited source label, reports labels skipped because no sample was retained, compresses mismatches into contiguous ranges, and reports pending-input transition drift against the retained cadence.
Modes¶
active
- creates a recorder-owned tmux attach path
- records
asciinemainput frames with--capture-input - publishes recorder state back into the target tmux session so repo-managed
send-keyscalls can append structured managed-input events - starts with
input_capture_level=authoritative_managed - degrades to
managed_onlyif the run becomes tainted, for example when extra tmux clients attach to the target session
passive
- observes a live tmux session without becoming the required input path
- records the visual session and pane snapshots only
- marks the run as
input_capture_level=output_only
Commands¶
Start an active run against a single-pane session:
pixi run python -m tools.terminal_record start --mode active --target-session HOUMAO-gpu --tool codex
Start a passive run against a specific pane:
pixi run python -m tools.terminal_record start --mode passive --target-session HOUMAO-gpu --target-pane %1 --tool codex
Kimi Code signal-corpus captures use the same recorder with --tool kimi, a repo-local run root, and a high-rate sampling interval:
pixi run python -m tools.terminal_record start \
--mode passive \
--target-session HMKIMI-dev-001 \
--tool kimi \
--run-root tmp/kimi-tui-tracking/dev-001 \
--sample-interval-seconds 0.1 \
--duration-seconds 30
Inspect a run:
pixi run python -m tools.terminal_record status --run-root tmp/terminal_record/20260319-120000-HOUMAO-gpu
Stop a run:
pixi run python -m tools.terminal_record stop --run-root tmp/terminal_record/20260319-120000-HOUMAO-gpu
Analyze recorded pane snapshots:
pixi run python -m tools.terminal_record analyze --run-root tmp/terminal_record/20260319-120000-HOUMAO-gpu
Derive a low-rate stream from a high-rate capture:
Analyze the derived stream and keep its observed outputs beside the source stream:
pixi run python -m tools.terminal_record analyze \
--run-root tmp/kimi-tui-tracking/dev-001 \
--tool kimi \
--snapshots-path tmp/kimi-tui-tracking/dev-001/pane_snapshots_2fps.ndjson \
--output-tag 2fps
Persist a label for a recorded checkpoint:
pixi run python -m tools.terminal_record add-label \
--run-root tmp/terminal_record/20260319-120000-HOUMAO-gpu \
--label-id trust-prompt-blocked \
--scenario-id trust-prompt-recovery \
--sample-id s000021 \
--diagnostics-availability available \
--surface-pending-input unknown \
--turn-phase unknown \
--last-turn-source none \
--evidence-note "bounded approval panel with numbered choices"
Validate observations against labels:
pixi run python -m tools.terminal_record validate --run-root tmp/kimi-tui-tracking/dev-001
pixi run python -m tools.terminal_record validate \
--run-root tmp/kimi-tui-tracking/dev-001 \
--state-path tmp/kimi-tui-tracking/dev-001/state_observed_2fps.ndjson \
--parser-path tmp/kimi-tui-tracking/dev-001/parser_observed_2fps.ndjson
Artifact Contract¶
Each run writes one recorder-owned root under tmp/terminal_record/<run-id>/ unless --run-root overrides it.
Important artifacts:
manifest.json: recorder mode, tmux target, capture authority, taint metadata, attach command, and timing metadatalive_state.json: long-running controller status used bystatusandstopsession.cast: operator-facingasciinemacast produced via the repo-ownedpixi run asciinematask backed byextern/orphan/bin/asciinema-x86_64-unknown-linux-gnupane_snapshots.ndjson: exact tmux pane samples captured withtmux capture-pane; this is the authoritative replay surface, and eachoutput_textentry can feed the standalone shared TUI tracker directlypane_snapshots_2fps.ndjson: optional derived stream produced frompane_snapshots.ndjson; each derived row records the selected source sample insource_sample_idinput_events.ndjson: normalized input events when the selected mode provides managed input captureparser_observed.ndjson: parser-facing observations generated byanalyzestate_observed.ndjson: official tracked-state observations generated byanalyze, including diagnostics posture, independent accepting/editing/ready/pending surface fields,turn, andlast_turn, with any retained readiness/completion fields treated as debug-onlylabels.json: structured operator labels for recorded samples or sample ranges; every label requiressurface_pending_input=yes|no|unknown
The recorder keeps session.cast for human review, but automated replay and testing should consume pane_snapshots.ndjson.
Managed send-keys Integration¶
The existing send-keys path remains the runtime delivery mechanism for mixed tmux control input. When an active recorder is live for the same tmux-backed session, the backend's send_input_ex() method appends a structured managed_send_keys event to input_events.ndjson after successful tmux delivery.
This integration is additive. Recorder logging failures do not block the underlying control-input delivery.