@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,9 +1,9 @@
|
|
|
1
|
-
use super::*;
|
|
2
1
|
use super::agent_ops::lanea_team_ws;
|
|
3
2
|
use super::lane_ops::{LaneSpawns, LaneTransport};
|
|
4
3
|
use super::launch_spawn::{
|
|
5
4
|
restart_ws_two_resumable_workers, seed_healthy_coordinator, DELEG_ROLE_ALPHA, DELEG_ROLE_BRAVO,
|
|
6
5
|
};
|
|
6
|
+
use super::*;
|
|
7
7
|
|
|
8
8
|
type RecordedSpawns = LaneSpawns;
|
|
9
9
|
|
|
@@ -46,56 +46,118 @@ impl crate::transport::Transport for SessionProbeRecordingTransport {
|
|
|
46
46
|
fn kind(&self) -> crate::transport::BackendKind {
|
|
47
47
|
crate::transport::BackendKind::Tmux
|
|
48
48
|
}
|
|
49
|
-
fn spawn_first(
|
|
50
|
-
self
|
|
49
|
+
fn spawn_first(
|
|
50
|
+
&self,
|
|
51
|
+
session: &crate::transport::SessionName,
|
|
52
|
+
window: &crate::transport::WindowName,
|
|
53
|
+
argv: &[String],
|
|
54
|
+
_cwd: &std::path::Path,
|
|
55
|
+
_env: &std::collections::BTreeMap<String, String>,
|
|
56
|
+
) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
|
|
57
|
+
self.spawns
|
|
58
|
+
.lock()
|
|
59
|
+
.unwrap()
|
|
60
|
+
.push(("spawn_first".to_string(), argv.to_vec()));
|
|
51
61
|
self.spawn_targets.lock().unwrap().push((
|
|
52
62
|
session.as_str().to_string(),
|
|
53
63
|
window.as_str().to_string(),
|
|
54
64
|
"%0".to_string(),
|
|
55
65
|
));
|
|
56
|
-
Ok(crate::transport::SpawnResult {
|
|
66
|
+
Ok(crate::transport::SpawnResult {
|
|
67
|
+
pane_id: crate::transport::PaneId::new("%0"),
|
|
68
|
+
session: session.clone(),
|
|
69
|
+
window: window.clone(),
|
|
70
|
+
child_pid: Some(6856),
|
|
71
|
+
})
|
|
57
72
|
}
|
|
58
|
-
fn spawn_into(
|
|
59
|
-
self
|
|
73
|
+
fn spawn_into(
|
|
74
|
+
&self,
|
|
75
|
+
session: &crate::transport::SessionName,
|
|
76
|
+
window: &crate::transport::WindowName,
|
|
77
|
+
argv: &[String],
|
|
78
|
+
_cwd: &std::path::Path,
|
|
79
|
+
_env: &std::collections::BTreeMap<String, String>,
|
|
80
|
+
) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
|
|
81
|
+
self.spawns
|
|
82
|
+
.lock()
|
|
83
|
+
.unwrap()
|
|
84
|
+
.push(("spawn_into".to_string(), argv.to_vec()));
|
|
60
85
|
let pane = format!("%{}", window.as_str());
|
|
61
86
|
self.spawn_targets.lock().unwrap().push((
|
|
62
87
|
session.as_str().to_string(),
|
|
63
88
|
window.as_str().to_string(),
|
|
64
89
|
pane.clone(),
|
|
65
90
|
));
|
|
66
|
-
Ok(crate::transport::SpawnResult {
|
|
91
|
+
Ok(crate::transport::SpawnResult {
|
|
92
|
+
pane_id: crate::transport::PaneId::new(pane),
|
|
93
|
+
session: session.clone(),
|
|
94
|
+
window: window.clone(),
|
|
95
|
+
child_pid: Some(6856),
|
|
96
|
+
})
|
|
67
97
|
}
|
|
68
|
-
fn inject(
|
|
98
|
+
fn inject(
|
|
99
|
+
&self,
|
|
100
|
+
_t: &crate::transport::Target,
|
|
101
|
+
_p: &crate::transport::InjectPayload,
|
|
102
|
+
_s: crate::transport::Key,
|
|
103
|
+
_b: bool,
|
|
104
|
+
) -> Result<crate::transport::InjectReport, crate::transport::TransportError> {
|
|
69
105
|
unimplemented!("not reached by start_agent respawn")
|
|
70
106
|
}
|
|
71
|
-
fn send_keys(
|
|
107
|
+
fn send_keys(
|
|
108
|
+
&self,
|
|
109
|
+
_t: &crate::transport::Target,
|
|
110
|
+
_k: &[crate::transport::Key],
|
|
111
|
+
) -> Result<(), crate::transport::TransportError> {
|
|
72
112
|
unimplemented!("not reached")
|
|
73
113
|
}
|
|
74
|
-
fn capture(
|
|
114
|
+
fn capture(
|
|
115
|
+
&self,
|
|
116
|
+
_t: &crate::transport::Target,
|
|
117
|
+
_r: crate::transport::CaptureRange,
|
|
118
|
+
) -> Result<crate::transport::CapturedText, crate::transport::TransportError> {
|
|
75
119
|
Ok(crate::transport::CapturedText {
|
|
76
120
|
text: String::new(),
|
|
77
121
|
range: crate::transport::CaptureRange::Full,
|
|
78
122
|
})
|
|
79
123
|
}
|
|
80
|
-
fn query(
|
|
124
|
+
fn query(
|
|
125
|
+
&self,
|
|
126
|
+
_t: &crate::transport::Target,
|
|
127
|
+
_f: crate::transport::PaneField,
|
|
128
|
+
) -> Result<Option<String>, crate::transport::TransportError> {
|
|
81
129
|
unimplemented!("not reached")
|
|
82
130
|
}
|
|
83
|
-
fn liveness(
|
|
131
|
+
fn liveness(
|
|
132
|
+
&self,
|
|
133
|
+
p: &crate::transport::PaneId,
|
|
134
|
+
) -> Result<crate::model::enums::PaneLiveness, crate::transport::TransportError> {
|
|
84
135
|
let killed = self.killed_panes.lock().unwrap();
|
|
85
136
|
if killed.contains(p.as_str()) {
|
|
86
137
|
return Ok(crate::model::enums::PaneLiveness::Dead);
|
|
87
138
|
}
|
|
88
139
|
Ok(crate::model::enums::PaneLiveness::Live)
|
|
89
140
|
}
|
|
90
|
-
fn has_pane(
|
|
141
|
+
fn has_pane(
|
|
142
|
+
&self,
|
|
143
|
+
p: &crate::transport::PaneId,
|
|
144
|
+
) -> Result<Option<bool>, crate::transport::TransportError> {
|
|
91
145
|
let killed = self.killed_panes.lock().unwrap();
|
|
92
146
|
Ok(Some(!killed.contains(p.as_str())))
|
|
93
147
|
}
|
|
94
|
-
fn kill_pane(
|
|
95
|
-
self
|
|
148
|
+
fn kill_pane(
|
|
149
|
+
&self,
|
|
150
|
+
p: &crate::transport::PaneId,
|
|
151
|
+
) -> Result<(), crate::transport::TransportError> {
|
|
152
|
+
self.killed_panes
|
|
153
|
+
.lock()
|
|
154
|
+
.unwrap()
|
|
155
|
+
.insert(p.as_str().to_string());
|
|
96
156
|
Ok(())
|
|
97
157
|
}
|
|
98
|
-
fn list_targets(
|
|
158
|
+
fn list_targets(
|
|
159
|
+
&self,
|
|
160
|
+
) -> Result<Vec<crate::transport::PaneInfo>, crate::transport::TransportError> {
|
|
99
161
|
Ok(self
|
|
100
162
|
.spawn_targets
|
|
101
163
|
.lock()
|
|
@@ -116,23 +178,47 @@ impl crate::transport::Transport for SessionProbeRecordingTransport {
|
|
|
116
178
|
})
|
|
117
179
|
.collect())
|
|
118
180
|
}
|
|
119
|
-
fn has_session(
|
|
181
|
+
fn has_session(
|
|
182
|
+
&self,
|
|
183
|
+
_s: &crate::transport::SessionName,
|
|
184
|
+
) -> Result<bool, crate::transport::TransportError> {
|
|
120
185
|
Ok(self.session_exists)
|
|
121
186
|
}
|
|
122
|
-
fn list_windows(
|
|
187
|
+
fn list_windows(
|
|
188
|
+
&self,
|
|
189
|
+
_s: &crate::transport::SessionName,
|
|
190
|
+
) -> Result<Vec<crate::transport::WindowName>, crate::transport::TransportError> {
|
|
123
191
|
// a dead server has no windows; a live one has the agent window.
|
|
124
|
-
Ok(if self.session_exists {
|
|
192
|
+
Ok(if self.session_exists {
|
|
193
|
+
vec![crate::transport::WindowName::new("alpha")]
|
|
194
|
+
} else {
|
|
195
|
+
Vec::new()
|
|
196
|
+
})
|
|
125
197
|
}
|
|
126
|
-
fn set_session_env(
|
|
198
|
+
fn set_session_env(
|
|
199
|
+
&self,
|
|
200
|
+
_s: &crate::transport::SessionName,
|
|
201
|
+
_k: &str,
|
|
202
|
+
_v: &str,
|
|
203
|
+
) -> Result<crate::transport::SetEnvOutcome, crate::transport::TransportError> {
|
|
127
204
|
unimplemented!("not reached")
|
|
128
205
|
}
|
|
129
|
-
fn kill_session(
|
|
206
|
+
fn kill_session(
|
|
207
|
+
&self,
|
|
208
|
+
_s: &crate::transport::SessionName,
|
|
209
|
+
) -> Result<(), crate::transport::TransportError> {
|
|
130
210
|
unimplemented!("not reached")
|
|
131
211
|
}
|
|
132
|
-
fn kill_window(
|
|
212
|
+
fn kill_window(
|
|
213
|
+
&self,
|
|
214
|
+
_t: &crate::transport::Target,
|
|
215
|
+
) -> Result<(), crate::transport::TransportError> {
|
|
133
216
|
Ok(())
|
|
134
217
|
}
|
|
135
|
-
fn attach_session(
|
|
218
|
+
fn attach_session(
|
|
219
|
+
&self,
|
|
220
|
+
_s: &crate::transport::SessionName,
|
|
221
|
+
) -> Result<crate::transport::AttachOutcome, crate::transport::TransportError> {
|
|
136
222
|
unimplemented!("not reached")
|
|
137
223
|
}
|
|
138
224
|
}
|
|
@@ -163,10 +249,27 @@ fn start_agent_respawn_into_dead_session_uses_new_session_not_new_window() {
|
|
|
163
249
|
let ws = respawn_ws_one_resumable_worker();
|
|
164
250
|
let spawns = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
|
|
165
251
|
// session_exists=false models the dead CP-1 `-L` server after --discard-session.
|
|
166
|
-
let transport = SessionProbeRecordingTransport {
|
|
167
|
-
|
|
252
|
+
let transport = SessionProbeRecordingTransport {
|
|
253
|
+
spawns: std::sync::Arc::clone(&spawns),
|
|
254
|
+
spawn_targets: std::sync::Mutex::new(Vec::new()),
|
|
255
|
+
session_exists: false,
|
|
256
|
+
killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()),
|
|
257
|
+
};
|
|
258
|
+
let _ = start_agent_with_transport(
|
|
259
|
+
&ws,
|
|
260
|
+
&AgentId::new("alpha"),
|
|
261
|
+
false,
|
|
262
|
+
false,
|
|
263
|
+
false,
|
|
264
|
+
None,
|
|
265
|
+
&transport,
|
|
266
|
+
);
|
|
168
267
|
let recorded = spawns.lock().unwrap().clone();
|
|
169
|
-
assert_eq!(
|
|
268
|
+
assert_eq!(
|
|
269
|
+
recorded.len(),
|
|
270
|
+
1,
|
|
271
|
+
"exactly one respawn for alpha; got {recorded:?}"
|
|
272
|
+
);
|
|
170
273
|
assert_eq!(
|
|
171
274
|
recorded[0].0, "spawn_first",
|
|
172
275
|
"golden runtime.py:1017: when `tmux has-session` is FALSE (dead -L server after \
|
|
@@ -195,7 +298,12 @@ fn reset_agent_discard_session_rebuilds_window_via_start_respawn() {
|
|
|
195
298
|
let ws = restart_ws_two_resumable_workers(); // compiled spec + state(alpha,bravo running) + seeded coordinator
|
|
196
299
|
let spawns = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
|
|
197
300
|
// live session present (other workers keep it alive) but alpha's window was discarded.
|
|
198
|
-
let transport = SessionProbeRecordingTransport {
|
|
301
|
+
let transport = SessionProbeRecordingTransport {
|
|
302
|
+
spawns: std::sync::Arc::clone(&spawns),
|
|
303
|
+
spawn_targets: std::sync::Mutex::new(Vec::new()),
|
|
304
|
+
session_exists: true,
|
|
305
|
+
killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()),
|
|
306
|
+
};
|
|
199
307
|
let _ = reset_agent_with_transport(&ws, &AgentId::new("alpha"), true, false, None, &transport);
|
|
200
308
|
let recorded = spawns.lock().unwrap().clone();
|
|
201
309
|
assert!(
|
|
@@ -233,9 +341,15 @@ fn reset_agent_discard_session_syncs_projection_epoch_inputs_for_restart_agent_c
|
|
|
233
341
|
|
|
234
342
|
let before = chrono::Utc::now();
|
|
235
343
|
let spawns = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
|
|
236
|
-
let transport = SessionProbeRecordingTransport {
|
|
237
|
-
|
|
238
|
-
|
|
344
|
+
let transport = SessionProbeRecordingTransport {
|
|
345
|
+
spawns: std::sync::Arc::clone(&spawns),
|
|
346
|
+
spawn_targets: std::sync::Mutex::new(Vec::new()),
|
|
347
|
+
session_exists: true,
|
|
348
|
+
killed_panes: std::sync::Mutex::new(std::collections::BTreeSet::new()),
|
|
349
|
+
};
|
|
350
|
+
let outcome =
|
|
351
|
+
reset_agent_with_transport(&ws, &AgentId::new("alpha"), true, false, None, &transport)
|
|
352
|
+
.expect("reset-agent alpha --discard-session --no-display");
|
|
239
353
|
assert!(
|
|
240
354
|
matches!(outcome, ResetAgentOutcome::Reset { .. }),
|
|
241
355
|
"restart-agent alpha --discard-session --no-display must complete reset; got {outcome:?}"
|
|
@@ -323,7 +437,10 @@ fn bugb_routed_team_ws() -> PathBuf {
|
|
|
323
437
|
let yaml = crate::model::yaml::dumps(&spec);
|
|
324
438
|
// sanity: the UNMODIFIED compiled spec really routes bravo — that route-bravo rule is the dangling
|
|
325
439
|
// ref the cascade-prune must drop (and which makes golden's validate_spec raise + rollback).
|
|
326
|
-
assert!(
|
|
440
|
+
assert!(
|
|
441
|
+
yaml.contains("assign_to: \"bravo\""),
|
|
442
|
+
"fixture: compiled spec must carry route-bravo (assign_to bravo); got:\n{yaml}"
|
|
443
|
+
);
|
|
327
444
|
std::fs::write(ws.join("team.spec.yaml"), yaml).unwrap();
|
|
328
445
|
crate::state::persist::save_runtime_state(
|
|
329
446
|
&ws,
|
|
@@ -383,7 +500,10 @@ fn remove_routed_worker_prunes_dangling_routing_rule_divergence() {
|
|
|
383
500
|
);
|
|
384
501
|
let state = crate::state::persist::load_runtime_state(&ws).expect("load state");
|
|
385
502
|
assert!(
|
|
386
|
-
state
|
|
503
|
+
state
|
|
504
|
+
.get("agents")
|
|
505
|
+
.and_then(serde_json::Value::as_object)
|
|
506
|
+
.is_some_and(|a| !a.contains_key("bravo")),
|
|
387
507
|
"bravo must also be removed from state.agents; got {state:?}"
|
|
388
508
|
);
|
|
389
509
|
let _ = std::fs::remove_dir_all(&ws);
|
|
@@ -720,11 +720,7 @@ impl Transport for BRealTransport {
|
|
|
720
720
|
})
|
|
721
721
|
}
|
|
722
722
|
|
|
723
|
-
fn query(
|
|
724
|
-
&self,
|
|
725
|
-
_target: &Target,
|
|
726
|
-
_field: PaneField,
|
|
727
|
-
) -> Result<Option<String>, TransportError> {
|
|
723
|
+
fn query(&self, _target: &Target, _field: PaneField) -> Result<Option<String>, TransportError> {
|
|
728
724
|
Ok(Some("node".to_string()))
|
|
729
725
|
}
|
|
730
726
|
|
|
@@ -545,6 +545,9 @@ fn normalize_value(value: Value, ctx: &mut NormalizeCtx, key: Option<&str>) -> V
|
|
|
545
545
|
if matches!(key, Some("env_overlay_keys" | "env_unset_keys")) {
|
|
546
546
|
return json!("<ENV_KEYS>");
|
|
547
547
|
}
|
|
548
|
+
if matches!(key, Some("env_unset")) {
|
|
549
|
+
return json!([]);
|
|
550
|
+
}
|
|
548
551
|
match value {
|
|
549
552
|
Value::Object(map) => {
|
|
550
553
|
let sorted = map.into_iter().collect::<BTreeMap<_, _>>();
|
|
@@ -20,11 +20,11 @@ fn sess(s: &str) -> SessionName {
|
|
|
20
20
|
SessionName(s.to_string())
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
mod agent_ops;
|
|
23
24
|
mod core;
|
|
25
|
+
mod lane_ops;
|
|
24
26
|
mod launch_spawn;
|
|
25
27
|
mod lifecycle_lock;
|
|
26
|
-
mod agent_ops;
|
|
27
|
-
mod lane_ops;
|
|
28
28
|
mod main_preserved;
|
|
29
29
|
mod phase_b_contracts;
|
|
30
30
|
mod phase_golden;
|
|
@@ -13,10 +13,10 @@ fn main() -> anyhow::Result<()> {
|
|
|
13
13
|
other => return Err(anyhow::anyhow!("unknown fake-worker argument: {other}")),
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
let workspace =
|
|
17
|
-
.ok_or_else(|| anyhow::anyhow!("fake-worker requires --workspace <path>"))?;
|
|
18
|
-
let agent_id =
|
|
19
|
-
.ok_or_else(|| anyhow::anyhow!("fake-worker requires --agent-id <id>"))?;
|
|
16
|
+
let workspace =
|
|
17
|
+
workspace.ok_or_else(|| anyhow::anyhow!("fake-worker requires --workspace <path>"))?;
|
|
18
|
+
let agent_id =
|
|
19
|
+
agent_id.ok_or_else(|| anyhow::anyhow!("fake-worker requires --agent-id <id>"))?;
|
|
20
20
|
let stdin = std::io::stdin();
|
|
21
21
|
let stdout = std::io::stdout();
|
|
22
22
|
team_agent::fake_worker::run(&workspace, &agent_id, stdin.lock(), stdout.lock())
|
|
@@ -3,8 +3,8 @@ use std::path::{Path, PathBuf};
|
|
|
3
3
|
use serde_json::Value;
|
|
4
4
|
|
|
5
5
|
use crate::lifecycle::{ResetAgentOutcome, ResetRefusal};
|
|
6
|
-
use crate::model::yaml::{self, Value as YamlValue};
|
|
7
6
|
use crate::model::ids::{AgentId, TeamKey};
|
|
7
|
+
use crate::model::yaml::{self, Value as YamlValue};
|
|
8
8
|
|
|
9
9
|
use super::super::helpers::{enum_value, object_fields, tool_runtime_error};
|
|
10
10
|
use super::super::{ToolOk, ToolResult};
|
|
@@ -126,7 +126,9 @@ pub(crate) fn fork_agent(
|
|
|
126
126
|
|
|
127
127
|
fn reset_refusal_reason(reason: ResetRefusal) -> Value {
|
|
128
128
|
match reason {
|
|
129
|
-
ResetRefusal::DiscardSessionRequired =>
|
|
129
|
+
ResetRefusal::DiscardSessionRequired => {
|
|
130
|
+
Value::String("discard_session_required".to_string())
|
|
131
|
+
}
|
|
130
132
|
}
|
|
131
133
|
}
|
|
132
134
|
|
|
@@ -211,7 +213,10 @@ fn state_spec_workspace(state: &Value, team: Option<&str>) -> Option<PathBuf> {
|
|
|
211
213
|
state_spec_workspace_from_entry(state).or_else(|| {
|
|
212
214
|
let teams = state.get("teams").and_then(Value::as_object)?;
|
|
213
215
|
if teams.len() == 1 {
|
|
214
|
-
teams
|
|
216
|
+
teams
|
|
217
|
+
.values()
|
|
218
|
+
.next()
|
|
219
|
+
.and_then(state_spec_workspace_from_entry)
|
|
215
220
|
} else {
|
|
216
221
|
None
|
|
217
222
|
}
|
|
@@ -294,20 +299,35 @@ fn materialize_mcp_lifecycle_spec(
|
|
|
294
299
|
),
|
|
295
300
|
(
|
|
296
301
|
"leader".to_string(),
|
|
297
|
-
YamlValue::Map(vec![(
|
|
302
|
+
YamlValue::Map(vec![(
|
|
303
|
+
"provider".to_string(),
|
|
304
|
+
YamlValue::Str("codex".to_string()),
|
|
305
|
+
)]),
|
|
298
306
|
),
|
|
299
307
|
("agents".to_string(), YamlValue::List(agent_items)),
|
|
300
308
|
]);
|
|
301
309
|
let spec_workspace = workspace.join(".team").join(&team_name);
|
|
302
310
|
std::fs::create_dir_all(&spec_workspace).map_err(|e| {
|
|
303
|
-
tool_runtime_error(format!(
|
|
311
|
+
tool_runtime_error(format!(
|
|
312
|
+
"create MCP lifecycle spec dir {}: {e}",
|
|
313
|
+
spec_workspace.display()
|
|
314
|
+
))
|
|
304
315
|
})?;
|
|
305
316
|
let spec_path = spec_workspace.join("team.spec.yaml");
|
|
306
317
|
std::fs::write(&spec_path, yaml::dumps(&spec)).map_err(|e| {
|
|
307
|
-
tool_runtime_error(format!(
|
|
318
|
+
tool_runtime_error(format!(
|
|
319
|
+
"write MCP lifecycle spec {}: {e}",
|
|
320
|
+
spec_path.display()
|
|
321
|
+
))
|
|
308
322
|
})?;
|
|
309
323
|
if prepare_state {
|
|
310
|
-
prepare_selected_team_state(
|
|
324
|
+
prepare_selected_team_state(
|
|
325
|
+
workspace,
|
|
326
|
+
&mut state,
|
|
327
|
+
&team_name,
|
|
328
|
+
&spec_workspace,
|
|
329
|
+
&spec_path,
|
|
330
|
+
)?;
|
|
311
331
|
}
|
|
312
332
|
Ok(spec_workspace)
|
|
313
333
|
}
|
|
@@ -316,16 +336,15 @@ fn selected_agents<'a>(
|
|
|
316
336
|
state: &'a Value,
|
|
317
337
|
team: Option<&str>,
|
|
318
338
|
) -> Option<&'a serde_json::Map<String, Value>> {
|
|
319
|
-
team
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
.or_else(|| state.get("agents").and_then(Value::as_object))
|
|
339
|
+
team.and_then(|team| {
|
|
340
|
+
state
|
|
341
|
+
.get("teams")
|
|
342
|
+
.and_then(Value::as_object)
|
|
343
|
+
.and_then(|teams| teams.get(team))
|
|
344
|
+
.and_then(|entry| entry.get("agents"))
|
|
345
|
+
.and_then(Value::as_object)
|
|
346
|
+
})
|
|
347
|
+
.or_else(|| state.get("agents").and_then(Value::as_object))
|
|
329
348
|
}
|
|
330
349
|
|
|
331
350
|
fn prepare_selected_team_state(
|
|
@@ -370,19 +389,27 @@ fn prepare_selected_team_state(
|
|
|
370
389
|
if let Some(top_agents) = top_agents {
|
|
371
390
|
if let Some(team_agents) = entry.get_mut("agents").and_then(Value::as_object_mut) {
|
|
372
391
|
for (agent_id, top_agent) in top_agents {
|
|
373
|
-
let Some(team_agent) = team_agents
|
|
392
|
+
let Some(team_agent) = team_agents
|
|
393
|
+
.get_mut(&agent_id)
|
|
394
|
+
.and_then(Value::as_object_mut)
|
|
395
|
+
else {
|
|
374
396
|
continue;
|
|
375
397
|
};
|
|
376
398
|
let Some(top_agent) = top_agent.as_object() else {
|
|
377
399
|
continue;
|
|
378
400
|
};
|
|
379
401
|
for (key, value) in top_agent {
|
|
380
|
-
team_agent
|
|
402
|
+
team_agent
|
|
403
|
+
.entry(key.clone())
|
|
404
|
+
.or_insert_with(|| value.clone());
|
|
381
405
|
}
|
|
382
406
|
}
|
|
383
407
|
}
|
|
384
408
|
}
|
|
385
409
|
crate::state::persist::save_runtime_state(workspace, state).map_err(|e| {
|
|
386
|
-
tool_runtime_error(format!(
|
|
410
|
+
tool_runtime_error(format!(
|
|
411
|
+
"save MCP lifecycle scoped state {}: {e}",
|
|
412
|
+
workspace.display()
|
|
413
|
+
))
|
|
387
414
|
})
|
|
388
415
|
}
|
|
@@ -56,7 +56,15 @@
|
|
|
56
56
|
|
|
57
57
|
// ROUND-0 skeleton: fn bodies are all unimplemented!() so imports/fields/methods are
|
|
58
58
|
// not yet exercised; P2 porter removes this when implementing.
|
|
59
|
-
#![allow(
|
|
59
|
+
#![allow(
|
|
60
|
+
dead_code,
|
|
61
|
+
unused_imports,
|
|
62
|
+
unused_variables,
|
|
63
|
+
clippy::result_large_err,
|
|
64
|
+
clippy::doc_overindented_list_items,
|
|
65
|
+
clippy::doc_lazy_continuation,
|
|
66
|
+
clippy::io_other_error
|
|
67
|
+
)]
|
|
60
68
|
// §10:MCP stdio handlers 实现层禁 unwrap/expect/panic(unimplemented!() stub 不被拦);tests 子模块各自 allow。
|
|
61
69
|
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
|
62
70
|
|
|
@@ -104,7 +112,8 @@ pub(crate) use helpers::{
|
|
|
104
112
|
object_fields, text_field, text_of_value, tool_error_reason_wire, tool_runtime_error,
|
|
105
113
|
};
|
|
106
114
|
pub(crate) use normalize::{
|
|
107
|
-
normalize_artifacts, normalize_changes, normalize_next_actions, normalize_risks,
|
|
115
|
+
normalize_artifacts, normalize_changes, normalize_next_actions, normalize_risks,
|
|
116
|
+
normalize_tests,
|
|
108
117
|
};
|
|
109
118
|
pub(crate) use wire::dispatch_tool;
|
|
110
119
|
|
|
@@ -275,7 +275,11 @@ impl ToolError {
|
|
|
275
275
|
|
|
276
276
|
/// `_tool_error_result(reason, message, exc_type)` (`server.py:98-106`) for the
|
|
277
277
|
/// `unknown_tool` / `peer_not_in_scope` non-exception paths.
|
|
278
|
-
pub fn new(
|
|
278
|
+
pub fn new(
|
|
279
|
+
reason: ToolErrorReason,
|
|
280
|
+
message: impl Into<String>,
|
|
281
|
+
exc_type: impl Into<String>,
|
|
282
|
+
) -> Self {
|
|
279
283
|
Self {
|
|
280
284
|
reason,
|
|
281
285
|
exc_type: exc_type.into(),
|
|
@@ -289,7 +293,11 @@ impl ToolError {
|
|
|
289
293
|
pub fn public_exception_message(raw: &str, exc_type: &str) -> String {
|
|
290
294
|
let cleaned = raw.replace(['\n', '\r'], " ");
|
|
291
295
|
let trimmed = cleaned.trim();
|
|
292
|
-
let base = if trimmed.is_empty() {
|
|
296
|
+
let base = if trimmed.is_empty() {
|
|
297
|
+
exc_type
|
|
298
|
+
} else {
|
|
299
|
+
trimmed
|
|
300
|
+
};
|
|
293
301
|
base.chars().take(200).collect()
|
|
294
302
|
}
|
|
295
303
|
}
|
|
@@ -320,7 +328,10 @@ impl SendOutcome {
|
|
|
320
328
|
/// the [`ToolResult`] `Ok` body.
|
|
321
329
|
pub fn to_value(&self) -> Value {
|
|
322
330
|
match self {
|
|
323
|
-
SendOutcome::WorkerAccepted {
|
|
331
|
+
SendOutcome::WorkerAccepted {
|
|
332
|
+
message_id,
|
|
333
|
+
poll_via,
|
|
334
|
+
} => {
|
|
324
335
|
let mut obj = serde_json::Map::new();
|
|
325
336
|
obj.insert("status".to_string(), Value::String("accepted".to_string()));
|
|
326
337
|
obj.insert("delivery_pending".to_string(), Value::Bool(true));
|