@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.
Files changed (156) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +129 -54
  4. package/crates/team-agent/src/cli/diagnose.rs +1 -2
  5. package/crates/team-agent/src/cli/emit.rs +1 -7
  6. package/crates/team-agent/src/cli/helpers.rs +3 -1
  7. package/crates/team-agent/src/cli/leader.rs +2 -1
  8. package/crates/team-agent/src/cli/mod.rs +126 -16
  9. package/crates/team-agent/src/cli/named_address.rs +14 -5
  10. package/crates/team-agent/src/cli/profile.rs +19 -7
  11. package/crates/team-agent/src/cli/send.rs +9 -3
  12. package/crates/team-agent/src/cli/status.rs +8 -30
  13. package/crates/team-agent/src/cli/status_port.rs +1341 -1272
  14. package/crates/team-agent/src/cli/tests/base.rs +738 -660
  15. package/crates/team-agent/src/cli/tests/compile.rs +45 -18
  16. package/crates/team-agent/src/cli/tests/divergence.rs +462 -444
  17. package/crates/team-agent/src/cli/tests/lane_c.rs +365 -282
  18. package/crates/team-agent/src/cli/tests/leader_watch.rs +356 -329
  19. package/crates/team-agent/src/cli/tests/main_preserved.rs +672 -564
  20. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +284 -224
  21. package/crates/team-agent/src/cli/tests/mod.rs +17 -7
  22. package/crates/team-agent/src/cli/tests/named_address.rs +8 -2
  23. package/crates/team-agent/src/cli/tests/peer_allow.rs +10 -2
  24. package/crates/team-agent/src/cli/tests/run_delegation.rs +314 -273
  25. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +26 -15
  26. package/crates/team-agent/src/cli/tests/status_send.rs +707 -629
  27. package/crates/team-agent/src/cli/tests/verb_install_skill.rs +20 -4
  28. package/crates/team-agent/src/cli/tests/verb_profile.rs +46 -17
  29. package/crates/team-agent/src/cli/tests/verb_validate.rs +15 -3
  30. package/crates/team-agent/src/codex_app_server.rs +2 -5
  31. package/crates/team-agent/src/compiler/tests.rs +139 -33
  32. package/crates/team-agent/src/compiler.rs +55 -22
  33. package/crates/team-agent/src/conpty/backend.rs +23 -33
  34. package/crates/team-agent/src/coordinator/backoff.rs +2 -7
  35. package/crates/team-agent/src/coordinator/conpty_shim.rs +55 -67
  36. package/crates/team-agent/src/coordinator/health.rs +46 -31
  37. package/crates/team-agent/src/coordinator/mod.rs +3 -3
  38. package/crates/team-agent/src/coordinator/orphan.rs +22 -10
  39. package/crates/team-agent/src/coordinator/steps/abnormal.rs +51 -56
  40. package/crates/team-agent/src/coordinator/tests/abnormal.rs +55 -19
  41. package/crates/team-agent/src/coordinator/tests/basics.rs +179 -41
  42. package/crates/team-agent/src/coordinator/tests/daemon.rs +53 -13
  43. package/crates/team-agent/src/coordinator/tests/health_sync.rs +78 -19
  44. package/crates/team-agent/src/coordinator/tests/main_preserved.rs +61 -11
  45. package/crates/team-agent/src/coordinator/tests/mod.rs +33 -39
  46. package/crates/team-agent/src/coordinator/tests/spine.rs +52 -12
  47. package/crates/team-agent/src/coordinator/tests/takeover.rs +73 -15
  48. package/crates/team-agent/src/coordinator/tests/tick_core.rs +50 -15
  49. package/crates/team-agent/src/coordinator/tests/watch.rs +74 -20
  50. package/crates/team-agent/src/db/message_store.rs +138 -30
  51. package/crates/team-agent/src/db/migration.rs +249 -61
  52. package/crates/team-agent/src/db/schema.rs +303 -82
  53. package/crates/team-agent/src/diagnose/comms.rs +9 -2
  54. package/crates/team-agent/src/diagnose/mod.rs +1 -3
  55. package/crates/team-agent/src/diagnose/orphans.rs +79 -61
  56. package/crates/team-agent/src/event_log.rs +70 -16
  57. package/crates/team-agent/src/layout/manager.rs +15 -4
  58. package/crates/team-agent/src/layout/mod.rs +4 -4
  59. package/crates/team-agent/src/layout/overlay.rs +10 -3
  60. package/crates/team-agent/src/layout/placement.rs +5 -1
  61. package/crates/team-agent/src/layout/recovery.rs +4 -2
  62. package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
  63. package/crates/team-agent/src/layout/sessions.rs +17 -9
  64. package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
  65. package/crates/team-agent/src/layout/worker_env.rs +87 -19
  66. package/crates/team-agent/src/leader/helpers.rs +7 -1
  67. package/crates/team-agent/src/leader/lease.rs +199 -89
  68. package/crates/team-agent/src/leader/owner_bind.rs +55 -22
  69. package/crates/team-agent/src/leader/provider_attribution.rs +25 -6
  70. package/crates/team-agent/src/leader/rediscover/tests.rs +88 -24
  71. package/crates/team-agent/src/leader/rediscover.rs +74 -25
  72. package/crates/team-agent/src/leader/registry.rs +1 -1
  73. package/crates/team-agent/src/leader/start.rs +75 -54
  74. package/crates/team-agent/src/leader/takeover.rs +46 -11
  75. package/crates/team-agent/src/leader/tests/basics.rs +320 -167
  76. package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
  77. package/crates/team-agent/src/leader/tests/identity.rs +428 -356
  78. package/crates/team-agent/src/leader/tests/idle.rs +285 -254
  79. package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
  80. package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
  81. package/crates/team-agent/src/leader/tests/mod.rs +115 -99
  82. package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
  83. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
  84. package/crates/team-agent/src/lib.rs +4 -4
  85. package/crates/team-agent/src/lifecycle/display.rs +7 -3
  86. package/crates/team-agent/src/lifecycle/launch.rs +55 -15
  87. package/crates/team-agent/src/lifecycle/mod.rs +9 -1
  88. package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
  89. package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
  90. package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
  91. package/crates/team-agent/src/lifecycle/restart/common.rs +6 -2
  92. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
  93. package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
  94. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
  95. package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
  96. package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
  97. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
  98. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
  99. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
  100. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
  101. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
  102. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +3 -0
  103. package/crates/team-agent/src/lifecycle/tests.rs +2 -2
  104. package/crates/team-agent/src/main.rs +4 -4
  105. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
  106. package/crates/team-agent/src/mcp_server/mod.rs +11 -2
  107. package/crates/team-agent/src/mcp_server/types.rs +14 -3
  108. package/crates/team-agent/src/mcp_server/wire.rs +230 -68
  109. package/crates/team-agent/src/messaging/delivery.rs +20 -18
  110. package/crates/team-agent/src/messaging/helpers.rs +25 -4
  111. package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
  112. package/crates/team-agent/src/messaging/mod.rs +2 -3
  113. package/crates/team-agent/src/messaging/selftest.rs +46 -26
  114. package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
  115. package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
  116. package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
  117. package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
  118. package/crates/team-agent/src/messaging/trust.rs +25 -2
  119. package/crates/team-agent/src/messaging/watchers.rs +37 -9
  120. package/crates/team-agent/src/model/enums.rs +65 -16
  121. package/crates/team-agent/src/model/ids.rs +12 -3
  122. package/crates/team-agent/src/model/paths.rs +28 -7
  123. package/crates/team-agent/src/model/permissions.rs +176 -33
  124. package/crates/team-agent/src/model/routing.rs +66 -20
  125. package/crates/team-agent/src/model/spec.rs +365 -69
  126. package/crates/team-agent/src/model/task_graph.rs +36 -9
  127. package/crates/team-agent/src/model/yaml/tests.rs +24 -6
  128. package/crates/team-agent/src/model/yaml.rs +7 -6
  129. package/crates/team-agent/src/packaging/install.rs +23 -9
  130. package/crates/team-agent/src/packaging/migrate.rs +5 -7
  131. package/crates/team-agent/src/packaging/mod.rs +9 -1
  132. package/crates/team-agent/src/packaging/repair.rs +13 -6
  133. package/crates/team-agent/src/packaging/tests.rs +63 -16
  134. package/crates/team-agent/src/packaging/types.rs +22 -7
  135. package/crates/team-agent/src/platform/argv.rs +4 -1
  136. package/crates/team-agent/src/platform/file_lock.rs +22 -8
  137. package/crates/team-agent/src/platform/process.rs +54 -24
  138. package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
  139. package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
  140. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
  141. package/crates/team-agent/src/provider/classify.rs +127 -42
  142. package/crates/team-agent/src/provider/faults.rs +17 -5
  143. package/crates/team-agent/src/provider/helpers.rs +6 -5
  144. package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
  145. package/crates/team-agent/src/state/persist.rs +28 -0
  146. package/crates/team-agent/src/state/repository.rs +8 -3
  147. package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
  148. package/crates/team-agent/src/tmux_backend.rs +80 -44
  149. package/crates/team-agent/src/topology.rs +40 -20
  150. package/crates/team-agent/src/transport/test_support.rs +20 -23
  151. package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
  152. package/crates/team-agent/src/transport/tests/mod.rs +178 -187
  153. package/crates/team-agent/src/transport/tests/wire.rs +561 -525
  154. package/crates/team-agent/src/transport.rs +12 -17
  155. package/crates/team-agent/src/transport_factory.rs +29 -14
  156. package/package.json +4 -4
