@team-agent/installer 0.5.2 → 0.5.4
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 +3 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +20 -0
- package/crates/team-agent/src/cli/diagnose.rs +191 -31
- package/crates/team-agent/src/cli/emit.rs +5 -0
- package/crates/team-agent/src/cli/mod.rs +213 -89
- package/crates/team-agent/src/cli/tests/named_address.rs +4 -0
- package/crates/team-agent/src/codex_app_server.rs +62 -1
- package/crates/team-agent/src/conpty/backend.rs +120 -8
- package/crates/team-agent/src/coordinator/backoff.rs +88 -2
- package/crates/team-agent/src/coordinator/conpty_shim.rs +730 -0
- package/crates/team-agent/src/coordinator/health.rs +97 -11
- package/crates/team-agent/src/coordinator/mod.rs +8 -0
- package/crates/team-agent/src/coordinator/tests/daemon.rs +2 -1
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +6 -0
- package/crates/team-agent/src/coordinator/tick.rs +13 -0
- package/crates/team-agent/src/diagnose/orphans.rs +18 -7
- package/crates/team-agent/src/leader/provider_attribution.rs +19 -34
- package/crates/team-agent/src/leader/tests/lease_api.rs +7 -0
- package/crates/team-agent/src/lib.rs +14 -1
- package/crates/team-agent/src/lifecycle/launch.rs +89 -92
- package/crates/team-agent/src/lifecycle/lock.rs +40 -33
- package/crates/team-agent/src/lifecycle/restart/agent.rs +25 -19
- package/crates/team-agent/src/lifecycle/restart/common.rs +53 -2
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +108 -6
- package/crates/team-agent/src/lifecycle/restart/selection.rs +47 -18
- package/crates/team-agent/src/lifecycle/restart.rs +16 -6
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +7 -0
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +287 -53
- package/crates/team-agent/src/lifecycle/tests/restart.rs +144 -5
- package/crates/team-agent/src/lifecycle/types.rs +1 -0
- package/crates/team-agent/src/mcp_server/wire.rs +6 -7
- package/crates/team-agent/src/messaging/tests/runtime.rs +5 -0
- package/crates/team-agent/src/packaging/tests.rs +41 -6
- package/crates/team-agent/src/packaging/types.rs +31 -3
- package/crates/team-agent/src/platform/argv.rs +324 -0
- package/crates/team-agent/src/platform/errors.rs +95 -0
- package/crates/team-agent/src/platform/file_lock.rs +418 -0
- package/crates/team-agent/src/platform/mod.rs +66 -0
- package/crates/team-agent/src/platform/process.rs +555 -0
- package/crates/team-agent/src/provider/adapter.rs +103 -41
- package/crates/team-agent/src/provider/session/capture.rs +328 -66
- package/crates/team-agent/src/provider/session/resume.rs +30 -28
- package/crates/team-agent/src/provider/session_scan/common.rs +117 -1
- package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
- package/crates/team-agent/src/provider/session_scan.rs +1 -0
- package/crates/team-agent/src/state/persist.rs +222 -25
- package/crates/team-agent/src/state/projection.rs +59 -4
- package/crates/team-agent/src/tmux_backend.rs +63 -13
- package/crates/team-agent/src/transport_factory.rs +124 -5
- package/package.json +4 -4
package/Cargo.lock
CHANGED
|
@@ -100,6 +100,7 @@ version = "0.1.0"
|
|
|
100
100
|
dependencies = [
|
|
101
101
|
"serde",
|
|
102
102
|
"serde_json",
|
|
103
|
+
"windows",
|
|
103
104
|
]
|
|
104
105
|
|
|
105
106
|
[[package]]
|
|
@@ -574,7 +575,7 @@ dependencies = [
|
|
|
574
575
|
|
|
575
576
|
[[package]]
|
|
576
577
|
name = "team-agent"
|
|
577
|
-
version = "0.5.
|
|
578
|
+
version = "0.5.4"
|
|
578
579
|
dependencies = [
|
|
579
580
|
"anyhow",
|
|
580
581
|
"chrono",
|
|
@@ -587,6 +588,7 @@ dependencies = [
|
|
|
587
588
|
"serial_test",
|
|
588
589
|
"sha2",
|
|
589
590
|
"thiserror",
|
|
591
|
+
"windows",
|
|
590
592
|
]
|
|
591
593
|
|
|
592
594
|
[[package]]
|
package/Cargo.toml
CHANGED
|
@@ -27,6 +27,26 @@ chrono.workspace = true
|
|
|
27
27
|
libc.workspace = true
|
|
28
28
|
rusqlite.workspace = true # step 3 db(bundled SQLite,静态链接)
|
|
29
29
|
|
|
30
|
+
# 0.5.x Windows portability Batch 2/3: uses the same `windows = "0.61"`
|
|
31
|
+
# crate as `win-conpty-phase0` so the workspace shares a single
|
|
32
|
+
# Windows-API crate + already-locked Cargo.lock version.
|
|
33
|
+
#
|
|
34
|
+
# - Batch 2 (`platform::file_lock`): `Win32_Storage_FileSystem`
|
|
35
|
+
# (`LockFileEx`/`UnlockFileEx`) + `Win32_System_IO::OVERLAPPED`.
|
|
36
|
+
# - Batch 3 (`platform::process`): `Win32_System_Threading`
|
|
37
|
+
# (`OpenProcess`, `TerminateProcess`, `GetExitCodeProcess`,
|
|
38
|
+
# `GetCurrentProcessId`) + `Win32_System_Diagnostics_ToolHelp`
|
|
39
|
+
# (Toolhelp snapshot for parent-pid discovery).
|
|
40
|
+
[target.'cfg(windows)'.dependencies.windows]
|
|
41
|
+
version = "0.61"
|
|
42
|
+
features = [
|
|
43
|
+
"Win32_Foundation",
|
|
44
|
+
"Win32_Storage_FileSystem",
|
|
45
|
+
"Win32_System_IO",
|
|
46
|
+
"Win32_System_Threading",
|
|
47
|
+
"Win32_System_Diagnostics_ToolHelp",
|
|
48
|
+
]
|
|
49
|
+
|
|
30
50
|
[dev-dependencies]
|
|
31
51
|
serial_test = { version = "3", features = ["file_locks"] }
|
|
32
52
|
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
//! diagnose/preflight/wait-ready CLI helpers.
|
|
2
2
|
use super::*;
|
|
3
|
-
use crate::provider::wire::{command_name, provider_wire};
|
|
3
|
+
use crate::provider::wire::{command_name, parse_provider, provider_wire};
|
|
4
4
|
use crate::transport::Transport;
|
|
5
5
|
|
|
6
6
|
pub(crate) fn diagnose_runtime(state: &Value, backend: &dyn Transport) -> (Value, Value) {
|
|
7
7
|
let mut issues = Vec::new();
|
|
8
8
|
let mut repairs = Vec::new();
|
|
9
9
|
|
|
10
|
-
if let Some(session_name) = state
|
|
11
|
-
|
|
10
|
+
if let Some(session_name) = state
|
|
11
|
+
.get("session_name")
|
|
12
|
+
.and_then(Value::as_str)
|
|
13
|
+
.filter(|s| !s.is_empty())
|
|
14
|
+
{
|
|
15
|
+
match backend.has_session(&crate::transport::SessionName::new(
|
|
16
|
+
session_name.to_string(),
|
|
17
|
+
)) {
|
|
12
18
|
Ok(true) => {}
|
|
13
19
|
Ok(false) => {
|
|
14
20
|
issues.push(json!("tmux_session_missing"));
|
|
@@ -69,13 +75,55 @@ pub(crate) fn diagnose_runtime(state: &Value, backend: &dyn Transport) -> (Value
|
|
|
69
75
|
}
|
|
70
76
|
}
|
|
71
77
|
|
|
72
|
-
if let Some(session_name) = state
|
|
73
|
-
|
|
78
|
+
if let Some(session_name) = state
|
|
79
|
+
.get("session_name")
|
|
80
|
+
.and_then(Value::as_str)
|
|
81
|
+
.filter(|s| !s.is_empty())
|
|
82
|
+
{
|
|
83
|
+
if let Ok(windows) = backend.list_windows(&crate::transport::SessionName::new(
|
|
84
|
+
session_name.to_string(),
|
|
85
|
+
)) {
|
|
74
86
|
if let Some(agents) = state.get("agents").and_then(Value::as_object) {
|
|
75
87
|
for (agent_id, agent_state) in agents {
|
|
88
|
+
let provider = agent_state
|
|
89
|
+
.get("provider")
|
|
90
|
+
.and_then(Value::as_str)
|
|
91
|
+
.and_then(parse_provider)
|
|
92
|
+
.unwrap_or(crate::provider::Provider::Codex);
|
|
93
|
+
let rollout_path = agent_state
|
|
94
|
+
.get("rollout_path")
|
|
95
|
+
.and_then(Value::as_str)
|
|
96
|
+
.filter(|path| !path.is_empty())
|
|
97
|
+
.map(crate::provider::RolloutPath::new);
|
|
98
|
+
let identity_probe =
|
|
99
|
+
crate::lifecycle::restart::session_identity_probe_for_agent(
|
|
100
|
+
&crate::model::ids::AgentId::new(agent_id.clone()),
|
|
101
|
+
provider,
|
|
102
|
+
rollout_path.as_ref(),
|
|
103
|
+
);
|
|
104
|
+
if identity_probe.identity_ok == Some(false) {
|
|
105
|
+
let issue = format!("session_identity_mismatch:{agent_id}");
|
|
106
|
+
issues.push(json!(issue));
|
|
107
|
+
repairs.push(json!({
|
|
108
|
+
"issue": format!("session_identity_mismatch:{agent_id}"),
|
|
109
|
+
"action": format!(
|
|
110
|
+
"run `team-agent restart --allow-fresh` to discard the poisoned session tuple for `{agent_id}`"
|
|
111
|
+
),
|
|
112
|
+
"expected_agent_id": agent_id,
|
|
113
|
+
"embedded_agent_id": identity_probe.embedded_agent_id,
|
|
114
|
+
"rollout_path": identity_probe
|
|
115
|
+
.rollout_path
|
|
116
|
+
.map(|path| path.to_string_lossy().to_string()),
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
76
119
|
let window = ["window", "window_name"]
|
|
77
120
|
.iter()
|
|
78
|
-
.find_map(|key|
|
|
121
|
+
.find_map(|key| {
|
|
122
|
+
agent_state
|
|
123
|
+
.get(*key)
|
|
124
|
+
.and_then(Value::as_str)
|
|
125
|
+
.filter(|s| !s.is_empty())
|
|
126
|
+
})
|
|
79
127
|
.unwrap_or(agent_id);
|
|
80
128
|
if !windows.iter().any(|w| w.as_str() == window) {
|
|
81
129
|
issues.push(json!(format!("worker_window_missing:{agent_id}")));
|
|
@@ -131,6 +179,80 @@ fn leader_receiver_attached(state: &Value) -> bool {
|
|
|
131
179
|
mode_direct && status_attached && pane_present
|
|
132
180
|
}
|
|
133
181
|
|
|
182
|
+
#[cfg(test)]
|
|
183
|
+
mod tests {
|
|
184
|
+
use super::*;
|
|
185
|
+
|
|
186
|
+
fn write_codex_identity_rollout(
|
|
187
|
+
workspace: &std::path::Path,
|
|
188
|
+
session_id: &str,
|
|
189
|
+
embedded_agent_id: &str,
|
|
190
|
+
) -> std::path::PathBuf {
|
|
191
|
+
let path = workspace.join("rollout-poison.jsonl");
|
|
192
|
+
let text = format!(
|
|
193
|
+
"{{\"session_meta\":{{\"payload\":{{\"id\":\"{session_id}\",\"cwd\":\"{}\"}}}}}}\n\
|
|
194
|
+
{{\"type\":\"turn_context\",\"payload\":{{}}}}\n\
|
|
195
|
+
{{\"type\":\"response_item\",\"payload\":{{\"content\":[{{\"type\":\"input_text\",\"text\":\"You are Team Agent worker `{embedded_agent_id}` with role `fixture`.\"}}]}}}}\n",
|
|
196
|
+
workspace.to_string_lossy()
|
|
197
|
+
);
|
|
198
|
+
std::fs::write(&path, text).expect("write rollout");
|
|
199
|
+
path
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#[test]
|
|
203
|
+
fn diagnose_surfaces_codex_session_identity_mismatch() {
|
|
204
|
+
let workspace =
|
|
205
|
+
std::env::temp_dir().join(format!("ta-diagnose-crossbind-{}", std::process::id()));
|
|
206
|
+
let _ = std::fs::remove_dir_all(&workspace);
|
|
207
|
+
std::fs::create_dir_all(&workspace).unwrap();
|
|
208
|
+
let rollout = write_codex_identity_rollout(
|
|
209
|
+
&workspace,
|
|
210
|
+
"019f3327-c35a-7023-b3cd-1bea93a7a157",
|
|
211
|
+
"ios-dev",
|
|
212
|
+
);
|
|
213
|
+
let state = json!({
|
|
214
|
+
"session_name": "team-fixture",
|
|
215
|
+
"leader_receiver": {
|
|
216
|
+
"mode": "direct_tmux",
|
|
217
|
+
"status": "attached",
|
|
218
|
+
"pane_id": "%leader",
|
|
219
|
+
"provider": "codex"
|
|
220
|
+
},
|
|
221
|
+
"agents": {
|
|
222
|
+
"frontend": {
|
|
223
|
+
"provider": "codex",
|
|
224
|
+
"status": "running",
|
|
225
|
+
"session_id": "019f3327-c35a-7023-b3cd-1bea93a7a157",
|
|
226
|
+
"rollout_path": rollout.to_string_lossy(),
|
|
227
|
+
"captured_at": "2026-07-05T17:04:04Z",
|
|
228
|
+
"captured_via": "fs_watch",
|
|
229
|
+
"spawn_cwd": workspace.to_string_lossy(),
|
|
230
|
+
"window": "frontend"
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
let backend = crate::transport::test_support::OfflineTransport::new()
|
|
235
|
+
.with_session_present(true)
|
|
236
|
+
.with_windows(vec![crate::transport::WindowName::new("frontend")]);
|
|
237
|
+
let (issues, repairs) = diagnose_runtime(&state, &backend);
|
|
238
|
+
assert!(
|
|
239
|
+
issues
|
|
240
|
+
.as_array()
|
|
241
|
+
.is_some_and(|items| items.iter().any(|item| item.as_str()
|
|
242
|
+
== Some("session_identity_mismatch:frontend"))),
|
|
243
|
+
"diagnose must surface session_identity_mismatch for poisoned Codex tuples; issues={issues}"
|
|
244
|
+
);
|
|
245
|
+
assert!(
|
|
246
|
+
repairs.as_array().is_some_and(|items| items.iter().any(|item| {
|
|
247
|
+
item.get("issue").and_then(Value::as_str)
|
|
248
|
+
== Some("session_identity_mismatch:frontend")
|
|
249
|
+
})),
|
|
250
|
+
"diagnose should include an explicit repair hint for the mismatched tuple; repairs={repairs}"
|
|
251
|
+
);
|
|
252
|
+
let _ = std::fs::remove_dir_all(&workspace);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
134
256
|
pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, CliError> {
|
|
135
257
|
let mut checks = Vec::new();
|
|
136
258
|
let mut next_actions = Vec::new();
|
|
@@ -150,7 +272,9 @@ pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, Cl
|
|
|
150
272
|
"ok": false,
|
|
151
273
|
"error": error.to_string(),
|
|
152
274
|
}));
|
|
153
|
-
next_actions.push(json!(
|
|
275
|
+
next_actions.push(json!(
|
|
276
|
+
"fix TEAM.md and role front matter, then run preflight again"
|
|
277
|
+
));
|
|
154
278
|
None
|
|
155
279
|
}
|
|
156
280
|
};
|
|
@@ -183,7 +307,11 @@ pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, Cl
|
|
|
183
307
|
|
|
184
308
|
let workspace = crate::model::paths::team_workspace(team)
|
|
185
309
|
.map_err(|error| CliError::Runtime(error.to_string()))?;
|
|
186
|
-
let profile_dir_exists = workspace
|
|
310
|
+
let profile_dir_exists = workspace
|
|
311
|
+
.join(".team")
|
|
312
|
+
.join("current")
|
|
313
|
+
.join("profiles")
|
|
314
|
+
.exists()
|
|
187
315
|
|| team.join("profiles").exists();
|
|
188
316
|
let profile_dir_check = json!({
|
|
189
317
|
"name": "profile_dir",
|
|
@@ -229,7 +357,10 @@ pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, Cl
|
|
|
229
357
|
let summary = if ok {
|
|
230
358
|
"preflight passed".to_string()
|
|
231
359
|
} else {
|
|
232
|
-
format!(
|
|
360
|
+
format!(
|
|
361
|
+
"preflight found blockers: {}",
|
|
362
|
+
blocker_names(&blockers).join(", ")
|
|
363
|
+
)
|
|
233
364
|
};
|
|
234
365
|
let checks_value = Value::Array(checks);
|
|
235
366
|
let details_log = write_details_log(
|
|
@@ -255,7 +386,9 @@ pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, Cl
|
|
|
255
386
|
Ok(report)
|
|
256
387
|
}
|
|
257
388
|
|
|
258
|
-
pub(crate) fn build_profile_smoke_check_for_team(
|
|
389
|
+
pub(crate) fn build_profile_smoke_check_for_team(
|
|
390
|
+
team: &std::path::Path,
|
|
391
|
+
) -> Result<Value, CliError> {
|
|
259
392
|
let workspace = crate::model::paths::team_workspace(team)
|
|
260
393
|
.map_err(|error| CliError::Runtime(error.to_string()))?;
|
|
261
394
|
let spec = match crate::compiler::compile_team(team) {
|
|
@@ -376,10 +509,14 @@ pub(crate) fn build_wait_ready_report(
|
|
|
376
509
|
}));
|
|
377
510
|
}
|
|
378
511
|
};
|
|
379
|
-
let timeout = if timeout.is_finite() && timeout > 0.0 {
|
|
512
|
+
let timeout = if timeout.is_finite() && timeout > 0.0 {
|
|
513
|
+
timeout
|
|
514
|
+
} else {
|
|
515
|
+
0.0
|
|
516
|
+
};
|
|
380
517
|
let deadline = std::time::Instant::now() + std::time::Duration::from_secs_f64(timeout);
|
|
381
518
|
let mut readiness;
|
|
382
|
-
let mut state_read_error: Option<String
|
|
519
|
+
let mut state_read_error: Option<String>;
|
|
383
520
|
loop {
|
|
384
521
|
let mut state = match crate::state::projection::select_runtime_state(
|
|
385
522
|
&selected.run_workspace,
|
|
@@ -419,7 +556,9 @@ pub(crate) fn build_wait_ready_report(
|
|
|
419
556
|
"error",
|
|
420
557
|
"state_read_error",
|
|
421
558
|
"runtime state could not be read",
|
|
422
|
-
vec![json!(
|
|
559
|
+
vec![json!(
|
|
560
|
+
"inspect .team/runtime/state.json (corrupt or unreadable) and retry"
|
|
561
|
+
)],
|
|
423
562
|
)
|
|
424
563
|
} else if awaiting_trust {
|
|
425
564
|
(
|
|
@@ -427,11 +566,17 @@ pub(crate) fn build_wait_ready_report(
|
|
|
427
566
|
"pending",
|
|
428
567
|
"awaiting_trust_prompt",
|
|
429
568
|
"workers are awaiting trust prompt",
|
|
430
|
-
vec![json!(
|
|
569
|
+
vec![json!(
|
|
570
|
+
"answer the provider trust prompt, then run wait-ready again"
|
|
571
|
+
)],
|
|
431
572
|
)
|
|
432
573
|
} else if ready {
|
|
433
574
|
(true, "ready", "ready", "workers ready", Vec::new())
|
|
434
|
-
} else if readiness
|
|
575
|
+
} else if readiness
|
|
576
|
+
.get("session_capture_complete")
|
|
577
|
+
.and_then(Value::as_bool)
|
|
578
|
+
== Some(false)
|
|
579
|
+
{
|
|
435
580
|
(
|
|
436
581
|
false,
|
|
437
582
|
"pending",
|
|
@@ -445,7 +590,9 @@ pub(crate) fn build_wait_ready_report(
|
|
|
445
590
|
"timeout",
|
|
446
591
|
"workers_not_ready",
|
|
447
592
|
"workers not ready before timeout",
|
|
448
|
-
vec![json!(
|
|
593
|
+
vec![json!(
|
|
594
|
+
"inspect team-agent diagnose output and worker terminals"
|
|
595
|
+
)],
|
|
449
596
|
)
|
|
450
597
|
};
|
|
451
598
|
let details_log = write_details_log(
|
|
@@ -479,7 +626,11 @@ fn inject_tmux_session_present(workspace: &std::path::Path, state: &mut Value) {
|
|
|
479
626
|
// runtime actually uses (state.tmux_endpoint / tmux_socket), not the
|
|
480
627
|
// workspace-hash socket. Otherwise readiness reports `process_started=false`
|
|
481
628
|
// even though the session is alive on the persisted socket.
|
|
482
|
-
let Some(session_name) = state
|
|
629
|
+
let Some(session_name) = state
|
|
630
|
+
.get("session_name")
|
|
631
|
+
.and_then(Value::as_str)
|
|
632
|
+
.filter(|s| !s.is_empty())
|
|
633
|
+
else {
|
|
483
634
|
return;
|
|
484
635
|
};
|
|
485
636
|
let session_name_owned = session_name.to_string();
|
|
@@ -519,14 +670,11 @@ pub(crate) fn wait_readiness(state: &Value) -> Value {
|
|
|
519
670
|
.get("leader_receiver")
|
|
520
671
|
.and_then(Value::as_object)
|
|
521
672
|
.is_some_and(|receiver| {
|
|
522
|
-
receiver
|
|
523
|
-
.get("status")
|
|
524
|
-
.and_then(Value::as_str)
|
|
525
|
-
== Some("attached")
|
|
673
|
+
receiver.get("status").and_then(Value::as_str) == Some("attached")
|
|
526
674
|
|| receiver
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
675
|
+
.get("pane_id")
|
|
676
|
+
.and_then(Value::as_str)
|
|
677
|
+
.is_some_and(|pane| !pane.is_empty() && pane != "__team_agent_unbound__")
|
|
530
678
|
});
|
|
531
679
|
|
|
532
680
|
if let Some(agents) = agents {
|
|
@@ -544,8 +692,7 @@ pub(crate) fn wait_readiness(state: &Value) -> Value {
|
|
|
544
692
|
Some("running" | "busy" | "ready")
|
|
545
693
|
)
|
|
546
694
|
});
|
|
547
|
-
mcp_ready = !agents.is_empty()
|
|
548
|
-
&& agents.values().all(agent_mcp_ready);
|
|
695
|
+
mcp_ready = !agents.is_empty() && agents.values().all(agent_mcp_ready);
|
|
549
696
|
task_prompt_delivered = !agents.is_empty()
|
|
550
697
|
&& (message_counts_positive(state.get("messages"))
|
|
551
698
|
|| agents.values().all(|agent| {
|
|
@@ -560,7 +707,8 @@ pub(crate) fn wait_readiness(state: &Value) -> Value {
|
|
|
560
707
|
.and_then(Value::as_str)
|
|
561
708
|
== Some("awaiting_trust_prompt")
|
|
562
709
|
});
|
|
563
|
-
incomplete_sessions =
|
|
710
|
+
incomplete_sessions =
|
|
711
|
+
crate::session_capture::incomplete_interacted_resumable_agent_ids(state);
|
|
564
712
|
}
|
|
565
713
|
let all_resumable_have_session = incomplete_sessions.is_empty();
|
|
566
714
|
let session_capture_incomplete = !all_resumable_have_session;
|
|
@@ -591,7 +739,9 @@ fn inject_message_counts(workspace: &std::path::Path, state: &mut Value) -> Resu
|
|
|
591
739
|
let mut stmt = conn
|
|
592
740
|
.prepare("select status, count(*) from messages group by status order by status")
|
|
593
741
|
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
594
|
-
let mut rows = stmt
|
|
742
|
+
let mut rows = stmt
|
|
743
|
+
.query([])
|
|
744
|
+
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
595
745
|
let mut messages = Map::new();
|
|
596
746
|
while let Some(row) = rows.next().map_err(|e| CliError::Runtime(e.to_string()))? {
|
|
597
747
|
let status: String = row.get(0).map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
@@ -619,7 +769,10 @@ fn message_counts_positive(value: Option<&Value>) -> bool {
|
|
|
619
769
|
fn legacy_process_started(agents: &serde_json::Map<String, Value>) -> bool {
|
|
620
770
|
!agents.is_empty()
|
|
621
771
|
&& agents.values().all(|agent| {
|
|
622
|
-
agent
|
|
772
|
+
agent
|
|
773
|
+
.get("pane_id")
|
|
774
|
+
.and_then(Value::as_str)
|
|
775
|
+
.is_some_and(|s| !s.is_empty())
|
|
623
776
|
|| agent.get("pid").and_then(Value::as_i64).is_some()
|
|
624
777
|
|| agent.get("process_started").and_then(Value::as_bool) == Some(true)
|
|
625
778
|
|| fake_agent_started(agent)
|
|
@@ -717,7 +870,10 @@ fn preflight_blockers(checks: &[Value]) -> Vec<Value> {
|
|
|
717
870
|
.iter()
|
|
718
871
|
.filter(|check| check.get("ok").and_then(Value::as_bool) == Some(false))
|
|
719
872
|
.map(|check| {
|
|
720
|
-
let name = check
|
|
873
|
+
let name = check
|
|
874
|
+
.get("name")
|
|
875
|
+
.and_then(Value::as_str)
|
|
876
|
+
.unwrap_or("unknown");
|
|
721
877
|
let reason = check
|
|
722
878
|
.get("error")
|
|
723
879
|
.or_else(|| check.get("reason"))
|
|
@@ -747,7 +903,11 @@ fn yaml_path_str<'a>(value: &'a crate::model::yaml::Value, keys: &[&str]) -> Opt
|
|
|
747
903
|
current.as_str()
|
|
748
904
|
}
|
|
749
905
|
|
|
750
|
-
fn write_details_log(
|
|
906
|
+
fn write_details_log(
|
|
907
|
+
workspace: &std::path::Path,
|
|
908
|
+
prefix: &str,
|
|
909
|
+
value: &Value,
|
|
910
|
+
) -> Result<std::path::PathBuf, CliError> {
|
|
751
911
|
let logs = workspace.join(".team").join("logs");
|
|
752
912
|
std::fs::create_dir_all(&logs)?;
|
|
753
913
|
let path = logs.join(format!("{prefix}-{}.json", timestamp_slug()));
|
|
@@ -1517,10 +1517,15 @@ fn peek_args(args: &[String], cwd: &Path) -> Result<PeekArgs, CliError> {
|
|
|
1517
1517
|
fn run_coordinator(args: &[String], cwd: &Path) -> Result<ExitCode, CliError> {
|
|
1518
1518
|
let parsed = parse_args(args);
|
|
1519
1519
|
let workspace = crate::coordinator::WorkspacePath::new(workspace(&parsed, cwd));
|
|
1520
|
+
// 0.5.x Windows portability Batch 9 F8: pass `--team` through
|
|
1521
|
+
// to the daemon so it doesn't have to derive from state at
|
|
1522
|
+
// boot time. `parse_args` already recognizes `--team`; we just
|
|
1523
|
+
// thread the value into `DaemonArgs::team_key`.
|
|
1520
1524
|
crate::coordinator::run_daemon(crate::coordinator::DaemonArgs {
|
|
1521
1525
|
workspace,
|
|
1522
1526
|
once: parsed.once,
|
|
1523
1527
|
tick_interval_sec: parsed.tick_interval,
|
|
1528
|
+
team_key: parsed.team.clone(),
|
|
1524
1529
|
})
|
|
1525
1530
|
.map(|()| ExitCode::Ok)
|
|
1526
1531
|
.map_err(|e| CliError::Runtime(e.to_string()))
|