@team-agent/installer 0.2.6 → 0.2.7
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
|
@@ -10,8 +10,8 @@ is sourced from the caller-supplied positive facts only:
|
|
|
10
10
|
|
|
11
11
|
Reverse enumeration of panes / windows / clients is forbidden. Heuristic
|
|
12
12
|
ranking ("active pane", "current client", "first leader-shaped pane") is
|
|
13
|
-
forbidden. ``$TMUX_PANE`` missing
|
|
14
|
-
|
|
13
|
+
forbidden. ``$TMUX_PANE`` missing → refuse and emit ``owner.bind_refused``.
|
|
14
|
+
The pane's current command is diagnostic metadata only. Successful binds emit
|
|
15
15
|
``owner.bound_from_caller_pane`` and force-write every owner identity
|
|
16
16
|
field; old fields are not merged or migrated.
|
|
17
17
|
"""
|
|
@@ -37,11 +37,9 @@ def run_cmd(args: list[str], timeout: int = 5) -> subprocess.CompletedProcess[st
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
LEADER_HOST_COMMANDS = frozenset({"claude", "claude.exe", "codex"})
|
|
41
|
-
|
|
42
40
|
_HINT_RUN_FROM_LEADER_PANE = (
|
|
43
41
|
"run team-agent from inside your leader pane "
|
|
44
|
-
"(the tmux pane
|
|
42
|
+
"(the tmux pane you want to own this team)."
|
|
45
43
|
)
|
|
46
44
|
|
|
47
45
|
|
|
@@ -71,7 +69,7 @@ def bind_owner_from_caller_pane(
|
|
|
71
69
|
|
|
72
70
|
{
|
|
73
71
|
"ok": False,
|
|
74
|
-
"reason": "caller_pane_missing"
|
|
72
|
+
"reason": "caller_pane_missing",
|
|
75
73
|
"caller_pane_id": ..., "caller_current_command": ...,
|
|
76
74
|
"hint": ...,
|
|
77
75
|
}
|
|
@@ -110,26 +108,6 @@ def bind_owner_from_caller_pane(
|
|
|
110
108
|
caller_command = ""
|
|
111
109
|
else:
|
|
112
110
|
caller_command = (getattr(proc, "stdout", "") or "").strip()
|
|
113
|
-
if caller_command not in LEADER_HOST_COMMANDS:
|
|
114
|
-
hint = (
|
|
115
|
-
f"run team-agent from inside your leader pane "
|
|
116
|
-
f"(this pane is running {caller_command or '<unknown>'})."
|
|
117
|
-
)
|
|
118
|
-
event_log.write(
|
|
119
|
-
"owner.bind_refused",
|
|
120
|
-
reason="caller_not_leader_shaped",
|
|
121
|
-
caller_pane_id=caller_pane,
|
|
122
|
-
caller_current_command=caller_command,
|
|
123
|
-
team_id=team_id,
|
|
124
|
-
hint=hint,
|
|
125
|
-
)
|
|
126
|
-
return {
|
|
127
|
-
"ok": False,
|
|
128
|
-
"reason": "caller_not_leader_shaped",
|
|
129
|
-
"caller_pane_id": caller_pane,
|
|
130
|
-
"caller_current_command": caller_command,
|
|
131
|
-
"hint": hint,
|
|
132
|
-
}
|
|
133
111
|
machine_fingerprint = os.environ.get("TEAM_AGENT_MACHINE_FINGERPRINT") or ""
|
|
134
112
|
os_user = os.environ.get("USER") or os.environ.get("USERNAME") or ""
|
|
135
113
|
provider = (
|
|
@@ -367,6 +367,18 @@ def restart(workspace: Path, allow_fresh: bool = False, team: str | None = None)
|
|
|
367
367
|
from team_agent.leader import autobind_leader_receiver_from_env
|
|
368
368
|
leader_provider = str(spec.get("leader", {}).get("provider") or "codex")
|
|
369
369
|
rebound_receiver = autobind_leader_receiver_from_env(workspace, leader_provider, source="restart")
|
|
370
|
+
if rebound_receiver is None and state.get("leader_receiver"):
|
|
371
|
+
stale = state.pop("leader_receiver", None)
|
|
372
|
+
event_log.write(
|
|
373
|
+
"leader_receiver.rebind_required",
|
|
374
|
+
reason="restart_autobind_unresolved",
|
|
375
|
+
old_pane_id=(stale or {}).get("pane_id") if isinstance(stale, dict) else None,
|
|
376
|
+
old_session_name=(stale or {}).get("session_name") if isinstance(stale, dict) else None,
|
|
377
|
+
source="restart",
|
|
378
|
+
)
|
|
379
|
+
save_runtime_state(workspace, state)
|
|
380
|
+
save_team_runtime_snapshot(workspace, state)
|
|
381
|
+
write_team_state(workspace, spec, state)
|
|
370
382
|
rebuild_restart_display_after_rebind(display_backend, workspace, session_name, spec, event_log, restarted, receiver=rebound_receiver)
|
|
371
383
|
coordinator = start_coordinator(workspace)
|
|
372
384
|
event_log.write("restart.complete", session=session_name, agents=restarted, coordinator=coordinator)
|
package/src/team_agent/state.py
CHANGED
|
@@ -4,6 +4,7 @@ import hashlib
|
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
6
|
import copy
|
|
7
|
+
import subprocess
|
|
7
8
|
import uuid
|
|
8
9
|
from datetime import datetime, timezone
|
|
9
10
|
from pathlib import Path
|
|
@@ -313,7 +314,7 @@ def _caller_identity_from_env(state: dict[str, Any] | None = None, team_id: str
|
|
|
313
314
|
team_id or os.environ.get("TEAM_AGENT_TEAM_ID") or team_state_key(state),
|
|
314
315
|
)
|
|
315
316
|
return {
|
|
316
|
-
"pane_id": os.environ.get("TEAM_AGENT_LEADER_PANE_ID") or "",
|
|
317
|
+
"pane_id": os.environ.get("TEAM_AGENT_LEADER_PANE_ID") or os.environ.get("TMUX_PANE") or "",
|
|
317
318
|
"provider": os.environ.get("TEAM_AGENT_LEADER_PROVIDER") or "",
|
|
318
319
|
"machine_fingerprint": machine_fingerprint,
|
|
319
320
|
"leader_session_uuid": leader_uuid,
|
|
@@ -321,6 +322,36 @@ def _caller_identity_from_env(state: dict[str, Any] | None = None, team_id: str
|
|
|
321
322
|
}
|
|
322
323
|
|
|
323
324
|
|
|
325
|
+
_TMUX_PANE_LIVE = "live"
|
|
326
|
+
_TMUX_PANE_DEAD = "dead"
|
|
327
|
+
_TMUX_PANE_UNKNOWN = "unknown"
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _tmux_pane_liveness(pane_id: str) -> str:
|
|
331
|
+
if not pane_id:
|
|
332
|
+
return _TMUX_PANE_UNKNOWN
|
|
333
|
+
try:
|
|
334
|
+
from team_agent.runtime import run_cmd
|
|
335
|
+
proc = run_cmd(["tmux", "display-message", "-p", "-t", pane_id, "#{pane_id}"], timeout=3)
|
|
336
|
+
except Exception:
|
|
337
|
+
try:
|
|
338
|
+
proc = subprocess.run(
|
|
339
|
+
["tmux", "display-message", "-p", "-t", pane_id, "#{pane_id}"],
|
|
340
|
+
text=True,
|
|
341
|
+
capture_output=True,
|
|
342
|
+
timeout=3,
|
|
343
|
+
check=False,
|
|
344
|
+
)
|
|
345
|
+
except Exception:
|
|
346
|
+
return _TMUX_PANE_UNKNOWN
|
|
347
|
+
if proc.returncode == 0:
|
|
348
|
+
return _TMUX_PANE_LIVE
|
|
349
|
+
stderr = str(getattr(proc, "stderr", "") or "").lower()
|
|
350
|
+
if "can't find pane" in stderr or "can't find window" in stderr or "can't find session" in stderr:
|
|
351
|
+
return _TMUX_PANE_DEAD
|
|
352
|
+
return _TMUX_PANE_UNKNOWN
|
|
353
|
+
|
|
354
|
+
|
|
324
355
|
def check_team_owner(state: dict[str, Any]) -> dict[str, Any] | None:
|
|
325
356
|
owner = state.get("team_owner") or {}
|
|
326
357
|
if not owner:
|
|
@@ -331,6 +362,15 @@ def check_team_owner(state: dict[str, Any]) -> dict[str, Any] | None:
|
|
|
331
362
|
caller_uuid = caller["leader_session_uuid"]
|
|
332
363
|
owner_pane = str(owner.get("pane_id") or "")
|
|
333
364
|
caller_pane = caller.get("pane_id") or ""
|
|
365
|
+
if caller_pane and caller_pane == owner_pane:
|
|
366
|
+
return None
|
|
367
|
+
if (
|
|
368
|
+
caller_pane
|
|
369
|
+
and not os.environ.get("TEAM_AGENT_ID")
|
|
370
|
+
and owner_pane
|
|
371
|
+
and _tmux_pane_liveness(owner_pane) != _TMUX_PANE_LIVE
|
|
372
|
+
):
|
|
373
|
+
return None
|
|
334
374
|
if caller_uuid == owner_uuid and (not caller_pane or caller_pane == owner_pane):
|
|
335
375
|
return None
|
|
336
376
|
same_uuid = caller_uuid == owner_uuid
|