okstra 0.170.3 → 0.171.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.170.3",
3
+ "version": "0.171.0",
4
4
  "description": "Host-aware multi-provider cross-verification orchestrator runtime and agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.170.3",
3
- "builtAt": "2026-08-14T18:24:32.087Z",
2
+ "package": "0.171.0",
3
+ "builtAt": "2026-08-15T08:20:00.683Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -30,17 +30,26 @@ LOGIN_SHELL_TIMEOUT_SECONDS = 15
30
30
  # re-enters cmux's own agent wrapper instead of the real CLI.
31
31
  SHIM_DIR_MARKER = "cmux-cli-shims"
32
32
 
33
- # Workers fill two columns and then grow downward. A third column would cost
34
- # width, and width is the dimension a horizontal split cannot give back.
35
- GRID_COLUMNS = 2
36
-
37
- # Measured against a Claude Code worker: 67 columns renders losslessly, 33 drops
38
- # content off the right edge. Below this floor okstra stacks a tab instead of
39
- # splitting, because an unreadable pane costs the lead its view of that worker.
40
- WORKER_MIN_COLUMNS = 60
41
-
42
- # What the lead keeps for itself once workers arrive.
43
- LEAD_TARGET_COLUMNS = 80
33
+ # The workspace is split in fifths: two for the lead, three for the workers.
34
+ # Every worker lands in that one column and the column divides downward, so all
35
+ # workers hold the same width and only one border — the lead's — is ever
36
+ # computed. Splitting sideways instead would make each worker's width a
37
+ # function of how many rounds preceded it.
38
+ LEAD_SHARE_WITH_WORKERS = 2 / 5
39
+
40
+ # Three fifths is what a worker needs to be worth watching: measured against a
41
+ # Claude Code worker, 67 columns renders losslessly and 33 drops content off the
42
+ # right edge, and three fifths clears 67 on any window wide enough to hold two
43
+ # panes at all.
44
+ #
45
+ # With no workers on screen the lead has nothing to share with, so it takes the
46
+ # whole workspace back rather than sitting at its working width.
47
+ LEAD_SHARE_ALONE = 1.0
48
+
49
+ # A floor for the dimension the worker column actually divides. A pane
50
+ # this short still shows a command and its first lines of output; below it the
51
+ # pane stops being a window onto the worker and the next one stacks as a tab.
52
+ WORKER_MIN_ROWS = 20
44
53
 
45
54
  # Sidebar entries are keyed by source so tools do not overwrite each other's.
46
55
  SIDEBAR_SOURCE = "okstra"
@@ -151,6 +160,7 @@ class PaneGeometry:
151
160
  x: int
152
161
  y: int
153
162
  cell_width_points: int
163
+ width_points: float = 0.0
154
164
  ref: str = ""
155
165
  selected_surface_id: str = ""
156
166
 
@@ -169,10 +179,16 @@ def plan_worker_placement(
169
179
  *,
170
180
  lead_pane_id: str,
171
181
  owned_surface_ids: Collection[str],
172
- min_columns: int,
182
+ min_rows: int,
173
183
  ) -> Placement:
174
184
  """Pick the next worker slot from the workspace's current geometry.
175
185
 
186
+ One column for every worker: the first split takes it off the lead, and each
187
+ one after that divides the column downward. Width is therefore decided once,
188
+ by where the lead's border sits, and every worker inherits it — nothing here
189
+ computes a width, and no worker's width depends on how many rounds ran
190
+ before it.
191
+
176
192
  Stateless by design: okstra records surface UUIDs, never a layout, so a
177
193
  resumed or crashed run cannot carry a layout model that no longer matches
178
194
  the screen. Every dispatch re-reads the panes and derives the next slot.
@@ -180,9 +196,9 @@ def plan_worker_placement(
180
196
  Those same UUIDs say which panes okstra may place into. A workspace also
181
197
  holds panes okstra never opened — another agent session, a shell the user
182
198
  keeps around — and "not the lead" does not make a pane a worker slot. Taken
183
- as one, a stranger's pane is split or, when it is too narrow to split,
184
- stacked into: the workers land as background tabs in someone else's window,
185
- so nothing appears on screen and that window grows tabs it did not ask for.
199
+ as one, a stranger's pane is split or stacked into: the workers land as
200
+ background tabs in someone else's window, so nothing appears on screen and
201
+ that window grows tabs it did not ask for.
186
202
  """
