@team-agent/installer 0.5.112 → 0.5.113

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.
Files changed (44) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +100 -44
  4. package/crates/team-agent/src/cli/emit.rs +127 -98
  5. package/crates/team-agent/src/cli/leaders.rs +32 -32
  6. package/crates/team-agent/src/cli/mod.rs +6 -6
  7. package/crates/team-agent/src/cli/named_address.rs +17 -16
  8. package/crates/team-agent/src/cli/route.rs +4 -4
  9. package/crates/team-agent/src/cli/send/presentation.rs +19 -18
  10. package/crates/team-agent/src/cli/send/resolve.rs +11 -11
  11. package/crates/team-agent/src/cli/send.rs +4 -4
  12. package/crates/team-agent/src/cli/spec.rs +29 -29
  13. package/crates/team-agent/src/cli/status.rs +7 -7
  14. package/crates/team-agent/src/cli/tests/base.rs +6 -6
  15. package/crates/team-agent/src/cli/tests/divergence.rs +3 -2
  16. package/crates/team-agent/src/cli/tests/lane_c.rs +1 -1
  17. package/crates/team-agent/src/cli/tests/named_address.rs +3 -3
  18. package/crates/team-agent/src/cli/tests/status_send.rs +7 -7
  19. package/crates/team-agent/src/cli/triage.rs +53 -2
  20. package/crates/team-agent/src/cli/types.rs +8 -8
  21. package/crates/team-agent/src/compiler/tests.rs +12 -0
  22. package/crates/team-agent/src/compiler.rs +0 -9
  23. package/crates/team-agent/src/leader/start.rs +5 -0
  24. package/crates/team-agent/src/leader/tests/identity.rs +21 -0
  25. package/crates/team-agent/src/lifecycle/launch/identity.rs +172 -13
  26. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +325 -14
  27. package/crates/team-agent/src/lifecycle/launch.rs +2 -2
  28. package/crates/team-agent/src/lifecycle/profile_launch.rs +58 -46
  29. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +97 -52
  30. package/crates/team-agent/src/lifecycle/restart.rs +1 -1
  31. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +3 -2
  32. package/crates/team-agent/src/provider/adapter.rs +8 -59
  33. package/crates/team-agent/src/provider/adapters/claude.rs +6 -14
  34. package/crates/team-agent/src/provider/adapters/codex.rs +8 -18
  35. package/crates/team-agent/src/provider/adapters/copilot.rs +5 -22
  36. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +8 -45
  37. package/crates/team-agent/src/provider/adapters/grok.rs +7 -31
  38. package/crates/team-agent/src/provider/adapters/pi.rs +7 -18
  39. package/crates/team-agent/src/provider/command_helpers.rs +45 -0
  40. package/crates/team-agent/src/provider/mod.rs +1 -0
  41. package/crates/team-agent/src/provider/model_catalog.rs +21 -28
  42. package/crates/team-agent/src/provider/tests/adapter.rs +70 -0
  43. package/crates/team-agent/src/provider/tests/command_helpers.rs +55 -0
  44. package/package.json +4 -4
