@team-agent/installer 0.3.12 → 0.3.14

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 (35) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +23 -0
  4. package/crates/team-agent/src/cli/leader.rs +2 -5
  5. package/crates/team-agent/src/cli/mod.rs +258 -79
  6. package/crates/team-agent/src/cli/tests/compile.rs +69 -0
  7. package/crates/team-agent/src/cli/tests/main_preserved.rs +68 -0
  8. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +160 -2
  9. package/crates/team-agent/src/compiler.rs +30 -0
  10. package/crates/team-agent/src/coordinator/tick.rs +16 -83
  11. package/crates/team-agent/src/leader/lease.rs +4 -20
  12. package/crates/team-agent/src/leader/mod.rs +3 -1
  13. package/crates/team-agent/src/leader/owner_bind.rs +46 -48
  14. package/crates/team-agent/src/leader/provider_attribution.rs +214 -0
  15. package/crates/team-agent/src/leader/rediscover/tests.rs +3 -3
  16. package/crates/team-agent/src/leader/rediscover.rs +34 -17
  17. package/crates/team-agent/src/leader/start.rs +279 -4
  18. package/crates/team-agent/src/lifecycle/launch.rs +279 -24
  19. package/crates/team-agent/src/lifecycle/restart/common.rs +50 -40
  20. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +343 -49
  21. package/crates/team-agent/src/lifecycle/restart/selection.rs +9 -6
  22. package/crates/team-agent/src/lifecycle/tests/core.rs +181 -45
  23. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +390 -61
  24. package/crates/team-agent/src/lifecycle/types.rs +28 -0
  25. package/crates/team-agent/src/messaging/delivery.rs +5 -1
  26. package/crates/team-agent/src/messaging/helpers.rs +1 -1
  27. package/crates/team-agent/src/messaging/tests/runtime.rs +14 -0
  28. package/crates/team-agent/src/provider/adapter.rs +6 -3
  29. package/crates/team-agent/src/provider/startup_prompt.rs +173 -0
  30. package/crates/team-agent/src/provider/testdata/copilot-ready-marker.txt +5 -0
  31. package/crates/team-agent/src/provider/testdata/copilot-trust-prompt.txt +19 -0
  32. package/crates/team-agent/src/tmux_backend.rs +5 -0
  33. package/crates/team-agent/src/transport/test_support.rs +22 -7
  34. package/crates/team-agent/src/transport.rs +4 -0
  35. package/package.json +4 -4
@@ -620,6 +620,23 @@ pub enum RestartReport {
620
620
  next_actions: Vec<String>,
621
621
  attach_commands: Vec<String>,
622
622
  },
623
+ /// At least one worker failed during live spawn, but other workers were isolated
624
+ /// and restarted. The CLI reports `status=partial` and exits non-zero.
625
+ Partial {
626
+ session_name: SessionName,
627
+ agents: Vec<RestartedAgent>,
628
+ failed_agents: Vec<RestartFailedAgent>,
629
+ coordinator_started: bool,
630
+ next_actions: Vec<String>,
631
+ attach_commands: Vec<String>,
632
+ },
633
+ /// All workers failed during live spawn. No worker is reported as restarted.
634
+ Failed {
635
+ session_name: SessionName,
636
+ failed_agents: Vec<RestartFailedAgent>,
637
+ next_actions: Vec<String>,
638
+ attach_commands: Vec<String>,
639
+ },
623
640
  /// atomic refusal(`reason=resume_atomicity`):某 interacted worker 不可 resume
624
641
  /// 且非 allow_fresh。**nothing created yet**,无需回滚。
