@team-agent/installer 0.5.116 → 0.5.118

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.
@@ -73,6 +73,8 @@ use crate::transport::{
73
73
  SubmitVerification, Target, Transport, TransportError, TurnVerification, WindowName,
74
74
  };
75
75
 
76
+ mod native_input;
77
+
76
78
  pub const PANE_BINDING_NONCE_METADATA_KEY: &str = "TEAM_AGENT_PANE_BINDING_NONCE";
77
79
  pub(crate) const KEEPALIVE_WINDOW_NAME: &str = "[team-agent keepalive]";
78
80
  const TMUX_PANE_BINDING_NONCE_OPTION: &str = "@team_agent_pane_binding_nonce";
@@ -80,6 +82,29 @@ const TMUX_SESSION_OWNER_WORKSPACE_OPTION: &str = "@team_agent_owner_workspace";
80
82
  const TMUX_SESSION_OWNER_TEAM_OPTION: &str = "@team_agent_owner_team";
81
83
  const TMUX_SESSION_OWNER_GENERATION_OPTION: &str = "@team_agent_owner_generation";
82
84
 
85
+ /// Shared existing pane nonce generator; transport owns the physical nonce.
86
+ /// Same material/algorithm as the original Leader claim producer, no new dependency.
87
+ pub(crate) fn next_pane_binding_nonce(workspace: &Path, endpoint: &str, pane: &PaneId) -> String {
88
+ use sha2::{Digest, Sha256};
89
+ use std::sync::atomic::{AtomicU64, Ordering};
90
+ use std::time::{SystemTime, UNIX_EPOCH};
91
+
92
+ static COUNTER: AtomicU64 = AtomicU64::new(0);
93
+ let nanos = SystemTime::now()
94
+ .duration_since(UNIX_EPOCH)
95
+ .map_or(0, |duration| duration.as_nanos());
96
+ let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
97
+ let canonical = workspace.canonicalize().unwrap_or_else(|_| workspace.to_path_buf());
98
+ let material = format!(
99
+ "{}\0{endpoint}\0{}\0{}\0{nanos}\0{counter}",
100
+ canonical.display(),
101
+ pane.as_str(),
102
+ std::process::id(),
103
+ );
104
+ let digest = Sha256::digest(material.as_bytes());
105
+ digest[..16].iter().map(|byte| format!("{byte:02x}")).collect()
106
+ }
107
+
83
108
  /// Result of running an external command — the typed output of the OS edge.
84
109
  #[derive(Debug, Clone)]
