@team-agent/installer 0.5.61 → 0.5.63
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 +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +5 -2
- package/crates/team-agent/src/cli/attach_app_server_leader.rs +1 -0
- package/crates/team-agent/src/cli/diagnose.rs +1 -0
- package/crates/team-agent/src/cli/emit.rs +41 -7
- package/crates/team-agent/src/cli/helpers.rs +1 -0
- package/crates/team-agent/src/cli/leader.rs +6 -1
- package/crates/team-agent/src/cli/leaders.rs +2 -0
- package/crates/team-agent/src/cli/mod.rs +33 -3
- package/crates/team-agent/src/cli/named_address.rs +8 -1
- package/crates/team-agent/src/cli/profile.rs +2 -1
- package/crates/team-agent/src/cli/send/coordinator.rs +1 -0
- package/crates/team-agent/src/cli/send/mailbox.rs +3 -0
- package/crates/team-agent/src/cli/send/persist.rs +1 -0
- package/crates/team-agent/src/cli/send/presentation.rs +1 -0
- package/crates/team-agent/src/cli/send/resolve.rs +206 -1
- package/crates/team-agent/src/cli/send.rs +2 -1
- package/crates/team-agent/src/cli/spec.rs +1 -0
- package/crates/team-agent/src/cli/status.rs +2 -1
- package/crates/team-agent/src/cli/status_port/compact.rs +1 -0
- package/crates/team-agent/src/cli/status_port/format.rs +1 -0
- package/crates/team-agent/src/cli/status_port/inbox.rs +1 -0
- package/crates/team-agent/src/cli/tests/verb_profile.rs +38 -0
- package/crates/team-agent/src/cli/types.rs +3 -2
- package/crates/team-agent/src/communication_mode/mod.rs +1 -0
- package/crates/team-agent/src/compiler.rs +1 -0
- package/crates/team-agent/src/conpty/backend.rs +1 -0
- package/crates/team-agent/src/conpty/mod.rs +1 -0
- package/crates/team-agent/src/coordinator/backoff.rs +5 -8
- package/crates/team-agent/src/coordinator/conpty_shim.rs +3 -2
- package/crates/team-agent/src/coordinator/health.rs +1 -0
- package/crates/team-agent/src/coordinator/mod.rs +2 -2
- package/crates/team-agent/src/coordinator/orphan.rs +4 -116
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +1 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +15 -866
- package/crates/team-agent/src/coordinator/steps/delivery.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +2 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +1 -0
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +0 -174
- package/crates/team-agent/src/coordinator/tests/api_error_recovery.rs +139 -375
- package/crates/team-agent/src/coordinator/tests/basics.rs +0 -184
- package/crates/team-agent/src/coordinator/tests/mod.rs +2 -17
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -49
- package/crates/team-agent/src/coordinator/tick.rs +23 -40
- package/crates/team-agent/src/coordinator/types.rs +3 -176
- package/crates/team-agent/src/db/agent_health_capture.rs +1 -0
- package/crates/team-agent/src/db/message_store.rs +2 -1
- package/crates/team-agent/src/db/migration.rs +5 -4
- package/crates/team-agent/src/db/mod.rs +1 -0
- package/crates/team-agent/src/db/schema.rs +1 -0
- package/crates/team-agent/src/diagnose/comms.rs +2 -1
- package/crates/team-agent/src/diagnose/mod.rs +1 -0
- package/crates/team-agent/src/diagnose/orphans.rs +1 -0
- package/crates/team-agent/src/layout/manager.rs +3 -7
- package/crates/team-agent/src/layout/mod.rs +2 -2
- package/crates/team-agent/src/layout/overlay.rs +6 -1
- package/crates/team-agent/src/layout/placement.rs +1 -0
- package/crates/team-agent/src/layout/recovery.rs +3 -0
- package/crates/team-agent/src/layout/runtime_sessions.rs +8 -3
- package/crates/team-agent/src/layout/sessions.rs +8 -1
- package/crates/team-agent/src/layout/tmux_endpoint.rs +3 -0
- package/crates/team-agent/src/layout/worker_env.rs +3 -0
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/helpers.rs +1 -0
- package/crates/team-agent/src/leader/incident.rs +1 -0
- package/crates/team-agent/src/leader/lease.rs +27 -22
- package/crates/team-agent/src/leader/mod.rs +2 -3
- package/crates/team-agent/src/leader/owner_bind.rs +13 -3
- package/crates/team-agent/src/leader/provider_attribution.rs +1 -0
- package/crates/team-agent/src/leader/rediscover.rs +4 -17
- package/crates/team-agent/src/leader/registry.rs +3 -15
- package/crates/team-agent/src/leader/start.rs +373 -125
- package/crates/team-agent/src/leader/takeover.rs +10 -232
- package/crates/team-agent/src/leader/tests/claim_leader_mailbox_flush_contract.rs +544 -0
- package/crates/team-agent/src/leader/tests/historical_inventory_upgrade_gate_red.rs +624 -0
- package/crates/team-agent/src/leader/tests/identity.rs +7 -2
- package/crates/team-agent/src/leader/tests/idle.rs +0 -193
- package/crates/team-agent/src/leader/tests/lease_api.rs +0 -45
- package/crates/team-agent/src/leader/tests/mod.rs +4 -41
- package/crates/team-agent/src/leader/tests/rehomed_env.rs +83 -0
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1273 -0
- package/crates/team-agent/src/leader/tests/terminal_and_replay_vs_rerender_invariants_red.rs +417 -0
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +22 -103
- package/crates/team-agent/src/leader/types.rs +1 -28
- package/crates/team-agent/src/lib.rs +3 -0
- package/crates/team-agent/src/lifecycle/display.rs +7 -1
- package/crates/team-agent/src/lifecycle/helpers.rs +3 -1
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +2 -2
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +2 -2
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +4 -26
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -27
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +1 -27
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +4 -32
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch.rs +11 -12
- package/crates/team-agent/src/lifecycle/mod.rs +2 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +3 -66
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +2 -2
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -5
- package/crates/team-agent/src/lifecycle/restart/remove.rs +1 -1
- package/crates/team-agent/src/lifecycle/restart/selection.rs +3 -3
- package/crates/team-agent/src/lifecycle/restart.rs +13 -15
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1007 -0
- package/crates/team-agent/src/lifecycle/tests/b0_legacy_snapshot_nonauthority_contract.rs +265 -0
- package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +459 -0
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +849 -0
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +323 -0
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +423 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +737 -0
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +1187 -0
- package/crates/team-agent/src/lifecycle/tests/codex_weak_window_attribution_red.rs +683 -0
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +394 -0
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +1856 -0
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +917 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +481 -0
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +301 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +516 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +986 -0
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +651 -0
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -0
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +332 -0
- package/crates/team-agent/src/lifecycle/tests/realmachine_clusters_1_6_red.rs +846 -0
- package/crates/team-agent/src/lifecycle/tests/realmachine_residual_g1_g4_red.rs +648 -0
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +910 -0
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +630 -0
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1083 -0
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +1677 -0
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +410 -0
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +428 -0
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1032 -0
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +869 -0
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1249 -0
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +342 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +542 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1103 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1276 -0
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_roster_red.rs +145 -0
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +603 -0
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +837 -0
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +928 -0
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +951 -0
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +327 -0
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +760 -0
- package/crates/team-agent/src/lifecycle/tests.rs +59 -0
- package/crates/team-agent/src/lifecycle/types.rs +0 -9
- package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -0
- package/crates/team-agent/src/mcp_server/mod.rs +1 -0
- package/crates/team-agent/src/mcp_server/normalize.rs +4 -3
- package/crates/team-agent/src/mcp_server/tools.rs +1 -0
- package/crates/team-agent/src/mcp_server/types.rs +1 -18
- package/crates/team-agent/src/mcp_server/wire.rs +5 -5
- package/crates/team-agent/src/messaging/activity.rs +1 -0
- package/crates/team-agent/src/messaging/address.rs +1 -0
- package/crates/team-agent/src/messaging/delivery.rs +4 -3
- package/crates/team-agent/src/messaging/helpers.rs +2 -1
- package/crates/team-agent/src/messaging/leader_channel.rs +1 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +2 -1
- package/crates/team-agent/src/messaging/mod.rs +10 -14
- package/crates/team-agent/src/messaging/peers.rs +2 -0
- package/crates/team-agent/src/messaging/persist.rs +3 -1
- package/crates/team-agent/src/messaging/presentation.rs +2 -1
- package/crates/team-agent/src/messaging/results.rs +1 -0
- package/crates/team-agent/src/messaging/scheduler.rs +1 -0
- package/crates/team-agent/src/messaging/selftest.rs +1 -1
- package/crates/team-agent/src/messaging/send.rs +41 -8
- package/crates/team-agent/src/messaging/tests/mod.rs +5 -0
- package/crates/team-agent/src/messaging/types.rs +2 -2
- package/crates/team-agent/src/messaging/watchers.rs +19 -13
- package/crates/team-agent/src/model/enums.rs +1 -0
- package/crates/team-agent/src/model/errors.rs +1 -0
- package/crates/team-agent/src/model/ids.rs +1 -0
- package/crates/team-agent/src/model/mod.rs +1 -0
- package/crates/team-agent/src/model/name_similarity.rs +1 -0
- package/crates/team-agent/src/model/pane_authority_refusal.rs +1 -0
- package/crates/team-agent/src/model/paths.rs +1 -0
- package/crates/team-agent/src/model/permissions.rs +3 -2
- package/crates/team-agent/src/model/routing.rs +1 -0
- package/crates/team-agent/src/model/spec.rs +1 -0
- package/crates/team-agent/src/model/task_graph.rs +1 -0
- package/crates/team-agent/src/model/yaml/tests.rs +1 -0
- package/crates/team-agent/src/model/yaml.rs +1 -0
- package/crates/team-agent/src/packaging/install.rs +9 -147
- package/crates/team-agent/src/packaging/migrate.rs +2 -0
- package/crates/team-agent/src/packaging/mod.rs +1 -0
- package/crates/team-agent/src/packaging/repair.rs +2 -0
- package/crates/team-agent/src/packaging/tests.rs +29 -218
- package/crates/team-agent/src/packaging/types.rs +8 -15
- package/crates/team-agent/src/platform/argv.rs +4 -0
- package/crates/team-agent/src/platform/errors.rs +10 -1
- package/crates/team-agent/src/platform/file_lock.rs +4 -157
- package/crates/team-agent/src/platform/mod.rs +0 -61
- package/crates/team-agent/src/platform/process.rs +1 -0
- package/crates/team-agent/src/provider/adapter.rs +1 -0
- package/crates/team-agent/src/provider/adapters/claude.rs +1 -0
- package/crates/team-agent/src/provider/adapters/claude_fork.rs +1 -0
- package/crates/team-agent/src/provider/adapters/codex.rs +1 -0
- package/crates/team-agent/src/provider/adapters/copilot.rs +1 -0
- package/crates/team-agent/src/provider/adapters/copilot_fork.rs +1 -0
- package/crates/team-agent/src/provider/adapters/fake.rs +1 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +1 -0
- package/crates/team-agent/src/provider/approvals/mod.rs +1 -0
- package/crates/team-agent/src/provider/approvals/parsing.rs +2 -5
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +6 -5
- package/crates/team-agent/src/provider/classify.rs +1 -0
- package/crates/team-agent/src/provider/faults.rs +1 -26
- package/crates/team-agent/src/provider/helpers.rs +1 -0
- package/crates/team-agent/src/provider/mod.rs +1 -1
- package/crates/team-agent/src/provider/session/capture.rs +1 -4
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork.rs +1 -0
- package/crates/team-agent/src/provider/session/mod.rs +2 -4
- package/crates/team-agent/src/provider/session/resume.rs +2 -209
- package/crates/team-agent/src/provider/session_scan/claude.rs +1 -0
- package/crates/team-agent/src/provider/session_scan/codex.rs +1 -0
- package/crates/team-agent/src/provider/session_scan/common.rs +1 -0
- package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
- package/crates/team-agent/src/provider/session_scan.rs +1 -42
- package/crates/team-agent/src/provider/startup_prompt.rs +1 -0
- package/crates/team-agent/src/provider/types.rs +1 -0
- package/crates/team-agent/src/provider/wire.rs +1 -0
- package/crates/team-agent/src/state/identity.rs +16 -5
- package/crates/team-agent/src/state/identity_keys.rs +1 -0
- package/crates/team-agent/src/state/mod.rs +2 -0
- package/crates/team-agent/src/state/owner_gate.rs +5 -0
- package/crates/team-agent/src/state/ownership.rs +10 -4
- package/crates/team-agent/src/state/paths.rs +12 -0
- package/crates/team-agent/src/state/persist.rs +9 -3
- package/crates/team-agent/src/state/projection.rs +15 -2
- package/crates/team-agent/src/state/repository/intent.rs +1 -0
- package/crates/team-agent/src/state/repository/tests.rs +1 -0
- package/crates/team-agent/src/state/repository.rs +7 -13
- package/crates/team-agent/src/state/selector.rs +1 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +39 -0
- package/crates/team-agent/src/tmux_backend.rs +63 -0
- package/crates/team-agent/src/transport.rs +15 -0
- package/package.json +4 -4
- package/crates/team-agent/src/leader/inject.rs +0 -33
- package/crates/team-agent/src/provider/command.rs +0 -80
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! state.json 持久化(bug-084 韧性;真相源 `state.py:save_runtime_state` + `_self_heal_runtime_state`
|
|
2
3
|
//! + `runtime.py:_runtime_lock`)。
|
|
3
4
|
//!
|
|
@@ -71,6 +72,7 @@ static RUNTIME_STATE_CACHE: LazyLock<Mutex<HashMap<PathBuf, Value>>> =
|
|
|
71
72
|
LazyLock::new(|| Mutex::new(HashMap::new()));
|
|
72
73
|
static TMP_SEQ: AtomicU64 = AtomicU64::new(0);
|
|
73
74
|
|
|
75
|
+
///
|
|
74
76
|
/// `state.py:41`。
|
|
75
77
|
pub fn runtime_state_path(workspace: &Path) -> PathBuf {
|
|
76
78
|
runtime_dir(workspace).join("state.json")
|
|
@@ -202,6 +204,7 @@ impl Drop for RuntimeLock {
|
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
206
|
|
|
207
|
+
///
|
|
205
208
|
/// `save_runtime_state`(bug-084)。`state` 是 state.json 的内存 Value(插入序保留)。
|
|
206
209
|
/// 注:Python 在此还调 `_migrate_state_identity`(identity slice 落地后接入;本 slice 不改 state 内容)。
|
|
207
210
|
pub fn save_runtime_state(workspace: &Path, state: &Value) -> Result<(), StateError> {
|
|
@@ -1135,10 +1138,11 @@ fn self_heal(
|
|
|
1135
1138
|
}
|
|
1136
1139
|
}
|
|
1137
1140
|
|
|
1141
|
+
///
|
|
1138
1142
|
/// `load_runtime_state`(本 slice 最小:读+parse+缓存;normalize/migration 待 identity slice)。
|
|
1139
1143
|
/// `normalize_agent_session_state`(`state.py:45`):为每个 agent dict 的 SESSION_STATE_FIELDS
|
|
1140
1144
|
/// setdefault None(缺则末尾插)。
|
|
1141
|
-
|
|
1145
|
+
fn normalize_agent_session_state(state: &mut Value) {
|
|
1142
1146
|
let Some(agents) = state.get_mut("agents").and_then(Value::as_object_mut) else {
|
|
1143
1147
|
return;
|
|
1144
1148
|
};
|
|
@@ -1151,12 +1155,13 @@ pub fn normalize_agent_session_state(state: &mut Value) {
|
|
|
1151
1155
|
}
|
|
1152
1156
|
}
|
|
1153
1157
|
|
|
1158
|
+
///
|
|
1154
1159
|
/// `_migrate_active_team_key`(`state.py:73`,0.2.6 Family B C6):legacy state 缺 active_team_key
|
|
1155
1160
|
/// 时 seed 一次。返回是否有改动。
|
|
1156
1161
|
///
|
|
1157
1162
|
/// 注:Python `seed if seed in teams or not teams else seed` 两支均为 `seed`(死三元),
|
|
1158
1163
|
/// 此处直接 `= seed`,与可观测行为一致。
|
|
1159
|
-
|
|
1164
|
+
fn migrate_active_team_key(state: &mut Value) -> bool {
|
|
1160
1165
|
if state
|
|
1161
1166
|
.as_object()
|
|
1162
1167
|
.is_some_and(|o| o.contains_key("active_team_key"))
|
|
@@ -1191,6 +1196,7 @@ pub fn migrate_active_team_key(state: &mut Value) -> bool {
|
|
|
1191
1196
|
true
|
|
1192
1197
|
}
|
|
1193
1198
|
|
|
1199
|
+
///
|
|
1194
1200
|
/// RM-039-STAT-001 second-round compat normalizer (architect verdict
|
|
1195
1201
|
/// 2026-06-22). When `active_team_key` names an existing `teams` entry
|
|
1196
1202
|
/// but root `team_key` is missing, set root `team_key = active_team_key`
|
|
@@ -1210,7 +1216,7 @@ pub fn migrate_active_team_key(state: &mut Value) -> bool {
|
|
|
1210
1216
|
/// * Does NOT touch any other field (no generic deep merge).
|
|
1211
1217
|
///
|
|
1212
1218
|
/// Returns `true` if state was mutated and the caller should persist.
|
|
1213
|
-
|
|
1219
|
+
fn migrate_team_key_to_match_active_team(state: &mut Value) -> bool {
|
|
1214
1220
|
let Some(obj) = state.as_object() else {
|
|
1215
1221
|
return false;
|
|
1216
1222
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! projection:team 选择 / top-level 派生视图(真相源 `state.py`,0.2.6 Family B C6-C8)。
|
|
2
3
|
//!
|
|
3
4
|
//! `state.teams` 是唯一候选源(`status == "alive"`);顶层 `session_name`/`team_dir`/`agents`/`tasks`
|
|
@@ -24,6 +25,7 @@ use crate::state::persist::{
|
|
|
24
25
|
|
|
25
26
|
pub(crate) const CURRENT_TEAM_ALIAS: &str = "current";
|
|
26
27
|
|
|
28
|
+
///
|
|
27
29
|
/// `team_state_key`(`state.py:93`):从 team_dir(.name)/spec_path(.parent.name)派生 team key,
|
|
28
30
|
/// 跳过 `.team`/`runtime`;兜底 `session_name` 或 `"current"`。
|
|
29
31
|
pub fn team_state_key(state: &Value) -> String {
|
|
@@ -184,6 +186,7 @@ fn legacy_owner_team_aliases(entry: &Value) -> impl Iterator<Item = String> + '_
|
|
|
184
186
|
.map(str::to_string)
|
|
185
187
|
}
|
|
186
188
|
|
|
189
|
+
///
|
|
187
190
|
/// `compact_team_state`(`state.py:105`):剔除 `teams`(team entry 不嵌套全量 teams),保序。
|
|
188
191
|
pub fn compact_team_state(state: &Value) -> Value {
|
|
189
192
|
match state.as_object() {
|
|
@@ -208,6 +211,7 @@ pub fn state_is_managed_leader(state: &Value) -> bool {
|
|
|
208
211
|
!state_is_external_leader(state)
|
|
209
212
|
}
|
|
210
213
|
|
|
214
|
+
///
|
|
211
215
|
/// `merge_workspace_team_state`(`state.py:111`):把新启动的 team 并入既有 workspace state。
|
|
212
216
|
pub fn merge_workspace_team_state(existing: &Value, launched: &Value) -> Value {
|
|
213
217
|
let launched_key = team_state_key(launched);
|
|
@@ -239,6 +243,7 @@ pub fn merge_workspace_team_state(existing: &Value, launched: &Value) -> Value {
|
|
|
239
243
|
Value::Object(merged)
|
|
240
244
|
}
|
|
241
245
|
|
|
246
|
+
///
|
|
242
247
|
/// `team_state_candidates`(`state.py:131`):唯一候选源 = `state.teams` 中
|
|
243
248
|
/// `status=="alive"`(大小写不敏感;缺 status/空 视为 alive,但 0.5.26 起
|
|
244
249
|
/// legacy shutdown 残留「无 status + 全体 agents 处于终态」不再算 alive)。
|
|
@@ -263,6 +268,7 @@ pub fn team_state_candidates(state: &Value) -> Map<String, Value> {
|
|
|
263
268
|
out
|
|
264
269
|
}
|
|
265
270
|
|
|
271
|
+
///
|
|
266
272
|
/// 0.5.26 (`.team/artifacts/stale-team-saveconflict-locate.md` §7.1): 唯一 alive
|
|
267
273
|
/// 谓词。规则:
|
|
268
274
|
/// - 非 object → 排除;
|
|
@@ -348,8 +354,9 @@ pub(crate) fn agent_status_is_terminal(status: &str) -> bool {
|
|
|
348
354
|
)
|
|
349
355
|
}
|
|
350
356
|
|
|
357
|
+
///
|
|
351
358
|
/// `format_team_candidates`(`state.py:148`):候选摘要串(key 排序;agents 排序逗号连,空→`-`)。
|
|
352
|
-
|
|
359
|
+
fn format_team_candidates(team_states: &Map<String, Value>) -> String {
|
|
353
360
|
if team_states.is_empty() {
|
|
354
361
|
return "No team state was found.".to_string();
|
|
355
362
|
}
|
|
@@ -412,6 +419,7 @@ fn valid_owner_pane_id(pane_id: &str) -> bool {
|
|
|
412
419
|
pane_id.starts_with('%') || pane_id.chars().all(|ch| ch.is_ascii_digit())
|
|
413
420
|
}
|
|
414
421
|
|
|
422
|
+
///
|
|
415
423
|
/// `_project_top_level_view`(`state.py:167`,C8):把 `teams[team_key]` 投影成扁平顶层视图。
|
|
416
424
|
pub fn project_top_level_view(state: &Value, team_key: &str) -> Value {
|
|
417
425
|
// entry = _team_entry_from_state(...) or {} —— 空 dict 亦走 {} 分支(同内容)。
|
|
@@ -488,6 +496,7 @@ pub struct TeamScopeResolution {
|
|
|
488
496
|
pub state: Value,
|
|
489
497
|
}
|
|
490
498
|
|
|
499
|
+
///
|
|
491
500
|
/// Resolve a requested team selector once, returning both the canonical map key and its
|
|
492
501
|
/// projected state. Callers must carry `canonical_team_key` forward rather than deriving team
|
|
493
502
|
/// identity again from the projection or the original alias.
|
|
@@ -646,6 +655,7 @@ pub fn resolve_runtime_team_scope(
|
|
|
646
655
|
))
|
|
647
656
|
}
|
|
648
657
|
|
|
658
|
+
///
|
|
649
659
|
/// `select_runtime_state`(`state.py:193`):compatibility projection wrapper.
|
|
650
660
|
pub fn select_runtime_state(workspace: &Path, team: Option<&str>) -> Result<Value, StateError> {
|
|
651
661
|
resolve_runtime_team_scope(workspace, team).map(|resolved| resolved.state)
|
|
@@ -677,8 +687,9 @@ fn team_selector_matches(team: &str, key: &str, value: &Value) -> bool {
|
|
|
677
687
|
.is_some_and(|name| team == name)
|
|
678
688
|
}
|
|
679
689
|
|
|
690
|
+
///
|
|
680
691
|
/// `ambiguous_team_target_result`(`state.py:226`):无显式 team 且多候选 → 拒绝 dict;否则 None。
|
|
681
|
-
|
|
692
|
+
fn ambiguous_team_target_result(state: &Value) -> Option<Value> {
|
|
682
693
|
let alive = team_state_candidates(state);
|
|
683
694
|
let active = state
|
|
684
695
|
.get("active_team_key")
|
|
@@ -704,6 +715,7 @@ pub fn ambiguous_team_target_result(state: &Value) -> Option<Value> {
|
|
|
704
715
|
}))
|
|
705
716
|
}
|
|
706
717
|
|
|
718
|
+
///
|
|
707
719
|
/// `resolve_team_scoped_state`(`state.py:243`):返回 `(state, refusal)`,二者恰一为 Some。
|
|
708
720
|
/// `team=None` 且歧义 → refusal;否则 select,RuntimeError → `team_target_unresolved` refusal。
|
|
709
721
|
pub fn resolve_team_scoped_state(
|
|
@@ -732,6 +744,7 @@ pub fn resolve_team_scoped_state(
|
|
|
732
744
|
}
|
|
733
745
|
}
|
|
734
746
|
|
|
747
|
+
///
|
|
735
748
|
/// `save_team_scoped_state`(`state.py:594`):把 team-scoped(投影后)state 写回 workspace,**保全**
|
|
736
749
|
/// 多 team workspace 里其他 team 的持久态。单 team(磁盘无 `teams` 且 primary key == target)退化为
|
|
737
750
|
/// 纯 `save_runtime_state`(字节等价);多 team 时把本 team 落到 `teams[target_key]=compact(...)`,顶层
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! StateRepository facade.
|
|
2
3
|
//!
|
|
3
4
|
//! Ref:
|
|
@@ -73,16 +74,19 @@ pub struct StateRepository<'a> {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
impl<'a> StateRepository<'a> {
|
|
77
|
+
///
|
|
76
78
|
/// Construct a repository bound to a workspace root.
|
|
77
79
|
pub fn new(workspace: &'a Path) -> Self {
|
|
78
80
|
Self { workspace }
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
///
|
|
81
84
|
/// Load the raw workspace state document.
|
|
82
85
|
pub fn load_workspace(&self) -> Result<Value, StateError> {
|
|
83
86
|
helper_load_workspace(self.workspace)
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
///
|
|
86
90
|
/// Load the canonical workspace document without running read-time
|
|
87
91
|
/// migrations. `None` preserves the legacy raw-reader distinction between
|
|
88
92
|
/// a missing file and a present empty/default document.
|
|
@@ -96,6 +100,7 @@ impl<'a> StateRepository<'a> {
|
|
|
96
100
|
serde_json::from_str(&text).map(Some).map_err(StateError::from)
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
///
|
|
99
104
|
/// Resolve a team-scoped projection using the existing projection selector.
|
|
100
105
|
/// `team_key = None` selects the ambient team the same way as pre-S1a.
|
|
101
106
|
pub fn load_team(&self, team_key: Option<&str>) -> Result<Value, StateError> {
|
|
@@ -110,6 +115,7 @@ impl<'a> StateRepository<'a> {
|
|
|
110
115
|
})
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
///
|
|
113
119
|
/// Persist `state` under the supplied intent. S1a dispatch is a pure
|
|
114
120
|
/// forward call to the same legacy helper family that the caller used
|
|
115
121
|
/// before S1a; the intent selects the family, not the merge semantics.
|
|
@@ -117,6 +123,7 @@ impl<'a> StateRepository<'a> {
|
|
|
117
123
|
route_direct(self.workspace, intent, state)
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
///
|
|
120
127
|
/// Persist `state` and let `reapply` re-materialize the write on
|
|
121
128
|
/// SaveConflict, matching the pre-S1a `_reapplying_after_conflict` helper
|
|
122
129
|
/// behavior for reapply-shaped intents.
|
|
@@ -212,14 +219,6 @@ pub enum StateWriteIntent<'a> {
|
|
|
212
219
|
CoordinatorConptyShim {
|
|
213
220
|
team_key: Option<&'a str>,
|
|
214
221
|
},
|
|
215
|
-
/// 0.5.36 (`.team/artifacts/supermarket-api-error-recovery-locate.md` §7.3):
|
|
216
|
-
/// post-save recovery step writes back the recovery intent outcome
|
|
217
|
-
/// (attempts / status / last_error / blocked_reason). Distinct intent
|
|
218
|
-
/// so future S1b migration can route it deliberately.
|
|
219
|
-
CoordinatorApiErrorRecovery {
|
|
220
|
-
team_key: Option<&'a str>,
|
|
221
|
-
agent_id: Option<&'a str>,
|
|
222
|
-
},
|
|
223
222
|
McpAssignTask {
|
|
224
223
|
team_key: Option<&'a str>,
|
|
225
224
|
task_id: &'a str,
|
|
@@ -399,11 +398,6 @@ fn route_direct(
|
|
|
399
398
|
// CoordinatorConptyShim -> root save
|
|
400
399
|
// (coordinator/conpty_shim.rs:426/674).
|
|
401
400
|
StateWriteIntent::CoordinatorConptyShim { .. } => helper_write_root(workspace, state),
|
|
402
|
-
// 0.5.36 CoordinatorApiErrorRecovery -> root save. The recovery
|
|
403
|
-
// intent lives under `coordinator.abnormal_api_error_recovery`, a
|
|
404
|
-
// root-scoped bookkeeping namespace that must survive across team
|
|
405
|
-
// boundaries; use the same helper family as CoordinatorConptyShim.
|
|
406
|
-
StateWriteIntent::CoordinatorApiErrorRecovery { .. } => helper_write_root(workspace, state),
|
|
407
401
|
// McpAssignTask uses the reapply variant today; direct save routes
|
|
408
402
|
// to the root helper for parity.
|
|
409
403
|
StateWriteIntent::McpAssignTask { .. } => helper_write_root(workspace, state),
|
|
@@ -146,6 +146,45 @@ fn backend_with_stdin(
|
|
|
146
146
|
)
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
#[test]
|
|
150
|
+
fn a21_spawn_command_too_long_is_structured_before_transport() {
|
|
151
|
+
let (backend, recorded) = backend_with(MockResp::Out(ok("%0\n")), Vec::new());
|
|
152
|
+
let session = SessionName::new("a21-session");
|
|
153
|
+
let window = WindowName::new("worker-long-role");
|
|
154
|
+
let argv = vec!["codex".to_string(), "x".repeat(20_000)];
|
|
155
|
+
|
|
156
|
+
let error = backend
|
|
157
|
+
.spawn_first(
|
|
158
|
+
&session,
|
|
159
|
+
&window,
|
|
160
|
+
&argv,
|
|
161
|
+
Path::new("/tmp/a21-workspace"),
|
|
162
|
+
&BTreeMap::new(),
|
|
163
|
+
)
|
|
164
|
+
.expect_err("oversized spawn must fail before tmux transport");
|
|
165
|
+
let text = error.to_string();
|
|
166
|
+
assert!(
|
|
167
|
+
text.contains("command_too_long"),
|
|
168
|
+
"structured error: {text}"
|
|
169
|
+
);
|
|
170
|
+
assert!(
|
|
171
|
+
text.contains("worker-long-role"),
|
|
172
|
+
"seat must be named: {text}"
|
|
173
|
+
);
|
|
174
|
+
assert!(
|
|
175
|
+
text.contains("actual_bytes="),
|
|
176
|
+
"actual length must be visible: {text}"
|
|
177
|
+
);
|
|
178
|
+
assert!(
|
|
179
|
+
text.contains("limit_bytes="),
|
|
180
|
+
"limit must be visible: {text}"
|
|
181
|
+
);
|
|
182
|
+
assert!(
|
|
183
|
+
recorded.lock().unwrap().is_empty(),
|
|
184
|
+
"tmux must not be called: {text}"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
149
188
|
fn svec(items: &[&str]) -> Vec<String> {
|
|
150
189
|
items.iter().map(|s| (*s).to_string()).collect()
|
|
151
190
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! Concrete tmux `Transport` backend (SKELETON) — the real executor that runs `tmux <argv>`.
|
|
2
3
|
//!
|
|
3
4
|
//! step 9 shipped the [`crate::transport::Transport`] trait + the pure tmux argv-builders
|
|
@@ -77,6 +78,11 @@ const COMMAND_TIMEOUT: Duration = Duration::from_secs(5);
|
|
|
77
78
|
const SPAWN_IDENTITY_TIMEOUT: Duration = Duration::from_millis(500);
|
|
78
79
|
const SPAWN_IDENTITY_POLL_INTERVAL: Duration = Duration::from_millis(25);
|
|
79
80
|
|
|
81
|
+
/// Keep the final tmux argv below the ~16 KiB command envelope observed on macOS.
|
|
82
|
+
/// This is checked after shell quoting and socket arguments are applied, before
|
|
83
|
+
/// the external tmux process is called.
|
|
84
|
+
pub const TMUX_SPAWN_COMMAND_LIMIT_BYTES: usize = 16_000;
|
|
85
|
+
|
|
80
86
|
impl CommandRunner for RealCommandRunner {
|
|
81
87
|
fn run(&self, argv: &[String]) -> Result<CommandOutput, std::io::Error> {
|
|
82
88
|
self.run_inner(argv, None)
|
|
@@ -390,6 +396,27 @@ impl TmuxBackend {
|
|
|
390
396
|
if self.socket.is_none() {
|
|
391
397
|
return;
|
|
392
398
|
}
|
|
399
|
+
// A server-level kill is only safe after the caller has removed every
|
|
400
|
+
// resource it owns. A non-empty server may carry a pre-existing team
|
|
401
|
+
// (or a leader owned by another launcher), so fail closed instead of
|
|
402
|
+
// turning an endpoint cleanup into a shared-server teardown. An
|
|
403
|
+
// inconclusive probe is also not proof of ownership.
|
|
404
|
+
let server_is_empty = match self.list_targets() {
|
|
405
|
+
Ok(targets) => targets.is_empty(),
|
|
406
|
+
Err(_) => false,
|
|
407
|
+
};
|
|
408
|
+
if !server_is_empty {
|
|
409
|
+
if let Some(workspace) = &self.event_workspace {
|
|
410
|
+
let _ = crate::event_log::EventLog::new(workspace).write(
|
|
411
|
+
"tmux.kill_server_skipped_nonempty_or_unknown",
|
|
412
|
+
serde_json::json!({
|
|
413
|
+
"reason": "server_ownership_not_proven",
|
|
414
|
+
"endpoint": self.tmux_endpoint(),
|
|
415
|
+
}),
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
393
420
|
let argv = self.tmux_argv(&["tmux".to_string(), "kill-server".to_string()]);
|
|
394
421
|
let _ = self.runner.run(&argv);
|
|
395
422
|
}
|
|
@@ -787,6 +814,16 @@ impl TmuxBackend {
|
|
|
787
814
|
first: bool,
|
|
788
815
|
) -> Result<SpawnResult, TransportError> {
|
|
789
816
|
let spawn_argv = tmux_spawn_argv(session, window, command, first);
|
|
817
|
+
self.validate_spawn_command(
|
|
818
|
+
&spawn_argv,
|
|
819
|
+
if first {
|
|
820
|
+
"tmux.new-session"
|
|
821
|
+
} else {
|
|
822
|
+
"tmux.new-window"
|
|
823
|
+
},
|
|
824
|
+
session,
|
|
825
|
+
window,
|
|
826
|
+
)?;
|
|
790
827
|
let output = self.run_spawn(&spawn_argv)?;
|
|
791
828
|
let pane = output.stdout.trim();
|
|
792
829
|
if pane.is_empty() {
|
|
@@ -897,6 +934,7 @@ impl TmuxBackend {
|
|
|
897
934
|
"-lc".to_string(),
|
|
898
935
|
command,
|
|
899
936
|
];
|
|
937
|
+
self.validate_spawn_command(&split_argv, "tmux.split-window", session, window)?;
|
|
900
938
|
let output = self.run_spawn(&split_argv)?;
|
|
901
939
|
let pane = output.stdout.trim();
|
|
902
940
|
if pane.is_empty() {
|
|
@@ -932,6 +970,31 @@ impl TmuxBackend {
|
|
|
932
970
|
}
|
|
933
971
|
}
|
|
934
972
|
|
|
973
|
+
fn validate_spawn_command(
|
|
974
|
+
&self,
|
|
975
|
+
argv: &[String],
|
|
976
|
+
segment: &str,
|
|
977
|
+
session: &SessionName,
|
|
978
|
+
window: &WindowName,
|
|
979
|
+
) -> Result<(), TransportError> {
|
|
980
|
+
let final_argv = self.tmux_argv(argv);
|
|
981
|
+
let actual_bytes = final_argv
|
|
982
|
+
.iter()
|
|
983
|
+
.map(|arg| arg.len().saturating_add(1))
|
|
984
|
+
.sum::<usize>();
|
|
985
|
+
if actual_bytes > TMUX_SPAWN_COMMAND_LIMIT_BYTES {
|
|
986
|
+
return Err(TransportError::CommandTooLong {
|
|
987
|
+
backend: BackendKind::Tmux,
|
|
988
|
+
segment: segment.to_string(),
|
|
989
|
+
session: session.as_str().to_string(),
|
|
990
|
+
window: window.as_str().to_string(),
|
|
991
|
+
actual_bytes,
|
|
992
|
+
limit_bytes: TMUX_SPAWN_COMMAND_LIMIT_BYTES,
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
Ok(())
|
|
996
|
+
}
|
|
997
|
+
|
|
935
998
|
fn run_spawn(&self, argv: &[String]) -> Result<CommandOutput, TransportError> {
|
|
936
999
|
let argv = self.tmux_argv(argv);
|
|
937
1000
|
let output = self
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! step 9 · transport — 控制面 trait + typed payload(ROUND-0 SKELETON).
|
|
2
3
|
//!
|
|
3
4
|
//! 设计真相源:`docs/phase0/transport-backend-design.md` §1(Rust 草图:Target /
|
|
@@ -486,6 +487,20 @@ pub enum TransportError {
|
|
|
486
487
|
code: Option<i32>,
|
|
487
488
|
stderr: String,
|
|
488
489
|
},
|
|
490
|
+
/// The final tmux spawn argv is too large for the transport command envelope.
|
|
491
|
+
/// Keep this separate from `Subprocess` so callers do not have to parse a
|
|
492
|
+
/// provider-sized argv dump to learn the actionable limit.
|
|
493
|
+
#[error(
|
|
494
|
+
"command_too_long: backend={backend:?}; segment={segment}; session={session}; window={window}; actual_bytes={actual_bytes}; limit_bytes={limit_bytes}"
|
|
495
|
+
)]
|
|
496
|
+
CommandTooLong {
|
|
497
|
+
backend: BackendKind,
|
|
498
|
+
segment: String,
|
|
499
|
+
session: String,
|
|
500
|
+
window: String,
|
|
501
|
+
actual_bytes: usize,
|
|
502
|
+
limit_bytes: usize,
|
|
503
|
+
},
|
|
489
504
|
/// wezterm mux 连不上 / tmux server 不在。
|
|
490
505
|
#[error("mux unavailable on {backend:?}: {detail}")]
|
|
491
506
|
MuxUnavailable {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.63",
|
|
4
4
|
"description": "npx installer for Team Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"team-agent-installer": "npm/install.mjs"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@team-agent/cli-darwin-arm64": "0.5.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.5.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.5.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.5.63",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.63",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.63"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
//! leader::inject — `push_idle_reminder` no-op shim.
|
|
2
|
-
//!
|
|
3
|
-
//! #236 nag_removal (N35): the framework no longer auto-pings the leader pane based on
|
|
4
|
-
//! idle-classification inferences. `push_idle_reminder` used to emit the
|
|
5
|
-
//! `idle_takeover.reminder` event AND route a "leader takeover may be appropriate"
|
|
6
|
-
//! message through the N31/N32 funnel; both were nag outputs synthesized from
|
|
7
|
-
//! time/state, not from a real delivery obligation. They are deleted by design —
|
|
8
|
-
//! ownership / handover changes only via explicit `claim-leader` / `takeover` commands.
|
|
9
|
-
//!
|
|
10
|
-
//! The function signature is preserved (callers in coordinator/tick.rs and lifecycle
|
|
11
|
-
//! tests still resolve) but the body is a strict no-op: no event, no primitive call,
|
|
12
|
-
//! no state mutation. The `_` discards keep clippy quiet and document that the
|
|
13
|
-
//! arguments are intentionally ignored under N35.
|
|
14
|
-
|
|
15
|
-
use std::path::Path;
|
|
16
|
-
|
|
17
|
-
use serde_json::Value;
|
|
18
|
-
|
|
19
|
-
use super::{LeaderError, TakeoverReminderResult};
|
|
20
|
-
|
|
21
|
-
/// #236 nag_removal: was the take-over reminder injector; now a true no-op. Delivery
|
|
22
|
-
/// primitives (`report_result` / `send_to_leader` / N31/N32 funnel / `request_human` /
|
|
23
|
-
/// broadcast-leader) continue to flow through their own callsites — only the
|
|
24
|
-
/// time/state-inferred nag output that lived in this helper is gone.
|
|
25
|
-
pub fn push_idle_reminder(
|
|
26
|
-
workspace: &Path,
|
|
27
|
-
state: &Value,
|
|
28
|
-
event_log: &crate::event_log::EventLog,
|
|
29
|
-
result: &TakeoverReminderResult,
|
|
30
|
-
) -> Result<(), LeaderError> {
|
|
31
|
-
let _ = (workspace, state, event_log, result);
|
|
32
|
-
Ok(())
|
|
33
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
//! unit-7 (Stage 3) — provider command-assembly boundary.
|
|
2
|
-
//!
|
|
3
|
-
//! The provider/adapter.rs monolith mixes three concerns:
|
|
4
|
-
//! 1. The `ProviderAdapter` TRAIT and facade dispatch (the public API).
|
|
5
|
-
//! 2. Per-provider COMMAND ASSEMBLY (argv plan, base/resume flags,
|
|
6
|
-
//! --session-id injection — adapter.rs:409-508 region).
|
|
7
|
-
//! 3. SESSION SCANNING (capture polling + transcript scanning —
|
|
8
|
-
//! adapter.rs:511-546, 969-1078).
|
|
9
|
-
//!
|
|
10
|
-
//! This module CREATES the dedicated home for #2 so future work can
|
|
11
|
-
//! migrate command-assembly fns here in small, behavior-neutral commits.
|
|
12
|
-
//! Today it only carries the documented intent and a small typed helper —
|
|
13
|
-
//! the bulk of the assembly fns are still in adapter.rs and reachable
|
|
14
|
-
//! through the existing pub(crate) surface.
|
|
15
|
-
//!
|
|
16
|
-
//! Why incremental: every command-assembly fn touches multiple internal
|
|
17
|
-
//! constants (`--session-id`, `--resume`, MCP-config-path conventions) and
|
|
18
|
-
//! per-provider quirks. Moving them blindly is high-risk. The boundary
|
|
19
|
-
//! exists so the next commit (or any contributor) has a documented target
|
|
20
|
-
//! for relocations.
|
|
21
|
-
|
|
22
|
-
use crate::provider::Provider;
|
|
23
|
-
|
|
24
|
-
/// Lightweight tag for command-plan branches in the existing adapter
|
|
25
|
-
/// (`Base` vs `Resume`). Distinct type so command-assembly code reads
|
|
26
|
-
/// clearly even when it lives next to adapter dispatch.
|
|
27
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
28
|
-
pub enum CommandPlanKind {
|
|
29
|
-
/// Initial spawn — `--session-id <uuid>` predetermined.
|
|
30
|
-
Base,
|
|
31
|
-
/// Resume an existing session — `--resume <sid>`, no fresh
|
|
32
|
-
/// `--session-id` injected.
|
|
33
|
-
Resume,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/// Returns true when the provider's command plan honors `--session-id`
|
|
37
|
-
/// for fresh spawns. Mirrors the existing adapter logic but lives next to
|
|
38
|
-
/// where future command-assembly fns will land.
|
|
39
|
-
pub fn honors_session_id_for_base_spawn(provider: Provider) -> bool {
|
|
40
|
-
matches!(
|
|
41
|
-
provider,
|
|
42
|
-
Provider::Claude | Provider::ClaudeCode | Provider::Copilot
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
#[cfg(test)]
|
|
47
|
-
mod tests {
|
|
48
|
-
use super::*;
|
|
49
|
-
|
|
50
|
-
#[test]
|
|
51
|
-
fn fake_provider_does_not_honor_session_id() {
|
|
52
|
-
assert!(!honors_session_id_for_base_spawn(Provider::Fake));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
#[test]
|
|
56
|
-
fn provider_matrix_for_base_spawn_session_id_support() {
|
|
57
|
-
let cases = [
|
|
58
|
-
(Provider::Claude, true),
|
|
59
|
-
(Provider::ClaudeCode, true),
|
|
60
|
-
(Provider::Copilot, true),
|
|
61
|
-
(Provider::Codex, false),
|
|
62
|
-
(Provider::GeminiCli, false),
|
|
63
|
-
(Provider::Fake, false),
|
|
64
|
-
];
|
|
65
|
-
for (provider, expected) in cases {
|
|
66
|
-
assert_eq!(
|
|
67
|
-
honors_session_id_for_base_spawn(provider),
|
|
68
|
-
expected,
|
|
69
|
-
"{provider:?}"
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
#[test]
|
|
75
|
-
fn command_plan_kind_is_copy() {
|
|
76
|
-
let a = CommandPlanKind::Base;
|
|
77
|
-
let b = a;
|
|
78
|
-
assert_eq!(a, b);
|
|
79
|
-
}
|
|
80
|
-
}
|