@team-agent/installer 0.3.1 → 0.3.3

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.
Files changed (79) hide show
  1. package/Cargo.lock +34 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +1 -1
  4. package/crates/team-agent/src/cli/adapters.rs +234 -26
  5. package/crates/team-agent/src/cli/diagnose.rs +144 -10
  6. package/crates/team-agent/src/cli/emit.rs +289 -54
  7. package/crates/team-agent/src/cli/leader.rs +37 -8
  8. package/crates/team-agent/src/cli/mod.rs +1281 -196
  9. package/crates/team-agent/src/cli/status_port.rs +195 -46
  10. package/crates/team-agent/src/cli/tests/divergence.rs +1 -2
  11. package/crates/team-agent/src/cli/tests/lane_c.rs +23 -13
  12. package/crates/team-agent/src/cli/tests/main_preserved.rs +2 -0
  13. package/crates/team-agent/src/cli/tests/run_delegation.rs +59 -3
  14. package/crates/team-agent/src/cli/types.rs +18 -0
  15. package/crates/team-agent/src/compiler.rs +15 -5
  16. package/crates/team-agent/src/coordinator/health.rs +95 -17
  17. package/crates/team-agent/src/coordinator/mod.rs +4 -0
  18. package/crates/team-agent/src/coordinator/runtime_detectors.rs +500 -0
  19. package/crates/team-agent/src/coordinator/runtime_observation.rs +58 -0
  20. package/crates/team-agent/src/coordinator/tick.rs +222 -69
  21. package/crates/team-agent/src/coordinator/types.rs +15 -3
  22. package/crates/team-agent/src/db/schema.rs +37 -2
  23. package/crates/team-agent/src/diagnose/comms.rs +226 -0
  24. package/crates/team-agent/src/diagnose/mod.rs +45 -0
  25. package/crates/team-agent/src/diagnose/orphans.rs +658 -0
  26. package/crates/team-agent/src/fake_worker.rs +146 -3
  27. package/crates/team-agent/src/leader/start.rs +121 -23
  28. package/crates/team-agent/src/leader/types.rs +44 -1
  29. package/crates/team-agent/src/lib.rs +3 -0
  30. package/crates/team-agent/src/lifecycle/display.rs +645 -47
  31. package/crates/team-agent/src/lifecycle/launch.rs +1061 -146
  32. package/crates/team-agent/src/lifecycle/mod.rs +2 -0
  33. package/crates/team-agent/src/lifecycle/profile_launch.rs +810 -0
  34. package/crates/team-agent/src/lifecycle/profile_smoke.rs +522 -0
  35. package/crates/team-agent/src/lifecycle/restart/agent.rs +99 -23
  36. package/crates/team-agent/src/lifecycle/restart/common.rs +183 -24
  37. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +498 -22
  38. package/crates/team-agent/src/lifecycle/restart/remove.rs +27 -7
  39. package/crates/team-agent/src/lifecycle/restart/team_state.rs +19 -0
  40. package/crates/team-agent/src/lifecycle/restart.rs +24 -1
  41. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +5 -5
  42. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +37 -7
  43. package/crates/team-agent/src/lifecycle/types.rs +19 -0
  44. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  45. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +341 -0
  46. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +10 -0
  47. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +158 -0
  48. package/crates/team-agent/src/mcp_server/mod.rs +3 -74
  49. package/crates/team-agent/src/mcp_server/tests/scoped.rs +1 -1
  50. package/crates/team-agent/src/mcp_server/tests/send.rs +6 -5
  51. package/crates/team-agent/src/mcp_server/tools.rs +312 -111
  52. package/crates/team-agent/src/mcp_server/types.rs +6 -4
  53. package/crates/team-agent/src/mcp_server/wire.rs +19 -7
  54. package/crates/team-agent/src/message_store.rs +21 -4
  55. package/crates/team-agent/src/messaging/delivery.rs +470 -59
  56. package/crates/team-agent/src/messaging/mod.rs +9 -6
  57. package/crates/team-agent/src/messaging/results.rs +353 -63
  58. package/crates/team-agent/src/messaging/selftest.rs +199 -12
  59. package/crates/team-agent/src/messaging/send.rs +35 -3
  60. package/crates/team-agent/src/messaging/tests/runtime.rs +19 -4
  61. package/crates/team-agent/src/messaging/types.rs +11 -3
  62. package/crates/team-agent/src/os_probe.rs +119 -0
  63. package/crates/team-agent/src/packaging/migrate.rs +10 -2
  64. package/crates/team-agent/src/packaging/tests.rs +23 -0
  65. package/crates/team-agent/src/provider/adapter.rs +564 -63
  66. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +1 -7
  67. package/crates/team-agent/src/provider/classify.rs +51 -4
  68. package/crates/team-agent/src/provider/helpers.rs +10 -1
  69. package/crates/team-agent/src/provider/startup_prompt.rs +94 -0
  70. package/crates/team-agent/src/provider/types.rs +47 -0
  71. package/crates/team-agent/src/session_capture.rs +616 -0
  72. package/crates/team-agent/src/state/persist.rs +170 -1
  73. package/crates/team-agent/src/state/projection.rs +141 -8
  74. package/crates/team-agent/src/state/selector.rs +5 -2
  75. package/crates/team-agent/src/tmux_backend.rs +161 -64
  76. package/crates/team-agent/src/transport/test_support.rs +9 -0
  77. package/crates/team-agent/src/transport/tests/wire.rs +4 -0
  78. package/crates/team-agent/src/transport.rs +13 -2
  79. package/package.json +4 -4
