@team-agent/installer 0.2.10 → 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 -83
- package/src/team_agent/coordinator/lifecycle.py +0 -363
- 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 -200
- package/src/team_agent/idle_takeover.py +0 -59
- package/src/team_agent/idle_takeover_wiring.py +0 -111
- 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 -254
- package/src/team_agent/messaging/delivery.py +0 -473
- 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 -457
- 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 -86
- 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 -1239
- 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 -143
- 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 -602
- 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,52 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from team_agent.state import load_runtime_state
|
|
7
|
-
from team_agent.status.constants import APPROVAL_SCAN_LINES
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def approvals(workspace: Path, agent_id: str | None = None) -> dict[str, Any]:
|
|
11
|
-
from team_agent.runtime import RuntimeError, _extract_approval_prompt, _tmux_window_exists, run_cmd
|
|
12
|
-
state = load_runtime_state(workspace)
|
|
13
|
-
session_name = state.get("session_name")
|
|
14
|
-
approvals_found: list[dict[str, Any]] = []
|
|
15
|
-
agents = state.get("agents", {})
|
|
16
|
-
target_ids = [agent_id] if agent_id else sorted(agents)
|
|
17
|
-
for target_id in target_ids:
|
|
18
|
-
agent = agents.get(target_id)
|
|
19
|
-
if not agent:
|
|
20
|
-
raise RuntimeError(f"unknown agent id: {target_id}")
|
|
21
|
-
window = agent.get("window", target_id)
|
|
22
|
-
if not session_name or not _tmux_window_exists(session_name, window):
|
|
23
|
-
continue
|
|
24
|
-
proc = run_cmd(["tmux", "capture-pane", "-p", "-S", f"-{APPROVAL_SCAN_LINES}", "-t", f"{session_name}:{window}"], timeout=5)
|
|
25
|
-
if proc.returncode != 0:
|
|
26
|
-
continue
|
|
27
|
-
prompt = _extract_approval_prompt(target_id, proc.stdout)
|
|
28
|
-
if prompt:
|
|
29
|
-
approvals_found.append(prompt)
|
|
30
|
-
return {
|
|
31
|
-
"ok": True,
|
|
32
|
-
"waiting": bool(approvals_found),
|
|
33
|
-
"waiting_count": len(approvals_found),
|
|
34
|
-
"approvals": approvals_found,
|
|
35
|
-
"scan": {"mode": "tail", "lines": APPROVAL_SCAN_LINES, "raw_output": False},
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def format_approvals(workspace: Path, agent_id: str | None = None) -> str:
|
|
40
|
-
result = approvals(workspace, agent_id=agent_id)
|
|
41
|
-
if not result["approvals"]:
|
|
42
|
-
return "No pending approvals."
|
|
43
|
-
lines: list[str] = []
|
|
44
|
-
for item in result["approvals"]:
|
|
45
|
-
detail = item.get("tool") or item.get("command") or item.get("kind")
|
|
46
|
-
lines.append(f"{item['agent_id']}: {item['state']} {item['kind']} {detail}".rstrip())
|
|
47
|
-
if item.get("prompt"):
|
|
48
|
-
lines.append(f" prompt: {item['prompt']}")
|
|
49
|
-
if item.get("choices"):
|
|
50
|
-
lines.append(" choices: " + "; ".join(item["choices"]))
|
|
51
|
-
lines.append(" raw terminal output omitted; use debug-only peek with --search/--tail/--head if the user explicitly asks.")
|
|
52
|
-
return "\n".join(lines)
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from team_agent.status.constants import STATUS_EVENT_LIMIT, STATUS_TEXT_LIMIT
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def compact_status(data: dict[str, Any]) -> dict[str, Any]:
|
|
9
|
-
return {
|
|
10
|
-
"team": data.get("team"),
|
|
11
|
-
"session_name": data.get("session_name"),
|
|
12
|
-
"tmux_session_present": data.get("tmux_session_present"),
|
|
13
|
-
"leader_receiver": compact_mapping(
|
|
14
|
-
data.get("leader_receiver", {}),
|
|
15
|
-
{
|
|
16
|
-
"status",
|
|
17
|
-
"provider",
|
|
18
|
-
"mode",
|
|
19
|
-
"session_name",
|
|
20
|
-
"window_name",
|
|
21
|
-
"pane_id",
|
|
22
|
-
"pane_current_command",
|
|
23
|
-
},
|
|
24
|
-
),
|
|
25
|
-
"agents": {
|
|
26
|
-
agent_id: compact_agent_state(agent_id, agent)
|
|
27
|
-
for agent_id, agent in (data.get("agents") or {}).items()
|
|
28
|
-
},
|
|
29
|
-
"agent_health": data.get("agent_health", {}),
|
|
30
|
-
"tasks": [compact_task(task) for task in data.get("tasks", [])],
|
|
31
|
-
"messages": data.get("messages", {}),
|
|
32
|
-
"queued_messages": data.get("queued_messages", [])[:8],
|
|
33
|
-
"results": data.get("results", {}),
|
|
34
|
-
"latest_results": data.get("latest_results", [])[:5],
|
|
35
|
-
"coordinator": compact_mapping(data.get("coordinator", {}), {"status", "pid", "metadata_ok", "schema_ok"}),
|
|
36
|
-
"last_events": [compact_event(event) for event in data.get("last_events", [])[-STATUS_EVENT_LIMIT:]],
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def compact_agent_state(agent_id: str, agent: dict[str, Any]) -> dict[str, Any]:
|
|
41
|
-
display = agent.get("display") or {}
|
|
42
|
-
result = compact_mapping(
|
|
43
|
-
agent,
|
|
44
|
-
{
|
|
45
|
-
"agent_id",
|
|
46
|
-
"status",
|
|
47
|
-
"provider",
|
|
48
|
-
"model",
|
|
49
|
-
"tmux_window_present",
|
|
50
|
-
"session_id",
|
|
51
|
-
"captured_via",
|
|
52
|
-
"attribution_confidence",
|
|
53
|
-
},
|
|
54
|
-
)
|
|
55
|
-
result.setdefault("agent_id", agent_id)
|
|
56
|
-
if display:
|
|
57
|
-
result["display"] = compact_mapping(
|
|
58
|
-
display,
|
|
59
|
-
{
|
|
60
|
-
"backend",
|
|
61
|
-
"status",
|
|
62
|
-
"workspace_window",
|
|
63
|
-
"pane_id",
|
|
64
|
-
"pid",
|
|
65
|
-
"pids",
|
|
66
|
-
"reason",
|
|
67
|
-
},
|
|
68
|
-
)
|
|
69
|
-
return result
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def compact_task(task: dict[str, Any]) -> dict[str, Any]:
|
|
73
|
-
return compact_mapping(
|
|
74
|
-
task,
|
|
75
|
-
{
|
|
76
|
-
"id",
|
|
77
|
-
"title",
|
|
78
|
-
"status",
|
|
79
|
-
"assignee",
|
|
80
|
-
"type",
|
|
81
|
-
"risk",
|
|
82
|
-
"accepted_result_id",
|
|
83
|
-
"last_result_summary",
|
|
84
|
-
},
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def compact_event(event: dict[str, Any]) -> dict[str, Any]:
|
|
89
|
-
skipped = {"command", "payload", "launch_args", "content", "prompt", "developer_instructions"}
|
|
90
|
-
kept = {
|
|
91
|
-
"event",
|
|
92
|
-
"ts",
|
|
93
|
-
"agent_id",
|
|
94
|
-
"task_id",
|
|
95
|
-
"message_id",
|
|
96
|
-
"result_id",
|
|
97
|
-
"status",
|
|
98
|
-
"ok",
|
|
99
|
-
"reason",
|
|
100
|
-
"error",
|
|
101
|
-
"session",
|
|
102
|
-
"window",
|
|
103
|
-
"target",
|
|
104
|
-
"backend",
|
|
105
|
-
"workspace_window",
|
|
106
|
-
"pane_id",
|
|
107
|
-
"restart_mode",
|
|
108
|
-
"provider",
|
|
109
|
-
"delivery_status",
|
|
110
|
-
"warning",
|
|
111
|
-
"collected",
|
|
112
|
-
"notified",
|
|
113
|
-
"lock",
|
|
114
|
-
"waited_sec",
|
|
115
|
-
"once",
|
|
116
|
-
"pid",
|
|
117
|
-
}
|
|
118
|
-
result: dict[str, Any] = {}
|
|
119
|
-
for key, value in event.items():
|
|
120
|
-
if key in skipped or key not in kept | {"agents", "coordinator"}:
|
|
121
|
-
continue
|
|
122
|
-
if key == "agents" and isinstance(value, list):
|
|
123
|
-
result["agent_count"] = len(value)
|
|
124
|
-
result["agents"] = [
|
|
125
|
-
compact_mapping(item, {"agent_id", "restart_mode", "session_id"})
|
|
126
|
-
for item in value[:8]
|
|
127
|
-
if isinstance(item, dict)
|
|
128
|
-
]
|
|
129
|
-
continue
|
|
130
|
-
result[key] = compact_value(value)
|
|
131
|
-
return result
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def compact_mapping(source: Any, keys: set[str]) -> dict[str, Any]:
|
|
135
|
-
if not isinstance(source, dict):
|
|
136
|
-
return {}
|
|
137
|
-
return {key: compact_value(source[key]) for key in keys if key in source}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def compact_value(value: Any) -> Any:
|
|
141
|
-
if isinstance(value, str):
|
|
142
|
-
return value if len(value) <= STATUS_TEXT_LIMIT else value[: STATUS_TEXT_LIMIT - 1] + "…"
|
|
143
|
-
if isinstance(value, (int, float, bool)) or value is None:
|
|
144
|
-
return value
|
|
145
|
-
if isinstance(value, list):
|
|
146
|
-
if all(isinstance(item, (str, int, float, bool)) or item is None for item in value):
|
|
147
|
-
compact = [compact_value(item) for item in value[:8]]
|
|
148
|
-
if len(value) > 8:
|
|
149
|
-
compact.append(f"... {len(value) - 8} more")
|
|
150
|
-
return compact
|
|
151
|
-
return f"{len(value)} item(s)"
|
|
152
|
-
if isinstance(value, dict):
|
|
153
|
-
return {
|
|
154
|
-
key: compact_value(item)
|
|
155
|
-
for key, item in value.items()
|
|
156
|
-
if key not in {"command", "payload", "launch_args", "content", "prompt", "developer_instructions"}
|
|
157
|
-
}
|
|
158
|
-
return str(value)
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
STATUS_TEXT_LIMIT = 240
|
|
5
|
-
STATUS_EVENT_LIMIT = 3
|
|
6
|
-
PEEK_MAX_LINES = 80
|
|
7
|
-
PEEK_SEARCH_SCAN_LINES = 300
|
|
8
|
-
PEEK_MAX_MATCHES = 5
|
|
9
|
-
APPROVAL_SCAN_LINES = 120
|
|
10
|
-
|
|
11
|
-
PENDING_DELIVERY_STATUSES = {
|
|
12
|
-
"pending",
|
|
13
|
-
"accepted",
|
|
14
|
-
"queued_until_idle",
|
|
15
|
-
"queued_until_start",
|
|
16
|
-
"queued_stopped",
|
|
17
|
-
"queued_pane_missing",
|
|
18
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Any
|
|
6
|
-
|
|
7
|
-
from team_agent.message_store import MessageStore
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _parse_since(since: str | None) -> datetime | None:
|
|
11
|
-
if not since:
|
|
12
|
-
return None
|
|
13
|
-
try:
|
|
14
|
-
dt = datetime.fromisoformat(since.replace("Z", "+00:00"))
|
|
15
|
-
except (ValueError, AttributeError):
|
|
16
|
-
return None
|
|
17
|
-
if dt.tzinfo is None:
|
|
18
|
-
dt = dt.replace(tzinfo=timezone.utc)
|
|
19
|
-
return dt
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _filter_since(rows: list[dict[str, Any]], since: str | None) -> list[dict[str, Any]]:
|
|
23
|
-
cutoff = _parse_since(since)
|
|
24
|
-
if cutoff is None:
|
|
25
|
-
return rows
|
|
26
|
-
filtered: list[dict[str, Any]] = []
|
|
27
|
-
for row in rows:
|
|
28
|
-
ts_raw = str(row.get("created_at") or "")
|
|
29
|
-
ts = _parse_since(ts_raw)
|
|
30
|
-
if ts and ts >= cutoff:
|
|
31
|
-
filtered.append(row)
|
|
32
|
-
return filtered
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def inbox(workspace: Path, agent_id: str, limit: int = 20, since: str | None = None) -> dict[str, Any]:
|
|
36
|
-
rows = MessageStore(workspace).inbox(agent_id, limit=limit)
|
|
37
|
-
rows = _filter_since(rows, since)
|
|
38
|
-
return {"ok": True, "agent_id": agent_id, "messages": rows, "since": since}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def format_inbox(workspace: Path, agent_id: str, limit: int = 20, since: str | None = None) -> str:
|
|
42
|
-
store = MessageStore(workspace)
|
|
43
|
-
rows = store.inbox(agent_id, limit=limit)
|
|
44
|
-
rows = _filter_since(rows, since)
|
|
45
|
-
result_counts = store.result_counts()
|
|
46
|
-
note = "final results are not in inbox; use team-agent collect"
|
|
47
|
-
if result_counts.get("uncollected", 0):
|
|
48
|
-
note += f" ({result_counts['uncollected']} uncollected result(s) pending)"
|
|
49
|
-
if not rows:
|
|
50
|
-
if since:
|
|
51
|
-
return f"{agent_id}: no messages since {since}\n{note}"
|
|
52
|
-
return f"{agent_id}: no messages\n{note}"
|
|
53
|
-
lines = [
|
|
54
|
-
f"{row['created_at']} {row['sender']} -> {row['recipient']} {row['status']}: {row['content']}"
|
|
55
|
-
for row in rows
|
|
56
|
-
]
|
|
57
|
-
lines.append(note)
|
|
58
|
-
return "\n".join(lines)
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from team_agent.state import load_runtime_state
|
|
7
|
-
from team_agent.status.constants import (
|
|
8
|
-
PEEK_MAX_LINES,
|
|
9
|
-
PEEK_MAX_MATCHES,
|
|
10
|
-
PEEK_SEARCH_SCAN_LINES,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def peek(
|
|
15
|
-
workspace: Path,
|
|
16
|
-
agent_id: str,
|
|
17
|
-
*,
|
|
18
|
-
head: int | None = None,
|
|
19
|
-
tail: int | None = None,
|
|
20
|
-
search: str | None = None,
|
|
21
|
-
context: int = 3,
|
|
22
|
-
) -> dict[str, Any]:
|
|
23
|
-
from team_agent.runtime import RuntimeError, _tmux_window_exists, run_cmd
|
|
24
|
-
modes = [head is not None, tail is not None, search is not None]
|
|
25
|
-
if sum(modes) != 1:
|
|
26
|
-
raise RuntimeError("peek requires exactly one of --head, --tail, or --search")
|
|
27
|
-
if head is not None:
|
|
28
|
-
validate_line_count("--head", head)
|
|
29
|
-
if tail is not None:
|
|
30
|
-
validate_line_count("--tail", tail)
|
|
31
|
-
if search is not None and not search.strip():
|
|
32
|
-
raise RuntimeError("--search must not be empty")
|
|
33
|
-
if context < 0 or context > 10:
|
|
34
|
-
raise RuntimeError("--context must be between 0 and 10")
|
|
35
|
-
state = load_runtime_state(workspace)
|
|
36
|
-
agent = state.get("agents", {}).get(agent_id)
|
|
37
|
-
if not agent:
|
|
38
|
-
raise RuntimeError(f"unknown agent id: {agent_id}")
|
|
39
|
-
session_name = state.get("session_name")
|
|
40
|
-
window = agent.get("window", agent_id)
|
|
41
|
-
if not session_name or not _tmux_window_exists(session_name, window):
|
|
42
|
-
raise RuntimeError(f"agent terminal is not available: {agent_id}")
|
|
43
|
-
scan_lines = tail or PEEK_SEARCH_SCAN_LINES
|
|
44
|
-
proc = run_cmd(["tmux", "capture-pane", "-p", "-S", f"-{scan_lines}", "-t", f"{session_name}:{window}"], timeout=5)
|
|
45
|
-
if proc.returncode != 0:
|
|
46
|
-
raise RuntimeError(proc.stderr.strip() or f"capture failed for {agent_id}")
|
|
47
|
-
captured = proc.stdout.splitlines()
|
|
48
|
-
if head is not None:
|
|
49
|
-
selected = captured[:head]
|
|
50
|
-
return {
|
|
51
|
-
"ok": True,
|
|
52
|
-
"agent_id": agent_id,
|
|
53
|
-
"mode": "head",
|
|
54
|
-
"lines": head,
|
|
55
|
-
"scanned_lines": scan_lines,
|
|
56
|
-
"text": "\n".join(selected),
|
|
57
|
-
}
|
|
58
|
-
if tail is not None:
|
|
59
|
-
return {
|
|
60
|
-
"ok": True,
|
|
61
|
-
"agent_id": agent_id,
|
|
62
|
-
"mode": "tail",
|
|
63
|
-
"lines": tail,
|
|
64
|
-
"scanned_lines": scan_lines,
|
|
65
|
-
"text": "\n".join(captured[-tail:]),
|
|
66
|
-
}
|
|
67
|
-
assert search is not None
|
|
68
|
-
matches = search_lines(captured, search, context)
|
|
69
|
-
return {
|
|
70
|
-
"ok": True,
|
|
71
|
-
"agent_id": agent_id,
|
|
72
|
-
"mode": "search",
|
|
73
|
-
"search": search,
|
|
74
|
-
"context": context,
|
|
75
|
-
"scanned_lines": scan_lines,
|
|
76
|
-
"matches": matches,
|
|
77
|
-
"truncated": len(matches) >= PEEK_MAX_MATCHES,
|
|
78
|
-
"text": format_search_matches(matches),
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def validate_line_count(flag: str, value: int) -> None:
|
|
83
|
-
from team_agent.runtime import RuntimeError
|
|
84
|
-
if value < 1 or value > PEEK_MAX_LINES:
|
|
85
|
-
raise RuntimeError(f"{flag} must be between 1 and {PEEK_MAX_LINES}")
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def search_lines(lines: list[str], needle: str, context: int) -> list[dict[str, Any]]:
|
|
89
|
-
needle_lower = needle.lower()
|
|
90
|
-
matches: list[dict[str, Any]] = []
|
|
91
|
-
used_ranges: list[tuple[int, int]] = []
|
|
92
|
-
for index, line in enumerate(lines):
|
|
93
|
-
if needle_lower not in line.lower():
|
|
94
|
-
continue
|
|
95
|
-
start = max(0, index - context)
|
|
96
|
-
end = min(len(lines), index + context + 1)
|
|
97
|
-
if used_ranges and start <= used_ranges[-1][1]:
|
|
98
|
-
previous = matches[-1]
|
|
99
|
-
previous["lines"] = lines[previous["start_line"] - 1 : end]
|
|
100
|
-
previous["end_line"] = end
|
|
101
|
-
used_ranges[-1] = (previous["start_line"] - 1, end)
|
|
102
|
-
else:
|
|
103
|
-
matches.append({"line": index + 1, "start_line": start + 1, "end_line": end, "lines": lines[start:end]})
|
|
104
|
-
used_ranges.append((start, end))
|
|
105
|
-
if len(matches) >= PEEK_MAX_MATCHES:
|
|
106
|
-
break
|
|
107
|
-
return matches
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def format_search_matches(matches: list[dict[str, Any]]) -> str:
|
|
111
|
-
if not matches:
|
|
112
|
-
return "no matches"
|
|
113
|
-
blocks: list[str] = []
|
|
114
|
-
for match in matches:
|
|
115
|
-
blocks.append(f"match line {match['line']} ({match['start_line']}-{match['end_line']}):")
|
|
116
|
-
blocks.extend(str(line) for line in match["lines"])
|
|
117
|
-
return "\n".join(blocks)
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from team_agent.events import EventLog
|
|
9
|
-
from team_agent.message_store import MessageStore
|
|
10
|
-
from team_agent.state import load_runtime_state, save_runtime_state
|
|
11
|
-
from team_agent.status.compact import compact_status
|
|
12
|
-
from team_agent.status.constants import PENDING_DELIVERY_STATUSES
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def _interacted_marker(first_send_at: Any) -> str:
|
|
16
|
-
"""C3 (cr verdict, 2026-05-27): render the persisted first_send_at as a
|
|
17
|
-
user-visible status field. Valid ISO 8601 UTC strings pass through; any
|
|
18
|
-
other shape (None, empty string, 0, False, corrupt garbage) renders as
|
|
19
|
-
the literal "never" so the operator sees a consistent classification
|
|
20
|
-
instead of leaking raw garbage into status output. Restart enforces
|
|
21
|
-
strict typing separately (corrupt values fail the operation); status is
|
|
22
|
-
a read-only surface and tolerantly degrades to "never" rather than
|
|
23
|
-
failing the status command."""
|
|
24
|
-
if isinstance(first_send_at, str) and first_send_at:
|
|
25
|
-
try:
|
|
26
|
-
datetime.fromisoformat(first_send_at)
|
|
27
|
-
except (ValueError, TypeError):
|
|
28
|
-
return "never"
|
|
29
|
-
return first_send_at
|
|
30
|
-
return "never"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def status(workspace: Path, as_json: bool = False, *, compact: bool = False) -> dict[str, Any]:
|
|
34
|
-
from team_agent.runtime import (
|
|
35
|
-
_capture_missing_sessions,
|
|
36
|
-
_handle_provider_startup_prompts,
|
|
37
|
-
_refresh_agent_runtime_statuses,
|
|
38
|
-
_sync_agent_health,
|
|
39
|
-
_tmux_session_exists,
|
|
40
|
-
coordinator_health,
|
|
41
|
-
)
|
|
42
|
-
_ = as_json
|
|
43
|
-
state = load_runtime_state(workspace)
|
|
44
|
-
store = MessageStore(workspace)
|
|
45
|
-
event_log = EventLog(workspace)
|
|
46
|
-
_capture_missing_sessions(workspace, state, event_log, timeout_s=0.0, log_miss=False)
|
|
47
|
-
_refresh_agent_runtime_statuses(workspace, state, event_log)
|
|
48
|
-
_handle_provider_startup_prompts(workspace, state, event_log)
|
|
49
|
-
_sync_agent_health(workspace, state, store)
|
|
50
|
-
save_runtime_state(workspace, state)
|
|
51
|
-
session_name = state.get("session_name")
|
|
52
|
-
tmux_exists = _tmux_session_exists(session_name) if session_name else False
|
|
53
|
-
# C3 (cr verdict): enrich each worker entry with an explicit `interacted`
|
|
54
|
-
# field derived from the persisted first_send_at. The original entry
|
|
55
|
-
# passes through unchanged so any pre-existing field (including raw
|
|
56
|
-
# first_send_at) stays visible.
|
|
57
|
-
enriched_agents: dict[str, Any] = {}
|
|
58
|
-
for aid, raw in (state.get("agents") or {}).items():
|
|
59
|
-
if isinstance(raw, dict):
|
|
60
|
-
entry = dict(raw)
|
|
61
|
-
entry["interacted"] = _interacted_marker(raw.get("first_send_at"))
|
|
62
|
-
else:
|
|
63
|
-
entry = raw
|
|
64
|
-
enriched_agents[aid] = entry
|
|
65
|
-
result = {
|
|
66
|
-
"team": state.get("leader", {}).get("id", "leader"),
|
|
67
|
-
"session_name": session_name,
|
|
68
|
-
"tmux_session_present": tmux_exists,
|
|
69
|
-
"leader_receiver": state.get("leader_receiver", {}),
|
|
70
|
-
"agents": enriched_agents,
|
|
71
|
-
"agent_health": store.agent_health(),
|
|
72
|
-
"tasks": state.get("tasks", []),
|
|
73
|
-
"messages": store.message_counts(),
|
|
74
|
-
"queued_messages": queued_message_statuses(store.messages()),
|
|
75
|
-
"results": store.result_counts(),
|
|
76
|
-
"latest_results": latest_result_summaries(store),
|
|
77
|
-
"coordinator": coordinator_health(workspace),
|
|
78
|
-
"last_events": EventLog(workspace).tail(10),
|
|
79
|
-
}
|
|
80
|
-
return compact_status(result) if compact else result
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def latest_result_summaries(store: MessageStore, limit: int = 5) -> list[dict[str, Any]]:
|
|
84
|
-
summaries: list[dict[str, Any]] = []
|
|
85
|
-
for row in store.latest_results(limit=limit):
|
|
86
|
-
summary = result_summary_from_row(row)
|
|
87
|
-
if summary:
|
|
88
|
-
summaries.append(summary)
|
|
89
|
-
return summaries
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def result_summary_from_row(row: dict[str, Any]) -> dict[str, Any] | None:
|
|
93
|
-
try:
|
|
94
|
-
envelope = json.loads(row["envelope"]) if isinstance(row.get("envelope"), str) else row.get("envelope")
|
|
95
|
-
except (TypeError, json.JSONDecodeError):
|
|
96
|
-
return None
|
|
97
|
-
if not isinstance(envelope, dict):
|
|
98
|
-
return None
|
|
99
|
-
return {
|
|
100
|
-
"result_id": row.get("result_id"),
|
|
101
|
-
"task_id": envelope.get("task_id") or row.get("task_id"),
|
|
102
|
-
"agent_id": envelope.get("agent_id") or row.get("agent_id"),
|
|
103
|
-
"status": envelope.get("status") or row.get("status"),
|
|
104
|
-
"summary": envelope.get("summary"),
|
|
105
|
-
"created_at": row.get("created_at"),
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def queued_message_statuses(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
110
|
-
from team_agent.runtime import _age_text
|
|
111
|
-
visible_statuses = PENDING_DELIVERY_STATUSES | {"target_resolved", "delivery_blocked", "injected_unverified"}
|
|
112
|
-
queued: list[dict[str, Any]] = []
|
|
113
|
-
for row in messages:
|
|
114
|
-
if row.get("status") not in visible_statuses:
|
|
115
|
-
continue
|
|
116
|
-
queued.append(
|
|
117
|
-
{
|
|
118
|
-
"message_id": row.get("message_id"),
|
|
119
|
-
"recipient": row.get("recipient"),
|
|
120
|
-
"sender": row.get("sender"),
|
|
121
|
-
"status": row.get("status"),
|
|
122
|
-
"reason": row.get("error"),
|
|
123
|
-
"age": _age_text(row.get("created_at")),
|
|
124
|
-
"attempts": row.get("delivery_attempts") or 0,
|
|
125
|
-
}
|
|
126
|
-
)
|
|
127
|
-
return queued
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def format_status(workspace: Path, agent_id: str | None = None) -> str:
|
|
131
|
-
from team_agent.runtime import RuntimeError, _agent_health_status, _age_text, _current_task_for_agent
|
|
132
|
-
data = status(workspace, as_json=True)
|
|
133
|
-
health = data.get("agent_health", {})
|
|
134
|
-
tasks = data.get("tasks", [])
|
|
135
|
-
if agent_id:
|
|
136
|
-
if agent_id not in data.get("agents", {}) and agent_id not in health:
|
|
137
|
-
raise RuntimeError(f"unknown agent id: {agent_id}")
|
|
138
|
-
agent = data.get("agents", {}).get(agent_id, {})
|
|
139
|
-
row = health.get(agent_id, {})
|
|
140
|
-
task_id = _current_task_for_agent(tasks, agent_id) or "-"
|
|
141
|
-
inbox_rows = MessageStore(workspace).inbox(agent_id, limit=3)
|
|
142
|
-
lines = [
|
|
143
|
-
f"{agent_id} {row.get('status', _agent_health_status(agent))}",
|
|
144
|
-
f" provider: {agent.get('provider', '-')}",
|
|
145
|
-
f" model: {agent.get('model', '-')}",
|
|
146
|
-
f" profile: {agent.get('profile', '-')}",
|
|
147
|
-
f" session_id: {agent.get('session_id') or '-'}",
|
|
148
|
-
f" captured_via: {agent.get('captured_via') or '-'}",
|
|
149
|
-
f" attribution_confidence: {agent.get('attribution_confidence') or '-'}",
|
|
150
|
-
f" task: {task_id}",
|
|
151
|
-
f" handoff: {agent.get('handoff_path', '-')}",
|
|
152
|
-
" recent messages:",
|
|
153
|
-
]
|
|
154
|
-
if inbox_rows:
|
|
155
|
-
for item in inbox_rows:
|
|
156
|
-
lines.append(
|
|
157
|
-
f" {item['created_at']} {item['sender']} -> {item['recipient']} "
|
|
158
|
-
f"{item['status']}: {item['content'][:120]}"
|
|
159
|
-
)
|
|
160
|
-
else:
|
|
161
|
-
lines.append(" none")
|
|
162
|
-
return "\n".join(lines)
|
|
163
|
-
|
|
164
|
-
agents = data.get("agents", {})
|
|
165
|
-
state_name = "up" if data.get("tmux_session_present") else "down"
|
|
166
|
-
results = data.get("results", {})
|
|
167
|
-
lines = [
|
|
168
|
-
f"team {data.get('session_name') or '-'} ({state_name})",
|
|
169
|
-
(
|
|
170
|
-
"results "
|
|
171
|
-
f"total {results.get('total', 0)} "
|
|
172
|
-
f"uncollected {results.get('uncollected', 0)} "
|
|
173
|
-
f"collected {results.get('collected', 0)} "
|
|
174
|
-
f"invalid {results.get('invalid', 0)}"
|
|
175
|
-
),
|
|
176
|
-
]
|
|
177
|
-
if results.get("uncollected", 0):
|
|
178
|
-
lines.append(" final result pending in result store; run team-agent collect")
|
|
179
|
-
queued_messages = data.get("queued_messages") or []
|
|
180
|
-
if queued_messages:
|
|
181
|
-
lines.append("queued messages")
|
|
182
|
-
for item in queued_messages[:8]:
|
|
183
|
-
reason = item.get("reason") or "-"
|
|
184
|
-
lines.append(
|
|
185
|
-
f" {item.get('message_id')} -> {item.get('recipient')} "
|
|
186
|
-
f"{item.get('status')} age {item.get('age')} attempts {item.get('attempts')} reason {reason}"
|
|
187
|
-
)
|
|
188
|
-
for aid in sorted(agents):
|
|
189
|
-
agent = agents[aid]
|
|
190
|
-
row = health.get(aid, {})
|
|
191
|
-
status_value = row.get("status") or _agent_health_status(agent)
|
|
192
|
-
task_id = _current_task_for_agent(tasks, aid) or "-"
|
|
193
|
-
context = row.get("context_usage_pct")
|
|
194
|
-
context_text = f"ctx {context}%" if context is not None else "ctx -"
|
|
195
|
-
last = _age_text(row.get("last_output_at"))
|
|
196
|
-
session_text = f"sid {agent.get('session_id') or '-'}"
|
|
197
|
-
capture_text = f"via {agent.get('captured_via') or '-'} {agent.get('attribution_confidence') or '-'}"
|
|
198
|
-
lines.append(f" {aid} {status_value} {task_id} {context_text} last {last} {session_text} {capture_text}")
|
|
199
|
-
return "\n".join(lines)
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
TASK_STATUSES = {
|
|
6
|
-
"pending",
|
|
7
|
-
"ready",
|
|
8
|
-
"running",
|
|
9
|
-
"blocked",
|
|
10
|
-
"needs_retry",
|
|
11
|
-
"done",
|
|
12
|
-
"failed",
|
|
13
|
-
"cancelled",
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
TERMINAL_TASK_STATUSES = {"done", "failed", "cancelled"}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def find_dependency_cycle(tasks: list[dict[str, Any]]) -> list[str]:
|
|
20
|
-
graph = {t.get("id"): list(t.get("deps", [])) for t in tasks if t.get("id")}
|
|
21
|
-
visiting: set[str] = set()
|
|
22
|
-
visited: set[str] = set()
|
|
23
|
-
stack: list[str] = []
|
|
24
|
-
|
|
25
|
-
def visit(node: str) -> list[str] | None:
|
|
26
|
-
if node in visited:
|
|
27
|
-
return None
|
|
28
|
-
if node in visiting:
|
|
29
|
-
if node in stack:
|
|
30
|
-
return stack[stack.index(node) :] + [node]
|
|
31
|
-
return [node, node]
|
|
32
|
-
visiting.add(node)
|
|
33
|
-
stack.append(node)
|
|
34
|
-
for dep in graph.get(node, []):
|
|
35
|
-
if dep in graph:
|
|
36
|
-
cycle = visit(dep)
|
|
37
|
-
if cycle:
|
|
38
|
-
return cycle
|
|
39
|
-
stack.pop()
|
|
40
|
-
visiting.remove(node)
|
|
41
|
-
visited.add(node)
|
|
42
|
-
return None
|
|
43
|
-
|
|
44
|
-
for node in graph:
|
|
45
|
-
cycle = visit(node)
|
|
46
|
-
if cycle:
|
|
47
|
-
return cycle
|
|
48
|
-
return []
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def ready_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
52
|
-
by_id = {t["id"]: t for t in tasks}
|
|
53
|
-
ready: list[dict[str, Any]] = []
|
|
54
|
-
for task in tasks:
|
|
55
|
-
if task.get("status", "pending") not in {"pending", "ready", "needs_retry"}:
|
|
56
|
-
continue
|
|
57
|
-
deps_done = all(by_id.get(dep, {}).get("status") == "done" for dep in task.get("deps", []))
|
|
58
|
-
if deps_done:
|
|
59
|
-
ready.append(task)
|
|
60
|
-
return ready
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def update_task_status(
|
|
64
|
-
tasks: list[dict[str, Any]],
|
|
65
|
-
task_id: str,
|
|
66
|
-
status: str,
|
|
67
|
-
summary: str | None = None,
|
|
68
|
-
artifact_refs: list[dict[str, Any]] | None = None,
|
|
69
|
-
) -> None:
|
|
70
|
-
if status not in TASK_STATUSES:
|
|
71
|
-
raise ValueError(f"Unknown task status: {status}")
|
|
72
|
-
for task in tasks:
|
|
73
|
-
if task.get("id") == task_id:
|
|
74
|
-
task["status"] = status
|
|
75
|
-
if summary is not None:
|
|
76
|
-
task["last_result_summary"] = summary
|
|
77
|
-
if artifact_refs is not None:
|
|
78
|
-
task["artifact_refs"] = artifact_refs
|
|
79
|
-
return
|
|
80
|
-
raise KeyError(f"Unknown task id: {task_id}")
|