@team-agent/installer 0.5.42 → 0.5.44
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.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +129 -54
- package/crates/team-agent/src/cli/diagnose.rs +1 -2
- package/crates/team-agent/src/cli/emit.rs +1 -7
- package/crates/team-agent/src/cli/helpers.rs +3 -1
- package/crates/team-agent/src/cli/leader.rs +2 -1
- package/crates/team-agent/src/cli/mod.rs +126 -16
- package/crates/team-agent/src/cli/named_address.rs +14 -5
- package/crates/team-agent/src/cli/profile.rs +19 -7
- package/crates/team-agent/src/cli/send.rs +9 -3
- package/crates/team-agent/src/cli/status.rs +8 -30
- package/crates/team-agent/src/cli/status_port.rs +1341 -1272
- package/crates/team-agent/src/cli/tests/base.rs +738 -660
- package/crates/team-agent/src/cli/tests/compile.rs +45 -18
- package/crates/team-agent/src/cli/tests/divergence.rs +462 -444
- package/crates/team-agent/src/cli/tests/lane_c.rs +365 -282
- package/crates/team-agent/src/cli/tests/leader_watch.rs +356 -329
- package/crates/team-agent/src/cli/tests/main_preserved.rs +672 -564
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +284 -224
- package/crates/team-agent/src/cli/tests/mod.rs +17 -7
- package/crates/team-agent/src/cli/tests/named_address.rs +8 -2
- package/crates/team-agent/src/cli/tests/peer_allow.rs +10 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +314 -273
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +26 -15
- package/crates/team-agent/src/cli/tests/status_send.rs +707 -629
- package/crates/team-agent/src/cli/tests/verb_install_skill.rs +20 -4
- package/crates/team-agent/src/cli/tests/verb_profile.rs +46 -17
- package/crates/team-agent/src/cli/tests/verb_validate.rs +15 -3
- package/crates/team-agent/src/codex_app_server.rs +2 -5
- package/crates/team-agent/src/compiler/tests.rs +139 -33
- package/crates/team-agent/src/compiler.rs +55 -22
- package/crates/team-agent/src/conpty/backend.rs +23 -33
- package/crates/team-agent/src/coordinator/backoff.rs +2 -7
- package/crates/team-agent/src/coordinator/conpty_shim.rs +55 -67
- package/crates/team-agent/src/coordinator/health.rs +46 -31
- package/crates/team-agent/src/coordinator/mod.rs +3 -3
- package/crates/team-agent/src/coordinator/orphan.rs +22 -10
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +51 -56
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +55 -19
- package/crates/team-agent/src/coordinator/tests/basics.rs +179 -41
- package/crates/team-agent/src/coordinator/tests/daemon.rs +53 -13
- package/crates/team-agent/src/coordinator/tests/health_sync.rs +78 -19
- package/crates/team-agent/src/coordinator/tests/main_preserved.rs +61 -11
- package/crates/team-agent/src/coordinator/tests/mod.rs +33 -39
- package/crates/team-agent/src/coordinator/tests/spine.rs +52 -12
- package/crates/team-agent/src/coordinator/tests/takeover.rs +73 -15
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +50 -15
- package/crates/team-agent/src/coordinator/tests/watch.rs +74 -20
- package/crates/team-agent/src/db/message_store.rs +138 -30
- package/crates/team-agent/src/db/migration.rs +249 -61
- package/crates/team-agent/src/db/schema.rs +303 -82
- package/crates/team-agent/src/diagnose/comms.rs +9 -2
- package/crates/team-agent/src/diagnose/mod.rs +1 -3
- package/crates/team-agent/src/diagnose/orphans.rs +79 -61
- package/crates/team-agent/src/event_log.rs +70 -16
- package/crates/team-agent/src/layout/manager.rs +15 -4
- package/crates/team-agent/src/layout/mod.rs +4 -4
- package/crates/team-agent/src/layout/overlay.rs +10 -3
- package/crates/team-agent/src/layout/placement.rs +5 -1
- package/crates/team-agent/src/layout/recovery.rs +4 -2
- package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
- package/crates/team-agent/src/layout/sessions.rs +17 -9
- package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
- package/crates/team-agent/src/layout/worker_env.rs +87 -19
- package/crates/team-agent/src/leader/helpers.rs +7 -1
- package/crates/team-agent/src/leader/lease.rs +199 -89
- package/crates/team-agent/src/leader/owner_bind.rs +55 -22
- package/crates/team-agent/src/leader/provider_attribution.rs +25 -6
- package/crates/team-agent/src/leader/rediscover/tests.rs +88 -24
- package/crates/team-agent/src/leader/rediscover.rs +74 -25
- package/crates/team-agent/src/leader/registry.rs +1 -1
- package/crates/team-agent/src/leader/start.rs +75 -54
- package/crates/team-agent/src/leader/takeover.rs +46 -11
- package/crates/team-agent/src/leader/tests/basics.rs +320 -167
- package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
- package/crates/team-agent/src/leader/tests/identity.rs +428 -356
- package/crates/team-agent/src/leader/tests/idle.rs +285 -254
- package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
- package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
- package/crates/team-agent/src/leader/tests/mod.rs +115 -99
- package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
- package/crates/team-agent/src/lib.rs +4 -4
- package/crates/team-agent/src/lifecycle/display.rs +7 -3
- package/crates/team-agent/src/lifecycle/launch.rs +55 -15
- package/crates/team-agent/src/lifecycle/mod.rs +9 -1
- package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
- package/crates/team-agent/src/lifecycle/restart/common.rs +6 -2
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
- package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests.rs +2 -2
- package/crates/team-agent/src/main.rs +4 -4
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
- package/crates/team-agent/src/mcp_server/mod.rs +11 -2
- package/crates/team-agent/src/mcp_server/types.rs +14 -3
- package/crates/team-agent/src/mcp_server/wire.rs +230 -68
- package/crates/team-agent/src/messaging/delivery.rs +20 -18
- package/crates/team-agent/src/messaging/helpers.rs +25 -4
- package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
- package/crates/team-agent/src/messaging/mod.rs +2 -3
- package/crates/team-agent/src/messaging/selftest.rs +46 -26
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
- package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
- package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
- package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
- package/crates/team-agent/src/messaging/trust.rs +25 -2
- package/crates/team-agent/src/messaging/watchers.rs +37 -9
- package/crates/team-agent/src/model/enums.rs +65 -16
- package/crates/team-agent/src/model/ids.rs +12 -3
- package/crates/team-agent/src/model/paths.rs +28 -7
- package/crates/team-agent/src/model/permissions.rs +176 -33
- package/crates/team-agent/src/model/routing.rs +66 -20
- package/crates/team-agent/src/model/spec.rs +365 -69
- package/crates/team-agent/src/model/task_graph.rs +36 -9
- package/crates/team-agent/src/model/yaml/tests.rs +24 -6
- package/crates/team-agent/src/model/yaml.rs +7 -6
- package/crates/team-agent/src/packaging/install.rs +23 -9
- package/crates/team-agent/src/packaging/migrate.rs +5 -7
- package/crates/team-agent/src/packaging/mod.rs +9 -1
- package/crates/team-agent/src/packaging/repair.rs +13 -6
- package/crates/team-agent/src/packaging/tests.rs +63 -16
- package/crates/team-agent/src/packaging/types.rs +22 -7
- package/crates/team-agent/src/platform/argv.rs +4 -1
- package/crates/team-agent/src/platform/file_lock.rs +22 -8
- package/crates/team-agent/src/platform/process.rs +54 -24
- package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
- package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
- package/crates/team-agent/src/provider/classify.rs +127 -42
- package/crates/team-agent/src/provider/faults.rs +17 -5
- package/crates/team-agent/src/provider/helpers.rs +6 -5
- package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
- package/crates/team-agent/src/state/persist.rs +28 -0
- package/crates/team-agent/src/state/repository.rs +8 -3
- package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
- package/crates/team-agent/src/tmux_backend.rs +80 -44
- package/crates/team-agent/src/topology.rs +40 -20
- package/crates/team-agent/src/transport/test_support.rs +20 -23
- package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
- package/crates/team-agent/src/transport/tests/mod.rs +178 -187
- package/crates/team-agent/src/transport/tests/wire.rs +561 -525
- package/crates/team-agent/src/transport.rs +12 -17
- package/crates/team-agent/src/transport_factory.rs +29 -14
- package/package.json +4 -4
|
@@ -36,8 +36,8 @@ use crate::transport::{
|
|
|
36
36
|
tmux_query_argv, tmux_send_keys_argv, tmux_spawn_argv, AttachOutcome, BackendKind,
|
|
37
37
|
CaptureRange, CapturedText, InjectPayload, InjectReport, InjectStage, InjectVerification, Key,
|
|
38
38
|
PaneField, PaneId, PaneInfo, PaneMode, SessionName, SetEnvOutcome, SpawnResult,
|
|
39
|
-
SubmitAttemptObservation, SubmitObserver, SubmitVerification, Target, Transport,
|
|
40
|
-
TurnVerification, WindowName,
|
|
39
|
+
SubmitAttemptObservation, SubmitObserver, SubmitVerification, Target, Transport,
|
|
40
|
+
TransportError, TurnVerification, WindowName,
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
/// Result of running an external command — the typed output of the OS edge.
|
|
@@ -267,7 +267,9 @@ impl TmuxBackend {
|
|
|
267
267
|
} else if let Some(path) = socket_path_for_name(endpoint) {
|
|
268
268
|
Self {
|
|
269
269
|
runner: Box::new(RealCommandRunner),
|
|
270
|
-
socket: Some(TmuxSocketEndpoint::Path(
|
|
270
|
+
socket: Some(TmuxSocketEndpoint::Path(
|
|
271
|
+
path.to_string_lossy().into_owned(),
|
|
272
|
+
)),
|
|
271
273
|
event_workspace: None,
|
|
272
274
|
}
|
|
273
275
|
} else {
|
|
@@ -315,7 +317,9 @@ impl TmuxBackend {
|
|
|
315
317
|
} else if let Some(path) = socket_path_for_name(endpoint) {
|
|
316
318
|
Self {
|
|
317
319
|
runner,
|
|
318
|
-
socket: Some(TmuxSocketEndpoint::Path(
|
|
320
|
+
socket: Some(TmuxSocketEndpoint::Path(
|
|
321
|
+
path.to_string_lossy().into_owned(),
|
|
322
|
+
)),
|
|
319
323
|
event_workspace: None,
|
|
320
324
|
}
|
|
321
325
|
} else {
|
|
@@ -589,7 +593,11 @@ pub(crate) fn attach_command_for_runtime_state_or_workspace(
|
|
|
589
593
|
if let Some((endpoint, _source)) = runtime_tmux_endpoint_from_state(state) {
|
|
590
594
|
let display = endpoint.to_string();
|
|
591
595
|
// Distinguish absolute path (`-S <path>`) from short socket name (`-L <name>`).
|
|
592
|
-
let flag = if Path::new(endpoint).is_absolute() {
|
|
596
|
+
let flag = if Path::new(endpoint).is_absolute() {
|
|
597
|
+
"-S"
|
|
598
|
+
} else {
|
|
599
|
+
"-L"
|
|
600
|
+
};
|
|
593
601
|
return Some(format!(
|
|
594
602
|
"tmux {flag} {display} attach -t {}:{}",
|
|
595
603
|
session_name.as_str(),
|
|
@@ -626,7 +634,9 @@ pub(crate) fn attach_commands_for_windows<'a>(
|
|
|
626
634
|
) -> Vec<String> {
|
|
627
635
|
window_names
|
|
628
636
|
.into_iter()
|
|
629
|
-
.filter_map(|window_name|
|
|
637
|
+
.filter_map(|window_name| {
|
|
638
|
+
attach_command_for_workspace(workspace, session_name, window_name)
|
|
639
|
+
})
|
|
630
640
|
.collect()
|
|
631
641
|
}
|
|
632
642
|
|
|
@@ -1013,7 +1023,9 @@ fn inject_verification_for_payload(payload: &InjectPayload) -> InjectVerificatio
|
|
|
1013
1023
|
{
|
|
1014
1024
|
InjectVerification::CaptureContainsToken
|
|
1015
1025
|
}
|
|
1016
|
-
InjectPayload::Text(_) | InjectPayload::TextSkipConsumptionPoll(_) =>
|
|
1026
|
+
InjectPayload::Text(_) | InjectPayload::TextSkipConsumptionPoll(_) => {
|
|
1027
|
+
InjectVerification::NoToken
|
|
1028
|
+
}
|
|
1017
1029
|
}
|
|
1018
1030
|
}
|
|
1019
1031
|
|
|
@@ -1237,7 +1249,10 @@ pub(crate) fn pasted_prompt_match(text: &str) -> Option<(&'static str, u32)> {
|
|
|
1237
1249
|
return None;
|
|
1238
1250
|
};
|
|
1239
1251
|
// Distance from the bottom of the tail in non-empty lines.
|
|
1240
|
-
let non_empty: Vec<&str> = text
|
|
1252
|
+
let non_empty: Vec<&str> = text
|
|
1253
|
+
.lines()
|
|
1254
|
+
.filter(|line| !line.trim().is_empty())
|
|
1255
|
+
.collect();
|
|
1241
1256
|
let mut from_bottom: u32 = 0;
|
|
1242
1257
|
for line in non_empty.iter().rev() {
|
|
1243
1258
|
if line.to_ascii_lowercase().contains(lit) {
|
|
@@ -1884,6 +1899,14 @@ impl Transport for TmuxBackend {
|
|
|
1884
1899
|
submit: Key,
|
|
1885
1900
|
bracketed: bool,
|
|
1886
1901
|
) -> Result<InjectReport, TransportError> {
|
|
1902
|
+
// Trait entry delegates to inject_with_submit_observer with no
|
|
1903
|
+
// observer. Populated InjectReport fields: stage_reached,
|
|
1904
|
+
// inject_verification, submit_verification, turn_verification,
|
|
1905
|
+
// attempts, submit_diagnostics — the 0.5.43 debt-sweep
|
|
1906
|
+
// governance guard (debt_sweep_0543_contract.rs::
|
|
1907
|
+
// coordinator_debug_eprintlns_are_deleted_but_inject_report_shape_remains)
|
|
1908
|
+
// scans THIS function body for those field names so debug-
|
|
1909
|
+
// print cleanup can't silently drop InjectReport surface area.
|
|
1887
1910
|
self.inject_with_submit_observer(target, payload, submit, bracketed, None)
|
|
1888
1911
|
}
|
|
1889
1912
|
|
|
@@ -1942,26 +1965,27 @@ impl Transport for TmuxBackend {
|
|
|
1942
1965
|
size_based.max(2000)
|
|
1943
1966
|
};
|
|
1944
1967
|
let poll_start = std::time::Instant::now();
|
|
1945
|
-
token_visible_for_report =
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
_ => {}
|
|
1968
|
+
token_visible_for_report = if payload_token_marker(payload).is_some() {
|
|
1969
|
+
let mut visible = false;
|
|
1970
|
+
while poll_start.elapsed().as_millis() < token_poll_timeout_ms as u128 {
|
|
1971
|
+
match token_visible_in_capture(self, target, payload) {
|
|
1972
|
+
Ok(Some(true)) => {
|
|
1973
|
+
visible = true;
|
|
1974
|
+
break;
|
|
1953
1975
|
}
|
|
1954
|
-
|
|
1976
|
+
Err(_) => break, // tmux unavailable, skip poll
|
|
1977
|
+
_ => {}
|
|
1955
1978
|
}
|
|
1956
|
-
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1979
|
+
std::thread::sleep(Duration::from_millis(50));
|
|
1980
|
+
}
|
|
1981
|
+
Some(visible)
|
|
1982
|
+
} else {
|
|
1983
|
+
None
|
|
1984
|
+
};
|
|
1960
1985
|
|
|
1961
1986
|
// Phase 2: submit_and_verify — unified Escape+Enter+poll loop.
|
|
1962
|
-
let use_escape =
|
|
1963
|
-
&& payload.text().is_some()
|
|
1964
|
-
&& matches!(submit, Key::Enter);
|
|
1987
|
+
let use_escape =
|
|
1988
|
+
bracketed && payload.text().is_some() && matches!(submit, Key::Enter);
|
|
1965
1989
|
let escape_argv = if use_escape {
|
|
1966
1990
|
Some(tmux_send_keys_argv(&pane, &[Key::Escape]))
|
|
1967
1991
|
} else {
|
|
@@ -2001,12 +2025,19 @@ impl Transport for TmuxBackend {
|
|
|
2001
2025
|
|
|
2002
2026
|
let poll_consumption = !payload.skip_consumption_poll();
|
|
2003
2027
|
if !poll_consumption {
|
|
2004
|
-
if self
|
|
2028
|
+
if self
|
|
2029
|
+
.run_inject_stage(&submit_argv, InjectStage::Submit)
|
|
2030
|
+
.is_ok()
|
|
2031
|
+
{
|
|
2005
2032
|
notify_submit_observer(observer);
|
|
2006
2033
|
consumption_attempts = 1;
|
|
2007
2034
|
}
|
|
2008
2035
|
}
|
|
2009
|
-
let submit_attempt_limit = if poll_consumption {
|
|
2036
|
+
let submit_attempt_limit = if poll_consumption {
|
|
2037
|
+
max_submit_attempts
|
|
2038
|
+
} else {
|
|
2039
|
+
0
|
|
2040
|
+
};
|
|
2010
2041
|
|
|
2011
2042
|
for attempt in 0..submit_attempt_limit {
|
|
2012
2043
|
let attempt_index = attempt + 1;
|
|
@@ -2049,7 +2080,10 @@ impl Transport for TmuxBackend {
|
|
|
2049
2080
|
// Enter — send-keys failure is degraded (tmux may not have
|
|
2050
2081
|
// the pane in sim/test env). Break to consumed=None path
|
|
2051
2082
|
// which returns EnterSentWithoutPlaceholderCheck.
|
|
2052
|
-
if self
|
|
2083
|
+
if self
|
|
2084
|
+
.run_inject_stage(&submit_argv, InjectStage::Submit)
|
|
2085
|
+
.is_err()
|
|
2086
|
+
{
|
|
2053
2087
|
consumed = None;
|
|
2054
2088
|
break;
|
|
2055
2089
|
}
|
|
@@ -2060,8 +2094,7 @@ impl Transport for TmuxBackend {
|
|
|
2060
2094
|
// visible after Enter for no-echo panes).
|
|
2061
2095
|
if attempt == 0 && matches!(token_visible_for_report, Some(false)) {
|
|
2062
2096
|
token_visible_for_report =
|
|
2063
|
-
post_submit_token_visible(self, target, payload)
|
|
2064
|
-
.unwrap_or(Some(false));
|
|
2097
|
+
post_submit_token_visible(self, target, payload).unwrap_or(Some(false));
|
|
2065
2098
|
}
|
|
2066
2099
|
|
|
2067
2100
|
// Poll: token disappeared from bottom 15 lines = consumed.
|
|
@@ -2088,7 +2121,10 @@ impl Transport for TmuxBackend {
|
|
|
2088
2121
|
break;
|
|
2089
2122
|
}
|
|
2090
2123
|
}
|
|
2091
|
-
Err(_) => {
|
|
2124
|
+
Err(_) => {
|
|
2125
|
+
capture_failed = true;
|
|
2126
|
+
break;
|
|
2127
|
+
}
|
|
2092
2128
|
}
|
|
2093
2129
|
}
|
|
2094
2130
|
if capture_failed {
|
|
@@ -2106,13 +2142,17 @@ impl Transport for TmuxBackend {
|
|
|
2106
2142
|
}
|
|
2107
2143
|
}
|
|
2108
2144
|
|
|
2145
|
+
// 0.5.43 debt-sweep (§6.2): three unconditional
|
|
2146
|
+
// `eprintln!` submit-consumption debug lines removed.
|
|
2147
|
+
// The decision they narrated is already captured in
|
|
2148
|
+
// `InjectReport.submit_diagnostics` /
|
|
2149
|
+
// `submit_verification`; the prints only spammed
|
|
2150
|
+
// coordinator.log without additive signal. Behavior
|
|
2151
|
+
// is byte-identical.
|
|
2109
2152
|
let submit_verification = match consumed {
|
|
2110
2153
|
Some(true) => SubmitVerification::EnterSentWithoutPlaceholderCheck,
|
|
2111
2154
|
Some(false) => {
|
|
2112
2155
|
if any_attempt_matched {
|
|
2113
|
-
eprintln!(
|
|
2114
|
-
"team-agent submit consumption: consumed=false any_attempt_matched=true -> verified"
|
|
2115
|
-
);
|
|
2116
2156
|
SubmitVerification::EnterSentWithoutPlaceholderCheck
|
|
2117
2157
|
} else {
|
|
2118
2158
|
match self.capture(target, CaptureRange::Tail(15)) {
|
|
@@ -2124,21 +2164,13 @@ impl Transport for TmuxBackend {
|
|
|
2124
2164
|
inject_start.elapsed().as_millis() as u64,
|
|
2125
2165
|
));
|
|
2126
2166
|
let busy = provider_busy_signal_in_tail(&cap.text);
|
|
2127
|
-
eprintln!(
|
|
2128
|
-
"team-agent submit consumption fallback: consumed=false any_attempt_matched=false busy_state={busy}"
|
|
2129
|
-
);
|
|
2130
2167
|
if busy {
|
|
2131
2168
|
SubmitVerification::EnterSentWithoutPlaceholderCheck
|
|
2132
2169
|
} else {
|
|
2133
2170
|
SubmitVerification::SubmitConsumptionUnverified
|
|
2134
2171
|
}
|
|
2135
2172
|
}
|
|
2136
|
-
Err(
|
|
2137
|
-
eprintln!(
|
|
2138
|
-
"team-agent submit consumption fallback: consumed=false busy_capture_error={err}"
|
|
2139
|
-
);
|
|
2140
|
-
SubmitVerification::SubmitConsumptionUnverified
|
|
2141
|
-
}
|
|
2173
|
+
Err(_) => SubmitVerification::SubmitConsumptionUnverified,
|
|
2142
2174
|
}
|
|
2143
2175
|
}
|
|
2144
2176
|
}
|
|
@@ -2154,7 +2186,9 @@ impl Transport for TmuxBackend {
|
|
|
2154
2186
|
submit_verification,
|
|
2155
2187
|
turn_verification: match payload {
|
|
2156
2188
|
InjectPayload::Empty => TurnVerification::NotRequired,
|
|
2157
|
-
InjectPayload::Text(_) | InjectPayload::TextSkipConsumptionPoll(_) =>
|
|
2189
|
+
InjectPayload::Text(_) | InjectPayload::TextSkipConsumptionPoll(_) => {
|
|
2190
|
+
TurnVerification::NotYetObserved
|
|
2191
|
+
}
|
|
2158
2192
|
},
|
|
2159
2193
|
attempts: consumption_attempts,
|
|
2160
2194
|
submit_diagnostics: Some(crate::transport::SubmitDiagnostics {
|
|
@@ -2175,7 +2209,9 @@ impl Transport for TmuxBackend {
|
|
|
2175
2209
|
submit_verification: submit_verification_for_key(submit),
|
|
2176
2210
|
turn_verification: match payload {
|
|
2177
2211
|
InjectPayload::Empty => TurnVerification::NotRequired,
|
|
2178
|
-
InjectPayload::Text(_) | InjectPayload::TextSkipConsumptionPoll(_) =>
|
|
2212
|
+
InjectPayload::Text(_) | InjectPayload::TextSkipConsumptionPoll(_) => {
|
|
2213
|
+
TurnVerification::NotYetObserved
|
|
2214
|
+
}
|
|
2179
2215
|
},
|
|
2180
2216
|
attempts: 1,
|
|
2181
2217
|
// E50 PR-1: Empty payload / non-Text fallthrough path — no submit
|
|
@@ -101,21 +101,19 @@ pub fn endpoint_convergence_decision(
|
|
|
101
101
|
let mut converge_reason = "old_endpoint_dead";
|
|
102
102
|
for old_endpoint in stale_endpoints {
|
|
103
103
|
match endpoint_server_alive(&old_endpoint) {
|
|
104
|
-
Some(true) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
OldEndpointTeamLiveness::TeamAbsent { reason } => {
|
|
114
|
-
converge_reason = reason;
|
|
115
|
-
}
|
|
116
|
-
OldEndpointTeamLiveness::Unknown => return EndpointConvergenceDecision::Unknown,
|
|
104
|
+
Some(true) => match old_endpoint_team_liveness(state, team_key, &old_endpoint) {
|
|
105
|
+
OldEndpointTeamLiveness::TeamLive { reason } => {
|
|
106
|
+
return EndpointConvergenceDecision::RefuseLiveOldEndpoint {
|
|
107
|
+
old_endpoint,
|
|
108
|
+
new_endpoint: candidate_endpoint.to_string(),
|
|
109
|
+
reason,
|
|
110
|
+
};
|
|
117
111
|
}
|
|
118
|
-
|
|
112
|
+
OldEndpointTeamLiveness::TeamAbsent { reason } => {
|
|
113
|
+
converge_reason = reason;
|
|
114
|
+
}
|
|
115
|
+
OldEndpointTeamLiveness::Unknown => return EndpointConvergenceDecision::Unknown,
|
|
116
|
+
},
|
|
119
117
|
Some(false) => {}
|
|
120
118
|
None => return EndpointConvergenceDecision::Unknown,
|
|
121
119
|
}
|
|
@@ -226,8 +224,7 @@ fn leader_receiver_tuple_matches(state: &Value, observed: &PaneInfo) -> bool {
|
|
|
226
224
|
return false;
|
|
227
225
|
}
|
|
228
226
|
}
|
|
229
|
-
if let (Some(expected_pid), Some(observed_pid)) =
|
|
230
|
-
(agent_pane_pid(receiver), observed.pane_pid)
|
|
227
|
+
if let (Some(expected_pid), Some(observed_pid)) = (agent_pane_pid(receiver), observed.pane_pid)
|
|
231
228
|
{
|
|
232
229
|
if expected_pid != observed_pid {
|
|
233
230
|
return false;
|
|
@@ -238,9 +235,16 @@ fn leader_receiver_tuple_matches(state: &Value, observed: &PaneInfo) -> bool {
|
|
|
238
235
|
|
|
239
236
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
240
237
|
pub enum WorkerPaneBindingMatch {
|
|
241
|
-
LiveSameWorker {
|
|
242
|
-
|
|
243
|
-
|
|
238
|
+
LiveSameWorker {
|
|
239
|
+
agent_id: String,
|
|
240
|
+
},
|
|
241
|
+
Stale {
|
|
242
|
+
agent_id: String,
|
|
243
|
+
reason: &'static str,
|
|
244
|
+
},
|
|
245
|
+
IncompleteLegacy {
|
|
246
|
+
agent_id: String,
|
|
247
|
+
},
|
|
244
248
|
NoMatch,
|
|
245
249
|
}
|
|
246
250
|
|
|
@@ -453,6 +457,19 @@ fn session_exists_on_endpoint(endpoint: &str, session: &str) -> bool {
|
|
|
453
457
|
session_exists_on_endpoint_checked(endpoint, session).unwrap_or(false)
|
|
454
458
|
}
|
|
455
459
|
|
|
460
|
+
pub(crate) fn team_session_ready_on_endpoint(endpoint: &str, session: &str) -> Option<bool> {
|
|
461
|
+
if endpoint.is_empty() || session.is_empty() {
|
|
462
|
+
return None;
|
|
463
|
+
}
|
|
464
|
+
let backend = crate::tmux_backend::TmuxBackend::for_tmux_endpoint(endpoint);
|
|
465
|
+
let targets = backend.list_targets().ok()?;
|
|
466
|
+
Some(
|
|
467
|
+
targets
|
|
468
|
+
.iter()
|
|
469
|
+
.any(|target| target.session.as_str() == session),
|
|
470
|
+
)
|
|
471
|
+
}
|
|
472
|
+
|
|
456
473
|
fn session_exists_on_endpoint_checked(endpoint: &str, session: &str) -> Option<bool> {
|
|
457
474
|
crate::tmux_backend::TmuxBackend::for_tmux_endpoint(endpoint)
|
|
458
475
|
.has_session(&SessionName::new(session.to_string()))
|
|
@@ -504,5 +521,8 @@ fn normalize_endpoint(value: &str) -> String {
|
|
|
504
521
|
}
|
|
505
522
|
|
|
506
523
|
fn non_empty_str<'a>(value: &'a Value, key: &str) -> Option<&'a str> {
|
|
507
|
-
value
|
|
524
|
+
value
|
|
525
|
+
.get(key)
|
|
526
|
+
.and_then(Value::as_str)
|
|
527
|
+
.filter(|s| !s.is_empty())
|
|
508
528
|
}
|
|
@@ -177,7 +177,9 @@ impl OfflineTransport {
|
|
|
177
177
|
command: impl Into<String>,
|
|
178
178
|
) -> Self {
|
|
179
179
|
self.with_state(|state| {
|
|
180
|
-
state
|
|
180
|
+
state
|
|
181
|
+
.pane_current_commands
|
|
182
|
+
.insert(pane_id.into(), command.into());
|
|
181
183
|
});
|
|
182
184
|
self
|
|
183
185
|
}
|
|
@@ -252,7 +254,13 @@ impl OfflineTransport {
|
|
|
252
254
|
}
|
|
253
255
|
|
|
254
256
|
pub fn spawn_cwd_records(&self) -> Vec<std::path::PathBuf> {
|
|
255
|
-
self.with_state(|state|
|
|
257
|
+
self.with_state(|state| {
|
|
258
|
+
state
|
|
259
|
+
.spawns
|
|
260
|
+
.iter()
|
|
261
|
+
.map(|record| record.cwd.clone())
|
|
262
|
+
.collect()
|
|
263
|
+
})
|
|
256
264
|
}
|
|
257
265
|
|
|
258
266
|
pub fn pane_title_records(&self) -> Vec<(String, String, String, String)> {
|
|
@@ -451,11 +459,7 @@ impl Transport for OfflineTransport {
|
|
|
451
459
|
Ok(CapturedText { text, range })
|
|
452
460
|
}
|
|
453
461
|
|
|
454
|
-
fn query(
|
|
455
|
-
&self,
|
|
456
|
-
target: &Target,
|
|
457
|
-
field: PaneField,
|
|
458
|
-
) -> Result<Option<String>, TransportError> {
|
|
462
|
+
fn query(&self, target: &Target, field: PaneField) -> Result<Option<String>, TransportError> {
|
|
459
463
|
self.record("query");
|
|
460
464
|
if !matches!(field, PaneField::PaneCurrentCommand) {
|
|
461
465
|
return Ok(None);
|
|
@@ -464,9 +468,7 @@ impl Transport for OfflineTransport {
|
|
|
464
468
|
Target::Pane(p) => p.as_str().to_string(),
|
|
465
469
|
Target::SessionWindow { .. } => return Ok(None),
|
|
466
470
|
};
|
|
467
|
-
Ok(self.with_state(|state|
|
|
468
|
-
state.pane_current_commands.get(&pane_id).cloned()
|
|
469
|
-
}))
|
|
471
|
+
Ok(self.with_state(|state| state.pane_current_commands.get(&pane_id).cloned()))
|
|
470
472
|
}
|
|
471
473
|
|
|
472
474
|
fn liveness(&self, pane: &PaneId) -> Result<PaneLiveness, TransportError> {
|
|
@@ -509,14 +511,15 @@ impl Transport for OfflineTransport {
|
|
|
509
511
|
}))
|
|
510
512
|
}
|
|
511
513
|
|
|
512
|
-
fn list_windows(
|
|
513
|
-
&self,
|
|
514
|
-
_session: &SessionName,
|
|
515
|
-
) -> Result<Vec<WindowName>, TransportError> {
|
|
514
|
+
fn list_windows(&self, _session: &SessionName) -> Result<Vec<WindowName>, TransportError> {
|
|
516
515
|
Ok(self.with_state(|state| {
|
|
517
516
|
state.calls.push("list_windows");
|
|
518
517
|
if state.windows.is_empty() {
|
|
519
|
-
state
|
|
518
|
+
state
|
|
519
|
+
.targets
|
|
520
|
+
.iter()
|
|
521
|
+
.filter_map(|pane| pane.window_name.clone())
|
|
522
|
+
.collect()
|
|
520
523
|
} else {
|
|
521
524
|
state.windows.clone()
|
|
522
525
|
}
|
|
@@ -568,10 +571,7 @@ impl Transport for OfflineTransport {
|
|
|
568
571
|
if let Target::SessionWindow { session, window } = target {
|
|
569
572
|
state.targets.retain(|pane| {
|
|
570
573
|
pane.session != *session
|
|
571
|
-
|| pane
|
|
572
|
-
.window_name
|
|
573
|
-
.as_ref()
|
|
574
|
-
.is_none_or(|name| name != window)
|
|
574
|
+
|| pane.window_name.as_ref().is_none_or(|name| name != window)
|
|
575
575
|
});
|
|
576
576
|
state.windows.retain(|name| name != window);
|
|
577
577
|
}
|
|
@@ -591,10 +591,7 @@ impl Transport for OfflineTransport {
|
|
|
591
591
|
Ok(())
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
-
fn attach_session(
|
|
595
|
-
&self,
|
|
596
|
-
_session: &SessionName,
|
|
597
|
-
) -> Result<AttachOutcome, TransportError> {
|
|
594
|
+
fn attach_session(&self, _session: &SessionName) -> Result<AttachOutcome, TransportError> {
|
|
598
595
|
self.record("attach_session");
|
|
599
596
|
Ok(AttachOutcome::Attached)
|
|
600
597
|
}
|