@team-agent/installer 0.5.71 → 0.5.73
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 +107 -9
- package/crates/team-agent/src/cli/emit.rs +35 -5
- package/crates/team-agent/src/cli/mod.rs +78 -7
- package/crates/team-agent/src/cli/models.rs +443 -0
- package/crates/team-agent/src/cli/spec.rs +4 -1
- package/crates/team-agent/src/cli/types.rs +8 -0
- package/crates/team-agent/src/compiler.rs +157 -0
- package/crates/team-agent/src/leader/owner_bind.rs +21 -0
- package/crates/team-agent/src/leader/provider_attribution.rs +25 -0
- package/crates/team-agent/src/leader/registry.rs +25 -1
- package/crates/team-agent/src/leader/start.rs +41 -9
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +169 -10
- package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +119 -9
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +859 -12
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +1 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +1 -0
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +168 -0
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +209 -0
- package/crates/team-agent/src/lifecycle/tests/pi_catalog_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/pi_command_red.rs +71 -1
- package/crates/team-agent/src/lifecycle/tests/pi_compiler_red.rs +3 -1
- package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +396 -3
- package/crates/team-agent/src/lifecycle/tests/pi_session_delivery_red.rs +3 -1
- package/crates/team-agent/src/lifecycle/types.rs +8 -0
- package/crates/team-agent/src/provider/adapters/pi.rs +12 -6
- package/npm/check-skill-links.mjs +88 -0
- package/package.json +5 -4
- package/skills/team-agent/SKILL.md +15 -49
- package/skills/team-agent/command-coverage.json +8 -1
- package/skills/team-agent/references/team-agent-operator.md +665 -0
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -102,7 +102,7 @@ pub fn cmd_init(args: &InitArgs) -> Result<CmdResult, CliError> {
|
|
|
102
102
|
|
|
103
103
|
/// `cmd_quick_start`(`commands.py:18`)。`--json` 或 `!ok` → 整 dict;否则 `result["summary"]`。
|
|
104
104
|
pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
105
|
-
let value = lifecycle_port::quick_start(
|
|
105
|
+
let mut value = lifecycle_port::quick_start(
|
|
106
106
|
&args.workspace,
|
|
107
107
|
&args.agents_dir,
|
|
108
108
|
args.name.as_deref(),
|
|
@@ -111,6 +111,7 @@ pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
|
111
111
|
!args.no_display,
|
|
112
112
|
args.backend.as_deref(),
|
|
113
113
|
)?;
|
|
114
|
+
append_send_guidance(&mut value, &args.workspace, args.team_id.as_deref());
|
|
114
115
|
let readiness = value.get("readiness").and_then(Value::as_object);
|
|
115
116
|
let all_resumable_have_session = readiness
|
|
116
117
|
.and_then(|readiness| readiness.get("all_resumable_have_session"))
|
|
@@ -156,18 +157,74 @@ fn quickstart_human(value: &Value) -> String {
|
|
|
156
157
|
.and_then(Value::as_array)
|
|
157
158
|
.map(|items| items.iter().filter_map(Value::as_str).collect())
|
|
158
159
|
.unwrap_or_default();
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
let sends: Vec<&str> = value
|
|
161
|
+
.get("send_commands")
|
|
162
|
+
.and_then(Value::as_array)
|
|
163
|
+
.map(|items| items.iter().filter_map(Value::as_str).collect())
|
|
164
|
+
.unwrap_or_default();
|
|
162
165
|
let mut out = String::from(summary);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
if !attach.is_empty() {
|
|
167
|
+
out.push_str("\n\nattach:");
|
|
168
|
+
for cmd in attach {
|
|
169
|
+
out.push_str("\n ");
|
|
170
|
+
out.push_str(cmd);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if !sends.is_empty() {
|
|
174
|
+
out.push_str("\n\nsend:");
|
|
175
|
+
for cmd in sends {
|
|
176
|
+
out.push_str("\n ");
|
|
177
|
+
out.push_str(cmd);
|
|
178
|
+
}
|
|
167
179
|
}
|
|
168
180
|
append_reminder(out, crate::cli::QUICK_START_REMINDER)
|
|
169
181
|
}
|
|
170
182
|
|
|
183
|
+
fn append_send_guidance(value: &mut Value, workspace: &Path, team: Option<&str>) {
|
|
184
|
+
if value.get("ok").and_then(Value::as_bool) != Some(true) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
let agents = value
|
|
188
|
+
.get("agent_ids")
|
|
189
|
+
.and_then(Value::as_array)
|
|
190
|
+
.cloned()
|
|
191
|
+
.unwrap_or_default();
|
|
192
|
+
let commands: Vec<Value> = agents
|
|
193
|
+
.iter()
|
|
194
|
+
.filter_map(Value::as_str)
|
|
195
|
+
.filter_map(|agent| send_command(agent, workspace, team).map(Value::String))
|
|
196
|
+
.collect();
|
|
197
|
+
if !commands.is_empty() {
|
|
198
|
+
if let Some(object) = value.as_object_mut() {
|
|
199
|
+
object.insert("send_commands".to_string(), Value::Array(commands));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
pub(crate) fn send_command(agent: &str, workspace: &Path, team: Option<&str>) -> Option<String> {
|
|
205
|
+
let workspace = workspace.to_str()?;
|
|
206
|
+
let mut command = format!(
|
|
207
|
+
"team-agent send {} \\\"MESSAGE\\\" --workspace {}",
|
|
208
|
+
shell_quote(agent),
|
|
209
|
+
shell_quote(workspace)
|
|
210
|
+
);
|
|
211
|
+
if let Some(team) = team {
|
|
212
|
+
command.push_str(" --team ");
|
|
213
|
+
command.push_str(&shell_quote(team));
|
|
214
|
+
}
|
|
215
|
+
Some(command)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
fn shell_quote(value: &str) -> String {
|
|
219
|
+
if value.bytes().all(|byte| {
|
|
220
|
+
byte.is_ascii_alphanumeric() || matches!(byte, b'/' | b'.' | b'_' | b'-' | b':')
|
|
221
|
+
}) {
|
|
222
|
+
value.to_string()
|
|
223
|
+
} else {
|
|
224
|
+
format!("'{}'", value.replace('\'', "'\\''"))
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
171
228
|
fn append_reminder(text: String, reminder: &str) -> String {
|
|
172
229
|
if text.is_empty() {
|
|
173
230
|
reminder.to_string()
|
|
@@ -1550,8 +1607,9 @@ pub fn cmd_doctor(args: &DoctorArgs) -> Result<CmdResult, CliError> {
|
|
|
1550
1607
|
mod tests {
|
|
1551
1608
|
#![allow(clippy::unwrap_used)]
|
|
1552
1609
|
|
|
1553
|
-
use super::{agent_pane_id, quickstart_human};
|
|
1610
|
+
use super::{agent_pane_id, append_send_guidance, quickstart_human, send_command};
|
|
1554
1611
|
use serde_json::json;
|
|
1612
|
+
use std::path::{Path, PathBuf};
|
|
1555
1613
|
|
|
1556
1614
|
// E13:happy 人类输出必须带 attach 块(此前 else 分支只打 summary 丢 attach_commands)。
|
|
1557
1615
|
#[test]
|
|
@@ -1594,6 +1652,46 @@ mod tests {
|
|
|
1594
1652
|
);
|
|
1595
1653
|
}
|
|
1596
1654
|
|
|
1655
|
+
#[test]
|
|
1656
|
+
fn send_guidance_is_copyable_and_preserves_explicit_scope() {
|
|
1657
|
+
let command = send_command("worker name; echo unsafe", Path::new("/tmp/my workspace"), Some("team-a"))
|
|
1658
|
+
.unwrap();
|
|
1659
|
+
assert_eq!(
|
|
1660
|
+
command,
|
|
1661
|
+
"team-agent send 'worker name; echo unsafe' \\\"MESSAGE\\\" --workspace '/tmp/my workspace' --team team-a"
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
#[cfg(unix)]
|
|
1666
|
+
#[test]
|
|
1667
|
+
fn non_utf8_workspace_fails_closed_without_send_guidance() {
|
|
1668
|
+
use std::ffi::OsString;
|
|
1669
|
+
use std::os::unix::ffi::OsStringExt;
|
|
1670
|
+
|
|
1671
|
+
let workspace = PathBuf::from(OsString::from_vec(b"/tmp/workspace-\xff".to_vec()));
|
|
1672
|
+
assert!(send_command("worker", &workspace, None).is_none());
|
|
1673
|
+
let mut value = json!({"ok": true, "agent_ids": ["worker"]});
|
|
1674
|
+
append_send_guidance(&mut value, &workspace, None);
|
|
1675
|
+
assert!(value.get("send_commands").is_none());
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
#[test]
|
|
1679
|
+
fn quick_start_guidance_lists_every_known_agent_without_guessing() {
|
|
1680
|
+
let mut value = json!({"ok": true, "agent_ids": ["sol", "luna"]});
|
|
1681
|
+
append_send_guidance(&mut value, Path::new("/tmp/ws"), Some("team"));
|
|
1682
|
+
let commands = value.get("send_commands").and_then(|v| v.as_array()).unwrap();
|
|
1683
|
+
assert_eq!(commands.len(), 2);
|
|
1684
|
+
assert!(commands[0].as_str().unwrap().contains("send sol"));
|
|
1685
|
+
assert!(commands[1].as_str().unwrap().contains("send luna"));
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
#[test]
|
|
1689
|
+
fn error_results_do_not_get_send_guidance() {
|
|
1690
|
+
let mut value = json!({"ok": false, "agent_ids": ["worker"]});
|
|
1691
|
+
append_send_guidance(&mut value, Path::new("/tmp/ws"), None);
|
|
1692
|
+
assert!(value.get("send_commands").is_none());
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1597
1695
|
#[test]
|
|
1598
1696
|
fn agent_pane_id_resolves_session_window_even_with_recorded_pane_id() {
|
|
1599
1697
|
let state = json!({
|
|
@@ -126,6 +126,15 @@ fn write_stdout_line(text: &str) -> std::io::Result<()> {
|
|
|
126
126
|
stdout.write_all(b"\n")
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
#[cfg(test)]
|
|
130
|
+
pub(crate) fn __test_dispatch(
|
|
131
|
+
command: &str,
|
|
132
|
+
args: &[String],
|
|
133
|
+
cwd: &Path,
|
|
134
|
+
) -> Result<ExitCode, CliError> {
|
|
135
|
+
dispatch(command, args, cwd)
|
|
136
|
+
}
|
|
137
|
+
|
|
129
138
|
fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliError> {
|
|
130
139
|
let Some(spec) = command_spec(command) else {
|
|
131
140
|
return Ok(emit_unknown_subcommand_usage(command));
|
|
@@ -188,6 +197,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
188
197
|
// (workspace, team_key) tuple and delegates to the E6 named-leader
|
|
189
198
|
// delivery path — no separate route authority.
|
|
190
199
|
"leaders" => cmd_leaders(&leaders_args(args, cwd)).map(emit_result),
|
|
200
|
+
"models" => cmd_models(&models_args(args)?).map(emit_result),
|
|
191
201
|
"validate" => cmd_validate(&validate_args(args, cwd)).map(emit_result),
|
|
192
202
|
"install-skill" => cmd_install_skill(&install_skill_args(args)?).map(emit_result),
|
|
193
203
|
"profile" => cmd_profile(&profile_args(args, cwd)?).map(emit_result),
|
|
@@ -239,6 +249,7 @@ const DISPATCH_COMMANDS: &[&str] = &[
|
|
|
239
249
|
"sessions",
|
|
240
250
|
// 0.5.9 E7: host leader discovery command surface.
|
|
241
251
|
"leaders",
|
|
252
|
+
"models",
|
|
242
253
|
"validate",
|
|
243
254
|
"install-skill",
|
|
244
255
|
"profile",
|
|
@@ -291,7 +302,7 @@ pub(crate) fn default_help() -> String {
|
|
|
291
302
|
append_help_section(
|
|
292
303
|
&mut out,
|
|
293
304
|
"Core",
|
|
294
|
-
&["quick-start", "send", "status", "collect", "results"],
|
|
305
|
+
&["quick-start", "send", "status", "collect", "results", "models"],
|
|
295
306
|
);
|
|
296
307
|
append_help_section(
|
|
297
308
|
&mut out,
|
|
@@ -311,7 +322,8 @@ pub(crate) fn default_help() -> String {
|
|
|
311
322
|
"Guided recovery",
|
|
312
323
|
&["claim-leader", "takeover", "attach-leader"],
|
|
313
324
|
);
|
|
314
|
-
out.push_str("\
|
|
325
|
+
out.push_str("\nWorkflow: `team-agent models --provider pi`, then quick-start/add-agent/start-agent, then `team-agent send AGENT MESSAGE`.\n");
|
|
326
|
+
out.push_str("Provider launchers:\n team-agent codex|claude|copilot|grok|cursor|pi ...\n");
|
|
315
327
|
out.push_str("\nRun `team-agent <command> --help` for command flags.");
|
|
316
328
|
out
|
|
317
329
|
}
|
|
@@ -362,7 +374,7 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
362
374
|
match command {
|
|
363
375
|
None => default_help(),
|
|
364
376
|
Some("init") => compat_hidden_help("init", "usage: team-agent init [--workspace WORKSPACE] [--force] [--json]"),
|
|
365
|
-
Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--no-display] [--backend tmux|conpty] [--json]\n\ndefaults: display_backend=adaptive; set display_backend: none in TEAM.md or pass --no-display to use one worker window per agent.\n\n--backend selects the worker transport (Phase 1d Batch 2): tmux (default on POSIX; unchanged behavior), conpty (Windows-native ConPTY worker transport; requires the shim binary and Windows host)
|
|
377
|
+
Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--no-display] [--backend tmux|conpty] [--json]\n\ndefaults: display_backend=adaptive; set display_backend: none in TEAM.md or pass --no-display to use one worker window per agent.\n\n--backend selects the worker transport (Phase 1d Batch 2): tmux (default on POSIX; unchanged behavior), conpty (Windows-native ConPTY worker transport; requires the shim binary and Windows host).\n\nAfter a successful start, use the returned `send_commands` (or choose an agent explicitly) with `team-agent send AGENT MESSAGE`.".to_string(),
|
|
366
378
|
Some("start") => compat_hidden_help("start", "usage: team-agent start [TEAMDIR] [--yes] [--fresh] [--json]"),
|
|
367
379
|
Some("compile") => "usage: team-agent compile --team TEAM [--out FILE] [--json]".to_string(),
|
|
368
380
|
Some("send") => concat!(
|
|
@@ -376,14 +388,15 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
376
388
|
.to_string(),
|
|
377
389
|
Some("allow-peer-talk") => "usage: team-agent allow-peer-talk A B [--workspace WORKSPACE] [--json]".to_string(),
|
|
378
390
|
Some("status") => "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]\n\n默认输出: worker,空闲|工作|错误;错误细分走 status --summary".to_string(),
|
|
391
|
+
Some("models") => "usage: team-agent models --provider pi [--search TEXT] [--json]\n\nPrints models.v1 exact role_model entries; each entry includes current=true|false. Catalog readiness is reported as auth=ok|not_ready with auth_basis=catalog_visibility.".to_string(),
|
|
379
392
|
Some("stop") => compat_hidden_help("stop", "usage: team-agent stop [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]"),
|
|
380
393
|
Some("shutdown") => "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
|
|
381
394
|
Some("restart") => "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json]".to_string(),
|
|
382
395
|
Some("restart-agent") => compat_hidden_help("restart-agent", "usage: team-agent restart-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--no-display] [--json]"),
|
|
383
396
|
Some("reset-agent") => "usage: team-agent reset-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--no-display] [--json]".to_string(),
|
|
384
|
-
Some("start-agent") => "usage: team-agent start-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--force] [--allow-fresh] [--no-display] [--json]".to_string(),
|
|
397
|
+
Some("start-agent") => "usage: team-agent start-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--force] [--allow-fresh] [--no-display] [--json]\n\nAfter a successful start, use the returned `send_commands` with `team-agent send AGENT MESSAGE`.".to_string(),
|
|
385
398
|
Some("stop-agent") => "usage: team-agent stop-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
386
|
-
Some("add-agent") => "usage: team-agent add-agent AGENT --role-file FILE [--force] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]".to_string(),
|
|
399
|
+
Some("add-agent") => "usage: team-agent add-agent AGENT --role-file FILE [--force] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]\n\nAfter a successful add, use the returned `send_commands` with `team-agent send AGENT MESSAGE`.".to_string(),
|
|
387
400
|
Some("clone-agent") => "usage: team-agent clone-agent SOURCE_AGENT --as AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]".to_string(),
|
|
388
401
|
Some("fork-agent") => "usage: team-agent fork-agent SOURCE_AGENT --as AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]".to_string(),
|
|
389
402
|
Some("remove-agent") => "usage: team-agent remove-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--from-spec] [--confirm] [--force] [--json]".to_string(),
|
|
@@ -1054,6 +1067,23 @@ fn quick_start_args(args: &[String], cwd: &Path) -> Result<QuickStartArgs, CliEr
|
|
|
1054
1067
|
})
|
|
1055
1068
|
}
|
|
1056
1069
|
|
|
1070
|
+
fn models_args(args: &[String]) -> Result<ModelsArgs, CliError> {
|
|
1071
|
+
let parsed = parse_args(args);
|
|
1072
|
+
let provider = parsed
|
|
1073
|
+
.provider
|
|
1074
|
+
.ok_or_else(|| CliError::Usage("models requires --provider pi".to_string()))?;
|
|
1075
|
+
if provider != "pi" {
|
|
1076
|
+
return Err(CliError::Usage(format!(
|
|
1077
|
+
"models supports only --provider pi, got {provider:?}"
|
|
1078
|
+
)));
|
|
1079
|
+
}
|
|
1080
|
+
Ok(ModelsArgs {
|
|
1081
|
+
provider,
|
|
1082
|
+
search: parsed.search,
|
|
1083
|
+
json: parsed.json,
|
|
1084
|
+
})
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1057
1087
|
fn init_args(args: &[String], cwd: &Path) -> InitArgs {
|
|
1058
1088
|
let parsed = parse_args(args);
|
|
1059
1089
|
InitArgs {
|
|
@@ -60,6 +60,7 @@ pub mod emit;
|
|
|
60
60
|
pub mod helpers;
|
|
61
61
|
pub mod leader;
|
|
62
62
|
pub mod leaders;
|
|
63
|
+
pub mod models;
|
|
63
64
|
pub mod named_address;
|
|
64
65
|
pub mod profile;
|
|
65
66
|
pub mod send;
|
|
@@ -73,6 +74,7 @@ pub use diagnose::*;
|
|
|
73
74
|
pub use emit::*;
|
|
74
75
|
pub use leader::*;
|
|
75
76
|
pub use leaders::*;
|
|
77
|
+
pub use models::*;
|
|
76
78
|
pub use named_address::*;
|
|
77
79
|
pub use profile::*;
|
|
78
80
|
pub use send::*;
|
|
@@ -2641,9 +2643,48 @@ pub mod lifecycle_port {
|
|
|
2641
2643
|
allow_fresh,
|
|
2642
2644
|
team,
|
|
2643
2645
|
) {
|
|
2644
|
-
Ok(
|
|
2645
|
-
|
|
2646
|
+
Ok(crate::lifecycle::StartAgentOutcome::Running {
|
|
2647
|
+
env,
|
|
2648
|
+
start_mode,
|
|
2649
|
+
target,
|
|
2650
|
+
session_id,
|
|
2651
|
+
new_session_id,
|
|
2652
|
+
rollout_path,
|
|
2653
|
+
}) => Ok(agent_action_value(
|
|
2654
|
+
agent,
|
|
2655
|
+
workspace,
|
|
2656
|
+
team,
|
|
2657
|
+
json!({
|
|
2658
|
+
"ok": true,
|
|
2659
|
+
"agent_id": env.agent_id.as_str(),
|
|
2660
|
+
"status": "running",
|
|
2661
|
+
"start_mode": start_mode,
|
|
2662
|
+
"target": target,
|
|
2663
|
+
"session_id": session_id.as_ref().map(|id| id.as_str()),
|
|
2664
|
+
"new_session_id": new_session_id.as_ref().map(|id| id.as_str()),
|
|
2665
|
+
"rollout_path": rollout_path
|
|
2666
|
+
.as_ref()
|
|
2667
|
+
.map(|path| path.as_path().to_string_lossy().to_string()),
|
|
2668
|
+
"coordinator_started": env.coordinator_started,
|
|
2669
|
+
"state_file": env.state_file.to_string_lossy(),
|
|
2670
|
+
}),
|
|
2671
|
+
)),
|
|
2672
|
+
Ok(crate::lifecycle::StartAgentOutcome::Noop { env, target }) => {
|
|
2673
|
+
Ok(agent_action_value(agent, workspace, team, json!({
|
|
2674
|
+
"ok": true,
|
|
2675
|
+
"agent_id": env.agent_id.as_str(),
|
|
2676
|
+
"status": "already_running",
|
|
2677
|
+
"target": target,
|
|
2678
|
+
"coordinator_started": env.coordinator_started,
|
|
2679
|
+
"state_file": env.state_file.to_string_lossy(),
|
|
2680
|
+
})))
|
|
2646
2681
|
}
|
|
2682
|
+
Ok(crate::lifecycle::StartAgentOutcome::Paused { agent_id }) => Ok(json!({
|
|
2683
|
+
"ok": false,
|
|
2684
|
+
"agent_id": agent_id.as_str(),
|
|
2685
|
+
"status": "paused",
|
|
2686
|
+
"reason": "agent_paused",
|
|
2687
|
+
})),
|
|
2647
2688
|
Err(e) => Ok(error_value(e)),
|
|
2648
2689
|
}
|
|
2649
2690
|
}
|
|
@@ -2730,14 +2771,34 @@ pub mod lifecycle_port {
|
|
|
2730
2771
|
team,
|
|
2731
2772
|
force,
|
|
2732
2773
|
) {
|
|
2733
|
-
Ok(report) => Ok(
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2774
|
+
Ok(report) => Ok(agent_action_value(
|
|
2775
|
+
agent,
|
|
2776
|
+
workspace,
|
|
2777
|
+
team,
|
|
2778
|
+
json!({
|
|
2779
|
+
"ok": true,
|
|
2780
|
+
"agent_id": report.env.agent_id.as_str(),
|
|
2781
|
+
"status": "running",
|
|
2782
|
+
"start_mode": report.start_mode,
|
|
2783
|
+
"role_file": report.role_file.to_string_lossy(),
|
|
2784
|
+
"coordinator_started": report.env.coordinator_started,
|
|
2785
|
+
"state_file": report.env.state_file.to_string_lossy(),
|
|
2786
|
+
}),
|
|
2787
|
+
)),
|
|
2738
2788
|
Err(e) => Ok(error_value(e)),
|
|
2739
2789
|
}
|
|
2740
2790
|
}
|
|
2791
|
+
fn agent_action_value(agent: &str, workspace: &Path, team: Option<&str>, mut value: Value) -> Value {
|
|
2792
|
+
if value.get("ok").and_then(Value::as_bool) == Some(true) {
|
|
2793
|
+
if let Some(object) = value.as_object_mut() {
|
|
2794
|
+
if let Some(command) = super::adapters::send_command(agent, workspace, team) {
|
|
2795
|
+
object.insert("send_commands".to_string(), json!([command]));
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
value
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2741
2802
|
///
|
|
2742
2803
|
/// `runtime.fork_agent`(`cmd_fork_agent`;`--as` 必需)。
|
|
2743
2804
|
pub fn fork_agent(
|
|
@@ -3014,6 +3075,15 @@ pub mod lifecycle_port {
|
|
|
3014
3075
|
}
|
|
3015
3076
|
|
|
3016
3077
|
fn error_value(error: crate::lifecycle::LifecycleError) -> Value {
|
|
3078
|
+
if let crate::lifecycle::LifecycleError::PiModelPreflight {
|
|
3079
|
+
requested,
|
|
3080
|
+
candidates,
|
|
3081
|
+
action,
|
|
3082
|
+
not_ready,
|
|
3083
|
+
} = error
|
|
3084
|
+
{
|
|
3085
|
+
return json!({"ok": false, "error": "pi model preflight failed", "pi": {"requested_model": requested, "candidates": candidates, "not_ready": not_ready, "action": action.clone(), "next_action": action}});
|
|
3086
|
+
}
|
|
3017
3087
|
let message = error.to_string();
|
|
3018
3088
|
let mut payload = json!({"ok": false, "error": message});
|
|
3019
3089
|
if let Some(next_action) = error_next_action(&message) {
|
|
@@ -3295,6 +3365,7 @@ pub mod lifecycle_port {
|
|
|
3295
3365
|
"reason": readiness_json.get("reason").cloned().unwrap_or(Value::Null),
|
|
3296
3366
|
"ready": readiness_json.get("ready").cloned().unwrap_or(Value::Bool(false)),
|
|
3297
3367
|
"session_name": session_name.as_str(),
|
|
3368
|
+
"agent_ids": launch.started.iter().map(|agent| agent.agent_id.as_str()).collect::<Vec<_>>(),
|
|
3298
3369
|
"dry_run": launch.dry_run,
|
|
3299
3370
|
"display_backend": display_backend,
|
|
3300
3371
|
"next_actions": next_actions,
|