@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,216 +0,0 @@
|
|
|
1
|
-
"""Gap 28 (Slice 2 Stage 2): observe-only detection of leader-pane API errors.
|
|
2
|
-
|
|
3
|
-
The coordinator tick captures the leader pane scrollback once per cycle, scans it for
|
|
4
|
-
known upstream-API error patterns (Claude/Codex CLI errors that occur mid-turn), and
|
|
5
|
-
emits a structured `leader.api_error` audit event. The intent is observability — auto-
|
|
6
|
-
retry belongs to the upstream CLI; this module never touches the pane.
|
|
7
|
-
|
|
8
|
-
Event schema (logged via EventLog.write):
|
|
9
|
-
|
|
10
|
-
event: 'leader.api_error'
|
|
11
|
-
ts: ISO-8601 UTC (added by EventLog)
|
|
12
|
-
leader_session_uuid: str | None
|
|
13
|
-
error_class: 'Overloaded' | 'RateLimit' | 'Timeout' |
|
|
14
|
-
'NetworkError' | 'Unknown'
|
|
15
|
-
provider: 'claude' | 'codex' | 'claude_code' | str | None
|
|
16
|
-
partial_response_streamed: bool (heuristic: assistant text before the error)
|
|
17
|
-
worker_dispatch_just_before: list[str] (leader→worker msg_ids in the prior 60s)
|
|
18
|
-
retry_count: int (always 0 — the framework does not retry today)
|
|
19
|
-
matched_pattern_snippet: str (the captured error line, ≤160 chars)
|
|
20
|
-
|
|
21
|
-
Detection dedupes within the coordinator state via a (error_class, snippet-tail)
|
|
22
|
-
fingerprint stored under `state['coordinator']['last_api_error_fingerprint']`. A
|
|
23
|
-
clean tick (no error pattern present) clears the fingerprint so the next genuine
|
|
24
|
-
error re-emits. This keeps event volume bounded while still catching distinct
|
|
25
|
-
errors as they occur.
|
|
26
|
-
"""
|
|
27
|
-
from __future__ import annotations
|
|
28
|
-
|
|
29
|
-
import re
|
|
30
|
-
from datetime import datetime, timedelta, timezone
|
|
31
|
-
from pathlib import Path
|
|
32
|
-
from typing import Any, Callable
|
|
33
|
-
|
|
34
|
-
from team_agent.events import EventLog
|
|
35
|
-
from team_agent.message_store import MessageStore
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# Spark MEDIUM sweeps (2026-05-26):
|
|
39
|
-
# (#3) Require an API/provider context marker near the error keyword. Bare '503' /
|
|
40
|
-
# 'fetch failed' / 'timed out' in user text used to false-fire.
|
|
41
|
-
# (#7) Match across short sliding windows of 1-3 adjacent lines so wrapped tmux
|
|
42
|
-
# output ("claude:\n request timed out") still resolves to a single
|
|
43
|
-
# detection. Window joined with a single space; capped at _WINDOW_MAX_CHARS
|
|
44
|
-
# so the scan stays bounded.
|
|
45
|
-
_API_CONTEXT = (
|
|
46
|
-
r"(?:API\s+Error|HTTP\s*Error|HTTPError|request\s+failed|"
|
|
47
|
-
r"codex|claude|Anthropic|OpenAI|TypeError)"
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
# Patterns operate against a sliding window of up to 3 joined lines. The window
|
|
51
|
-
# never contains '\n' (lines are joined with a single space), so `[^\n]` and `.`
|
|
52
|
-
# behave the same; we use `[^\n]` for self-documentation.
|
|
53
|
-
_ERROR_PATTERNS: list[tuple[re.Pattern[str], str]] = [
|
|
54
|
-
# Overloaded — keyword itself already includes the "API Error:" prefix.
|
|
55
|
-
(re.compile(r"API\s+Error:\s*Overloaded", re.IGNORECASE), "Overloaded"),
|
|
56
|
-
# RateLimit — 429 with "Too Many Requests" is sufficiently specific; require it
|
|
57
|
-
# appear AFTER an API context marker OR before "Too Many Requests" tightly.
|
|
58
|
-
(re.compile(rf"(?:{_API_CONTEXT}[^\n]*\b429\b|\b429\s+Too\s+Many\s+Requests)", re.IGNORECASE), "RateLimit"),
|
|
59
|
-
# 5xx — must share a window with an API-context marker on either side.
|
|
60
|
-
(re.compile(rf"{_API_CONTEXT}[^\n]{{0,200}}\b5(?:00|02|03|04)\b", re.IGNORECASE), "NetworkError"),
|
|
61
|
-
(re.compile(rf"\b5(?:00|02|03|04)\b[^\n]{{0,200}}{_API_CONTEXT}", re.IGNORECASE), "NetworkError"),
|
|
62
|
-
# fetch failed — needs an API-context marker in the same window. The TypeError
|
|
63
|
-
# marker on its own counts (Node fetch frames the error this way).
|
|
64
|
-
(re.compile(rf"{_API_CONTEXT}[^\n]{{0,200}}fetch\s+failed", re.IGNORECASE), "NetworkError"),
|
|
65
|
-
(re.compile(rf"fetch\s+failed[^\n]{{0,200}}{_API_CONTEXT}", re.IGNORECASE), "NetworkError"),
|
|
66
|
-
# Timeout — likewise requires an API-context marker in the window, except for
|
|
67
|
-
# the unambiguous syscall token ETIMEDOUT.
|
|
68
|
-
(re.compile(rf"{_API_CONTEXT}[^\n]{{0,200}}(?:request|connection)\s+(?:timed\s+out|timeout)", re.IGNORECASE), "Timeout"),
|
|
69
|
-
(re.compile(rf"(?:request|connection)\s+(?:timed\s+out|timeout)[^\n]{{0,200}}{_API_CONTEXT}", re.IGNORECASE), "Timeout"),
|
|
70
|
-
(re.compile(r"\bETIMEDOUT\b", re.IGNORECASE), "Timeout"),
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
_RECENT_LINE_WINDOW = 100 # scan only the most recent N lines
|
|
74
|
-
_SLIDING_WINDOW_LINES = 3 # join up to 3 adjacent lines per scan window
|
|
75
|
-
_WINDOW_MAX_CHARS = 400 # discard windows beyond this length to bound work
|
|
76
|
-
_DISPATCH_WINDOW_SECONDS = 60 # leader→worker sends counted within this lookback
|
|
77
|
-
_PARTIAL_RESPONSE_HEAD_BYTES = 4000
|
|
78
|
-
|
|
79
|
-
_PARTIAL_RESPONSE_HINT = re.compile(
|
|
80
|
-
r"(?:^|\n)\s*(?:Assistant|⏺|●|> |I'll |I will |I'm |I am |Let me )",
|
|
81
|
-
re.IGNORECASE,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def detect_leader_api_errors(
|
|
86
|
-
workspace: Path,
|
|
87
|
-
state: dict[str, Any],
|
|
88
|
-
store: MessageStore,
|
|
89
|
-
event_log: EventLog,
|
|
90
|
-
*,
|
|
91
|
-
capture_fn: Callable[[str], dict[str, Any]] | None = None,
|
|
92
|
-
now_fn: Callable[[], datetime] | None = None,
|
|
93
|
-
) -> list[dict[str, Any]]:
|
|
94
|
-
"""Coordinator-tick entry point. Returns a list of emitted events (0 or 1)."""
|
|
95
|
-
receiver = state.get("leader_receiver") or {}
|
|
96
|
-
pane = receiver.get("pane_id") if receiver.get("mode") == "direct_tmux" else None
|
|
97
|
-
if not pane:
|
|
98
|
-
return []
|
|
99
|
-
capture_fn = capture_fn or _default_capture_fn()
|
|
100
|
-
capture = capture_fn(str(pane))
|
|
101
|
-
if not capture.get("ok"):
|
|
102
|
-
return []
|
|
103
|
-
scrollback = str(capture.get("capture") or "")
|
|
104
|
-
coordinator_state = state.setdefault("coordinator", {})
|
|
105
|
-
found = _match_first_error(scrollback)
|
|
106
|
-
if not found:
|
|
107
|
-
if coordinator_state.get("last_api_error_fingerprint"):
|
|
108
|
-
coordinator_state["last_api_error_fingerprint"] = None
|
|
109
|
-
return []
|
|
110
|
-
error_class, snippet = found
|
|
111
|
-
fingerprint = f"{error_class}::{snippet[-120:]}"
|
|
112
|
-
if coordinator_state.get("last_api_error_fingerprint") == fingerprint:
|
|
113
|
-
return []
|
|
114
|
-
coordinator_state["last_api_error_fingerprint"] = fingerprint
|
|
115
|
-
now = (now_fn() if now_fn else datetime.now(timezone.utc))
|
|
116
|
-
cutoff_iso = (now - timedelta(seconds=_DISPATCH_WINDOW_SECONDS)).isoformat()
|
|
117
|
-
leader_uuid = (
|
|
118
|
-
str((state.get("team_owner") or {}).get("leader_session_uuid") or "")
|
|
119
|
-
or str(receiver.get("leader_session_uuid") or "")
|
|
120
|
-
or None
|
|
121
|
-
)
|
|
122
|
-
provider = str(receiver.get("provider") or "") or None
|
|
123
|
-
event = event_log.write(
|
|
124
|
-
"leader.api_error",
|
|
125
|
-
leader_session_uuid=leader_uuid,
|
|
126
|
-
error_class=error_class,
|
|
127
|
-
provider=provider,
|
|
128
|
-
partial_response_streamed=_scrollback_has_partial_response(scrollback, snippet),
|
|
129
|
-
worker_dispatch_just_before=_recent_leader_dispatches(store, cutoff_iso),
|
|
130
|
-
retry_count=0,
|
|
131
|
-
matched_pattern_snippet=snippet[:160],
|
|
132
|
-
)
|
|
133
|
-
return [event]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _default_capture_fn() -> Callable[[str], dict[str, Any]]:
|
|
137
|
-
from team_agent.messaging.deps import _capture_tmux_pane_text
|
|
138
|
-
return _capture_tmux_pane_text
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def _match_first_error(scrollback: str) -> tuple[str, str] | None:
|
|
142
|
-
"""Spark MEDIUM #7: sliding window of 1..N adjacent lines. Lines inside a
|
|
143
|
-
window are joined with a single space so a wrapped pair such as
|
|
144
|
-
claude:
|
|
145
|
-
request timed out
|
|
146
|
-
is detected as one event without permitting unbounded cross-line matches.
|
|
147
|
-
Latest window wins so the freshest error is reported."""
|
|
148
|
-
if not scrollback:
|
|
149
|
-
return None
|
|
150
|
-
lines = [line.strip() for line in scrollback.splitlines()[-_RECENT_LINE_WINDOW:]]
|
|
151
|
-
if not lines:
|
|
152
|
-
return None
|
|
153
|
-
best: tuple[int, str, str] | None = None
|
|
154
|
-
for start in range(len(lines)):
|
|
155
|
-
for size in range(1, _SLIDING_WINDOW_LINES + 1):
|
|
156
|
-
end = start + size
|
|
157
|
-
if end > len(lines):
|
|
158
|
-
break
|
|
159
|
-
window = " ".join(line for line in lines[start:end] if line)
|
|
160
|
-
if not window:
|
|
161
|
-
continue
|
|
162
|
-
# Spark MEDIUM sweep #3 (2026-05-26): tail-preserve instead of
|
|
163
|
-
# dropping the window wholesale. Errors land at the END of verbose
|
|
164
|
-
# diagnostics (stack traces, retry chatter, etc.). If we discarded
|
|
165
|
-
# any window over the cap we silently lost recall on long wrapped
|
|
166
|
-
# output. Scanning the LAST _WINDOW_MAX_CHARS still bounds regex
|
|
167
|
-
# cost while keeping the freshest context — the bit most likely to
|
|
168
|
-
# contain the actual provider error keyword.
|
|
169
|
-
if len(window) > _WINDOW_MAX_CHARS:
|
|
170
|
-
window = window[-_WINDOW_MAX_CHARS:]
|
|
171
|
-
for pattern, error_class in _ERROR_PATTERNS:
|
|
172
|
-
match = pattern.search(window)
|
|
173
|
-
if not match:
|
|
174
|
-
continue
|
|
175
|
-
snippet = window[:240]
|
|
176
|
-
if best is None or start > best[0]:
|
|
177
|
-
best = (start, error_class, snippet)
|
|
178
|
-
# First match per window is enough; later windows may override.
|
|
179
|
-
break
|
|
180
|
-
if best is None:
|
|
181
|
-
return None
|
|
182
|
-
return best[1], best[2]
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
def _scrollback_has_partial_response(scrollback: str, error_snippet: str) -> bool:
|
|
186
|
-
idx = scrollback.rfind(error_snippet)
|
|
187
|
-
if idx == -1:
|
|
188
|
-
return False
|
|
189
|
-
head = scrollback[max(0, idx - _PARTIAL_RESPONSE_HEAD_BYTES): idx]
|
|
190
|
-
return bool(_PARTIAL_RESPONSE_HINT.search(head))
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def _recent_leader_dispatches(store: MessageStore, cutoff_iso: str) -> list[str]:
|
|
194
|
-
out: list[str] = []
|
|
195
|
-
try:
|
|
196
|
-
rows = store.messages()
|
|
197
|
-
except Exception:
|
|
198
|
-
return out
|
|
199
|
-
for row in rows:
|
|
200
|
-
sender = str(row.get("sender") or "")
|
|
201
|
-
if sender not in {"leader", "Leader"} and not _looks_like_leader_sender(sender):
|
|
202
|
-
continue
|
|
203
|
-
created = str(row.get("created_at") or "")
|
|
204
|
-
if not created or created < cutoff_iso:
|
|
205
|
-
continue
|
|
206
|
-
msg_id = str(row.get("message_id") or "")
|
|
207
|
-
if msg_id:
|
|
208
|
-
out.append(msg_id)
|
|
209
|
-
return out
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def _looks_like_leader_sender(sender: str) -> bool:
|
|
213
|
-
return sender.startswith("leader") or sender.lower() == "leader"
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
__all__ = ["detect_leader_api_errors"]
|