@team-agent/installer 0.5.61 → 0.5.63

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 (250) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +5 -2
  4. package/crates/team-agent/src/cli/attach_app_server_leader.rs +1 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +1 -0
  6. package/crates/team-agent/src/cli/emit.rs +41 -7
  7. package/crates/team-agent/src/cli/helpers.rs +1 -0
  8. package/crates/team-agent/src/cli/leader.rs +6 -1
  9. package/crates/team-agent/src/cli/leaders.rs +2 -0
  10. package/crates/team-agent/src/cli/mod.rs +33 -3
  11. package/crates/team-agent/src/cli/named_address.rs +8 -1
  12. package/crates/team-agent/src/cli/profile.rs +2 -1
  13. package/crates/team-agent/src/cli/send/coordinator.rs +1 -0
  14. package/crates/team-agent/src/cli/send/mailbox.rs +3 -0
  15. package/crates/team-agent/src/cli/send/persist.rs +1 -0
  16. package/crates/team-agent/src/cli/send/presentation.rs +1 -0
  17. package/crates/team-agent/src/cli/send/resolve.rs +206 -1
  18. package/crates/team-agent/src/cli/send.rs +2 -1
  19. package/crates/team-agent/src/cli/spec.rs +1 -0
  20. package/crates/team-agent/src/cli/status.rs +2 -1
  21. package/crates/team-agent/src/cli/status_port/compact.rs +1 -0
  22. package/crates/team-agent/src/cli/status_port/format.rs +1 -0
  23. package/crates/team-agent/src/cli/status_port/inbox.rs +1 -0
  24. package/crates/team-agent/src/cli/tests/verb_profile.rs +38 -0
  25. package/crates/team-agent/src/cli/types.rs +3 -2
  26. package/crates/team-agent/src/communication_mode/mod.rs +1 -0
  27. package/crates/team-agent/src/compiler.rs +1 -0
  28. package/crates/team-agent/src/conpty/backend.rs +1 -0
  29. package/crates/team-agent/src/conpty/mod.rs +1 -0
  30. package/crates/team-agent/src/coordinator/backoff.rs +5 -8
  31. package/crates/team-agent/src/coordinator/conpty_shim.rs +3 -2
  32. package/crates/team-agent/src/coordinator/health.rs +1 -0
  33. package/crates/team-agent/src/coordinator/mod.rs +2 -2
  34. package/crates/team-agent/src/coordinator/orphan.rs +4 -116
  35. package/crates/team-agent/src/coordinator/runtime_detectors.rs +1 -0
  36. package/crates/team-agent/src/coordinator/runtime_observation.rs +1 -0
  37. package/crates/team-agent/src/coordinator/steps/abnormal.rs +15 -866
  38. package/crates/team-agent/src/coordinator/steps/delivery.rs +1 -0
  39. package/crates/team-agent/src/coordinator/steps/health_sync.rs +1 -0
  40. package/crates/team-agent/src/coordinator/steps/mod.rs +2 -0
  41. package/crates/team-agent/src/coordinator/steps/persist.rs +1 -0
  42. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +1 -0
  43. package/crates/team-agent/src/coordinator/steps/session_gate.rs +1 -0
  44. package/crates/team-agent/src/coordinator/tests/abnormal.rs +0 -174
  45. package/crates/team-agent/src/coordinator/tests/api_error_recovery.rs +139 -375
  46. package/crates/team-agent/src/coordinator/tests/basics.rs +0 -184
  47. package/crates/team-agent/src/coordinator/tests/mod.rs +2 -17
  48. package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -49
  49. package/crates/team-agent/src/coordinator/tick.rs +23 -40
  50. package/crates/team-agent/src/coordinator/types.rs +3 -176
  51. package/crates/team-agent/src/db/agent_health_capture.rs +1 -0
  52. package/crates/team-agent/src/db/message_store.rs +2 -1
  53. package/crates/team-agent/src/db/migration.rs +5 -4
  54. package/crates/team-agent/src/db/mod.rs +1 -0
  55. package/crates/team-agent/src/db/schema.rs +1 -0
  56. package/crates/team-agent/src/diagnose/comms.rs +2 -1
  57. package/crates/team-agent/src/diagnose/mod.rs +1 -0
  58. package/crates/team-agent/src/diagnose/orphans.rs +1 -0
  59. package/crates/team-agent/src/layout/manager.rs +3 -7
  60. package/crates/team-agent/src/layout/mod.rs +2 -2
  61. package/crates/team-agent/src/layout/overlay.rs +6 -1
  62. package/crates/team-agent/src/layout/placement.rs +1 -0
  63. package/crates/team-agent/src/layout/recovery.rs +3 -0
  64. package/crates/team-agent/src/layout/runtime_sessions.rs +8 -3
  65. package/crates/team-agent/src/layout/sessions.rs +8 -1
  66. package/crates/team-agent/src/layout/tmux_endpoint.rs +3 -0
  67. package/crates/team-agent/src/layout/worker_env.rs +3 -0
  68. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  69. package/crates/team-agent/src/leader/helpers.rs +1 -0
  70. package/crates/team-agent/src/leader/incident.rs +1 -0
  71. package/crates/team-agent/src/leader/lease.rs +27 -22
  72. package/crates/team-agent/src/leader/mod.rs +2 -3
  73. package/crates/team-agent/src/leader/owner_bind.rs +13 -3
  74. package/crates/team-agent/src/leader/provider_attribution.rs +1 -0
  75. package/crates/team-agent/src/leader/rediscover.rs +4 -17
  76. package/crates/team-agent/src/leader/registry.rs +3 -15
  77. package/crates/team-agent/src/leader/start.rs +373 -125
  78. package/crates/team-agent/src/leader/takeover.rs +10 -232
  79. package/crates/team-agent/src/leader/tests/claim_leader_mailbox_flush_contract.rs +544 -0
  80. package/crates/team-agent/src/leader/tests/historical_inventory_upgrade_gate_red.rs +624 -0
  81. package/crates/team-agent/src/leader/tests/identity.rs +7 -2
  82. package/crates/team-agent/src/leader/tests/idle.rs +0 -193
  83. package/crates/team-agent/src/leader/tests/lease_api.rs +0 -45
  84. package/crates/team-agent/src/leader/tests/mod.rs +4 -41
  85. package/crates/team-agent/src/leader/tests/rehomed_env.rs +83 -0
  86. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1273 -0
  87. package/crates/team-agent/src/leader/tests/terminal_and_replay_vs_rerender_invariants_red.rs +417 -0
  88. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +22 -103
  89. package/crates/team-agent/src/leader/types.rs +1 -28
  90. package/crates/team-agent/src/lib.rs +3 -0
  91. package/crates/team-agent/src/lifecycle/display.rs +7 -1
  92. package/crates/team-agent/src/lifecycle/helpers.rs +3 -1
  93. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +2 -2
  94. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +1 -0
  95. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +1 -0
  96. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +1 -0
  97. package/crates/team-agent/src/lifecycle/launch/plan.rs +2 -2
  98. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +4 -26
  99. package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -27
  100. package/crates/team-agent/src/lifecycle/launch/spawn.rs +1 -27
  101. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +4 -32
  102. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +1 -0
  103. package/crates/team-agent/src/lifecycle/launch.rs +11 -12
  104. package/crates/team-agent/src/lifecycle/mod.rs +2 -1
  105. package/crates/team-agent/src/lifecycle/restart/agent.rs +3 -66
  106. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +2 -2
  107. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -5
  108. package/crates/team-agent/src/lifecycle/restart/remove.rs +1 -1
  109. package/crates/team-agent/src/lifecycle/restart/selection.rs +3 -3
  110. package/crates/team-agent/src/lifecycle/restart.rs +13 -15
  111. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1007 -0
  112. package/crates/team-agent/src/lifecycle/tests/b0_legacy_snapshot_nonauthority_contract.rs +265 -0
  113. package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +459 -0
  114. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +849 -0
  115. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +323 -0
  116. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +423 -0
  117. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +737 -0
  118. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +1187 -0
  119. package/crates/team-agent/src/lifecycle/tests/codex_weak_window_attribution_red.rs +683 -0
  120. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +394 -0
  121. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +1856 -0
  122. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +917 -0
  123. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +481 -0
  124. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +301 -0
  125. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +516 -0
  126. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +986 -0
  127. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +651 -0
  128. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -0
  129. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +332 -0
  130. package/crates/team-agent/src/lifecycle/tests/realmachine_clusters_1_6_red.rs +846 -0
  131. package/crates/team-agent/src/lifecycle/tests/realmachine_residual_g1_g4_red.rs +648 -0
  132. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +910 -0
  133. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +630 -0
  134. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1083 -0
  135. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +1677 -0
  136. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +410 -0
  137. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +428 -0
  138. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1032 -0
  139. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +869 -0
  140. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1249 -0
  141. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +342 -0
  142. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +542 -0
  143. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1103 -0
  144. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1276 -0
  145. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_roster_red.rs +145 -0
  146. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +603 -0
  147. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +837 -0
  148. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +928 -0
  149. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +951 -0
  150. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +327 -0
  151. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +760 -0
  152. package/crates/team-agent/src/lifecycle/tests.rs +59 -0
  153. package/crates/team-agent/src/lifecycle/types.rs +0 -9
  154. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  155. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -0
  156. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -0
  157. package/crates/team-agent/src/mcp_server/mod.rs +1 -0
  158. package/crates/team-agent/src/mcp_server/normalize.rs +4 -3
  159. package/crates/team-agent/src/mcp_server/tools.rs +1 -0
  160. package/crates/team-agent/src/mcp_server/types.rs +1 -18
  161. package/crates/team-agent/src/mcp_server/wire.rs +5 -5
  162. package/crates/team-agent/src/messaging/activity.rs +1 -0
  163. package/crates/team-agent/src/messaging/address.rs +1 -0
  164. package/crates/team-agent/src/messaging/delivery.rs +4 -3
  165. package/crates/team-agent/src/messaging/helpers.rs +2 -1
  166. package/crates/team-agent/src/messaging/leader_channel.rs +1 -0
  167. package/crates/team-agent/src/messaging/leader_receiver.rs +2 -1
  168. package/crates/team-agent/src/messaging/mod.rs +10 -14
  169. package/crates/team-agent/src/messaging/peers.rs +2 -0
  170. package/crates/team-agent/src/messaging/persist.rs +3 -1
  171. package/crates/team-agent/src/messaging/presentation.rs +2 -1
  172. package/crates/team-agent/src/messaging/results.rs +1 -0
  173. package/crates/team-agent/src/messaging/scheduler.rs +1 -0
  174. package/crates/team-agent/src/messaging/selftest.rs +1 -1
  175. package/crates/team-agent/src/messaging/send.rs +41 -8
  176. package/crates/team-agent/src/messaging/tests/mod.rs +5 -0
  177. package/crates/team-agent/src/messaging/types.rs +2 -2
  178. package/crates/team-agent/src/messaging/watchers.rs +19 -13
  179. package/crates/team-agent/src/model/enums.rs +1 -0
  180. package/crates/team-agent/src/model/errors.rs +1 -0
  181. package/crates/team-agent/src/model/ids.rs +1 -0
  182. package/crates/team-agent/src/model/mod.rs +1 -0
  183. package/crates/team-agent/src/model/name_similarity.rs +1 -0
  184. package/crates/team-agent/src/model/pane_authority_refusal.rs +1 -0
  185. package/crates/team-agent/src/model/paths.rs +1 -0
  186. package/crates/team-agent/src/model/permissions.rs +3 -2
  187. package/crates/team-agent/src/model/routing.rs +1 -0
  188. package/crates/team-agent/src/model/spec.rs +1 -0
  189. package/crates/team-agent/src/model/task_graph.rs +1 -0
  190. package/crates/team-agent/src/model/yaml/tests.rs +1 -0
  191. package/crates/team-agent/src/model/yaml.rs +1 -0
  192. package/crates/team-agent/src/packaging/install.rs +9 -147
  193. package/crates/team-agent/src/packaging/migrate.rs +2 -0
  194. package/crates/team-agent/src/packaging/mod.rs +1 -0
  195. package/crates/team-agent/src/packaging/repair.rs +2 -0
  196. package/crates/team-agent/src/packaging/tests.rs +29 -218
  197. package/crates/team-agent/src/packaging/types.rs +8 -15
  198. package/crates/team-agent/src/platform/argv.rs +4 -0
  199. package/crates/team-agent/src/platform/errors.rs +10 -1
  200. package/crates/team-agent/src/platform/file_lock.rs +4 -157
  201. package/crates/team-agent/src/platform/mod.rs +0 -61
  202. package/crates/team-agent/src/platform/process.rs +1 -0
  203. package/crates/team-agent/src/provider/adapter.rs +1 -0
  204. package/crates/team-agent/src/provider/adapters/claude.rs +1 -0
  205. package/crates/team-agent/src/provider/adapters/claude_fork.rs +1 -0
  206. package/crates/team-agent/src/provider/adapters/codex.rs +1 -0
  207. package/crates/team-agent/src/provider/adapters/copilot.rs +1 -0
  208. package/crates/team-agent/src/provider/adapters/copilot_fork.rs +1 -0
  209. package/crates/team-agent/src/provider/adapters/fake.rs +1 -0
  210. package/crates/team-agent/src/provider/adapters/mod.rs +1 -0
  211. package/crates/team-agent/src/provider/approvals/mod.rs +1 -0
  212. package/crates/team-agent/src/provider/approvals/parsing.rs +2 -5
  213. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +6 -5
  214. package/crates/team-agent/src/provider/classify.rs +1 -0
  215. package/crates/team-agent/src/provider/faults.rs +1 -26
  216. package/crates/team-agent/src/provider/helpers.rs +1 -0
  217. package/crates/team-agent/src/provider/mod.rs +1 -1
  218. package/crates/team-agent/src/provider/session/capture.rs +1 -4
  219. package/crates/team-agent/src/provider/session/context_fork/claude.rs +1 -0
  220. package/crates/team-agent/src/provider/session/context_fork/codex.rs +1 -0
  221. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +1 -0
  222. package/crates/team-agent/src/provider/session/context_fork.rs +1 -0
  223. package/crates/team-agent/src/provider/session/mod.rs +2 -4
  224. package/crates/team-agent/src/provider/session/resume.rs +2 -209
  225. package/crates/team-agent/src/provider/session_scan/claude.rs +1 -0
  226. package/crates/team-agent/src/provider/session_scan/codex.rs +1 -0
  227. package/crates/team-agent/src/provider/session_scan/common.rs +1 -0
  228. package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
  229. package/crates/team-agent/src/provider/session_scan.rs +1 -42
  230. package/crates/team-agent/src/provider/startup_prompt.rs +1 -0
  231. package/crates/team-agent/src/provider/types.rs +1 -0
  232. package/crates/team-agent/src/provider/wire.rs +1 -0
  233. package/crates/team-agent/src/state/identity.rs +16 -5
  234. package/crates/team-agent/src/state/identity_keys.rs +1 -0
  235. package/crates/team-agent/src/state/mod.rs +2 -0
  236. package/crates/team-agent/src/state/owner_gate.rs +5 -0
  237. package/crates/team-agent/src/state/ownership.rs +10 -4
  238. package/crates/team-agent/src/state/paths.rs +12 -0
  239. package/crates/team-agent/src/state/persist.rs +9 -3
  240. package/crates/team-agent/src/state/projection.rs +15 -2
  241. package/crates/team-agent/src/state/repository/intent.rs +1 -0
  242. package/crates/team-agent/src/state/repository/tests.rs +1 -0
  243. package/crates/team-agent/src/state/repository.rs +7 -13
  244. package/crates/team-agent/src/state/selector.rs +1 -0
  245. package/crates/team-agent/src/tmux_backend/tests.rs +39 -0
  246. package/crates/team-agent/src/tmux_backend.rs +63 -0
  247. package/crates/team-agent/src/transport.rs +15 -0
  248. package/package.json +4 -4
  249. package/crates/team-agent/src/leader/inject.rs +0 -33
  250. package/crates/team-agent/src/provider/command.rs +0 -80
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use super::mailbox::maybe_enqueue_offline_leader_mailbox;
2
3
  use super::persist::persist_resolved_target;