625
642
  RefusedResumeAtomicity {
@@ -652,6 +669,17 @@ pub struct RestartedAgent {
652
669
  pub session_id: Option<SessionId>,
653
670
  }
654
671
 
672
+ /// Worker restart failure isolated from the rest of the restart pass.
673
+ #[derive(Debug, Clone, PartialEq, Eq)]
674
+ pub struct RestartFailedAgent {
675
+ pub agent_id: AgentId,
676
+ pub restart_mode: StartMode,
677
+ pub decision: ResumeDecision,
678
+ pub session_id: Option<SessionId>,
679
+ pub phase: String,
680
+ pub error: String,
681
+ }
682
+
655
683
  /// atomic refusal 里的不可重建 worker(`orchestration.py:517`)。
656
684
  #[derive(Debug, Clone, PartialEq, Eq)]
657
685
  pub struct UnresumableWorker {
@@ -744,7 +744,7 @@ fn recipient_pane_has_actionable_startup_prompt(
744
744
  let provider = agent
745
745
  .and_then(|agent| agent.get("provider"))
746
746
  .and_then(serde_json::Value::as_str);
747
- if !matches!(provider, Some("codex" | "claude" | "claude_code")) {
747
+ if !matches!(provider, Some("codex" | "claude" | "claude_code" | "copilot")) {
748
748
  return false;
749
749
  }
750
750
  // step2-retry/scrollback root-cause (rt binary 6c9c6c1c): once the agent's
@@ -777,6 +777,10 @@ fn recipient_pane_has_actionable_startup_prompt(
777
777
  crate::provider::classify_claude_startup_screen(&captured),
778
778
  crate::provider::StartupScreenDecision::AnswerWorkspaceTrust
779
779
  ),
780
+ Some("copilot") => matches!(
781
+ crate::provider::classify_copilot_startup_screen(&captured),
782
+ crate::provider::StartupScreenDecision::AnswerWorkspaceTrust
783
+ ),
780
784
  _ => false,
781
785
  }
782
786
  }
@@ -123,7 +123,7 @@ pub(crate) fn non_provider_command(command: &str) -> Option<&str> {
123
123
  let base = command.rsplit('/').next().unwrap_or(command);
124
124
  let normalized = base.to_ascii_lowercase();
125
125
  match normalized.as_str() {
126
- "" | "codex" | "claude" | "gemini" | "openai" | "team-agent" => None,
126
+ "" | "codex" | "claude" | "copilot" | "gemini" | "openai" | "team-agent" => None,
127
127
  _ => Some(base),
128
128
  }
129
129
  }
@@ -174,6 +174,20 @@ fn classify_idle_prompt_beats_recent_output_for_just_launched_agent() {
174
174
  );
175
175
  }
176
176
 
177
+ #[test]
178
+ fn classify_copilot_idle_prompt_is_not_masked_by_current_command() {
179
+ let state = json(serde_json::json!({}));
180
+ let a = classify_agent_activity(
181
+ &state,
182
+ " / commands · ? help\n❯ \n",
183
+ false,
184
+ Some("copilot"),
185
+ None,
186
+ );
187
+ assert_eq!(a.status, ActivityStatus::Idle);
188
+ assert_eq!(a.confidence, 0.9);
189
+ }
190
+
177
191
  // ════════════════════════════════════════════════════════════════════════
178
192
  // GROUP H — attempt_trust_auto_answer: own-vs-foreign realpath + fail-safe
179
193
  // pane-width + opt-in gate + reason byte-locks. leader_panes.py:383-470.
@@ -233,6 +233,11 @@ pub trait ProviderAdapter {
233
233
  transport, target, checks, sleep_s,
234
234
  )
235
235
  }
236
+ Provider::Copilot => {
237
+ super::startup_prompt::copilot_handle_startup_prompts(
238
+ transport, target, checks, sleep_s,
239
+ )
240
+ }
236
241
  _ => super::startup_prompt::StartupPromptOutcome::default(),
237
242
  }))
238
243
  .unwrap_or_default()
@@ -869,9 +874,7 @@ impl ProviderAdapter for BasicProviderAdapter {
869
874
  match self.provider {
870
875
  Provider::Claude | Provider::ClaudeCode => patterns(r"[>❯]\s", r"[✶✢✽✻✳·].*…", r"Error|Traceback"),
871
876
  Provider::Codex => patterns(r"(›|❯|codex>)", r"•.*esc to interrupt", r"Error|Traceback|panic"),
872
- // C-3-3 cr verdict: copilot 真值待用户真会话样本(§E3 line 105),一期占位
873
- // 仅 error 行至少能识别;idle/processing 留 Unknown(N11 守,classify→None)。
874
- Provider::Copilot => patterns(r">", r"working|processing", r"Error|panic"),
877
+ Provider::Copilot => patterns(r"(?m)^\s*❯\s*$| / commands · \? help", r"working|processing", r"Error|panic"),
875
878
  Provider::GeminiCli | Provider::Fake => patterns(r">", r"working|processing", r"Error|Traceback"),
876
879
  }
877
880
  }
