@team-agent/installer 0.5.107 → 0.5.109
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 +1 -1
- package/crates/team-agent/src/cli/emit.rs +3 -3
- package/crates/team-agent/src/cli/mod.rs +87 -4
- package/crates/team-agent/src/cli/models.rs +252 -3
- package/crates/team-agent/src/cli/spec.rs +1 -1
- package/crates/team-agent/src/cli/status_port/brief.rs +379 -70
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +184 -0
- package/crates/team-agent/src/cli/tests/status_brief.rs +624 -0
- package/crates/team-agent/src/provider/model_catalog.rs +343 -5
- package/crates/team-agent/src/provider/testdata/cursor-list-models-native.stdout +250 -0
- package/crates/team-agent/src/provider/testdata/grok-models-authenticated.stdout +9 -0
- package/crates/team-agent/src/provider/testdata/grok-models-unauthenticated.stdout +7 -0
- package/crates/team-agent/src/tmux_backend.rs +99 -2
- package/package.json +4 -4
|
@@ -1930,3 +1930,187 @@ fn unit0_sessions_to_kill_leader_prefixed_session_is_always_spared() {
|
|
|
1930
1930
|
),
|
|
1931
1931
|
}
|
|
1932
1932
|
}
|
|
1933
|
+
|
|
1934
|
+
const ISSUE_269_ROOT_PID: u32 = 2_147_000_001;
|
|
1935
|
+
|
|
1936
|
+
std::thread_local! {
|
|
1937
|
+
static ISSUE_269_LIVENESS_CALLS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
|
1938
|
+
static ISSUE_269_FIRST_ALIVE: std::cell::RefCell<Option<std::time::Instant>> = const { std::cell::RefCell::new(None) };
|
|
1939
|
+
static ISSUE_269_DEATH_DELAY: std::cell::Cell<Option<std::time::Duration>> = const { std::cell::Cell::new(None) };
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
struct ShutdownWorkspaceCleanup(PathBuf);
|
|
1943
|
+
|
|
1944
|
+
impl Drop for ShutdownWorkspaceCleanup {
|
|
1945
|
+
fn drop(&mut self) {
|
|
1946
|
+
let _ = std::fs::remove_dir_all(&self.0);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
fn reset_issue_269_probe_state() {
|
|
1951
|
+
ISSUE_269_LIVENESS_CALLS.with(|calls| calls.set(0));
|
|
1952
|
+
ISSUE_269_FIRST_ALIVE.with(|first| *first.borrow_mut() = None);
|
|
1953
|
+
ISSUE_269_DEATH_DELAY.with(|delay| delay.set(None));
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
fn issue_269_liveness_call_count() -> usize {
|
|
1957
|
+
ISSUE_269_LIVENESS_CALLS.with(std::cell::Cell::get)
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
fn issue_269_empty_process_table() -> Result<Vec<String>, String> {
|
|
1961
|
+
Ok(Vec::new())
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
fn issue_269_transient_liveness(pid: u32) -> bool {
|
|
1965
|
+
if pid != ISSUE_269_ROOT_PID {
|
|
1966
|
+
return false;
|
|
1967
|
+
}
|
|
1968
|
+
ISSUE_269_LIVENESS_CALLS.with(|calls| {
|
|
1969
|
+
let call = calls.get();
|
|
1970
|
+
calls.set(call + 1);
|
|
1971
|
+
match call {
|
|
1972
|
+
0 => {
|
|
1973
|
+
ISSUE_269_FIRST_ALIVE.with(|first| *first.borrow_mut() = Some(std::time::Instant::now()));
|
|
1974
|
+
true
|
|
1975
|
+
}
|
|
1976
|
+
1 => {
|
|
1977
|
+
let elapsed = ISSUE_269_FIRST_ALIVE.with(|first| {
|
|
1978
|
+
first.borrow().as_ref().map(std::time::Instant::elapsed)
|
|
1979
|
+
});
|
|
1980
|
+
ISSUE_269_DEATH_DELAY.with(|delay| delay.set(elapsed));
|
|
1981
|
+
false
|
|
1982
|
+
}
|
|
1983
|
+
_ => false,
|
|
1984
|
+
}
|
|
1985
|
+
})
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
fn issue_269_persistent_liveness(pid: u32) -> bool {
|
|
1989
|
+
if pid == ISSUE_269_ROOT_PID {
|
|
1990
|
+
ISSUE_269_LIVENESS_CALLS.with(|calls| calls.set(calls.get() + 1));
|
|
1991
|
+
true
|
|
1992
|
+
} else {
|
|
1993
|
+
false
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
fn issue_269_already_gone_liveness(pid: u32) -> bool {
|
|
1998
|
+
if pid == ISSUE_269_ROOT_PID {
|
|
1999
|
+
ISSUE_269_LIVENESS_CALLS.with(|calls| calls.set(calls.get() + 1));
|
|
2000
|
+
}
|
|
2001
|
+
false
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
fn save_issue_269_root_state(workspace: &Path, session: &str) {
|
|
2005
|
+
crate::state::persist::save_runtime_state(
|
|
2006
|
+
workspace,
|
|
2007
|
+
&json!({
|
|
2008
|
+
"session_name": session,
|
|
2009
|
+
"team_key": session,
|
|
2010
|
+
"generation": session,
|
|
2011
|
+
"agents": {
|
|
2012
|
+
"worker": {
|
|
2013
|
+
"status": "running",
|
|
2014
|
+
"provider": "fake",
|
|
2015
|
+
"window": "worker",
|
|
2016
|
+
"provider_pid": ISSUE_269_ROOT_PID
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}),
|
|
2020
|
+
)
|
|
2021
|
+
.unwrap();
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
fn shutdown_issue_269_with_probes(
|
|
2025
|
+
workspace: &Path,
|
|
2026
|
+
session: &str,
|
|
2027
|
+
liveness: fn(u32) -> bool,
|
|
2028
|
+
) -> serde_json::Value {
|
|
2029
|
+
let transport = CleanShutdownTransport::new().with_owned_session(
|
|
2030
|
+
workspace,
|
|
2031
|
+
session,
|
|
2032
|
+
session,
|
|
2033
|
+
session,
|
|
2034
|
+
);
|
|
2035
|
+
crate::cli::lifecycle_port::with_process_table_test_probe(
|
|
2036
|
+
issue_269_empty_process_table,
|
|
2037
|
+
|| {
|
|
2038
|
+
crate::cli::lifecycle_port::with_pid_liveness_test_probe(liveness, || {
|
|
2039
|
+
crate::cli::lifecycle_port::shutdown_with_transport(
|
|
2040
|
+
workspace,
|
|
2041
|
+
true,
|
|
2042
|
+
None,
|
|
2043
|
+
&transport,
|
|
2044
|
+
)
|
|
2045
|
+
.expect("shutdown should complete")
|
|
2046
|
+
})
|
|
2047
|
+
},
|
|
2048
|
+
)
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
#[test]
|
|
2052
|
+
fn issue269_transient_unmatched_root_converges_inside_grace() {
|
|
2053
|
+
let workspace = tmp_shutdown_workspace("issue269-transient-root");
|
|
2054
|
+
let _workspace_cleanup = ShutdownWorkspaceCleanup(workspace.clone());
|
|
2055
|
+
let session = "team-issue269-transient";
|
|
2056
|
+
save_issue_269_root_state(&workspace, session);
|
|
2057
|
+
reset_issue_269_probe_state();
|
|
2058
|
+
|
|
2059
|
+
let started = std::time::Instant::now();
|
|
2060
|
+
let out = shutdown_issue_269_with_probes(&workspace, session, issue_269_transient_liveness);
|
|
2061
|
+
let elapsed = started.elapsed();
|
|
2062
|
+
let calls = issue_269_liveness_call_count();
|
|
2063
|
+
let death_delay = ISSUE_269_DEATH_DELAY.with(std::cell::Cell::get);
|
|
2064
|
+
|
|
2065
|
+
assert_eq!(
|
|
2066
|
+
out["ok"],
|
|
2067
|
+
json!(true),
|
|
2068
|
+
"an absent final process-table row plus a root that dies during the bounded grace must converge; ppid=0/empty-command is only a conservative placeholder, not a measured identity: {out}"
|
|
2069
|
+
);
|
|
2070
|
+
assert_eq!(out["residuals"]["processes"], json!([]), "{out}");
|
|
2071
|
+
assert!(calls >= 2, "the wait must observe both live and gone states; calls={calls}");
|
|
2072
|
+
assert!(death_delay.is_some_and(|delay| delay <= std::time::Duration::from_millis(500)), "root did not disappear within the bounded grace: {death_delay:?}");
|
|
2073
|
+
assert!(elapsed < std::time::Duration::from_secs(2), "shutdown exceeded the bounded wait: {elapsed:?}");
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
#[test]
|
|
2077
|
+
fn issue269_persistent_unmatched_root_remains_a_residual_after_grace() {
|
|
2078
|
+
let workspace = tmp_shutdown_workspace("issue269-persistent-root");
|
|
2079
|
+
let _workspace_cleanup = ShutdownWorkspaceCleanup(workspace.clone());
|
|
2080
|
+
let session = "team-issue269-persistent";
|
|
2081
|
+
save_issue_269_root_state(&workspace, session);
|
|
2082
|
+
reset_issue_269_probe_state();
|
|
2083
|
+
|
|
2084
|
+
let started = std::time::Instant::now();
|
|
2085
|
+
let out = shutdown_issue_269_with_probes(&workspace, session, issue_269_persistent_liveness);
|
|
2086
|
+
let elapsed = started.elapsed();
|
|
2087
|
+
let calls = issue_269_liveness_call_count();
|
|
2088
|
+
let residuals = out["residuals"]["processes"].as_array().unwrap();
|
|
2089
|
+
|
|
2090
|
+
assert_eq!(out["ok"], json!(false), "a live root must not be filtered by placeholder fields: {out}");
|
|
2091
|
+
assert_eq!(residuals.len(), 1, "{out}");
|
|
2092
|
+
assert_eq!(residuals[0]["pid"], json!(ISSUE_269_ROOT_PID), "{out}");
|
|
2093
|
+
assert_eq!(residuals[0]["ppid"], json!(0), "{out}");
|
|
2094
|
+
assert_eq!(residuals[0]["command"], json!(""), "{out}");
|
|
2095
|
+
assert!((2..=30).contains(&calls), "persistent-root polling must remain bounded; calls={calls}");
|
|
2096
|
+
assert!(elapsed < std::time::Duration::from_secs(2), "shutdown exceeded the bounded wait: {elapsed:?}");
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
#[test]
|
|
2100
|
+
fn issue269_already_gone_root_returns_without_fixed_grace_sleep() {
|
|
2101
|
+
let workspace = tmp_shutdown_workspace("issue269-already-gone-root");
|
|
2102
|
+
let _workspace_cleanup = ShutdownWorkspaceCleanup(workspace.clone());
|
|
2103
|
+
let session = "team-issue269-gone";
|
|
2104
|
+
save_issue_269_root_state(&workspace, session);
|
|
2105
|
+
reset_issue_269_probe_state();
|
|
2106
|
+
|
|
2107
|
+
let started = std::time::Instant::now();
|
|
2108
|
+
let out = shutdown_issue_269_with_probes(&workspace, session, issue_269_already_gone_liveness);
|
|
2109
|
+
let elapsed = started.elapsed();
|
|
2110
|
+
let calls = issue_269_liveness_call_count();
|
|
2111
|
+
|
|
2112
|
+
assert_eq!(out["ok"], json!(true), "{out}");
|
|
2113
|
+
assert_eq!(out["residuals"]["processes"], json!([]), "{out}");
|
|
2114
|
+
assert!(calls >= 2, "the final verification path and wait path must both observe liveness; calls={calls}");
|
|
2115
|
+
assert!(elapsed < std::time::Duration::from_millis(450), "already-gone roots must not incur a fixed grace sleep: {elapsed:?}");
|
|
2116
|
+
}
|