@team-agent/installer 0.2.11 → 0.3.1
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 +1204 -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 +1207 -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 +557 -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 +1084 -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 +526 -0
- package/crates/team-agent/src/leader/rediscover.rs +1101 -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 +237 -0
- package/crates/team-agent/src/leader/tests/identity.rs +206 -0
- package/crates/team-agent/src/leader/tests/idle.rs +272 -0
- package/crates/team-agent/src/leader/tests/lease_api.rs +225 -0
- package/crates/team-agent/src/leader/tests/lease_claim.rs +410 -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 +489 -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 +2109 -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 +985 -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 +710 -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 +187 -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 +468 -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 +743 -0
- package/crates/team-agent/src/messaging/helpers.rs +209 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +329 -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 +553 -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 +578 -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 +659 -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 +765 -0
- package/crates/team-agent/src/tmux_backend.rs +810 -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 +118 -112
- 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,262 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
import re
|
|
5
|
-
from typing import Any
|
|
6
|
-
|
|
7
|
-
from team_agent.events import EventLog
|
|
8
|
-
from team_agent.messaging.deps import datetime, save_runtime_state, team_state_key, timezone
|
|
9
|
-
|
|
10
|
-
_COMPACTION_RESET_THRESHOLD_DEFAULT = 3
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _compaction_reset_threshold(state: dict[str, Any]) -> int:
|
|
14
|
-
from team_agent.messaging.deps import load_spec
|
|
15
|
-
spec_path = state.get("spec_path")
|
|
16
|
-
if spec_path:
|
|
17
|
-
try:
|
|
18
|
-
spec = load_spec(spec_path)
|
|
19
|
-
except Exception:
|
|
20
|
-
spec = {}
|
|
21
|
-
runtime_cfg = spec.get("runtime", {}) if isinstance(spec, dict) else {}
|
|
22
|
-
raw = runtime_cfg.get("compaction_reset_threshold")
|
|
23
|
-
if raw is not None:
|
|
24
|
-
try:
|
|
25
|
-
value = int(raw)
|
|
26
|
-
if value > 0:
|
|
27
|
-
return value
|
|
28
|
-
except (TypeError, ValueError):
|
|
29
|
-
pass
|
|
30
|
-
return _COMPACTION_RESET_THRESHOLD_DEFAULT
|
|
31
|
-
_PROVIDER_COMMANDS = {"claude", "claude-code", "codex", "node", "node.exe", "claude.exe"}
|
|
32
|
-
_COMPACTION_PATTERNS = (
|
|
33
|
-
re.compile(r"context compacted", re.IGNORECASE),
|
|
34
|
-
re.compile(r"compaction occurred", re.IGNORECASE),
|
|
35
|
-
)
|
|
36
|
-
_IDLE_PROMPT_PATTERNS = (
|
|
37
|
-
re.compile(r"›\s*Find and fix a bug in @filename"),
|
|
38
|
-
re.compile(r"─\s*for agents"),
|
|
39
|
-
re.compile(r"^›[^\n]*\n(?:\s*\n){0,8}\s*gpt-[\w.-]+\s+\S+\s+·", re.MULTILINE),
|
|
40
|
-
# Codex idle input prompt line (rotating hints like
|
|
41
|
-
# "› Use /skills to list available skills"). Working lines start with a
|
|
42
|
-
# spinner/✱ glyph, not "›". An optional leading "│ " tolerates a boxed
|
|
43
|
-
# input frame.
|
|
44
|
-
re.compile(r"^(?:│\s*)?›\s", re.MULTILINE),
|
|
45
|
-
# Claude Code idle input prompt: an empty "❯" line (the box may render the
|
|
46
|
-
# trailing space as U+00A0). Only the empty prompt is idle; a "❯ <command>"
|
|
47
|
-
# line is a submitted turn, so the trailing-content form is deliberately
|
|
48
|
-
# excluded to avoid false IDLE while Claude is still working.
|
|
49
|
-
re.compile(r"^(?:│\s*)?❯[ \t\xa0]*$", re.MULTILINE),
|
|
50
|
-
)
|
|
51
|
-
# Substantive working indicators carry their own text ("Working", "Thinking",
|
|
52
|
-
# "esc to interrupt", ...). The bare spinner glyph alone is only a pane-refresh
|
|
53
|
-
# artifact, so it is kept separate: it still counts as working when nothing else
|
|
54
|
-
# is present, but it must not override a fresh idle prompt (C14).
|
|
55
|
-
_SUBSTANTIVE_WORKING_PATTERNS = (
|
|
56
|
-
re.compile(r"\bWorking(?:\s*\((?P<working_seconds>\d+)s\))?", re.IGNORECASE),
|
|
57
|
-
re.compile(r"\bReticulating\b", re.IGNORECASE),
|
|
58
|
-
re.compile(r"\bBaked for (?P<baked_seconds>\d+)s\b", re.IGNORECASE),
|
|
59
|
-
re.compile(r"\bThinking\b", re.IGNORECASE),
|
|
60
|
-
re.compile(r"esc to interrupt", re.IGNORECASE),
|
|
61
|
-
)
|
|
62
|
-
_SPINNER_GLYPH_PATTERN = re.compile(r"[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]")
|
|
63
|
-
_WORKING_PATTERNS = _SUBSTANTIVE_WORKING_PATTERNS + (_SPINNER_GLYPH_PATTERN,)
|
|
64
|
-
# A live provider working footer is a bullet status line carrying a live
|
|
65
|
-
# elapsed-time counter plus the "esc to interrupt" hint, e.g.
|
|
66
|
-
# "• Working (35s • esc to interrupt) · 1 background terminal running"
|
|
67
|
-
# "• Waiting for background terminal (1m 06s • esc to interrupt) · ..."
|
|
68
|
-
# This is matched by the COMMON shape, not per verb (Working/Waiting/Baked/...):
|
|
69
|
-
# a "•" line with a parenthesized elapsed counter in either "Ns" or "Nm NNs"
|
|
70
|
-
# form, followed by "esc to interrupt" inside the same parentheses. That live
|
|
71
|
-
# counter + interrupt hint is only rendered during an active interruptible turn
|
|
72
|
-
# and is removed when the turn ends, so it never appears in prose/scrollback
|
|
73
|
-
# history (unlike a bare "Working" word or an "esc to interrupt" mention). It is
|
|
74
|
-
# the positive "provider is working right now" signal that the permanent input
|
|
75
|
-
# box ("› ... gpt-" / "❯") rendered below it must not override.
|
|
76
|
-
_LIVE_WORKING_PATTERNS = (
|
|
77
|
-
re.compile(r"•\s*[^\n]*?\(\s*(?:\d+m\s*)?\d+s\b[^)\n]*esc to interrupt", re.IGNORECASE),
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def _latest_live_working_footer(scrollback: str) -> str | None:
|
|
82
|
-
best: tuple[int, str] | None = None
|
|
83
|
-
for pattern in _LIVE_WORKING_PATTERNS:
|
|
84
|
-
for match in pattern.finditer(scrollback):
|
|
85
|
-
if best is None or match.start() > best[0]:
|
|
86
|
-
best = (match.start(), match.group(0))
|
|
87
|
-
return best[1] if best else None
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def classify_agent_activity(
|
|
91
|
-
agent_id: str,
|
|
92
|
-
provider: str,
|
|
93
|
-
last_output_at: str | None,
|
|
94
|
-
pane: dict[str, Any] | None,
|
|
95
|
-
scrollback: str,
|
|
96
|
-
*,
|
|
97
|
-
now: datetime | None = None,
|
|
98
|
-
stuck_timeout_sec: int = 300,
|
|
99
|
-
active_task: bool = False,
|
|
100
|
-
pane_delta_recent: bool = False,
|
|
101
|
-
) -> dict[str, Any]:
|
|
102
|
-
_ = agent_id, provider
|
|
103
|
-
now = now or datetime.now(timezone.utc)
|
|
104
|
-
pane = pane or {}
|
|
105
|
-
pane_in_mode = str(pane.get("pane_in_mode") or "0")
|
|
106
|
-
if pane_in_mode != "0":
|
|
107
|
-
return {"status": "uncertain", "confidence": 0.9, "rationale": f"pane_in_mode={pane_in_mode}"}
|
|
108
|
-
command = str(pane.get("pane_current_command") or "").split("/")[-1]
|
|
109
|
-
if command and command not in _PROVIDER_COMMANDS:
|
|
110
|
-
return {"status": "uncertain", "confidence": 0.75, "rationale": f"unexpected pane current_command={command}"}
|
|
111
|
-
working = _latest_working_match(scrollback)
|
|
112
|
-
substantive = _latest_working_match(scrollback, _SUBSTANTIVE_WORKING_PATTERNS)
|
|
113
|
-
idle_pos = _latest_idle_prompt_position(scrollback)
|
|
114
|
-
# bug-071: a live provider working footer ("Working (Ns ...)") plus an active
|
|
115
|
-
# task is an active turn. The provider input box ("› ... gpt-" / "❯") is
|
|
116
|
-
# permanent UI rendered BELOW the footer, so the position-based idle-prompt
|
|
117
|
-
# check would otherwise flip a working Codex turn to IDLE. Checked before the
|
|
118
|
-
# idle-prompt rule. The seconds-counter form never appears in prose, so a
|
|
119
|
-
# real idle prompt (no live footer) is unaffected (C14); gating on
|
|
120
|
-
# active_task keeps task-less classifier cases on the existing logic.
|
|
121
|
-
live_footer = _latest_live_working_footer(scrollback)
|
|
122
|
-
if active_task and live_footer is not None:
|
|
123
|
-
return {"status": "working", "confidence": 0.9, "rationale": f"live working footer '{live_footer}' with active task"}
|
|
124
|
-
# C14: a fresh idle prompt is the strongest signal. Only a substantive
|
|
125
|
-
# working indicator positioned after the prompt counts as newer work; a
|
|
126
|
-
# trailing bare spinner glyph (pane refresh) or pane delta must not flip a
|
|
127
|
-
# fresh idle prompt to WORKING.
|
|
128
|
-
if idle_pos is not None and (substantive is None or idle_pos > substantive[0]):
|
|
129
|
-
return {"status": "idle", "confidence": 0.9, "rationale": "provider idle prompt is the latest scrollback signal"}
|
|
130
|
-
if working:
|
|
131
|
-
_pos, label, elapsed = working
|
|
132
|
-
if elapsed is not None and elapsed >= stuck_timeout_sec:
|
|
133
|
-
return {"status": "stuck", "confidence": 0.85, "rationale": f"stale {label} indicator for {elapsed}s"}
|
|
134
|
-
return {"status": "working", "confidence": 0.9, "rationale": f"{label} indicator is the latest scrollback signal"}
|
|
135
|
-
# C15: an active task whose pane changed since the last sync is real work,
|
|
136
|
-
# not idle. Placed after the idle-prompt check so a fresh idle prompt always
|
|
137
|
-
# wins; without an active task this rule never fires and raw running may stay
|
|
138
|
-
# IDLE.
|
|
139
|
-
if active_task and pane_delta_recent and (not command or command in _PROVIDER_COMMANDS):
|
|
140
|
-
return {"status": "working", "confidence": 0.9, "rationale": "active task with recent pane delta"}
|
|
141
|
-
age = _last_output_age_seconds(last_output_at, now)
|
|
142
|
-
if age is not None and age >= stuck_timeout_sec:
|
|
143
|
-
return {"status": "stuck", "confidence": 0.85, "rationale": "last_output_at exceeded timeout with no idle prompt"}
|
|
144
|
-
if age is not None and age <= 120 and (not command or command in _PROVIDER_COMMANDS):
|
|
145
|
-
return {"status": "working", "confidence": 0.7, "rationale": "recent output from provider command"}
|
|
146
|
-
return {"status": "uncertain", "confidence": 0.5, "rationale": "no decisive prompt or working signal"}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def _latest_idle_prompt_position(scrollback: str) -> int | None:
|
|
150
|
-
best: int | None = None
|
|
151
|
-
for pattern in _IDLE_PROMPT_PATTERNS:
|
|
152
|
-
for match in pattern.finditer(scrollback):
|
|
153
|
-
if best is None or match.start() > best:
|
|
154
|
-
best = match.start()
|
|
155
|
-
return best
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def detect_compaction_degradation(
|
|
159
|
-
workspace: Path,
|
|
160
|
-
state: dict[str, Any],
|
|
161
|
-
event_log: EventLog,
|
|
162
|
-
*,
|
|
163
|
-
agent_id: str,
|
|
164
|
-
provider: str,
|
|
165
|
-
scrollback: str,
|
|
166
|
-
stuck_loop: bool = False,
|
|
167
|
-
) -> dict[str, Any]:
|
|
168
|
-
count = _count_compaction_markers(scrollback)
|
|
169
|
-
owner_team_id = team_state_key(state)
|
|
170
|
-
team_counts = state.setdefault("coordinator", {}).setdefault("compaction_counts", {}).setdefault(owner_team_id, {})
|
|
171
|
-
current = max(int(team_counts.get(agent_id) or 0), count)
|
|
172
|
-
team_counts[agent_id] = current
|
|
173
|
-
try:
|
|
174
|
-
save_runtime_state(workspace, state)
|
|
175
|
-
except Exception as exc:
|
|
176
|
-
event_log.write("runtime.state.save_failed", phase="compaction_detect", error=str(exc), exc_type=type(exc).__name__)
|
|
177
|
-
return {"ok": False, "event": "compaction_threshold_crossed.unpersisted", "agent_id": agent_id, "compaction_count": current}
|
|
178
|
-
if current <= 0:
|
|
179
|
-
return {"ok": True, "event": "compaction_threshold_crossed.none", "compaction_count": current}
|
|
180
|
-
event_log.write(
|
|
181
|
-
"coordinator.compaction_observed",
|
|
182
|
-
agent_id=agent_id,
|
|
183
|
-
provider=provider,
|
|
184
|
-
team=owner_team_id,
|
|
185
|
-
compaction_count=current,
|
|
186
|
-
stuck_loop=stuck_loop,
|
|
187
|
-
)
|
|
188
|
-
if provider != "codex":
|
|
189
|
-
event = "compaction_threshold_crossed.ignored_lossless_provider"
|
|
190
|
-
event_log.write(event, agent_id=agent_id, provider=provider, team=owner_team_id, compaction_count=current)
|
|
191
|
-
return {"ok": True, "event": event, "agent_id": agent_id, "provider": provider, "compaction_count": current}
|
|
192
|
-
threshold = _compaction_reset_threshold(state)
|
|
193
|
-
if current < threshold and not (current >= 1 and stuck_loop):
|
|
194
|
-
return {"ok": True, "event": "compaction_threshold_crossed.below_threshold", "agent_id": agent_id, "compaction_count": current, "threshold": threshold}
|
|
195
|
-
return _reset_or_recommend(workspace, state, event_log, agent_id, provider, owner_team_id, current, threshold)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def _reset_or_recommend(
|
|
199
|
-
workspace: Path,
|
|
200
|
-
state: dict[str, Any],
|
|
201
|
-
event_log: EventLog,
|
|
202
|
-
agent_id: str,
|
|
203
|
-
provider: str,
|
|
204
|
-
owner_team_id: str,
|
|
205
|
-
compaction_count: int,
|
|
206
|
-
threshold: int,
|
|
207
|
-
) -> dict[str, Any]:
|
|
208
|
-
from team_agent.runtime import reset_agent
|
|
209
|
-
reset = reset_agent(workspace, agent_id, discard_session=True)
|
|
210
|
-
if reset.get("ok"):
|
|
211
|
-
team_counts = state.setdefault("coordinator", {}).setdefault("compaction_counts", {}).setdefault(owner_team_id, {})
|
|
212
|
-
team_counts[agent_id] = 0
|
|
213
|
-
try:
|
|
214
|
-
save_runtime_state(workspace, state)
|
|
215
|
-
except Exception as exc:
|
|
216
|
-
event_log.write("runtime.state.save_failed", phase="compaction_detect", error=str(exc), exc_type=type(exc).__name__)
|
|
217
|
-
return {"ok": False, "event": "compaction_threshold_crossed.unpersisted", "agent_id": agent_id, "compaction_count": compaction_count}
|
|
218
|
-
event = "compaction_threshold_crossed.auto_reset"
|
|
219
|
-
event_log.write(event, agent_id=agent_id, provider=provider, team=owner_team_id, compaction_count=compaction_count, threshold=threshold)
|
|
220
|
-
return {"ok": True, "event": event, "agent_id": agent_id, "compaction_count": compaction_count, "threshold": threshold, "reset": reset}
|
|
221
|
-
event = "compaction_threshold_crossed.recommend_reset"
|
|
222
|
-
message = f"agent {agent_id} crossed Codex compaction threshold; run team-agent reset-agent {agent_id} --discard-session"
|
|
223
|
-
event_log.write(
|
|
224
|
-
event,
|
|
225
|
-
agent_id=agent_id,
|
|
226
|
-
provider=provider,
|
|
227
|
-
team=owner_team_id,
|
|
228
|
-
compaction_count=compaction_count,
|
|
229
|
-
threshold=threshold,
|
|
230
|
-
leader_visible_message=message,
|
|
231
|
-
reset_error=reset.get("error") or reset.get("reason"),
|
|
232
|
-
)
|
|
233
|
-
return {"ok": True, "event": event, "agent_id": agent_id, "compaction_count": compaction_count, "threshold": threshold, "leader_visible_message": message, "reset": reset}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _latest_working_match(
|
|
237
|
-
scrollback: str, patterns: tuple[re.Pattern[str], ...] = _WORKING_PATTERNS
|
|
238
|
-
) -> tuple[int, str, int | None] | None:
|
|
239
|
-
best: tuple[int, str, int | None] | None = None
|
|
240
|
-
for pattern in patterns:
|
|
241
|
-
for match in pattern.finditer(scrollback):
|
|
242
|
-
elapsed_raw = match.groupdict().get("working_seconds") or match.groupdict().get("baked_seconds")
|
|
243
|
-
elapsed = int(elapsed_raw) if elapsed_raw else None
|
|
244
|
-
if best is None or match.start() > best[0]:
|
|
245
|
-
best = (match.start(), match.group(0), elapsed)
|
|
246
|
-
return best
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
def _last_output_age_seconds(last_output_at: str | None, now: datetime) -> float | None:
|
|
250
|
-
if not last_output_at:
|
|
251
|
-
return None
|
|
252
|
-
try:
|
|
253
|
-
last = datetime.fromisoformat(last_output_at)
|
|
254
|
-
except ValueError:
|
|
255
|
-
return None
|
|
256
|
-
if last.tzinfo is None:
|
|
257
|
-
last = last.replace(tzinfo=timezone.utc)
|
|
258
|
-
return max(0.0, (now - last).total_seconds())
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
def _count_compaction_markers(scrollback: str) -> int:
|
|
262
|
-
return sum(len(pattern.findall(scrollback)) for pattern in _COMPACTION_PATTERNS)
|