@team-agent/installer 0.5.105 → 0.5.106

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.
@@ -29,6 +29,7 @@ struct OfflineState {
29
29
  target_snapshots: VecDeque<Vec<PaneInfo>>,
30
30
  windows: Vec<WindowName>,
31
31
  pane_presence: BTreeMap<String, bool>,
32
+ dead_panes: BTreeMap<String, bool>,
32
33
  spawn_failures: BTreeMap<String, String>,
33
34
  spawned_panes_addressable: bool,
34
35
  liveness: BTreeMap<String, PaneLiveness>,
@@ -67,6 +68,7 @@ impl Default for OfflineState {
67
68
  target_snapshots: VecDeque::new(),
68
69
  windows: Vec::new(),
69
70
  pane_presence: BTreeMap::new(),
71
+ dead_panes: BTreeMap::new(),
70
72
  spawn_failures: BTreeMap::new(),
71
73
  spawned_panes_addressable: true,
72
74
  liveness: BTreeMap::new(),
@@ -144,6 +146,13 @@ impl OfflineTransport {
144
146
  self
145
147
  }
146
148
 
149
+ pub fn with_pane_dead(self, pane: impl Into<String>, dead: bool) -> Self {
150
+ self.with_state(|state| {
151
+ state.dead_panes.insert(pane.into(), dead);
152
+ });
153
+ self
154
+ }
155
+
147
156
  pub fn with_spawn_failure(self, window: impl Into<String>, error: impl Into<String>) -> Self {
148
157
  self.with_state(|state| {
149
158
  state.spawn_failures.insert(window.into(), error.into());
@@ -523,6 +532,13 @@ impl Transport for OfflineTransport {
523
532
  Ok(self.with_state(|state| state.pane_current_commands.get(&pane_id).cloned()))
524
533
  }
525
534
 
535
+ fn pane_is_dead(&self, pane: &PaneId) -> Result<Option<bool>, TransportError> {
536
+ Ok(self.with_state(|state| {
537
+ state.calls.push("pane_is_dead");
538
+ state.dead_panes.get(pane.as_str()).copied()
539
+ }))
540
+ }
541
+
526
542
  fn liveness(&self, pane: &PaneId) -> Result<PaneLiveness, TransportError> {
527
543
  Ok(self.with_state(|state| {
528
544
  state.calls.push("liveness");
@@ -582,6 +598,25 @@ impl Transport for OfflineTransport {
582
598
  Ok(self.with_state(|state| state.session_owner.clone()))
583
599
  }
584
600
 
601
+ fn set_session_owner_with_generation(
602
+ &self,
603
+ _session: &SessionName,
604
+ workspace: &Path,
605
+ team: &str,
606
+ generation: &str,
607
+ ) -> Result<(), TransportError> {
608
+ self.with_state(|state| {
609
+ state.calls.push("set_session_owner_with_generation");
610
+ state.session_owner = Some(super::SessionOwner {
611
+ workspace: workspace.canonicalize().unwrap_or_else(|_| workspace.to_path_buf())
612
+ .to_string_lossy().to_string(),
613
+ team: team.to_string(),
614
+ generation: generation.to_string(),
615
+ });
616
+ });
617
+ Ok(())
618
+ }
619
+
585
620
  fn list_windows(&self, _session: &SessionName) -> Result<Vec<WindowName>, TransportError> {
586
621
  Ok(self.with_state(|state| {
587
622
  state.calls.push("list_windows");
@@ -366,16 +366,45 @@ fn inject_empty_payload_reports_empty_text_send_keys_and_turn_not_required() {
366
366
  assert_eq!(report.attempts, 1);
367
367
  }
368
368
 
369
+ #[cfg(unix)]
370
+ #[test]
371
+ #[ignore = "requires native tmux 3.5+ and python3; owns an isolated socket/session"]
372
+ fn submit_key_protocol_native_tmux_is_return_in_legacy_and_extended_modes() {
373
+ let fixture = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
374
+ .join("tests/fixtures/submit_key_protocol_probe.py");
375
+ let output = std::process::Command::new("python3")
376
+ .arg(fixture)
377
+ .args([
378
+ "--submit-key",
379
+ crate::transport::tmux_submit_key_name(Key::Enter),
380
+ ])
381
+ .output()
382
+ .expect("run isolated raw-PTY submit diagnostic");
383
+ assert!(
384
+ output.status.success(),
385
+ "production submit key is not Return: stdout={} stderr={}",
386
+ String::from_utf8_lossy(&output.stdout),
387
+ String::from_utf8_lossy(&output.stderr),
388
+ );
389
+ assert!(String::from_utf8_lossy(&output.stdout)
390
+ .contains("ProductionSubmitReturn=verified-three-modes"));
391
+ }
392
+
369
393
  #[test]
370
394
  fn empty_inject_is_single_direct_send_keys_never_buffer() {
371
395
  // 命令构造 golden(STEP-9):空文本禁走 buffer,只发一条 send-keys 提交键
372
396
  // (tmux_io.py:42 —— tmux 拒空 buffer 会卡 trust prompt)。
373
- // golden empty_inject_calls 仅 1 条:send-keys -t %7 C-m(锁定阳性 CR)。
397
+ // Return 与 Ctrl-M 只在 legacy 模式等价;提交必须保持逻辑 Enter。
374
398
  assert_eq!(
375
399
  tmux_empty_inject_argv(&PaneId::new("%7"), Key::Enter),
376
- vec!["tmux", "send-keys", "-t", "%7", "C-m"]
400
+ vec!["tmux", "send-keys", "-t", "%7", "Enter"]
401
+ );
402
+ assert_eq!(
403
+ crate::transport::tmux_send_submit_argv(&PaneId::new("%7"), Key::Enter),
404
+ vec!["tmux", "send-keys", "-t", "%7", "Enter"]
377
405
  );
378
- assert_eq!(crate::transport::tmux_submit_key_name(Key::Enter), "C-m");
406
+ assert_eq!(crate::transport::tmux_submit_key_name(Key::Enter), "Enter");
407
+ assert_eq!(crate::transport::tmux_submit_key_name(Key::Down), "Down");
379
408
  }
380
409
 
381
410
  #[test]
@@ -13,7 +13,7 @@
13
13
  //! - name: tmux_inject_text_argv
14
14
  //! what: 文本注入三步 argv(装 buffer→paste→删 buffer)的唯一构造点
15
15
  //! - name: tmux_send_submit_argv
16
- //! what: 提交键 argv 的唯一构造点,Enter 锁定为 C-m
16
+ //! what: 提交键 argv 的唯一构造点,Enter 保持逻辑 Return 而非 Ctrl-M
17
17
  //! - name: tmux_capture_argv
18
18
  //! what: capture-pane argv 的唯一构造点
19
19
  //! - name: tmux_query_argv
@@ -994,6 +994,12 @@ pub trait Transport: Send + Sync {
994
994
  Ok(None)
995
995
  }
996
996
 
997
+ /// Native dead-pane observation. Only Some(true) proves no live pane
998
+ /// process; a missing/unsupported sample must remain unknown.
999
+ fn pane_is_dead(&self, _pane: &PaneId) -> Result<Option<bool>, TransportError> {
1000
+ Ok(None)
1001
+ }
1002
+
997
1003
  /// Cheap direct pane existence check when a backend can prove it. `Ok(None)`
998
1004
  /// preserves the existing Unknown boundary.
999
1005
  fn has_pane(&self, pane: &PaneId) -> Result<Option<bool>, TransportError> {
@@ -1085,6 +1091,17 @@ pub trait Transport: Send + Sync {
1085
1091
 
1086
1092
  // —— LIFECYCLE(SL)——
1087
1093
 
1094
+ /// Before stopping a seat, preserve its session if removing these targets
1095
+ /// would otherwise destroy it. This is not called by team shutdown/restart.
1096
+ /// Backends whose sessions outlive their panes need no special handling.
1097
+ fn preserve_session_before_stop(
1098
+ &self,
1099
+ _session: &SessionName,
1100
+ _targets: &[Target],
1101
+ ) -> Result<(), TransportError> {
1102
+ Ok(())
1103
+ }
1104
+
1088
1105
  fn kill_server(&self) -> Result<(), TransportError> {
1089
1106
  Ok(())
1090
1107
  }
@@ -1199,21 +1216,18 @@ pub fn tmux_send_keys_argv(pane: &PaneId, keys: &[Key]) -> Vec<String> {
1199
1216
 
1200
1217
  /// 投递提交键的 tmux 拼写。
1201
1218
  ///
1202
- /// ledger.p0enter 锁定对:`C-j`/LF 5/5 不成回合,`C-m`/CR 5/5 成回合。
1203
- /// `send-keys Enter` 在 raw PTY 上也是 `0d`,但提交路径只发锁定阳性名
1204
- /// `C-m`,避免落到 LF 族。菜单 `send_keys([Down, Enter])` 不走这里。
1219
+ /// `Enter` 必须保持逻辑 Return,不能替换成控制组合 `C-m`。
1220
+ /// legacy PTY 中两者都是 CR,但启用 extended keys 后 `C-m` 会成为
1221
+ /// CSI-u / modifyOtherKeys Ctrl-M,Pi 不把它当提交;`Enter` 仍发送 CR。
1205
1222
  /// ---
1206
- /// purpose: 投递提交键的 tmux 拼写:Enter 锁定为 C-m(CR),其余键回落通用翻译
1223
+ /// purpose: 投递提交键复用通用逻辑键翻译,避免协议相关的控制键别名
1207
1224
  /// params:
1208
- /// key: 提交键;只有 Enter 有专门拼写
1209
- /// returns: 提交用键名;非 Enter 与 tmux_key_name 同值(含空串情形)
1225
+ /// key: 逻辑提交键
1226
+ /// returns: 与 tmux_key_name 同值(含空串情形)
1210
1227
  /// boundary: 只管拼写,不管何时提交、提交是否成功;不发送、不验证
1211
1228
  /// ---
1212
1229
  pub fn tmux_submit_key_name(key: Key) -> &'static str {
1213
- match key {
1214
- Key::Enter => "C-m",
1215
- other => tmux_key_name(other),
1216
- }
1230
+ tmux_key_name(key)
1217
1231
  }
1218
1232
 
1219
1233
  /// 投递提交:`send-keys -t <pane> <submit-spelling>`。
@@ -1478,7 +1492,7 @@ pub fn tmux_inject_text_argv(
1478
1492
 
1479
1493
  /// 空文本 inject:纯 `send-keys -t <target> <submit_key>`,**禁** buffer 路径
1480
1494
  /// (tmux 拒空 buffer 会卡 trust prompt;tmux_io.py:42)。
1481
- /// `Key::Enter` 在此译成锁定阳性 `C-m`,不是菜单用的 `Enter`。
1495
+ /// `Key::Enter` 保持逻辑 `Enter`,不会在 extended keys 下误发 Ctrl-M。
1482
1496
  /// ---
1483
1497
  /// purpose: 空载荷注入(唤醒/ping)的 argv:只发提交键,不碰 buffer
1484
1498
  /// params:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-agent/installer",
3
- "version": "0.5.105",
3
+ "version": "0.5.106",
4
4
  "description": "npx installer for Team Agent",
5
5
  "keywords": [
6
6
  "codex",
@@ -20,9 +20,9 @@
20
20
  "team-agent-installer": "npm/install.mjs"
21
21
  },
22
22
  "optionalDependencies": {
23
- "@team-agent/cli-darwin-arm64": "0.5.105",
24
- "@team-agent/cli-darwin-x64": "0.5.105",
25
- "@team-agent/cli-linux-x64": "0.5.105"
23
+ "@team-agent/cli-darwin-arm64": "0.5.106",
24
+ "@team-agent/cli-darwin-x64": "0.5.106",
25
+ "@team-agent/cli-linux-x64": "0.5.106"
26
26
  },
27
27
  "scripts": {
28
28
  "postinstall": "node npm/bincheck.mjs",
@@ -54,7 +54,7 @@ Diagnosis:
54
54
 
55
55
  ```bash
56
56
  team-agent status --workspace "$WORKSPACE" --json --detail
57
- team-agent collect --workspace "$WORKSPACE" --json
57
+ team-agent inbox "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" -n 3 --json
58
58
  ```
59
59
 
60
60
  What to look for:
@@ -167,7 +167,7 @@ Validate:
167
167
 
168
168
  ```bash
169
169
  team-agent status --workspace "$WORKSPACE" --json --detail
170
- team-agent collect --workspace "$WORKSPACE" --json
170
+ team-agent inbox "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" -n 3 --json
171
171
  ```
172
172
 
173
173
  The expected result is `status="restarted"` or an equivalent running projection without duplicate-session errors. Do not treat a partial failure on one worker as permission to reset the whole team.