@@ -134,13 +134,18 @@ pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, Cl
134
134
  || team.join("profiles").exists();
135
135
  let profile_dir_check = json!({
136
136
  "name": "profile_dir",
137
- "ok": profile_dir_exists,
138
- });
139
- let profile_smoke_check = json!({
140
- "name": "profile_smoke",
141
137
  "ok": true,
142
- "status": if profile_dir_exists { "passed" } else { "not_required" },
138
+ "status": if profile_dir_exists { "present" } else { "not_required" },
143
139
  });
140
+ let profile_smoke_check = build_profile_smoke_check_for_team(team)?;
141
+ if profile_smoke_check.get("ok").and_then(Value::as_bool) == Some(false) {
142
+ let reason = profile_smoke_check
143
+ .get("reason")
144
+ .or_else(|| profile_smoke_check.get("error"))
145
+ .and_then(Value::as_str)
146
+ .unwrap_or("profile smoke failed");
147
+ next_actions.push(json!(format!("fix compatible_api profile smoke: {reason}")));
148
+ }
144
149
  checks.push(json!({
145
150
  "name": "profiles",
146
151
  "ok": true,
@@ -197,14 +202,110 @@ pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, Cl
197
202
  Ok(report)
198
203
  }
199
204
 
205
+ pub(crate) fn build_profile_smoke_check_for_team(team: &std::path::Path) -> Result<Value, CliError> {
206
+ let workspace = crate::model::paths::team_workspace(team)
207
+ .map_err(|error| CliError::Runtime(error.to_string()))?;
208
+ let spec = match crate::compiler::compile_team(team) {
209
+ Ok(spec) => spec,
210
+ Err(error) => {
211
+ return Ok(json!({
212
+ "name": "profile_smoke",
213
+ "ok": false,
214
+ "status": "profile_invalid",
215
+ "reason": error.to_string(),
216
+ "secret_values_printed": false,
217
+ "checks": [],
218
+ }));
219
+ }
220
+ };
221
+ let agents = spec
222
+ .get("agents")
223
+ .and_then(crate::model::yaml::Value::as_list)
224
+ .unwrap_or(&[]);
225
+ let checks = crate::lifecycle::profile_smoke::profile_smoke_checks_for_agents_with_profile_dir(
226
+ &workspace,
227
+ agents,
228
+ Some(&team.join("profiles")),
229
+ crate::lifecycle::profile_smoke::DEFAULT_PROFILE_SMOKE_TIMEOUT,
230
+ );
231
+ Ok(aggregate_profile_smoke_checks(checks))
232
+ }
233
+
234
+ fn aggregate_profile_smoke_checks(checks: Vec<Value>) -> Value {
235
+ let failed = checks
236
+ .iter()
237
+ .filter(|check| check.get("ok").and_then(Value::as_bool) == Some(false))
238
+ .cloned()
239
+ .collect::<Vec<_>>();
240
+ let ok = failed.is_empty();
241
+ let status = if !ok {
242
+ failed
243
+ .first()
244
+ .and_then(|check| check.get("status").and_then(Value::as_str))
245
+ .unwrap_or("smoke_failed")
246
+ } else if checks
247
+ .iter()
248
+ .any(|check| check.get("status").and_then(Value::as_str) == Some("smoke_passed"))
249
+ {
250
+ "smoke_passed"
251
+ } else if checks
252
+ .iter()
253
+ .any(|check| check.get("status").and_then(Value::as_str) == Some("skipped_by_profile"))
254
+ {
255
+ "skipped_by_profile"
256
+ } else {
257
+ "not_required"
258
+ };
259
+ let mut out = json!({
260
+ "name": "profile_smoke",
261
+ "ok": ok,
262
+ "status": status,
263
+ "checks": checks,
264
+ "secret_values_printed": false,
265
+ });
266
+ if let Some(first) = failed.first() {
267
+ copy_optional_field(first, &mut out, "reason");
268
+ copy_optional_field(first, &mut out, "http_status");
269
+ copy_optional_field(first, &mut out, "endpoint");
270
+ copy_optional_field(first, &mut out, "error");
271
+ } else if let Some(first_passed) = checks
272
+ .iter()
273
+ .find(|check| check.get("status").and_then(Value::as_str) == Some("smoke_passed"))
274
+ {
275
+ copy_optional_field(first_passed, &mut out, "http_status");
276
+ copy_optional_field(first_passed, &mut out, "endpoint");
277
+ }
278
+ out
279
+ }
280
+
281
+ fn copy_optional_field(from: &Value, to: &mut Value, key: &str) {
282
+ let Some(value) = from.get(key).cloned() else {
283
+ return;
284
+ };
285
+ let Some(obj) = to.as_object_mut() else {
286
+ return;
287
+ };
288
+ obj.insert(key.to_string(), value);
289
+ }
290
+
200
291
  pub(crate) fn build_wait_ready_report(workspace: &std::path::Path, timeout: f64) -> Result<Value, CliError> {
292
+ let selected = crate::state::selector::resolve_active_team(
293
+ workspace,
294
+ None,
295
+ crate::state::selector::SelectorMode::RuntimeOnly,
296
+ )
297
+ .map_err(|e| CliError::Runtime(e.to_string()))?;
201
298
  let timeout = if timeout.is_finite() && timeout > 0.0 { timeout } else { 0.0 };
202
299
  let deadline = std::time::Instant::now() + std::time::Duration::from_secs_f64(timeout);
203
300
  let mut readiness;
204
301
  loop {
205
- let mut state = crate::state::persist::load_runtime_state(workspace)?;
206
- inject_tmux_session_present(workspace, &mut state);
207
- inject_message_counts(workspace, &mut state)?;
302
+ let mut state = crate::state::projection::select_runtime_state(
303
+ &selected.run_workspace,
304
+ Some(&selected.team_key),
305
+ )
306
+ .unwrap_or_else(|_| selected.state.clone());
307
+ inject_tmux_session_present(&selected.run_workspace, &mut state);
308
+ inject_message_counts(&selected.run_workspace, &mut state)?;
208
309
  readiness = wait_readiness(&state);
209
310
  let awaiting_trust = readiness
210
311
  .get("awaiting_trust_prompt")
@@ -231,6 +332,14 @@ pub(crate) fn build_wait_ready_report(workspace: &std::path::Path, timeout: f64)
231
332
  )
232
333
  } else if ready {
233
334
  (true, "ready", "ready", "workers ready", Vec::new())
335
+ } else if readiness.get("session_capture_complete").and_then(Value::as_bool) == Some(false) {
336
+ (
337
+ false,
338
+ "pending",
339
+ "session_capture_incomplete",
340
+ "provider session capture is incomplete",
341
+ vec![json!("wait for provider session capture before restart")],
342
+ )
234
343
  } else {
235
344
  (
236
345
  false,
@@ -241,7 +350,7 @@ pub(crate) fn build_wait_ready_report(workspace: &std::path::Path, timeout: f64)
241
350
  )
242
351
  };
243
352
  let details_log = write_details_log(
244
- workspace,
353
+ &selected.run_workspace,
245
354
  "wait-ready",
246
355
  &json!({
247
356
  "ok": ok,
@@ -282,6 +391,20 @@ pub(crate) fn wait_readiness(state: &Value) -> Value {
282
391
  let mut mcp_ready = false;
283
392
  let mut task_prompt_delivered = false;
284
393
  let mut awaiting_trust_prompt = false;
394
+ let mut incomplete_sessions = Vec::new();
395
+ let all_attached_receiver = state
396
+ .get("leader_receiver")
397
+ .and_then(Value::as_object)
398
+ .is_none_or(|receiver| {
399
+ receiver
400
+ .get("status")
401
+ .and_then(Value::as_str)
402
+ == Some("attached")
403
+ || receiver
404
+ .get("pane_id")
405
+ .and_then(Value::as_str)
406
+ .is_some_and(|pane| !pane.is_empty() && pane != "__team_agent_unbound__")
407
+ });
285
408
 
286
409
  if let Some(agents) = agents {
287
410
  process_started = state
@@ -314,14 +437,25 @@ pub(crate) fn wait_readiness(state: &Value) -> Value {
314
437
  .and_then(Value::as_str)
315
438
  == Some("awaiting_trust_prompt")
316
439
  });
440
+ incomplete_sessions = crate::session_capture::incomplete_interacted_resumable_agent_ids(state);
317
441
  }
318
- let ready = process_started && cli_prompt_ready && mcp_ready && task_prompt_delivered;
442
+ let all_resumable_have_session = incomplete_sessions.is_empty();
443
+ let session_capture_incomplete = !all_resumable_have_session;
444
+ let all_spawned = process_started && cli_prompt_ready && mcp_ready;
445
+ let ready = all_spawned && all_attached_receiver && all_resumable_have_session;
319
446
  json!({
447
+ "all_attached_receiver": all_attached_receiver,
448
+ "all_resumable_have_session": all_resumable_have_session,
449
+ "all_spawned": all_spawned,
320
450
  "awaiting_trust_prompt": awaiting_trust_prompt,
321
451
  "cli_prompt_ready": cli_prompt_ready,
452
+ "incomplete_session_capture_agents": incomplete_sessions.clone(),
322
453
  "mcp_ready": mcp_ready,
323
454
  "process_started": process_started,
324
455
  "ready": ready,
456
+ "session_capture_complete": all_resumable_have_session,
457
+ "session_capture_incomplete": session_capture_incomplete,
458
+ "pending_session_agent_ids": incomplete_sessions,
325
459
  "task_prompt_delivered": task_prompt_delivered,
326
460
  })
327
461
  }