@@ -28,6 +28,18 @@ const CLAUDE_TRUST_MARKERS: &[&str] = &[
28
28
  "Enter to confirm",
29
29
  ];
30
30
  const CLAUDE_READY_MARKERS: &[&str] = &["Claude Code"];
31
+ pub const COPILOT_TRUST_PROMPT_MARKER: &str =
32
+ include_str!("testdata/copilot-trust-prompt.txt");
33
+ pub const COPILOT_READY_MARKER: &str =
34
+ include_str!("testdata/copilot-ready-marker.txt");
35
+ const COPILOT_TRUST_MARKERS: &[&str] = &[COPILOT_TRUST_PROMPT_MARKER];
36
+ const COPILOT_READY_MARKERS: &[&str] = &[COPILOT_READY_MARKER];
37
+ const COPILOT_TRUST_TITLE: &str = "Confirm folder trust";
38
+ const COPILOT_TRUST_QUESTION: &str = "Do you trust the files in this folder?";
39
+ const COPILOT_TRUST_YES_SESSION: &str = "❯ 1. Yes";
40
+ const COPILOT_TRUST_REMEMBER: &str = "2. Yes, and remember this folder for future sessions";
41
+ const COPILOT_TRUST_NO: &str = "3. No (Esc)";
42
+ const COPILOT_READY_FOOTER: &str = " / commands · ? help";
31
43
  /// Plain ready markers (not the bare `›` glyph — that glyph also indicates a
32
44
  /// numbered-menu selector and is handled by [`rightmost_input_prompt_glyph`] with
33
45
  /// shape gating per N15 / CR-063: detect by SHAPE, not a single Unicode codepoint).
@@ -106,6 +118,20 @@ pub fn classify_claude_startup_screen(output: &str) -> StartupScreenDecision {
106
118
  }
107
119
  }
108
120
 
121
+ pub fn classify_copilot_startup_screen(output: &str) -> StartupScreenDecision {
122
+ let ready_pos = copilot_ready_pos(output);
123
+ if has_active_copilot_trust_shape(output)
124
+ && is_more_recent(copilot_trust_pos(output), ready_pos)
125
+ {
126
+ return StartupScreenDecision::AnswerWorkspaceTrust;
127
+ }
128
+ if ready_pos.is_some() {
129
+ StartupScreenDecision::Ready
130
+ } else {
131
+ StartupScreenDecision::KeepPolling
132
+ }
133
+ }
134
+
109
135
  /// Actionable trust shape (N15): the captured text contains a trust phrase AND a
110
136
  /// numbered-menu selector line `› <digit>. `. This is the modal-still-active signal
111
137
  /// that survives Codex's pre-rendering of the banner/input prompt below the menu.
@@ -161,6 +187,53 @@ fn has_claude_ready_shape(output: &str) -> bool {
161
187
  && rightmost_claude_input_prompt_glyph(output).is_some()
162
188
  }
163
189
 
