@team-agent/installer 0.1.9 → 0.1.10
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/README.md +6 -1
- package/README.zh.md +4 -1
- package/package.json +1 -1
- package/skills/team-agent/SKILL.md +8 -2
- package/src/team_agent/runtime.py +149 -72
package/README.md
CHANGED
|
@@ -128,7 +128,12 @@ Lead: [proposes a team — refactor architect (Claude), code mover (Codex),
|
|
|
128
128
|
You: Go.
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
That's it.
|
|
131
|
+
That's it. With the default display, teammates appear in separate Ghostty
|
|
132
|
+
windows. If `display_backend: ghostty_workspace` is set, teammates appear in one
|
|
133
|
+
Ghostty window with tmux tabs/windows, up to three side-by-side panes per tab
|
|
134
|
+
(`4` workers => `3 + 1`, `8` workers => `3 + 3 + 2`). The lead reports
|
|
135
|
+
progress, raises decisions when needed, and shuts everything down when you say
|
|
136
|
+
so.
|
|
132
137
|
|
|
133
138
|
### Stop / resume
|
|
134
139
|
|
package/README.zh.md
CHANGED
|
@@ -124,7 +124,10 @@ Lead: [提议一个 team:重构架构师 (Claude)、代码搬运工 (Codex)、
|
|
|
124
124
|
你: 开始。
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
完事。默认显示模式下,队员会分别出现在独立 Ghostty 窗口。设置
|
|
128
|
+
`display_backend: ghostty_workspace` 时,队员会出现在同一个 Ghostty 窗口里,
|
|
129
|
+
用 tmux tab/window 分页,每个 tab 最多三列 pane:4 个队员是 `3 + 1`,8 个队员是
|
|
130
|
+
`3 + 3 + 2`。lead 推进工作、需要决策时停下来问你,你说"关掉"就关掉。
|
|
128
131
|
|
|
129
132
|
### 关闭 / 恢复
|
|
130
133
|
|
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@ cat > .team/current/TEAM.md <<'EOF'
|
|
|
27
27
|
name: demo-team
|
|
28
28
|
objective: One worker handles bounded tasks and reports through Team Agent MCP.
|
|
29
29
|
dangerous_auto_approve: false
|
|
30
|
-
display_backend:
|
|
30
|
+
display_backend: ghostty_workspace
|
|
31
31
|
fast: false
|
|
32
32
|
provider_models:
|
|
33
33
|
codex: gpt-5.5
|
|
@@ -59,7 +59,13 @@ team-agent quick-start .team/current
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
YAML lists must be block style. Use `tools:\n - fs_read`; do not use `tools: [fs_read, mcp_team]`.
|
|
62
|
-
|
|
62
|
+
|
|
63
|
+
Display choices:
|
|
64
|
+
- `ghostty_workspace`: one Ghostty window. Workers are shown in tmux tabs/windows, up to 3 side-by-side panes per tab. Four workers become `3 + 1`; eight become `3 + 3 + 2`.
|
|
65
|
+
- `ghostty_window`: one Ghostty window per worker.
|
|
66
|
+
- `none`: headless/CI.
|
|
67
|
+
|
|
68
|
+
Omitting `display_backend` defaults to `ghostty_window`.
|
|
63
69
|
|
|
64
70
|
## Provider Prep
|
|
65
71
|
|
|
@@ -55,6 +55,7 @@ TMUX_PANE_FORMAT = (
|
|
|
55
55
|
)
|
|
56
56
|
HEALTH_STATUSES = {"RUNNING", "IDLE", "AWAITING_APPROVAL", "BLOCKED", "ERROR", "DONE"}
|
|
57
57
|
GHOSTTY_DISPLAY_BACKENDS = {"ghostty", "ghostty_window", "ghostty_workspace"}
|
|
58
|
+
GHOSTTY_WORKSPACE_PANES_PER_WINDOW = 3
|
|
58
59
|
PEEK_MAX_LINES = 80
|
|
59
60
|
PEEK_SEARCH_SCAN_LINES = 300
|
|
60
61
|
PEEK_MAX_MATCHES = 5
|
|
@@ -227,6 +228,22 @@ def _spec_team_dir(spec_path: Path, workspace: Path) -> Path:
|
|
|
227
228
|
return workspace.resolve() / ".team" / "current"
|
|
228
229
|
|
|
229
230
|
|
|
231
|
+
def _is_team_doc_dir(team_dir: Path) -> bool:
|
|
232
|
+
return (team_dir / "TEAM.md").exists() and (team_dir / "agents").is_dir()
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _compile_team_dir_spec(team_dir: Path, workspace: Path) -> dict[str, Any]:
|
|
236
|
+
from team_agent.compiler import compile_team
|
|
237
|
+
|
|
238
|
+
spec_path = team_dir / "team.spec.yaml"
|
|
239
|
+
compiled = compile_team(team_dir, spec_path)
|
|
240
|
+
if compiled["spec"].get("context", {}).get("state_file") == "team_state.md":
|
|
241
|
+
state_file = str(team_dir.relative_to(workspace) / "team_state.md") if team_dir.is_relative_to(workspace) else "team_state.md"
|
|
242
|
+
compiled["spec"]["context"]["state_file"] = state_file
|
|
243
|
+
spec_path.write_text(dumps(compiled["spec"]), encoding="utf-8")
|
|
244
|
+
return compiled
|
|
245
|
+
|
|
246
|
+
|
|
230
247
|
def _attach_team_profile_dirs(spec: dict[str, Any], spec_path: Path, workspace: Path | None = None, team_dir: Path | None = None) -> None:
|
|
231
248
|
workspace = workspace.resolve() if workspace else workspace_from_spec(spec, spec_path)
|
|
232
249
|
team_dir = team_dir.resolve() if team_dir else _spec_team_dir(spec_path, workspace)
|
|
@@ -1766,8 +1783,12 @@ def shutdown(workspace: Path, keep_logs: bool = True) -> dict[str, Any]:
|
|
|
1766
1783
|
closed_displays.add(agent_id)
|
|
1767
1784
|
proc = run_cmd(["tmux", "kill-session", "-t", session_name], timeout=10)
|
|
1768
1785
|
if proc.returncode != 0:
|
|
1769
|
-
|
|
1770
|
-
|
|
1786
|
+
if "can't find session" in proc.stderr:
|
|
1787
|
+
event_log.write("shutdown.idempotent", session=session_name, reason="session disappeared before kill")
|
|
1788
|
+
else:
|
|
1789
|
+
raise RuntimeError(f"tmux kill-session failed: {proc.stderr.strip()}")
|
|
1790
|
+
else:
|
|
1791
|
+
event_log.write("shutdown.kill_session", session=session_name, keep_logs=keep_logs, captured=captured)
|
|
1771
1792
|
else:
|
|
1772
1793
|
event_log.write("shutdown.idempotent", session=session_name, reason="session missing")
|
|
1773
1794
|
_close_ghostty_workspace(state, event_log)
|
|
@@ -1801,10 +1822,16 @@ def shutdown(workspace: Path, keep_logs: bool = True) -> dict[str, Any]:
|
|
|
1801
1822
|
def restart(workspace: Path, allow_fresh: bool = False, team: str | None = None) -> dict[str, Any]:
|
|
1802
1823
|
state = _select_restart_state(workspace, team)
|
|
1803
1824
|
spec_path = Path(state.get("spec_path", workspace / "team.spec.yaml"))
|
|
1804
|
-
if not spec_path.exists():
|
|
1805
|
-
raise RuntimeError(f"missing spec for restart: {spec_path}")
|
|
1806
|
-
spec = load_spec(spec_path)
|
|
1807
1825
|
team_dir = Path(str(state.get("team_dir"))) if state.get("team_dir") else _spec_team_dir(spec_path, workspace)
|
|
1826
|
+
if _is_team_doc_dir(team_dir):
|
|
1827
|
+
compiled = _compile_team_dir_spec(team_dir, workspace)
|
|
1828
|
+
spec = compiled["spec"]
|
|
1829
|
+
spec_path = team_dir / "team.spec.yaml"
|
|
1830
|
+
state["spec_path"] = str(spec_path)
|
|
1831
|
+
else:
|
|
1832
|
+
if not spec_path.exists():
|
|
1833
|
+
raise RuntimeError(f"missing spec for restart: {spec_path}")
|
|
1834
|
+
spec = load_spec(spec_path)
|
|
1808
1835
|
_attach_team_profile_dirs(spec, spec_path, workspace, team_dir)
|
|
1809
1836
|
ensure_workspace_dirs(workspace)
|
|
1810
1837
|
event_log = EventLog(workspace)
|
|
@@ -1819,6 +1846,9 @@ def restart(workspace: Path, allow_fresh: bool = False, team: str | None = None)
|
|
|
1819
1846
|
raise RuntimeError(_tmux_session_conflict_error(session_name))
|
|
1820
1847
|
runtime_cfg = _effective_runtime_config(spec.get("runtime", {}))
|
|
1821
1848
|
display_backend = spec.get("runtime", {}).get("display_backend", state.get("display_backend", "none"))
|
|
1849
|
+
_close_ghostty_workspace(state, event_log)
|
|
1850
|
+
for agent_id, agent_state in state.get("agents", {}).items():
|
|
1851
|
+
_close_ghostty_display(agent_id, agent_state, event_log)
|
|
1822
1852
|
state["display_backend"] = display_backend
|
|
1823
1853
|
restart_agents = [
|
|
1824
1854
|
agent
|
|
@@ -3126,18 +3156,12 @@ def quick_start(
|
|
|
3126
3156
|
fresh: bool = False,
|
|
3127
3157
|
team_id: str | None = None,
|
|
3128
3158
|
) -> dict[str, Any]:
|
|
3129
|
-
from team_agent.compiler import compile_team
|
|
3130
|
-
|
|
3131
3159
|
team_dir = _prepare_quick_start_team(agents_dir.resolve(), Path.cwd().resolve(), name, team_id=team_id)
|
|
3132
3160
|
workspace = team_workspace(team_dir)
|
|
3133
3161
|
ensure_workspace_dirs(workspace)
|
|
3134
3162
|
_ensure_profiles_for_roles(team_dir)
|
|
3163
|
+
compiled = _compile_team_dir_spec(team_dir, workspace)
|
|
3135
3164
|
spec_path = team_dir / "team.spec.yaml"
|
|
3136
|
-
compiled = compile_team(team_dir, spec_path)
|
|
3137
|
-
if compiled["spec"].get("context", {}).get("state_file") == "team_state.md":
|
|
3138
|
-
state_file = str(team_dir.relative_to(workspace) / "team_state.md") if team_dir.is_relative_to(workspace) else "team_state.md"
|
|
3139
|
-
compiled["spec"]["context"]["state_file"] = state_file
|
|
3140
|
-
spec_path.write_text(dumps(compiled["spec"]), encoding="utf-8")
|
|
3141
3165
|
existing = _quick_start_existing_context(workspace, compiled["spec"]["runtime"]["session_name"])
|
|
3142
3166
|
if existing and not fresh:
|
|
3143
3167
|
return {
|
|
@@ -4715,6 +4739,7 @@ def _open_ghostty_workspace(
|
|
|
4715
4739
|
"linked_session": linked_session,
|
|
4716
4740
|
"aggregator_session": aggregator_session,
|
|
4717
4741
|
"display_session": aggregator_session,
|
|
4742
|
+
"workspace_window": pane.get("window_name"),
|
|
4718
4743
|
"pane_id": pane.get("pane_id"),
|
|
4719
4744
|
"launch_args": launch_args,
|
|
4720
4745
|
"pid": pids[0] if pids else None,
|
|
@@ -4790,6 +4815,10 @@ def _ghostty_workspace_aggregator_name(session_name: str) -> str:
|
|
|
4790
4815
|
return f"{safe_session}__display__workspace__{digest}"
|
|
4791
4816
|
|
|
4792
4817
|
|
|
4818
|
+
def _ghostty_workspace_window_name(index: int) -> str:
|
|
4819
|
+
return "overview" if index == 0 else f"overview-{index + 1}"
|
|
4820
|
+
|
|
4821
|
+
|
|
4793
4822
|
def _ghostty_workspace_pane_command(linked_session: str) -> str:
|
|
4794
4823
|
return f"TMUX= tmux attach-session -t {shlex.quote(linked_session)}"
|
|
4795
4824
|
|
|
@@ -4833,75 +4862,121 @@ def _prepare_ghostty_workspace_aggregator(
|
|
|
4833
4862
|
aggregator_session: str,
|
|
4834
4863
|
linked_jobs: list[tuple[str, dict[str, Any], str]],
|
|
4835
4864
|
) -> dict[str, Any]:
|
|
4836
|
-
window_name = "overview"
|
|
4837
4865
|
if _tmux_session_exists(aggregator_session):
|
|
4838
4866
|
proc = run_cmd(["tmux", "kill-session", "-t", aggregator_session], timeout=10)
|
|
4839
4867
|
if proc.returncode != 0:
|
|
4840
4868
|
return {"ok": False, "reason": "display_session_cleanup_failed", "error": proc.stderr.strip()}
|
|
4841
4869
|
|
|
4842
|
-
|
|
4843
|
-
first_agent_id, first_agent, first_linked_session = linked_jobs[0]
|
|
4844
|
-
proc = run_cmd(
|
|
4845
|
-
[
|
|
4846
|
-
"tmux",
|
|
4847
|
-
"new-session",
|
|
4848
|
-
"-d",
|
|
4849
|
-
"-P",
|
|
4850
|
-
"-F",
|
|
4851
|
-
"#{pane_id}",
|
|
4852
|
-
"-s",
|
|
4853
|
-
aggregator_session,
|
|
4854
|
-
"-n",
|
|
4855
|
-
window_name,
|
|
4856
|
-
_ghostty_workspace_pane_command(first_linked_session),
|
|
4857
|
-
],
|
|
4858
|
-
timeout=10,
|
|
4859
|
-
)
|
|
4860
|
-
if proc.returncode != 0:
|
|
4861
|
-
return {"ok": False, "reason": "display_session_create_failed", "error": proc.stderr.strip()}
|
|
4862
|
-
first_pane_id = _tmux_stdout_last_line(proc.stdout) or f"{aggregator_session}:{window_name}.0"
|
|
4863
|
-
first_title = _ghostty_workspace_pane_title(first_agent)
|
|
4864
|
-
title_result = _set_ghostty_workspace_pane_title(first_pane_id, first_title)
|
|
4865
|
-
if not title_result["ok"]:
|
|
4870
|
+
def fail(reason: str, proc: Any | None = None, target: str | None = None) -> dict[str, Any]:
|
|
4866
4871
|
run_cmd(["tmux", "kill-session", "-t", aggregator_session], timeout=10)
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
return {"ok": False, "reason": "display_session_remain_on_exit_failed", "error": proc.stderr.strip()}
|
|
4872
|
+
result = {"ok": False, "reason": reason}
|
|
4873
|
+
if proc is not None:
|
|
4874
|
+
result["error"] = proc.stderr.strip()
|
|
4875
|
+
if target:
|
|
4876
|
+
result["target"] = target
|
|
4877
|
+
return result
|
|
4874
4878
|
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4879
|
+
panes: list[dict[str, Any]] = []
|
|
4880
|
+
for window_index, start in enumerate(range(0, len(linked_jobs), GHOSTTY_WORKSPACE_PANES_PER_WINDOW)):
|
|
4881
|
+
window_name = _ghostty_workspace_window_name(window_index)
|
|
4882
|
+
window_jobs = linked_jobs[start : start + GHOSTTY_WORKSPACE_PANES_PER_WINDOW]
|
|
4883
|
+
first_agent_id, first_agent, first_linked_session = window_jobs[0]
|
|
4884
|
+
if window_index == 0:
|
|
4885
|
+
proc = run_cmd(
|
|
4886
|
+
[
|
|
4887
|
+
"tmux",
|
|
4888
|
+
"new-session",
|
|
4889
|
+
"-d",
|
|
4890
|
+
"-P",
|
|
4891
|
+
"-F",
|
|
4892
|
+
"#{pane_id}",
|
|
4893
|
+
"-s",
|
|
4894
|
+
aggregator_session,
|
|
4895
|
+
"-n",
|
|
4896
|
+
window_name,
|
|
4897
|
+
_ghostty_workspace_pane_command(first_linked_session),
|
|
4898
|
+
],
|
|
4899
|
+
timeout=10,
|
|
4900
|
+
)
|
|
4901
|
+
if proc.returncode != 0:
|
|
4902
|
+
return {"ok": False, "reason": "display_session_create_failed", "error": proc.stderr.strip()}
|
|
4903
|
+
else:
|
|
4904
|
+
proc = run_cmd(
|
|
4905
|
+
[
|
|
4906
|
+
"tmux",
|
|
4907
|
+
"new-window",
|
|
4908
|
+
"-t",
|
|
4909
|
+
aggregator_session,
|
|
4910
|
+
"-n",
|
|
4911
|
+
window_name,
|
|
4912
|
+
"-P",
|
|
4913
|
+
"-F",
|
|
4914
|
+
"#{pane_id}",
|
|
4915
|
+
_ghostty_workspace_pane_command(first_linked_session),
|
|
4916
|
+
],
|
|
4917
|
+
timeout=10,
|
|
4918
|
+
)
|
|
4919
|
+
if proc.returncode != 0:
|
|
4920
|
+
return fail("display_session_window_create_failed", proc, first_linked_session)
|
|
4921
|
+
first_pane_id = _tmux_stdout_last_line(proc.stdout) or f"{aggregator_session}:{window_name}.0"
|
|
4922
|
+
first_title = _ghostty_workspace_pane_title(first_agent)
|
|
4923
|
+
title_result = _set_ghostty_workspace_pane_title(first_pane_id, first_title)
|
|
4924
|
+
if not title_result["ok"]:
|
|
4925
|
+
return fail(title_result["reason"], target=first_pane_id)
|
|
4926
|
+
panes.append(
|
|
4927
|
+
{
|
|
4928
|
+
"agent_id": first_agent_id,
|
|
4929
|
+
"pane_id": first_pane_id,
|
|
4930
|
+
"title": first_title,
|
|
4931
|
+
"linked_session": first_linked_session,
|
|
4932
|
+
"window_name": window_name,
|
|
4933
|
+
}
|
|
4889
4934
|
)
|
|
4935
|
+
|
|
4936
|
+
proc = run_cmd(["tmux", "set-window-option", "-t", f"{aggregator_session}:{window_name}", "remain-on-exit", "on"], timeout=10)
|
|
4890
4937
|
if proc.returncode != 0:
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4938
|
+
return fail("display_session_remain_on_exit_failed", proc)
|
|
4939
|
+
|
|
4940
|
+
for index, (agent_id, agent, linked_session) in enumerate(window_jobs[1:], start=1):
|
|
4941
|
+
proc = run_cmd(
|
|
4942
|
+
[
|
|
4943
|
+
"tmux",
|
|
4944
|
+
"split-window",
|
|
4945
|
+
"-t",
|
|
4946
|
+
f"{aggregator_session}:{window_name}",
|
|
4947
|
+
"-h",
|
|
4948
|
+
"-P",
|
|
4949
|
+
"-F",
|
|
4950
|
+
"#{pane_id}",
|
|
4951
|
+
_ghostty_workspace_pane_command(linked_session),
|
|
4952
|
+
],
|
|
4953
|
+
timeout=10,
|
|
4954
|
+
)
|
|
4955
|
+
if proc.returncode != 0:
|
|
4956
|
+
return fail("display_session_split_failed", proc, linked_session)
|
|
4957
|
+
pane_id = _tmux_stdout_last_line(proc.stdout) or f"{aggregator_session}:{window_name}.{index}"
|
|
4958
|
+
title = _ghostty_workspace_pane_title(agent)
|
|
4959
|
+
title_result = _set_ghostty_workspace_pane_title(pane_id, title)
|
|
4960
|
+
if not title_result["ok"]:
|
|
4961
|
+
return fail(title_result["reason"], target=pane_id)
|
|
4962
|
+
panes.append(
|
|
4963
|
+
{
|
|
4964
|
+
"agent_id": agent_id,
|
|
4965
|
+
"pane_id": pane_id,
|
|
4966
|
+
"title": title,
|
|
4967
|
+
"linked_session": linked_session,
|
|
4968
|
+
"window_name": window_name,
|
|
4969
|
+
}
|
|
4970
|
+
)
|
|
4971
|
+
|
|
4972
|
+
proc = run_cmd(["tmux", "select-layout", "-t", f"{aggregator_session}:{window_name}", "even-horizontal"], timeout=10)
|
|
4973
|
+
if proc.returncode != 0:
|
|
4974
|
+
return fail("display_session_layout_failed", proc)
|
|
4900
4975
|
|
|
4901
|
-
proc = run_cmd(["tmux", "
|
|
4976
|
+
proc = run_cmd(["tmux", "set-option", "-t", aggregator_session, "mouse", "on"], timeout=10)
|
|
4902
4977
|
if proc.returncode != 0:
|
|
4903
|
-
|
|
4904
|
-
|
|
4978
|
+
return fail("display_session_mouse_failed", proc)
|
|
4979
|
+
run_cmd(["tmux", "select-window", "-t", f"{aggregator_session}:{_ghostty_workspace_window_name(0)}"], timeout=10)
|
|
4905
4980
|
return {"ok": True, "aggregator_session": aggregator_session, "panes": panes}
|
|
4906
4981
|
|
|
4907
4982
|
|
|
@@ -4963,6 +5038,7 @@ def _open_ghostty_workspace_agent_display(
|
|
|
4963
5038
|
pane_title = _ghostty_workspace_pane_title(agent)
|
|
4964
5039
|
command = _ghostty_workspace_pane_command(linked_session)
|
|
4965
5040
|
pane_id = str(previous_display.get("pane_id") or "")
|
|
5041
|
+
workspace_window = str(previous_display.get("workspace_window") or _ghostty_workspace_window_name(0))
|
|
4966
5042
|
refreshed = False
|
|
4967
5043
|
if pane_id:
|
|
4968
5044
|
proc = run_cmd(["tmux", "respawn-pane", "-k", "-t", pane_id, command], timeout=10)
|
|
@@ -4973,7 +5049,7 @@ def _open_ghostty_workspace_agent_display(
|
|
|
4973
5049
|
"tmux",
|
|
4974
5050
|
"split-window",
|
|
4975
5051
|
"-t",
|
|
4976
|
-
f"{aggregator_session}:
|
|
5052
|
+
f"{aggregator_session}:{workspace_window}",
|
|
4977
5053
|
"-h",
|
|
4978
5054
|
"-P",
|
|
4979
5055
|
"-F",
|
|
@@ -5002,7 +5078,7 @@ def _open_ghostty_workspace_agent_display(
|
|
|
5002
5078
|
reason=title_result["reason"],
|
|
5003
5079
|
note=title_result.get("error") or "pane refresh requires full team restart",
|
|
5004
5080
|
)
|
|
5005
|
-
run_cmd(["tmux", "select-layout", "-t", f"{aggregator_session}:
|
|
5081
|
+
run_cmd(["tmux", "select-layout", "-t", f"{aggregator_session}:{workspace_window}", "even-horizontal"], timeout=10)
|
|
5006
5082
|
title = str(previous_display.get("title") or f"team-agent:{session_name}:workspace")
|
|
5007
5083
|
pids = [int(pid) for pid in previous_display.get("pids", []) if str(pid).isdigit()]
|
|
5008
5084
|
display = {
|
|
@@ -5014,6 +5090,7 @@ def _open_ghostty_workspace_agent_display(
|
|
|
5014
5090
|
"linked_session": linked_session,
|
|
5015
5091
|
"aggregator_session": aggregator_session,
|
|
5016
5092
|
"display_session": aggregator_session,
|
|
5093
|
+
"workspace_window": workspace_window,
|
|
5017
5094
|
"pane_id": pane_id,
|
|
5018
5095
|
"pid": pids[0] if pids else None,
|
|
5019
5096
|
"pids": pids,
|