@team-agent/installer 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/Cargo.lock +34 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +1 -1
  4. package/crates/team-agent/src/cli/adapters.rs +234 -26
  5. package/crates/team-agent/src/cli/diagnose.rs +144 -10
  6. package/crates/team-agent/src/cli/emit.rs +289 -54
  7. package/crates/team-agent/src/cli/leader.rs +37 -8
  8. package/crates/team-agent/src/cli/mod.rs +1281 -196
  9. package/crates/team-agent/src/cli/status_port.rs +195 -46
  10. package/crates/team-agent/src/cli/tests/divergence.rs +1 -2
  11. package/crates/team-agent/src/cli/tests/lane_c.rs +23 -13
  12. package/crates/team-agent/src/cli/tests/main_preserved.rs +2 -0
  13. package/crates/team-agent/src/cli/tests/run_delegation.rs +59 -3
  14. package/crates/team-agent/src/cli/types.rs +18 -0
  15. package/crates/team-agent/src/compiler.rs +15 -5
  16. package/crates/team-agent/src/coordinator/health.rs +95 -17
  17. package/crates/team-agent/src/coordinator/mod.rs +4 -0
  18. package/crates/team-agent/src/coordinator/runtime_detectors.rs +500 -0
  19. package/crates/team-agent/src/coordinator/runtime_observation.rs +58 -0
  20. package/crates/team-agent/src/coordinator/tick.rs +222 -69
  21. package/crates/team-agent/src/coordinator/types.rs +15 -3
  22. package/crates/team-agent/src/db/schema.rs +37 -2
  23. package/crates/team-agent/src/diagnose/comms.rs +226 -0
  24. package/crates/team-agent/src/diagnose/mod.rs +45 -0
  25. package/crates/team-agent/src/diagnose/orphans.rs +658 -0
  26. package/crates/team-agent/src/fake_worker.rs +146 -3
  27. package/crates/team-agent/src/leader/start.rs +121 -23
  28. package/crates/team-agent/src/leader/types.rs +44 -1
  29. package/crates/team-agent/src/lib.rs +3 -0
  30. package/crates/team-agent/src/lifecycle/display.rs +645 -47
  31. package/crates/team-agent/src/lifecycle/launch.rs +1061 -146
  32. package/crates/team-agent/src/lifecycle/mod.rs +2 -0
  33. package/crates/team-agent/src/lifecycle/profile_launch.rs +810 -0
  34. package/crates/team-agent/src/lifecycle/profile_smoke.rs +522 -0
  35. package/crates/team-agent/src/lifecycle/restart/agent.rs +99 -23
  36. package/crates/team-agent/src/lifecycle/restart/common.rs +183 -24
  37. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +498 -22
  38. package/crates/team-agent/src/lifecycle/restart/remove.rs +27 -7
  39. package/crates/team-agent/src/lifecycle/restart/team_state.rs +19 -0
  40. package/crates/team-agent/src/lifecycle/restart.rs +24 -1
  41. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +5 -5
  42. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +37 -7
  43. package/crates/team-agent/src/lifecycle/types.rs +19 -0
  44. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  45. package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +341 -0
  46. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +10 -0
  47. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +158 -0
  48. package/crates/team-agent/src/mcp_server/mod.rs +3 -74
  49. package/crates/team-agent/src/mcp_server/tests/scoped.rs +1 -1
  50. package/crates/team-agent/src/mcp_server/tests/send.rs +6 -5
  51. package/crates/team-agent/src/mcp_server/tools.rs +312 -111
  52. package/crates/team-agent/src/mcp_server/types.rs +6 -4
  53. package/crates/team-agent/src/mcp_server/wire.rs +19 -7
  54. package/crates/team-agent/src/message_store.rs +21 -4
  55. package/crates/team-agent/src/messaging/delivery.rs +470 -59
  56. package/crates/team-agent/src/messaging/mod.rs +9 -6
  57. package/crates/team-agent/src/messaging/results.rs +353 -63
  58. package/crates/team-agent/src/messaging/selftest.rs +199 -12
  59. package/crates/team-agent/src/messaging/send.rs +35 -3
  60. package/crates/team-agent/src/messaging/tests/runtime.rs +19 -4
  61. package/crates/team-agent/src/messaging/types.rs +11 -3
  62. package/crates/team-agent/src/os_probe.rs +119 -0
  63. package/crates/team-agent/src/packaging/migrate.rs +10 -2
  64. package/crates/team-agent/src/packaging/tests.rs +23 -0
  65. package/crates/team-agent/src/provider/adapter.rs +564 -63
  66. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +1 -7
  67. package/crates/team-agent/src/provider/classify.rs +51 -4
  68. package/crates/team-agent/src/provider/helpers.rs +10 -1
  69. package/crates/team-agent/src/provider/startup_prompt.rs +94 -0
  70. package/crates/team-agent/src/provider/types.rs +47 -0
  71. package/crates/team-agent/src/session_capture.rs +616 -0
  72. package/crates/team-agent/src/state/persist.rs +170 -1
  73. package/crates/team-agent/src/state/projection.rs +141 -8
  74. package/crates/team-agent/src/state/selector.rs +5 -2
  75. package/crates/team-agent/src/tmux_backend.rs +161 -64
  76. package/crates/team-agent/src/transport/test_support.rs +9 -0
  77. package/crates/team-agent/src/transport/tests/wire.rs +4 -0
  78. package/crates/team-agent/src/transport.rs +13 -2
  79. package/package.json +4 -4