190
+ fn has_active_copilot_trust_shape(output: &str) -> bool {
191
+ if max_rfind(output, COPILOT_TRUST_MARKERS).is_some() {
192
+ return true;
193
+ }
194
+ [
195
+ COPILOT_TRUST_TITLE,
196
+ COPILOT_TRUST_QUESTION,
197
+ COPILOT_TRUST_YES_SESSION,
198
+ COPILOT_TRUST_REMEMBER,
199
+ COPILOT_TRUST_NO,
200
+ ]
201
+ .iter()
202
+ .all(|marker| output.contains(marker))
203
+ }
204
+
205
+ fn copilot_trust_pos(output: &str) -> Option<usize> {
206
+ max_two(
207
+ max_rfind(output, COPILOT_TRUST_MARKERS),
208
+ max_rfind(output, &[COPILOT_TRUST_TITLE, COPILOT_TRUST_QUESTION]),
209
+ )
210
+ }
211
+
212
+ fn copilot_ready_pos(output: &str) -> Option<usize> {
213
+ let fixture_pos = max_rfind(output, COPILOT_READY_MARKERS);
214
+ let footer_pos = output.rfind(COPILOT_READY_FOOTER);
215
+ let prompt_pos = rightmost_copilot_input_prompt_glyph(output);
216
+ if footer_pos.is_some() && prompt_pos.is_some() {
217
+ max_two(fixture_pos, max_two(footer_pos, prompt_pos))
218
+ } else {
219
+ fixture_pos
220
+ }
221
+ }
222
+
223
+ fn rightmost_copilot_input_prompt_glyph(output: &str) -> Option<usize> {
224
+ let mut best = None;
225
+ let mut offset = 0;
226
+ for line in output.split_inclusive('\n') {
227
+ if line.trim() == "❯" {
228
+ if let Some(idx) = line.find('❯') {
229
+ best = Some(offset + idx);
230
+ }
231
+ }
232
+ offset += line.len();
233
+ }
234
+ best
235
+ }
236
+
164
237
  fn rightmost_claude_input_prompt_glyph(output: &str) -> Option<usize> {
165
238
  let mut best = rightmost_input_prompt_for_glyph(output, '❯');
166
239
  best = max_two(best, rightmost_input_prompt_for_glyph(output, '>'));
@@ -316,6 +389,40 @@ pub fn claude_handle_startup_prompts(
316
389
  StartupPromptOutcome { handled, capture_error }
317
390
  }
318
391
 
392
+ pub fn copilot_handle_startup_prompts(
393
+ transport: &dyn Transport,
394
+ target: &Target,
395
+ checks: usize,
396
+ sleep_s: f64,
397
+ ) -> StartupPromptOutcome {
398
+ let mut handled = Vec::new();
399
+ let mut capture_error: Option<String> = None;
400
+ for _ in 0..checks {
401
+ let screen = match transport.capture(target, CaptureRange::Full) {
402
+ Ok(captured) => captured.text,
403
+ Err(error) => {
404
+ capture_error.get_or_insert_with(|| error.to_string());
405
+ String::new()
406
+ }
407
+ };
408
+ match classify_copilot_startup_screen(&screen) {
409
+ StartupScreenDecision::AnswerWorkspaceTrust => {
410
+ let _ = transport.send_keys(target, &[Key::Enter]);
411
+ handled.push(HandledPrompt {
412
+ prompt: "copilot_workspace_trust".to_string(),
413
+ action: "sent_enter_yes_session".to_string(),
414
+ });
415
+ sleep_between_polls(sleep_s);
416
+ }
417
+ StartupScreenDecision::Ready => break,
418
+ StartupScreenDecision::SkipUpdatePrompt | StartupScreenDecision::KeepPolling => {
419
+ sleep_between_polls(sleep_s);
420
+ }
421
+ }
422
+ }
423
+ StartupPromptOutcome { handled, capture_error }
424
+ }
425
+
319
426
  fn max_rfind(output: &str, needles: &[&str]) -> Option<usize> {
320
427
  needles.iter().filter_map(|needle| output.rfind(needle)).max()
321
428
  }
@@ -445,6 +552,44 @@ mod tests {
445
552
  );
446
553
  }
447
554
 
555
+ #[test]
556
+ fn copilot_trust_fixture_answers_yes_for_this_session() {
557
+ assert_eq!(
558
+ classify_copilot_startup_screen(COPILOT_TRUST_PROMPT_MARKER),
559
+ StartupScreenDecision::AnswerWorkspaceTrust
560
+ );
561
+ assert!(
562
+ COPILOT_TRUST_PROMPT_MARKER.contains(COPILOT_TRUST_YES_SESSION),
563
+ "copilot trust default must remain option 1, not persistent option 2"
564
+ );
565
+ }
566
+
567
+ #[test]
568
+ fn copilot_trust_prompt_tolerates_path_variation() {
569
+ let dir_b = COPILOT_TRUST_PROMPT_MARKER.replace("/dir-a", "/dir-b");
570
+ assert_eq!(
571
+ classify_copilot_startup_screen(&dir_b),
572
+ StartupScreenDecision::AnswerWorkspaceTrust
573
+ );
574
+ }
575
+
576
+ #[test]
577
+ fn copilot_ready_fixture_is_ready_without_trust_prompt() {
578
+ assert_eq!(
579
+ classify_copilot_startup_screen(COPILOT_READY_MARKER),
580
+ StartupScreenDecision::Ready
581
+ );
582
+ }
583
+
584
+ #[test]
585
+ fn copilot_ready_after_stale_trust_wins() {
586
+ let screen = format!("{COPILOT_TRUST_PROMPT_MARKER}\n{COPILOT_READY_MARKER}");
587
+ assert_eq!(
588
+ classify_copilot_startup_screen(&screen),
589
+ StartupScreenDecision::Ready
590
+ );
591
+ }
592
+
448
593
  // ── ④ transport.capture() SEAM — the loop answers trust then breaks on ready, via the seam ───────