@@ -70,11 +70,9 @@ pub fn read_front_matter(path: &Path) -> Result<(Value, String), ModelError> {
70
70
  let after_meta = rest.get(close..).ok_or_else(|| {
71
71
  ModelError::Validation(format!("{}: unterminated front matter", path.display()))
72
72
  })?;
73
- let after_marker = after_meta
74
- .strip_prefix("\n---")
75
- .ok_or_else(|| {
76
- ModelError::Validation(format!("{}: unterminated front matter", path.display()))
77
- })?;
73
+ let after_marker = after_meta.strip_prefix("\n---").ok_or_else(|| {
74
+ ModelError::Validation(format!("{}: unterminated front matter", path.display()))
75
+ })?;
78
76
  let meta = if raw_meta.trim().is_empty() {
79
77
  Value::Map(Vec::new())
80
78
  } else {
@@ -89,7 +87,9 @@ pub fn read_front_matter(path: &Path) -> Result<(Value, String), ModelError> {
89
87
  Ok((meta, after_marker.trim_start_matches('\n').to_string()))
90
88
  }
91
89
 
92
- pub fn ignored_owner_team_id_from_team_md(team_dir: &Path) -> Result<Option<IgnoredTeamField>, ModelError> {
90
+ pub fn ignored_owner_team_id_from_team_md(
91
+ team_dir: &Path,
92
+ ) -> Result<Option<IgnoredTeamField>, ModelError> {
93
93
  let team_md = team_dir.join("TEAM.md");
94
94
  if !team_md.exists() {
95
95
  return Ok(None);
@@ -138,8 +138,8 @@ pub fn compile_team(team_dir: &Path) -> Result<Value, ModelError> {
138
138
  for entry in fs::read_dir(&agents_dir)
139
139
  .map_err(|e| ModelError::Runtime(format!("{}: {e}", agents_dir.display())))?
140
140
  {
141
- let entry = entry
142
- .map_err(|e| ModelError::Runtime(format!("{}: {e}", agents_dir.display())))?;
141
+ let entry =
142
+ entry.map_err(|e| ModelError::Runtime(format!("{}: {e}", agents_dir.display())))?;
143
143
  let path = entry.path();
144
144
  if path.extension().and_then(|s| s.to_str()) == Some("md") {
145
145
  role_paths.push(path);
@@ -156,7 +156,8 @@ pub fn compile_team(team_dir: &Path) -> Result<Value, ModelError> {
156
156
 
157
157
  let workspace = paths::team_workspace(team_dir)?;
158
158
  let workspace_s = workspace.display().to_string();
159
- let team_name = string_field(&team_meta, "name").unwrap_or_else(|| team_dir_parent_name(team_dir));
159
+ let team_name =
160
+ string_field(&team_meta, "name").unwrap_or_else(|| team_dir_parent_name(team_dir));
160
161
  let objective = string_field(&team_meta, "objective")
161
162
  .or_else(|| non_empty_trimmed(&team_body))
162
163
  .unwrap_or_else(|| "Team Agent document-driven team.".to_string());
@@ -180,7 +181,10 @@ pub fn compile_team(team_dir: &Path) -> Result<Value, ModelError> {
180
181
  .map(|id| {
181
182
  map(vec![
182
183
  ("id", Value::Str(format!("route-{id}"))),
183
- ("match", map(vec![("assignee", list_str(vec![id.as_str()]))])),
184
+ (
185
+ "match",
186
+ map(vec![("assignee", list_str(vec![id.as_str()]))]),
187
+ ),
184
188
  ("assign_to", Value::Str(id.clone())),
185
189
  ("priority", Value::Int(10)),
186
190
  ])
@@ -250,9 +254,15 @@ pub fn compile_team(team_dir: &Path) -> Result<Value, ModelError> {
250
254
  map(vec![
251
255
  ("protocol", Value::Str("mcp_inbox".to_string())),
252
256
  ("topology", Value::Str("leader_centered".to_string())),
253
- ("worker_to_worker", bool_field(&team_meta, "worker_to_worker", true)),
257
+ (
258
+ "worker_to_worker",
259
+ bool_field(&team_meta, "worker_to_worker", true),
260
+ ),
254
261
  ("ack_timeout_sec", Value::Int(60)),
255
- ("result_format", Value::Str("result_envelope_v1".to_string())),
262
+ (
263
+ "result_format",
264
+ Value::Str("result_envelope_v1".to_string()),
265
+ ),
256
266
  (
257
267
  "message_store",
258
268
  map(vec![
@@ -273,19 +283,34 @@ pub fn compile_team(team_dir: &Path) -> Result<Value, ModelError> {
273
283
  .unwrap_or_else(|| "adaptive".to_string()),
274
284
  ),
275
285
  ),
276
- ("session_name", Value::Str(session_name(&team_meta, &team_name))),
286
+ (
287
+ "session_name",
288
+ Value::Str(session_name(&team_meta, &team_name)),
289
+ ),
277
290
  ("auto_launch", Value::Bool(true)),
278
291
  ("require_user_approval_before_launch", Value::Bool(true)),
279
- ("max_active_agents", Value::Int(max_active_agents(agent_ids.len()))),
292
+ (
293
+ "max_active_agents",
294
+ Value::Int(max_active_agents(agent_ids.len())),
295
+ ),
280
296
  ("startup_order", list_str(agent_ids)),
281
297
  (
282
298
  "dangerous_auto_approve",
283
299
  bool_field(&team_meta, "dangerous_auto_approve", false),
284
300
  ),
285
301
  ("fast", bool_field(&team_meta, "fast", false)),
286
- ("tick_interval_sec", int_field(&team_meta, "tick_interval_sec", 2)),
287
- ("push_min_interval_sec", int_field(&team_meta, "push_min_interval_sec", 60)),
288
- ("stuck_timeout_sec", int_field(&team_meta, "stuck_timeout_sec", 300)),
302
+ (
303
+ "tick_interval_sec",
304
+ int_field(&team_meta, "tick_interval_sec", 2),
305
+ ),
306
+ (
307
+ "push_min_interval_sec",
308
+ int_field(&team_meta, "push_min_interval_sec", 60),
309
+ ),
310
+ (
311
+ "stuck_timeout_sec",
312
+ int_field(&team_meta, "stuck_timeout_sec", 300),
313
+ ),
289
314
  ]),
290
315
  ),
291
316
  (
@@ -310,11 +335,17 @@ pub fn compile_team(team_dir: &Path) -> Result<Value, ModelError> {
310
335
  "tasks",
311
336
  Value::List(vec![map(vec![
312
337
  ("id", Value::Str("task_initial".to_string())),
313
- ("title", Value::Str("Initial document-driven team task".to_string())),
338
+ (
339
+ "title",
340
+ Value::Str("Initial document-driven team task".to_string()),
341
+ ),
314
342
  ("type", Value::Str("implementation".to_string())),
315
343
  ("assignee", Value::Str(default_assignee)),
316
344
  ("deps", Value::List(Vec::new())),
317
- ("acceptance", list_str(vec!["Worker reports valid result_envelope_v1"])),
345
+ (
346
+ "acceptance",
347
+ list_str(vec!["Worker reports valid result_envelope_v1"]),
348
+ ),
318
349
  ("status", Value::Str("pending".to_string())),
319
350
  ("requires_tools", list_str(vec!["mcp_team"])),
320
351
  ("files", Value::List(Vec::new())),
@@ -451,7 +482,9 @@ where
451
482
  }
452
483
 
453
484
  fn string_field(meta: &Value, key: &str) -> Option<String> {
454
- meta.get(key).and_then(Value::as_str).map(ToString::to_string)
485
+ meta.get(key)
486
+ .and_then(Value::as_str)
487
+ .map(ToString::to_string)
455
488
  }
456
489
 
457
490
  fn required_string(meta: &Value, path: &Path, key: &str) -> Result<String, ModelError> {
@@ -524,8 +557,8 @@ fn resolve_model(role_meta: &Value, team_meta: &Value, provider: &str) -> Value
524
557
  if let Some(model) = string_field(role_meta, "model") {
525
558
  return Value::Str(model);
526
559
  }
527
- if let Some(model) = provider_model(team_meta, provider)
528
- .or_else(|| string_field(team_meta, "default_model"))
560
+ if let Some(model) =
561
+ provider_model(team_meta, provider).or_else(|| string_field(team_meta, "default_model"))
529
562
  {
530
563
  return Value::Str(model);
531
564
  }
@@ -53,9 +53,9 @@ use std::sync::atomic::{AtomicU64, Ordering};
53
53
  use crate::model::enums::PaneLiveness;
54
54
  use crate::transport::{
55
55
  AttachOutcome, BackendKind, CaptureRange, CapturedText, InjectPayload, InjectReport,
56
- InjectStage, InjectVerification, Key, PaneField, PaneId, PaneInfo, SessionName,
57
- SetEnvOutcome, SpawnResult, SubmitVerification, Target, Transport, TransportError,
58
- TurnVerification, WindowName,
56
+ InjectStage, InjectVerification, Key, PaneField, PaneId, PaneInfo, SessionName, SetEnvOutcome,
57
+ SpawnResult, SubmitVerification, Target, Transport, TransportError, TurnVerification,
58
+ WindowName,
59
59
  };
60
60
 
61
61
  use conpty_transport::{
@@ -164,8 +164,8 @@ impl ConPtyBackend {
164
164
  detail: format!("hello failed: {:?}", resp.error),
165
165
  });
166
166
  }
167
- let hello: HelloResult = serde_json::from_value(resp.result)
168
- .map_err(|e| TransportError::MuxUnavailable {
167
+ let hello: HelloResult =
168
+ serde_json::from_value(resp.result).map_err(|e| TransportError::MuxUnavailable {
169
169
  backend: BackendKind::ConPty,
170
170
  detail: format!("hello response malformed: {e}"),
171
171
  })?;
@@ -303,12 +303,11 @@ impl ConPtyBackend {
303
303
  source: std::io::Error::other(e),
304
304
  })?;
305
305
  let resp = self.dispatch(Op::Spawn, payload)?;
306
- let spawn: ProtoSpawnResult = serde_json::from_value(resp.result).map_err(|e| {
307
- TransportError::Spawn {
306
+ let spawn: ProtoSpawnResult =
307
+ serde_json::from_value(resp.result).map_err(|e| TransportError::Spawn {
308
308
  backend: BackendKind::ConPty,
309
309
  source: std::io::Error::other(e),
310
- }
311
- })?;
310
+ })?;
312
311
  Ok(SpawnResult {
313
312
  pane_id: PaneId::new(spawn.pane_id),
314
313
  session: SessionName::new(spawn.session),
@@ -368,13 +367,17 @@ fn map_protocol_error(err: Option<&ProtocolError>) -> TransportError {
368
367
  backend: BackendKind::ConPty,
369
368
  detail: format!("pipe_token_mismatch: {message}"),
370
369
  },
371
- ProtocolError::SchemaSkew { message, sent, expected } => TransportError::MuxUnavailable {
370
+ ProtocolError::SchemaSkew {
371
+ message,
372
+ sent,
373
+ expected,
374
+ } => TransportError::MuxUnavailable {
372
375
  backend: BackendKind::ConPty,
373
376
  detail: format!("schema_skew sent={sent} expected={expected}: {message}"),
374
377
  },
375
- ProtocolError::TargetNotFound { message } => {
376
- TransportError::TargetNotFound { target: message.clone() }
377
- }
378
+ ProtocolError::TargetNotFound { message } => TransportError::TargetNotFound {
379
+ target: message.clone(),
380
+ },
378
381
  ProtocolError::Spawn { message } => TransportError::Spawn {
379
382
  backend: BackendKind::ConPty,
380
383
  source: std::io::Error::other(message.clone()),
@@ -556,11 +559,10 @@ impl Transport for ConPtyBackend {
556
559
  source: std::io::Error::other(e),
557
560
  })?;
558
561
  let resp = self.dispatch(Op::Capture, payload)?;
559
- let cap: protocol::CaptureResult = serde_json::from_value(resp.result).map_err(|e| {
560
- TransportError::Capture {
562
+ let cap: protocol::CaptureResult =
563
+ serde_json::from_value(resp.result).map_err(|e| TransportError::Capture {
561
564
  source: std::io::Error::other(e),
562
- }
563
- })?;
565
+ })?;
564
566
  Ok(CapturedText {
565
567
  text: cap.text,
566
568
  range,
@@ -572,10 +574,7 @@ impl Transport for ConPtyBackend {
572
574
  }
573
575
 
574
576
  fn liveness(&self, pane: &PaneId) -> Result<PaneLiveness, TransportError> {
575
- let resp = self.dispatch(
576
- Op::Liveness,
577
- serde_json::json!({"pane_id": pane.as_str()}),
578
- )?;
577
+ let resp = self.dispatch(Op::Liveness, serde_json::json!({"pane_id": pane.as_str()}))?;
579
578
  let known = resp.result["known"].as_bool().unwrap_or(false);
580
579
  let alive = resp.result["alive"].as_bool().unwrap_or(false);
581
580
  Ok(match (known, alive) {
@@ -645,10 +644,7 @@ impl Transport for ConPtyBackend {
645
644
  }
646
645
 
647
646
  fn has_pane(&self, pane: &PaneId) -> Result<Option<bool>, TransportError> {
648
- let resp = self.dispatch(
649
- Op::HasPane,
650
- serde_json::json!({"pane_id": pane.as_str()}),
651
- )?;
647
+ let resp = self.dispatch(Op::HasPane, serde_json::json!({"pane_id": pane.as_str()}))?;
652
648
  Ok(Some(resp.result["present"].as_bool().unwrap_or(false)))
653
649
  }
654
650
 
@@ -673,10 +669,7 @@ impl Transport for ConPtyBackend {
673
669
  fn kill_window(&self, target: &Target) -> Result<(), TransportError> {
674
670
  match target {
675
671
  Target::Pane(pane) => {
676
- self.dispatch(
677
- Op::KillPane,
678
- serde_json::json!({"pane_id": pane.as_str()}),
679
- )?;
672
+ self.dispatch(Op::KillPane, serde_json::json!({"pane_id": pane.as_str()}))?;
680
673
  }
681
674
  Target::SessionWindow { session, window } => {
682
675
  self.dispatch(
@@ -692,10 +685,7 @@ impl Transport for ConPtyBackend {
692
685
  }
693
686
 
694
687
  fn kill_pane(&self, pane: &PaneId) -> Result<(), TransportError> {
695
- self.dispatch(
696
- Op::KillPane,
697
- serde_json::json!({"pane_id": pane.as_str()}),
698
- )?;
688
+ self.dispatch(Op::KillPane, serde_json::json!({"pane_id": pane.as_str()}))?;
699
689
  Ok(())
700
690
  }
701
691
 
@@ -350,11 +350,7 @@ fn run_daemon_body_with_panic_marker(
350
350
  );
351
351
  match run_tick_with_panic_marker(&event_log, || coordinator.tick()) {
352
352
  Ok(report) => {
353
- let status = if report.stop {
354
- "stop_requested"
355
- } else {
356
- "ok"
357
- };
353
+ let status = if report.stop { "stop_requested" } else { "ok" };
358
354
  let _ = write_coordinator_heartbeat(
359
355
  &args.workspace,
360
356
  pid,
@@ -584,8 +580,7 @@ pub enum DaemonError {
584
580
  static SIGNAL_STOP_REQUESTED: std::sync::atomic::AtomicBool =
585
581
  std::sync::atomic::AtomicBool::new(false);
586
582
  #[cfg(unix)]
587
- static SIGNAL_STOP_NUMBER: std::sync::atomic::AtomicI32 =
588
- std::sync::atomic::AtomicI32::new(0);
583
+ static SIGNAL_STOP_NUMBER: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
589
584
 
590
585
  #[cfg(unix)]
591
586
  extern "C" fn coordinator_signal_handler(signal: libc::c_int) {
@@ -120,26 +120,31 @@ impl Drop for ShimHandle {
120
120
 
121
121
  #[derive(Debug, thiserror::Error)]
122
122
  pub enum ShimError {
123
- #[error("windows-shim binary not found: expected `{expected}` alongside team-agent.exe; \
124
- action: reinstall team-agent so the shim exe sits next to the main binary")]
123
+ #[error(
124
+ "windows-shim binary not found: expected `{expected}` alongside team-agent.exe; \
125
+ action: reinstall team-agent so the shim exe sits next to the main binary"
126
+ )]
125
127
  BinaryMissing { expected: String },
126
- #[error("windows-shim spawn failed: {source} (pipe_name={pipe_name}); \
127
- action: check windows-shim.exe permissions and PATH")]
128
+ #[error(
129
+ "windows-shim spawn failed: {source} (pipe_name={pipe_name}); \
130
+ action: check windows-shim.exe permissions and PATH"
131
+ )]
128
132
  Spawn {
129
133
  pipe_name: String,
130
134
  #[source]
131
135
  source: std::io::Error,
132
136
  },
133
- #[error("windows-shim connect timed out after {attempts} attempts (pipe_name={pipe_name}); \
137
+ #[error(
138
+ "windows-shim connect timed out after {attempts} attempts (pipe_name={pipe_name}); \
134
139
  action: check shim.err.log for CreateNamedPipeW / ACL errors, \
135
- then re-run team-agent quick-start")]
136
- ConnectTimeout {
137
- attempts: u32,
138
- pipe_name: String,
139
- },
140
- #[error("windows-shim hello handshake failed: {reason} (pipe_name={pipe_name}); \
140
+ then re-run team-agent quick-start"
141
+ )]
142
+ ConnectTimeout { attempts: u32, pipe_name: String },
143
+ #[error(
144
+ "windows-shim hello handshake failed: {reason} (pipe_name={pipe_name}); \
141
145
  action: ensure team-agent.exe and windows-shim.exe are the same build \
142
- (`sha256sum team-agent.exe windows-shim.exe` matches CI tracking)")]
146
+ (`sha256sum team-agent.exe windows-shim.exe` matches CI tracking)"
147
+ )]
143
148
  HelloFailed { pipe_name: String, reason: String },
144
149
  #[error("state persistence failed after shim spawn: {source}")]
145
150
  StatePersist {
@@ -191,7 +196,11 @@ fn fresh_pipe_token() -> String {
191
196
  .duration_since(std::time::UNIX_EPOCH)
192
197
  .map(|d| d.as_nanos())
193
198
  .unwrap_or(0);
194
- format!("{:08x}{:024x}", pid, nanos & 0xffff_ffff_ffff_ffff_ffff_ffff)
199
+ format!(
200
+ "{:08x}{:024x}",
201
+ pid,
202
+ nanos & 0xffff_ffff_ffff_ffff_ffff_ffff
203
+ )
195
204
  }
196
205
 
197
206
  /// Coordinator hook: spawn `windows-shim.exe` for the given
@@ -305,10 +314,7 @@ pub fn spawn_shim_and_handshake(
305
314
  let mut child = child;
306
315
  let _ = child.kill();
307
316
  let _ = child.wait();
308
- return Err(ShimError::HelloFailed {
309
- pipe_name,
310
- reason,
311
- });
317
+ return Err(ShimError::HelloFailed { pipe_name, reason });
312
318
  }
313
319
  }
314
320
  }
@@ -401,12 +407,14 @@ fn finalize(
401
407
  json!({})
402
408
  };
403
409
  // CR C-1: token NOT stored. Only pid/pipe_name/pipe_ready.
404
- let obj = state.as_object_mut().ok_or_else(|| ShimError::StatePersist {
405
- source: StateError::Io(std::io::Error::new(
406
- std::io::ErrorKind::InvalidData,
407
- "state.json root not an object",
408
- )),
409
- })?;
410
+ let obj = state
411
+ .as_object_mut()
412
+ .ok_or_else(|| ShimError::StatePersist {
413
+ source: StateError::Io(std::io::Error::new(
414
+ std::io::ErrorKind::InvalidData,
415
+ "state.json root not an object",
416
+ )),
417
+ })?;
410
418
  let transport = obj
411
419
  .entry("transport".to_string())
412
420
  .or_insert_with(|| json!({}));
@@ -540,12 +548,11 @@ pub fn reconnect_recorded_shim(
540
548
  team_key: &str,
541
549
  _workspace_hash: &str,
542
550
  ) -> Result<ShimHandle, ShimError> {
543
- let pipe_name = recorded_shim_pipe_name(workspace).ok_or_else(|| {
544
- ShimError::ConnectTimeout {
551
+ let pipe_name =
552
+ recorded_shim_pipe_name(workspace).ok_or_else(|| ShimError::ConnectTimeout {
545
553
  attempts: 0,
546
554
  pipe_name: "<state.transport.shim.pipe_name missing>".to_string(),
547
- }
548
- })?;
555
+ })?;
549
556
  // For reconnect we don't have the original pipe_token (CR C-1:
550
557
  // token was never persisted). Hello is designed to accept any
551
558
  // token from the client and echo back the shim's own token, so
@@ -561,24 +568,19 @@ pub fn reconnect_recorded_shim(
561
568
  let mut last_err: Option<ShimError> = None;
562
569
  for attempt in 1..=CONNECT_ATTEMPTS {
563
570
  match NamedPipeClient::connect(&pipe_name, 500) {
564
- Ok(mut client) => {
565
- match reconnect_hello(&mut client, team_key) {
566
- Ok(()) => {
567
- return Ok(ShimHandle {
568
- child: None,
569
- pid: recorded_shim_pid(workspace).unwrap_or(0),
570
- pipe_name,
571
- client: Some(client),
572
- });
573
- }
574
- Err(reason) => {
575
- return Err(ShimError::HelloFailed {
576
- pipe_name,
577
- reason,
578
- });
579
- }
571
+ Ok(mut client) => match reconnect_hello(&mut client, team_key) {
572
+ Ok(()) => {
573
+ return Ok(ShimHandle {
574
+ child: None,
575
+ pid: recorded_shim_pid(workspace).unwrap_or(0),
576
+ pipe_name,
577
+ client: Some(client),
578
+ });
580
579
  }
581
- }
580
+ Err(reason) => {
581
+ return Err(ShimError::HelloFailed { pipe_name, reason });
582
+ }
583
+ },
582
584
  Err(err) => {
583
585
  let _ = attempt;
584
586
  let _ = placeholder_token;
@@ -599,10 +601,7 @@ pub fn reconnect_recorded_shim(
599
601
  /// Reconnect Hello: the client sends Hello with the current workspace/
600
602
  /// team scope; the shim replies with its OWN pipe_token (which the
601
603
  /// client doesn't know yet). We just validate `resp.ok`.
602
- fn reconnect_hello(
603
- client: &mut NamedPipeClient,
604
- team_key: &str,
605
- ) -> Result<(), String> {
604
+ fn reconnect_hello(client: &mut NamedPipeClient, team_key: &str) -> Result<(), String> {
606
605
  use conpty_transport::{Op, PipeClient, Request};
607
606
  let req = Request::new(
608
607
  "coord-reconnect-hello",
@@ -633,15 +632,10 @@ fn reconnect_hello(
633
632
  /// The Windows-only `#[cfg]` gate is at the mod level (see
634
633
  /// `coordinator/mod.rs`); on Unix this file isn't compiled, so
635
634
  /// downstream callers must cfg-gate their reference themselves.
636
- pub fn mark_transport_unavailable(
637
- workspace: &Path,
638
- reason: &str,
639
- ) -> Result<(), StateError> {
635
+ pub fn mark_transport_unavailable(workspace: &Path, reason: &str) -> Result<(), StateError> {
640
636
  // Best-effort event emission — a failed event write should not
641
637
  // fail the caller. The state-clearing step below is authoritative.
642
- if let Ok(event_log) = std::panic::catch_unwind(|| {
643
- crate::event_log::EventLog::new(workspace)
644
- }) {
638
+ if let Ok(event_log) = std::panic::catch_unwind(|| crate::event_log::EventLog::new(workspace)) {
645
639
  let _ = event_log.write(
646
640
  "transport.conpty_shim_unavailable",
647
641
  serde_json::json!({
@@ -659,16 +653,10 @@ pub fn mark_transport_unavailable(
659
653
  }
660
654
  let text = std::fs::read_to_string(&state_path).map_err(StateError::from)?;
661
655
  let mut state: Value = serde_json::from_str(&text).unwrap_or_else(|_| serde_json::json!({}));
662
- if let Some(transport) = state
663
- .get_mut("transport")
664
- .and_then(|t| t.as_object_mut())
665
- {
656
+ if let Some(transport) = state.get_mut("transport").and_then(|t| t.as_object_mut()) {
666
657
  if let Some(shim) = transport.get_mut("shim").and_then(|s| s.as_object_mut()) {
667
658
  shim.insert("pipe_ready".to_string(), serde_json::json!(false));
668
- shim.insert(
669
- "unavailable_reason".to_string(),
670
- serde_json::json!(reason),
671
- );
659
+ shim.insert("unavailable_reason".to_string(), serde_json::json!(reason));
672
660
  }
673
661
  }
674
662
  save_runtime_state(workspace, &state)
@@ -687,9 +675,7 @@ mod tests {
687
675
  let src = include_str!("conpty_shim.rs");
688
676
  // Locate the `finalize` fn body and grep for pipe_token
689
677
  // insertion.
690
- let (_, finalize_and_after) = src
691
- .split_once("fn finalize(")
692
- .expect("finalize fn present");
678
+ let (_, finalize_and_after) = src.split_once("fn finalize(").expect("finalize fn present");
693
679
  let finalize_body = finalize_and_after
694
680
  .split_once("\n}")
695
681
  .map(|(body, _)| body)
@@ -711,7 +697,9 @@ mod tests {
711
697
  // {pid, pipe_name, pipe_ready}. If a future edit adds a 4th
712
698
  // key that carries a secret, this test fires.
713
699
  let src = include_str!("conpty_shim.rs");
714
- let (_, after) = src.split_once("\"shim\".to_string(),").expect("shim insert");
700
+ let (_, after) = src
701
+ .split_once("\"shim\".to_string(),")
702
+ .expect("shim insert");
715
703
  let block_end = after.find("}),").unwrap_or(after.len());
716
704
  let block = &after[..block_end];
717
705
  for expected in ["pid", "pipe_name", "pipe_ready"] {
@@ -178,13 +178,12 @@ pub fn start_coordinator_with_team(
178
178
  action: None,
179
179
  });
180
180
  }
181
- let rotation_reason = if matches!(health.status, CoordinatorHealthStatus::Running)
182
- && !health.metadata_ok
183
- {
184
- health.metadata_mismatch_reason.clone()
185
- } else {
186
- None
187
- };
181
+ let rotation_reason =
182
+ if matches!(health.status, CoordinatorHealthStatus::Running) && !health.metadata_ok {
183
+ health.metadata_mismatch_reason.clone()
184
+ } else {
185
+ None
186
+ };
188
187
  if matches!(health.status, CoordinatorHealthStatus::Running) && !health.metadata_ok {
189
188
  crate::event_log::EventLog::new(workspace.as_path()).write(
190
189
  "coordinator.rotation_required",
@@ -215,7 +214,10 @@ pub fn start_coordinator_with_team(
215
214
  binary_identity_relation: health.binary_identity_relation,
216
215
  log: None,
217
216
  schema_error: None,
218
- action: Some("refusing to rotate coordinator metadata that points at the caller process".to_string()),
217
+ action: Some(
218
+ "refusing to rotate coordinator metadata that points at the caller process"
219
+ .to_string(),
220
+ ),
219
221
  });
220
222
  }
221
223
  match stop_coordinator(workspace) {
@@ -413,8 +415,7 @@ fn discover_coordinator_pids(workspace: &WorkspacePath) -> Vec<Pid> {
413
415
  Command::new("ps").args(["-axo", "pid=,command="]),
414
416
  "ps_table",
415
417
  None,
416
- )
417
- {
418
+ ) {
418
419
  Ok(output) if output.status.success() => output,
419
420
  _ => return Vec::new(),
420
421
  };
@@ -432,9 +433,7 @@ fn discover_coordinator_pids(workspace: &WorkspacePath) -> Vec<Pid> {
432
433
 
433
434
  fn parse_ps_command_line(line: &str) -> Option<(u32, &str)> {
434
435
  let line = line.trim_start();
435
- let split = line
436
- .find(char::is_whitespace)
437
- .unwrap_or(line.len());
436
+ let split = line.find(char::is_whitespace).unwrap_or(line.len());
438
437
  let pid = line.get(..split)?.trim().parse::<u32>().ok()?;
439
438
  let command = line.get(split..)?.trim();
440
439
  Some((pid, command))
@@ -455,9 +454,13 @@ fn coordinator_command_matches_workspace(command: &str, workspaces: &[String]) -
455
454
  command
456
455
  .split_whitespace()
457
456
  .any(|token| token == "team-agent" || token.ends_with("/team-agent"))
458
- && command.split_whitespace().any(|token| token == "coordinator")
457
+ && command
458
+ .split_whitespace()
459
+ .any(|token| token == "coordinator")
459
460
  && command.contains("--workspace")
460
- && workspaces.iter().any(|workspace| command.contains(workspace))
461
+ && workspaces
462
+ .iter()
463
+ .any(|workspace| command.contains(workspace))
461
464
  }
462
465
 
463
466
  fn terminate_pid(pid: Pid) -> bool {
@@ -505,17 +508,17 @@ fn process_tree_pids(root: Pid) -> Vec<Pid> {
505
508
  "ps_parent",
506
509
  None,
507
510
  )
508
- .ok()
509
- .map(|out| String::from_utf8_lossy(&out.stdout).to_string())
510
- .unwrap_or_default()
511
- .lines()
512
- .filter_map(|line| {
513
- let mut parts = line.split_whitespace();
514
- let pid = parts.next()?.parse::<u32>().ok()?;
515
- let ppid = parts.next()?.parse::<u32>().ok()?;
516
- Some((pid, ppid))
517
- })
518
- .collect::<Vec<_>>();
511
+ .ok()
512
+ .map(|out| String::from_utf8_lossy(&out.stdout).to_string())
513
+ .unwrap_or_default()
514
+ .lines()
515
+ .filter_map(|line| {
516
+ let mut parts = line.split_whitespace();
517
+ let pid = parts.next()?.parse::<u32>().ok()?;
518
+ let ppid = parts.next()?.parse::<u32>().ok()?;
519
+ Some((pid, ppid))
520
+ })
521
+ .collect::<Vec<_>>();
519
522
  let mut out = Vec::new();
520
523
  collect_child_pids(root_pid, &pairs, &mut out);
521
524
  out.push(root_pid);
@@ -718,11 +721,17 @@ fn coordinator_binary_identity_mismatch_reason(
718
721
  let Some(metadata) = metadata else {
719
722
  return Some(CoordinatorMetadataMismatchReason::MetadataMissing);
720
723
  };
721
- let Some(binary_version) = metadata.binary_version.as_deref().filter(|value| !value.is_empty())
724
+ let Some(binary_version) = metadata
725
+ .binary_version
726
+ .as_deref()
727
+ .filter(|value| !value.is_empty())
722
728
  else {
723
729
  return Some(CoordinatorMetadataMismatchReason::BinaryIdentityMissing);
724
730
  };
725
- let Some(binary_path) = metadata.binary_path.as_deref().filter(|value| !value.is_empty())
731
+ let Some(binary_path) = metadata
732
+ .binary_path
733
+ .as_deref()
734
+ .filter(|value| !value.is_empty())
726
735
  else {
727
736
  return Some(CoordinatorMetadataMismatchReason::BinaryIdentityMissing);
728
737
  };
@@ -928,7 +937,9 @@ fn collect_event_lines(
928
937
  let archive_signature = file_signature(&archive_path)?;
929
938
  let mut lines = Vec::new();
930
939
 
931
- let size = std::fs::metadata(&events_path).map(|m| m.len()).unwrap_or(0);
940
+ let size = std::fs::metadata(&events_path)
941
+ .map(|m| m.len())
942
+ .unwrap_or(0);
932
943
  let rotated = cursor.initialized
933
944
  && (cursor.archive_signature != archive_signature || cursor.event_offset > size);
934
945
  if rotated {
@@ -1004,7 +1015,10 @@ fn collect_result_lines(
1004
1015
  let mut summary = crate::message_store::result_summary_from_row(&row)
1005
1016
  .unwrap_or_else(|| serde_json::json!({}));
1006
1017
  if let Some(obj) = summary.as_object_mut() {
1007
- obj.insert("event".to_string(), Value::String("result_received".to_string()));
1018
+ obj.insert(
1019
+ "event".to_string(),
1020
+ Value::String("result_received".to_string()),
1021
+ );
1008
1022
  }
1009
1023
  if let Some(rendered) = render_event_line(&summary) {
1010
1024
  lines.push(rendered);
@@ -1100,7 +1114,8 @@ fn file_signature(path: &Path) -> Result<Option<(u64, i128)>, WatchError> {
1100
1114
  }
1101
1115
 
1102
1116
  fn first_field<'a>(event: &'a Value, keys: &[&str]) -> Option<&'a str> {
1103
- keys.iter().find_map(|key| event.get(*key).and_then(Value::as_str))
1117
+ keys.iter()
1118
+ .find_map(|key| event.get(*key).and_then(Value::as_str))
1104
1119
  }
1105
1120
 
1106
1121
  fn clean_field(event: &Value, keys: &[&str], default: &str) -> String {