@team-agent/installer 0.5.7 → 0.5.9

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 (32) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/attach_app_server_leader.rs +82 -1
  4. package/crates/team-agent/src/cli/diagnose.rs +46 -17
  5. package/crates/team-agent/src/cli/emit.rs +30 -1
  6. package/crates/team-agent/src/cli/leaders.rs +94 -0
  7. package/crates/team-agent/src/cli/mod.rs +138 -2
  8. package/crates/team-agent/src/cli/named_address.rs +288 -28
  9. package/crates/team-agent/src/cli/send.rs +230 -0
  10. package/crates/team-agent/src/cli/status_port.rs +117 -14
  11. package/crates/team-agent/src/cli/tests/leader_watch.rs +1 -0
  12. package/crates/team-agent/src/cli/tests/named_address.rs +9 -3
  13. package/crates/team-agent/src/cli/tests/status_send.rs +1 -0
  14. package/crates/team-agent/src/cli/types.rs +14 -0
  15. package/crates/team-agent/src/coordinator/tick.rs +7 -1
  16. package/crates/team-agent/src/db/agent_health_capture.rs +96 -0
  17. package/crates/team-agent/src/db/mod.rs +1 -0
  18. package/crates/team-agent/src/leader/mod.rs +1 -0
  19. package/crates/team-agent/src/leader/registry.rs +402 -0
  20. package/crates/team-agent/src/lifecycle/restart/remove.rs +13 -61
  21. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +1 -0
  22. package/crates/team-agent/src/mcp_server/helpers.rs +18 -1
  23. package/crates/team-agent/src/mcp_server/normalize.rs +79 -0
  24. package/crates/team-agent/src/mcp_server/tests/send.rs +125 -13
  25. package/crates/team-agent/src/mcp_server/tools.rs +24 -2
  26. package/crates/team-agent/src/messaging/delivery.rs +12 -5
  27. package/crates/team-agent/src/messaging/leader_receiver.rs +58 -0
  28. package/crates/team-agent/src/messaging/mod.rs +2 -1
  29. package/crates/team-agent/src/messaging/results.rs +53 -0
  30. package/crates/team-agent/src/messaging/tests/runtime.rs +7 -2
  31. package/crates/team-agent/src/messaging/watchers.rs +13 -3
  32. package/package.json +4 -4