449
594
  /// Scripted transport: `capture` pops the next canned screen; `send_keys` records the keys. All
450
595
  /// other methods are unreachable by the startup-prompt loop.
@@ -532,4 +677,32 @@ mod tests {
532
677
  "on workspace-trust the loop must send Enter via the transport.capture() seam; got {sent:?}"
533
678
  );
534
679
  }
680
+
681
+ #[test]
682
+ fn copilot_loop_answers_trust_then_breaks_on_ready_via_capture_seam() {
683
+ let t = ScriptedTransport {
684
+ screens: Mutex::new(vec![
685
+ COPILOT_TRUST_PROMPT_MARKER.to_string(),
686
+ COPILOT_READY_MARKER.to_string(),
687
+ ]),
688
+ sent: Mutex::new(Vec::new()),
689
+ };
690
+ let target = Target::Pane(PaneId::new("%1"));
691
+
692
+ let handled = copilot_handle_startup_prompts(&t, &target, 5, 0.0).handled;
693
+
694
+ assert_eq!(
695
+ handled,
696
+ vec![HandledPrompt {
697
+ prompt: "copilot_workspace_trust".to_string(),
698
+ action: "sent_enter_yes_session".to_string(),
699
+ }]
700
+ );
701
+ let sent = t.sent.lock().unwrap();
702
+ assert_eq!(
703
+ sent.iter().filter(|keys| keys.as_slice() == [Key::Enter]).count(),
704
+ 1,
705
+ "copilot trust prompt must choose option 1 with one Enter; got {sent:?}"
706
+ );
707
+ }
535
708
  }
@@ -0,0 +1,5 @@
1
+ /private/tmp/copilot-trust-fixture-20260611T160323Z/dir-a
2
+ ────────────────────────────────────────────────────────────────────────────────
3
+
4
+ ────────────────────────────────────────────────────────────────────────────────
5
+ / commands · ? help qwen3.6-local
@@ -0,0 +1,19 @@
1
+ ╭──────────────────────────────────────────────────────────────────────────────╮
2
+ │ Confirm folder trust │
3
+ │ ──────────────────────────────────────────────────────────────────────────── │
4
+ │ ╭──────────────────────────────────────────────────────────────────────────╮ │
5
+ │ │ /private/tmp/copilot-trust-fixture-20260611T160323Z/dir-a │ │
6
+ │ ╰──────────────────────────────────────────────────────────────────────────╯ │
7
+ │ │
8
+ │ Copilot can read files in this folder and, with your permission, edit them │
9
+ │ or run code and shell commands. It will remember your permissions for the │
10
+ │ rest of this session. │
11
+ │ │
12
+ │ Do you trust the files in this folder? │
13
+ │ │
14
+ │ ❯ 1. Yes │
15
+ │ 2. Yes, and remember this folder for future sessions │
16
+ │ 3. No (Esc) │
17
+ │ │
18
+ │ ↑/↓ to navigate · enter to select · esc to cancel │
19
+ ╰──────────────────────────────────────────────────────────────────────────────╯
@@ -1041,6 +1041,11 @@ impl Transport for TmuxBackend {
1041
1041
  Ok(SetEnvOutcome::Applied)
1042
1042
  }
1043
1043
 
