@team-agent/installer 0.3.21 → 0.3.23
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/emit.rs +9 -1
- package/crates/team-agent/src/cli/mod.rs +158 -2
- package/crates/team-agent/src/cli/status.rs +61 -0
- package/crates/team-agent/src/cli/status_port.rs +2 -2
- package/crates/team-agent/src/cli/tests/base.rs +26 -0
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +233 -9
- package/crates/team-agent/src/coordinator/tests/energy.rs +548 -0
- package/crates/team-agent/src/coordinator/tests/mod.rs +1 -0
- package/crates/team-agent/src/coordinator/tick.rs +452 -17
- package/crates/team-agent/src/lifecycle/display.rs +23 -302
- package/crates/team-agent/src/lifecycle/launch.rs +38 -0
- package/crates/team-agent/src/lifecycle/restart/agent.rs +27 -6
- package/crates/team-agent/src/lifecycle/restart/common.rs +179 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +295 -13
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +30 -0
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +89 -5
- package/crates/team-agent/src/messaging/delivery.rs +324 -95
- package/crates/team-agent/src/messaging/scheduler.rs +95 -73
- package/crates/team-agent/src/messaging/send.rs +10 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +460 -0
- package/crates/team-agent/src/messaging/tests/spine.rs +51 -2
- package/crates/team-agent/src/session_capture.rs +261 -1
- package/crates/team-agent/src/tmux_backend/tests.rs +97 -1
- package/crates/team-agent/src/tmux_backend.rs +138 -2
- package/crates/team-agent/src/transport/test_support.rs +25 -0
- package/crates/team-agent/src/transport.rs +11 -0
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +2 -1
- package/skills/team-agent/references/team-in-team.md +127 -0
|
@@ -41,11 +41,11 @@ impl crate::transport::Transport for SessionProbeRecordingTransport {
|
|
|
41
41
|
}
|
|
42
42
|
fn spawn_first(&self, session: &crate::transport::SessionName, window: &crate::transport::WindowName, argv: &[String], _cwd: &std::path::Path, _env: &std::collections::BTreeMap<String, String>) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
|
|
43
43
|
self.spawns.lock().unwrap().push(("spawn_first".to_string(), argv.to_vec()));
|
|
44
|
-
Ok(crate::transport::SpawnResult { pane_id: crate::transport::PaneId::new("%0"), session: session.clone(), window: window.clone(), child_pid:
|
|
44
|
+
Ok(crate::transport::SpawnResult { pane_id: crate::transport::PaneId::new("%0"), session: session.clone(), window: window.clone(), child_pid: Some(6856) })
|
|
45
45
|
}
|
|
46
46
|
fn spawn_into(&self, session: &crate::transport::SessionName, window: &crate::transport::WindowName, argv: &[String], _cwd: &std::path::Path, _env: &std::collections::BTreeMap<String, String>) -> Result<crate::transport::SpawnResult, crate::transport::TransportError> {
|
|
47
47
|
self.spawns.lock().unwrap().push(("spawn_into".to_string(), argv.to_vec()));
|
|
48
|
-
Ok(crate::transport::SpawnResult { pane_id: crate::transport::PaneId::new(format!("%{}", window.as_str())), session: session.clone(), window: window.clone(), child_pid:
|
|
48
|
+
Ok(crate::transport::SpawnResult { pane_id: crate::transport::PaneId::new(format!("%{}", window.as_str())), session: session.clone(), window: window.clone(), child_pid: Some(6856) })
|
|
49
49
|
}
|
|
50
50
|
fn inject(&self, _t: &crate::transport::Target, _p: &crate::transport::InjectPayload, _s: crate::transport::Key, _b: bool) -> Result<crate::transport::InjectReport, crate::transport::TransportError> {
|
|
51
51
|
unimplemented!("not reached by start_agent respawn")
|
|
@@ -54,16 +54,19 @@ impl crate::transport::Transport for SessionProbeRecordingTransport {
|
|
|
54
54
|
unimplemented!("not reached")
|
|
55
55
|
}
|
|
56
56
|
fn capture(&self, _t: &crate::transport::Target, _r: crate::transport::CaptureRange) -> Result<crate::transport::CapturedText, crate::transport::TransportError> {
|
|
57
|
-
|
|
57
|
+
Ok(crate::transport::CapturedText {
|
|
58
|
+
text: String::new(),
|
|
59
|
+
range: crate::transport::CaptureRange::Full,
|
|
60
|
+
})
|
|
58
61
|
}
|
|
59
62
|
fn query(&self, _t: &crate::transport::Target, _f: crate::transport::PaneField) -> Result<Option<String>, crate::transport::TransportError> {
|
|
60
63
|
unimplemented!("not reached")
|
|
61
64
|
}
|
|
62
65
|
fn liveness(&self, _p: &crate::transport::PaneId) -> Result<crate::model::enums::PaneLiveness, crate::transport::TransportError> {
|
|
63
|
-
|
|
66
|
+
Ok(crate::model::enums::PaneLiveness::Live)
|
|
64
67
|
}
|
|
65
68
|
fn list_targets(&self) -> Result<Vec<crate::transport::PaneInfo>, crate::transport::TransportError> {
|
|
66
|
-
|
|
69
|
+
Ok(Vec::new())
|
|
67
70
|
}
|
|
68
71
|
fn has_session(&self, _s: &crate::transport::SessionName) -> Result<bool, crate::transport::TransportError> {
|
|
69
72
|
Ok(self.session_exists)
|
|
@@ -163,6 +166,87 @@ fn reset_agent_discard_session_rebuilds_window_via_start_respawn() {
|
|
|
163
166
|
"into a LIVE session (has_session=true) the window rebuild is a new-window (spawn_into); got {recorded:?}"
|
|
164
167
|
);
|
|
165
168
|
}
|
|
169
|
+
|
|
170
|
+
#[test]
|
|
171
|
+
fn reset_agent_discard_session_syncs_projection_epoch_inputs_for_restart_agent_command() {
|
|
172
|
+
let ws = restart_ws_two_resumable_workers();
|
|
173
|
+
let mut state = crate::state::persist::load_runtime_state(&ws).expect("load state");
|
|
174
|
+
state["team_dir"] = json!(ws.to_string_lossy());
|
|
175
|
+
state["active_team_key"] = json!("restartteam");
|
|
176
|
+
state["agents"]["alpha"]["window"] = json!("alpha");
|
|
177
|
+
state["agents"]["alpha"]["pane_id"] = json!("%old");
|
|
178
|
+
state["agents"]["alpha"]["pane_pid"] = json!(6304);
|
|
179
|
+
state["agents"]["alpha"]["spawned_at"] = json!("2026-06-13T01:00:00+00:00");
|
|
180
|
+
state["agents"]["alpha"]["startup_prompts"] = json!("disabled_for_epoch");
|
|
181
|
+
state["agents"]["alpha"]["startup_prompt_status"] = json!("disabled_for_epoch");
|
|
182
|
+
state["agents"]["alpha"]["startup_prompt_probe_epoch"] = json!("pane_pid:6304");
|
|
183
|
+
state["agents"]["alpha"]["startup_prompt_probe_disabled_at"] =
|
|
184
|
+
json!("2026-06-13T01:01:00+00:00");
|
|
185
|
+
let compact = crate::state::projection::compact_team_state(&state);
|
|
186
|
+
state["teams"] = json!({
|
|
187
|
+
"current": compact.clone(),
|
|
188
|
+
"restartteam": compact,
|
|
189
|
+
});
|
|
190
|
+
crate::state::persist::save_runtime_state(&ws, &state).expect("save stale projections");
|
|
191
|
+
|
|
192
|
+
let before = chrono::Utc::now();
|
|
193
|
+
let spawns = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
|
|
194
|
+
let transport = SessionProbeRecordingTransport {
|
|
195
|
+
spawns: std::sync::Arc::clone(&spawns),
|
|
196
|
+
session_exists: true,
|
|
197
|
+
};
|
|
198
|
+
let outcome = reset_agent_with_transport(&ws, &AgentId::new("alpha"), true, false, None, &transport)
|
|
199
|
+
.expect("reset-agent alpha --discard-session --no-display");
|
|
200
|
+
assert!(
|
|
201
|
+
matches!(outcome, ResetAgentOutcome::Reset { .. }),
|
|
202
|
+
"restart-agent alpha --discard-session --no-display must complete reset; got {outcome:?}"
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
let state = crate::state::persist::load_runtime_state(&ws).expect("reload state");
|
|
206
|
+
for pointer in [
|
|
207
|
+
"/agents/alpha",
|
|
208
|
+
"/teams/current/agents/alpha",
|
|
209
|
+
"/teams/restartteam/agents/alpha",
|
|
210
|
+
] {
|
|
211
|
+
let agent = state.pointer(pointer).expect(pointer);
|
|
212
|
+
assert_eq!(
|
|
213
|
+
agent.get("pane_id").and_then(serde_json::Value::as_str),
|
|
214
|
+
Some("%alpha"),
|
|
215
|
+
"{pointer} must project the reset-spawned pane, not the stale pre-reset pane"
|
|
216
|
+
);
|
|
217
|
+
assert_eq!(
|
|
218
|
+
agent.get("pane_pid").and_then(serde_json::Value::as_u64),
|
|
219
|
+
Some(6856),
|
|
220
|
+
"{pointer} must project the reset-spawned pane pid, not stale pid 6304"
|
|
221
|
+
);
|
|
222
|
+
let spawned_at = agent
|
|
223
|
+
.get("spawned_at")
|
|
224
|
+
.and_then(serde_json::Value::as_str)
|
|
225
|
+
.expect("spawned_at refreshed");
|
|
226
|
+
assert_ne!(
|
|
227
|
+
spawned_at, "2026-06-13T01:00:00+00:00",
|
|
228
|
+
"{pointer} must not retain stale spawned_at"
|
|
229
|
+
);
|
|
230
|
+
let spawned_at = chrono::DateTime::parse_from_rfc3339(spawned_at)
|
|
231
|
+
.expect("spawned_at rfc3339")
|
|
232
|
+
.with_timezone(&chrono::Utc);
|
|
233
|
+
assert!(
|
|
234
|
+
spawned_at >= before - chrono::Duration::seconds(1),
|
|
235
|
+
"{pointer} must get a fresh startup-probe grace epoch; spawned_at={spawned_at}, before={before}"
|
|
236
|
+
);
|
|
237
|
+
for key in [
|
|
238
|
+
"startup_prompts",
|
|
239
|
+
"startup_prompt_status",
|
|
240
|
+
"startup_prompt_probe_epoch",
|
|
241
|
+
"startup_prompt_probe_disabled_at",
|
|
242
|
+
] {
|
|
243
|
+
assert!(
|
|
244
|
+
agent.get(key).is_none(),
|
|
245
|
+
"{pointer} must not retain old startup probe epoch field {key}"
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
166
250
|
// item ② — remove invoked with a NON-canonical input (the workspace's own `.team` subpath) must resolve
|
|
167
251
|
// the CANONICAL live run-workspace and apply the removal to the LIVE roster — not the subpath's detached
|
|
168
252
|
// runtime. canonical_run_workspace($ws/.team) == $ws. The regression made remove "succeed" against a
|