85
110
  pub struct CommandOutput {
@@ -687,7 +712,7 @@ pub(crate) fn tmux_backend_for_runtime_state_or_workspace(
687
712
  workspace: &Path,
688
713
  state: Option<&serde_json::Value>,
689
714
  ) -> RuntimeTmuxBackendSelection {
690
- let (backend, source) =
715
+ let (mut backend, source) =
691
716
  if let Some((endpoint, source)) = runtime_tmux_endpoint_from_state(state) {
692
717
  (TmuxBackend::for_tmux_endpoint(endpoint), source)
693
718
  } else {
@@ -696,6 +721,8 @@ pub(crate) fn tmux_backend_for_runtime_state_or_workspace(
696
721
  RuntimeTmuxEndpointSource::WorkspaceFallback,
697
722
  )
698
723
  };
724
+ // A persisted endpoint changes addressing, not the owning input-lock scope.
725
+ backend.event_workspace = Some(workspace.to_path_buf());
699
726
  RuntimeTmuxBackendSelection {
700
727
  tmux_endpoint_used: backend.tmux_endpoint(),
701
728
  backend,
@@ -719,7 +746,7 @@ pub(crate) fn tmux_backend_with_runner_for_runtime_state_or_workspace(
719
746
  workspace: &Path,
720
747
  state: Option<&serde_json::Value>,
721
748
  ) -> RuntimeTmuxBackendSelection {
722
- let (backend, source) =
749
+ let (mut backend, source) =
723
750
  if let Some((endpoint, source)) = runtime_tmux_endpoint_from_state(state) {
724
751
  (
725
752
  TmuxBackend::with_runner_for_tmux_endpoint(runner, endpoint),
@@ -731,6 +758,7 @@ pub(crate) fn tmux_backend_with_runner_for_runtime_state_or_workspace(
731
758
  RuntimeTmuxEndpointSource::WorkspaceFallback,
732
759
  )
733
760
  };
761
+ backend.event_workspace = Some(workspace.to_path_buf());
734
762
  RuntimeTmuxBackendSelection {
735
763
  tmux_endpoint_used: backend.tmux_endpoint(),
736
764
  backend,
@@ -3069,6 +3097,10 @@ impl Transport for TmuxBackend {
3069
3097
  true
3070
3098
  }
3071
3099
 
3100
+ fn supports_owned_native_input(&self) -> bool {
3101
+ true
3102
+ }
3103
+
3072
3104
  fn tmux_endpoint(&self) -> Option<String> {
3073
3105
  self.socket
3074
3106
  .as_ref()
@@ -3249,14 +3281,19 @@ impl Transport for TmuxBackend {
3249
3281
  bracketed: bool,
3250
3282
  observer: Option<&dyn SubmitObserver>,
3251
3283
  ) -> Result<InjectReport, TransportError> {
3284
+ if let (Some(request), InjectPayload::Text(text), Key::Enter) =
3285
+ (crate::transport::native_input::current_request(), payload, submit)
3286
+ {
3287
+ return self.inject_native_input(target, text, bracketed, request, observer);
3288
+ }
3252
3289
  let pane = pane_from_target(target);
3253
- let _pane_input = crate::lifecycle::pane_input_lock::acquire_or_proceed(
3290
+ let _pane_input = crate::lifecycle::pane_input_lock::acquire_for_write(
3254
3291
  crate::lifecycle::pane_input_lock::PaneInputLockRequest {
3255
3292
  workspace: self.event_workspace.as_deref(),
3256
3293
  target_key: pane.as_str(),
3257
3294
  operation: "inject",
3258
3295
  },
3259
- );
3296
+ ).map_err(|_| TransportError::NativeInputNotReady { reason: "required_guard_active".into() })?;
3260
3297
  // U1 #7: pane readback signal for the non-pasted-prompt text path.
3261
3298
  let mut token_visible_for_report: Option<bool> = None;
3262
3299
  match payload {
@@ -3811,15 +3848,21 @@ impl Transport for TmuxBackend {
3811
3848
  })
3812
3849
  }
3813
3850
 
3851
+ fn rescue_enter(&self, evidence: &crate::transport::native_input::NativeInputEvidence, authorize: &dyn Fn() -> bool, observer: Option<&dyn SubmitObserver>)
3852
+ -> Result<crate::transport::native_input::EnterRescueOutcome, TransportError>
3853
+ {
3854
+ self.rescue_native_input(evidence, authorize, observer)
3855
+ }
3856
+
3814
3857
  fn send_keys(&self, target: &Target, keys: &[Key]) -> Result<(), TransportError> {
3815
3858
  let pane = pane_from_target(target);
3816
- let _pane_input = crate::lifecycle::pane_input_lock::acquire_or_proceed(
3859
+ let _pane_input = crate::lifecycle::pane_input_lock::acquire_for_write(
3817
3860
  crate::lifecycle::pane_input_lock::PaneInputLockRequest {
3818
3861
  workspace: self.event_workspace.as_deref(),
3819
3862
  target_key: pane.as_str(),
3820
3863
  operation: "send_keys",
3821
3864
  },
3822
- );
3865
+ ).map_err(|_| TransportError::NativeInputNotReady { reason: "required_guard_active".into() })?;
3823
3866
  if keys.contains(&Key::CancelMode) {
3824
3867
  if let Some(mode) = pane_mode_from_raw(self.query(target, PaneField::PaneMode)?) {
3825
3868
  let argv = crate::transport::tmux_cancel_mode_argv(&pane, mode);