1044
+ fn kill_server(&self) -> Result<(), TransportError> {
1045
+ TmuxBackend::kill_server(self);
1046
+ Ok(())
1047
+ }
1048
+
1044
1049
  fn kill_session(&self, session: &SessionName) -> Result<(), TransportError> {
1045
1050
  let argv = self.tmux_argv(&[
1046
1051
  "tmux".to_string(),
@@ -25,6 +25,7 @@ struct OfflineState {
25
25
  targets: Vec<PaneInfo>,
26
26
  windows: Vec<WindowName>,
27
27
  pane_presence: BTreeMap<String, bool>,
28
+ spawn_failures: BTreeMap<String, String>,
28
29
  spawned_panes_addressable: bool,
29
30
  liveness: BTreeMap<String, PaneLiveness>,
30
31
  default_liveness: PaneLiveness,
@@ -42,6 +43,7 @@ impl Default for OfflineState {
42
43
  targets: Vec::new(),
43
44
  windows: Vec::new(),
44
45
  pane_presence: BTreeMap::new(),
46
+ spawn_failures: BTreeMap::new(),
45
47
  spawned_panes_addressable: true,
46
48
  liveness: BTreeMap::new(),
47
49
  default_liveness: PaneLiveness::Unknown,
@@ -102,6 +104,13 @@ impl OfflineTransport {
102
104
  self
103
105
  }
104
106
 
107
+ pub fn with_spawn_failure(self, window: impl Into<String>, error: impl Into<String>) -> Self {
108
+ self.with_state(|state| {
109
+ state.spawn_failures.insert(window.into(), error.into());
110
+ });
111
+ self
112
+ }
113
+
105
114
  pub fn with_spawned_panes_addressable(self, present: bool) -> Self {
106
115
  self.with_state(|state| state.spawned_panes_addressable = present);
107
116
  self
@@ -149,10 +158,16 @@ impl OfflineTransport {
149
158
  session: &SessionName,
150
159
  window: &WindowName,
151
160
  argv: &[String],
152
- ) -> SpawnResult {
161
+ ) -> Result<SpawnResult, TransportError> {
153
162
  let pane_index = self.with_state(|state| {
154
163
  state.calls.push(kind);
155
164
  state.spawns.push(SpawnRecord { kind: kind.to_string(), argv: argv.to_vec() });
165
+ if let Some(error) = state.spawn_failures.get(window.as_str()) {
166
+ return Err(TransportError::Spawn {
167
+ backend: BackendKind::Tmux,
168
+ source: std::io::Error::other(error.clone()),
169
+ });
170
+ }
156
171
  if kind == "spawn_first" && !state.session_absent_after_spawn_first {
157
172
  state.session_present = true;
158
173
  }
@@ -160,14 +175,14 @@ impl OfflineTransport {
160
175
  state
161
176
  .pane_presence
162
177
  .insert(format!("%{pane_index}"), state.spawned_panes_addressable);
163
- pane_index
164
- });
165
- SpawnResult {
178
+ Ok(pane_index)
179
+ })?;
180
+ Ok(SpawnResult {
166
181
  pane_id: PaneId::new(format!("%{pane_index}")),
167
182
  session: session.clone(),
168
183
  window: window.clone(),
169
184
  child_pid: None,
170
- }
185
+ })
171
186
  }
172
187
 
173
188
  fn inject_report() -> InjectReport {
@@ -194,7 +209,7 @@ impl Transport for OfflineTransport {
194
209
  _cwd: &Path,
195
210
  _env: &BTreeMap<String, String>,
196
211
  ) -> Result<SpawnResult, TransportError> {
197
- Ok(self.spawn_result("spawn_first", session, window, argv))
212
+ self.spawn_result("spawn_first", session, window, argv)
198
213
  }
199
214
 
200
215
  fn spawn_into(
@@ -205,7 +220,7 @@ impl Transport for OfflineTransport {
205
220
  _cwd: &Path,
206
221
  _env: &BTreeMap<String, String>,
207
222
  ) -> Result<SpawnResult, TransportError> {
208
- Ok(self.spawn_result("spawn_into", session, window, argv))
223
+ self.spawn_result("spawn_into", session, window, argv)
209
224
  }
210
225
 
211
226
  fn inject(
@@ -519,6 +519,10 @@ pub trait Transport: Send + Sync {
519
519
 
520
520
  // —— LIFECYCLE(SL)——
521
521
 
522
+ fn kill_server(&self) -> Result<(), TransportError> {
523
+ Ok(())
524
+ }
525
+
522
526
  fn kill_session(&self, session: &SessionName) -> Result<(), TransportError>;
523
527
 
524
528
  fn kill_window(&self, target: &Target) -> Result<(), TransportError>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-agent/installer",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
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.3.12",
24
- "@team-agent/cli-darwin-x64": "0.3.12",
25
- "@team-agent/cli-linux-x64": "0.3.12"
23
+ "@team-agent/cli-darwin-arm64": "0.3.14",
24
+ "@team-agent/cli-darwin-x64": "0.3.14",
25
+ "@team-agent/cli-linux-x64": "0.3.14"
26
26
  },
27
27
  "scripts": {
28
28
  "postinstall": "node npm/bincheck.mjs",