okstra 0.163.1 → 0.163.2
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
package/runtime/BUILD.json
CHANGED
|
@@ -14,7 +14,7 @@ import socket
|
|
|
14
14
|
import subprocess
|
|
15
15
|
from dataclasses import dataclass
|
|
16
16
|
from pathlib import Path
|
|
17
|
-
from typing import Any, Sequence
|
|
17
|
+
from typing import Any, Collection, Sequence
|
|
18
18
|
|
|
19
19
|
PING_OK = "PONG"
|
|
20
20
|
|
|
@@ -165,15 +165,31 @@ class Placement:
|
|
|
165
165
|
|
|
166
166
|
|
|
167
167
|
def plan_worker_placement(
|
|
168
|
-
panes: Sequence[PaneGeometry],
|
|
168
|
+
panes: Sequence[PaneGeometry],
|
|
169
|
+
*,
|
|
170
|
+
lead_pane_id: str,
|
|
171
|
+
owned_surface_ids: Collection[str],
|
|
172
|
+
min_columns: int,
|
|
169
173
|
) -> Placement:
|
|
170
174
|
"""Pick the next worker slot from the workspace's current geometry.
|
|
171
175
|
|
|
172
176
|
Stateless by design: okstra records surface UUIDs, never a layout, so a
|
|
173
177
|
resumed or crashed run cannot carry a layout model that no longer matches
|
|
174
178
|
the screen. Every dispatch re-reads the panes and derives the next slot.
|
|
179
|
+
|
|
180
|
+
Those same UUIDs say which panes okstra may place into. A workspace also
|
|
181
|
+
holds panes okstra never opened — another agent session, a shell the user
|
|
182
|
+
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.
|
|
175
186
|
"""
|
|
176
|
-
workers = [
|
|
187
|
+
workers = [
|
|
188
|
+
pane
|
|
189
|
+
for pane in panes
|
|
190
|
+
if pane.pane_id != lead_pane_id
|
|
191
|
+
and _holds_an_okstra_surface(pane, owned_surface_ids)
|
|
192
|
+
]
|
|
177
193
|
if not workers:
|
|
178
194
|
return Placement(pane_id=lead_pane_id, direction="right", stack_as_tab=False)
|
|
179
195
|
|
|
@@ -196,6 +212,17 @@ def lead_shrink_points(lead: PaneGeometry, *, target_columns: int) -> int:
|
|
|
196
212
|
return surplus * lead.cell_width_points
|
|
197
213
|
|
|
198
214
|
|
|
215
|
+
def _holds_an_okstra_surface(
|
|
216
|
+
pane: PaneGeometry, owned_surface_ids: Collection[str]
|
|
217
|
+
) -> bool:
|
|
218
|
+
"""Whether okstra opened anything in this pane.
|
|
219
|
+
|
|
220
|
+
Any one recorded surface is enough: workers that stacked as tabs share a
|
|
221
|
+
pane, and only the tab that opened it carries the UUID okstra split on.
|
|
222
|
+
"""
|
|
223
|
+
return any(surface_id in owned_surface_ids for surface_id in pane.surface_ids)
|
|
224
|
+
|
|
225
|
+
|
|
199
226
|
def _widen_the_grid(
|
|
200
227
|
workers: Sequence[PaneGeometry], *, min_columns: int
|
|
201
228
|
) -> Placement:
|
|
@@ -270,18 +297,30 @@ def list_panes(workspace: str) -> list[PaneGeometry]:
|
|
|
270
297
|
|
|
271
298
|
|
|
272
299
|
def spawn_worker_surface(
|
|
273
|
-
*,
|
|
300
|
+
*,
|
|
301
|
+
workspace: str,
|
|
302
|
+
cwd: Path,
|
|
303
|
+
command: Sequence[str],
|
|
304
|
+
title: str,
|
|
305
|
+
owned_surface_ids: Collection[str],
|
|
274
306
|
) -> str:
|
|
275
307
|
"""Start one worker beside the lead and return its surface UUID.
|
|
276
308
|
|
|
277
309
|
The UUID is what okstra records and later closes by. Positional refs cannot
|
|
278
310
|
serve that purpose: cmux renumbers them as surfaces open and close, so a
|
|
279
311
|
close by ref can land on a pane okstra never created.
|
|
312
|
+
|
|
313
|
+
`owned_surface_ids` are the UUIDs earlier dispatches returned. They are what
|
|
314
|
+
keeps this placement inside okstra's own panes; the workspace belongs to the
|
|
315
|
+
user and may hold anything.
|
|
280
316
|
"""
|
|
281
317
|
panes = list_panes(workspace)
|
|
282
318
|
lead = _lead_pane(panes)
|
|
283
319
|
placement = plan_worker_placement(
|
|
284
|
-
panes,
|
|
320
|
+
panes,
|
|
321
|
+
lead_pane_id=lead.pane_id,
|
|
322
|
+
owned_surface_ids=owned_surface_ids,
|
|
323
|
+
min_columns=WORKER_MIN_COLUMNS,
|
|
285
324
|
)
|
|
286
325
|
target = _pane_by_id(panes, placement.pane_id)
|
|
287
326
|
surface_uuid = _open_worker_surface(workspace, placement, target)
|
|
@@ -452,6 +452,7 @@ def _start_cmux_or_degrade(plan: DispatchPlan, job: WorkerJob) -> WorkerHandle:
|
|
|
452
452
|
cwd=plan.project_root,
|
|
453
453
|
command=job.command,
|
|
454
454
|
title=f"{job.worker_id}-worker",
|
|
455
|
+
owned_surface_ids=_opened_cmux_surfaces(plan.team_state_path),
|
|
455
456
|
)
|
|
456
457
|
except (RuntimeError, OSError, subprocess.SubprocessError):
|
|
457
458
|
return _run_cli_wrapper(plan, job, BACKEND_CMUX_PANE)
|
|
@@ -460,6 +461,28 @@ def _start_cmux_or_degrade(plan: DispatchPlan, job: WorkerJob) -> WorkerHandle:
|
|
|
460
461
|
)
|
|
461
462
|
|
|
462
463
|
|
|
464
|
+
def _opened_cmux_surfaces(team_state_path: Path) -> tuple[str, ...]:
|
|
465
|
+
"""The surfaces this run has already opened in the user's cmux workspace.
|
|
466
|
+
|
|
467
|
+
Placement needs them to tell okstra's own panes from the rest of the
|
|
468
|
+
workspace, which belongs to the user and may hold another agent session or
|
|
469
|
+
a shell okstra must not split or stack into. A degraded dispatch opened no
|
|
470
|
+
surface and records an empty id, which would name no pane at all.
|
|
471
|
+
"""
|
|
472
|
+
payload = _load_json_object(team_state_path, "team-state")
|
|
473
|
+
dispatches = payload.get("workerDispatches")
|
|
474
|
+
if not isinstance(dispatches, list):
|
|
475
|
+
return ()
|
|
476
|
+
opened = []
|
|
477
|
+
for record in dispatches:
|
|
478
|
+
if not isinstance(record, dict):
|
|
479
|
+
continue
|
|
480
|
+
surface_id = str(record.get("paneId", ""))
|
|
481
|
+
if surface_id:
|
|
482
|
+
opened.append(surface_id)
|
|
483
|
+
return tuple(opened)
|
|
484
|
+
|
|
485
|
+
|
|
463
486
|
def _start_tmux_or_degrade(plan: DispatchPlan, job: WorkerJob) -> WorkerHandle:
|
|
464
487
|
lead_pane = tmux.resolve_caller_pane()
|
|
465
488
|
if not lead_pane:
|