187
203
  workers = [
188
204
  pane
@@ -192,14 +208,48 @@ def plan_worker_placement(
192
208
  ]
193
209
  if not workers:
194
210
  return Placement(pane_id=lead_pane_id, direction="right", stack_as_tab=False)
211
+ return _extend_the_worker_column(workers, min_rows=min_rows)
195
212
 
196
- columns = _panes_by_column(workers)
197
- if len(columns) < GRID_COLUMNS:
198
- return _widen_the_grid(workers, min_columns=min_columns)
199
- return _extend_the_shortest_column(columns)
213
+
214
+ def lead_target_width(
215
+ panes: Sequence[PaneGeometry],
216
+ *,
217
+ lead_pane_id: str,
218
+ owned_surface_ids: Collection[str],
219
+ container_width_points: float,
220
+ ) -> float:
221
+ """How wide the lead should be, in the points `pixel_frame` reports.
222
+
223
+ Two fifths while workers are on screen, all of it when they are gone. The
224
+ share is taken of what okstra may actually place into, not of the window:
225
+ a workspace can hold panes okstra never opened, and handing the lead the
226
+ whole container would shove those off their own width. Their width is
227
+ subtracted first and the share applies to the remainder.
228
+
229
+ Deriving the target from the container rather than from a fixed column count
230
+ is what keeps the split honest at any window size — a hardcoded 80 columns
231
+ is two fifths of one particular display and an arbitrary slice of every
232
+ other.
233
+ """
234
+ strangers = sum(
235
+ pane.width_points
236
+ for pane in panes
237
+ if pane.pane_id != lead_pane_id
238
+ and not _holds_an_okstra_surface(pane, owned_surface_ids)
239
+ )
240
+ usable = container_width_points - strangers
241
+ if usable <= 0:
242
+ return 0.0
243
+ workers_on_screen = any(
244
+ pane.pane_id != lead_pane_id
245
+ and _holds_an_okstra_surface(pane, owned_surface_ids)
246
+ for pane in panes
247
+ )
248
+ share = LEAD_SHARE_WITH_WORKERS if workers_on_screen else LEAD_SHARE_ALONE
249
+ return usable * share
200
250
 
201
251
 
202
- def lead_resize_points(lead: PaneGeometry, *, target_columns: int) -> int:
252
+ def lead_resize_points(lead: PaneGeometry, *, target_width_points: float) -> int:
203
253
  """How far to move the lead's right border, in the points `pane.resize` takes.
204
254
 
205
255
  Signed: positive when the lead is too wide and the border comes in, negative
@@ -208,14 +258,14 @@ def lead_resize_points(lead: PaneGeometry, *, target_columns: int) -> int:
208
258
  Both directions are needed. A split halves whatever pane it lands on, and
209
259
  the first worker of every round lands on the lead — so a rule that only ever
210
260
  shrinks leaves that half permanent, and the round after it takes half of
211
- what is left. Measured on this display: 215 columns becomes 80, then 40,
212
- then 20, until neither the lead nor its workers can be read.
261
+ what is left. Measured on this display: 215 columns becomes 107, then 53,
262
+ then 26, until neither the lead nor its workers can be read.
213
263
 
214
- The API's `amount` is points, not cells measured at this pane's own
215
- `cell_width_points`, so passing a column count moves the border by an eighth
216
- of the intent on a typical display.
264
+ Measured in the same points `pixel_frame` reports, so no cell-size
265
+ conversion happens here at all. A column count would need one, and would
266
+ round the target to a whole cell before the border ever moved.
217
267
  """
218
- return (lead.columns - target_columns) * lead.cell_width_points
268
+ return round(lead.width_points - target_width_points)
219
269
 
220
270
 
221
271
  def _holds_an_okstra_surface(
@@ -229,31 +279,22 @@ def _holds_an_okstra_surface(
229
279
  return any(surface_id in owned_surface_ids for surface_id in pane.surface_ids)
230
280
 
231
281
 
232
- def _widen_the_grid(
233
- workers: Sequence[PaneGeometry], *, min_columns: int
234
- ) -> Placement:
235
- rightmost = max(workers, key=lambda pane: pane.x)
236
- if rightmost.columns // GRID_COLUMNS >= min_columns:
237
- return Placement(pane_id=rightmost.pane_id, direction="right", stack_as_tab=False)
238
- roomiest = min(workers, key=lambda pane: (len(pane.surface_ids), pane.x))
239
- return Placement(pane_id=roomiest.pane_id, direction="", stack_as_tab=True)
240
-
241
-
242
- def _extend_the_shortest_column(
243
- columns: dict[int, list[PaneGeometry]]
282
+ def _extend_the_worker_column(
283
+ workers: Sequence[PaneGeometry], *, min_rows: int
244
284
  ) -> Placement:
245
- shortest = min(columns.values(), key=lambda group: (len(group), group[0].x))
246
- bottom = max(shortest, key=lambda pane: pane.y)
247
- return Placement(pane_id=bottom.pane_id, direction="down", stack_as_tab=False)
285
+ """Divide the bottom worker, or stack when the halves would be unreadable.
248
286
 
249
-
250
- def _panes_by_column(
251
- workers: Sequence[PaneGeometry],
252
- ) -> dict[int, list[PaneGeometry]]:
253
- columns: dict[int, list[PaneGeometry]] = {}
254
- for pane in workers:
255
- columns.setdefault(pane.x, []).append(pane)
256
- return columns
287
+ A split halves the pane it lands on, so the bottom pane's own height decides
288
+ whether the column can take another worker at all. Once it cannot, further
289
+ workers stack as tabs into the pane holding the fewest — a background tab is
290
+ worse than a visible pane, but better than two panes too short to show a
291
+ command and its first lines of output.
292
+ """
293
+ bottom = max(workers, key=lambda pane: pane.y)
294
+ if bottom.rows // 2 >= min_rows:
295
+ return Placement(pane_id=bottom.pane_id, direction="down", stack_as_tab=False)
296
+ roomiest = min(workers, key=lambda pane: (len(pane.surface_ids), pane.y))
297
+ return Placement(pane_id=roomiest.pane_id, direction="", stack_as_tab=True)
257
298
 
258
299
 
259
300
  def shim_free_login_path() -> str:
@@ -298,8 +339,7 @@ def worker_command_line(
298
339
 
299
340
 
300
341
  def list_panes(workspace: str) -> list[PaneGeometry]:
301
- payload = rpc("pane.list", {"workspace_id": workspace})
302
- return [_pane_geometry(entry) for entry in payload.get("panes", [])]
342
+ return _panes_from(_pane_list_payload(workspace))
303
343
 
304
344
 
305
345
  def spawn_worker_surface(
@@ -326,13 +366,13 @@ def spawn_worker_surface(
326
366
  panes,
327
367
  lead_pane_id=lead.pane_id,
328
368
  owned_surface_ids=owned_surface_ids,
329
- min_columns=WORKER_MIN_COLUMNS,
369
+ min_rows=WORKER_MIN_ROWS,
330
370
  )
331
371
  target = _pane_by_id(panes, placement.pane_id)
332
372
  surface_uuid = _open_worker_surface(workspace, placement, target)
333
373
  run_cmux(["rename-tab", "--surface", surface_uuid, "--title", title])
334
374
  _exec_worker(surface_uuid, cwd=cwd, command=command)
335
- _size_lead_pane(workspace)
375
+ _size_lead_pane(workspace, (*owned_surface_ids, surface_uuid))
336
376
  return surface_uuid
337
377
 
338
378
 
@@ -358,11 +398,17 @@ def restore_lead_width() -> None:
358
398
  holds a run manifest rather than the workspace UUID that `pane.resize`
359
399
  needs. An unresolvable workspace means cmux is gone or was never there, and
360
400
  there is no pane left to size.
401
+
402
+ Owning nothing is the point of the empty ledger. Teardown has just closed
403
+ every surface it opened, so no pane on screen is okstra's but the lead's —
404
+ which is what makes the lead's share the whole workspace. A pane teardown
405
+ failed to close counts as a stranger's and keeps its width, which is the
406
+ safe way to be wrong here.
361
407
  """
362
408
  workspace = resolve_lead_workspace()
363
409
  if not workspace:
364
410
  return
365
- _size_lead_pane(workspace)
411
+ _size_lead_pane(workspace, ())
366
412
 
367
413
 
368
414
  def capture_surface(surface_uuid: str, *, last_lines: int = 200) -> str:
@@ -557,8 +603,12 @@ def _exec_worker(surface_uuid: str, *, cwd: Path, command: Sequence[str]) -> Non
557
603
  raise RuntimeError(started.stderr.strip() or "cmux could not start the worker")
558
604
 
559
605
 
560
- def _size_lead_pane(workspace: str) -> None:
561
- """Put the lead back on its target width, leaving the rest to the workers.
606
+ def _size_lead_pane(workspace: str, owned_surface_ids: Collection[str]) -> None:
607
+ """Move the lead's border to its share of the workspace.
608
+
609
+ This is the only border okstra places. Workers divide their column downward
610
+ and therefore all inherit whatever is left of it, so placing this one border
611
+ sizes every pane on screen.
562
612
 
563
613
  Which pane carries the request follows from what `pane.resize` does: it
564
614
  moves the named pane's own border in the direction given. The lead can push
@@ -568,11 +618,26 @@ def _size_lead_pane(workspace: str) -> None:
568
618
  and widening is the lead's.
569
619
 
570
620
  Run after every worker opens rather than once per round: the split that just
571
- happened is what knocked the lead off its width, and no other event does.
621
+ happened is what knocked the lead off its share, and no other event does.
572
622
  """
573
- panes = list_panes(workspace)
623
+ payload = _pane_list_payload(workspace)
624
+ container_width = _container_width_points(payload)
625
+ if container_width <= 0:
626
+ # Without the frame the shares are taken of, there is no target to move
627
+ # toward — and a guessed one would move the border to a wrong place
628
+ # rather than leave it where the user last saw it.
629
+ return
630
+ panes = _panes_from(payload)
574
631
  lead = _lead_pane(panes)
575
- offset = lead_resize_points(lead, target_columns=LEAD_TARGET_COLUMNS)
632
+ offset = lead_resize_points(
633
+ lead,
634
+ target_width_points=lead_target_width(
635
+ panes,
636
+ lead_pane_id=lead.pane_id,
637
+ owned_surface_ids=owned_surface_ids,
638
+ container_width_points=container_width,
639
+ ),
640
+ )
576
641
  if offset == 0:
577
642
  return
578
643
  neighbours = [pane for pane in panes if pane.x > lead.x]
@@ -609,6 +674,25 @@ def _pane_by_id(panes: Sequence[PaneGeometry], pane_id: str) -> PaneGeometry:
609
674
  raise RuntimeError(f"cmux pane {pane_id} disappeared while placing a worker")
610
675
 
611
676
 
677
+ def _pane_list_payload(workspace: str) -> dict[str, Any]:
678
+ return rpc("pane.list", {"workspace_id": workspace})
679
+
680
+
681
+ def _panes_from(payload: dict[str, Any]) -> list[PaneGeometry]:
682
+ return [_pane_geometry(entry) for entry in payload.get("panes", [])]
683
+
684
+
685
+ def _container_width_points(payload: dict[str, Any]) -> float:
686
+ """The workspace's own width, which every share here is taken of.
687
+
688
+ Reported once per `pane.list` reply rather than per pane, because it is the
689
+ frame the panes are laid out inside — summing the panes would instead give
690
+ whatever they currently happen to occupy.
691
+ """
692
+ frame = payload.get("container_frame") or {}
693
+ return float(frame.get("width") or 0)
694
+
695
+
612
696
  def _pane_geometry(entry: dict[str, Any]) -> PaneGeometry:
613
697
  frame = entry.get("pixel_frame") or {}
614
698
  return PaneGeometry(
@@ -619,6 +703,7 @@ def _pane_geometry(entry: dict[str, Any]) -> PaneGeometry:
619
703
  x=int(frame.get("x", 0)),
620
704
  y=int(frame.get("y", 0)),
621
705
  cell_width_points=int(entry.get("cell_width_points", 0)),
706
+ width_points=float(frame.get("width") or 0),
622
707
  ref=str(entry.get("ref", "")),
623
708
  selected_surface_id=str(entry.get("selected_surface_id", "")),
624
709
  )