package/Cargo.lock CHANGED
@@ -581,7 +581,7 @@ dependencies = [
581
581
 
582
582
  [[package]]
583
583
  name = "team-agent"
584
- version = "0.5.112"
584
+ version = "0.5.113"
585
585
  dependencies = [
586
586
  "anyhow",
587
587
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent", "crates/win-conpty-phase0", "crates/conpty-trans
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.5.112"
12
+ version = "0.5.113"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -19,6 +19,10 @@ pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
19
19
  return Ok(result);
20
20
  }
21
21
  append_send_guidance(&mut value, &args.workspace, args.team_id.as_deref());
22
+ finish_quick_start(value, args)
23
+ }
24
+
25
+ fn finish_quick_start(mut value: Value, args: &QuickStartArgs) -> Result<CmdResult, CliError> {
22
26
  let readiness = value.get("readiness").and_then(Value::as_object);
23
27
  let all_resumable_have_session = readiness
24
28
  .and_then(|readiness| readiness.get("all_resumable_have_session"))
@@ -36,7 +40,7 @@ pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
36
40
  .get("status")
37
41
  .and_then(Value::as_str)
38
42
  .map(str::to_string);
39
- if !args.detail {
43
+ if !args.detail && value.get("ok").and_then(Value::as_bool) != Some(false) {
40
44
  lifecycle_port::compact_quick_start_value(&mut value);
41
45
  }
42
46
  if args.json
@@ -45,30 +49,18 @@ pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
45
49
  || !readiness_ready
46
50
  {
47
51
  let mut result = CmdResult::from_json(value, args.json);
48
- if args.json && status.as_deref() == Some("pending_tool_load") {
52
+ if status.as_deref() == Some("pending_tool_load") {
49
53
  result.exit = ExitCode::Ok;
50
54
  }
51
55
  if !args.json {
52
56
  if let CmdOutput::Json(value) = &result.output {
53
- let explanation = if value.get("ok").and_then(Value::as_bool) == Some(false) {
54
- format!(
55
- "启动未完成:{}",
56
- value
57
- .get("error")
58
- .and_then(Value::as_str)
59
- .unwrap_or("队伍尚未准备好接收任务")
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 查看状态,不反复起队或重发任务。"));
57
+ // Typed refusals often have no `error`. Preserve the complete
58
+ // report instead of replacing its reason/actions with a guess.
59
+ let safe = crate::redaction::redact_external_value(value);
60
+ result.output = CmdOutput::Human(format!(
61
+ "quick-start report:\n{}",
62
+ serde_json::to_string_pretty(&safe)?
63
+ ));
72
64
  }
73
65
  }
74
66
  Ok(result)
@@ -126,11 +118,11 @@ fn quick_start_config_guidance(value: &mut Value, args: &QuickStartArgs) -> Opti
126
118
  retry.push_str(" --json");
127
119
  }
128
120
  let explanation = if missing_team {
129
- format!("这里还没有队伍配置:{}。没有启动任何队友。请自行创建下面两个文件;本命令没有写入这些文件。", team_path.display())
121
+ 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
122
  } else {
131
- format!("队友配置尚不完整:{detail}。请补齐角色目录或必需字段;下面仅给安全示例,不覆盖已有文件。")
123
+ format!("Worker configuration is incomplete: {detail}. Add the role directory or required fields. The examples below do not overwrite existing files. Error: {error}")
132
124
  };
133
- let next = format!("确认 Pi 已安装并登录;运行 team-agent models --provider pi 核对模型。\n运行 team-agent pi,在该主控的命令行/工具上下文再次执行:\n{retry}");
125
+ 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
126
  let mut templates =
135
127
  vec![serde_json::json!({"path": role_path, "content": super::emit::WORKER_TEMPLATE})];
136
128
  let mut human = explanation.clone();
@@ -140,13 +132,13 @@ fn quick_start_config_guidance(value: &mut Value, args: &QuickStartArgs) -> Opti
140
132
  serde_json::json!({"path": team_path, "content": super::emit::TEAM_TEMPLATE}),
141
133
  );
142
134
  human.push_str(&format!(
143
- "\n\n{}:\n{}",
135
+ "\n\n{}:\n{}",
144
136
  team_path.display(),
145
137
  super::emit::TEAM_TEMPLATE
146
138
  ));
147
139
  }
148
140
  human.push_str(&format!(
149
- "\n\n{}:\n{}\n下一步:\n{next}",
141
+ "\n\n{}:\n{}\nNext steps:\n{next}",
150
142
  role_path.display(),
151
143
  super::emit::WORKER_TEMPLATE
152
144
  ));
@@ -166,11 +158,7 @@ fn quick_start_config_guidance(value: &mut Value, args: &QuickStartArgs) -> Opti
166
158
  /// E13:quick-start "team 起了" 人类输出 = summary + attach 块。所有成功出口共用(别每分支手拷)。
167
159
  /// attach_commands 缺/空 → 只 summary(向后兼容)。
168
160
  fn quickstart_human(value: &Value) -> String {
169
- let summary = if value.get("summary").and_then(Value::as_str) == Some("existing runtime") {
170
- "发现已有队伍;请查看返回的连接方式,不会另建同名队伍。"
171
- } else {
172
- "队伍已启动;下面是连接方式和派发任务的命令。"
173
- };
161
+ let summary = value.get("summary").and_then(Value::as_str).unwrap_or("Team started.");
174
162
  let attach: Vec<&str> = value
175
163
  .get("attach_commands")
176
164
  .and_then(Value::as_array)
@@ -183,14 +171,14 @@ fn quickstart_human(value: &Value) -> String {
183
171
  .unwrap_or_default();
184
172
  let mut out = String::from(summary);
185
173
  if !attach.is_empty() {
186
- out.push_str("\n\n连接队伍:");
174
+ out.push_str("\n\nConnect to the team:");
187
175
  for cmd in attach {
188
176
  out.push_str("\n ");
189
177
  out.push_str(cmd);
190
178
  }
191
179
  }
192
180
  if !sends.is_empty() {
193
- out.push_str("\n\n派发任务(请替换任务内容):");
181
+ out.push_str("\n\nSend a task (replace the example message):");
194
182
  for cmd in sends {
195
183
  out.push_str("\n ");
196
184
  out.push_str(cmd);
@@ -257,7 +245,7 @@ pub(crate) fn send_command(agent: &str, workspace: &Path, team: Option<&str>) ->
257
245
  let mut command = format!(
258
246
  "team-agent send {} {} --workspace {}",
259
247
  shell_quote(agent),
260
- shell_quote("请完成任务并把答案回复给 leader。"),
248
+ shell_quote("Complete the task and reply to the leader."),
261
249
  shell_quote(workspace)
262
250
  );
263
251
  if let Some(team) = team {
@@ -1014,12 +1002,13 @@ fn finalize_doctor_report(report: &mut Value, default_report: bool) {
1014
1002
 
1015
1003
  #[cfg(test)]
1016
1004
  mod tests {
1017
- #![allow(clippy::unwrap_used)]
1005
+ #![allow(clippy::unwrap_used, clippy::panic)]
1018
1006
 
1019
1007
  use super::{
1020
- append_send_guidance, format_inbox_human, quickstart_human, send_command,
1021
- split_shell_argv,
1008
+ append_send_guidance, finish_quick_start, format_inbox_human, quickstart_human,
1009
+ send_command, split_shell_argv,
1022
1010
  };
1011
+ use crate::cli::{CmdOutput, ExitCode, QuickStartArgs};
1023
1012
  use serde_json::{json, Value};
1024
1013
  use std::path::{Path, PathBuf};
1025
1014
 
@@ -1057,11 +1046,11 @@ mod tests {
1057
1046
  });
1058
1047
  let out = quickstart_human(&value);
1059
1048
  assert!(
1060
- out.contains("队伍已启动"),
1049
+ out.contains("team started"),
1061
1050
  "must show startup summary; got {out}"
1062
1051
  );
1063
1052
  assert!(
1064
- out.contains("连接队伍:"),
1053
+ out.contains("Connect to the team:"),
1065
1054
  "must render attach block; got {out}"
1066
1055
  );
1067
1056
  assert!(
@@ -1100,7 +1089,7 @@ mod tests {
1100
1089
  assert_eq!(
1101
1090
  quickstart_human(&value),
1102
1091
  format!(
1103
- "队伍已启动;下面是连接方式和派发任务的命令。\n{}",
1092
+ "quick-start complete\n{}",
1104
1093
  crate::cli::QUICK_START_REMINDER
1105
1094
  )
1106
1095
  );
@@ -1109,7 +1098,7 @@ mod tests {
1109
1098
  assert_eq!(
1110
1099
  quickstart_human(&value2),
1111
1100
  format!(
1112
- "队伍已启动;下面是连接方式和派发任务的命令。\n{}",
1101
+ "s\n{}",
1113
1102
  crate::cli::QUICK_START_REMINDER
1114
1103
  )
1115
1104
  );
@@ -1130,7 +1119,7 @@ mod tests {
1130
1119
  "team-agent",
1131
1120
  "send",
1132
1121
  "worker name; echo unsafe",
1133
- "请完成任务并把答案回复给 leader。",
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' '请完成任务并把答案回复给 leader。' --workspace '/tmp/my workspace' --team team-a"
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
- "请完成任务并把答案回复给 leader。",
1198
+ "Complete the task and reply to the leader.",
1210
1199
  "--workspace",
1211
1200
  "/tmp/ws",
1212
1201
  "--team",
@@ -1215,4 +1204,71 @@ 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 ["existing_runtime", "preflight_blocked", "leader_binding_refused"] {
1223
+ let value = json!({
1224
+ "ok": false,
1225
+ "status": status,
1226
+ "reason": "the exact underlying refusal",
1227
+ "summary": "creation refused",
1228
+ "blockers": [{"stage": "identity", "reason": "owner mismatch"}],
1229
+ "next_actions": ["team-agent restart --workspace /tmp/first-launch --team current"],
1230
+ "state_path": "/tmp/first-launch/.team/runtime/state.json",
1231
+ "readiness": {"reason": "underlying readiness evidence"}
1232
+ });
1233
+ let result = finish_quick_start(value.clone(), &quick_start_args(false)).unwrap();
1234
+ assert_eq!(result.exit, ExitCode::Error);
1235
+ let CmdOutput::Human(text) = result.output else { panic!("expected human report") };
1236
+ let rendered: Value = serde_json::from_str(text.strip_prefix("quick-start report:\n").unwrap()).unwrap();
1237
+ assert_eq!(rendered, value, "human projection lost fields for {status}");
1238
+ assert!(!text.contains("team-agent doctor"), "must not replace actual actions");
1239
+ let json_result = finish_quick_start(value.clone(), &quick_start_args(true)).unwrap();
1240
+ assert_eq!(json_result.exit, ExitCode::Error);
1241
+ assert_eq!(json_result.output, CmdOutput::Json(value));
1242
+ }
1243
+ }
1244
+
1245
+ #[test]
1246
+ fn quick_start_human_preserves_original_error_and_actions() {
1247
+ let value = json!({
1248
+ "ok": false,
1249
+ "error": "database initialization failed at /tmp/first-launch/.team/team.db: permission denied",
1250
+ "action": "repair directory permissions",
1251
+ "next_actions": ["inspect the exact database path"]
1252
+ });
1253
+ let result = finish_quick_start(value.clone(), &quick_start_args(false)).unwrap();
1254
+ assert_eq!(result.exit, ExitCode::Error);
1255
+ let CmdOutput::Human(text) = result.output else { panic!("expected human report") };
1256
+ let rendered: Value = serde_json::from_str(text.strip_prefix("quick-start report:\n").unwrap()).unwrap();
1257
+ assert_eq!(rendered, value);
1258
+ }
1259
+
1260
+ #[test]
1261
+ fn pending_tool_load_is_not_reported_as_failed_team_creation() {
1262
+ for as_json in [false, true] {
1263
+ let value = json!({
1264
+ "ok": true,
1265
+ "status": "pending_tool_load",
1266
+ "summary": "Team started; worker tool loading is not yet verified",
1267
+ "readiness": {"ready": false},
1268
+ "next_actions": ["send a task and wait for the actual reply"]
1269
+ });
1270
+ let result = finish_quick_start(value, &quick_start_args(as_json)).unwrap();
1271
+ assert_eq!(result.exit, ExitCode::Ok);
1272
+ }
1273
+ }
1218
1274
  }