3
4
  use crate::cli::{CliError, SendArgs};
@@ -207,7 +208,7 @@ pub(super) fn send_to_logical_to(
207
208
  &args.workspace,
208
209
  name,
209
210
  content,
210
- args.sender.as_str(),
211
+ args.sender.display_name(),
211
212
  args.task.as_deref(),
212
213
  &error,
213
214
  )? {
@@ -359,3 +360,207 @@ pub(super) fn send_to_resolved_name(
359
360
  }
360
361
  Ok(value)
361
362
  }
363
+
364
+ #[cfg(test)]
365
+ mod a13_red_tests {
366
+ #![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
367
+
368
+ use super::send_to_resolved_name;
369
+ use crate::cli::named_address::{NamedTargetKind, ResolvedNamedAddress};
370
+ use crate::cli::SendArgs;
371
+ use crate::message_store::MessageStore;
372
+ use crate::messaging::TrustedSender;
373
+ use crate::model::ids::AgentId;
374
+ use serde_json::{json, Value};
375
+ use std::path::{Path, PathBuf};
376
+ use std::sync::atomic::{AtomicU64, Ordering};
377
+
378
+ fn fresh_workspace(tag: &str) -> PathBuf {
379
+ static NEXT: AtomicU64 = AtomicU64::new(0);
380
+ let workspace = std::env::temp_dir().join(format!(
381
+ "team-agent-a13-{tag}-{}-{}",
382
+ std::process::id(),
383
+ NEXT.fetch_add(1, Ordering::Relaxed)
384
+ ));
385
+ let _ = std::fs::remove_dir_all(&workspace);
386
+ std::fs::create_dir_all(workspace.join(".team/runtime")).expect("create fixture");
387
+ workspace
388
+ }
389
+
390
+ fn seed_team(workspace: &Path, team: &str) {
391
+ let session = format!("team-{team}");
392
+ let state = json!({
393
+ "active_team_key": team,
394
+ "status": "alive",
395
+ "session_name": session,
396
+ "agents": {},
397
+ "teams": {
398
+ team: {
399
+ "status": "alive",
400
+ "session_name": session,
401
+ "agents": {},
402
+ "leader_receiver": {"status": "unbound"}
403
+ }
404
+ }
405
+ });
406
+ crate::state::persist::save_runtime_state(workspace, &state).expect("save fixture state");
407
+ MessageStore::open(workspace).expect("create fixture store");
408
+ }
409
+
410
+ fn send_args(workspace: &Path) -> SendArgs {
411
+ SendArgs {
412
+ target: None,
413
+ message: vec!["payload".to_string()],
414
+ targets: None,
415
+ workspace: workspace.to_path_buf(),
416
+ team: None,
417
+ task: None,
418
+ sender: TrustedSender::from_runtime_identity_with_source(
419
+ AgentId::new("worker-1"),
420
+ workspace.display(),
421
+ "sender-team",
422
+ ),
423
+ no_ack: false,
424
+ no_wait: true,
425
+ watch_result: false,
426
+ timeout: 0.0,
427
+ confirm_human: false,
428
+ json: true,
429
+ message_id: None,
430
+ presentation: crate::messaging::presentation::PresentationRequest::default(),
431
+ pane: None,
432
+ to_name: Some("other-team/leader".to_string()),
433
+ to_leader: None,
434
+ }
435
+ }
436
+
437
+ fn leader_address(sender_workspace: &Path, target_workspace: &Path) -> ResolvedNamedAddress {
438
+ ResolvedNamedAddress {
439
+ raw_name: format!("{}::other-team/leader", target_workspace.display()),
440
+ target_kind: NamedTargetKind::Leader,
441
+ sender_workspace: sender_workspace.to_path_buf(),
442
+ target_workspace: target_workspace.to_path_buf(),
443
+ team_key: Some("other-team".to_string()),
444
+ agent_id: None,
445
+ pane_id: String::new(),
446
+ session_name: Some("team-other-team".to_string()),
447
+ window_name: None,
448
+ tmux_endpoint: None,
449
+ transport_kind: Some("direct_tmux".to_string()),
450
+ app_server: None,
451
+ state_pane_id: None,
452
+ state_pane_stale: false,
453
+ agent_status: None,
454
+ warning: None,
455
+ }
456
+ }
457
+
458
+ fn message_count(workspace: &Path) -> i64 {
459
+ let store = MessageStore::open(workspace).expect("open fixture store");
460
+ crate::db::schema::open_db(store.db_path())
461
+ .expect("open fixture database")
462
+ .query_row("select count(*) from messages", [], |row| row.get(0))
463
+ .expect("count fixture messages")
464
+ }
465
+
466
+ #[test]
467
+ fn a13_worker_cross_workspace_leader_send_preserves_target_scope() {
468
+ let sender_workspace = fresh_workspace("sender");
469
+ let target_workspace = fresh_workspace("target");
470
+ seed_team(&target_workspace, "other-team");
471
+ let mut args = send_args(&sender_workspace);
472
+ let to_name = format!("{}::other-team/leader", target_workspace.display());
473
+ args.to_name = Some(to_name.clone());
474
+ let value = super::send_to_logical_to(&args, &to_name, "payload")
475
+ .expect("cross-workspace leader send must preserve a structured outcome");
476
+ assert_eq!(
477
+ value["to_name"],
478
+ json!(to_name),
479
+ "cross-workspace receipt must retain the original target: {value}"
480
+ );
481
+ assert_eq!(
482
+ value["sender"],
483
+ json!(format!(
484
+ "{}::sender-team/worker-1",
485
+ sender_workspace.display()
486
+ )),
487
+ "cross-workspace receipt must retain the full sender: {value}"
488
+ );
489
+ assert_eq!(
490
+ message_count(&sender_workspace),
491
+ 0,
492
+ "cross-workspace send must not fold into the sender workspace"
493
+ );
494
+ assert_eq!(
495
+ message_count(&target_workspace),
496
+ 1,
497
+ "cross-workspace send must persist in the target workspace"
498
+ );
499
+
500
+ let _ = std::fs::remove_dir_all(sender_workspace);
501
+ let _ = std::fs::remove_dir_all(target_workspace);
502
+ }
503
+
504
+ #[test]
505
+ fn a13_worker_cross_workspace_identity_is_preserved_in_row_and_render() {
506
+ let sender_workspace = fresh_workspace("identity-sender");
507
+ let target_workspace = fresh_workspace("identity-target");
508
+ seed_team(&target_workspace, "other-team");
509
+ let mut args = send_args(&sender_workspace);
510
+ args.to_name = Some(format!("{}::other-team/leader", target_workspace.display()));
511
+ let resolved = leader_address(&sender_workspace, &target_workspace);
512
+
513
+ send_to_resolved_name(&args, &resolved, "identity payload")
514
+ .expect("cross-workspace identity send must persist");
515
+ let store = MessageStore::open(&target_workspace).expect("open target store");
516
+ let connection = crate::db::schema::open_db(store.db_path()).expect("open target db");
517
+ let sender: String = connection
518
+ .query_row("select sender from messages limit 1", [], |row| row.get(0))
519
+ .expect("read sender");
520
+ let expected = format!("{}::sender-team/worker-1", sender_workspace.display());
521
+ assert_eq!(
522
+ sender, expected,
523
+ "durable row must retain the full sender identity"
524
+ );
525
+ let rendered = crate::messaging::delivery::render_message(
526
+ &sender,
527
+ None,
528
+ "identity payload",
529
+ "msg-a13",
530
+ );
531
+ assert!(
532
+ rendered.contains(&expected),
533
+ "rendered pane must show full sender: {rendered}"
534
+ );
535
+ assert!(
536
+ !rendered.contains("from leader"),
537
+ "worker must not render as leader: {rendered}"
538
+ );
539
+
540
+ let _ = std::fs::remove_dir_all(sender_workspace);
541
+ let _ = std::fs::remove_dir_all(target_workspace);
542
+ }
543
+
544
+ #[test]
545
+ fn a13_worker_in_workspace_leader_send_remains_allowed() {
546
+ let workspace = fresh_workspace("in-workspace");
547
+ seed_team(&workspace, "other-team");
548
+ let args = send_args(&workspace);
549
+ let resolved = leader_address(&workspace, &workspace);
550
+
551
+ let value = send_to_resolved_name(&args, &resolved, "payload")
552
+ .expect("in-workspace leader send should preserve the existing path");
553
+ assert_ne!(
554
+ value["reason"],
555
+ Value::String("target_out_of_scope".to_string()),
556
+ "in-workspace send must not be classified as cross-workspace: {value}"
557
+ );
558
+ assert_eq!(
559
+ message_count(&workspace),
560
+ 1,
561
+ "allowed send must persist once"
562
+ );
563
+
564
+ let _ = std::fs::remove_dir_all(workspace);
565
+ }
566
+ }
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! cli · send — `cmd_send` + target 解析(`_send_target`)+ `SendArgs`→`SendOptions` 翻译
2
3
  //! (`send_options_from_args`,旗标取反语义)。
