@team-agent/installer 0.5.41 → 0.5.43
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 +129 -54
- package/crates/team-agent/src/cli/diagnose.rs +1 -2
- package/crates/team-agent/src/cli/emit.rs +1 -7
- package/crates/team-agent/src/cli/helpers.rs +3 -1
- package/crates/team-agent/src/cli/leader.rs +2 -1
- package/crates/team-agent/src/cli/mod.rs +27 -7
- package/crates/team-agent/src/cli/named_address.rs +14 -5
- package/crates/team-agent/src/cli/profile.rs +19 -7
- package/crates/team-agent/src/cli/send.rs +9 -3
- package/crates/team-agent/src/cli/status.rs +8 -30
- package/crates/team-agent/src/cli/status_port.rs +1341 -1272
- package/crates/team-agent/src/cli/tests/base.rs +738 -660
- package/crates/team-agent/src/cli/tests/compile.rs +45 -18
- package/crates/team-agent/src/cli/tests/divergence.rs +462 -444
- package/crates/team-agent/src/cli/tests/lane_c.rs +365 -282
- package/crates/team-agent/src/cli/tests/leader_watch.rs +356 -329
- package/crates/team-agent/src/cli/tests/main_preserved.rs +672 -564
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +284 -224
- package/crates/team-agent/src/cli/tests/mod.rs +17 -7
- package/crates/team-agent/src/cli/tests/named_address.rs +8 -2
- package/crates/team-agent/src/cli/tests/peer_allow.rs +10 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +314 -273
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +26 -15
- package/crates/team-agent/src/cli/tests/status_send.rs +707 -629
- package/crates/team-agent/src/cli/tests/verb_install_skill.rs +20 -4
- package/crates/team-agent/src/cli/tests/verb_profile.rs +46 -17
- package/crates/team-agent/src/cli/tests/verb_validate.rs +15 -3
- package/crates/team-agent/src/codex_app_server.rs +2 -5
- package/crates/team-agent/src/compiler/tests.rs +139 -33
- package/crates/team-agent/src/compiler.rs +55 -22
- package/crates/team-agent/src/conpty/backend.rs +23 -33
- package/crates/team-agent/src/coordinator/backoff.rs +2 -7
- package/crates/team-agent/src/coordinator/conpty_shim.rs +55 -67
- package/crates/team-agent/src/coordinator/health.rs +46 -31
- package/crates/team-agent/src/coordinator/mod.rs +3 -3
- package/crates/team-agent/src/coordinator/orphan.rs +22 -10
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +47 -52
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +55 -19
- package/crates/team-agent/src/coordinator/tests/basics.rs +179 -41
- package/crates/team-agent/src/coordinator/tests/daemon.rs +53 -13
- package/crates/team-agent/src/coordinator/tests/health_sync.rs +78 -19
- package/crates/team-agent/src/coordinator/tests/main_preserved.rs +61 -11
- package/crates/team-agent/src/coordinator/tests/mod.rs +33 -39
- package/crates/team-agent/src/coordinator/tests/spine.rs +52 -12
- package/crates/team-agent/src/coordinator/tests/takeover.rs +73 -15
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +50 -15
- package/crates/team-agent/src/coordinator/tests/watch.rs +74 -20
- package/crates/team-agent/src/coordinator/tick.rs +19 -1
- package/crates/team-agent/src/db/message_store.rs +138 -30
- package/crates/team-agent/src/db/migration.rs +249 -61
- package/crates/team-agent/src/db/schema.rs +303 -82
- package/crates/team-agent/src/diagnose/comms.rs +9 -2
- package/crates/team-agent/src/diagnose/mod.rs +1 -3
- package/crates/team-agent/src/diagnose/orphans.rs +79 -61
- package/crates/team-agent/src/event_log.rs +70 -16
- package/crates/team-agent/src/layout/manager.rs +15 -4
- package/crates/team-agent/src/layout/mod.rs +4 -4
- package/crates/team-agent/src/layout/overlay.rs +10 -3
- package/crates/team-agent/src/layout/placement.rs +5 -1
- package/crates/team-agent/src/layout/recovery.rs +4 -2
- package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
- package/crates/team-agent/src/layout/sessions.rs +17 -9
- package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
- package/crates/team-agent/src/layout/worker_env.rs +52 -19
- package/crates/team-agent/src/leader/helpers.rs +7 -1
- package/crates/team-agent/src/leader/lease.rs +195 -82
- package/crates/team-agent/src/leader/owner_bind.rs +55 -22
- package/crates/team-agent/src/leader/rediscover/tests.rs +88 -24
- package/crates/team-agent/src/leader/rediscover.rs +74 -25
- package/crates/team-agent/src/leader/start.rs +75 -54
- package/crates/team-agent/src/leader/takeover.rs +46 -11
- package/crates/team-agent/src/leader/tests/basics.rs +320 -167
- package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
- package/crates/team-agent/src/leader/tests/identity.rs +428 -356
- package/crates/team-agent/src/leader/tests/idle.rs +285 -254
- package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
- package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
- package/crates/team-agent/src/leader/tests/mod.rs +115 -99
- package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
- package/crates/team-agent/src/lib.rs +4 -4
- package/crates/team-agent/src/lifecycle/display.rs +7 -3
- package/crates/team-agent/src/lifecycle/launch.rs +6 -1
- package/crates/team-agent/src/lifecycle/mod.rs +9 -1
- package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
- package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
- package/crates/team-agent/src/lifecycle/tests.rs +2 -2
- package/crates/team-agent/src/main.rs +4 -4
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
- package/crates/team-agent/src/mcp_server/mod.rs +11 -2
- package/crates/team-agent/src/mcp_server/types.rs +14 -3
- package/crates/team-agent/src/mcp_server/wire.rs +230 -68
- package/crates/team-agent/src/messaging/delivery.rs +20 -18
- package/crates/team-agent/src/messaging/helpers.rs +25 -4
- package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
- package/crates/team-agent/src/messaging/mod.rs +2 -3
- package/crates/team-agent/src/messaging/selftest.rs +46 -26
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
- package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
- package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
- package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
- package/crates/team-agent/src/messaging/trust.rs +25 -2
- package/crates/team-agent/src/messaging/watchers.rs +37 -9
- package/crates/team-agent/src/model/enums.rs +65 -16
- package/crates/team-agent/src/model/ids.rs +12 -3
- package/crates/team-agent/src/model/paths.rs +28 -7
- package/crates/team-agent/src/model/permissions.rs +176 -33
- package/crates/team-agent/src/model/routing.rs +66 -20
- package/crates/team-agent/src/model/spec.rs +365 -69
- package/crates/team-agent/src/model/task_graph.rs +36 -9
- package/crates/team-agent/src/model/yaml/tests.rs +24 -6
- package/crates/team-agent/src/model/yaml.rs +7 -6
- package/crates/team-agent/src/packaging/install.rs +23 -9
- package/crates/team-agent/src/packaging/migrate.rs +5 -7
- package/crates/team-agent/src/packaging/mod.rs +9 -1
- package/crates/team-agent/src/packaging/repair.rs +13 -6
- package/crates/team-agent/src/packaging/tests.rs +63 -16
- package/crates/team-agent/src/packaging/types.rs +22 -7
- package/crates/team-agent/src/platform/argv.rs +4 -1
- package/crates/team-agent/src/platform/file_lock.rs +22 -8
- package/crates/team-agent/src/platform/process.rs +21 -22
- package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
- package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
- package/crates/team-agent/src/provider/classify.rs +127 -42
- package/crates/team-agent/src/provider/faults.rs +17 -5
- package/crates/team-agent/src/provider/helpers.rs +6 -5
- package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
- package/crates/team-agent/src/state/repository.rs +27 -14
- package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
- package/crates/team-agent/src/tmux_backend.rs +80 -44
- package/crates/team-agent/src/topology.rs +27 -20
- package/crates/team-agent/src/transport/test_support.rs +20 -23
- package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
- package/crates/team-agent/src/transport/tests/mod.rs +178 -187
- package/crates/team-agent/src/transport/tests/wire.rs +561 -525
- package/crates/team-agent/src/transport.rs +12 -17
- package/crates/team-agent/src/transport_factory.rs +29 -14
- package/package.json +4 -4
|
@@ -80,14 +80,15 @@ pub fn leader_start_plan(
|
|
|
80
80
|
if !in_tmux {
|
|
81
81
|
ensure_tmux_installed()?;
|
|
82
82
|
}
|
|
83
|
-
let existing_session =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
let existing_session =
|
|
84
|
+
if external_path && !in_tmux && !attach_existing && attach_session.is_none() {
|
|
85
|
+
match session_name.as_ref() {
|
|
86
|
+
Some(session) => tmux_session_exists(workspace, session)?,
|
|
87
|
+
None => false,
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
false
|
|
91
|
+
};
|
|
91
92
|
let mode = if !external_path && in_tmux {
|
|
92
93
|
LeaderStartMode::ExecProvider
|
|
93
94
|
} else if !external_path {
|
|
@@ -342,7 +343,9 @@ fn start_argv(
|
|
|
342
343
|
}
|
|
343
344
|
LeaderStartMode::ManagedTmuxClient => {
|
|
344
345
|
let Some(session) = session_name else {
|
|
345
|
-
return Err(LeaderError::Start(
|
|
346
|
+
return Err(LeaderError::Start(
|
|
347
|
+
"managed leader session missing".to_string(),
|
|
348
|
+
));
|
|
346
349
|
};
|
|
347
350
|
managed_client_argv(workspace, session, provider)
|
|
348
351
|
}
|
|
@@ -402,7 +405,9 @@ fn provider_command_argv(provider: Provider, provider_args: &[String]) -> Vec<St
|
|
|
402
405
|
fn normalized_provider_args(provider_args: &[String]) -> impl Iterator<Item = String> + '_ {
|
|
403
406
|
provider_args
|
|
404
407
|
.iter()
|
|
405
|
-
.skip(usize::from(
|
|
408
|
+
.skip(usize::from(
|
|
409
|
+
provider_args.first().is_some_and(|arg| arg == "--"),
|
|
410
|
+
))
|
|
406
411
|
.cloned()
|
|
407
412
|
}
|
|
408
413
|
|
|
@@ -444,10 +449,14 @@ fn execute_managed_leader_plan(
|
|
|
444
449
|
workspace: &Path,
|
|
445
450
|
) -> Result<LeaderLaunchOutcome, LeaderError> {
|
|
446
451
|
let Some(session) = plan.session_name.as_ref() else {
|
|
447
|
-
return Err(LeaderError::Start(
|
|
452
|
+
return Err(LeaderError::Start(
|
|
453
|
+
"managed leader session missing".to_string(),
|
|
454
|
+
));
|
|
448
455
|
};
|
|
449
456
|
let Some(window) = plan.leader_window.as_ref() else {
|
|
450
|
-
return Err(LeaderError::Start(
|
|
457
|
+
return Err(LeaderError::Start(
|
|
458
|
+
"managed leader window missing".to_string(),
|
|
459
|
+
));
|
|
451
460
|
};
|
|
452
461
|
// 0.5.x Phase 1d Batch 6: use the factory tmux channel helper
|
|
453
462
|
// (thin wrapper over `TmuxBackend::for_workspace`) for
|
|
@@ -854,9 +863,15 @@ fn persist_exec_provider_leader_binding(
|
|
|
854
863
|
});
|
|
855
864
|
if let Some(target) = target.as_ref() {
|
|
856
865
|
if let Some(obj) = receiver.as_object_mut() {
|
|
857
|
-
obj.insert(
|
|
866
|
+
obj.insert(
|
|
867
|
+
"session_name".to_string(),
|
|
868
|
+
serde_json::json!(target.session.as_str()),
|
|
869
|
+
);
|
|
858
870
|
if let Some(window_name) = target.window_name.as_ref() {
|
|
859
|
-
obj.insert(
|
|
871
|
+
obj.insert(
|
|
872
|
+
"window_name".to_string(),
|
|
873
|
+
serde_json::json!(window_name.as_str()),
|
|
874
|
+
);
|
|
860
875
|
}
|
|
861
876
|
}
|
|
862
877
|
}
|
|
@@ -881,7 +896,10 @@ fn persist_exec_provider_leader_binding(
|
|
|
881
896
|
serde_json::json!(identity.team_id.as_str()),
|
|
882
897
|
);
|
|
883
898
|
if let Some(target) = target.as_ref() {
|
|
884
|
-
obj.insert(
|
|
899
|
+
obj.insert(
|
|
900
|
+
"session_name".to_string(),
|
|
901
|
+
serde_json::json!(target.session.as_str()),
|
|
902
|
+
);
|
|
885
903
|
}
|
|
886
904
|
if let Some(socket) = socket.as_ref() {
|
|
887
905
|
obj.insert("tmux_endpoint".to_string(), serde_json::json!(socket));
|
|
@@ -1006,10 +1024,9 @@ fn refresh_managed_leader_provider_binding(
|
|
|
1006
1024
|
plan: &LeaderStartPlan,
|
|
1007
1025
|
workspace: &Path,
|
|
1008
1026
|
) -> Result<(), LeaderError> {
|
|
1009
|
-
let identity = plan
|
|
1010
|
-
.
|
|
1011
|
-
|
|
1012
|
-
.ok_or_else(|| LeaderError::Start("managed leader re-entry identity missing".to_string()))?;
|
|
1027
|
+
let identity = plan.identity.as_ref().ok_or_else(|| {
|
|
1028
|
+
LeaderError::Start("managed leader re-entry identity missing".to_string())
|
|
1029
|
+
})?;
|
|
1013
1030
|
let pane = std::env::var("TMUX_PANE")
|
|
1014
1031
|
.ok()
|
|
1015
1032
|
.filter(|value| !value.trim().is_empty())
|
|
@@ -1029,8 +1046,16 @@ fn refresh_managed_leader_provider_binding(
|
|
|
1029
1046
|
let existing_epoch = state
|
|
1030
1047
|
.pointer(&format!("/teams/{team_key}/owner_epoch"))
|
|
1031
1048
|
.and_then(serde_json::Value::as_u64)
|
|
1032
|
-
.or_else(||
|
|
1033
|
-
|
|
1049
|
+
.or_else(|| {
|
|
1050
|
+
existing_owner
|
|
1051
|
+
.get("owner_epoch")
|
|
1052
|
+
.and_then(serde_json::Value::as_u64)
|
|
1053
|
+
})
|
|
1054
|
+
.or_else(|| {
|
|
1055
|
+
existing_receiver
|
|
1056
|
+
.get("owner_epoch")
|
|
1057
|
+
.and_then(serde_json::Value::as_u64)
|
|
1058
|
+
})
|
|
1034
1059
|
.unwrap_or(0);
|
|
1035
1060
|
let now = chrono::Utc::now().to_rfc3339();
|
|
1036
1061
|
let provider = serde_json::to_value(plan.provider)?;
|
|
@@ -1050,9 +1075,15 @@ fn refresh_managed_leader_provider_binding(
|
|
|
1050
1075
|
obj.entry("leader_session_uuid".to_string())
|
|
1051
1076
|
.or_insert_with(|| serde_json::json!(identity.leader_session_uuid.as_str()));
|
|
1052
1077
|
if let Some(target) = target.as_ref() {
|
|
1053
|
-
obj.insert(
|
|
1078
|
+
obj.insert(
|
|
1079
|
+
"session_name".to_string(),
|
|
1080
|
+
serde_json::json!(target.session.as_str()),
|
|
1081
|
+
);
|
|
1054
1082
|
if let Some(window_name) = target.window_name.as_ref() {
|
|
1055
|
-
obj.insert(
|
|
1083
|
+
obj.insert(
|
|
1084
|
+
"window_name".to_string(),
|
|
1085
|
+
serde_json::json!(window_name.as_str()),
|
|
1086
|
+
);
|
|
1056
1087
|
}
|
|
1057
1088
|
if let Some(pane_pid) = target.pane_pid {
|
|
1058
1089
|
obj.insert("pane_pid".to_string(), serde_json::json!(pane_pid));
|
|
@@ -1117,7 +1148,9 @@ fn persist_external_leader_topology_marker(
|
|
|
1117
1148
|
.unwrap_or_else(|_| serde_json::json!({}));
|
|
1118
1149
|
if let Some(obj) = state.as_object_mut() {
|
|
1119
1150
|
obj.entry("workspace".to_string()).or_insert_with(|| {
|
|
1120
|
-
serde_json::json!(resolve_workspace_for_hash(workspace)
|
|
1151
|
+
serde_json::json!(resolve_workspace_for_hash(workspace)
|
|
1152
|
+
.to_string_lossy()
|
|
1153
|
+
.to_string())
|
|
1121
1154
|
});
|
|
1122
1155
|
obj.entry("active_team_key".to_string())
|
|
1123
1156
|
.or_insert_with(|| serde_json::json!(identity.team_id.as_str()));
|
|
@@ -1231,12 +1264,7 @@ fn spawn_managed_provider_startup_prompt_handler(
|
|
|
1231
1264
|
// grep-visibility. Semantics unchanged.
|
|
1232
1265
|
let transport = crate::transport_factory::tmux_workspace_transport(&workspace);
|
|
1233
1266
|
let _ = handle_exec_provider_startup_prompts(
|
|
1234
|
-
provider,
|
|
1235
|
-
&workspace,
|
|
1236
|
-
&pane_id,
|
|
1237
|
-
&transport,
|
|
1238
|
-
30,
|
|
1239
|
-
0.5,
|
|
1267
|
+
provider, &workspace, &pane_id, &transport, 30, 0.5,
|
|
1240
1268
|
);
|
|
1241
1269
|
});
|
|
1242
1270
|
}
|
|
@@ -1671,10 +1699,8 @@ mod tests {
|
|
|
1671
1699
|
|
|
1672
1700
|
#[test]
|
|
1673
1701
|
fn external_exec_provider_persists_topology_before_provider_exec() {
|
|
1674
|
-
let workspace =
|
|
1675
|
-
"ta-external-pre-exec-{}",
|
|
1676
|
-
std::process::id()
|
|
1677
|
-
));
|
|
1702
|
+
let workspace =
|
|
1703
|
+
std::env::temp_dir().join(format!("ta-external-pre-exec-{}", std::process::id()));
|
|
1678
1704
|
std::fs::create_dir_all(&workspace).unwrap();
|
|
1679
1705
|
let state_path = crate::state::persist::runtime_state_path(&workspace);
|
|
1680
1706
|
let command = format!(
|
|
@@ -1716,17 +1742,18 @@ mod tests {
|
|
|
1716
1742
|
assert_eq!(outcome.status, crate::leader::LeaderLaunchStatus::Exited);
|
|
1717
1743
|
let state = crate::state::persist::load_runtime_state(&workspace).unwrap();
|
|
1718
1744
|
assert_eq!(state["is_external_leader"], serde_json::json!(true));
|
|
1719
|
-
assert_eq!(
|
|
1745
|
+
assert_eq!(
|
|
1746
|
+
state["teams"]["current"]["is_external_leader"],
|
|
1747
|
+
serde_json::json!(true)
|
|
1748
|
+
);
|
|
1720
1749
|
let _ = std::fs::remove_dir_all(&workspace);
|
|
1721
1750
|
}
|
|
1722
1751
|
|
|
1723
1752
|
#[test]
|
|
1724
1753
|
#[serial_test::serial(env)]
|
|
1725
1754
|
fn default_exec_provider_persists_current_pane_binding_before_provider_exec() {
|
|
1726
|
-
let workspace =
|
|
1727
|
-
"ta-current-pane-pre-exec-{}",
|
|
1728
|
-
std::process::id()
|
|
1729
|
-
));
|
|
1755
|
+
let workspace =
|
|
1756
|
+
std::env::temp_dir().join(format!("ta-current-pane-pre-exec-{}", std::process::id()));
|
|
1730
1757
|
std::fs::create_dir_all(&workspace).unwrap();
|
|
1731
1758
|
let state_path = crate::state::persist::runtime_state_path(&workspace);
|
|
1732
1759
|
let command = format!(
|
|
@@ -1834,14 +1861,13 @@ mod tests {
|
|
|
1834
1861
|
// managed path must still match LEADER_SESSION_PREFIX so the
|
|
1835
1862
|
// shutdown / cli/mod.rs prefix matchers still classify it as a
|
|
1836
1863
|
// leader session.
|
|
1837
|
-
let workspace =
|
|
1838
|
-
"ta_rs_mgr_prefix_{}",
|
|
1839
|
-
std::process::id()
|
|
1840
|
-
));
|
|
1864
|
+
let workspace =
|
|
1865
|
+
std::env::temp_dir().join(format!("ta_rs_mgr_prefix_{}", std::process::id()));
|
|
1841
1866
|
let _ = std::fs::create_dir_all(&workspace);
|
|
1842
1867
|
let name = super::managed_leader_session_name(Provider::ClaudeCode, &workspace);
|
|
1843
1868
|
assert!(
|
|
1844
|
-
name.as_str()
|
|
1869
|
+
name.as_str()
|
|
1870
|
+
.starts_with(super::super::LEADER_SESSION_PREFIX),
|
|
1845
1871
|
"managed session name must carry LEADER_SESSION_PREFIX (`{}`); \
|
|
1846
1872
|
got `{}`",
|
|
1847
1873
|
super::super::LEADER_SESSION_PREFIX,
|
|
@@ -1859,10 +1885,8 @@ mod tests {
|
|
|
1859
1885
|
fn managed_leader_session_name_is_unique_per_call() {
|
|
1860
1886
|
// Two consecutive calls must return DIFFERENT names — the
|
|
1861
1887
|
// per-launch nonce defeats the mirror-session bug.
|
|
1862
|
-
let workspace =
|
|
1863
|
-
"ta_rs_mgr_unique_{}",
|
|
1864
|
-
std::process::id()
|
|
1865
|
-
));
|
|
1888
|
+
let workspace =
|
|
1889
|
+
std::env::temp_dir().join(format!("ta_rs_mgr_unique_{}", std::process::id()));
|
|
1866
1890
|
let _ = std::fs::create_dir_all(&workspace);
|
|
1867
1891
|
let a = super::managed_leader_session_name(Provider::ClaudeCode, &workspace);
|
|
1868
1892
|
// Sleep > 1ns to ensure epoch_nanos advances even on coarse clocks.
|
|
@@ -1885,10 +1909,8 @@ mod tests {
|
|
|
1885
1909
|
// External / attach paths must keep the stable workspace-keyed
|
|
1886
1910
|
// name — `--attach-session <name>` reattach would break if the
|
|
1887
1911
|
// name varied per call.
|
|
1888
|
-
let workspace =
|
|
1889
|
-
"ta_rs_lsn_stable_{}",
|
|
1890
|
-
std::process::id()
|
|
1891
|
-
));
|
|
1912
|
+
let workspace =
|
|
1913
|
+
std::env::temp_dir().join(format!("ta_rs_lsn_stable_{}", std::process::id()));
|
|
1892
1914
|
let _ = std::fs::create_dir_all(&workspace);
|
|
1893
1915
|
let a = super::leader_session_name(Provider::ClaudeCode, &workspace);
|
|
1894
1916
|
let b = super::leader_session_name(Provider::ClaudeCode, &workspace);
|
|
@@ -1911,8 +1933,7 @@ mod tests {
|
|
|
1911
1933
|
fn managed_path_uses_nonce_session_name_grep_guard() {
|
|
1912
1934
|
let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
1913
1935
|
let start_rs = manifest.join("src").join("leader").join("start.rs");
|
|
1914
|
-
let contents =
|
|
1915
|
-
std::fs::read_to_string(&start_rs).expect("read leader/start.rs");
|
|
1936
|
+
let contents = std::fs::read_to_string(&start_rs).expect("read leader/start.rs");
|
|
1916
1937
|
// leader_start_plan must dispatch to managed_leader_session_name
|
|
1917
1938
|
// on the managed path.
|
|
1918
1939
|
let start = contents
|
|
@@ -34,7 +34,11 @@ pub fn build_idle_nodes(
|
|
|
34
34
|
) {
|
|
35
35
|
continue;
|
|
36
36
|
}
|
|
37
|
-
let Some(provider) = agent
|
|
37
|
+
let Some(provider) = agent
|
|
38
|
+
.get("provider")
|
|
39
|
+
.and_then(Value::as_str)
|
|
40
|
+
.and_then(parse_provider)
|
|
41
|
+
else {
|
|
38
42
|
continue;
|
|
39
43
|
};
|
|
40
44
|
let rollout_path = agent
|
|
@@ -50,7 +54,10 @@ pub fn build_idle_nodes(
|
|
|
50
54
|
turn_id: classification.turn_id,
|
|
51
55
|
annotations: classification.annotations,
|
|
52
56
|
provider: Some(provider),
|
|
53
|
-
auth_mode: agent
|
|
57
|
+
auth_mode: agent
|
|
58
|
+
.get("auth_mode")
|
|
59
|
+
.and_then(Value::as_str)
|
|
60
|
+
.map(str::to_string),
|
|
54
61
|
rollout_path,
|
|
55
62
|
});
|
|
56
63
|
}
|
|
@@ -72,12 +79,20 @@ pub fn leader_node(
|
|
|
72
79
|
let provider = leader
|
|
73
80
|
.and_then(|l| l.get("provider"))
|
|
74
81
|
.and_then(Value::as_str)
|
|
75
|
-
.or_else(||
|
|
82
|
+
.or_else(|| {
|
|
83
|
+
receiver
|
|
84
|
+
.and_then(|r| r.get("provider"))
|
|
85
|
+
.and_then(Value::as_str)
|
|
86
|
+
})
|
|
76
87
|
.and_then(parse_provider);
|
|
77
88
|
let rollout_path = leader
|
|
78
89
|
.and_then(|l| l.get("rollout_path"))
|
|
79
90
|
.and_then(Value::as_str)
|
|
80
|
-
.or_else(||
|
|
91
|
+
.or_else(|| {
|
|
92
|
+
receiver
|
|
93
|
+
.and_then(|r| r.get("rollout_path"))
|
|
94
|
+
.and_then(Value::as_str)
|
|
95
|
+
})
|
|
81
96
|
.map(|p| RolloutPath::new(PathBuf::from(p)));
|
|
82
97
|
let (Some(provider), Some(rollout_path)) = (provider, rollout_path) else {
|
|
83
98
|
return Ok(None);
|
|
@@ -108,7 +123,10 @@ pub fn classify_provider_turn_state(
|
|
|
108
123
|
event_log: Option<&crate::event_log::EventLog>,
|
|
109
124
|
) -> Result<TurnClassification, LeaderError> {
|
|
110
125
|
let classification = classifier.classify(provider, session_log_text)?;
|
|
111
|
-
if matches!(
|
|
126
|
+
if matches!(
|
|
127
|
+
classification.state,
|
|
128
|
+
TurnState::Unknown | TurnState::Abnormal
|
|
129
|
+
) {
|
|
112
130
|
if let Some(log) = event_log {
|
|
113
131
|
log.write(
|
|
114
132
|
LeaderEvent::IdleTakeoverClassify.name(),
|
|
@@ -171,7 +189,9 @@ pub fn evaluate_takeover_reminder(
|
|
|
171
189
|
.collect();
|
|
172
190
|
Ok(TakeoverReminderResult {
|
|
173
191
|
should_ping: true,
|
|
174
|
-
message: Some(
|
|
192
|
+
message: Some(
|
|
193
|
+
"All active nodes appear idle; leader takeover may be appropriate.".to_string(),
|
|
194
|
+
),
|
|
175
195
|
interrupted_nodes,
|
|
176
196
|
reason: Some("all_idle_debounce_elapsed".to_string()),
|
|
177
197
|
})
|
|
@@ -211,19 +231,34 @@ pub fn should_reread(
|
|
|
211
231
|
) -> RereadDecision {
|
|
212
232
|
let _ = last_mtime;
|
|
213
233
|
let Some(current) = current_mtime else {
|
|
214
|
-
return RereadDecision {
|
|
234
|
+
return RereadDecision {
|
|
235
|
+
reread: false,
|
|
236
|
+
reason: RereadReason::NoFile,
|
|
237
|
+
};
|
|
215
238
|
};
|
|
216
239
|
let Some(classified) = last_classified_mtime else {
|
|
217
|
-
return RereadDecision {
|
|
240
|
+
return RereadDecision {
|
|
241
|
+
reread: true,
|
|
242
|
+
reason: RereadReason::NeverClassified,
|
|
243
|
+
};
|
|
218
244
|
};
|
|
219
245
|
if current != classified {
|
|
220
|
-
return RereadDecision {
|
|
246
|
+
return RereadDecision {
|
|
247
|
+
reread: true,
|
|
248
|
+
reason: RereadReason::FileChanged,
|
|
249
|
+
};
|
|
221
250
|
}
|
|
222
251
|
let silent_for = (now - current).max(0.0);
|
|
223
252
|
if silent_for >= debounce_seconds {
|
|
224
|
-
RereadDecision {
|
|
253
|
+
RereadDecision {
|
|
254
|
+
reread: false,
|
|
255
|
+
reason: RereadReason::QuiescentAlreadyClassified,
|
|
256
|
+
}
|
|
225
257
|
} else {
|
|
226
|
-
RereadDecision {
|
|
258
|
+
RereadDecision {
|
|
259
|
+
reread: false,
|
|
260
|
+
reason: RereadReason::Unchanged,
|
|
261
|
+
}
|
|
227
262
|
}
|
|
228
263
|
}
|
|
229
264
|
|