@team-agent/installer 0.5.112 → 0.5.114
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 +252 -84
- package/Cargo.toml +3 -1
- package/crates/team-agent/Cargo.toml +3 -0
- package/crates/team-agent/src/cli/adapters.rs +119 -50
- package/crates/team-agent/src/cli/diagnose.rs +36 -13
- package/crates/team-agent/src/cli/emit.rs +173 -109
- package/crates/team-agent/src/cli/leader.rs +16 -0
- package/crates/team-agent/src/cli/leader_prompt.rs +261 -0
- package/crates/team-agent/src/cli/leaders.rs +32 -32
- package/crates/team-agent/src/cli/mod.rs +158 -14
- package/crates/team-agent/src/cli/models.rs +68 -18
- package/crates/team-agent/src/cli/named_address.rs +17 -16
- package/crates/team-agent/src/cli/route.rs +4 -4
- package/crates/team-agent/src/cli/send/presentation.rs +19 -18
- package/crates/team-agent/src/cli/send/resolve.rs +11 -11
- package/crates/team-agent/src/cli/send.rs +4 -4
- package/crates/team-agent/src/cli/spec.rs +35 -29
- package/crates/team-agent/src/cli/status.rs +7 -7
- package/crates/team-agent/src/cli/status_port/brief.rs +268 -101
- package/crates/team-agent/src/cli/tests/base.rs +94 -6
- package/crates/team-agent/src/cli/tests/divergence.rs +3 -2
- package/crates/team-agent/src/cli/tests/lane_c.rs +1 -1
- package/crates/team-agent/src/cli/tests/named_address.rs +3 -3
- package/crates/team-agent/src/cli/tests/status_send.rs +9 -7
- package/crates/team-agent/src/cli/triage.rs +53 -2
- package/crates/team-agent/src/cli/types.rs +8 -8
- package/crates/team-agent/src/compiler/tests.rs +12 -0
- package/crates/team-agent/src/compiler.rs +65 -26
- package/crates/team-agent/src/contract_runtime/backend.rs +646 -0
- package/crates/team-agent/src/contract_runtime/config.rs +309 -0
- package/crates/team-agent/src/contract_runtime/discovery.rs +237 -0
- package/crates/team-agent/src/contract_runtime/forward.rs +229 -0
- package/crates/team-agent/src/contract_runtime/framework.rs +261 -0
- package/crates/team-agent/src/contract_runtime/mcp.rs +396 -0
- package/crates/team-agent/src/contract_runtime/mod.rs +14 -0
- package/crates/team-agent/src/contract_runtime/prompt.rs +216 -0
- package/crates/team-agent/src/contract_runtime/pump.rs +207 -0
- package/crates/team-agent/src/contract_runtime/registry.rs +30 -0
- package/crates/team-agent/src/coordinator/tick.rs +26 -8
- package/crates/team-agent/src/leader/mod.rs +1 -0
- package/crates/team-agent/src/leader/prompt.rs +906 -0
- package/crates/team-agent/src/leader/start.rs +265 -16
- package/crates/team-agent/src/leader/tests/identity.rs +21 -0
- package/crates/team-agent/src/leader/tests/mod.rs +2 -0
- package/crates/team-agent/src/leader/tests/prompt_contract.rs +268 -0
- package/crates/team-agent/src/leader/types.rs +6 -0
- package/crates/team-agent/src/lib.rs +4 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +172 -13
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +760 -365
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +38 -9
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +17 -0
- package/crates/team-agent/src/lifecycle/launch.rs +39 -28
- package/crates/team-agent/src/lifecycle/profile_launch.rs +58 -46
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +97 -52
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +3 -2
- package/crates/team-agent/src/lifecycle/types.rs +4 -1
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +37 -27
- package/crates/team-agent/src/main.rs +6 -0
- package/crates/team-agent/src/messaging/delivery.rs +50 -1
- package/crates/team-agent/src/messaging/send.rs +10 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +131 -0
- package/crates/team-agent/src/model/spec.rs +24 -12
- package/crates/team-agent/src/provider/adapter.rs +8 -59
- package/crates/team-agent/src/provider/adapters/claude.rs +6 -14
- package/crates/team-agent/src/provider/adapters/codex.rs +8 -18
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -22
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +8 -45
- package/crates/team-agent/src/provider/adapters/grok.rs +7 -31
- package/crates/team-agent/src/provider/adapters/pi.rs +7 -18
- package/crates/team-agent/src/provider/command_helpers.rs +45 -0
- package/crates/team-agent/src/provider/mod.rs +1 -0
- package/crates/team-agent/src/provider/model_catalog.rs +21 -28
- package/crates/team-agent/src/provider/tests/adapter.rs +70 -0
- package/crates/team-agent/src/provider/tests/command_helpers.rs +55 -0
- package/crates/team-agent/src/state/persist.rs +73 -0
- package/package.json +4 -4
|
@@ -28,6 +28,9 @@ chrono.workspace = true
|
|
|
28
28
|
libc.workspace = true
|
|
29
29
|
rusqlite.workspace = true # step 3 db(bundled SQLite,静态链接)
|
|
30
30
|
|
|
31
|
+
[target.'cfg(unix)'.dependencies]
|
|
32
|
+
team-agent-contract = { path = "../team-agent-contract" }
|
|
33
|
+
|
|
31
34
|
# 0.5.x Windows portability Batch 2/3: uses the same `windows = "0.61"`
|
|
32
35
|
# crate as `win-conpty-phase0` so the workspace shares a single
|
|
33
36
|
# Windows-API crate + already-locked Cargo.lock version.
|
|
@@ -7,6 +7,9 @@ use super::*;
|
|
|
7
7
|
|
|
8
8
|
/// `cmd_quick_start`(`commands.py:18`)。`--json` 或 `!ok` → 整 dict;否则 `result["summary"]`。
|
|
9
9
|
pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
10
|
+
#[cfg(unix)]
|
|
11
|
+
crate::contract_runtime::config::read_team(args)
|
|
12
|
+
.map_err(|error| CliError::Runtime(error.to_string()))?;
|
|
10
13
|
let mut value = lifecycle_port::quick_start(
|
|
11
14
|
&args.workspace,
|
|
12
15
|
&args.agents_dir,
|
|
@@ -19,6 +22,10 @@ pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
|
19
22
|
return Ok(result);
|
|
20
23
|
}
|
|
21
24
|
append_send_guidance(&mut value, &args.workspace, args.team_id.as_deref());
|
|
25
|
+
finish_quick_start(value, args)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fn finish_quick_start(mut value: Value, args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
22
29
|
let readiness = value.get("readiness").and_then(Value::as_object);
|
|
23
30
|
let all_resumable_have_session = readiness
|
|
24
31
|
.and_then(|readiness| readiness.get("all_resumable_have_session"))
|
|
@@ -36,7 +43,7 @@ pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
|
36
43
|
.get("status")
|
|
37
44
|
.and_then(Value::as_str)
|
|
38
45
|
.map(str::to_string);
|
|
39
|
-
if !args.detail {
|
|
46
|
+
if !args.detail && value.get("ok").and_then(Value::as_bool) != Some(false) {
|
|
40
47
|
lifecycle_port::compact_quick_start_value(&mut value);
|
|
41
48
|
}
|
|
42
49
|
if args.json
|
|
@@ -45,30 +52,18 @@ pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
|
45
52
|
|| !readiness_ready
|
|
46
53
|
{
|
|
47
54
|
let mut result = CmdResult::from_json(value, args.json);
|
|
48
|
-
if
|
|
55
|
+
if status.as_deref() == Some("pending_tool_load") {
|
|
49
56
|
result.exit = ExitCode::Ok;
|
|
50
57
|
}
|
|
51
58
|
if !args.json {
|
|
52
59
|
if let CmdOutput::Json(value) = &result.output {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
)
|
|
61
|
-
} else {
|
|
62
|
-
"队伍尚未准备好接收任务;请等待工具完成启动。".to_string()
|
|
63
|
-
};
|
|
64
|
-
let mut doctor = format!(
|
|
65
|
-
"team-agent doctor --workspace {}",
|
|
66
|
-
shell_quote(&args.workspace.to_string_lossy())
|
|
67
|
-
);
|
|
68
|
-
if let Some(team) = args.team_id.as_deref() {
|
|
69
|
-
doctor.push_str(&format!(" --team {}", shell_quote(team)));
|
|
70
|
-
}
|
|
71
|
-
result.output = CmdOutput::Human(format!("{explanation}\n下一步:运行 {doctor},按体检提示处理;用 status 查看状态,不反复起队或重发任务。"));
|
|
60
|
+
// Typed refusals often have no `error`. Preserve the complete
|
|
61
|
+
// report instead of replacing its reason/actions with a guess.
|
|
62
|
+
let safe = crate::redaction::redact_external_value(value);
|
|
63
|
+
result.output = CmdOutput::Human(format!(
|
|
64
|
+
"quick-start report:\n{}",
|
|
65
|
+
serde_json::to_string_pretty(&safe)?
|
|
66
|
+
));
|
|
72
67
|
}
|
|
73
68
|
}
|
|
74
69
|
Ok(result)
|
|
@@ -126,11 +121,11 @@ fn quick_start_config_guidance(value: &mut Value, args: &QuickStartArgs) -> Opti
|
|
|
126
121
|
retry.push_str(" --json");
|
|
127
122
|
}
|
|
128
123
|
let explanation = if missing_team {
|
|
129
|
-
format!("
|
|
124
|
+
format!("Team configuration is missing: {}. No workers were started. Create the two files below; this command did not write them. Error: {error}", team_path.display())
|
|
130
125
|
} else {
|
|
131
|
-
format!("
|
|
126
|
+
format!("Worker configuration is incomplete: {detail}. Add the role directory or required fields. The examples below do not overwrite existing files. Error: {error}")
|
|
132
127
|
};
|
|
133
|
-
let next = format!("
|
|
128
|
+
let next = format!("Check that Pi is installed and signed in; verify the model with team-agent models --provider pi.\nRun team-agent pi, then execute this command in that leader's tool context:\n{retry}");
|
|
134
129
|
let mut templates =
|
|
135
130
|
vec![serde_json::json!({"path": role_path, "content": super::emit::WORKER_TEMPLATE})];
|
|
136
131
|
let mut human = explanation.clone();
|
|
@@ -140,13 +135,13 @@ fn quick_start_config_guidance(value: &mut Value, args: &QuickStartArgs) -> Opti
|
|
|
140
135
|
serde_json::json!({"path": team_path, "content": super::emit::TEAM_TEMPLATE}),
|
|
141
136
|
);
|
|
142
137
|
human.push_str(&format!(
|
|
143
|
-
"\n\n{}
|
|
138
|
+
"\n\n{}:\n{}",
|
|
144
139
|
team_path.display(),
|
|
145
140
|
super::emit::TEAM_TEMPLATE
|
|
146
141
|
));
|
|
147
142
|
}
|
|
148
143
|
human.push_str(&format!(
|
|
149
|
-
"\n\n{}
|
|
144
|
+
"\n\n{}:\n{}\nNext steps:\n{next}",
|
|
150
145
|
role_path.display(),
|
|
151
146
|
super::emit::WORKER_TEMPLATE
|
|
152
147
|
));
|
|
@@ -166,11 +161,10 @@ fn quick_start_config_guidance(value: &mut Value, args: &QuickStartArgs) -> Opti
|
|
|
166
161
|
/// E13:quick-start "team 起了" 人类输出 = summary + attach 块。所有成功出口共用(别每分支手拷)。
|
|
167
162
|
/// attach_commands 缺/空 → 只 summary(向后兼容)。
|
|
168
163
|
fn quickstart_human(value: &Value) -> String {
|
|
169
|
-
let summary =
|
|
170
|
-
"
|
|
171
|
-
|
|
172
|
-
"
|
|
173
|
-
};
|
|
164
|
+
let summary = value
|
|
165
|
+
.get("summary")
|
|
166
|
+
.and_then(Value::as_str)
|
|
167
|
+
.unwrap_or("Team started.");
|
|
174
168
|
let attach: Vec<&str> = value
|
|
175
169
|
.get("attach_commands")
|
|
176
170
|
.and_then(Value::as_array)
|
|
@@ -183,14 +177,14 @@ fn quickstart_human(value: &Value) -> String {
|
|
|
183
177
|
.unwrap_or_default();
|
|
184
178
|
let mut out = String::from(summary);
|
|
185
179
|
if !attach.is_empty() {
|
|
186
|
-
out.push_str("\n\
|
|
180
|
+
out.push_str("\n\nConnect to the team:");
|
|
187
181
|
for cmd in attach {
|
|
188
182
|
out.push_str("\n ");
|
|
189
183
|
out.push_str(cmd);
|
|
190
184
|
}
|
|
191
185
|
}
|
|
192
186
|
if !sends.is_empty() {
|
|
193
|
-
out.push_str("\n\
|
|
187
|
+
out.push_str("\n\nSend a task (replace the example message):");
|
|
194
188
|
for cmd in sends {
|
|
195
189
|
out.push_str("\n ");
|
|
196
190
|
out.push_str(cmd);
|
|
@@ -257,7 +251,7 @@ pub(crate) fn send_command(agent: &str, workspace: &Path, team: Option<&str>) ->
|
|
|
257
251
|
let mut command = format!(
|
|
258
252
|
"team-agent send {} {} --workspace {}",
|
|
259
253
|
shell_quote(agent),
|
|
260
|
-
shell_quote("
|
|
254
|
+
shell_quote("Complete the task and reply to the leader."),
|
|
261
255
|
shell_quote(workspace)
|
|
262
256
|
);
|
|
263
257
|
if let Some(team) = team {
|
|
@@ -1014,12 +1008,13 @@ fn finalize_doctor_report(report: &mut Value, default_report: bool) {
|
|
|
1014
1008
|
|
|
1015
1009
|
#[cfg(test)]
|
|
1016
1010
|
mod tests {
|
|
1017
|
-
#![allow(clippy::unwrap_used)]
|
|
1011
|
+
#![allow(clippy::unwrap_used, clippy::panic)]
|
|
1018
1012
|
|
|
1019
1013
|
use super::{
|
|
1020
|
-
append_send_guidance, format_inbox_human, quickstart_human,
|
|
1021
|
-
split_shell_argv,
|
|
1014
|
+
append_send_guidance, finish_quick_start, format_inbox_human, quickstart_human,
|
|
1015
|
+
send_command, split_shell_argv,
|
|
1022
1016
|
};
|
|
1017
|
+
use crate::cli::{CmdOutput, ExitCode, QuickStartArgs};
|
|
1023
1018
|
use serde_json::{json, Value};
|
|
1024
1019
|
use std::path::{Path, PathBuf};
|
|
1025
1020
|
|
|
@@ -1057,11 +1052,11 @@ mod tests {
|
|
|
1057
1052
|
});
|
|
1058
1053
|
let out = quickstart_human(&value);
|
|
1059
1054
|
assert!(
|
|
1060
|
-
out.contains("
|
|
1055
|
+
out.contains("team started"),
|
|
1061
1056
|
"must show startup summary; got {out}"
|
|
1062
1057
|
);
|
|
1063
1058
|
assert!(
|
|
1064
|
-
out.contains("
|
|
1059
|
+
out.contains("Connect to the team:"),
|
|
1065
1060
|
"must render attach block; got {out}"
|
|
1066
1061
|
);
|
|
1067
1062
|
assert!(
|
|
@@ -1099,19 +1094,13 @@ mod tests {
|
|
|
1099
1094
|
let value = json!({"summary": "quick-start complete"});
|
|
1100
1095
|
assert_eq!(
|
|
1101
1096
|
quickstart_human(&value),
|
|
1102
|
-
format!(
|
|
1103
|
-
"队伍已启动;下面是连接方式和派发任务的命令。\n{}",
|
|
1104
|
-
crate::cli::QUICK_START_REMINDER
|
|
1105
|
-
)
|
|
1097
|
+
format!("quick-start complete\n{}", crate::cli::QUICK_START_REMINDER)
|
|
1106
1098
|
);
|
|
1107
1099
|
// 空数组也只显示人类摘要与下一步,不凭空编造连接方式。
|
|
1108
1100
|
let value2 = json!({"summary": "s", "attach_commands": []});
|
|
1109
1101
|
assert_eq!(
|
|
1110
1102
|
quickstart_human(&value2),
|
|
1111
|
-
format!(
|
|
1112
|
-
"队伍已启动;下面是连接方式和派发任务的命令。\n{}",
|
|
1113
|
-
crate::cli::QUICK_START_REMINDER
|
|
1114
|
-
)
|
|
1103
|
+
format!("s\n{}", crate::cli::QUICK_START_REMINDER)
|
|
1115
1104
|
);
|
|
1116
1105
|
}
|
|
1117
1106
|
|
|
@@ -1130,7 +1119,7 @@ mod tests {
|
|
|
1130
1119
|
"team-agent",
|
|
1131
1120
|
"send",
|
|
1132
1121
|
"worker name; echo unsafe",
|
|
1133
|
-
"
|
|
1122
|
+
"Complete the task and reply to the leader.",
|
|
1134
1123
|
"--workspace",
|
|
1135
1124
|
"/tmp/my workspace",
|
|
1136
1125
|
"--team",
|
|
@@ -1149,7 +1138,7 @@ mod tests {
|
|
|
1149
1138
|
.unwrap();
|
|
1150
1139
|
assert_eq!(
|
|
1151
1140
|
command,
|
|
1152
|
-
"team-agent send 'worker name; echo unsafe' '
|
|
1141
|
+
"team-agent send 'worker name; echo unsafe' 'Complete the task and reply to the leader.' --workspace '/tmp/my workspace' --team team-a"
|
|
1153
1142
|
);
|
|
1154
1143
|
}
|
|
1155
1144
|
|
|
@@ -1206,7 +1195,7 @@ mod tests {
|
|
|
1206
1195
|
"team-agent",
|
|
1207
1196
|
"send",
|
|
1208
1197
|
"worker",
|
|
1209
|
-
"
|
|
1198
|
+
"Complete the task and reply to the leader.",
|
|
1210
1199
|
"--workspace",
|
|
1211
1200
|
"/tmp/ws",
|
|
1212
1201
|
"--team",
|
|
@@ -1215,4 +1204,84 @@ mod tests {
|
|
|
1215
1204
|
);
|
|
1216
1205
|
}
|
|
1217
1206
|
|
|
1207
|
+
fn quick_start_args(as_json: bool) -> QuickStartArgs {
|
|
1208
|
+
QuickStartArgs {
|
|
1209
|
+
workspace: PathBuf::from("/tmp/first-launch"),
|
|
1210
|
+
agents_dir: PathBuf::from("/tmp/first-launch/.team/current"),
|
|
1211
|
+
name: None,
|
|
1212
|
+
team_id: None,
|
|
1213
|
+
yes: true,
|
|
1214
|
+
json: as_json,
|
|
1215
|
+
detail: false,
|
|
1216
|
+
backend: None,
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
#[test]
|
|
1221
|
+
fn quick_start_human_preserves_typed_refusals_without_error_field() {
|
|
1222
|
+
for status in [
|
|
1223
|
+
"existing_runtime",
|
|
1224
|
+
"preflight_blocked",
|
|
1225
|
+
"leader_binding_refused",
|
|
1226
|
+
] {
|
|
1227
|
+
let value = json!({
|
|
1228
|
+
"ok": false,
|
|
1229
|
+
"status": status,
|
|
1230
|
+
"reason": "the exact underlying refusal",
|
|
1231
|
+
"summary": "creation refused",
|
|
1232
|
+
"blockers": [{"stage": "identity", "reason": "owner mismatch"}],
|
|
1233
|
+
"next_actions": ["team-agent restart --workspace /tmp/first-launch --team current"],
|
|
1234
|
+
"state_path": "/tmp/first-launch/.team/runtime/state.json",
|
|
1235
|
+
"readiness": {"reason": "underlying readiness evidence"}
|
|
1236
|
+
});
|
|
1237
|
+
let result = finish_quick_start(value.clone(), &quick_start_args(false)).unwrap();
|
|
1238
|
+
assert_eq!(result.exit, ExitCode::Error);
|
|
1239
|
+
let CmdOutput::Human(text) = result.output else {
|
|
1240
|
+
panic!("expected human report")
|
|
1241
|
+
};
|
|
1242
|
+
let rendered: Value =
|
|
1243
|
+
serde_json::from_str(text.strip_prefix("quick-start report:\n").unwrap()).unwrap();
|
|
1244
|
+
assert_eq!(rendered, value, "human projection lost fields for {status}");
|
|
1245
|
+
assert!(
|
|
1246
|
+
!text.contains("team-agent doctor"),
|
|
1247
|
+
"must not replace actual actions"
|
|
1248
|
+
);
|
|
1249
|
+
let json_result = finish_quick_start(value.clone(), &quick_start_args(true)).unwrap();
|
|
1250
|
+
assert_eq!(json_result.exit, ExitCode::Error);
|
|
1251
|
+
assert_eq!(json_result.output, CmdOutput::Json(value));
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
#[test]
|
|
1256
|
+
fn quick_start_human_preserves_original_error_and_actions() {
|
|
1257
|
+
let value = json!({
|
|
1258
|
+
"ok": false,
|
|
1259
|
+
"error": "database initialization failed at /tmp/first-launch/.team/team.db: permission denied",
|
|
1260
|
+
"action": "repair directory permissions",
|
|
1261
|
+
"next_actions": ["inspect the exact database path"]
|
|
1262
|
+
});
|
|
1263
|
+
let result = finish_quick_start(value.clone(), &quick_start_args(false)).unwrap();
|
|
1264
|
+
assert_eq!(result.exit, ExitCode::Error);
|
|
1265
|
+
let CmdOutput::Human(text) = result.output else {
|
|
1266
|
+
panic!("expected human report")
|
|
1267
|
+
};
|
|
1268
|
+
let rendered: Value =
|
|
1269
|
+
serde_json::from_str(text.strip_prefix("quick-start report:\n").unwrap()).unwrap();
|
|
1270
|
+
assert_eq!(rendered, value);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
#[test]
|
|
1274
|
+
fn pending_tool_load_is_not_reported_as_failed_team_creation() {
|
|
1275
|
+
for as_json in [false, true] {
|
|
1276
|
+
let value = json!({
|
|
1277
|
+
"ok": true,
|
|
1278
|
+
"status": "pending_tool_load",
|
|
1279
|
+
"summary": "Team started; worker tool loading is not yet verified",
|
|
1280
|
+
"readiness": {"ready": false},
|
|
1281
|
+
"next_actions": ["send a task and wait for the actual reply"]
|
|
1282
|
+
});
|
|
1283
|
+
let result = finish_quick_start(value, &quick_start_args(as_json)).unwrap();
|
|
1284
|
+
assert_eq!(result.exit, ExitCode::Ok);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1218
1287
|
}
|
|
@@ -342,7 +342,13 @@ pub(crate) fn diagnose_runtime_for_workspace(
|
|
|
342
342
|
&mut repairs,
|
|
343
343
|
);
|
|
344
344
|
append_legacy_snapshot_issue(workspace, state, &mut issues);
|
|
345
|
-
append_coordinator_health_issue(
|
|
345
|
+
append_coordinator_health_issue(
|
|
346
|
+
workspace,
|
|
347
|
+
state,
|
|
348
|
+
coordinator_health,
|
|
349
|
+
&mut issues,
|
|
350
|
+
&mut repairs,
|
|
351
|
+
);
|
|
346
352
|
append_runtime_bindings_stale_after_boot_issue(workspace, state, &mut issues, &mut repairs);
|
|
347
353
|
(issues, repairs)
|
|
348
354
|
}
|
|
@@ -373,7 +379,9 @@ pub(crate) fn workspace_has_existing_team_runtime(
|
|
|
373
379
|
let terminal = team_state
|
|
374
380
|
.get("status")
|
|
375
381
|
.and_then(Value::as_str)
|
|
376
|
-
.is_some_and(|status|
|
|
382
|
+
.is_some_and(|status| {
|
|
383
|
+
matches!(status, "stopped" | "shutdown" | "archived" | "terminal")
|
|
384
|
+
});
|
|
377
385
|
if terminal {
|
|
378
386
|
return false;
|
|
379
387
|
}
|
|
@@ -695,14 +703,13 @@ pub(crate) fn append_registry_channel_unbound_to_report(
|
|
|
695
703
|
repairs.push(recovery_hint(&team_key, issue_id, repair));
|
|
696
704
|
object.insert("issues".to_string(), Value::Array(issues));
|
|
697
705
|
object.insert("suggested_repairs".to_string(), Value::Array(repairs));
|
|
698
|
-
let is_healthy_unattached_host =
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
.is_some_and(|checks| !checks.is_empty());
|
|
706
|
+
let is_healthy_unattached_host = class == crate::lifecycle::launch::LeaderBindingClass::Unbound
|
|
707
|
+
&& object.get("ok").and_then(Value::as_bool) == Some(true)
|
|
708
|
+
&& object
|
|
709
|
+
.get("profile_smoke")
|
|
710
|
+
.and_then(|profile| profile.get("checks"))
|
|
711
|
+
.and_then(Value::as_array)
|
|
712
|
+
.is_some_and(|checks| !checks.is_empty());
|
|
706
713
|
if !is_healthy_unattached_host {
|
|
707
714
|
object.insert("ok".to_string(), Value::Bool(false));
|
|
708
715
|
}
|
|
@@ -1054,8 +1061,7 @@ mod tests {
|
|
|
1054
1061
|
pane_pid: None,
|
|
1055
1062
|
leader_env: Default::default(),
|
|
1056
1063
|
}]);
|
|
1057
|
-
let Some((_, repair)) =
|
|
1058
|
-
live_leader_workspace_mismatch(&workspace, &state, &transport)
|
|
1064
|
+
let Some((_, repair)) = live_leader_workspace_mismatch(&workspace, &state, &transport)
|
|
1059
1065
|
else {
|
|
1060
1066
|
panic!("workspace mismatch repair required");
|
|
1061
1067
|
};
|
|
@@ -1109,7 +1115,8 @@ mod tests {
|
|
|
1109
1115
|
.as_nanos()
|
|
1110
1116
|
));
|
|
1111
1117
|
std::fs::create_dir_all(&workspace).unwrap();
|
|
1112
|
-
let state =
|
|
1118
|
+
let state =
|
|
1119
|
+
json!({"agents": {}, "tasks": [], "session_name": null, "active_team_key": null});
|
|
1113
1120
|
let mut issues = json!([]);
|
|
1114
1121
|
let mut repairs = json!([]);
|
|
1115
1122
|
append_registry_channel_unbound_issue(
|
|
@@ -1574,6 +1581,22 @@ pub(crate) fn provider_doctor_checks() -> Value {
|
|
|
1574
1581
|
}),
|
|
1575
1582
|
);
|
|
1576
1583
|
}
|
|
1584
|
+
#[cfg(unix)]
|
|
1585
|
+
for descriptor in crate::contract_runtime::registry::descriptors() {
|
|
1586
|
+
let identity = &descriptor.identity;
|
|
1587
|
+
// Inventory only: finding the launcher is not an engine, auth or readiness probe.
|
|
1588
|
+
providers.insert(
|
|
1589
|
+
identity.id.to_string(),
|
|
1590
|
+
json!({
|
|
1591
|
+
"auth": crate::provider::AuthHintStatus::Unknown,
|
|
1592
|
+
"command": identity.binary,
|
|
1593
|
+
"installed": crate::contract_runtime::discovery::executable(identity.binary).is_ok(),
|
|
1594
|
+
"version": "unknown",
|
|
1595
|
+
"probe_status": "not_run",
|
|
1596
|
+
"provider_probe_status": "not_run",
|
|
1597
|
+
}),
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1577
1600
|
Value::Object(providers)
|
|
1578
1601
|
}
|
|
1579
1602
|
|