@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
@@ -88,13 +88,7 @@ pub fn awaiting_human_confirm_reason(
88
88
  None
89
89
  }
90
90
  }
91
- ApprovalKind::Command => {
92
- if leader_auto_approval_allowed {
93
- None
94
- } else {
95
- Some("leader_restricted")
96
- }
97
- }
91
+ ApprovalKind::Command => Some("command_approval_requires_human"),
98
92
  ApprovalKind::Unknown => Some("approval_requires_human"),
99
93
  }
100
94
  }
@@ -229,7 +229,11 @@ fn decide_state(fact: &LifecycleFact, process: ProcessLiveness) -> ClassifyResul
229
229
  ProcessLiveness::Alive => classify_result(
230
230
  TurnState::Working,
231
231
  fact.turn_id.clone(),
232
- "open_turn",
232
+ if fact.reason == "assistant_in_flight" {
233
+ &fact.reason
234
+ } else {
235
+ "open_turn"
236
+ },
233
237
  ClassifySource::SessionFile,
234
238
  Vec::new(),
235
239
  ),
@@ -281,12 +285,55 @@ fn decide_state(fact: &LifecycleFact, process: ProcessLiveness) -> ClassifyResul
281
285
 
282
286
  fn claude_lifecycle_fact(record: &serde_json::Value) -> Option<LifecycleFact> {
283
287
  let record_type = record.get("type").and_then(serde_json::Value::as_str);
288
+ if record_type == Some("system")
289
+ && record.get("subtype").and_then(serde_json::Value::as_str) == Some("api_error")
290
+ && record.get("level").and_then(serde_json::Value::as_str) == Some("error")
291
+ {
292
+ return Some(lifecycle(
293
+ FactKind::Error,
294
+ record
295
+ .get("sessionId")
296
+ .or_else(|| record.get("parentUuid"))
297
+ .or_else(|| record.get("uuid"))
298
+ .and_then(serde_json::Value::as_str)
299
+ .map(TurnId::new),
300
+ "api_error",
301
+ vec!["api_error".to_string()],
302
+ ));
303
+ }
304
+ if record_type == Some("user") && claude_record_has_error_tool_result(record) {
305
+ return Some(lifecycle(
306
+ FactKind::Error,
307
+ record
308
+ .get("parentUuid")
309
+ .or_else(|| record.get("uuid"))
310
+ .and_then(serde_json::Value::as_str)
311
+ .map(TurnId::new),
312
+ "tool_result_is_error",
313
+ vec!["tool_result_is_error".to_string()],
314
+ ));
315
+ }
284
316
  if record_type == Some("assistant") {
285
- let stop_reason = record
317
+ let turn_id = record.get("requestId").and_then(serde_json::Value::as_str).map(TurnId::new);
318
+ let Some(stop_reason) = record
286
319
  .get("message")
287
320
  .and_then(|m| m.get("stop_reason"))
288
- .and_then(serde_json::Value::as_str)?;
289
- let turn_id = record.get("requestId").and_then(serde_json::Value::as_str).map(TurnId::new);
321
+ .and_then(serde_json::Value::as_str) else {
322
+ if record
323
+ .get("message")
324
+ .and_then(|m| m.get("content"))
325
+ .and_then(serde_json::Value::as_array)
326
+ .is_some()
327
+ {
328
+ return Some(lifecycle(
329
+ FactKind::TurnOpen,
330
+ turn_id,
331
+ "assistant_in_flight",
332
+ vec!["assistant_in_flight".to_string()],
333
+ ));
334
+ }
335
+ return None;
336
+ };
290
337
  return match stop_reason {
291
338
  "tool_use" => Some(lifecycle(FactKind::TurnOpen, turn_id, "open_turn", Vec::new())),
292
339
  "end_turn" => Some(lifecycle(FactKind::TurnComplete, turn_id, "end_turn", Vec::new())),
@@ -22,9 +22,18 @@ pub(crate) fn find_session_id(record: &serde_json::Value) -> Option<String> {
22
22
  if let Some(s) = record.get("sessionId").and_then(serde_json::Value::as_str) {
23
23
  return Some(s.to_string());
24
24
  }
25
- record
25
+ if let Some(s) = record
26
26
  .get("session_id")
27
27
  .and_then(serde_json::Value::as_str)
28
+ {
29
+ return Some(s.to_string());
30
+ }
31
+ record
32
+ .get("session_meta")
33
+ .and_then(|v| v.get("payload"))
34
+ .or_else(|| record.get("payload"))
35
+ .and_then(|v| v.get("id"))
36
+ .and_then(serde_json::Value::as_str)
28
37
  .map(ToString::to_string)
29
38
  }
30
39
 
@@ -20,6 +20,14 @@ const TRUST_MARKERS: &[&str] = &[
20
20
  "Do you trust this folder?",
21
21
  ];
22
22
  const UPDATE_MARKERS: &[&str] = &["Update available!", "Update now"];
23
+ const CLAUDE_TRUST_MARKERS: &[&str] = &[
24
+ "Quick safety check",
25
+ "Is this a project you created or one you trust?",
26
+ "Yes, I trust this folder",
27
+ "No, exit",
28
+ "Enter to confirm",
29
+ ];
30
+ const CLAUDE_READY_MARKERS: &[&str] = &["Claude Code"];
23
31
  /// Plain ready markers (not the bare `›` glyph — that glyph also indicates a
24
32
  /// numbered-menu selector and is handled by [`rightmost_input_prompt_glyph`] with
25
33
  /// shape gating per N15 / CR-063: detect by SHAPE, not a single Unicode codepoint).
@@ -87,6 +95,17 @@ pub fn classify_codex_startup_screen(output: &str) -> StartupScreenDecision {
87
95
  }
88
96
  }
89
97
 
98
+ pub fn classify_claude_startup_screen(output: &str) -> StartupScreenDecision {
99
+ if has_active_claude_yes_trust_shape(output) {
100
+ return StartupScreenDecision::AnswerWorkspaceTrust;
101
+ }
102
+ if has_claude_ready_shape(output) {
103
+ StartupScreenDecision::Ready
104
+ } else {
105
+ StartupScreenDecision::KeepPolling
106
+ }
107
+ }
108
+
90
109
  /// Actionable trust shape (N15): the captured text contains a trust phrase AND a
91
110
  /// numbered-menu selector line `› <digit>. `. This is the modal-still-active signal
92
111
  /// that survives Codex's pre-rendering of the banner/input prompt below the menu.
@@ -121,6 +140,51 @@ fn contains_numbered_menu_glyph(output: &str) -> bool {
121
140
  false
122
141
  }
123
142
 
143
+ fn has_active_claude_yes_trust_shape(output: &str) -> bool {
144
+ if !CLAUDE_TRUST_MARKERS
145
+ .iter()
146
+ .all(|marker| output.contains(marker))
147
+ {
148
+ return false;
149
+ }
150
+ output
151
+ .lines()
152
+ .map(str::trim_start)
153
+ .any(|line| {
154
+ line == "❯ 1. Yes, I trust this folder"
155
+ || line == "> 1. Yes, I trust this folder"
156
+ })
157
+ }
158
+
159
+ fn has_claude_ready_shape(output: &str) -> bool {
160
+ max_rfind(output, CLAUDE_READY_MARKERS).is_some()
161
+ && rightmost_claude_input_prompt_glyph(output).is_some()
162
+ }
163
+
164
+ fn rightmost_claude_input_prompt_glyph(output: &str) -> Option<usize> {
165
+ let mut best = rightmost_input_prompt_for_glyph(output, '❯');
166
+ best = max_two(best, rightmost_input_prompt_for_glyph(output, '>'));
167
+ best
168
+ }
169
+
170
+ fn rightmost_input_prompt_for_glyph(output: &str, glyph: char) -> Option<usize> {
171
+ let glyph_len = glyph.len_utf8();
172
+ let mut best = None;
173
+ let mut start = 0;
174
+ while let Some(rel) = output[start..].find(glyph) {
175
+ let abs = start + rel;
176
+ let tail_start = abs + glyph_len;
177
+ if tail_start <= output.len() && !is_numbered_menu_tail(&output[tail_start..]) {
178
+ best = Some(abs);
179
+ }
180
+ start = tail_start;
181
+ if start > output.len() {
182
+ break;
183
+ }
184
+ }
185
+ best
186
+ }
187
+
124
188
  /// Rightmost `›` whose tail is NOT a numbered-menu selector (` <digit>. `). A bare
125
189
  /// `›` followed by free text or whitespace is the Codex main-input prompt indicator;
126
190
  /// a `›` followed by `1. Yes, continue` is part of the trust/update menu and is NOT
@@ -204,6 +268,36 @@ pub fn codex_handle_startup_prompts(
204
268
  handled
205
269
  }
206
270
 
271
+ pub fn claude_handle_startup_prompts(
272
+ transport: &dyn Transport,
273
+ target: &Target,
274
+ checks: usize,
275
+ sleep_s: f64,
276
+ ) -> Vec<HandledPrompt> {
277
+ let mut handled = Vec::new();
278
+ for _ in 0..checks {
279
+ let screen = match transport.capture(target, CaptureRange::Full) {
280
+ Ok(captured) => captured.text,
281
+ Err(_) => String::new(),
282
+ };
283
+ match classify_claude_startup_screen(&screen) {
284
+ StartupScreenDecision::AnswerWorkspaceTrust => {
285
+ let _ = transport.send_keys(target, &[Key::Enter]);
286
+ handled.push(HandledPrompt {
287
+ prompt: "claude_workspace_trust".to_string(),
288
+ action: "sent_enter".to_string(),
289
+ });
290
+ sleep_between_polls(sleep_s);
291
+ }
292
+ StartupScreenDecision::Ready => break,
293
+ StartupScreenDecision::SkipUpdatePrompt | StartupScreenDecision::KeepPolling => {
294
+ sleep_between_polls(sleep_s);
295
+ }
296
+ }
297
+ }
298
+ handled
299
+ }
300
+
207
301
  fn max_rfind(output: &str, needles: &[&str]) -> Option<usize> {
208
302
  needles.iter().filter_map(|needle| output.rfind(needle)).max()
209
303
  }
@@ -1,10 +1,13 @@
1
1
  //! provider 共享类型:enums / newtypes / ProviderCaps / ProviderError / 捕获+classify payload / 占位结构。
2
2
 
3
+ use std::collections::{BTreeMap, BTreeSet};
3
4
  use std::path::PathBuf;
4
5
 
5
6
  use serde::{Deserialize, Serialize};
6
7
  use thiserror::Error;
7
8
 
9
+ use super::AuthMode;
10
+
8
11
  // ===========================================================================
9
12
  // ENUMS (穷尽 + serde rename 到精确 Python 字符串)
10
13
  // ===========================================================================
@@ -305,6 +308,50 @@ pub struct ProviderCaps {
305
308
  pub writes_global_settings: bool,
306
309
  }
307
310
 
311
+ #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
312
+ pub struct ProviderCommandOverrides {
313
+ pub model: Option<String>,
314
+ }
315
+
316
+ #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
317
+ pub struct ProviderProfileLaunch {
318
+ pub env_overlay: BTreeMap<String, String>,
319
+ pub env_unset: BTreeSet<String>,
320
+ pub command_overrides: ProviderCommandOverrides,
321
+ pub claude_config_dir: Option<PathBuf>,
322
+ pub claude_projects_root: Option<PathBuf>,
323
+ pub managed_mcp_config: bool,
324
+ }
325
+
326
+ #[derive(Debug, Clone, Copy)]
327
+ pub struct ProviderCommandContext<'a> {
328
+ pub auth_mode: AuthMode,
329
+ pub mcp_config: Option<&'a McpConfig>,
330
+ pub system_prompt: Option<&'a str>,
331
+ pub model: Option<&'a str>,
332
+ pub tools: &'a [&'a str],
333
+ pub profile_launch: Option<&'a ProviderProfileLaunch>,
334
+ }
335
+
336
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
337
+ pub struct CommandPlan {
338
+ pub argv: Vec<String>,
339
+ pub expected_session_id: Option<SessionId>,
340
+ pub provider_projects_root: Option<PathBuf>,
341
+ pub managed_mcp_config: bool,
342
+ }
343
+
344
+ impl CommandPlan {
345
+ pub fn argv_only(argv: Vec<String>) -> Self {
346
+ Self {
347
+ argv,
348
+ expected_session_id: None,
349
+ provider_projects_root: None,
350
+ managed_mcp_config: false,
351
+ }
352
+ }
353
+ }
354
+
308
355
  // ===========================================================================
309
356
  // ERROR
310
357
  // ===========================================================================