@team-agent/installer 0.2.11 → 0.3.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/Cargo.lock +744 -0
- package/Cargo.toml +34 -0
- package/crates/team-agent/Cargo.toml +33 -0
- package/crates/team-agent/src/cli/adapters.rs +1343 -0
- package/crates/team-agent/src/cli/diagnose.rs +554 -0
- package/crates/team-agent/src/cli/emit.rs +1077 -0
- package/crates/team-agent/src/cli/helpers.rs +88 -0
- package/crates/team-agent/src/cli/leader.rs +216 -0
- package/crates/team-agent/src/cli/mod.rs +1141 -0
- package/crates/team-agent/src/cli/profile.rs +306 -0
- package/crates/team-agent/src/cli/send.rs +215 -0
- package/crates/team-agent/src/cli/status.rs +179 -0
- package/crates/team-agent/src/cli/status_port.rs +502 -0
- package/crates/team-agent/src/cli/tests/base.rs +616 -0
- package/crates/team-agent/src/cli/tests/compile.rs +96 -0
- package/crates/team-agent/src/cli/tests/divergence.rs +509 -0
- package/crates/team-agent/src/cli/tests/lane_c.rs +333 -0
- package/crates/team-agent/src/cli/tests/leader_watch.rs +395 -0
- package/crates/team-agent/src/cli/tests/main_preserved.rs +675 -0
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +390 -0
- package/crates/team-agent/src/cli/tests/mod.rs +97 -0
- package/crates/team-agent/src/cli/tests/peer_allow.rs +137 -0
- package/crates/team-agent/src/cli/tests/repair_state_byte_lock.rs +302 -0
- package/crates/team-agent/src/cli/tests/run_delegation.rs +305 -0
- package/crates/team-agent/src/cli/tests/status_send.rs +385 -0
- package/crates/team-agent/src/cli/tests/verb_profile.rs +182 -0
- package/crates/team-agent/src/cli/tests/verb_settle.rs +236 -0
- package/crates/team-agent/src/cli/tests/verb_validate.rs +184 -0
- package/crates/team-agent/src/cli/types.rs +605 -0
- package/crates/team-agent/src/compiler/tests.rs +701 -0
- package/crates/team-agent/src/compiler.rs +489 -0
- package/crates/team-agent/src/coordinator/backoff.rs +153 -0
- package/crates/team-agent/src/coordinator/health.rs +436 -0
- package/crates/team-agent/src/coordinator/mod.rs +80 -0
- package/crates/team-agent/src/coordinator/orphan.rs +179 -0
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +255 -0
- package/crates/team-agent/src/coordinator/tests/basics.rs +262 -0
- package/crates/team-agent/src/coordinator/tests/daemon.rs +323 -0
- package/crates/team-agent/src/coordinator/tests/health_sync.rs +263 -0
- package/crates/team-agent/src/coordinator/tests/main_preserved.rs +136 -0
- package/crates/team-agent/src/coordinator/tests/mod.rs +310 -0
- package/crates/team-agent/src/coordinator/tests/spine.rs +261 -0
- package/crates/team-agent/src/coordinator/tests/takeover.rs +227 -0
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +256 -0
- package/crates/team-agent/src/coordinator/tests/watch.rs +167 -0
- package/crates/team-agent/src/coordinator/tick.rs +2032 -0
- package/crates/team-agent/src/coordinator/types.rs +584 -0
- package/crates/team-agent/src/db/migration.rs +716 -0
- package/crates/team-agent/src/db/mod.rs +23 -0
- package/crates/team-agent/src/db/schema.rs +378 -0
- package/crates/team-agent/src/event_log.rs +375 -0
- package/crates/team-agent/src/fake_worker.rs +253 -0
- package/crates/team-agent/src/leader/helpers.rs +190 -0
- package/crates/team-agent/src/leader/inject.rs +33 -0
- package/crates/team-agent/src/leader/lease.rs +1063 -0
- package/crates/team-agent/src/leader/mod.rs +99 -0
- package/crates/team-agent/src/leader/owner_bind.rs +292 -0
- package/crates/team-agent/src/leader/rediscover/tests.rs +525 -0
- package/crates/team-agent/src/leader/rediscover.rs +1099 -0
- package/crates/team-agent/src/leader/start.rs +273 -0
- package/crates/team-agent/src/leader/takeover.rs +235 -0
- package/crates/team-agent/src/leader/tests/basics.rs +183 -0
- package/crates/team-agent/src/leader/tests/byte_findings.rs +234 -0
- package/crates/team-agent/src/leader/tests/identity.rs +206 -0
- package/crates/team-agent/src/leader/tests/idle.rs +271 -0
- package/crates/team-agent/src/leader/tests/lease_api.rs +225 -0
- package/crates/team-agent/src/leader/tests/lease_claim.rs +253 -0
- package/crates/team-agent/src/leader/tests/mod.rs +125 -0
- package/crates/team-agent/src/leader/tests/rediscover.rs +351 -0
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +204 -0
- package/crates/team-agent/src/leader/types.rs +487 -0
- package/crates/team-agent/src/lib.rs +85 -0
- package/crates/team-agent/src/lifecycle/display.rs +228 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +112 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +227 -0
- package/crates/team-agent/src/lifecycle/launch.rs +1833 -0
- package/crates/team-agent/src/lifecycle/mod.rs +62 -0
- package/crates/team-agent/src/lifecycle/restart/agent.rs +533 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +517 -0
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +41 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +268 -0
- package/crates/team-agent/src/lifecycle/restart/remove.rs +780 -0
- package/crates/team-agent/src/lifecycle/restart/selection.rs +208 -0
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +242 -0
- package/crates/team-agent/src/lifecycle/restart.rs +76 -0
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +455 -0
- package/crates/team-agent/src/lifecycle/tests/core.rs +989 -0
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +583 -0
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +933 -0
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +265 -0
- package/crates/team-agent/src/lifecycle/tests.rs +27 -0
- package/crates/team-agent/src/lifecycle/types.rs +685 -0
- package/crates/team-agent/src/main.rs +41 -0
- package/crates/team-agent/src/mcp_server/helpers.rs +228 -0
- package/crates/team-agent/src/mcp_server/mod.rs +183 -0
- package/crates/team-agent/src/mcp_server/normalize.rs +312 -0
- package/crates/team-agent/src/mcp_server/tests/golden.rs +283 -0
- package/crates/team-agent/src/mcp_server/tests/normalize.rs +244 -0
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +189 -0
- package/crates/team-agent/src/mcp_server/tests/send.rs +222 -0
- package/crates/team-agent/src/mcp_server/tests/tools.rs +158 -0
- package/crates/team-agent/src/mcp_server/tests/wire.rs +159 -0
- package/crates/team-agent/src/mcp_server/tests.rs +38 -0
- package/crates/team-agent/src/mcp_server/tools.rs +603 -0
- package/crates/team-agent/src/mcp_server/types.rs +421 -0
- package/crates/team-agent/src/mcp_server/wire.rs +388 -0
- package/crates/team-agent/src/message_store.rs +767 -0
- package/crates/team-agent/src/messaging/activity.rs +433 -0
- package/crates/team-agent/src/messaging/delivery.rs +542 -0
- package/crates/team-agent/src/messaging/helpers.rs +209 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +340 -0
- package/crates/team-agent/src/messaging/mod.rs +147 -0
- package/crates/team-agent/src/messaging/peers.rs +32 -0
- package/crates/team-agent/src/messaging/results.rs +537 -0
- package/crates/team-agent/src/messaging/scheduler.rs +344 -0
- package/crates/team-agent/src/messaging/selftest.rs +100 -0
- package/crates/team-agent/src/messaging/send.rs +582 -0
- package/crates/team-agent/src/messaging/tests/basic.rs +357 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +122 -0
- package/crates/team-agent/src/messaging/tests/mod.rs +293 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +1422 -0
- package/crates/team-agent/src/messaging/tests/spine.rs +437 -0
- package/crates/team-agent/src/messaging/trust.rs +192 -0
- package/crates/team-agent/src/messaging/types.rs +355 -0
- package/crates/team-agent/src/messaging/watchers.rs +591 -0
- package/crates/team-agent/src/model/enums.rs +311 -0
- package/crates/team-agent/src/model/errors.rs +17 -0
- package/crates/team-agent/src/model/ids.rs +155 -0
- package/crates/team-agent/src/model/mod.rs +22 -0
- package/crates/team-agent/src/model/paths.rs +228 -0
- package/crates/team-agent/src/model/permissions.rs +567 -0
- package/crates/team-agent/src/model/routing.rs +340 -0
- package/crates/team-agent/src/model/spec.rs +680 -0
- package/crates/team-agent/src/model/task_graph.rs +380 -0
- package/crates/team-agent/src/model/testdata/fuzz.golden.yaml +43 -0
- package/crates/team-agent/src/model/testdata/fuzz.yaml +43 -0
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +207 -0
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +206 -0
- package/crates/team-agent/src/model/testdata/team.spec.yaml +206 -0
- package/crates/team-agent/src/model/yaml/tests.rs +288 -0
- package/crates/team-agent/src/model/yaml.rs +800 -0
- package/crates/team-agent/src/packaging/install.rs +305 -0
- package/crates/team-agent/src/packaging/migrate.rs +30 -0
- package/crates/team-agent/src/packaging/mod.rs +82 -0
- package/crates/team-agent/src/packaging/repair.rs +24 -0
- package/crates/team-agent/src/packaging/tests.rs +829 -0
- package/crates/team-agent/src/packaging/types.rs +369 -0
- package/crates/team-agent/src/provider/adapter.rs +801 -0
- package/crates/team-agent/src/provider/approvals/mod.rs +2 -0
- package/crates/team-agent/src/provider/approvals/parsing.rs +452 -0
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +163 -0
- package/crates/team-agent/src/provider/classify.rs +456 -0
- package/crates/team-agent/src/provider/faults.rs +136 -0
- package/crates/team-agent/src/provider/helpers.rs +41 -0
- package/crates/team-agent/src/provider/mod.rs +53 -0
- package/crates/team-agent/src/provider/startup_prompt.rs +423 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +239 -0
- package/crates/team-agent/src/provider/tests/classify.rs +240 -0
- package/crates/team-agent/src/provider/tests/faults.rs +120 -0
- package/crates/team-agent/src/provider/tests/idle.rs +208 -0
- package/crates/team-agent/src/provider/tests/wire.rs +213 -0
- package/crates/team-agent/src/provider/tests.rs +31 -0
- package/crates/team-agent/src/provider/types.rs +424 -0
- package/crates/team-agent/src/state/identity.rs +656 -0
- package/crates/team-agent/src/state/mod.rs +58 -0
- package/crates/team-agent/src/state/owner_gate.rs +423 -0
- package/crates/team-agent/src/state/persist.rs +712 -0
- package/crates/team-agent/src/state/projection.rs +657 -0
- package/crates/team-agent/src/state/selector.rs +105 -0
- package/crates/team-agent/src/state/testdata/state-rich.canonical.json +133 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +586 -0
- package/crates/team-agent/src/tmux_backend.rs +758 -0
- package/crates/team-agent/src/transport/test_support.rs +252 -0
- package/crates/team-agent/src/transport/tests/behavior.rs +327 -0
- package/crates/team-agent/src/transport/tests/mod.rs +199 -0
- package/crates/team-agent/src/transport/tests/wire.rs +527 -0
- package/crates/team-agent/src/transport.rs +774 -0
- package/npm/install.mjs +90 -106
- package/package.json +15 -13
- package/crates/team-agent-core/Cargo.toml +0 -12
- package/crates/team-agent-core/src/lib.rs +0 -332
- package/crates/team-agent-core/src/main.rs +0 -152
- package/pyproject.toml +0 -18
- package/scripts/install.py +0 -88
- package/scripts/run_regression_tests.py +0 -83
- package/src/team_agent/__init__.py +0 -3
- package/src/team_agent/__main__.py +0 -5
- package/src/team_agent/_legacy_pane_discovery.py +0 -186
- package/src/team_agent/abnormal_track.py +0 -253
- package/src/team_agent/approvals/__init__.py +0 -65
- package/src/team_agent/approvals/constants.py +0 -6
- package/src/team_agent/approvals/parsing.py +0 -176
- package/src/team_agent/approvals/runtime_prompts.py +0 -171
- package/src/team_agent/approvals/status.py +0 -176
- package/src/team_agent/cli/__init__.py +0 -137
- package/src/team_agent/cli/commands.py +0 -481
- package/src/team_agent/cli/e2e.py +0 -202
- package/src/team_agent/cli/helpers.py +0 -226
- package/src/team_agent/cli/parser.py +0 -540
- package/src/team_agent/compiler.py +0 -334
- package/src/team_agent/coordinator/__init__.py +0 -53
- package/src/team_agent/coordinator/__main__.py +0 -119
- package/src/team_agent/coordinator/lifecycle.py +0 -411
- package/src/team_agent/coordinator/metadata.py +0 -61
- package/src/team_agent/coordinator/paths.py +0 -17
- package/src/team_agent/diagnose/__init__.py +0 -48
- package/src/team_agent/diagnose/checks.py +0 -101
- package/src/team_agent/diagnose/comms.py +0 -213
- package/src/team_agent/diagnose/health.py +0 -241
- package/src/team_agent/diagnose/orphan_cleanup.py +0 -364
- package/src/team_agent/diagnose/preflight.py +0 -194
- package/src/team_agent/diagnose/quick_start.py +0 -324
- package/src/team_agent/display/__init__.py +0 -92
- package/src/team_agent/display/adaptive.py +0 -511
- package/src/team_agent/display/backend.py +0 -46
- package/src/team_agent/display/close.py +0 -154
- package/src/team_agent/display/ghostty.py +0 -77
- package/src/team_agent/display/rebuild.py +0 -102
- package/src/team_agent/display/tiling.py +0 -156
- package/src/team_agent/display/worker_window.py +0 -114
- package/src/team_agent/display/workspace.py +0 -382
- package/src/team_agent/errors.py +0 -10
- package/src/team_agent/events.py +0 -84
- package/src/team_agent/fake_worker.py +0 -80
- package/src/team_agent/idle_predicate.py +0 -218
- package/src/team_agent/idle_takeover.py +0 -59
- package/src/team_agent/idle_takeover_wiring.py +0 -114
- package/src/team_agent/launch/__init__.py +0 -41
- package/src/team_agent/launch/bootstrap.py +0 -85
- package/src/team_agent/launch/config.py +0 -106
- package/src/team_agent/launch/core.py +0 -301
- package/src/team_agent/launch/requirements.py +0 -57
- package/src/team_agent/leader/__init__.py +0 -926
- package/src/team_agent/leader_binding.py +0 -183
- package/src/team_agent/lifecycle/__init__.py +0 -5
- package/src/team_agent/lifecycle/agents.py +0 -278
- package/src/team_agent/lifecycle/operations.py +0 -411
- package/src/team_agent/lifecycle/paste_buffer_hygiene.py +0 -39
- package/src/team_agent/lifecycle/start.py +0 -363
- package/src/team_agent/mcp_server/__init__.py +0 -42
- package/src/team_agent/mcp_server/__main__.py +0 -7
- package/src/team_agent/mcp_server/contracts.py +0 -148
- package/src/team_agent/mcp_server/normalize.py +0 -257
- package/src/team_agent/mcp_server/server.py +0 -150
- package/src/team_agent/mcp_server/tools.py +0 -352
- package/src/team_agent/message_store/__init__.py +0 -23
- package/src/team_agent/message_store/agent_health.py +0 -113
- package/src/team_agent/message_store/core.py +0 -497
- package/src/team_agent/message_store/leader_notification_log.py +0 -198
- package/src/team_agent/message_store/result_watchers.py +0 -251
- package/src/team_agent/message_store/schema.py +0 -308
- package/src/team_agent/message_store/schema_migration.py +0 -448
- package/src/team_agent/messaging/__init__.py +0 -1
- package/src/team_agent/messaging/activity_detector.py +0 -262
- package/src/team_agent/messaging/delivery.py +0 -504
- package/src/team_agent/messaging/deps.py +0 -247
- package/src/team_agent/messaging/idle_alerts.py +0 -423
- package/src/team_agent/messaging/internal_delivery.py +0 -46
- package/src/team_agent/messaging/leader.py +0 -497
- package/src/team_agent/messaging/leader_api_errors.py +0 -216
- package/src/team_agent/messaging/leader_panes.py +0 -673
- package/src/team_agent/messaging/owner_bypass.py +0 -29
- package/src/team_agent/messaging/result_delivery.py +0 -539
- package/src/team_agent/messaging/results.py +0 -447
- package/src/team_agent/messaging/scheduler.py +0 -450
- package/src/team_agent/messaging/send.py +0 -532
- package/src/team_agent/messaging/session_drift.py +0 -94
- package/src/team_agent/messaging/tmux_io.py +0 -506
- package/src/team_agent/messaging/tmux_prompt.py +0 -338
- package/src/team_agent/messaging/trust_auto_answer.py +0 -52
- package/src/team_agent/orchestrator/__init__.py +0 -376
- package/src/team_agent/orchestrator/plan.py +0 -122
- package/src/team_agent/orchestrator/state.py +0 -128
- package/src/team_agent/paths.py +0 -45
- package/src/team_agent/permissions.py +0 -123
- package/src/team_agent/profiles/__init__.py +0 -82
- package/src/team_agent/profiles/constants.py +0 -19
- package/src/team_agent/profiles/core.py +0 -407
- package/src/team_agent/profiles/helpers.py +0 -69
- package/src/team_agent/profiles/provider_env.py +0 -188
- package/src/team_agent/profiles/smoke.py +0 -201
- package/src/team_agent/provider_cli/__init__.py +0 -43
- package/src/team_agent/provider_cli/adapter.py +0 -172
- package/src/team_agent/provider_cli/base.py +0 -48
- package/src/team_agent/provider_cli/claude.py +0 -503
- package/src/team_agent/provider_cli/codex.py +0 -336
- package/src/team_agent/provider_cli/copilot.py +0 -8
- package/src/team_agent/provider_cli/fake.py +0 -39
- package/src/team_agent/provider_cli/gemini.py +0 -95
- package/src/team_agent/provider_cli/opencode.py +0 -8
- package/src/team_agent/provider_cli/prompt.py +0 -62
- package/src/team_agent/provider_cli/registry.py +0 -18
- package/src/team_agent/provider_cli/unsupported.py +0 -32
- package/src/team_agent/provider_state/README.md +0 -78
- package/src/team_agent/provider_state/__init__.py +0 -91
- package/src/team_agent/provider_state/claude.py +0 -86
- package/src/team_agent/provider_state/codex.py +0 -84
- package/src/team_agent/provider_state/common.py +0 -207
- package/src/team_agent/provider_state/registry.py +0 -118
- package/src/team_agent/providers.py +0 -163
- package/src/team_agent/quality_gates.py +0 -104
- package/src/team_agent/restart/__init__.py +0 -34
- package/src/team_agent/restart/orchestration.py +0 -554
- package/src/team_agent/restart/selection.py +0 -89
- package/src/team_agent/restart/snapshot.py +0 -70
- package/src/team_agent/routing.py +0 -84
- package/src/team_agent/runtime.py +0 -1243
- package/src/team_agent/rust_core.py +0 -327
- package/src/team_agent/sessions/__init__.py +0 -25
- package/src/team_agent/sessions/capture.py +0 -144
- package/src/team_agent/sessions/inventory.py +0 -44
- package/src/team_agent/sessions/resume.py +0 -135
- package/src/team_agent/simple_yaml.py +0 -236
- package/src/team_agent/spec.py +0 -370
- package/src/team_agent/state.py +0 -693
- package/src/team_agent/status/__init__.py +0 -63
- package/src/team_agent/status/approvals.py +0 -52
- package/src/team_agent/status/compact.py +0 -158
- package/src/team_agent/status/constants.py +0 -18
- package/src/team_agent/status/inbox.py +0 -58
- package/src/team_agent/status/peek.py +0 -117
- package/src/team_agent/status/queries.py +0 -199
- package/src/team_agent/task_graph.py +0 -80
- package/src/team_agent/terminal.py +0 -57
- package/src/team_agent/wake.py +0 -58
- package/src/team_agent/watch/__init__.py +0 -145
|
@@ -1,511 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import platform as platform_module
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from team_agent.display.ghostty import ghostty_display_session_name
|
|
9
|
-
from team_agent.display.tiling import (
|
|
10
|
-
display_pane_title,
|
|
11
|
-
prepare_tmux_attached_panes,
|
|
12
|
-
set_tmux_display_pane_title,
|
|
13
|
-
team_scoped_display_window_name,
|
|
14
|
-
)
|
|
15
|
-
from team_agent.display.workspace import (
|
|
16
|
-
kill_ghostty_workspace_linked_sessions,
|
|
17
|
-
)
|
|
18
|
-
from team_agent.events import EventLog
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ADAPTIVE_BLOCK_REASONS = {
|
|
22
|
-
"leader_not_in_tmux",
|
|
23
|
-
"split_failed",
|
|
24
|
-
"window_create_failed",
|
|
25
|
-
"worker_session_missing",
|
|
26
|
-
"not_implemented_this_platform",
|
|
27
|
-
"aggregator_rebuild_failed",
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def probe_display_capabilities(
|
|
32
|
-
env: dict[str, str] | None = None,
|
|
33
|
-
platform: str | None = None,
|
|
34
|
-
tmux: Any | None = None,
|
|
35
|
-
) -> dict[str, Any]:
|
|
36
|
-
env_map = dict({} if env is None else env)
|
|
37
|
-
platform_name = _display_platform(platform, env_map)
|
|
38
|
-
unsupported = platform_name.startswith("win") or platform_name in {"windows", "wsl"}
|
|
39
|
-
tmux_info = _current_tmux_info(tmux, env_map) if tmux is not None else {}
|
|
40
|
-
leader_session = tmux_info.get("leader_session") or env_map.get("TEAM_AGENT_LEADER_SESSION_NAME")
|
|
41
|
-
leader_pane = tmux_info.get("leader_pane") or env_map.get("TMUX_PANE") or env_map.get("TEAM_AGENT_LEADER_PANE_ID")
|
|
42
|
-
in_tmux = bool(env_map.get("TMUX") or env_map.get("TMUX_PANE") or tmux_info.get("ok")) and not unsupported
|
|
43
|
-
caps = {
|
|
44
|
-
"tmux_append_windows": bool(in_tmux and not unsupported),
|
|
45
|
-
"adaptive_display": bool(in_tmux and not unsupported),
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
"in_tmux": in_tmux,
|
|
49
|
-
"platform": platform_name,
|
|
50
|
-
"leader_session": leader_session,
|
|
51
|
-
"leader_pane": leader_pane,
|
|
52
|
-
"caps": caps,
|
|
53
|
-
"adaptive_status": "not_implemented_this_platform" if unsupported else ("available" if in_tmux else "leader_not_in_tmux"),
|
|
54
|
-
"reason": "not_implemented_this_platform" if unsupported else (None if in_tmux else "leader_not_in_tmux"),
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def open_adaptive_display(
|
|
59
|
-
workspace: Path,
|
|
60
|
-
session_name: str,
|
|
61
|
-
jobs: list[tuple[str, dict[str, Any]]],
|
|
62
|
-
event_log: EventLog,
|
|
63
|
-
capability_probe: dict[str, Any] | None = None,
|
|
64
|
-
) -> dict[str, dict[str, Any]]:
|
|
65
|
-
from team_agent.runtime import run_cmd
|
|
66
|
-
_ = workspace
|
|
67
|
-
probe = capability_probe or probe_display_capabilities(env=dict(os.environ), tmux=run_cmd)
|
|
68
|
-
if probe.get("reason") == "not_implemented_this_platform":
|
|
69
|
-
return adaptive_blocked(jobs, event_log, "not_implemented_this_platform", platform=probe.get("platform"))
|
|
70
|
-
leader_session = str(probe.get("leader_session") or _state_leader_session(workspace) or "")
|
|
71
|
-
if not probe.get("in_tmux") or not leader_session:
|
|
72
|
-
return adaptive_blocked(jobs, event_log, "leader_not_in_tmux", platform=probe.get("platform"))
|
|
73
|
-
|
|
74
|
-
linked_results = prepare_adaptive_linked_sessions(session_name, jobs)
|
|
75
|
-
displays: dict[str, dict[str, Any]] = {}
|
|
76
|
-
linked_jobs: list[tuple[str, dict[str, Any], str]] = []
|
|
77
|
-
for agent_id, agent in jobs:
|
|
78
|
-
linked = linked_results.get(agent_id, {})
|
|
79
|
-
linked_session = linked.get("linked_session") or ghostty_display_session_name(session_name, agent_id)
|
|
80
|
-
if linked.get("ok"):
|
|
81
|
-
linked_jobs.append((agent_id, agent, linked_session))
|
|
82
|
-
continue
|
|
83
|
-
displays.update(
|
|
84
|
-
adaptive_blocked(
|
|
85
|
-
[(agent_id, agent)],
|
|
86
|
-
event_log,
|
|
87
|
-
"worker_session_missing",
|
|
88
|
-
leader_session=leader_session,
|
|
89
|
-
linked_sessions={agent_id: linked_session},
|
|
90
|
-
error=linked.get("error") or linked.get("reason"),
|
|
91
|
-
target=f"{session_name}:{agent_id}",
|
|
92
|
-
)
|
|
93
|
-
)
|
|
94
|
-
if displays:
|
|
95
|
-
kill_ghostty_workspace_linked_sessions([linked_session for _agent_id, _agent, linked_session in linked_jobs])
|
|
96
|
-
return adaptive_blocked(
|
|
97
|
-
jobs,
|
|
98
|
-
event_log,
|
|
99
|
-
"worker_session_missing",
|
|
100
|
-
leader_session=leader_session,
|
|
101
|
-
linked_sessions={agent_id: linked.get("linked_session") for agent_id, linked in linked_results.items()},
|
|
102
|
-
error=next((display.get("error") for display in displays.values() if display.get("error")), None),
|
|
103
|
-
)
|
|
104
|
-
if not linked_jobs:
|
|
105
|
-
return displays
|
|
106
|
-
|
|
107
|
-
close_adaptive_windows(leader_session, session_name, event_log)
|
|
108
|
-
prepared = prepare_adaptive_windows(leader_session, session_name, linked_jobs)
|
|
109
|
-
if not prepared["ok"]:
|
|
110
|
-
close_adaptive_windows(leader_session, session_name, event_log)
|
|
111
|
-
kill_ghostty_workspace_linked_sessions([linked_session for _agent_id, _agent, linked_session in linked_jobs])
|
|
112
|
-
displays.update(
|
|
113
|
-
adaptive_blocked(
|
|
114
|
-
[(agent_id, agent) for agent_id, agent, _linked_session in linked_jobs],
|
|
115
|
-
event_log,
|
|
116
|
-
prepared["reason"],
|
|
117
|
-
leader_session=leader_session,
|
|
118
|
-
linked_sessions={agent_id: linked_session for agent_id, _agent, linked_session in linked_jobs},
|
|
119
|
-
error=prepared.get("error"),
|
|
120
|
-
target=prepared.get("target"),
|
|
121
|
-
)
|
|
122
|
-
)
|
|
123
|
-
return displays
|
|
124
|
-
|
|
125
|
-
panes = {pane["agent_id"]: pane for pane in prepared["panes"]}
|
|
126
|
-
for agent_id, agent, linked_session in linked_jobs:
|
|
127
|
-
pane = panes.get(agent_id, {})
|
|
128
|
-
display = {
|
|
129
|
-
"backend": "adaptive",
|
|
130
|
-
"status": "opened",
|
|
131
|
-
"window": pane.get("window_name"),
|
|
132
|
-
"workspace_window": pane.get("window_name"),
|
|
133
|
-
"pane_id": pane.get("pane_id"),
|
|
134
|
-
"pane_title": pane.get("title") or display_pane_title(agent),
|
|
135
|
-
"target": f"{session_name}:{agent_id}",
|
|
136
|
-
"target_worker_session": f"{session_name}:{agent_id}",
|
|
137
|
-
"linked_session": linked_session,
|
|
138
|
-
"leader_session": leader_session,
|
|
139
|
-
"display_session": leader_session,
|
|
140
|
-
"fallback": "tmux_headless",
|
|
141
|
-
"note": "Adaptive display appends tagged tmux windows to the leader session; each pane attaches to a linked worker session.",
|
|
142
|
-
}
|
|
143
|
-
event_log.write("display.adaptive_opened", agent_id=agent_id, worker_id=agent_id, **display)
|
|
144
|
-
displays[agent_id] = display
|
|
145
|
-
return displays
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def prepare_adaptive_windows(
|
|
149
|
-
leader_session: str,
|
|
150
|
-
session_name: str,
|
|
151
|
-
linked_jobs: list[tuple[str, dict[str, Any], str]],
|
|
152
|
-
) -> dict[str, Any]:
|
|
153
|
-
prepared = prepare_tmux_attached_panes(
|
|
154
|
-
leader_session,
|
|
155
|
-
linked_jobs,
|
|
156
|
-
window_name_for_index=lambda index: team_scoped_display_window_name(session_name, index),
|
|
157
|
-
create_first_as_session=False,
|
|
158
|
-
reason_map={
|
|
159
|
-
"create_window": "window_create_failed",
|
|
160
|
-
"title": "aggregator_rebuild_failed",
|
|
161
|
-
"remain": "aggregator_rebuild_failed",
|
|
162
|
-
"split": "split_failed",
|
|
163
|
-
"layout": "aggregator_rebuild_failed",
|
|
164
|
-
},
|
|
165
|
-
stderr_reason_allowlist=ADAPTIVE_BLOCK_REASONS,
|
|
166
|
-
)
|
|
167
|
-
if prepared.get("ok"):
|
|
168
|
-
prepared["leader_session"] = leader_session
|
|
169
|
-
return prepared
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
def prepare_adaptive_linked_sessions(
|
|
173
|
-
session_name: str,
|
|
174
|
-
jobs: list[tuple[str, dict[str, Any]]],
|
|
175
|
-
) -> dict[str, dict[str, Any]]:
|
|
176
|
-
from team_agent.runtime import _tmux_session_exists, run_cmd
|
|
177
|
-
results: dict[str, dict[str, Any]] = {}
|
|
178
|
-
for agent_id, _agent in jobs:
|
|
179
|
-
linked_session = ghostty_display_session_name(session_name, agent_id)
|
|
180
|
-
if linked_session == session_name:
|
|
181
|
-
results[agent_id] = {"ok": False, "reason": "worker_session_missing", "linked_session": linked_session}
|
|
182
|
-
continue
|
|
183
|
-
if _tmux_session_exists(linked_session):
|
|
184
|
-
cleanup = run_cmd(["tmux", "kill-session", "-t", linked_session], timeout=10)
|
|
185
|
-
if cleanup.returncode != 0:
|
|
186
|
-
results[agent_id] = {
|
|
187
|
-
"ok": False,
|
|
188
|
-
"reason": "worker_session_missing",
|
|
189
|
-
"error": cleanup.stderr.strip(),
|
|
190
|
-
"linked_session": linked_session,
|
|
191
|
-
}
|
|
192
|
-
continue
|
|
193
|
-
created = run_cmd(["tmux", "new-session", "-d", "-t", session_name, "-s", linked_session], timeout=10)
|
|
194
|
-
if created.returncode != 0:
|
|
195
|
-
results[agent_id] = {
|
|
196
|
-
"ok": False,
|
|
197
|
-
"reason": "worker_session_missing",
|
|
198
|
-
"error": created.stderr.strip() or created.stdout.strip(),
|
|
199
|
-
"linked_session": linked_session,
|
|
200
|
-
}
|
|
201
|
-
continue
|
|
202
|
-
selected = run_cmd(["tmux", "select-window", "-t", f"{linked_session}:{agent_id}"], timeout=10)
|
|
203
|
-
if selected.returncode != 0:
|
|
204
|
-
run_cmd(["tmux", "kill-session", "-t", linked_session], timeout=10)
|
|
205
|
-
results[agent_id] = {
|
|
206
|
-
"ok": False,
|
|
207
|
-
"reason": "worker_session_missing",
|
|
208
|
-
"error": selected.stderr.strip() or selected.stdout.strip(),
|
|
209
|
-
"linked_session": linked_session,
|
|
210
|
-
}
|
|
211
|
-
continue
|
|
212
|
-
results[agent_id] = {"ok": True, "linked_session": linked_session}
|
|
213
|
-
return results
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def adaptive_blocked(
|
|
217
|
-
jobs: list[tuple[str, dict[str, Any]]],
|
|
218
|
-
event_log: EventLog,
|
|
219
|
-
reason: str,
|
|
220
|
-
leader_session: str | None = None,
|
|
221
|
-
linked_sessions: dict[str, str] | None = None,
|
|
222
|
-
error: str | None = None,
|
|
223
|
-
target: str | None = None,
|
|
224
|
-
platform: str | None = None,
|
|
225
|
-
) -> dict[str, dict[str, Any]]:
|
|
226
|
-
reason = reason if reason in ADAPTIVE_BLOCK_REASONS else "aggregator_rebuild_failed"
|
|
227
|
-
displays: dict[str, dict[str, Any]] = {}
|
|
228
|
-
for agent_id, _agent in jobs:
|
|
229
|
-
display = {
|
|
230
|
-
"backend": "adaptive",
|
|
231
|
-
"status": "blocked",
|
|
232
|
-
"reason": reason,
|
|
233
|
-
"error": error,
|
|
234
|
-
"target": target or f"{agent_id}",
|
|
235
|
-
"target_worker_session": target or f"{agent_id}",
|
|
236
|
-
"leader_session": leader_session,
|
|
237
|
-
"linked_session": (linked_sessions or {}).get(agent_id),
|
|
238
|
-
"display_session": leader_session,
|
|
239
|
-
"fallback": "tmux_headless",
|
|
240
|
-
"hint": "Start the leader inside tmux to enable adaptive team display." if reason == "leader_not_in_tmux" else None,
|
|
241
|
-
"platform": platform,
|
|
242
|
-
}
|
|
243
|
-
event_log.write("display.adaptive_blocked", agent_id=agent_id, worker_id=agent_id, **display)
|
|
244
|
-
displays[agent_id] = display
|
|
245
|
-
return displays
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def close_adaptive_display(state: dict[str, Any], event_log: EventLog) -> dict[str, Any]:
|
|
249
|
-
displays = [
|
|
250
|
-
(agent_id, agent_state.get("display") or {})
|
|
251
|
-
for agent_id, agent_state in state.get("agents", {}).items()
|
|
252
|
-
if (agent_state.get("display") or {}).get("backend") == "adaptive"
|
|
253
|
-
]
|
|
254
|
-
if not displays:
|
|
255
|
-
return {"windows": [], "linked_sessions": [], "orphans_detected": {}}
|
|
256
|
-
killed_windows: list[str] = []
|
|
257
|
-
linked_sessions: list[str] = []
|
|
258
|
-
session_name = str(state.get("session_name") or "")
|
|
259
|
-
leader_session = _adaptive_leader_session(state, displays)
|
|
260
|
-
needs_named_fallback = False
|
|
261
|
-
for _agent_id, display in displays:
|
|
262
|
-
linked = display.get("linked_session")
|
|
263
|
-
if linked:
|
|
264
|
-
linked_sessions.append(str(linked))
|
|
265
|
-
if not linked or not display.get("leader_session") or not (display.get("workspace_window") or display.get("window")):
|
|
266
|
-
needs_named_fallback = True
|
|
267
|
-
seen_targets: set[str] = set()
|
|
268
|
-
for _agent_id, display in displays:
|
|
269
|
-
display_leader_session = str(display.get("leader_session") or "")
|
|
270
|
-
window_name = str(display.get("workspace_window") or display.get("window") or "")
|
|
271
|
-
if not display_leader_session or not window_name:
|
|
272
|
-
continue
|
|
273
|
-
target = f"{display_leader_session}:{window_name}"
|
|
274
|
-
if target in seen_targets:
|
|
275
|
-
continue
|
|
276
|
-
seen_targets.add(target)
|
|
277
|
-
if kill_adaptive_window(target):
|
|
278
|
-
killed_windows.append(target)
|
|
279
|
-
removed_orphans: dict[str, list[str]] = {}
|
|
280
|
-
if needs_named_fallback and leader_session and session_name:
|
|
281
|
-
named_windows = close_adaptive_windows(leader_session, session_name, event_log)
|
|
282
|
-
killed_windows.extend(named_windows)
|
|
283
|
-
linked_closed = kill_ghostty_workspace_linked_sessions(linked_sessions)
|
|
284
|
-
named_closed, named_failed = _kill_adaptive_named_display_sessions(session_name, [agent_id for agent_id, _display in displays])
|
|
285
|
-
linked_closed.extend(named_closed)
|
|
286
|
-
removed_orphans = _adaptive_orphan_summary(named_closed, named_windows)
|
|
287
|
-
else:
|
|
288
|
-
named_failed = []
|
|
289
|
-
linked_closed = kill_ghostty_workspace_linked_sessions(linked_sessions)
|
|
290
|
-
orphans = _adaptive_orphans(session_name, leader_session, [agent_id for agent_id, _display in displays], named_failed) if needs_named_fallback else {}
|
|
291
|
-
event_log.write("display.adaptive_closed", windows=killed_windows, linked_sessions=linked_closed, orphans_detected=orphans, orphans_removed=removed_orphans)
|
|
292
|
-
return {"windows": killed_windows, "linked_sessions": linked_closed, "orphans_detected": orphans, "orphans_removed": removed_orphans}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
def close_adaptive_windows(leader_session: str, session_name: str, event_log: EventLog | None = None) -> list[str]:
|
|
296
|
-
from team_agent.runtime import run_cmd
|
|
297
|
-
prefix = f"team-agent:{session_name}:overview"
|
|
298
|
-
proc = run_cmd(["tmux", "list-windows", "-t", leader_session, "-F", "#{window_name}"], timeout=10)
|
|
299
|
-
if proc.returncode != 0:
|
|
300
|
-
return []
|
|
301
|
-
killed: list[str] = []
|
|
302
|
-
for window_name in proc.stdout.splitlines():
|
|
303
|
-
if window_name != prefix and not window_name.startswith(f"{prefix}-"):
|
|
304
|
-
continue
|
|
305
|
-
target = f"{leader_session}:{window_name}"
|
|
306
|
-
if kill_adaptive_window(target):
|
|
307
|
-
killed.append(target)
|
|
308
|
-
if event_log is not None and killed:
|
|
309
|
-
event_log.write("display.adaptive_stale_windows_closed", leader_session=leader_session, windows=killed)
|
|
310
|
-
return killed
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
def _adaptive_leader_session(state: dict[str, Any], displays: list[tuple[str, dict[str, Any]]]) -> str:
|
|
314
|
-
for _agent_id, display in displays:
|
|
315
|
-
if display.get("leader_session"):
|
|
316
|
-
return str(display["leader_session"])
|
|
317
|
-
receiver = state.get("leader_receiver") if isinstance(state.get("leader_receiver"), dict) else {}
|
|
318
|
-
return str(receiver.get("session_name") or "")
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
def _adaptive_named_display_sessions(session_name: str, agent_ids: list[str], fallback_exact: bool = True) -> list[str]:
|
|
322
|
-
from team_agent.runtime import run_cmd
|
|
323
|
-
if not session_name or not agent_ids:
|
|
324
|
-
return []
|
|
325
|
-
exact = [ghostty_display_session_name(session_name, agent_id) for agent_id in agent_ids]
|
|
326
|
-
proc = run_cmd(["tmux", "list-sessions", "-F", "#{session_name}"], timeout=10)
|
|
327
|
-
if proc.returncode != 0:
|
|
328
|
-
return exact if fallback_exact else []
|
|
329
|
-
prefixes = [ghostty_display_session_name(session_name, agent_id).rsplit("__", 1)[0] + "__" for agent_id in agent_ids]
|
|
330
|
-
matched = [name for name in proc.stdout.splitlines() if any(name.startswith(prefix) for prefix in prefixes)]
|
|
331
|
-
return matched or (exact if fallback_exact else [])
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
def _kill_adaptive_named_display_sessions(session_name: str, agent_ids: list[str]) -> tuple[list[str], list[str]]:
|
|
335
|
-
from team_agent.runtime import run_cmd
|
|
336
|
-
killed: list[str] = []
|
|
337
|
-
failed: list[str] = []
|
|
338
|
-
for display_session in _adaptive_named_display_sessions(session_name, agent_ids):
|
|
339
|
-
proc = run_cmd(["tmux", "kill-session", "-t", display_session], timeout=10)
|
|
340
|
-
if proc.returncode == 0:
|
|
341
|
-
killed.append(display_session)
|
|
342
|
-
else:
|
|
343
|
-
failed.append(display_session)
|
|
344
|
-
return killed, failed
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
def _adaptive_orphans(session_name: str, leader_session: str, agent_ids: list[str], failed_sessions: list[str]) -> dict[str, list[str]]:
|
|
348
|
-
display_sessions = sorted(set([*_adaptive_named_display_sessions(session_name, agent_ids, fallback_exact=False), *failed_sessions]))
|
|
349
|
-
windows: list[str] = []
|
|
350
|
-
if leader_session and session_name:
|
|
351
|
-
windows = _adaptive_window_orphans(leader_session, session_name)
|
|
352
|
-
if not display_sessions and not windows:
|
|
353
|
-
return {}
|
|
354
|
-
return {
|
|
355
|
-
"adaptive_display_sessions": sorted(set(display_sessions)),
|
|
356
|
-
"adaptive_overview_windows": sorted(set(windows)),
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
def _adaptive_orphan_summary(display_sessions: list[str], windows: list[str]) -> dict[str, list[str]]:
|
|
361
|
-
if not display_sessions and not windows:
|
|
362
|
-
return {}
|
|
363
|
-
return {
|
|
364
|
-
"adaptive_display_sessions": sorted(set(display_sessions)),
|
|
365
|
-
"adaptive_overview_windows": sorted(set(windows)),
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
def _adaptive_window_orphans(leader_session: str, session_name: str) -> list[str]:
|
|
370
|
-
from team_agent.runtime import run_cmd
|
|
371
|
-
prefix = f"team-agent:{session_name}:overview"
|
|
372
|
-
proc = run_cmd(["tmux", "list-windows", "-t", leader_session, "-F", "#{window_name}"], timeout=10)
|
|
373
|
-
if proc.returncode != 0:
|
|
374
|
-
return []
|
|
375
|
-
return [
|
|
376
|
-
f"{leader_session}:{window_name}"
|
|
377
|
-
for window_name in proc.stdout.splitlines()
|
|
378
|
-
if window_name == prefix or window_name.startswith(f"{prefix}-")
|
|
379
|
-
]
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
def kill_adaptive_window(target: str) -> bool:
|
|
383
|
-
from team_agent.runtime import run_cmd
|
|
384
|
-
proc = run_cmd(["tmux", "kill-window", "-t", target], timeout=10)
|
|
385
|
-
return proc.returncode == 0
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
def set_adaptive_pane_title(pane_id: str, title: str) -> dict[str, Any]:
|
|
389
|
-
return set_tmux_display_pane_title(pane_id, title, "aggregator_rebuild_failed")
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
def _display_platform(value: str | None, env: dict[str, str]) -> str:
|
|
393
|
-
if value:
|
|
394
|
-
return value.lower()
|
|
395
|
-
if env.get("WSL_DISTRO_NAME") or env.get("WSL_INTEROP"):
|
|
396
|
-
return "wsl"
|
|
397
|
-
return platform_module.system().lower()
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
def _current_tmux_info(tmux: Any, env: dict[str, str]) -> dict[str, Any]:
|
|
401
|
-
pane = env.get("TMUX_PANE") or ""
|
|
402
|
-
commands: list[list[str]] = []
|
|
403
|
-
if pane:
|
|
404
|
-
commands.insert(0, ["tmux", "display-message", "-p", "-t", pane, "-F", "#{session_name}\t#{pane_id}"])
|
|
405
|
-
commands.insert(1, ["tmux", "display-message", "-p", "-t", pane, "-F", "#{session_name}"])
|
|
406
|
-
commands.insert(2, ["tmux", "display-message", "-p", "-t", pane, "#{session_name}"])
|
|
407
|
-
if env.get("TMUX"):
|
|
408
|
-
commands.extend(
|
|
409
|
-
[
|
|
410
|
-
["tmux", "display-message", "-p", "-F", "#{session_name}\t#{pane_id}"],
|
|
411
|
-
["tmux", "display-message", "-p", "-F", "#{session_name}"],
|
|
412
|
-
["tmux", "display-message", "-p", "#{session_name}\t#{pane_id}"],
|
|
413
|
-
["tmux", "display-message", "-p", "#{session_name}"],
|
|
414
|
-
]
|
|
415
|
-
)
|
|
416
|
-
for command in commands:
|
|
417
|
-
proc = _call_tmux(tmux, command)
|
|
418
|
-
parsed = _parse_tmux_session_pane(proc)
|
|
419
|
-
if parsed:
|
|
420
|
-
return parsed
|
|
421
|
-
if pane:
|
|
422
|
-
listed = _leader_from_tmux_panes(tmux, pane)
|
|
423
|
-
if listed:
|
|
424
|
-
return listed
|
|
425
|
-
session = _first_tmux_session(tmux)
|
|
426
|
-
if session:
|
|
427
|
-
return {"ok": True, "leader_session": session, "leader_pane": pane}
|
|
428
|
-
return {"ok": False}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
def _call_tmux(tmux: Any, args: list[str]) -> Any | None:
|
|
432
|
-
try:
|
|
433
|
-
if callable(tmux):
|
|
434
|
-
try:
|
|
435
|
-
return tmux(args, timeout=5)
|
|
436
|
-
except TypeError:
|
|
437
|
-
return tmux(args)
|
|
438
|
-
if hasattr(tmux, "run_cmd"):
|
|
439
|
-
return tmux.run_cmd(args)
|
|
440
|
-
except Exception:
|
|
441
|
-
return None
|
|
442
|
-
return None
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
def _parse_tmux_session_pane(proc: Any | None) -> dict[str, Any] | None:
|
|
446
|
-
if not proc or getattr(proc, "returncode", 1) != 0:
|
|
447
|
-
return None
|
|
448
|
-
parts = str(getattr(proc, "stdout", "")).strip().split("\t")
|
|
449
|
-
if len(parts) >= 2 and parts[0].startswith("%") and parts[1]:
|
|
450
|
-
return {"ok": True, "leader_session": parts[1], "leader_pane": parts[0]}
|
|
451
|
-
if len(parts) >= 2 and parts[0]:
|
|
452
|
-
return {"ok": True, "leader_session": parts[0], "leader_pane": parts[1]}
|
|
453
|
-
if len(parts) == 1 and parts[0] and not parts[0].startswith("%"):
|
|
454
|
-
return {"ok": True, "leader_session": parts[0], "leader_pane": None}
|
|
455
|
-
return None
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
def _leader_from_tmux_panes(tmux: Any, pane: str) -> dict[str, Any] | None:
|
|
459
|
-
proc = _call_tmux(
|
|
460
|
-
tmux,
|
|
461
|
-
[
|
|
462
|
-
"tmux",
|
|
463
|
-
"list-panes",
|
|
464
|
-
"-a",
|
|
465
|
-
"-F",
|
|
466
|
-
"#{pane_id}\t#{session_name}\t#{pane_current_command}\t#{pane_active}",
|
|
467
|
-
],
|
|
468
|
-
)
|
|
469
|
-
if not proc or getattr(proc, "returncode", 1) != 0:
|
|
470
|
-
return None
|
|
471
|
-
rows = [line.split("\t") for line in str(getattr(proc, "stdout", "")).splitlines() if line.strip()]
|
|
472
|
-
if pane:
|
|
473
|
-
for row in rows:
|
|
474
|
-
if len(row) >= 2 and row[0] == pane:
|
|
475
|
-
return {"ok": True, "leader_session": row[1], "leader_pane": row[0]}
|
|
476
|
-
for row in rows:
|
|
477
|
-
if len(row) >= 3 and _leader_shaped_command(row[2]):
|
|
478
|
-
return {"ok": True, "leader_session": row[1], "leader_pane": row[0]}
|
|
479
|
-
if rows and len(rows[0]) >= 2:
|
|
480
|
-
return {"ok": True, "leader_session": rows[0][1], "leader_pane": rows[0][0]}
|
|
481
|
-
return None
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
def _leader_shaped_command(command: str) -> bool:
|
|
485
|
-
lowered = command.lower()
|
|
486
|
-
return any(token in lowered for token in ("claude", "codex", "fake"))
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
def _first_tmux_session(tmux: Any) -> str | None:
|
|
490
|
-
for command in (
|
|
491
|
-
["tmux", "list-clients", "-F", "#{client_session}"],
|
|
492
|
-
["tmux", "list-sessions", "-F", "#{session_name}"],
|
|
493
|
-
):
|
|
494
|
-
proc = _call_tmux(tmux, command)
|
|
495
|
-
if not proc or getattr(proc, "returncode", 1) != 0:
|
|
496
|
-
continue
|
|
497
|
-
for line in str(getattr(proc, "stdout", "")).splitlines():
|
|
498
|
-
if line.strip():
|
|
499
|
-
return line.strip()
|
|
500
|
-
return None
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
def _state_leader_session(workspace: Path) -> str | None:
|
|
504
|
-
try:
|
|
505
|
-
from team_agent.state import load_runtime_state
|
|
506
|
-
state = load_runtime_state(workspace)
|
|
507
|
-
except Exception:
|
|
508
|
-
return None
|
|
509
|
-
receiver = state.get("leader_receiver") if isinstance(state.get("leader_receiver"), dict) else {}
|
|
510
|
-
session_name = receiver.get("session_name")
|
|
511
|
-
return str(session_name) if session_name else None
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ADAPTIVE_DISPLAY_BACKEND = "adaptive"
|
|
7
|
-
GHOSTTY_DISPLAY_BACKENDS = {"ghostty", "ghostty_window", "ghostty_workspace"}
|
|
8
|
-
DISPLAY_BACKENDS_WITH_WORKER_VIEWS = GHOSTTY_DISPLAY_BACKENDS | {ADAPTIVE_DISPLAY_BACKEND}
|
|
9
|
-
VALID_DISPLAY_BACKENDS = {"none", "tmux_attach", "iterm"} | DISPLAY_BACKENDS_WITH_WORKER_VIEWS
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def resolve_display_backend(
|
|
13
|
-
requested: str | None,
|
|
14
|
-
*,
|
|
15
|
-
recorded: str | None = None,
|
|
16
|
-
event_log: Any | None = None,
|
|
17
|
-
source: str,
|
|
18
|
-
) -> str:
|
|
19
|
-
resolved = requested or recorded or ADAPTIVE_DISPLAY_BACKEND
|
|
20
|
-
reason = "explicit" if requested else ("recorded" if recorded else "default")
|
|
21
|
-
if event_log is not None and reason == "default":
|
|
22
|
-
event_log.write(
|
|
23
|
-
"display.backend_resolved",
|
|
24
|
-
requested=None,
|
|
25
|
-
resolved=resolved,
|
|
26
|
-
reason=reason,
|
|
27
|
-
source=source,
|
|
28
|
-
)
|
|
29
|
-
return resolved
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def resolve_restart_display_backend(spec: dict[str, Any], state: dict[str, Any], event_log: Any) -> str:
|
|
33
|
-
return resolve_display_backend(
|
|
34
|
-
spec.get("runtime", {}).get("display_backend"),
|
|
35
|
-
recorded=state.get("display_backend"),
|
|
36
|
-
event_log=event_log,
|
|
37
|
-
source="restart",
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def display_backend_has_worker_views(display_backend: str) -> bool:
|
|
42
|
-
return display_backend in DISPLAY_BACKENDS_WITH_WORKER_VIEWS
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def display_backend_opens_before_leader_rebind(display_backend: str) -> bool:
|
|
46
|
-
return display_backend_has_worker_views(display_backend) and display_backend != ADAPTIVE_DISPLAY_BACKEND
|