overcode 0.5.1__tar.gz → 0.5.2__tar.gz
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.
- {overcode-0.5.1/src/overcode.egg-info → overcode-0.5.2}/PKG-INFO +1 -1
- {overcode-0.5.1 → overcode-0.5.2}/pyproject.toml +1 -1
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/split.py +57 -13
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/history_reader.py +43 -5
- {overcode-0.5.1 → overcode-0.5.2/src/overcode.egg-info}/PKG-INFO +1 -1
- {overcode-0.5.1 → overcode-0.5.2}/LICENSE +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/MANIFEST.in +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/README.md +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/setup.cfg +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/__init__.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/agent_scanner.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/__init__.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/base.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/claude_code.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/codex.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/codex_stats.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/grok.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/grok_stats.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/opencode.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/backends/opencode_stats.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/bundled_skills.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/claude_config.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/claude_pid.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/__init__.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/__main__.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/_shared.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/agent.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/budget.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/config.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/daemon.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/doctor.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/focal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/hooks.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/jobs.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/monitoring.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/parallelism.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/perms.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/sister.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/skills.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/tags.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/cli/wrappers.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/config.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/daemon_claude_skill.md +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/daemon_logging.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/daemon_utils.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/data_export.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/dependency_check.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/doctor.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/duration.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/exceptions.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/follow_mode.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/hook_handler.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/hook_status_detector.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/implementations.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/interfaces.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/job_launcher.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/job_manager.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/launcher.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/logging_config.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/mocks.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/monitor_daemon.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/monitor_daemon_core.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/monitor_daemon_state.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/notifier.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/opencode_plugin/overcode-telemetry.js +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/pid_utils.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/presence_logger.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/pricing.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/process_resources.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/protocols.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/sandbox_detect.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/session_manager.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/settings.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/sister_controller.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/sister_poller.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/ssh_provisioner.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/standing_instructions.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/stats_reader.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/status_constants.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/status_detector.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/status_detector_factory.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/status_history.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/status_patterns.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/summarizer_client.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/summarizer_component.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/summary_columns.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/summary_groups.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/supervisor_daemon.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/supervisor_daemon_core.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/supervisor_layout.sh +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/testing/__init__.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/testing/renderer.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/testing/tmux_driver.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/testing/tui_eye.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/testing/tui_eye_skill.md +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/time_context.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tmux_manager.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tmux_utils.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui.tcss +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_actions/__init__.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_actions/daemon.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_actions/input.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_actions/navigation.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_actions/session.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_actions/view.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_helpers.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_logic.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_render.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/__init__.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/agent_select_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/command_bar.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/daemon_panel.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/daemon_status_bar.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/fullscreen_preview.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/help_overlay.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/instruction_history_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/job_summary.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/jump_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/modal_base.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/new_agent_defaults_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/new_agent_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/passthru_config_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/preview_pane.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/session_summary.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/sister_selection_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/status_timeline.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/summary_config_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/tmux_config_modal.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/tui_widgets/tui_log_panel.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/usage_monitor.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web/__init__.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web/templates/analytics.html +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web/templates/dashboard.html +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web_api.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web_chartjs.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web_control_api.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web_server.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web_server_runner.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/web_templates.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode/wrapper.py +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode.egg-info/SOURCES.txt +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode.egg-info/dependency_links.txt +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode.egg-info/entry_points.txt +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode.egg-info/requires.txt +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/src/overcode.egg-info/top_level.txt +0 -0
- {overcode-0.5.1 → overcode-0.5.2}/tests/test_e2e_multi_agent_jokes.py +0 -0
|
@@ -348,6 +348,30 @@ def _setup_keybindings(linked_session: str = "", toggle_key: str = "") -> None:
|
|
|
348
348
|
f"tmux show-window-option -t {linked_session} -v @is_ssh_proxy "
|
|
349
349
|
"2>/dev/null | grep -q on"
|
|
350
350
|
)
|
|
351
|
+
# Not every backend leaves its transcript in tmux's scrollback.
|
|
352
|
+
# Claude Code draws inline, so tmux history holds the trace and
|
|
353
|
+
# copy mode is the way to review it. opencode (and other
|
|
354
|
+
# full-screen TUIs) run on the alternate screen — tmux keeps no
|
|
355
|
+
# history for it — and scroll their own message list, asking
|
|
356
|
+
# tmux for mouse events to do so. Forcing such a pane into copy
|
|
357
|
+
# mode shows an empty, frozen buffer and swallows input until
|
|
358
|
+
# something cancels the mode. So the local branches below first
|
|
359
|
+
# ask the linked session's active pane whether its program owns
|
|
360
|
+
# scrolling (`alternate_on` / `mouse_any_flag`, the same signals
|
|
361
|
+
# tmux's own default WheelUpPane binding consults) and, if so,
|
|
362
|
+
# hand the gesture to the program instead of to copy mode.
|
|
363
|
+
#
|
|
364
|
+
# The pass-through is prefixed with `copy-mode -q` so a pane
|
|
365
|
+
# that was already stuck in copy mode (a scroll before these
|
|
366
|
+
# bindings were installed, or a stray prefix-[) is released
|
|
367
|
+
# first — `alternate_on` stays true inside copy mode, while
|
|
368
|
+
# `mouse_any_flag` reports the copy-mode screen and drops to 0.
|
|
369
|
+
# `-q` is a no-op outside a mode, unlike `send-keys -X cancel`,
|
|
370
|
+
# which errors and paints the status line (#454).
|
|
371
|
+
_pane_owns_screen = (
|
|
372
|
+
f"#{{||:#{{alternate_on}},#{{mouse_any_flag}}}}"
|
|
373
|
+
)
|
|
374
|
+
_release = f"copy-mode -q -t {linked_session}"
|
|
351
375
|
_tmux(
|
|
352
376
|
"bind-key", "-n", "PPage",
|
|
353
377
|
"if-shell", "-F",
|
|
@@ -355,11 +379,15 @@ def _setup_keybindings(linked_session: str = "", toggle_key: str = "") -> None:
|
|
|
355
379
|
# SSH proxy: send prefix + PPage so the REMOTE tmux enters
|
|
356
380
|
# copy mode (the remote's root-table PPage is overridden by
|
|
357
381
|
# overcode, but prefix PPage still maps to copy-mode -u).
|
|
358
|
-
# Local:
|
|
382
|
+
# Local: full-screen program → deliver PPage to it (opencode
|
|
383
|
+
# maps PageUp to messages_page_up); otherwise enter copy mode
|
|
384
|
+
# directly on the linked session.
|
|
359
385
|
f'if-shell "{_ssh_check}" '
|
|
360
386
|
f'"send-keys -t {linked_session} C-b ; '
|
|
361
387
|
f'send-keys -t {linked_session} PPage" '
|
|
362
|
-
f'"
|
|
388
|
+
f'"if-shell -t {linked_session} -F \'{_pane_owns_screen}\''
|
|
389
|
+
f' \'{_release} ; send-keys -t {linked_session} PPage\''
|
|
390
|
+
f' \'copy-mode -t {linked_session} -u\'"',
|
|
363
391
|
"send-keys PPage",
|
|
364
392
|
)
|
|
365
393
|
# PageDown in the inner session's copy mode
|
|
@@ -377,8 +405,15 @@ def _setup_keybindings(linked_session: str = "", toggle_key: str = "") -> None:
|
|
|
377
405
|
# which has no scrollback (just rendered inner tmux frames).
|
|
378
406
|
#
|
|
379
407
|
# For local agents: enter copy mode in the inner session then
|
|
380
|
-
# send scroll commands
|
|
381
|
-
#
|
|
408
|
+
# send scroll commands — unless the program owns its screen (see
|
|
409
|
+
# `_pane_owns_screen`), in which case `send-keys -M` passes the
|
|
410
|
+
# mouse event through to the nested tmux client. That client is
|
|
411
|
+
# on this same server, so the event re-enters this root table;
|
|
412
|
+
# its window is the agent's, not the split window, so it falls
|
|
413
|
+
# to the default branch below, whose `mouse_any_flag` test hands
|
|
414
|
+
# the wheel to the program (opencode scrolls its transcript).
|
|
415
|
+
# For SSH proxies: forward mouse events to the remote tmux
|
|
416
|
+
# through the pane.
|
|
382
417
|
_in_bottom = (
|
|
383
418
|
f"#{{&&:#{{==:#{{window_name}},{SPLIT_WINDOW_NAME}}},"
|
|
384
419
|
f"#{{!=:#{{pane_index}},{_base}}}}}"
|
|
@@ -388,12 +423,15 @@ def _setup_keybindings(linked_session: str = "", toggle_key: str = "") -> None:
|
|
|
388
423
|
"if-shell", "-F", _in_bottom,
|
|
389
424
|
# SSH proxy: send prefix+PPage to enter copy mode on remote
|
|
390
425
|
# (no-op if already in copy mode, then PPage scrolls up).
|
|
391
|
-
# Local:
|
|
426
|
+
# Local: program owns screen → pass the mouse event through;
|
|
427
|
+
# otherwise enter copy mode + scroll up directly.
|
|
392
428
|
f'if-shell "{_ssh_check}" '
|
|
393
429
|
f'"send-keys -t {linked_session} C-b ; '
|
|
394
430
|
f'send-keys -t {linked_session} PPage" '
|
|
395
|
-
f'"
|
|
396
|
-
f'
|
|
431
|
+
f'"if-shell -t {linked_session} -F \'{_pane_owns_screen}\''
|
|
432
|
+
f' \'{_release} ; send-keys -M\''
|
|
433
|
+
f' \'copy-mode -t {linked_session} -e ; '
|
|
434
|
+
f'send-keys -t {linked_session} -X -N 3 scroll-up\'"',
|
|
397
435
|
# Default behaviour for other contexts
|
|
398
436
|
"if-shell -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' "
|
|
399
437
|
"'send-keys -M' 'copy-mode -e'",
|
|
@@ -405,10 +443,15 @@ def _setup_keybindings(linked_session: str = "", toggle_key: str = "") -> None:
|
|
|
405
443
|
# `send-keys -X -N 3 scroll-down` only works inside copy mode; out
|
|
406
444
|
# of copy mode it errors with "not in a mode" and tmux paints the
|
|
407
445
|
# status line, leaving the client looking frozen (#454). So for
|
|
408
|
-
# the local branch we gate on
|
|
409
|
-
#
|
|
410
|
-
#
|
|
411
|
-
#
|
|
446
|
+
# the local branch we gate on the linked session's pane: in copy
|
|
447
|
+
# mode over real scrollback (not alternate screen) → scroll-down
|
|
448
|
+
# 3 lines (matches wheel-up); otherwise → release any copy mode
|
|
449
|
+
# and `send-keys -M`, which passes the wheel to the nested
|
|
450
|
+
# client. A program that asked for mouse events (opencode)
|
|
451
|
+
# scrolls its own transcript; one that didn't (Claude Code,
|
|
452
|
+
# already at the bottom) never sees it — tmux drops mouse events
|
|
453
|
+
# for panes without a mouse mode, so it's as harmless as the
|
|
454
|
+
# plain NPage keystroke this branch used to send.
|
|
412
455
|
#
|
|
413
456
|
# SSH proxy branch keeps the unconditional NPage: we can't
|
|
414
457
|
# observe the remote tmux's mode from here, and NPage is safe in
|
|
@@ -419,9 +462,10 @@ def _setup_keybindings(linked_session: str = "", toggle_key: str = "") -> None:
|
|
|
419
462
|
"if-shell", "-F", _in_bottom,
|
|
420
463
|
f'if-shell "{_ssh_check}" '
|
|
421
464
|
f'"send-keys -t {linked_session} NPage" '
|
|
422
|
-
f'"if-shell -t {linked_session} -F
|
|
465
|
+
f'"if-shell -t {linked_session} -F '
|
|
466
|
+
f'\'#{{&&:#{{pane_in_mode}},#{{==:#{{alternate_on}},0}}}}\''
|
|
423
467
|
f' \'send-keys -t {linked_session} -X -N 3 scroll-down\''
|
|
424
|
-
f' \'send-keys -
|
|
468
|
+
f' \'{_release} ; send-keys -M\'"',
|
|
425
469
|
"send-keys -M",
|
|
426
470
|
)
|
|
427
471
|
|
|
@@ -49,12 +49,15 @@ CLAUDE_PROJECTS_PATH = Path.home() / ".claude" / "projects"
|
|
|
49
49
|
# support extended context.
|
|
50
50
|
MODEL_CONTEXT_WINDOWS: Dict[str, int] = {
|
|
51
51
|
"claude-fable-5": 1_000_000,
|
|
52
|
+
"claude-opus-5": 1_000_000,
|
|
53
|
+
"claude-sonnet-5": 1_000_000,
|
|
52
54
|
"claude-opus-4-8": 1_000_000,
|
|
53
55
|
"claude-opus-4-7": 1_000_000,
|
|
54
56
|
"claude-opus-4-6": 1_000_000,
|
|
55
57
|
"claude-sonnet-4-6": 1_000_000,
|
|
56
58
|
"claude-sonnet-4-5-20250929": 200_000,
|
|
57
59
|
"claude-haiku-4-5-20251001": 200_000,
|
|
60
|
+
"claude-haiku-4-5": 200_000,
|
|
58
61
|
"claude-3-5-sonnet-20241022": 200_000,
|
|
59
62
|
"claude-3-5-haiku-20241022": 200_000,
|
|
60
63
|
"claude-3-opus-20240229": 200_000,
|
|
@@ -78,6 +81,12 @@ MODEL_CONTEXT_WINDOWS: Dict[str, int] = {
|
|
|
78
81
|
"gpt-5.4": 258_400,
|
|
79
82
|
"gpt-5.6-sol": 258_400,
|
|
80
83
|
"gpt-5.6-terra": 258_400,
|
|
84
|
+
# gpt-4o family, reachable via opencode (not by codex, which only drives
|
|
85
|
+
# the 5.x line). 128K per OpenAI's own model docs — these predate the
|
|
86
|
+
# codex CLI's `model_context_window` reporting, so unlike the 5.x rows
|
|
87
|
+
# above there is no live cross-check available for them.
|
|
88
|
+
"gpt-4o-mini": 128_000,
|
|
89
|
+
"gpt-4o": 128_000,
|
|
81
90
|
|
|
82
91
|
# xAI Grok Build models (#469). Sourced from docs.x.ai/docs/models
|
|
83
92
|
# (verified 2026-08-28) — the same source Phase 5's `pricing.py` entries
|
|
@@ -123,12 +132,15 @@ MODEL_SHORT_NAME_MAX_LEN = 7
|
|
|
123
132
|
|
|
124
133
|
MODEL_SHORT_NAMES: Dict[str, str] = {
|
|
125
134
|
"claude-fable-5": "Fb5",
|
|
135
|
+
"claude-opus-5": "Op5",
|
|
136
|
+
"claude-sonnet-5": "Sn5",
|
|
126
137
|
"claude-opus-4-8": "Op4.8",
|
|
127
138
|
"claude-opus-4-7": "Op4.7",
|
|
128
139
|
"claude-opus-4-6": "Op4.6",
|
|
129
140
|
"claude-sonnet-4-6": "Sn4.6",
|
|
130
141
|
"claude-sonnet-4-5-20250929": "Sn4.5",
|
|
131
142
|
"claude-haiku-4-5-20251001": "Hk4.5",
|
|
143
|
+
"claude-haiku-4-5": "Hk4.5",
|
|
132
144
|
"claude-3-5-sonnet-20241022": "Sn3.5",
|
|
133
145
|
"claude-3-5-haiku-20241022": "Hk3.5",
|
|
134
146
|
"claude-3-opus-20240229": "Op3",
|
|
@@ -142,6 +154,8 @@ MODEL_SHORT_NAMES: Dict[str, str] = {
|
|
|
142
154
|
"gpt-5.4": "G5.4",
|
|
143
155
|
"gpt-5.6-sol": "G5.6Sol",
|
|
144
156
|
"gpt-5.6-terra": "G5.6Ter",
|
|
157
|
+
"gpt-4o-mini": "G4oMn",
|
|
158
|
+
"gpt-4o": "G4o",
|
|
145
159
|
|
|
146
160
|
# xAI Grok (#469).
|
|
147
161
|
"grok-4.6": "Gk4.6",
|
|
@@ -194,6 +208,8 @@ _FAMILY_PREFIX_TAGS = [
|
|
|
194
208
|
]
|
|
195
209
|
|
|
196
210
|
_DATE_SUFFIX_RE = re.compile(r"-20\d{6}$")
|
|
211
|
+
# Trailing capacity-variant suffix, e.g. the "[1m]" in "claude-opus-5[1m]".
|
|
212
|
+
_CAPACITY_SUFFIX_RE = re.compile(r"\[[^\]]*\]$")
|
|
197
213
|
|
|
198
214
|
|
|
199
215
|
def _heuristic_short_name(bare: str) -> str:
|
|
@@ -243,7 +259,10 @@ def _heuristic_short_name(bare: str) -> str:
|
|
|
243
259
|
|
|
244
260
|
|
|
245
261
|
def _bare_model_id(model: str) -> str:
|
|
246
|
-
"""
|
|
262
|
+
"""Normalise a model id to the bare form this module's tables key on.
|
|
263
|
+
|
|
264
|
+
Strips an opencode-style ``provider/model`` qualifier and a trailing
|
|
265
|
+
capacity-variant suffix in brackets, if present.
|
|
247
266
|
|
|
248
267
|
opencode's stats reader stores the qualified id it launched with (e.g.
|
|
249
268
|
``"openai/gpt-5.6-sol"``, ``"anthropic/claude-opus-4-6"`` — see
|
|
@@ -255,8 +274,16 @@ def _bare_model_id(model: str) -> str:
|
|
|
255
274
|
"unrecognized." This was very likely the underlying mechanism behind
|
|
256
275
|
the #469 bug report, independent of which models the table happens to
|
|
257
276
|
cover at any given time.
|
|
277
|
+
|
|
278
|
+
The bracket suffix is a capacity variant, not a distinct model: Claude
|
|
279
|
+
Code names its 1M-context Opus 5 ``claude-opus-5[1m]``. Left in place it
|
|
280
|
+
breaks *both* lookups — an exact-match table miss (dash instead of a
|
|
281
|
+
window) and, worse, a wrong short name, since the rule-based shortener
|
|
282
|
+
joins every digit it finds and renders "Op5.1", which reads as a
|
|
283
|
+
different model rather than as an unrecognized one.
|
|
258
284
|
"""
|
|
259
|
-
|
|
285
|
+
bare = model.rsplit("/", 1)[-1] if "/" in model else model
|
|
286
|
+
return _CAPACITY_SUFFIX_RE.sub("", bare)
|
|
260
287
|
|
|
261
288
|
|
|
262
289
|
def model_short_name(model: Optional[str]) -> str:
|
|
@@ -336,7 +363,9 @@ class AgentSessionStats:
|
|
|
336
363
|
cache_creation_tokens: int
|
|
337
364
|
cache_read_tokens: int
|
|
338
365
|
work_times: List[float] # seconds per work cycle (prompt to next prompt)
|
|
339
|
-
|
|
366
|
+
# Size of the most recent turn's prompt: uncached input + cache reads +
|
|
367
|
+
# cache writes (all three are processed prompt — see read_session_file_stats).
|
|
368
|
+
current_context_tokens: int = 0
|
|
340
369
|
subagent_count: int = 0 # Number of subagent files (#176)
|
|
341
370
|
live_subagent_count: int = 0 # Subagents with recently-modified files (#256)
|
|
342
371
|
background_task_count: int = 0 # Number of background/farm tasks (#177)
|
|
@@ -817,7 +846,16 @@ def _parse_session_lines(
|
|
|
817
846
|
totals["output_tokens"] += output_tokens
|
|
818
847
|
totals["cache_creation_tokens"] += cache_creation
|
|
819
848
|
totals["cache_read_tokens"] += cache_read
|
|
820
|
-
|
|
849
|
+
# Every one of these three is prompt the model actually
|
|
850
|
+
# processed this turn, so all three count toward context
|
|
851
|
+
# occupancy. Omitting cache_creation under-reports badly
|
|
852
|
+
# rather than slightly: on a turn that *writes* the cache
|
|
853
|
+
# (the first turn after /clear, or after a cache expiry)
|
|
854
|
+
# the whole prompt lands in cache_creation and
|
|
855
|
+
# input_tokens is a literal handful — a 48K-token context
|
|
856
|
+
# reported as 2 tokens, rendering "0%" in a column whose
|
|
857
|
+
# entire job is to warn before the window fills.
|
|
858
|
+
context_size = input_tokens + cache_read + cache_creation
|
|
821
859
|
if context_size > 0:
|
|
822
860
|
totals["current_context_tokens"] = context_size
|
|
823
861
|
# Only track model/provider from messages with actual API
|
|
@@ -974,7 +1012,7 @@ def read_token_usage_from_session_file(
|
|
|
974
1012
|
|
|
975
1013
|
Returns:
|
|
976
1014
|
Dict with input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
|
|
977
|
-
and current_context_tokens (most recent
|
|
1015
|
+
and current_context_tokens (most recent turn's full prompt size)
|
|
978
1016
|
"""
|
|
979
1017
|
totals, _ = read_session_file_stats(session_file, since)
|
|
980
1018
|
return totals
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|