3
4
 
@@ -180,7 +181,7 @@ pub fn send_to_canonical_leader_target(
180
181
  }
181
182
 
182
183
  /// `_send_target`(`commands.py:181-184`):`--to` comma-split fanout / `target` 单值 / None。
183
- pub fn send_target(targets: Option<&str>, target: Option<&str>) -> MessageTarget {
184
+ pub(super) fn send_target(targets: Option<&str>, target: Option<&str>) -> MessageTarget {
184
185
  if let Some(targets) = targets.filter(|s| !s.is_empty()) {
185
186
  let recipients: Vec<String> = targets
186
187
  .split(',')
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! C1 command registry: one catalog for help visibility, known-command gates,
2
3
  //! suggestion candidates, and command-tier governance.
3
4
 
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! cli · status — 五行 triage summary 渲染 + agent 分类计数(commands.py
2
3
  //! `_format_status_summary` / `_agent_summary_counts` / `_interaction_counts` /
3
4
  //! `_latest_result_line` + `cmd_status` 的 compact 不变量)。
@@ -100,7 +101,7 @@ fn stale_agent_bucket(agent: &Value) -> Option<SummaryBucket> {
100
101
  }
101
102
 
102
103
  /// `_interaction_counts`(`commands.py:292-306`):遍历 agents 的 `interacted` 字段。
103
- pub fn interaction_counts(agents: &Value) -> InteractionCounts {
104
+ pub(super) fn interaction_counts(agents: &Value) -> InteractionCounts {
104
105
  let mut counts = InteractionCounts::default();
105
106
  let Some(agent_map) = agents.as_object() else {
106
107
  return counts;
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use super::*;
2
3
 
3
4
  pub(super) fn compact_status(full: Value) -> Value {
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use crate::cli::CliError;
2
3
  use serde_json::{json, Value};
3
4
 
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use super::*;
2
3
 
3
4
  pub fn inbox(
@@ -122,6 +122,44 @@ fn profile_init_routes_and_creates_secret_boundary_files() {
122
122
  let _ = std::fs::remove_dir_all(&ws);
123
123
  }
124
124
 
125
+ #[test]
126
+ fn compatible_api_profile_init_template_contains_common_five_fields() {
127
+ let ws = tmp_workspace();
128
+ let code = run(
129
+ &profile_argv(&[
130
+ "profile",
131
+ "init",
132
+ "compat",
133
+ "--workspace",
134
+ ".",
135
+ "--auth-mode",
136
+ "compatible_api",
137
+ "--json",
138
+ ]),
139
+ &ws,
140
+ );
141
+ assert_eq!(
142
+ code,
143
+ ExitCode::Ok,
144
+ "compatible_api profile init must route and exit 0"
145
+ );
146
+
147
+ let expected =
148
+ "AUTH_MODE=compatible_api\nPROFILE_NAME=compat\nBASE_URL=\nAPI_KEY=\nMODEL=\n";
149
+ let dir = profiles_dir(&ws);
150
+ assert_eq!(
151
+ std::fs::read_to_string(dir.join("compat.env")).unwrap(),
152
+ expected,
153
+ "compatible_api profile must expose the documented five-field template"
154
+ );
155
+ assert_eq!(
156
+ std::fs::read_to_string(dir.join("compat.example.env")).unwrap(),
157
+ expected,
158
+ "compatible_api example must expose the documented five-field template"
159
+ );
160
+ let _ = std::fs::remove_dir_all(&ws);
161
+ }
162
+
125
163
  // Golden source:
126
164
  // - profiles/core.py:95-119 doctor_profile returns ok=true for existing profiles and
127
165
  // ok=false for missing profiles; parser.py:506-508 maps result.ok false to exit 1.
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! cli · types — 错误信封 / 退出码 / 命令结果载体 / leader passthrough 参数 /
2
3
  //! 每子命令的 clap-style arg 结构 / 五行 summary 计数桶。
3
4
 
@@ -13,7 +14,7 @@ use crate::transport::PaneId;
13
14
  /// `{ok:false, error, action, log, reason?, session_name?, next_actions?}`(字节级保留)。
14
15
  /// 人读时打 `error:`/`action:`/`log:` 三行到 stderr(`helpers.py:132-134`)。
15
16
  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16
- pub struct CliErrorPayload {
17
+ pub(crate) struct CliErrorPayload {
17
18
  /// 恒 `false`(错误信封)。
18
19
  pub ok: bool,
19
20
  pub error: String,
@@ -70,7 +71,7 @@ impl From<crate::messaging::MessagingError> for CliError {
70
71
  impl CliError {
71
72
  /// `_cli_error_payload`(`helpers.py:137-155`):落盘日志后构造稳定信封。tmux session
72
73
  /// 冲突时按 command 富化 reason/session_name/next_actions(`helpers.py:158-187`)。
73
- pub fn to_payload(&self, log_path: &Path, command: &str) -> CliErrorPayload {
74
+ pub(crate) fn to_payload(&self, log_path: &Path, command: &str) -> CliErrorPayload {
74
75
  let error = self.to_string();
75
76
  let mut payload = CliErrorPayload {
76
77
  ok: false,
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Official communication modes shared by configuration and projection consumers.
2
3
 
3
4
  /// The complete product-supported communication-mode catalog.
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! step 6 · compiler — doc-driven team source → canonical `team.spec` dict.
2
3
  //!
3
4
  //! Truth source (READ-ONLY): `team-agent-public` @ v0.2.11, `team_agent/compiler.py`.
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! `ConPtyBackend` — implements the existing `Transport` trait using the
2
3
  //! named-pipe protocol in `super::protocol`.
3
4
  //!
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! 0.5.x Windows-native transport Phase 1: ConPTY backend.
2
3
  //!
3
4
  //! Wire protocol + portable shim state machine live in the
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! daemon 主循环面(`__main__.py`)—— 退避序列 + tick 间隔解析 + 子进程入口。
2
3
 
3
4
  use thiserror::Error;
@@ -10,7 +11,7 @@ use crate::provider::ProviderAdapter;
10
11
  use super::health::{coordinator_pid_path, write_coordinator_metadata};
11
12
  use super::tick::{write_coordinator_heartbeat, TickError, TickReport, HEARTBEAT_STATUS_PANIC};
12
13
  use super::types::{
13
- ErrorLists, MetadataSource, Pid, ProviderRegistry, WorkspacePath, BACKOFF_MAX_SEC,
14
+ MetadataSource, Pid, ProviderRegistry, WorkspacePath, BACKOFF_MAX_SEC,
14
15
  DEFAULT_TICK_INTERVAL_SEC,
15
16
  };
16
17
  use super::Coordinator;
@@ -122,7 +123,7 @@ pub fn run_daemon(args: DaemonArgs) -> Result<(), DaemonError> {
122
123
  run_daemon_with_coordinator_and_boot_tmux(&args, &coordinator, Some(tmux_metadata))
123
124
  }
124
125
 
125
- pub fn run_daemon_with_coordinator(
126
+ pub(crate) fn run_daemon_with_coordinator(
126
127
  args: &DaemonArgs,
127
128
  coordinator: &Coordinator,
128
129
  ) -> Result<(), DaemonError> {
@@ -519,14 +520,14 @@ fn current_ppid() -> u32 {
519
520
 
520
521
  /// 计算 tick 间隔(`_tick_interval`,`__main__.py:104-115`)。读 spec `runtime.tick_interval_sec`,
521
522
  /// 缺失/出错 → `DEFAULT_TICK_INTERVAL_SEC`;并确保 schema 存在(`MessageStore(workspace)`)。
522
- pub fn resolve_tick_interval(workspace: &WorkspacePath) -> Result<f64, TickError> {
523
+ pub(super) fn resolve_tick_interval(workspace: &WorkspacePath) -> Result<f64, TickError> {
523
524
  let _ = MessageStore::open(workspace.as_path())?;
524
525
  Ok(DEFAULT_TICK_INTERVAL_SEC)
525
526
  }
526
527
 
527
528
  /// 退避序列(`__main__.py:65`):`min(interval * 2^min(failures-1, 5), 60.0)` → 5→10→20→40→60→60s。
528
529
  /// unit test 锁死本序列(card §85)。**纯函数,无 I/O,可直接 impl 钉死**(但 ROUND-0 仍占位)。
529
- pub fn backoff_sleep_sec(interval: f64, consecutive_failures: u32) -> f64 {
530
+ pub(super) fn backoff_sleep_sec(interval: f64, consecutive_failures: u32) -> f64 {
530
531
  let failures = consecutive_failures.saturating_sub(1).min(5);
531
532
  let exp = i32::try_from(failures).unwrap_or(5);
532
533
  (interval * 2f64.powi(exp)).min(BACKOFF_MAX_SEC)
@@ -538,10 +539,6 @@ impl ProviderRegistry for RealProviderRegistry {
538
539
  fn adapter_for(&self, provider: Provider) -> Box<dyn ProviderAdapter> {
539
540
  crate::provider::get_adapter(provider)
540
541
  }
541
-
542
- fn error_lists(&self, _provider: Provider) -> ErrorLists {
543
- ErrorLists::default()
544
- }
545
542
  }
546
543
 
547
544
  fn sleep_seconds(seconds: f64) {
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Windows ConPTY shim lifecycle manager.
2
3
  //!
3
4
  //! 0.5.x Windows portability Batch 6 Option A. The design's
@@ -160,7 +161,7 @@ pub enum ShimError {
160
161
  /// installations.
161
162
  /// 2. Env override `TEAM_AGENT_WINDOWS_SHIM_PATH` — tests + CI
162
163
  /// harness (Batch 6 SSH real-machine harness sets this).
163
- pub fn locate_shim_binary() -> Result<PathBuf, ShimError> {
164
+ fn locate_shim_binary() -> Result<PathBuf, ShimError> {
164
165
  if let Ok(explicit) = std::env::var("TEAM_AGENT_WINDOWS_SHIM_PATH") {
165
166
  let p = PathBuf::from(&explicit);
166
167
  if p.exists() {
@@ -218,7 +219,7 @@ fn fresh_pipe_token() -> String {
218
219
  /// downstream `transport_factory::conpty_pipe_ready(...)` opens on
219
220
  /// the next factory resolve. The `pipe_token` is NOT persisted (CR
220
221
  /// C-1); it stays in `ShimHandle` memory only.
221
- pub fn spawn_shim_and_handshake(
222
+ fn spawn_shim_and_handshake(
222
223
  workspace: &Path,
223
224
  team_key: &str,
224
225
  workspace_hash: &str,
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! coordinator 健康/身份 & 只读可观测面:metadata 身份原语 + coordinator 路径 + watch 实时流。
2
3
 
3
4
  use std::io::{Read, Seek, SeekFrom};
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! step 12 · coordinator — daemon lifecycle / single-tick orchestration SKELETON (ROUND-0).
2
3
  //!
3
4
  //! Card: `docs/phase0/subsystems/12-coordinator.md`.
@@ -10,8 +11,7 @@
10
11
  //! - `coordinator/metadata.py` (COORDINATOR_PROTOCOL_VERSION=2, pid_is_running, read/write/ok meta)
11
12
  //! - `coordinator/paths.py` (coordinator.pid / coordinator.json / coordinator.log paths)
12
13
  //! - `watch/__init__.py` (run_watch / collect_watch_lines / render_event_line / WatchCursor)
13
- //! - `abnormal_track.py` (Gap 32 §4 provider-neutral abnormal track:
14
- //! process_abnormal_records / detect_whole_team_gone)
14
+ //! - `abnormal_track.py` (Gap 32 §4 provider-neutral whole-team-gone detection)
15
15
  //!
16
16
  //! 职责(card §职责):per-workspace daemon 生命周期 + 单次 tick 编排。tick 按固定顺序把
17
17
  //! step 8-11 的原子操作串成一个只读 + 投递既定 obligation 的回路 —— **绝不**在无 pending
@@ -1,18 +1,11 @@
1
- //! 孤儿自终止判定(Gap 37b)+ provider-neutral 异常轨(abnormal_track:整队消失检测 + 去重通知)。
2
-
3
- use std::hash::{Hash, Hasher};
4
-
5
- use serde_json::Value;
6
-
7
- use crate::model::enums::Provider;
8
- use crate::provider::{ProcessLiveness, Signature, TurnId, TurnState};
1
+ //!
2
+ //! 孤儿自终止判定(Gap 37b)+ provider-neutral 整队消失检测。
9
3
 
10
4
  use super::types::{
11
- AbnormalDecision, AbnormalError, AbnormalNotification, AbnormalNotificationState,
12
- AbnormalProcessOutput, MarkerStore, ProviderRegistry, TeamPresenceSnapshot, WholeTeamGoneClass,
13
- WholeTeamGoneReport, WorkspacePath,
5
+ MarkerStore, TeamPresenceSnapshot, WholeTeamGoneClass, WholeTeamGoneReport, WorkspacePath,
14
6
  };
15
7
 
8
+ ///
16
9
  /// 孤儿自终止判定(`__main__.py:51-59`,Gap 37b)。仅当
17
10
  /// `current_ppid != initial_ppid ∧ current_ppid == 1 ∧ !workspace.exists()` 三者**同时**成立 → true。
18
11
  /// 少一个条件都不能误杀正常 daemon(card §91)。
@@ -28,104 +21,7 @@ pub fn should_orphan_self_terminate(
28
21
  // abnormal_track 公共面(abnormal_track.py)—— Gap 32 §4 provider-neutral
29
22
  // ===========================================================================
30
23
 
31
- /// #236 dedicated `worker.abnormal_exit` surface.
32
24
  ///
33
- /// This is intentionally separate from `process_abnormal_records`: the generic
34
- /// abnormal path is transcript-only, while `worker.abnormal_exit` requires a fresh
35
- /// latest explicit provider error before leader notification.
36
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
37
- #[serde(tag = "event")]
38
- pub enum WorkerAbnormalExitSurface {
39
- #[serde(rename = "worker.abnormal_exit")]
40
- WorkerAbnormalExit {
41
- agent_id: String,
42
- provider: Provider,
43
- path: String,
44
- provider_process_dead: bool,
45
- latest_explicit_error: bool,
46
- signature: Signature,
47
- turn_id: Option<TurnId>,
48
- process_liveness: ProcessLiveness,
49
- },
50
- #[serde(rename = "abnormal_exit.single_signal_suppressed")]
51
- SingleSignalSuppressed {
52
- agent_id: String,
53
- provider: Provider,
54
- path: String,
55
- provider_process_dead: bool,
56
- latest_explicit_error: bool,
57
- reason: String,
58
- },
59
- }
60
-
61
- /// `process_abnormal_records`(`abnormal_track.py:14`)。把原始 provider session records 经
62
- /// provider reader 翻译成结构化 fault facts(本 module **不命名 provider、不读屏**),catch-bias
63
- /// (C9)+ `(signature, turn_id|fingerprint)` 去重(C8)。`registry` 携带 provider + 黑白名单。
64
- pub fn process_abnormal_records(
65
- records: &[Value],
66
- registry: &dyn ProviderRegistry,
67
- provider: Provider,
68
- notification_state: &AbnormalNotificationState,
69
- ) -> Result<AbnormalProcessOutput, AbnormalError> {
70
- let lists = registry.error_lists(provider);
71
- let mut state = notification_state.clone();
72
- let mut notifications = Vec::new();
73
- for record in records {
74
- let raw = record
75
- .get("raw")
76
- .and_then(Value::as_str)
77
- .unwrap_or_else(|| record.as_str().unwrap_or(""))
78
- .to_string();
79
- let signature = record
80
- .get("signature")
81
- .and_then(Value::as_str)
82
- .unwrap_or("unknown")
83
- .to_string();
84
- let signature_lower = signature.to_lowercase();
85
- let turn_id = record
86
- .get("turn_id")
87
- .and_then(Value::as_str)
88
- .map(TurnId::new);
89
- let bucket = match &turn_id {
90
- Some(id) => id.as_str().to_string(),
91
- None => stable_fingerprint(record),
92
- };
93
- let key = format!("{signature}\0{bucket}");
94
- if state.seen.contains(&key) {
95
- continue;
96
- }
97
- state.seen.insert(key);
98
- if lists.whitelist.iter().any(|needle| raw.contains(needle)) {
99
- continue;
100
- }
101
- let decision =
102
- if lists.blacklist.iter().any(|needle| {
103
- raw.contains(needle) || signature_lower.contains(&needle.to_lowercase())
104
- }) {
105
- AbnormalDecision::NotifyBlacklist
106
- } else {
107
- AbnormalDecision::NotifyDefault
108
- };
109
- let kind = record.get("kind").and_then(Value::as_str);
110
- notifications.push(AbnormalNotification {
111
- signature,
112
- turn_id,
113
- state: if kind == Some("approval") {
114
- TurnState::BlockedOnHuman
115
- } else {
116
- TurnState::Abnormal
117
- },
118
- decision,
119
- provider: Some(provider),
120
- raw: record.clone(),
121
- });
122
- }
123
- Ok(AbnormalProcessOutput {
124
- notifications,
125
- notification_state: state,
126
- })
127
- }
128
-
129
25
  /// `detect_whole_team_gone`(`abnormal_track.py:91`,C10/C13)。coordinator-independent 整队消失检测:
130
26
  /// 全死(coordinator + leader + 所有 worker + 所有 session)且非 clean_shutdown/restart →
131
27
  /// 写 durable marker + 延迟到下条 leader 命令再 escalate。clean/restart 静默。
@@ -166,14 +62,6 @@ pub fn detect_whole_team_gone(
166
62
  )
167
63
  }
168
64
 
169
- fn stable_fingerprint(value: &Value) -> String {
170
- let mut hasher = std::collections::hash_map::DefaultHasher::new();
171
- serde_json::to_string(value)
172
- .unwrap_or_else(|_| value.to_string())
173
- .hash(&mut hasher);
174
- format!("{:016x}", hasher.finish())
175
- }
176
-
177
65
  fn whole_team_report(
178
66
  whole_team_gone: bool,
179
67
  classification: WholeTeamGoneClass,
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use std::collections::BTreeMap;
2
3
  use std::path::Path;
3
4
 
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Shared coordinator runtime observation seam.
2
3
  //!
3
4
  //! S0 only defines the typed capture/result surface. Lane 1 fills capture facts;