@team-agent/installer 0.4.9 → 0.4.11
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/status_port.rs +4 -1
- package/crates/team-agent/src/compiler.rs +78 -9
- package/crates/team-agent/src/coordinator/tick.rs +96 -0
- package/crates/team-agent/src/leader/start.rs +169 -25
- package/crates/team-agent/src/lifecycle/launch.rs +155 -3
- package/crates/team-agent/src/lifecycle/profile_launch.rs +12 -0
- package/crates/team-agent/src/lifecycle/restart/agent.rs +279 -10
- package/crates/team-agent/src/lifecycle/restart/common.rs +17 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +187 -0
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +22 -13
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +24 -34
- package/crates/team-agent/src/messaging/mod.rs +2 -1
- package/crates/team-agent/src/messaging/types.rs +86 -0
- package/crates/team-agent/src/model/enums.rs +65 -0
- package/crates/team-agent/src/model/spec.rs +34 -2
- package/crates/team-agent/src/os_probe.rs +79 -0
- package/crates/team-agent/src/provider/adapter.rs +11 -3
- package/crates/team-agent/src/provider/adapters/claude.rs +8 -0
- package/crates/team-agent/src/provider/adapters/codex.rs +10 -0
- package/crates/team-agent/src/provider/types.rs +8 -0
- package/crates/team-agent/src/tmux_backend.rs +19 -1
- package/package.json +4 -4
|
@@ -128,3 +128,82 @@ fn read_and_remove(path: &std::path::Path) -> Vec<u8> {
|
|
|
128
128
|
let _ = std::fs::remove_file(path);
|
|
129
129
|
stdout
|
|
130
130
|
}
|
|
131
|
+
|
|
132
|
+
/// 0.4.x Phase 1 (fg-pgrp): terminal foreground process-group probe.
|
|
133
|
+
///
|
|
134
|
+
/// macOS probe confirmed (2026-06-29, .team/artifacts/fg-pgrp-probe.md):
|
|
135
|
+
/// `TIOCGPGRP` ioctl returns Errno 25 on tmux slave PTYs opened from a
|
|
136
|
+
/// non-controlling process. We use `ps -o tpgid,pgid -p <pid>` instead —
|
|
137
|
+
/// reliable on macOS AND Linux, no platform-specific cfg needed.
|
|
138
|
+
///
|
|
139
|
+
/// Returns `Ok(Some((tpgid, pgid)))` on success, `Ok(None)` when the PID
|
|
140
|
+
/// is gone / ps returned no row / values unparseable. The caller maps
|
|
141
|
+
/// `None` to `WorkerRuntimeState::Unknown` (never to idle — Iron Law).
|
|
142
|
+
///
|
|
143
|
+
/// `tpgid` = terminal foreground process group ID.
|
|
144
|
+
/// `pgid` = the agent root's own process group ID.
|
|
145
|
+
/// `tpgid != pgid` means a child process owns the foreground (BUSY signal).
|
|
146
|
+
///
|
|
147
|
+
/// Bounded via [`run_bounded_command`] so a wedged ps cannot stall the
|
|
148
|
+
/// coordinator tick.
|
|
149
|
+
pub(crate) fn pane_foreground_and_root_pgrp(pane_pid: u32) -> io::Result<Option<(u32, u32)>> {
|
|
150
|
+
let mut command = Command::new("ps");
|
|
151
|
+
command.args(["-o", "tpgid=,pgid=", "-p", &pane_pid.to_string()]);
|
|
152
|
+
let output = bounded_command_output_with_probe(
|
|
153
|
+
&mut command,
|
|
154
|
+
"pane_foreground_and_root_pgrp",
|
|
155
|
+
Some(pane_pid),
|
|
156
|
+
)?;
|
|
157
|
+
if probe_timed_out() {
|
|
158
|
+
return Ok(None);
|
|
159
|
+
}
|
|
160
|
+
if !output.status.success() {
|
|
161
|
+
return Ok(None);
|
|
162
|
+
}
|
|
163
|
+
let text = String::from_utf8_lossy(&output.stdout);
|
|
164
|
+
let first_line = text.lines().next().unwrap_or("").trim();
|
|
165
|
+
if first_line.is_empty() {
|
|
166
|
+
return Ok(None);
|
|
167
|
+
}
|
|
168
|
+
let parts: Vec<&str> = first_line.split_whitespace().collect();
|
|
169
|
+
if parts.len() < 2 {
|
|
170
|
+
return Ok(None);
|
|
171
|
+
}
|
|
172
|
+
let tpgid = parts[0].parse::<i64>().ok();
|
|
173
|
+
let pgid = parts[1].parse::<i64>().ok();
|
|
174
|
+
match (tpgid, pgid) {
|
|
175
|
+
// ps prints `-1` for tpgid when there is no controlling terminal —
|
|
176
|
+
// treat as unknown rather than fabricating a u32.
|
|
177
|
+
(Some(t), Some(p)) if t > 0 && p > 0 => Ok(Some((t as u32, p as u32))),
|
|
178
|
+
_ => Ok(None),
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#[cfg(test)]
|
|
183
|
+
mod fg_pgrp_tests {
|
|
184
|
+
use super::*;
|
|
185
|
+
|
|
186
|
+
#[test]
|
|
187
|
+
fn pane_foreground_and_root_pgrp_self_returns_some_or_none() {
|
|
188
|
+
// The current test process is not necessarily attached to a
|
|
189
|
+
// controlling terminal (CI runs detached), so we accept Some OR
|
|
190
|
+
// None. The contract is: never panic, return io::Result.
|
|
191
|
+
let pid = std::process::id();
|
|
192
|
+
match pane_foreground_and_root_pgrp(pid) {
|
|
193
|
+
Ok(Some((tpgid, pgid))) => {
|
|
194
|
+
assert!(tpgid > 0 && pgid > 0, "positive pgid/tpgid");
|
|
195
|
+
}
|
|
196
|
+
Ok(None) => {} // headless / no tty — acceptable
|
|
197
|
+
Err(e) => panic!("probe must not error in normal env: {e}"),
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#[test]
|
|
202
|
+
fn pane_foreground_and_root_pgrp_missing_pid_returns_none() {
|
|
203
|
+
// PID 1 is init/launchd — exists but we read fields; non-existent
|
|
204
|
+
// PIDs return Ok(None) (ps exits non-zero).
|
|
205
|
+
let result =
|
|
206
|
+
pane_foreground_and_root_pgrp(0xFFFF_FFFE).expect("must not error on missing pid");
|
|
207
|
+
assert!(result.is_none(), "missing pid → None");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -410,7 +410,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
410
410
|
Provider::Claude | Provider::ClaudeCode => {
|
|
411
411
|
Ok(claude_launch_command(self, auth_mode, mcp_config, system_prompt, model, tools)?)
|
|
412
412
|
}
|
|
413
|
-
Provider::Codex => Ok(codex_base_command(None, auth_mode, mcp_config, system_prompt, model, tools, None)),
|
|
413
|
+
Provider::Codex => Ok(codex_base_command(None, auth_mode, mcp_config, system_prompt, model, tools, None, None)),
|
|
414
414
|
// §C1 worker argv 形态 + C-1/C-5/C-6 cr verdict:
|
|
415
415
|
// copilot --no-color --no-auto-update [<dangerous|granular>] [--model]
|
|
416
416
|
// --additional-mcp-config <inline json> --session-id <expected_uuid> -C <cwd>
|
|
@@ -469,6 +469,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
469
469
|
model,
|
|
470
470
|
ctx.tools,
|
|
471
471
|
managed,
|
|
472
|
+
ctx.effort,
|
|
472
473
|
)?;
|
|
473
474
|
argv.push("--session-id".to_string());
|
|
474
475
|
argv.push(expected.clone());
|
|
@@ -507,6 +508,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
507
508
|
ctx.model,
|
|
508
509
|
ctx.tools,
|
|
509
510
|
ctx.profile_launch.map(|profile| &profile.command_overrides),
|
|
511
|
+
ctx.effort,
|
|
510
512
|
))),
|
|
511
513
|
// §C1 + §C4 cr verdict — copilot plan 端预定 UUID + workspace `-C` 双保险:
|
|
512
514
|
// * `--session-id <uuid>`(claude 同法,捕获免目录扫描,sqlite 仅校验)
|
|
@@ -638,13 +640,14 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
638
640
|
model,
|
|
639
641
|
tools,
|
|
640
642
|
None,
|
|
643
|
+
None,
|
|
641
644
|
);
|
|
642
645
|
argv.push(session_id.as_str().to_string());
|
|
643
646
|
Ok(argv)
|
|
644
647
|
}
|
|
645
648
|
Provider::Claude | Provider::ClaudeCode => {
|
|
646
649
|
let mut argv =
|
|
647
|
-
claude_base_command(self, auth_mode, mcp_config, system_prompt, model, tools, false)?;
|
|
650
|
+
claude_base_command(self, auth_mode, mcp_config, system_prompt, model, tools, false, None)?;
|
|
648
651
|
argv.push("--resume".to_string());
|
|
649
652
|
argv.push(session_id.as_str().to_string());
|
|
650
653
|
Ok(argv)
|
|
@@ -686,6 +689,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
686
689
|
model,
|
|
687
690
|
ctx.tools,
|
|
688
691
|
managed,
|
|
692
|
+
ctx.effort,
|
|
689
693
|
)?;
|
|
690
694
|
argv.push("--resume".to_string());
|
|
691
695
|
argv.push(session_id.as_str().to_string());
|
|
@@ -725,6 +729,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
725
729
|
ctx.model,
|
|
726
730
|
ctx.tools,
|
|
727
731
|
ctx.profile_launch.map(|profile| &profile.command_overrides),
|
|
732
|
+
ctx.effort,
|
|
728
733
|
);
|
|
729
734
|
argv.push(session_id.as_str().to_string());
|
|
730
735
|
Ok(CommandPlan::argv_only(argv))
|
|
@@ -779,13 +784,14 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
779
784
|
model,
|
|
780
785
|
tools,
|
|
781
786
|
None,
|
|
787
|
+
None,
|
|
782
788
|
);
|
|
783
789
|
argv.push(session_id.as_str().to_string());
|
|
784
790
|
Ok(argv)
|
|
785
791
|
}
|
|
786
792
|
Provider::Claude | Provider::ClaudeCode => {
|
|
787
793
|
let mut argv =
|
|
788
|
-
claude_base_command(self, auth_mode, mcp_config, system_prompt, model, tools, false)?;
|
|
794
|
+
claude_base_command(self, auth_mode, mcp_config, system_prompt, model, tools, false, None)?;
|
|
789
795
|
argv.push("--session-id".to_string());
|
|
790
796
|
argv.push(next_session_token());
|
|
791
797
|
argv.push("--resume".to_string());
|
|
@@ -837,6 +843,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
837
843
|
model,
|
|
838
844
|
ctx.tools,
|
|
839
845
|
managed,
|
|
846
|
+
ctx.effort,
|
|
840
847
|
)?;
|
|
841
848
|
argv.push("--session-id".to_string());
|
|
842
849
|
argv.push(expected.clone());
|
|
@@ -870,6 +877,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
870
877
|
ctx.model,
|
|
871
878
|
ctx.tools,
|
|
872
879
|
ctx.profile_launch.map(|profile| &profile.command_overrides),
|
|
880
|
+
ctx.effort,
|
|
873
881
|
);
|
|
874
882
|
argv.push(session_id.as_str().to_string());
|
|
875
883
|
Ok(CommandPlan::argv_only(argv))
|
|
@@ -34,6 +34,7 @@ pub(crate) fn claude_launch_command(
|
|
|
34
34
|
model,
|
|
35
35
|
tools,
|
|
36
36
|
false,
|
|
37
|
+
None,
|
|
37
38
|
)?;
|
|
38
39
|
argv.push("--session-id".to_string());
|
|
39
40
|
argv.push(next_session_token());
|
|
@@ -48,6 +49,9 @@ pub(crate) fn claude_base_command(
|
|
|
48
49
|
model: Option<&str>,
|
|
49
50
|
tools: &[&str],
|
|
50
51
|
managed_mcp_config: bool,
|
|
52
|
+
// 0.4.x provider effort MVP step 5: when Some, inject `--effort <level>`
|
|
53
|
+
// immediately after the model (before prompt/MCP).
|
|
54
|
+
effort: Option<crate::model::enums::ProviderEffort>,
|
|
51
55
|
) -> Result<Vec<String>, ProviderError> {
|
|
52
56
|
let mut argv = vec!["claude".to_string()];
|
|
53
57
|
if claude_dangerous_auto_approve(tools) {
|
|
@@ -60,6 +64,10 @@ pub(crate) fn claude_base_command(
|
|
|
60
64
|
argv.push("--model".to_string());
|
|
61
65
|
argv.push(model.to_string());
|
|
62
66
|
}
|
|
67
|
+
if let Some(effort) = effort {
|
|
68
|
+
argv.push("--effort".to_string());
|
|
69
|
+
argv.push(effort.as_str().to_string());
|
|
70
|
+
}
|
|
63
71
|
if let Some(prompt) = system_prompt {
|
|
64
72
|
argv.push("--append-system-prompt".to_string());
|
|
65
73
|
argv.push(prompt.to_string());
|
|
@@ -17,6 +17,12 @@ pub(crate) fn codex_base_command(
|
|
|
17
17
|
model: Option<&str>,
|
|
18
18
|
tools: &[&str],
|
|
19
19
|
overrides: Option<&ProviderCommandOverrides>,
|
|
20
|
+
// 0.4.x provider effort MVP step 6: when Some, inject
|
|
21
|
+
// `-c model_reasoning_effort=<level>` AFTER existing profile
|
|
22
|
+
// codex_config overrides — explicit launch effort wins over profile.
|
|
23
|
+
// Codex does not support `max`; the caller filters that case via
|
|
24
|
+
// `ProviderEffort::is_supported_by` before reaching this point.
|
|
25
|
+
effort: Option<crate::model::enums::ProviderEffort>,
|
|
20
26
|
) -> Vec<String> {
|
|
21
27
|
let mut argv = vec!["codex".to_string()];
|
|
22
28
|
if let Some(subcommand) = subcommand {
|
|
@@ -51,6 +57,10 @@ pub(crate) fn codex_base_command(
|
|
|
51
57
|
argv.push(config.clone());
|
|
52
58
|
}
|
|
53
59
|
}
|
|
60
|
+
if let Some(effort) = effort {
|
|
61
|
+
argv.push("-c".to_string());
|
|
62
|
+
argv.push(format!("model_reasoning_effort={}", effort.as_str()));
|
|
63
|
+
}
|
|
54
64
|
if let Some(prompt) = system_prompt {
|
|
55
65
|
// codex.py:120 — escape order matters: backslash first, then quote, then newline.
|
|
56
66
|
let escaped = prompt
|
|
@@ -347,6 +347,14 @@ pub struct ProviderCommandContext<'a> {
|
|
|
347
347
|
/// no `--name` CLI flag (probe verdict 2026-06-22) and ignores this
|
|
348
348
|
/// field. None = legacy callers (the field is purely additive).
|
|
349
349
|
pub agent_id_hint: Option<&'a str>,
|
|
350
|
+
/// 0.4.x provider effort MVP step 4: reasoning effort level resolved
|
|
351
|
+
/// from role doc / TEAM.md / provider default. `None` means the
|
|
352
|
+
/// framework passes no effort flag (provider default).
|
|
353
|
+
/// - Claude / ClaudeCode: low|medium|high|xhigh|max → `--effort <level>`
|
|
354
|
+
/// - Codex: low|medium|high|xhigh → `-c model_reasoning_effort=<level>`
|
|
355
|
+
/// - Copilot / Gemini / Fake: ignored, warning event emitted at the
|
|
356
|
+
/// caller (lifecycle/launch.rs / lifecycle/restart) before construct.
|
|
357
|
+
pub effort: Option<crate::model::enums::ProviderEffort>,
|
|
350
358
|
}
|
|
351
359
|
|
|
352
360
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
@@ -1321,6 +1321,8 @@ fn shell_command(
|
|
|
1321
1321
|
env: &BTreeMap<String, String>,
|
|
1322
1322
|
env_unset: &[String],
|
|
1323
1323
|
) -> String {
|
|
1324
|
+
let unset_set: std::collections::BTreeSet<&str> =
|
|
1325
|
+
env_unset.iter().map(String::as_str).collect();
|
|
1324
1326
|
let mut parts = Vec::new();
|
|
1325
1327
|
parts.push("cd".to_string());
|
|
1326
1328
|
parts.push(shell_quote(&cwd.to_string_lossy()));
|
|
@@ -1333,7 +1335,16 @@ fn shell_command(
|
|
|
1333
1335
|
parts.push(key.clone());
|
|
1334
1336
|
parts.push("&&".to_string());
|
|
1335
1337
|
}
|
|
1338
|
+
// 0.4.x ordering fix (env-leak symptom #3): KEY=val exports must NOT
|
|
1339
|
+
// re-introduce any key that was just unset. Filter env entries whose key
|
|
1340
|
+
// appears in env_unset so the unset wins on the final shell line. This
|
|
1341
|
+
// matters when inherited env (worker_spawn_env / apply_profile_launch_env)
|
|
1342
|
+
// contains the very keys we want to scrub (e.g. CLAUDE_EFFORT carried
|
|
1343
|
+
// forward from the launching shell into the env map).
|
|
1336
1344
|
for (key, value) in env {
|
|
1345
|
+
if unset_set.contains(key.as_str()) {
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1337
1348
|
parts.push(format!("{key}={}", shell_quote(value)));
|
|
1338
1349
|
}
|
|
1339
1350
|
parts.push("exec".to_string());
|
|
@@ -1378,6 +1389,8 @@ pub fn leader_shell_wrapper_command(
|
|
|
1378
1389
|
env_unset: &[String],
|
|
1379
1390
|
provider_label: &str,
|
|
1380
1391
|
) -> String {
|
|
1392
|
+
let unset_set: std::collections::BTreeSet<&str> =
|
|
1393
|
+
env_unset.iter().map(String::as_str).collect();
|
|
1381
1394
|
let mut parts = Vec::new();
|
|
1382
1395
|
// 1. cd
|
|
1383
1396
|
parts.push("cd".to_string());
|
|
@@ -1389,8 +1402,13 @@ pub fn leader_shell_wrapper_command(
|
|
|
1389
1402
|
parts.push(key.clone());
|
|
1390
1403
|
parts.push("&&".to_string());
|
|
1391
1404
|
}
|
|
1392
|
-
// 3. env exports + provider (NO `exec` so the provider is a child)
|
|
1405
|
+
// 3. env exports + provider (NO `exec` so the provider is a child).
|
|
1406
|
+
// 0.4.x ordering fix: skip keys present in env_unset so KEY=val does not
|
|
1407
|
+
// re-introduce a just-unset variable from the inherited env map.
|
|
1393
1408
|
for (key, value) in env {
|
|
1409
|
+
if unset_set.contains(key.as_str()) {
|
|
1410
|
+
continue;
|
|
1411
|
+
}
|
|
1394
1412
|
parts.push(format!("{key}={}", shell_quote(value)));
|
|
1395
1413
|
}
|
|
1396
1414
|
parts.extend(argv.iter().map(|arg| shell_quote(arg)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"description": "npx installer for Team Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"team-agent-installer": "npm/install.mjs"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@team-agent/cli-darwin-arm64": "0.4.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.4.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.4.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.4.11",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.4.11",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.4.11"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|