overcode 0.5.1__py3-none-any.whl → 0.5.2__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.
overcode/cli/split.py CHANGED
@@ -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: enter copy mode directly on the linked session.
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'"copy-mode -t {linked_session} -u"',
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. For SSH proxies: forward mouse events
381
- # to the remote tmux through the pane.
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: enter copy mode + scroll up directly.
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'"copy-mode -t {linked_session} -e ; '
396
- f'send-keys -t {linked_session} -X -N 3 scroll-up"',
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 `pane_in_mode` of the linked
409
- # session: in copy mode → scroll-down 3 lines (matches wheel-up);
410
- # out of copy mode (already at the bottom) → NPage, a plain
411
- # keystroke that's harmlessly delivered to the agent.
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 \'#{{pane_in_mode}}\''
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 -t {linked_session} NPage\'"',
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
- """Strip an opencode-style ``provider/model`` qualifier, if present.
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
- return model.rsplit("/", 1)[-1] if "/" in model else model
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
- current_context_tokens: int = 0 # Most recent input_tokens (current context size)
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
- context_size = input_tokens + cache_read
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 input_tokens value)
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: overcode
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: A supervisor for managing multiple Claude Code instances in tmux
5
5
  Author: Mike Bond
6
6
  Project-URL: Homepage, https://github.com/mkb23/overcode
@@ -13,7 +13,7 @@ overcode/doctor.py,sha256=wG5NJ6O6NAV7pHLjhLRPkWAE5lPAk_P3ioW5bqTpxfw,19033
13
13
  overcode/duration.py,sha256=4tqBTKjKON7uYHgXWndBF_-w4fvEwvZtEruLt6s_DPo,772
14
14
  overcode/exceptions.py,sha256=gkTDorSFMbzuyaynWec7rrOVtAwhaz3_5PDm_YveyW8,5189
15
15
  overcode/follow_mode.py,sha256=mbTj-qHZu2ycvlz5nJSZrio3XDrqkGzQq0q07yTiOVA,11639
16
- overcode/history_reader.py,sha256=cuFDp5D6PVOB4038mnZVoPsxsLhHqEIRHmwfwuya2mI,49450
16
+ overcode/history_reader.py,sha256=2J81pMqeIMCg8tUQV26NlNGdkcOEgGQ0DGHSMu8s43k,51565
17
17
  overcode/hook_handler.py,sha256=WdHhA7NuPR0lnhsc_NNT6jqkhI0y_neSHEcQoAPOUtE,32237
18
18
  overcode/hook_status_detector.py,sha256=ud_vwtHOViPthq6fUKHnlriVTgmq9XKnNUFU3Lcj2lA,34292
19
19
  overcode/implementations.py,sha256=wlsuwy5Heb26qN33nJvKDLASDg7DzkvSMuMo54MmvSs,14189
@@ -93,7 +93,7 @@ overcode/cli/parallelism.py,sha256=Kb79kCOO8u2olyQ4qKSuid-VRaNkd9w5NMheCqFsuDM,4
93
93
  overcode/cli/perms.py,sha256=G9_J5VyWvIg7nH61FjaoDHgRtyjQzxByE_3ZGOHEE5c,3618
94
94
  overcode/cli/sister.py,sha256=5iHuYPQDOjQQ4VQ81fXU1LTkPBy60GPdPQFScBdT44M,7963
95
95
  overcode/cli/skills.py,sha256=N8k-BtXY2voy7-5RMtLuSmm_nV-vO9PRrWcLtm7_jhU,4519
96
- overcode/cli/split.py,sha256=_ARG6k7UWHShVdddUBI1alM4xo7qI0VvH22Axhq2Hic,40602
96
+ overcode/cli/split.py,sha256=xBQ04iiviIN3a6gdpH0hz8jAdSIWddimt0sNeOH2cKQ,43443
97
97
  overcode/cli/tags.py,sha256=FnbRgaOETmrDFpgdVajhYlifM-fOSIxvt3GMHPtz_F4,3495
98
98
  overcode/cli/wrappers.py,sha256=DnwIzSVbs3e5OW6nlY3GDT6P0MBjnW928q7yLGjvHsA,3802
99
99
  overcode/opencode_plugin/overcode-telemetry.js,sha256=pXk4it6wenO74aUjYIKphbgSTvfV2H2ycssRYBSU_-Q,18593
@@ -132,9 +132,9 @@ overcode/tui_widgets/tui_log_panel.py,sha256=sf4F0YFiLzDYvoNccHFXqN7X6rWuLOMO_vw
132
132
  overcode/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
133
  overcode/web/templates/analytics.html,sha256=9vtjCFQkLSvGiZpJM4TOV9Xv_TCwIWiyM5IagYZMUmM,41050
134
134
  overcode/web/templates/dashboard.html,sha256=RSTDdm5bQpqLwyn1PLhkk0v1EcPSigi-Cml7H7OQAcE,17409
135
- overcode-0.5.1.dist-info/licenses/LICENSE,sha256=6C9I9dhq8QTa4P6LckWXugYJ3xm2ufZKrLymqOqRZFs,1066
136
- overcode-0.5.1.dist-info/METADATA,sha256=et8knHBzZ_Kl_xOmgrSzKlIqXNSpUkwG5PENEFpOEvM,8720
137
- overcode-0.5.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
138
- overcode-0.5.1.dist-info/entry_points.txt,sha256=cMXnVDRHx7_a9kpEMFCtRmFnW9NsgoiASo2u-U9p8og,87
139
- overcode-0.5.1.dist-info/top_level.txt,sha256=5cfXcNbSNvigE7coZgIRPCl_NDWHu_5UIBmWi9Xiupo,9
140
- overcode-0.5.1.dist-info/RECORD,,
135
+ overcode-0.5.2.dist-info/licenses/LICENSE,sha256=6C9I9dhq8QTa4P6LckWXugYJ3xm2ufZKrLymqOqRZFs,1066
136
+ overcode-0.5.2.dist-info/METADATA,sha256=or3wpPhY1ICYWvEZfwhw9_VK55ixF396A_PavevO0HA,8720
137
+ overcode-0.5.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
138
+ overcode-0.5.2.dist-info/entry_points.txt,sha256=cMXnVDRHx7_a9kpEMFCtRmFnW9NsgoiASo2u-U9p8og,87
139
+ overcode-0.5.2.dist-info/top_level.txt,sha256=5cfXcNbSNvigE7coZgIRPCl_NDWHu_5UIBmWi9Xiupo,9
140
+ overcode-0.5.2.dist-info/RECORD,,