@team-agent/installer 0.5.42 → 0.5.44
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 +126 -16
- 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 +51 -56
- 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/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 +87 -19
- package/crates/team-agent/src/leader/helpers.rs +7 -1
- package/crates/team-agent/src/leader/lease.rs +199 -89
- package/crates/team-agent/src/leader/owner_bind.rs +55 -22
- package/crates/team-agent/src/leader/provider_attribution.rs +25 -6
- 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/registry.rs +1 -1
- 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 +55 -15
- 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/common.rs +6 -2
- 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/phase_golden.rs +3 -0
- 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 +54 -24
- 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/persist.rs +28 -0
- package/crates/team-agent/src/state/repository.rs +8 -3
- 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 +40 -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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
use super::*;
|
|
2
1
|
use super::launch_spawn::{seed_healthy_coordinator, DELEG_ROLE_ALPHA, DELEG_ROLE_BRAVO};
|
|
2
|
+
use super::*;
|
|
3
3
|
|
|
4
4
|
// ═════════════════════════════════════════════════════════════════════════
|
|
5
5
|
|
|
@@ -38,30 +38,86 @@ impl crate::transport::Transport for LaneTransport {
|
|
|
38
38
|
fn kind(&self) -> crate::transport::BackendKind {
|
|
39
39
|
crate::transport::BackendKind::Tmux
|
|
40
40
|
}
|
|
41
|
-
fn spawn_first(
|
|
42
|
-
self
|
|
43
|
-
|
|
41
|
+
fn spawn_first(
|
|
42
|
+
&self,
|
|
43
|
+
session: &crate::transport::SessionName,
|
|
44
|
+
window: &crate::transport::WindowName,
|
|
45
|
+
argv: &[String],
|
|
46
|
+
_c: &std::path::Path,
|
|
47
|
+
_e: &std::collections::BTreeMap<String, String>,
|
|
48
|
+
) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
|
|
49
|
+
self.spawns
|
|
50
|
+
.lock()
|
|
51
|
+
.unwrap()
|
|
52
|
+
.push(("spawn_first".to_string(), argv.to_vec()));
|
|
53
|
+
Ok(crate::transport::SpawnResult {
|
|
54
|
+
pane_id: crate::transport::PaneId::new(format!("%{}", window.as_str())),
|
|
55
|
+
session: session.clone(),
|
|
56
|
+
window: window.clone(),
|
|
57
|
+
child_pid: None,
|
|
58
|
+
})
|
|
44
59
|
}
|
|
45
|
-
fn spawn_into(
|
|
46
|
-
self
|
|
47
|
-
|
|
60
|
+
fn spawn_into(
|
|
61
|
+
&self,
|
|
62
|
+
session: &crate::transport::SessionName,
|
|
63
|
+
window: &crate::transport::WindowName,
|
|
64
|
+
argv: &[String],
|
|
65
|
+
_c: &std::path::Path,
|
|
66
|
+
_e: &std::collections::BTreeMap<String, String>,
|
|
67
|
+
) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
|
|
68
|
+
self.spawns
|
|
69
|
+
.lock()
|
|
70
|
+
.unwrap()
|
|
71
|
+
.push(("spawn_into".to_string(), argv.to_vec()));
|
|
72
|
+
Ok(crate::transport::SpawnResult {
|
|
73
|
+
pane_id: crate::transport::PaneId::new(format!("%{}", window.as_str())),
|
|
74
|
+
session: session.clone(),
|
|
75
|
+
window: window.clone(),
|
|
76
|
+
child_pid: None,
|
|
77
|
+
})
|
|
48
78
|
}
|
|
49
|
-
fn inject(
|
|
79
|
+
fn inject(
|
|
80
|
+
&self,
|
|
81
|
+
_t: &crate::transport::Target,
|
|
82
|
+
_p: &crate::transport::InjectPayload,
|
|
83
|
+
_s: crate::transport::Key,
|
|
84
|
+
_b: bool,
|
|
85
|
+
) -> Result<crate::transport::InjectReport, crate::transport::TransportError> {
|
|
50
86
|
unimplemented!("LaneTransport::inject not reached by stop/reset/remove/fork")
|
|
51
87
|
}
|
|
52
|
-
fn send_keys(
|
|
88
|
+
fn send_keys(
|
|
89
|
+
&self,
|
|
90
|
+
_t: &crate::transport::Target,
|
|
91
|
+
_k: &[crate::transport::Key],
|
|
92
|
+
) -> Result<(), crate::transport::TransportError> {
|
|
53
93
|
Ok(())
|
|
54
94
|
}
|
|
55
|
-
fn capture(
|
|
56
|
-
|
|
95
|
+
fn capture(
|
|
96
|
+
&self,
|
|
97
|
+
_t: &crate::transport::Target,
|
|
98
|
+
r: crate::transport::CaptureRange,
|
|
99
|
+
) -> Result<crate::transport::CapturedText, crate::transport::TransportError> {
|
|
100
|
+
Ok(crate::transport::CapturedText {
|
|
101
|
+
text: String::new(),
|
|
102
|
+
range: r,
|
|
103
|
+
})
|
|
57
104
|
}
|
|
58
|
-
fn query(
|
|
105
|
+
fn query(
|
|
106
|
+
&self,
|
|
107
|
+
_t: &crate::transport::Target,
|
|
108
|
+
_f: crate::transport::PaneField,
|
|
109
|
+
) -> Result<Option<String>, crate::transport::TransportError> {
|
|
59
110
|
Ok(None)
|
|
60
111
|
}
|
|
61
|
-
fn liveness(
|
|
112
|
+
fn liveness(
|
|
113
|
+
&self,
|
|
114
|
+
_p: &crate::transport::PaneId,
|
|
115
|
+
) -> Result<crate::model::enums::PaneLiveness, crate::transport::TransportError> {
|
|
62
116
|
Ok(crate::model::enums::PaneLiveness::Unknown)
|
|
63
117
|
}
|
|
64
|
-
fn list_targets(
|
|
118
|
+
fn list_targets(
|
|
119
|
+
&self,
|
|
120
|
+
) -> Result<Vec<crate::transport::PaneInfo>, crate::transport::TransportError> {
|
|
65
121
|
Ok(self
|
|
66
122
|
.windows
|
|
67
123
|
.iter()
|
|
@@ -80,31 +136,57 @@ impl crate::transport::Transport for LaneTransport {
|
|
|
80
136
|
})
|
|
81
137
|
.collect())
|
|
82
138
|
}
|
|
83
|
-
fn has_session(
|
|
139
|
+
fn has_session(
|
|
140
|
+
&self,
|
|
141
|
+
_s: &crate::transport::SessionName,
|
|
142
|
+
) -> Result<bool, crate::transport::TransportError> {
|
|
84
143
|
Ok(true)
|
|
85
144
|
}
|
|
86
|
-
fn list_windows(
|
|
145
|
+
fn list_windows(
|
|
146
|
+
&self,
|
|
147
|
+
s: &crate::transport::SessionName,
|
|
148
|
+
) -> Result<Vec<crate::transport::WindowName>, crate::transport::TransportError> {
|
|
87
149
|
if s.as_str() == self.session {
|
|
88
|
-
Ok(self
|
|
150
|
+
Ok(self
|
|
151
|
+
.windows
|
|
152
|
+
.iter()
|
|
153
|
+
.map(|w| crate::transport::WindowName::new(w.as_str()))
|
|
154
|
+
.collect())
|
|
89
155
|
} else {
|
|
90
156
|
Ok(Vec::new())
|
|
91
157
|
}
|
|
92
158
|
}
|
|
93
|
-
fn set_session_env(
|
|
159
|
+
fn set_session_env(
|
|
160
|
+
&self,
|
|
161
|
+
_s: &crate::transport::SessionName,
|
|
162
|
+
_k: &str,
|
|
163
|
+
_v: &str,
|
|
164
|
+
) -> Result<crate::transport::SetEnvOutcome, crate::transport::TransportError> {
|
|
94
165
|
Ok(crate::transport::SetEnvOutcome::Applied)
|
|
95
166
|
}
|
|
96
|
-
fn kill_session(
|
|
167
|
+
fn kill_session(
|
|
168
|
+
&self,
|
|
169
|
+
_s: &crate::transport::SessionName,
|
|
170
|
+
) -> Result<(), crate::transport::TransportError> {
|
|
97
171
|
Ok(())
|
|
98
172
|
}
|
|
99
|
-
fn kill_window(
|
|
173
|
+
fn kill_window(
|
|
174
|
+
&self,
|
|
175
|
+
t: &crate::transport::Target,
|
|
176
|
+
) -> Result<(), crate::transport::TransportError> {
|
|
100
177
|
let name = match t {
|
|
101
178
|
crate::transport::Target::Pane(p) => p.as_str().to_string(),
|
|
102
|
-
crate::transport::Target::SessionWindow { session, window } =>
|
|
179
|
+
crate::transport::Target::SessionWindow { session, window } => {
|
|
180
|
+
format!("{}:{}", session.as_str(), window.as_str())
|
|
181
|
+
}
|
|
103
182
|
};
|
|
104
183
|
self.killed.lock().unwrap().push(name);
|
|
105
184
|
Ok(())
|
|
106
185
|
}
|
|
107
|
-
fn attach_session(
|
|
186
|
+
fn attach_session(
|
|
187
|
+
&self,
|
|
188
|
+
_s: &crate::transport::SessionName,
|
|
189
|
+
) -> Result<crate::transport::AttachOutcome, crate::transport::TransportError> {
|
|
108
190
|
Ok(crate::transport::AttachOutcome::Attached)
|
|
109
191
|
}
|
|
110
192
|
}
|
|
@@ -114,7 +196,11 @@ impl crate::transport::Transport for LaneTransport {
|
|
|
114
196
|
fn lanea_ws_agents(agents: serde_json::Value) -> PathBuf {
|
|
115
197
|
let ws = temp_ws().join("laneav2");
|
|
116
198
|
std::fs::create_dir_all(ws.join("agents")).unwrap();
|
|
117
|
-
std::fs::write(
|
|
199
|
+
std::fs::write(
|
|
200
|
+
ws.join("TEAM.md"),
|
|
201
|
+
"---\nname: laneateam\nobjective: Lane A v2 probe.\nprovider: codex\n---\n\nteam.\n",
|
|
202
|
+
)
|
|
203
|
+
.unwrap();
|
|
118
204
|
std::fs::write(ws.join("agents").join("alpha.md"), DELEG_ROLE_ALPHA).unwrap();
|
|
119
205
|
std::fs::write(ws.join("agents").join("bravo.md"), DELEG_ROLE_BRAVO).unwrap();
|
|
120
206
|
let spec = crate::compiler::compile_team(&ws).expect("compile lane-A v2 team");
|
|
@@ -128,11 +214,24 @@ fn lanea_ws_agents(agents: serde_json::Value) -> PathBuf {
|
|
|
128
214
|
.replace("default_assignee: \"alpha\"", "default_assignee: \"bravo\"")
|
|
129
215
|
.replace("assign_to: \"alpha\"", "assign_to: \"bravo\"")
|
|
130
216
|
.replace("assignee: \"alpha\"", "assignee: \"bravo\"");
|
|
131
|
-
assert!(
|
|
132
|
-
|
|
133
|
-
|
|
217
|
+
assert!(
|
|
218
|
+
!yaml.contains("default_assignee: \"alpha\""),
|
|
219
|
+
"fixture unroute: default_assignee still alpha"
|
|
220
|
+
);
|
|
221
|
+
assert!(
|
|
222
|
+
!yaml.contains("assign_to: \"alpha\""),
|
|
223
|
+
"fixture unroute: a routing rule still assign_to alpha"
|
|
224
|
+
);
|
|
225
|
+
assert!(
|
|
226
|
+
!yaml.contains("assignee: \"alpha\""),
|
|
227
|
+
"fixture unroute: task still assignee alpha"
|
|
228
|
+
);
|
|
134
229
|
std::fs::write(ws.join("team.spec.yaml"), yaml).unwrap();
|
|
135
|
-
crate::state::persist::save_runtime_state(
|
|
230
|
+
crate::state::persist::save_runtime_state(
|
|
231
|
+
&ws,
|
|
232
|
+
&json!({ "session_name": "team-laneateam", "agents": agents }),
|
|
233
|
+
)
|
|
234
|
+
.unwrap();
|
|
136
235
|
ws
|
|
137
236
|
}
|
|
138
237
|
|
|
@@ -142,7 +241,11 @@ fn lanea_ws_agents(agents: serde_json::Value) -> PathBuf {
|
|
|
142
241
|
fn lanea_one_agent_ws(alpha_status: &str) -> PathBuf {
|
|
143
242
|
let ws = temp_ws().join("lanea1");
|
|
144
243
|
std::fs::create_dir_all(ws.join("agents")).unwrap();
|
|
145
|
-
std::fs::write(
|
|
244
|
+
std::fs::write(
|
|
245
|
+
ws.join("TEAM.md"),
|
|
246
|
+
"---\nname: laneateam\nobjective: Lane A one-agent probe.\nprovider: codex\n---\n\nteam.\n",
|
|
247
|
+
)
|
|
248
|
+
.unwrap();
|
|
146
249
|
std::fs::write(ws.join("agents").join("alpha.md"), DELEG_ROLE_ALPHA).unwrap();
|
|
147
250
|
let spec = crate::compiler::compile_team(&ws).expect("compile 1-agent team");
|
|
148
251
|
std::fs::write(ws.join("team.spec.yaml"), crate::model::yaml::dumps(&spec)).unwrap();
|
|
@@ -156,8 +259,14 @@ fn lanea_one_agent_ws(alpha_status: &str) -> PathBuf {
|
|
|
156
259
|
fn set_context_state_file(ws: &std::path::Path, name: &str) {
|
|
157
260
|
let p = ws.join("team.spec.yaml");
|
|
158
261
|
let text = std::fs::read_to_string(&p).unwrap();
|
|
159
|
-
let replaced = text.replace(
|
|
160
|
-
|
|
262
|
+
let replaced = text.replace(
|
|
263
|
+
"state_file: \"team_state.md\"",
|
|
264
|
+
&format!("state_file: \"{name}\""),
|
|
265
|
+
);
|
|
266
|
+
assert_ne!(
|
|
267
|
+
replaced, text,
|
|
268
|
+
"expected to rewrite context.state_file in the compiled spec; got:\n{text}"
|
|
269
|
+
);
|
|
161
270
|
std::fs::write(&p, replaced).unwrap();
|
|
162
271
|
}
|
|
163
272
|
|
|
@@ -167,7 +276,11 @@ fn set_context_state_file(ws: &std::path::Path, name: &str) {
|
|
|
167
276
|
pub(super) fn fork_ws(alpha_role: &str) -> PathBuf {
|
|
168
277
|
let ws = temp_ws().join("forkv2");
|
|
169
278
|
std::fs::create_dir_all(ws.join("agents")).unwrap();
|
|
170
|
-
std::fs::write(
|
|
279
|
+
std::fs::write(
|
|
280
|
+
ws.join("TEAM.md"),
|
|
281
|
+
"---\nname: laneateam\nobjective: Fork v2 probe.\nprovider: codex\n---\n\nteam.\n",
|
|
282
|
+
)
|
|
283
|
+
.unwrap();
|
|
171
284
|
std::fs::write(ws.join("agents").join("alpha.md"), alpha_role).unwrap();
|
|
172
285
|
std::fs::write(ws.join("agents").join("bravo.md"), DELEG_ROLE_BRAVO).unwrap();
|
|
173
286
|
let spec = crate::compiler::compile_team(&ws).expect("compile fork team");
|
|
@@ -217,9 +330,12 @@ pub(super) fn fork_ws(alpha_role: &str) -> PathBuf {
|
|
|
217
330
|
// must skip the kill (killed empty) and set stopped=false; today kill_window IS called + stopped=true.
|
|
218
331
|
#[test]
|
|
219
332
|
fn lanea_stop_window_absent_returns_stopped_false_no_kill() {
|
|
220
|
-
let ws = lanea_ws_agents(
|
|
333
|
+
let ws = lanea_ws_agents(
|
|
334
|
+
json!({ "alpha": { "status": "running", "provider": "codex", "window": "alpha" } }),
|
|
335
|
+
);
|
|
221
336
|
let tx = LaneTransport::new("team-laneateam", &[]); // alpha's window is ABSENT
|
|
222
|
-
let report = stop_agent_with_transport(&ws, &aid("alpha"), None, &tx)
|
|
337
|
+
let report = stop_agent_with_transport(&ws, &aid("alpha"), None, &tx)
|
|
338
|
+
.expect("window-absent stop is a clean Ok, not an Err (golden returns stopped:false)");
|
|
223
339
|
assert!(
|
|
224
340
|
!report.stopped,
|
|
225
341
|
"golden operations.py:81-99: an ABSENT tmux window => stopped=FALSE (kill skipped); Rust hardcodes stopped:true"
|
|
@@ -237,10 +353,16 @@ fn lanea_stop_window_absent_returns_stopped_false_no_kill() {
|
|
|
237
353
|
// window-gate fix regressing it.
|
|
238
354
|
#[test]
|
|
239
355
|
fn lanea_stop_window_present_kills_and_stopped_true() {
|
|
240
|
-
let ws = lanea_ws_agents(
|
|
356
|
+
let ws = lanea_ws_agents(
|
|
357
|
+
json!({ "alpha": { "status": "running", "provider": "codex", "window": "alpha" } }),
|
|
358
|
+
);
|
|
241
359
|
let tx = LaneTransport::new("team-laneateam", &["alpha"]); // window present
|
|
242
|
-
let report =
|
|
243
|
-
|
|
360
|
+
let report =
|
|
361
|
+
stop_agent_with_transport(&ws, &aid("alpha"), None, &tx).expect("present-window stop ok");
|
|
362
|
+
assert!(
|
|
363
|
+
report.stopped,
|
|
364
|
+
"a present window must be killed => stopped=true"
|
|
365
|
+
);
|
|
244
366
|
assert_eq!(
|
|
245
367
|
tx.killed(),
|
|
246
368
|
vec!["team-laneateam:alpha".to_string()],
|
|
@@ -325,7 +447,10 @@ fn lanea_remove_dynamic_agent_removable_without_from_spec() {
|
|
|
325
447
|
);
|
|
326
448
|
let state = crate::state::persist::load_runtime_state(&ws).expect("load state");
|
|
327
449
|
assert!(
|
|
328
|
-
state
|
|
450
|
+
state
|
|
451
|
+
.get("agents")
|
|
452
|
+
.and_then(serde_json::Value::as_object)
|
|
453
|
+
.is_some_and(|a| !a.contains_key("alpha")),
|
|
329
454
|
"the dynamic agent must actually be removed from state.agents; got {result:?}"
|
|
330
455
|
);
|
|
331
456
|
}
|
|
@@ -337,9 +462,14 @@ fn lanea_remove_dynamic_agent_removable_without_from_spec() {
|
|
|
337
462
|
// a from_spec refusal. RED: an unknown agent with from_spec=false must surface "unknown worker".
|
|
338
463
|
#[test]
|
|
339
464
|
fn lanea_remove_unknown_agent_precedes_from_spec_refusal() {
|
|
340
|
-
let ws = lanea_ws_agents(
|
|
465
|
+
let ws = lanea_ws_agents(
|
|
466
|
+
json!({ "alpha": { "status": "stopped", "provider": "codex", "window": "alpha" } }),
|
|
467
|
+
);
|
|
341
468
|
let tx = LaneTransport::new("team-laneateam", &[]);
|
|
342
|
-
let text = format!(
|
|
469
|
+
let text = format!(
|
|
470
|
+
"{:?}",
|
|
471
|
+
remove_agent_with_transport(&ws, &aid("ghost"), false, false, None, &tx)
|
|
472
|
+
);
|
|
343
473
|
assert!(
|
|
344
474
|
text.contains("unknown worker"),
|
|
345
475
|
"golden agents.py:41-54: the unknown-worker check precedes the from_spec refusal; an unknown agent must \
|
|
@@ -360,8 +490,10 @@ fn lanea_remove_writes_markdown_team_state_not_json() {
|
|
|
360
490
|
"bravo": { "status": "running", "provider": "codex", "window": "bravo" }
|
|
361
491
|
}));
|
|
362
492
|
let tx = LaneTransport::new("team-laneateam", &[]);
|
|
363
|
-
let _ =
|
|
364
|
-
|
|
493
|
+
let _ =
|
|
494
|
+
remove_agent_with_transport(&ws, &aid("alpha"), true, true, None, &tx).expect("remove ok");
|
|
495
|
+
let team_state =
|
|
496
|
+
std::fs::read_to_string(ws.join("team_state.md")).expect("team_state.md written");
|
|
365
497
|
assert!(
|
|
366
498
|
team_state.starts_with("# Team State"),
|
|
367
499
|
"golden write_team_state emits a Markdown document starting '# Team State'; Rust dumps JSON; got:\n{team_state}"
|
|
@@ -370,7 +502,10 @@ fn lanea_remove_writes_markdown_team_state_not_json() {
|
|
|
370
502
|
!team_state.trim_start().starts_with('{'),
|
|
371
503
|
"team_state.md must NOT be a JSON dump of runtime state; got:\n{team_state}"
|
|
372
504
|
);
|
|
373
|
-
assert!(
|
|
505
|
+
assert!(
|
|
506
|
+
team_state.contains("## Agents"),
|
|
507
|
+
"golden has a '## Agents' section; got:\n{team_state}"
|
|
508
|
+
);
|
|
374
509
|
assert!(
|
|
375
510
|
team_state.contains("bravo: Bravo Worker on codex"),
|
|
376
511
|
"the '## Agents' section must list the remaining agent bravo (golden '- {{id}}: {{role}} on {{provider}} ({{status}})'); got:\n{team_state}"
|
|
@@ -446,7 +581,10 @@ fn lanea_remove_dynamic_role_file_missing_raises() {
|
|
|
446
581
|
"bravo": { "status": "stopped", "provider": "codex", "window": "bravo" }
|
|
447
582
|
}));
|
|
448
583
|
let tx = LaneTransport::new("team-laneateam", &[]);
|
|
449
|
-
let text = format!(
|
|
584
|
+
let text = format!(
|
|
585
|
+
"{:?}",
|
|
586
|
+
remove_agent_with_transport(&ws, &aid("alpha"), true, true, None, &tx)
|
|
587
|
+
);
|
|
450
588
|
assert!(
|
|
451
589
|
text.contains("dynamic role file missing"),
|
|
452
590
|
"golden agents.py:259-260: a state-recorded dynamic_role_file that is MISSING must RAISE 'dynamic role \
|
|
@@ -468,7 +606,10 @@ fn lanea_remove_rollback_restores_via_spec_state_file_path() {
|
|
|
468
606
|
std::fs::write(ws.join("custom_state.md"), "ORIGINAL CUSTOM TEAM STATE\n").unwrap(); // capture reads this
|
|
469
607
|
let tx = LaneTransport::new("team-laneateam", &[]);
|
|
470
608
|
let result = remove_agent_with_transport(&ws, &aid("alpha"), true, true, None, &tx);
|
|
471
|
-
assert!(
|
|
609
|
+
assert!(
|
|
610
|
+
result.is_err(),
|
|
611
|
+
"precondition: 1-worker removal -> validate_spec fails -> rollback runs; got {result:?}"
|
|
612
|
+
);
|
|
472
613
|
assert!(
|
|
473
614
|
!ws.join("team_state.md").exists(),
|
|
474
615
|
"golden agents.py:200-204: rollback restores the spec-derived state_file (custom_state.md), it must NOT \
|
|
@@ -486,7 +627,10 @@ fn lanea_remove_rollback_restores_via_spec_state_file_path() {
|
|
|
486
627
|
fn lanea_fork_dup_target_leader_id_is_already_exists() {
|
|
487
628
|
let ws = fork_ws(DELEG_ROLE_ALPHA);
|
|
488
629
|
let tx = LaneTransport::new("team-laneateam", &[]);
|
|
489
|
-
let text = format!(
|
|
630
|
+
let text = format!(
|
|
631
|
+
"{:?}",
|
|
632
|
+
fork_agent_with_transport(&ws, &aid("alpha"), &aid("leader"), None, false, None, &tx)
|
|
633
|
+
);
|
|
490
634
|
assert!(
|
|
491
635
|
text.contains("already exists"),
|
|
492
636
|
"golden operations.py:301-302 (_find_agent matches the leader): forking ONTO the leader id must raise \
|
|
@@ -503,7 +647,10 @@ fn lanea_fork_dup_target_leader_id_is_already_exists() {
|
|
|
503
647
|
fn lanea_fork_window_already_exists_guard_before_spec_mutation() {
|
|
504
648
|
let ws = fork_ws(DELEG_ROLE_ALPHA);
|
|
505
649
|
let tx = LaneTransport::new("team-laneateam", &["newfork"]); // the target window already exists
|
|
506
|
-
let text = format!(
|
|
650
|
+
let text = format!(
|
|
651
|
+
"{:?}",
|
|
652
|
+
fork_agent_with_transport(&ws, &aid("alpha"), &aid("newfork"), None, false, None, &tx)
|
|
653
|
+
);
|
|
507
654
|
assert!(
|
|
508
655
|
text.contains("tmux window already exists for fork target: team-laneateam:newfork"),
|
|
509
656
|
"golden operations.py:310-312: a pre-existing target window must raise 'tmux window already exists for \
|
|
@@ -527,7 +674,8 @@ fn lanea_fork_window_already_exists_guard_before_spec_mutation() {
|
|
|
527
674
|
fn lanea_fork_gate_error_text_and_spec_rollback_on_adapter_arm() {
|
|
528
675
|
let ws = fork_ws(DELEG_ROLE_ALPHA_COMPAT); // source alpha auth_mode=compatible_api -> native fork unsupported
|
|
529
676
|
let tx = LaneTransport::new("team-laneateam", &[]);
|
|
530
|
-
let result =
|
|
677
|
+
let result =
|
|
678
|
+
fork_agent_with_transport(&ws, &aid("alpha"), &aid("newfork"), None, false, None, &tx);
|
|
531
679
|
let text = format!("{result:?}");
|
|
532
680
|
assert!(
|
|
533
681
|
text.contains("codex does not support native session fork"),
|
|
@@ -552,7 +700,9 @@ fn lanea_fork_gate_error_text_and_spec_rollback_on_adapter_arm() {
|
|
|
552
700
|
fn lanea_fork_report_session_id_is_not_pane_id() {
|
|
553
701
|
let ws = fork_ws(DELEG_ROLE_ALPHA); // codex+subscription -> native fork supported -> full success path
|
|
554
702
|
let tx = LaneTransport::new("team-laneateam", &[]);
|
|
555
|
-
let report =
|
|
703
|
+
let report =
|
|
704
|
+
fork_agent_with_transport(&ws, &aid("alpha"), &aid("newfork"), None, false, None, &tx)
|
|
705
|
+
.expect("fork ok (codex subscription supports fork)");
|
|
556
706
|
assert_ne!(
|
|
557
707
|
report.session_id,
|
|
558
708
|
Some(crate::provider::SessionId::new("%newfork")),
|
|
@@ -884,7 +884,8 @@ fn e5_restart_missing_role_docs_refuses_and_preserves_old_spec() {
|
|
|
884
884
|
|
|
885
885
|
#[test]
|
|
886
886
|
fn e5_restart_missing_role_docs_refuses_even_after_endpoint_convergence_marker() {
|
|
887
|
-
let _harness_gate =
|
|
887
|
+
let _harness_gate =
|
|
888
|
+
EnvVarGuard::unset("TEAM_AGENT_TEST_ENDPOINT_CONVERGENCE_HARNESS_SPEC_FALLBACK");
|
|
888
889
|
let ws = restart_ws_two_resumable_workers();
|
|
889
890
|
let prev_spec = crate::model::paths::runtime_spec_path(&ws, "restartteam");
|
|
890
891
|
std::fs::create_dir_all(prev_spec.parent().unwrap()).unwrap();
|