@team-agent/installer 0.5.109 → 0.5.110
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/README.md +33 -0
- package/README.zh.md +30 -0
- package/crates/team-agent/src/cli/emit.rs +8 -1
- package/crates/team-agent/src/cli/mod.rs +2 -1
- package/crates/team-agent/src/cli/route.rs +207 -0
- package/crates/team-agent/src/cli/spec.rs +3 -0
- package/crates/team-agent/src/leader/start.rs +5 -1
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +2 -0
- package/crates/team-agent/src/lifecycle/tests/cli_argv_routing_red.rs +1457 -0
- package/crates/team-agent/src/lifecycle/tests.rs +1 -0
- package/crates/team-agent/src/provider/argv_route.rs +409 -0
- package/crates/team-agent/src/provider/mod.rs +3 -2
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +32 -0
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -66,6 +66,39 @@ The lead and each teammate independently choose which CLI to use:
|
|
|
66
66
|
|
|
67
67
|
In other words: let Claude be the lead for planning and review, let Codex teammates handle bulk implementation. Mix and match whatever subscriptions you have.
|
|
68
68
|
|
|
69
|
+
## Native CLI argv routing (opt-in)
|
|
70
|
+
|
|
71
|
+
Configure literal startup arguments globally, separately for each provider, from any directory:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
team-agent route set pi -- --mode rpc
|
|
75
|
+
team-agent route enable
|
|
76
|
+
team-agent route status --json
|
|
77
|
+
team-agent route show pi --json
|
|
78
|
+
team-agent route add pi -- --verbose
|
|
79
|
+
team-agent route clear pi
|
|
80
|
+
team-agent route disable
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The default is OFF. `~/.team-agent/argv-routing.json` stores the switch and mappings;
|
|
84
|
+
`TEAM_AGENT_CLI_ARGV_ROUTING=1|true|on` or `0|false|off` overrides the persisted switch
|
|
85
|
+
(case-insensitive, surrounding whitespace accepted). Invalid overrides safely disable routing.
|
|
86
|
+
`enable`/`disable` preserve mappings; `set` replaces, `add` appends, and `clear` removes one mapping.
|
|
87
|
+
Commands report both persisted and effective states, including an active environment override.
|
|
88
|
+
|
|
89
|
+
Provider keys: `claude`, `codex`, `copilot`, `gemini_cli`, `grok`, `cursor_agent`, `pi`.
|
|
90
|
+
Claude/Claude Code share one mapping; `claude_code`/`claude-code` and `agent`/`cursor` are accepted aliases.
|
|
91
|
+
Everything after the first `--` is literal argv data, including `--help`, `--json`, empty strings
|
|
92
|
+
and `{workspace}`; no shell expansion is performed. Arguments are inserted immediately after
|
|
93
|
+
the native executable, before its original arguments. Do not store secrets or override framework-managed
|
|
94
|
+
session/MCP/permission arguments; routing does not bypass existing identity or authentication checks.
|
|
95
|
+
|
|
96
|
+
Only the next actual native Leader/worker launch uses the current mapping, including restart,
|
|
97
|
+
start/reset/add/clone, Pi new-seat fork and remove rollback. Running/attached processes, dry runs,
|
|
98
|
+
model/version/auth probes and other helper processes are unchanged. Enabled invalid configuration
|
|
99
|
+
fails before native spawn; `TEAM_AGENT_CLI_ARGV_ROUTING=off` is an emergency override, not a file repair.
|
|
100
|
+
Pi `--mode rpc` is passed through as startup argv; it does **not** create an RPC host or change Team Agent's transport.
|
|
101
|
+
|
|
69
102
|
## FAQ
|
|
70
103
|
|
|
71
104
|
**What do I need?**
|
package/README.zh.md
CHANGED
|
@@ -66,6 +66,36 @@ team-agent claude
|
|
|
66
66
|
|
|
67
67
|
也就是说:你可以让 Claude 当队长出方案、做评审,让 Codex 队友批量干实现。你手上有哪几家的订阅,就能怎么搭着用。
|
|
68
68
|
|
|
69
|
+
## 原生 CLI 参数路由(默认关闭)
|
|
70
|
+
|
|
71
|
+
在任意目录按 Provider 全局配置字面启动参数,无需工作区或已登录的 CLI:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
team-agent route set pi -- --mode rpc
|
|
75
|
+
team-agent route enable
|
|
76
|
+
team-agent route status --json
|
|
77
|
+
team-agent route show pi --json
|
|
78
|
+
team-agent route add pi -- --verbose
|
|
79
|
+
team-agent route clear pi
|
|
80
|
+
team-agent route disable
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
默认 OFF;开关与映射保存于 `~/.team-agent/argv-routing.json`。
|
|
84
|
+
环境变量 `TEAM_AGENT_CLI_ARGV_ROUTING=1|true|on` 或 `0|false|off` 优先于持久化开关
|
|
85
|
+
(忽略大小写和两侧空白),无效值安全关闭。`enable/disable` 不清映射,`set` 精确替换、
|
|
86
|
+
`add` 按序追加、`clear` 删除一个映射。结果同时报告持久化态、实际有效态与环境覆盖。
|
|
87
|
+
|
|
88
|
+
Provider 键为 `claude`、`codex`、`copilot`、`gemini_cli`、`grok`、`cursor_agent`、`pi`。
|
|
89
|
+
Claude/Claude Code 共用映射;支持 `claude_code/claude-code` 和 `agent/cursor` 别名。
|
|
90
|
+
第一个 `--` 后全部是字面 argv,包括 `--help`、`--json`、空字符串、`{workspace}`,不会二次 shell 展开。
|
|
91
|
+
注入位置为原生 executable 之后、原有参数之前。不要保存密码/token,或覆盖框架管理的
|
|
92
|
+
session/MCP/权限参数;既有身份、认证与安全检查不变。
|
|
93
|
+
|
|
94
|
+
下一次真实 Leader/worker 启动读取当前配置,包括 restart、start/reset/add/clone、Pi 新席 fork
|
|
95
|
+
及 remove 失败回滚复活。运行中或仅 attach 的进程、dry-run、model/version/auth 探测与其他辅助进程不变。
|
|
96
|
+
已开启但无效的配置会在原生 spawn 前报错;可用 `TEAM_AGENT_CLI_ARGV_ROUTING=off` 紧急停用,
|
|
97
|
+
但这不会修复坏文件。Pi `--mode rpc` 只是启动参数,不会自动建立 RPC 宿主或改变 Team Agent 的传输。
|
|
98
|
+
|
|
69
99
|
## 常见问题
|
|
70
100
|
|
|
71
101
|
**需要什么前提?**
|
|
@@ -56,6 +56,9 @@ pub fn run(argv: &[String], cwd: &Path) -> ExitCode {
|
|
|
56
56
|
let Some(command) = argv.first().map(String::as_str) else {
|
|
57
57
|
return emit_missing_subcommand_usage();
|
|
58
58
|
};
|
|
59
|
+
if command == "route" {
|
|
60
|
+
return super::route::run(&argv[1..]);
|
|
61
|
+
}
|
|
59
62
|
if is_leader_passthrough_command(command) {
|
|
60
63
|
return match cmd_leader_passthrough(command, &argv[1..], cwd) {
|
|
61
64
|
Ok(result) => emit_result(result),
|
|
@@ -115,7 +118,7 @@ fn emit_status_cli_error(error: &CliError) -> ExitCode {
|
|
|
115
118
|
|
|
116
119
|
/// Print a handler's CmdResult to stdout (emit formats json/human), then surface its exit code.
|
|
117
120
|
/// (parser.py: `print(emit(result, as_json))` then the ok→exit mapping.)
|
|
118
|
-
fn emit_result(r: CmdResult) -> ExitCode {
|
|
121
|
+
pub(super) fn emit_result(r: CmdResult) -> ExitCode {
|
|
119
122
|
let persisted_message_id = match &r.output {
|
|
120
123
|
CmdOutput::Json(value) => value
|
|
121
124
|
.get("message_id")
|
|
@@ -160,6 +163,9 @@ pub(crate) fn __test_dispatch(
|
|
|
160
163
|
}
|
|
161
164
|
|
|
162
165
|
fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliError> {
|
|
166
|
+
if command == "route" {
|
|
167
|
+
return Ok(super::route::run(args));
|
|
168
|
+
}
|
|
163
169
|
// Keep the removed flag harmless for older scripts and persisted command
|
|
164
170
|
// lines; the default tmux backend is already the only runtime path.
|
|
165
171
|
let filtered_args: Vec<String> = args
|
|
@@ -341,6 +347,7 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
341
347
|
.to_string(),
|
|
342
348
|
Some("allow-peer-talk") => "usage: team-agent allow-peer-talk A B [--workspace WORKSPACE] [--json]".to_string(),
|
|
343
349
|
Some("status") => "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]\n\n输出九字段:name/provider/model/effort/runtime_status/activity/health/session_name/tmux_command;人读与 --json 使用同一投影字段(多节点人读紧凑显示 tmux_command)。--json 始终保留每节点完整可复制的 tmux_command;人读单节点保留完整旧格式,多节点按(socket, tmux session)显示每组一次共享 attach 模板,ATTACH列为<group>:<window>.<pane>,模板中的 <target> 替换为其 window.pane 部分。model/effort 为已接受启动配置,未设置为 null,不猜 provider 默认值;缺少可靠定位或原生 tmux/process 采样时显示 unknown,不推导attach信息。--summary/--detail 仅保留兼容性,不增加诊断字段。".to_string(),
|
|
350
|
+
Some("route") => super::route::HELP.to_string(),
|
|
344
351
|
Some("models") => "usage: team-agent models [--provider pi|cursor_agent|codex|claude|claude_code|grok] [QUERY|--search TEXT] [--json]\n\nLists exact provider model ids with case-insensitive multi-word search across provider, vendor, id, display name, and source aliases. Uses each provider's native catalog; Cursor uses `agent --list-models`, Codex uses `codex debug models`, Claude uses SDK stream-json initialize, and Grok uses the PATH-first `grok models` command.".to_string(),
|
|
345
352
|
Some("leaders") => "usage: team-agent leaders [QUERY|--search TEXT] [--all|--stale] [--json] | --prune [--dry-run] [--json]\n\nLists LIVE leaders by default. Use --all to include retained STALE entries, --stale to inspect only STALE entries, QUERY or --search TEXT to match workspace/team/name fields, and --prune to remove only entries proven terminal by canonical state. --dry-run is valid only with --prune.".to_string(),
|
|
346
353
|
Some("shutdown") => "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
|
|
@@ -58,11 +58,12 @@ pub mod attach_app_server_leader;
|
|
|
58
58
|
pub mod diagnose;
|
|
59
59
|
pub mod emit;
|
|
60
60
|
pub mod helpers;
|
|
61
|
-
pub mod models;
|
|
62
61
|
pub mod leader;
|
|
63
62
|
pub mod leaders;
|
|
63
|
+
pub mod models;
|
|
64
64
|
pub mod named_address;
|
|
65
65
|
pub mod profile;
|
|
66
|
+
pub(crate) mod route;
|
|
66
67
|
pub mod send;
|
|
67
68
|
pub(crate) mod spec;
|
|
68
69
|
pub mod status;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
//! Host-global route management: raw argv, no workspace selection or error log.
|
|
2
|
+
|
|
3
|
+
use super::{CmdOutput, CmdResult, ExitCode};
|
|
4
|
+
use crate::provider::argv_route::{self, Config, Mutation, Override, RouteError};
|
|
5
|
+
use crate::provider::Provider;
|
|
6
|
+
use serde_json::{json, Value};
|
|
7
|
+
|
|
8
|
+
pub(crate) const HELP: &str = "usage: team-agent route [status] [--json]\n team-agent route enable|disable [--json]\n team-agent route show [PROVIDER] [--json]\n team-agent route set|add PROVIDER [--json] -- ARG [ARG ...]\n team-agent route clear PROVIDER [--json]\n\nGlobal config: ~/.team-agent/argv-routing.json (default OFF).\nTEAM_AGENT_CLI_ARGV_ROUTING overrides the persisted switch.\nEverything after -- is literal argv data; routing affects the next native Agent launch only.";
|
|
9
|
+
|
|
10
|
+
struct Request {
|
|
11
|
+
operation: String,
|
|
12
|
+
provider: Option<Provider>,
|
|
13
|
+
tokens: Vec<String>,
|
|
14
|
+
json: bool,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn usage() -> RouteError {
|
|
18
|
+
RouteError {
|
|
19
|
+
error: "invalid route arguments".to_string(),
|
|
20
|
+
reason: "argv_route_usage",
|
|
21
|
+
action: "run `team-agent route --help`; set/add require PROVIDER -- ARG [ARG ...]"
|
|
22
|
+
.to_string(),
|
|
23
|
+
config_path: None,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fn parse(args: &[String], delimiter: usize) -> Result<Request, RouteError> {
|
|
28
|
+
let mut json = false;
|
|
29
|
+
let mut positionals = Vec::new();
|
|
30
|
+
for token in &args[..delimiter] {
|
|
31
|
+
if token == "--json" {
|
|
32
|
+
if json {
|
|
33
|
+
return Err(usage());
|
|
34
|
+
}
|
|
35
|
+
json = true;
|
|
36
|
+
} else if token.starts_with('-') {
|
|
37
|
+
return Err(usage());
|
|
38
|
+
} else {
|
|
39
|
+
positionals.push(token.as_str());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let operation = positionals.first().copied().unwrap_or("status");
|
|
43
|
+
let provider_required = matches!(operation, "set" | "add" | "clear");
|
|
44
|
+
let provider_allowed = provider_required || operation == "show";
|
|
45
|
+
if !matches!(
|
|
46
|
+
operation,
|
|
47
|
+
"status" | "enable" | "disable" | "show" | "set" | "add" | "clear"
|
|
48
|
+
) || positionals.len() > if provider_allowed { 2 } else { 1 }
|
|
49
|
+
|| (provider_required && positionals.len() != 2)
|
|
50
|
+
{
|
|
51
|
+
return Err(usage());
|
|
52
|
+
}
|
|
53
|
+
let provider = positionals
|
|
54
|
+
.get(1)
|
|
55
|
+
.map(|raw| argv_route::parse_route_provider(raw).ok_or_else(usage))
|
|
56
|
+
.transpose()?;
|
|
57
|
+
let tokens = if matches!(operation, "set" | "add") {
|
|
58
|
+
if delimiter == args.len() || delimiter + 1 == args.len() {
|
|
59
|
+
return Err(usage());
|
|
60
|
+
}
|
|
61
|
+
let tokens = args[delimiter + 1..].to_vec();
|
|
62
|
+
if !argv_route::valid_tokens(&tokens) {
|
|
63
|
+
return Err(usage());
|
|
64
|
+
}
|
|
65
|
+
tokens
|
|
66
|
+
} else {
|
|
67
|
+
if delimiter != args.len() {
|
|
68
|
+
return Err(usage());
|
|
69
|
+
}
|
|
70
|
+
Vec::new()
|
|
71
|
+
};
|
|
72
|
+
Ok(Request {
|
|
73
|
+
operation: operation.to_string(),
|
|
74
|
+
provider,
|
|
75
|
+
tokens,
|
|
76
|
+
json,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn switch_fields(
|
|
81
|
+
path: Option<&std::path::Path>,
|
|
82
|
+
persisted: Option<bool>,
|
|
83
|
+
present: bool,
|
|
84
|
+
override_state: Override,
|
|
85
|
+
) -> Value {
|
|
86
|
+
json!({
|
|
87
|
+
"config_path": path,
|
|
88
|
+
"persisted_enabled": persisted,
|
|
89
|
+
"effective_enabled": override_state.effective(persisted.unwrap_or(false)),
|
|
90
|
+
"enabled_source": override_state.source(present),
|
|
91
|
+
"override_status": override_state.status(),
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fn execute(request: &Request) -> Result<Value, RouteError> {
|
|
96
|
+
let path = argv_route::config_path()?;
|
|
97
|
+
let (config, present) = match request.operation.as_str() {
|
|
98
|
+
"enable" => argv_route::mutate(&path, Mutation::Enable(true))?,
|
|
99
|
+
"disable" => argv_route::mutate(&path, Mutation::Enable(false))?,
|
|
100
|
+
"set" | "add" | "clear" => {
|
|
101
|
+
let provider = request.provider.ok_or_else(usage)?;
|
|
102
|
+
let mutation = match request.operation.as_str() {
|
|
103
|
+
"set" => Mutation::Set(provider, request.tokens.clone()),
|
|
104
|
+
"add" => Mutation::Add(provider, request.tokens.clone()),
|
|
105
|
+
_ => Mutation::Clear(provider),
|
|
106
|
+
};
|
|
107
|
+
argv_route::mutate(&path, mutation)?
|
|
108
|
+
}
|
|
109
|
+
_ => argv_route::read_config(&path)?,
|
|
110
|
+
};
|
|
111
|
+
let override_state = Override::current();
|
|
112
|
+
let mut value = switch_fields(Some(&path), Some(config.enabled), present, override_state);
|
|
113
|
+
value["ok"] = json!(true);
|
|
114
|
+
value["operation"] = json!(request.operation);
|
|
115
|
+
if let Some(provider) = request.provider {
|
|
116
|
+
add_provider_fields(&mut value, &config, provider);
|
|
117
|
+
} else if request.operation == "show" {
|
|
118
|
+
value["providers"] = json!(config.providers);
|
|
119
|
+
}
|
|
120
|
+
if override_state != Override::Unset {
|
|
121
|
+
value["notice"] = json!(format!(
|
|
122
|
+
"effective switch is overridden by {} ({})",
|
|
123
|
+
argv_route::ENV_NAME,
|
|
124
|
+
override_state.status()
|
|
125
|
+
));
|
|
126
|
+
}
|
|
127
|
+
Ok(value)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fn add_provider_fields(value: &mut Value, config: &Config, provider: Provider) {
|
|
131
|
+
if let Some(key) = argv_route::route_key(provider) {
|
|
132
|
+
value["provider"] = json!(key);
|
|
133
|
+
value["configured"] = json!(config.providers.contains_key(key));
|
|
134
|
+
value["argv"] = json!(config.providers.get(key).cloned().unwrap_or_default());
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/// Route-only error renderer: reuse redaction/formatting, never cwd log policy.
|
|
139
|
+
fn emit_error(error: RouteError, args: &[String], delimiter: usize, as_json: bool) -> ExitCode {
|
|
140
|
+
let is_usage = error.reason == "argv_route_usage";
|
|
141
|
+
let mut value = json!({
|
|
142
|
+
"ok": false, "error": error.error, "reason": error.reason,
|
|
143
|
+
"action": error.action, "config_path": error.config_path,
|
|
144
|
+
});
|
|
145
|
+
if !is_usage {
|
|
146
|
+
// Best-effort switch observation even when strict management read fails.
|
|
147
|
+
// No config content or invalid argv is included in the error channel.
|
|
148
|
+
let path = error.config_path.as_deref();
|
|
149
|
+
let loose = path
|
|
150
|
+
.and_then(|path| std::fs::read(path).ok())
|
|
151
|
+
.and_then(|bytes| serde_json::from_slice::<Value>(&bytes).ok());
|
|
152
|
+
let persisted = loose
|
|
153
|
+
.as_ref()
|
|
154
|
+
.and_then(|v| v.get("enabled"))
|
|
155
|
+
.and_then(Value::as_bool);
|
|
156
|
+
let fields = switch_fields(
|
|
157
|
+
path,
|
|
158
|
+
persisted,
|
|
159
|
+
path.is_some_and(std::path::Path::exists),
|
|
160
|
+
Override::current(),
|
|
161
|
+
);
|
|
162
|
+
if let (Some(target), Some(fields)) = (value.as_object_mut(), fields.as_object()) {
|
|
163
|
+
target.extend(fields.clone());
|
|
164
|
+
}
|
|
165
|
+
value["operation"] = json!(args[..delimiter]
|
|
166
|
+
.iter()
|
|
167
|
+
.find(|arg| !arg.starts_with('-'))
|
|
168
|
+
.map(String::as_str)
|
|
169
|
+
.unwrap_or("status"));
|
|
170
|
+
}
|
|
171
|
+
if let Some(text) = super::emit(&CmdOutput::Json(value), as_json) {
|
|
172
|
+
if as_json {
|
|
173
|
+
println!("{text}");
|
|
174
|
+
} else {
|
|
175
|
+
eprintln!("{text}");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if is_usage {
|
|
179
|
+
ExitCode::Usage
|
|
180
|
+
} else {
|
|
181
|
+
ExitCode::Error
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
pub(crate) fn run(args: &[String]) -> ExitCode {
|
|
186
|
+
let delimiter = args
|
|
187
|
+
.iter()
|
|
188
|
+
.position(|arg| arg == "--")
|
|
189
|
+
.unwrap_or(args.len());
|
|
190
|
+
// Help and output-format controls stop at the first delimiter. Neither
|
|
191
|
+
// generic help scanning nor the old --no-display compatibility filter runs.
|
|
192
|
+
if args[..delimiter]
|
|
193
|
+
.iter()
|
|
194
|
+
.any(|arg| matches!(arg.as_str(), "--help" | "-h"))
|
|
195
|
+
{
|
|
196
|
+
println!("{HELP}");
|
|
197
|
+
return ExitCode::Ok;
|
|
198
|
+
}
|
|
199
|
+
let as_json = args[..delimiter].iter().any(|arg| arg == "--json");
|
|
200
|
+
match parse(args, delimiter) {
|
|
201
|
+
Ok(request) => match execute(&request) {
|
|
202
|
+
Ok(value) => super::emit::emit_result(CmdResult::from_json(value, request.json)),
|
|
203
|
+
Err(error) => emit_error(error, args, delimiter, as_json),
|
|
204
|
+
},
|
|
205
|
+
Err(error) => emit_error(error, args, delimiter, as_json),
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -74,6 +74,7 @@ pub(crate) enum DispatchKind {
|
|
|
74
74
|
Peek,
|
|
75
75
|
Coordinator,
|
|
76
76
|
Models,
|
|
77
|
+
Route,
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
pub(crate) const ALL_DISPATCH_KINDS: &[DispatchKind] = &[
|
|
@@ -112,6 +113,7 @@ pub(crate) const ALL_DISPATCH_KINDS: &[DispatchKind] = &[
|
|
|
112
113
|
DispatchKind::Peek,
|
|
113
114
|
DispatchKind::Coordinator,
|
|
114
115
|
DispatchKind::Models,
|
|
116
|
+
DispatchKind::Route,
|
|
115
117
|
];
|
|
116
118
|
|
|
117
119
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
@@ -175,6 +177,7 @@ pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
|
|
|
175
177
|
CommandSpec { name: "clone-agent", tier: CommandTier::Secondary, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::CloneAgent), summary: "clone a worker role into a fresh session", usage: "usage: team-agent clone-agent SOURCE_AGENT --as AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
176
178
|
CommandSpec { name: "allow-peer-talk", tier: CommandTier::Secondary, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::AllowPeerTalk), summary: "allow direct worker peer talk", usage: "usage: team-agent allow-peer-talk A B [--workspace WORKSPACE] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "capability/config surface" }) },
|
|
177
179
|
CommandSpec { name: "approvals", tier: CommandTier::Secondary, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Approvals), summary: "inspect provider approval state", usage: "usage: team-agent approvals [AGENT] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "approval diagnostics" }) },
|
|
180
|
+
CommandSpec { name: "route", tier: CommandTier::Secondary, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Route), summary: "manage global native CLI argv routing", usage: "usage: team-agent route [status|enable|disable|show|set|add|clear] [PROVIDER] [--json] [-- ARG ...]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "opt-in host-global provider argv setup" }) },
|
|
178
181
|
CommandSpec { name: "profile", tier: CommandTier::Secondary, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Profile), summary: "manage provider profiles", usage: "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--proxy-mode direct|inherit] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "provider/profile setup" }) },
|
|
179
182
|
CommandSpec { name: "install-skill", tier: CommandTier::Secondary, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::InstallSkill), summary: "install or uninstall Team Agent skills", usage: "usage: team-agent install-skill (--source DIR | --uninstall) [--target codex|claude|copilot|all] [--dest DIR] [--dry-run] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "capability setup" }) },
|
|
180
183
|
CommandSpec { name: "inbox", tier: CommandTier::Secondary, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Inbox), summary: "inspect recent message summaries", usage: "usage: team-agent inbox AGENT [-n N|--limit N] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
@@ -291,7 +291,7 @@ fn leader_start_plan_with_ambient_authority(
|
|
|
291
291
|
),
|
|
292
292
|
_ => None,
|
|
293
293
|
};
|
|
294
|
-
let provider_argv = if provider == Provider::Pi && mode != LeaderStartMode::AttachExisting {
|
|
294
|
+
let mut provider_argv = if provider == Provider::Pi && mode != LeaderStartMode::AttachExisting {
|
|
295
295
|
let parsed = crate::lifecycle::launch::pi_mcp::parse_pi_leader_args(provider_args)
|
|
296
296
|
.map_err(|error| LeaderError::Start(error.to_string()))?;
|
|
297
297
|
let mcp_config = adapter
|
|
@@ -329,6 +329,10 @@ fn leader_start_plan_with_ambient_authority(
|
|
|
329
329
|
} else {
|
|
330
330
|
provider_command_argv(provider, provider_args)
|
|
331
331
|
};
|
|
332
|
+
if mode != LeaderStartMode::AttachExisting {
|
|
333
|
+
crate::provider::argv_route::apply(provider, &mut provider_argv)
|
|
334
|
+
.map_err(|error| LeaderError::Start(error.to_string()))?;
|
|
335
|
+
}
|
|
332
336
|
let argv = start_argv(
|
|
333
337
|
mode,
|
|
334
338
|
provider,
|
|
@@ -330,6 +330,8 @@ pub(super) fn spawn_agents(
|
|
|
330
330
|
ensure_grok_login_and_folder_trust(workspace)?;
|
|
331
331
|
apply_grok_mcp_overlay(workspace, &mcp_config)?;
|
|
332
332
|
}
|
|
333
|
+
crate::provider::argv_route::apply(provider, &mut plan.argv)
|
|
334
|
+
.map_err(|error| LifecycleError::Provider(error.to_string()))?;
|
|
333
335
|
let spawn_epoch = u64::try_from(started.len()).unwrap_or(u64::MAX);
|
|
334
336
|
let spawned_at = spawn_timestamp_for_agent(u32::try_from(spawn_epoch).unwrap_or(u32::MAX));
|
|
335
337
|
// E6 层1 实证3 + 诊断留痕:落最终 worker argv(spawn 前的真实形态)。
|
|
@@ -516,6 +516,8 @@ pub(super) fn spawn_agent_window(
|
|
|
516
516
|
// Keep the launcher-known typed provider independent from inherited
|
|
517
517
|
// LEADER_* identity. The tmux wrapper binds pane/socket at invocation time.
|
|
518
518
|
crate::layout::worker_env::inject_current_caller_provider(&mut env, provider);
|
|
519
|
+
crate::provider::argv_route::apply(provider, &mut plan.argv)
|
|
520
|
+
.map_err(|error| LifecycleError::Provider(error.to_string()))?;
|
|
519
521
|
|
|
520
522
|
// 0.4.6 Stage 2: write actual spawn plan event BEFORE invoking the
|
|
521
523
|
// transport spawn. Mirrors `launch.rs:359-380` (the reference impl)
|