@@ -112,29 +112,43 @@ fn send_outcome_direct_renders_compact_body() {
112
112
  // ════════════════════════════════════════════════════════════════════════
113
113
  // CONTROL-PLANE: send_message worker recipient → WorkerAccepted (tools.py:135-183)
114
114
  // ════════════════════════════════════════════════════════════════════════
115
- #[test]
116
- fn send_message_worker_recipient_returns_accepted_with_poll_hint() {
117
- // A worker recipient w/ a delivered message_id → async accepted carrying the
118
- // byte-stable poll hint. Identity anchored on injected env (no candidate scan).
119
- // golden: a leader WITH owner_team_id on an unseeded ws would hit the C23 cross-team
120
- // refusal first (worker-1 not in visible peers) -> PeerNotInScope. owner_team_id=None
121
- // (legacy single-team) bypasses that, isolating the worker-recipient accepted path.
122
- // The cross-team refusal has its own tests (refuse_cross_team_peer_* / send_message_cross_team_*).
123
- // A-7: the accepted path needs a REAL stored message_id (tools.py:175-181 —
124
- // fabricated ids are gone), so the workspace seeds a running worker-1 the
125
- // delivery layer can actually queue for.
126
- let ws = unique_ws("send-worker");
115
+ fn seed_current_worker_state(tag: &str) -> std::path::PathBuf {
116
+ let ws = unique_ws(tag);
127
117
  crate::state::persist::save_runtime_state(
128
118
  &ws,
129
119
  &serde_json::json!({
130
120
  "session_name": "team-x",
121
+ "active_team_key": "current",
131
122
  "agents": {
132
123
  "worker-1": {"status": "running", "agent_id": "worker-1", "window": "worker-1"},
133
124
  },
125
+ "teams": {
126
+ "current": {
127
+ "status": "alive",
128
+ "agents": {
129
+ "worker-1": {"status": "running", "agent_id": "worker-1", "window": "worker-1"},
130
+ },
131
+ },
132
+ },
134
133
  }),
135
134
  )
136
135
  .unwrap();
137
- let tools = TeamOrchestratorTools::with_identity(&ws, Some(AgentId::new("leader")), None);
136
+ ws
137
+ }
138
+
139
+ #[test]
140
+ fn send_message_worker_recipient_returns_accepted_with_poll_hint() {
141
+ // A worker recipient w/ a delivered message_id → async accepted carrying the
142
+ // byte-stable poll hint. Identity anchored on injected env (no candidate scan).
143
+ // A-7: the accepted path needs a REAL stored message_id (tools.py:175-181 —
144
+ // fabricated ids are gone), so the workspace seeds a running worker-1 the
145
+ // delivery layer can actually queue for.
146
+ let ws = seed_current_worker_state("send-worker");
147
+ let tools = TeamOrchestratorTools::with_identity(
148
+ &ws,
149
+ Some(AgentId::new("leader")),
150
+ Some(TeamKey::new("current")),
151
+ );
138
152
  let outcome = tools.send_message(
139
153
  &MessageTarget::Single("worker-1".to_string()),
140
154
  "do the thing",
@@ -155,6 +169,104 @@ fn send_message_worker_recipient_returns_accepted_with_poll_hint() {
155
169
  }
156
170
  }
157
171
 
172
+ #[test]
173
+ fn ordinary_send_assign_shape_has_no_recovery_marker() {
174
+ let ws = seed_current_worker_state("ordinary-no-recovery");
175
+ let tools = TeamOrchestratorTools::with_identity(
176
+ &ws,
177
+ Some(AgentId::new("leader")),
178
+ Some(TeamKey::new("current")),
179
+ );
180
+
181
+ let sent = tools
182
+ .send_message(
183
+ &MessageTarget::Single("worker-1".to_string()),
184
+ "ordinary send",
185
+ None,
186
+ None,
187
+ None,
188
+ None,
189
+ )
190
+ .expect("ordinary send ok")
191
+ .to_value();
192
+ assert!(
193
+ !sent.as_object().unwrap().contains_key("recovery"),
194
+ "ordinary send must not carry recovery marker: {sent}"
195
+ );
196
+ assert!(
197
+ sent.get("acceptance_marker").is_none(),
198
+ "ordinary send must not carry acceptance marker: {sent}"
199
+ );
200
+
201
+ let assigned = tools
202
+ .assign_task(
203
+ &json!({"id": "ordinary-task", "assignee": "worker-1", "title": "ordinary"}),
204
+ None,
205
+ )
206
+ .expect("ordinary assign ok");
207
+ let assigned_value = serde_json::to_value(&assigned).expect("serialize ordinary assign");
208
+ assert!(
209
+ !assigned_value.as_object().unwrap().contains_key("recovery"),
210
+ "ordinary assign must not carry recovery marker: {assigned_value}"
211
+ );
212
+ assert!(
213
+ assigned_value.get("acceptance_marker").is_none(),
214
+ "ordinary assign must not carry acceptance marker: {assigned_value}"
215
+ );
216
+
217
+ let recovery_false_assigned = tools
218
+ .assign_task(
219
+ &json!({
220
+ "id": "ordinary-recovery-false-task",
221
+ "assignee": "worker-1",
222
+ "title": "ordinary recovery false",
223
+ "recovery": false,
224
+ }),
225
+ None,
226
+ )
227
+ .expect("recovery=false assign ok");
228
+ let recovery_false_value =
229
+ serde_json::to_value(&recovery_false_assigned).expect("serialize recovery=false assign");
230
+ assert!(
231
+ !recovery_false_value
232
+ .as_object()
233
+ .unwrap()
234
+ .contains_key("recovery"),
235
+ "recovery=false assign must not carry recovery marker: {recovery_false_value}"
236
+ );
237
+ assert!(
238
+ recovery_false_value.get("acceptance_marker").is_none(),
239
+ "recovery=false assign must not carry acceptance marker: {recovery_false_value}"
240
+ );
241
+ }
242
+
243
+ #[test]
244
+ fn recovery_assign_shape_has_structured_marker() {
245
+ let ws = seed_current_worker_state("recovery-marker");
246
+ let tools = TeamOrchestratorTools::with_identity(
247
+ &ws,
248
+ Some(AgentId::new("leader")),
249
+ Some(TeamKey::new("current")),
250
+ );
251
+
252
+ let assigned = tools
253
+ .assign_task(
254
+ &json!({
255
+ "id": "recovery-task",
256
+ "assignee": "worker-1",
257
+ "title": "recover",
258
+ "recovery": true,
259
+ }),
260
+ None,
261
+ )
262
+ .expect("recovery assign ok");
263
+ assert_eq!(assigned.fields.get("recovery"), Some(&json!(true)));
264
+ assert_eq!(
265
+ assigned.fields.get("acceptance_marker"),
266
+ Some(&json!("recovery"))
267
+ );
268
+ }
269
+
158
270
  #[test]
159
271
  fn send_message_worker_recipient_surfaces_dead_coordinator_warning() {
160
272
  let ws = unique_ws("send-worker-dead-coord");
@@ -26,6 +26,7 @@ use super::helpers::{
26
26
  };
27
27
  use super::normalize::{
28
28
  compact_tool_result, normalize_report_envelope, normalize_result_status_observed,
29
+ report_result_integrity_warnings,
29
30
  };
30
31
  use super::types::{
31
32
  Scope, SendOutcome, ToolError, ToolErrorReason, ToolOk, ToolResult, VisiblePeers,
@@ -116,6 +117,7 @@ impl TeamOrchestratorTools {
116
117
  };
117
118
 
118
119
  let task_value = Value::Object(task_obj.clone());
120
+ let recovery = task_recovery_marker(&task_value);
119
121
  let mut state = load_runtime_state(&self.workspace).map_err(tool_runtime_error)?;
120
122
  ensure_object(&mut state);
121
123
  let team_key = self
@@ -139,7 +141,16 @@ impl TeamOrchestratorTools {
139
141
  None,
140
142
  None,
141
143
  )?;
142
- compact_tool_result(&out.to_value())
144
+ let mut ok = compact_tool_result(&out.to_value())?;
145
+ if recovery {
146
+ ok.fields
147
+ .insert("recovery".to_string(), serde_json::json!(true));
148
+ ok.fields.insert(
149
+ "acceptance_marker".to_string(),
150
+ Value::String("recovery".to_string()),
151
+ );
152
+ }
153
+ Ok(ok)
143
154
  }
144
155
 
145
156
  /// `send_message` (`tools.py:135-183`): C14/C15/C17 scope resolution.
@@ -389,7 +400,13 @@ impl TeamOrchestratorTools {
389
400
  self.note_unknown_result_status(&raw);
390
401
  }
391
402
  let normalized = normalize_report_envelope(&base);
392
- let env_value = normalized_envelope_value(&normalized);
403
+ let warnings = report_result_integrity_warnings(&base, &normalized);
404
+ let mut env_value = normalized_envelope_value(&normalized);
405
+ if !warnings.is_empty() {
406
+ if let Some(obj) = env_value.as_object_mut() {
407
+ obj.insert("warnings".to_string(), Value::Array(warnings));
408
+ }
409
+ }
393
410
  let owner_team = self.canonical_owner_team_key()?;
394
411
  messaging::report_result_for_owner_team(
395
412
  &self.workspace,
@@ -989,6 +1006,11 @@ fn assignment_message(task: &Value, explicit: Option<&str>) -> String {
989
1006
  json_dumps_default(task)
990
1007
  }
991
1008
 
1009
+ fn task_recovery_marker(task: &Value) -> bool {
1010
+ task.get("recovery").and_then(Value::as_bool) == Some(true)
1011
+ || task.get("acceptance_marker").and_then(Value::as_str) == Some("recovery")
1012
+ }
1013
+
992
1014
  fn scope_override_name(scope: Scope) -> Option<&'static str> {
993
1015
  match scope {
994
1016
  Scope::Team => Some("team"),
@@ -1981,12 +1981,19 @@ fn arm_turn_open(state: &mut serde_json::Value, recipient: &str, message_id: &Op
1981
1981
  .and_then(|agents| agents.get_mut(recipient))
1982
1982
  .and_then(serde_json::Value::as_object_mut)
1983
1983
  {
1984
- agent.insert(
1985
- "current_task_id".to_string(),
1986
- message_id.as_ref().map_or(serde_json::Value::Null, |id| {
1984
+ // Phase-DX E2 (CR P0 red line #5): the leader→worker turn message id.
1985
+ // Renamed from a misleading legacy field name (which was never a task
1986
+ // FSM id — task FSM is A1) to `current_turn_message_id`, matching its
1987
+ // actual semantic. Reader compatibility lives in `coordinator/tick.rs`
1988
+ // (whitelisted for the transition) and `mcp_server/helpers.rs`; other
1989
+ // messaging/lifecycle/mcp_server code MUST NOT treat this as
1990
+ // authoritative task state.
1991
+ let field = message_id
1992
+ .as_ref()
1993
+ .map_or(serde_json::Value::Null, |id| {
1987
1994
  serde_json::Value::String(id.clone())
1988
- }),
1989
- );
1995
+ });
1996
+ agent.insert("current_turn_message_id".to_string(), field);
1990
1997
  }
1991
1998
  }
1992
1999
 
@@ -541,3 +541,61 @@ fn render_fallback_pane_message(content: &str, message_id: &str, primary_error:
541
541
  [team-agent-token:{message_id}]"
542
542
  )
543
543
  }
544
+
545
+ /// E6 (0.5.9 offline-mailbox-toname-design §6.3): enqueue a leader-bound message
546
+ /// row in the TARGET workspace `team.db` with status `queued_until_leader_attach`.
547
+ ///
548
+ /// This is a durable "leader mailbox" write, NOT a delivery. Coordinator delivery
549
+ /// never claims this status (§3.3), so the row will not churn or be marked
550
+ /// `failed` while the leader is unattached. When the target owner runs
551
+ /// `attach-leader` / `claim-leader`, the existing `requeue_blocked_leader_messages`
552
+ /// helper flips the same row to `accepted` and the standard delivery pipeline
553
+ /// injects it exactly once.
554
+ ///
555
+ /// Safety boundary (§5): only the `messages` table is touched. Owner identity
556
+ /// (`leader_receiver` / `team_owner` / `owner_epoch`) is never written by this
557
+ /// path, and no provider/worker process is spawned.
558
+ pub fn enqueue_leader_mailbox_until_attach(
559
+ target_workspace: &Path,
560
+ canonical_team_key: &str,
561
+ content: &str,
562
+ task_id: Option<&TaskId>,
563
+ sender: &str,
564
+ event_log: &EventLog,
565
+ ) -> Result<DeliveryOutcome, MessagingError> {
566
+ let store = MessageStore::open(target_workspace)?;
567
+ let message_id = store.create_message(
568
+ task_id.map(TaskId::as_str),
569
+ sender,
570
+ "leader",
571
+ content,
572
+ None,
573
+ false,
574
+ Some(canonical_team_key),
575
+ )?;
576
+ // The default row status after `create_message` is `accepted`. Flip it to
577
+ // `queued_until_leader_attach` so it stays out of `claim_for_delivery`
578
+ // eligible set and out of `deliver_pending_messages` scan until an
579
+ // attach/claim explicitly requeues it (see requeue_blocked_leader_messages).
580
+ store.mark(&message_id, "queued_until_leader_attach", None)?;
581
+ event_log.write(
582
+ "leader_mailbox.queued_until_attach",
583
+ serde_json::json!({
584
+ "message_id": message_id,
585
+ "owner_team_id": canonical_team_key,
586
+ "sender": sender,
587
+ "target_workspace": target_workspace.display().to_string(),
588
+ "channel": "leader_mailbox",
589
+ }),
590
+ )?;
591
+ Ok(DeliveryOutcome {
592
+ ok: true,
593
+ status: DeliveryStatus::Queued,
594
+ message_status: MessageStatusShadow("queued_until_leader_attach".to_string()),
595
+ message_id: Some(message_id),
596
+ verification: None,
597
+ stage: None,
598
+ reason: None,
599
+ channel: Some("leader_mailbox".to_string()),
600
+ })
601
+ }
@@ -83,7 +83,8 @@ pub use delivery::{
83
83
  };
84
84
  pub use helpers::fail_leader_delivery;
85
85
  pub use leader_receiver::{
86
- deliver_to_leader_fallback_pane, mirror_peer_message_to_leader, send_to_leader_receiver,
86
+ deliver_to_leader_fallback_pane, enqueue_leader_mailbox_until_attach,
87
+ mirror_peer_message_to_leader, send_to_leader_receiver,
87
88
  send_to_leader_receiver_with_message_id,
88
89
  };
89
90
  pub use peers::allow_peer_talk;
@@ -263,11 +263,21 @@ fn ensure_coordinator_after_collect(
263
263
  }
264
264
 
265
265
  /// `_coordinator_should_run`(`results.py:187-188`)。
266
+ ///
267
+ /// The top-level `state.leader_receiver` read below is a coordinator-scope
268
+ /// legacy compat bridge (pre-teams-map single-team layout) — deliberately
269
+ /// NOT team-scoped because this predicate answers "is a coordinator worth
270
+ /// starting for this workspace at all?" and does not itself route any
271
+ /// message. The `ALLOWED-LEGACY-READ` marker documents the exception and
272
+ /// keeps grep sweeps honest; delete once B1 canonical layout lands and
273
+ /// every state carries a `teams` map
274
+ /// (`.team/artifacts/next-version-staged-plan.md §5 Phase-Foundation-1`).
266
275
  fn coordinator_should_run(state: &serde_json::Value) -> bool {
267
276
  let has_session = state
268
277
  .get("session_name")
269
278
  .and_then(serde_json::Value::as_str)
270
279
  .is_some_and(|s| !s.is_empty());
280
+ // ALLOWED-LEGACY-READ: pre-teams-map coordinator-scope existence probe; never used for routing.
271
281
  has_session || leader_receiver_is_direct(state.get("leader_receiver"))
272
282
  }
273
283
 
@@ -942,6 +952,12 @@ fn report_result_for_owner_team_inner(
942
952
  );
943
953
  }
944
954
  out.insert("notification_event_id".to_string(), serde_json::Value::Null);
955
+ if let Some(warnings) = report_result_array(envelope, "warnings") {
956
+ out.insert(
957
+ "warnings".to_string(),
958
+ serde_json::Value::Array(warnings.clone()),
959
+ );
960
+ }
945
961
  Ok(serde_json::Value::Object(out))
946
962
  }
947
963
 
@@ -1036,6 +1052,9 @@ fn format_report_result_notification(
1036
1052
  if let Some(tests) = format_report_result_tests(envelope) {
1037
1053
  lines.push(tests);
1038
1054
  }
1055
+ if let Some(warnings) = format_report_result_warnings(envelope) {
1056
+ lines.push(warnings);
1057
+ }
1039
1058
  if let Some(changes) = format_report_result_changes(envelope) {
1040
1059
  lines.push(changes);
1041
1060
  }
@@ -1075,6 +1094,29 @@ fn format_report_result_tests(envelope: &serde_json::Value) -> Option<String> {
1075
1094
  }
1076
1095
  }
1077
1096
 
1097
+ fn format_report_result_warnings(envelope: &serde_json::Value) -> Option<String> {
1098
+ let parts = report_result_array(envelope, "warnings")?
1099
+ .iter()
1100
+ .filter_map(|warning| {
1101
+ let code = report_field(warning, "code")?;
1102
+ let field = report_field(warning, "field").unwrap_or("result");
1103
+ let severity = report_field(warning, "severity").unwrap_or("warning");
1104
+ let advisory =
1105
+ if warning.get("advisory").and_then(serde_json::Value::as_bool) == Some(true) {
1106
+ " advisory"
1107
+ } else {
1108
+ ""
1109
+ };
1110
+ Some(format!("{severity}{advisory} {field}: {code}"))
1111
+ })
1112
+ .collect::<Vec<_>>();
1113
+ if parts.is_empty() {
1114
+ None
1115
+ } else {
1116
+ Some(format!("Verification warnings: {}", parts.join(", ")))
1117
+ }
1118
+ }
1119
+
1078
1120
  fn report_result_array<'a>(
1079
1121
  envelope: &'a serde_json::Value,
1080
1122
  key: &str,
@@ -1203,6 +1245,14 @@ mod tests {
1203
1245
  "tests": [
1204
1246
  {"command": "cargo test", "status": "passed"}
1205
1247
  ],
1248
+ "warnings": [
1249
+ {
1250
+ "code": "result_success_without_executed_tests",
1251
+ "field": "tests",
1252
+ "severity": "warning",
1253
+ "advisory": true
1254
+ }
1255
+ ],
1206
1256
  "risks": [
1207
1257
  {"severity": "low", "description": "none known"}
1208
1258
  ],
@@ -1217,6 +1267,9 @@ mod tests {
1217
1267
  format_report_result_notification("res_1", "task-1", "worker", "success", &envelope);
1218
1268
  assert!(notification.contains("Task task-1 reported success from worker: done"));
1219
1269
  assert!(notification.contains("Tests: cargo test=passed"));
1270
+ assert!(notification.contains(
1271
+ "Verification warnings: warning advisory tests: result_success_without_executed_tests"
1272
+ ));
1220
1273
  assert!(notification.contains("Changes: modified src/a.rs: patched delivery"));
1221
1274
  assert!(notification.contains("Risks: low: none known"));
1222
1275
  assert!(notification.contains("Artifacts: .team/artifacts/evidence.md: evidence"));
@@ -1581,10 +1581,15 @@ fn deliver_pending_submit_unverified_is_not_saved_by_readback() {
1581
1581
  Some(&serde_json::json!(message_id)),
1582
1582
  "current-turn fact is armed immediately after physical inject succeeds"
1583
1583
  );
1584
+ // Phase-DX E2 rename: the leader→worker turn message id lives under
1585
+ // `current_turn_message_id` (the previous name misleadingly implied a task
1586
+ // FSM id — that is A1 territory). Same value written by
1587
+ // `delivery::arm_turn_open`; the invariant of arming-without-delivered is
1588
+ // unchanged.
1584
1589
  assert_eq!(
1585
- updated.pointer("/agents/w1/current_task_id"),
1590
+ updated.pointer("/agents/w1/current_turn_message_id"),
1586
1591
  Some(&serde_json::json!(message_id)),
1587
- "agent current_task_id is armed without marking the message delivered"
1592
+ "agent current-turn message id is armed without marking the message delivered"
1588
1593
  );
1589
1594
  }
1590
1595
 
@@ -459,15 +459,25 @@ pub(crate) fn requeue_blocked_leader_messages(
459
459
  owner_team_id: &TeamKey,
460
460
  claimed_pane_id: &PaneId,
461
461
  ) -> Result<usize, MessagingError> {
462
+ // E6 (0.5.9 offline-mailbox §6.5): also requeue rows that a third-party
463
+ // sender left in `queued_until_leader_attach` via the leader mailbox.
464
+ // Same idempotent requeue funnel (row/message_id/leader_notification_log
465
+ // PK are all preserved) — after attach/claim the coordinator's normal
466
+ // `deliver_pending_messages` picks them up as `accepted` and injects
467
+ // exactly once. status `queued_until_leader_attach` is deliberately NOT
468
+ // in the `claim_for_delivery` eligible set (see message_store.rs) so it
469
+ // could not have churned while the leader was unattached.
462
470
  let requeued = conn.execute(
463
471
  "update messages
464
472
  set status = 'accepted',
465
473
  error = null,
466
474
  updated_at = ?2
467
475
  where recipient = 'leader'
468
- and status = 'failed'
469
- and error = 'leader_not_attached'
470
- and owner_team_id = ?1",
476
+ and owner_team_id = ?1
477
+ and (
478
+ (status = 'failed' and error = 'leader_not_attached')
479
+ or status = 'queued_until_leader_attach'
480
+ )",
471
481
  params![owner_team_id.as_str(), chrono::Utc::now().to_rfc3339()],
472
482
  )?;
473
483
  if requeued > 0 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-agent/installer",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "npx installer for Team Agent",
5
5
  "keywords": [
6
6
  "codex",
@@ -20,9 +20,9 @@
20
20
  "team-agent-installer": "npm/install.mjs"
21
21
  },
22
22
  "optionalDependencies": {
23
- "@team-agent/cli-darwin-arm64": "0.5.7",
24
- "@team-agent/cli-darwin-x64": "0.5.7",
25
- "@team-agent/cli-linux-x64": "0.5.7"
23
+ "@team-agent/cli-darwin-arm64": "0.5.9",
24
+ "@team-agent/cli-darwin-x64": "0.5.9",
25
+ "@team-agent/cli-linux-x64": "0.5.9"
26
26
  },
27
27
  "scripts": {
28
28
  "postinstall": "node npm/bincheck.mjs",