@@ -0,0 +1,226 @@
1
+ use std::path::Path;
2
+
3
+ use serde_json::{json, Value};
4
+
5
+ use crate::cli::{CliError, COMMS_BOUNDARY_TEXT};
6
+ use crate::messaging::{
7
+ run_comms_selftest, CheckEvidence, CheckStatus, CommsSelftestDriver, ProviderSdkCalls,
8
+ SelftestCheck, SelftestReport,
9
+ };
10
+ use crate::model::ids::TeamKey;
11
+ use crate::transport::Transport;
12
+
13
+ pub fn doctor_comms_json(
14
+ workspace: &Path,
15
+ team: Option<&str>,
16
+ gate: Option<&str>,
17
+ ) -> Result<Value, CliError> {
18
+ let _ = (team, gate);
19
+ let team_key = team.map(TeamKey::new);
20
+ let report = comms_selftest_report(workspace, team_key.as_ref())
21
+ .map_err(|e| CliError::Runtime(e.to_string()))?;
22
+ Ok(report_json(&report))
23
+ }
24
+
25
+ pub fn comms_selftest_report(
26
+ workspace: &Path,
27
+ team: Option<&TeamKey>,
28
+ ) -> Result<SelftestReport, crate::messaging::MessagingError> {
29
+ run_comms_selftest(workspace, team, &RuntimeCommsDriver)
30
+ }
31
+
32
+ pub fn failing_check_names(report: &SelftestReport) -> Vec<&'static str> {
33
+ let mut names = Vec::new();
34
+ if report.receiver_binding.status != CheckStatus::Pass {
35
+ names.push("receiver_binding");
36
+ }
37
+ if report.contract_suite.status != CheckStatus::Pass {
38
+ names.push("contract_suite");
39
+ }
40
+ if report.provider_sdk_calls.status != CheckStatus::Pass {
41
+ names.push("provider_sdk_calls");
42
+ }
43
+ names
44
+ }
45
+
46
+ struct RuntimeCommsDriver;
47
+
48
+ impl CommsSelftestDriver for RuntimeCommsDriver {
49
+ fn run_id(&self) -> Option<String> {
50
+ None
51
+ }
52
+
53
+ fn provider_sdk_calls(&self) -> ProviderSdkCalls {
54
+ ProviderSdkCalls::default()
55
+ }
56
+
57
+ fn receiver_binding(&self, workspace: &Path, team: Option<&TeamKey>) -> Value {
58
+ receiver_binding_snapshot(workspace, team)
59
+ }
60
+ }
61
+
62
+ fn receiver_binding_snapshot(workspace: &Path, team: Option<&TeamKey>) -> Value {
63
+ let selected = crate::state::selector::resolve_active_team(
64
+ workspace,
65
+ team.map(TeamKey::as_str),
66
+ crate::state::selector::SelectorMode::RuntimeOnly,
67
+ );
68
+ let Ok(selected) = selected else {
69
+ let reason = selected.err().map(|e| e.to_string()).unwrap_or_default();
70
+ return json!({
71
+ "status": "fail",
72
+ "verifies": "binding_consistency",
73
+ "proof": "state_read",
74
+ "state_read_observed": false,
75
+ "pane_id": Value::Null,
76
+ "owner_pane_id": Value::Null,
77
+ "caller_pane_id": std::env::var("TMUX_PANE").ok().map(Value::String).unwrap_or(Value::Null),
78
+ "mismatches": ["runtime_state_unresolved"],
79
+ "reason": reason,
80
+ "configured": false,
81
+ });
82
+ };
83
+ let state = selected.state;
84
+ let receiver = state.get("leader_receiver").and_then(Value::as_object);
85
+ let owner_pane_id = state
86
+ .get("owner")
87
+ .or_else(|| state.get("team_owner"))
88
+ .and_then(|v| v.get("pane_id"))
89
+ .cloned()
90
+ .unwrap_or(Value::Null);
91
+ let caller_pane_id = std::env::var("TMUX_PANE").ok().map(Value::String).unwrap_or(Value::Null);
92
+ let pane_id = receiver
93
+ .and_then(|r| r.get("pane_id"))
94
+ .cloned()
95
+ .unwrap_or(Value::Null);
96
+ let tmux_socket = receiver
97
+ .and_then(|r| r.get("tmux_socket"))
98
+ .and_then(Value::as_str)
99
+ .filter(|s| !s.is_empty())
100
+ .map(ToString::to_string);
101
+ let mut mismatches = receiver_binding_mismatches(&owner_pane_id, &caller_pane_id, &pane_id);
102
+ if !crate::state::persist::runtime_state_path(&selected.run_workspace).exists() {
103
+ mismatches.push(json!("runtime_state_missing"));
104
+ }
105
+ if receiver.is_none() {
106
+ mismatches.push(json!("leader_receiver_missing"));
107
+ }
108
+ if pane_id.as_str().filter(|s| !s.is_empty()).is_none() {
109
+ mismatches.push(json!("leader_receiver_pane_missing"));
110
+ }
111
+ if let (Some(socket), Some(pane)) = (tmux_socket.as_deref(), pane_id.as_str()) {
112
+ if receiver_pane_stale(socket, pane) {
113
+ mismatches.push(json!("receiver_pane_stale"));
114
+ }
115
+ }
116
+ json!({
117
+ "status": if mismatches.is_empty() { "pass" } else { "fail" },
118
+ "verifies": "binding_consistency",
119
+ "proof": "state_read",
120
+ "state_read_observed": true,
121
+ "team_key": selected.team_key,
122
+ "workspace": selected.run_workspace.to_string_lossy().to_string(),
123
+ "pane_id": pane_id,
124
+ "owner_pane_id": owner_pane_id,
125
+ "caller_pane_id": caller_pane_id,
126
+ "tmux_socket": tmux_socket,
127
+ "mismatches": mismatches,
128
+ "configured": receiver.is_some(),
129
+ })
130
+ }
131
+
132
+ fn receiver_pane_stale(socket: &str, pane_id: &str) -> bool {
133
+ crate::tmux_backend::TmuxBackend::for_tmux_endpoint(socket)
134
+ .list_targets()
135
+ .map(|targets| !targets.iter().any(|target| target.pane_id.as_str() == pane_id))
136
+ .unwrap_or(true)
137
+ }
138
+
139
+ fn report_json(report: &SelftestReport) -> Value {
140
+ json!({
141
+ "ok": report.ok,
142
+ "status": status_json(report.status),
143
+ "run_id": report.run_id,
144
+ "scope": report.scope,
145
+ "boundary": COMMS_BOUNDARY_TEXT,
146
+ "checks": {
147
+ "receiver_binding": check_json(&report.receiver_binding),
148
+ "contract_suite": check_json(&report.contract_suite),
149
+ "provider_sdk_calls": check_json(&report.provider_sdk_calls),
150
+ },
151
+ })
152
+ }
153
+
154
+ fn check_json(check: &SelftestCheck) -> Value {
155
+ let mut value = serde_json::Map::new();
156
+ value.insert("status".to_string(), status_json(check.status));
157
+ value.insert("verifies".to_string(), verifies_json(check));
158
+ match &check.evidence {
159
+ CheckEvidence::ProviderSdkCalls(calls) => {
160
+ value.insert("calls".to_string(), json!(calls));
161
+ }
162
+ CheckEvidence::Binding { details, .. } => {
163
+ if let Some(obj) = details.as_object() {
164
+ for (key, detail) in obj {
165
+ value.entry(key.clone()).or_insert_with(|| detail.clone());
166
+ }
167
+ }
168
+ }
169
+ CheckEvidence::ContractSuite { checks } => {
170
+ let checks_json: Vec<Value> = checks
171
+ .iter()
172
+ .map(|check| {
173
+ json!({
174
+ "name": check.name,
175
+ "status": status_json(check.status),
176
+ "reason": check.reason,
177
+ })
178
+ })
179
+ .collect();
180
+ let failed: Vec<&str> = checks
181
+ .iter()
182
+ .filter(|check| check.status != CheckStatus::Pass)
183
+ .map(|check| check.name.as_str())
184
+ .collect();
185
+ value.insert("checks".to_string(), Value::Array(checks_json));
186
+ value.insert("failed".to_string(), json!(failed));
187
+ }
188
+ }
189
+ Value::Object(value)
190
+ }
191
+
192
+ fn status_json(status: CheckStatus) -> Value {
193
+ serde_json::to_value(status).unwrap_or_else(|_| json!("fail"))
194
+ }
195
+
196
+ fn verifies_json(check: &SelftestCheck) -> Value {
197
+ serde_json::to_value(check.verifies).unwrap_or_else(|_| json!("unknown"))
198
+ }
199
+
200
+ pub fn receiver_binding_mismatches(
201
+ owner_pane_id: &Value,
202
+ caller_pane_id: &Value,
203
+ pane_id: &Value,
204
+ ) -> Vec<Value> {
205
+ let mut mismatches = Vec::new();
206
+ if pane_mismatch(owner_pane_id, pane_id) {
207
+ mismatches.push(json!("owner_receiver_pane_mismatch"));
208
+ }
209
+ if pane_mismatch(caller_pane_id, owner_pane_id) {
210
+ mismatches.push(json!("caller_owner_pane_mismatch"));
211
+ }
212
+ if pane_mismatch(caller_pane_id, pane_id) {
213
+ mismatches.push(json!("caller_receiver_pane_mismatch"));
214
+ }
215
+ mismatches
216
+ }
217
+
218
+ fn pane_mismatch(left: &Value, right: &Value) -> bool {
219
+ let Some(left) = left.as_str().filter(|s| !s.is_empty()) else {
220
+ return false;
221
+ };
222
+ let Some(right) = right.as_str().filter(|s| !s.is_empty()) else {
223
+ return false;
224
+ };
225
+ left != right
226
+ }
@@ -0,0 +1,45 @@
1
+ //! Shared doctor gate seams.
2
+
3
+ use std::path::Path;
4
+
5
+ use crate::packaging::types::{
6
+ Blocker, BlockerSource, DoctorGate, PackagingError,
7
+ };
8
+
9
+ pub mod comms;
10
+ pub mod orphans;
11
+
12
+ pub fn doctor_gate_blockers(
13
+ workspace: &Path,
14
+ gate: Option<DoctorGate>,
15
+ fix: bool,
16
+ confirm: bool,
17
+ ) -> Result<Vec<Blocker>, PackagingError> {
18
+ let _ = (fix, confirm);
19
+ match gate {
20
+ Some(DoctorGate::Orphans) => {
21
+ if orphans::has_orphan_residue() {
22
+ Ok(vec![Blocker {
23
+ source: BlockerSource::OrphanCoordinator,
24
+ detail: orphans::orphan_blocker_detail(),
25
+ }])
26
+ } else {
27
+ Ok(Vec::new())
28
+ }
29
+ }
30
+ Some(DoctorGate::Comms) => {
31
+ let report = comms::comms_selftest_report(workspace, None)
32
+ .map_err(|e| PackagingError::State(e.to_string()))?;
33
+ if report.ok {
34
+ Ok(Vec::new())
35
+ } else {
36
+ let failed = comms::failing_check_names(&report).join(", ");
37
+ Ok(vec![Blocker {
38
+ source: BlockerSource::CommsGate,
39
+ detail: format!("comms selftest failed: {failed}"),
40
+ }])
41
+ }
42
+ }
43
+ _ => Ok(Vec::new()),
44
+ }
45
+ }