@team-agent/installer 0.4.10 → 0.5.0
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 +1 -0
- package/crates/team-agent/src/cli/diagnose.rs +2 -15
- package/crates/team-agent/src/cli/emit.rs +62 -4
- package/crates/team-agent/src/compiler.rs +11 -20
- package/crates/team-agent/src/coordinator/orphan.rs +2 -2
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +5 -4
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +1776 -2
- package/crates/team-agent/src/coordinator/steps/mod.rs +19 -0
- package/crates/team-agent/src/coordinator/tests/a0_lostupdate.rs +78 -7
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +195 -0
- package/crates/team-agent/src/coordinator/tick.rs +31 -932
- package/crates/team-agent/src/diagnose/orphans.rs +66 -8
- package/crates/team-agent/src/layout/worker_window_helpers.rs +5 -7
- package/crates/team-agent/src/leader/provider_attribution.rs +7 -5
- package/crates/team-agent/src/leader/rediscover.rs +1 -11
- package/crates/team-agent/src/leader/start.rs +169 -25
- package/crates/team-agent/src/lifecycle/launch/plan.rs +19 -8
- package/crates/team-agent/src/lifecycle/launch.rs +135 -58
- package/crates/team-agent/src/lifecycle/lock.rs +302 -0
- package/crates/team-agent/src/lifecycle/mod.rs +1 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +1 -11
- package/crates/team-agent/src/lifecycle/restart/agent.rs +436 -121
- package/crates/team-agent/src/lifecycle/restart/common.rs +108 -17
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -32
- package/crates/team-agent/src/lifecycle/restart/remove.rs +16 -2
- package/crates/team-agent/src/lifecycle/restart/selection.rs +2 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +193 -0
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/lifecycle_lock.rs +321 -0
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +54 -15
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +802 -0
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +648 -0
- package/crates/team-agent/src/lifecycle/tests.rs +3 -0
- package/crates/team-agent/src/lifecycle/types.rs +8 -0
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +24 -34
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +24 -9
- package/crates/team-agent/src/mcp_server/tools.rs +157 -68
- package/crates/team-agent/src/messaging/activity.rs +43 -18
- package/crates/team-agent/src/messaging/delivery.rs +161 -97
- package/crates/team-agent/src/messaging/helpers.rs +1 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +22 -1
- package/crates/team-agent/src/messaging/results.rs +34 -9
- package/crates/team-agent/src/messaging/scheduler.rs +52 -9
- package/crates/team-agent/src/model/spec.rs +10 -6
- package/crates/team-agent/src/provider/adapter.rs +10 -1038
- package/crates/team-agent/src/provider/adapters/claude.rs +3 -8
- package/crates/team-agent/src/provider/adapters/copilot.rs +2 -5
- package/crates/team-agent/src/provider/classify.rs +23 -48
- package/crates/team-agent/src/provider/command.rs +16 -7
- package/crates/team-agent/src/provider/faults.rs +93 -53
- package/crates/team-agent/src/provider/session/capture.rs +4 -15
- package/crates/team-agent/src/provider/session_scan/claude.rs +309 -0
- package/crates/team-agent/src/provider/session_scan/codex.rs +40 -0
- package/crates/team-agent/src/provider/session_scan/common.rs +350 -0
- package/crates/team-agent/src/provider/session_scan/copilot.rs +202 -0
- package/crates/team-agent/src/provider/session_scan.rs +65 -27
- package/crates/team-agent/src/provider/tests/faults.rs +80 -0
- package/crates/team-agent/src/provider/types.rs +33 -1
- package/crates/team-agent/src/provider/wire.rs +171 -1
- package/crates/team-agent/src/state/identity.rs +242 -57
- package/crates/team-agent/src/state/identity_keys.rs +9 -12
- package/crates/team-agent/src/state/mod.rs +5 -1
- package/crates/team-agent/src/state/owner_gate.rs +59 -15
- package/crates/team-agent/src/state/ownership.rs +12 -11
- package/crates/team-agent/src/state/paths.rs +13 -6
- package/crates/team-agent/src/state/persist.rs +671 -128
- package/crates/team-agent/src/state/projection.rs +240 -49
- package/crates/team-agent/src/state/selector.rs +11 -5
- package/crates/team-agent/src/tmux_backend/tests.rs +51 -2
- package/crates/team-agent/src/tmux_backend.rs +42 -5
- package/crates/team-agent/src/transport/test_support.rs +55 -6
- package/package.json +4 -4
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -993,6 +993,7 @@ fn command_version(command: &str) -> Value {
|
|
|
993
993
|
}
|
|
994
994
|
|
|
995
995
|
fn provider_command(provider: &str) -> &str {
|
|
996
|
+
// E2E provider command grammar, not persisted provider identity parsing.
|
|
996
997
|
match provider {
|
|
997
998
|
"claude" | "claude_code" => "claude",
|
|
998
999
|
"gemini" | "gemini_cli" => "gemini",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//! diagnose/preflight/wait-ready CLI helpers.
|
|
2
2
|
use super::*;
|
|
3
|
+
use crate::provider::wire::{command_name, provider_wire};
|
|
3
4
|
use crate::transport::Transport;
|
|
4
5
|
|
|
5
6
|
pub(crate) fn diagnose_runtime(state: &Value, backend: &dyn Transport) -> (Value, Value) {
|
|
@@ -781,23 +782,9 @@ pub(crate) fn provider_doctor_checks() -> Value {
|
|
|
781
782
|
Value::Object(providers)
|
|
782
783
|
}
|
|
783
784
|
|
|
784
|
-
fn provider_wire(provider: crate::provider::Provider) -> &'static str {
|
|
785
|
-
match provider {
|
|
786
|
-
crate::provider::Provider::Claude => "claude",
|
|
787
|
-
crate::provider::Provider::ClaudeCode => "claude_code",
|
|
788
|
-
crate::provider::Provider::Codex => "codex",
|
|
789
|
-
crate::provider::Provider::Copilot => "copilot",
|
|
790
|
-
crate::provider::Provider::GeminiCli => "gemini_cli",
|
|
791
|
-
crate::provider::Provider::Fake => "fake",
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
785
|
fn provider_command(provider: crate::provider::Provider) -> &'static str {
|
|
796
786
|
match provider {
|
|
797
|
-
crate::provider::Provider::Claude | crate::provider::Provider::ClaudeCode => "claude",
|
|
798
|
-
crate::provider::Provider::Codex => "codex",
|
|
799
|
-
crate::provider::Provider::Copilot => "copilot",
|
|
800
|
-
crate::provider::Provider::GeminiCli => "gemini",
|
|
801
787
|
crate::provider::Provider::Fake => "team-agent fake-worker",
|
|
788
|
+
other => command_name(other),
|
|
802
789
|
}
|
|
803
790
|
}
|
|
@@ -31,7 +31,7 @@ pub fn run(argv: &[String], cwd: &Path) -> ExitCode {
|
|
|
31
31
|
let Some(command) = argv.first().map(String::as_str) else {
|
|
32
32
|
return emit_missing_subcommand_usage();
|
|
33
33
|
};
|
|
34
|
-
if command
|
|
34
|
+
if is_leader_passthrough_command(command) {
|
|
35
35
|
return match cmd_leader_passthrough(command, &argv[1..], cwd) {
|
|
36
36
|
Ok(result) => emit_result(result),
|
|
37
37
|
Err(error) => emit_cli_error(command, &argv[1..], cwd, &error),
|
|
@@ -189,6 +189,13 @@ const DISPATCH_COMMANDS: &[&str] = &[
|
|
|
189
189
|
];
|
|
190
190
|
|
|
191
191
|
const SPEC_ONLY_HELP_COMMANDS: &[&str] = &["start", "purge-agent"];
|
|
192
|
+
// Command grammar, not provider identity parsing: these are top-level CLI
|
|
193
|
+
// passthrough verbs for starting a leader under a provider executable.
|
|
194
|
+
const LEADER_PASSTHROUGH_COMMANDS: &[&str] = &["codex", "claude", "copilot"];
|
|
195
|
+
|
|
196
|
+
fn is_leader_passthrough_command(command: &str) -> bool {
|
|
197
|
+
LEADER_PASSTHROUGH_COMMANDS.contains(&command)
|
|
198
|
+
}
|
|
192
199
|
|
|
193
200
|
fn emit_missing_subcommand_usage() -> ExitCode {
|
|
194
201
|
emit_usage_error("the following arguments are required: {codex,claude,...,doctor}");
|
|
@@ -206,7 +213,7 @@ fn is_known_subcommand(command: &str) -> bool {
|
|
|
206
213
|
fn command_help(command: Option<&str>) -> String {
|
|
207
214
|
match command {
|
|
208
215
|
None => {
|
|
209
|
-
let mut commands =
|
|
216
|
+
let mut commands = LEADER_PASSTHROUGH_COMMANDS.to_vec();
|
|
210
217
|
commands.extend_from_slice(DISPATCH_COMMANDS);
|
|
211
218
|
commands.extend_from_slice(SPEC_ONLY_HELP_COMMANDS);
|
|
212
219
|
format!(
|
|
@@ -276,7 +283,7 @@ fn emit_unknown_subcommand_usage(command: &str) -> ExitCode {
|
|
|
276
283
|
|
|
277
284
|
/// 在已知子命令里找与 `input` 最接近的一个(Levenshtein ≤ 阈值)。无足够接近者 → None。
|
|
278
285
|
fn nearest_subcommand(input: &str) -> Option<&'static str> {
|
|
279
|
-
let mut candidates: Vec<&'static str> =
|
|
286
|
+
let mut candidates: Vec<&'static str> = LEADER_PASSTHROUGH_COMMANDS.to_vec();
|
|
280
287
|
candidates.extend_from_slice(DISPATCH_COMMANDS);
|
|
281
288
|
candidates.extend_from_slice(SPEC_ONLY_HELP_COMMANDS);
|
|
282
289
|
// 阈值随长度放宽,但短词收紧,避免 'x' 误配任何东西。
|
|
@@ -868,7 +875,7 @@ fn resolve_cli_path(cwd: &Path, path: &Path) -> PathBuf {
|
|
|
868
875
|
|
|
869
876
|
fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
|
|
870
877
|
let parsed = parse_args(args);
|
|
871
|
-
let target = if parsed.targets.is_some() {
|
|
878
|
+
let target = if parsed.targets.is_some() || parsed.pane.is_some() {
|
|
872
879
|
None
|
|
873
880
|
} else {
|
|
874
881
|
parsed.positionals.first().cloned()
|
|
@@ -1747,6 +1754,57 @@ mod tests {
|
|
|
1747
1754
|
);
|
|
1748
1755
|
}
|
|
1749
1756
|
|
|
1757
|
+
#[test]
|
|
1758
|
+
fn send_pane_positionals_are_message_not_target() {
|
|
1759
|
+
let cwd = tmp_workspace();
|
|
1760
|
+
let args = send_args(&cli_argv(&["--pane", "%1596", "hello"]), &cwd).unwrap();
|
|
1761
|
+
assert_eq!(args.pane.as_deref(), Some("%1596"));
|
|
1762
|
+
assert_eq!(args.target, None);
|
|
1763
|
+
assert_eq!(args.targets, None);
|
|
1764
|
+
assert_eq!(args.message, vec!["hello".to_string()]);
|
|
1765
|
+
|
|
1766
|
+
let args = send_args(
|
|
1767
|
+
&cli_argv(&["--pane", "%1596", "multi", "word", "message"]),
|
|
1768
|
+
&cwd,
|
|
1769
|
+
)
|
|
1770
|
+
.unwrap();
|
|
1771
|
+
assert_eq!(args.target, None);
|
|
1772
|
+
assert_eq!(
|
|
1773
|
+
args.message,
|
|
1774
|
+
vec![
|
|
1775
|
+
"multi".to_string(),
|
|
1776
|
+
"word".to_string(),
|
|
1777
|
+
"message".to_string()
|
|
1778
|
+
]
|
|
1779
|
+
);
|
|
1780
|
+
|
|
1781
|
+
let _ = std::fs::remove_dir_all(&cwd);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
#[test]
|
|
1785
|
+
fn send_pane_still_rejects_to_and_empty_message() {
|
|
1786
|
+
let cwd = tmp_workspace();
|
|
1787
|
+
let args = send_args(
|
|
1788
|
+
&cli_argv(&["--pane", "%1596", "--to", "worker", "hello"]),
|
|
1789
|
+
&cwd,
|
|
1790
|
+
)
|
|
1791
|
+
.unwrap();
|
|
1792
|
+
let err = cmd_send(&args).unwrap_err();
|
|
1793
|
+
assert!(
|
|
1794
|
+
matches!(err, CliError::Usage(ref message) if message.contains("--pane and TARGET/--to are mutually exclusive")),
|
|
1795
|
+
"expected --pane/--to mutual-exclusion usage error, got {err:?}"
|
|
1796
|
+
);
|
|
1797
|
+
|
|
1798
|
+
let args = send_args(&cli_argv(&["--pane", "%1596"]), &cwd).unwrap();
|
|
1799
|
+
let err = cmd_send(&args).unwrap_err();
|
|
1800
|
+
assert!(
|
|
1801
|
+
matches!(err, CliError::Usage(ref message) if message == "--pane requires a non-empty message"),
|
|
1802
|
+
"expected empty-message usage error, got {err:?}"
|
|
1803
|
+
);
|
|
1804
|
+
|
|
1805
|
+
let _ = std::fs::remove_dir_all(&cwd);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1750
1808
|
#[test]
|
|
1751
1809
|
fn ux_quick_start_workspace_resolves_relative_agents_dir_inside_workspace() {
|
|
1752
1810
|
let cwd = tmp_workspace();
|
|
@@ -29,6 +29,10 @@ use std::path::Path;
|
|
|
29
29
|
use crate::model::enums::{Provider, ProviderEffort};
|
|
30
30
|
use crate::model::yaml::Value;
|
|
31
31
|
use crate::model::{paths, spec, yaml, ModelError};
|
|
32
|
+
use crate::provider::wire::{
|
|
33
|
+
builtin_provider_model as wire_builtin_provider_model, is_claude_family,
|
|
34
|
+
parse_canonical_provider, provider_model_keys,
|
|
35
|
+
};
|
|
32
36
|
|
|
33
37
|
pub const IGNORED_OWNER_TEAM_ID_FIELD: &str = "owner_team_id";
|
|
34
38
|
|
|
@@ -417,16 +421,8 @@ pub fn compile_role_agent(
|
|
|
417
421
|
_ => None,
|
|
418
422
|
})
|
|
419
423
|
.unwrap_or("");
|
|
420
|
-
let provider_enum =
|
|
421
|
-
|
|
422
|
-
"claude_code" => Provider::ClaudeCode,
|
|
423
|
-
"codex" => Provider::Codex,
|
|
424
|
-
"copilot" => Provider::Copilot,
|
|
425
|
-
"gemini_cli" => Provider::GeminiCli,
|
|
426
|
-
"fake" => Provider::Fake,
|
|
427
|
-
_ => Provider::Codex, // unknown providers caught elsewhere
|
|
428
|
-
};
|
|
429
|
-
if effort.is_claude_only() && !matches!(provider_enum, Provider::Claude | Provider::ClaudeCode) {
|
|
424
|
+
let provider_enum = parse_canonical_provider(provider_str).unwrap_or(Provider::Codex);
|
|
425
|
+
if effort.is_claude_only() && !is_claude_family(provider_enum) {
|
|
430
426
|
return Err(ModelError::Validation(format!(
|
|
431
427
|
"{}: effort '{}' is only supported by claude/claude_code (provider: {provider_str})",
|
|
432
428
|
role_path.display(),
|
|
@@ -543,19 +539,14 @@ fn resolve_model(role_meta: &Value, team_meta: &Value, provider: &str) -> Value
|
|
|
543
539
|
|
|
544
540
|
fn provider_model(team_meta: &Value, provider: &str) -> Option<String> {
|
|
545
541
|
let models = team_meta.get("provider_models")?;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
})
|
|
542
|
+
let provider = parse_canonical_provider(provider)?;
|
|
543
|
+
provider_model_keys(provider)
|
|
544
|
+
.iter()
|
|
545
|
+
.find_map(|key| string_field(models, key))
|
|
551
546
|
}
|
|
552
547
|
|
|
553
548
|
fn builtin_provider_model(provider: &str) -> Option<&'static str> {
|
|
554
|
-
|
|
555
|
-
"claude" | "claude_code" => Some("claude-sonnet-4-6"),
|
|
556
|
-
"codex" => Some("gpt-5.5"),
|
|
557
|
-
_ => None,
|
|
558
|
-
}
|
|
549
|
+
parse_canonical_provider(provider).and_then(wire_builtin_provider_model)
|
|
559
550
|
}
|
|
560
551
|
|
|
561
552
|
fn non_empty_trimmed(text: &str) -> Option<String> {
|
|
@@ -31,8 +31,8 @@ pub fn should_orphan_self_terminate(
|
|
|
31
31
|
/// #236 dedicated `worker.abnormal_exit` surface.
|
|
32
32
|
///
|
|
33
33
|
/// This is intentionally separate from `process_abnormal_records`: the generic
|
|
34
|
-
/// abnormal path is transcript-only, while `worker.abnormal_exit` requires
|
|
35
|
-
/// provider
|
|
34
|
+
/// abnormal path is transcript-only, while `worker.abnormal_exit` requires a fresh
|
|
35
|
+
/// latest explicit provider error before leader notification.
|
|
36
36
|
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
37
37
|
#[serde(tag = "event")]
|
|
38
38
|
pub enum WorkerAbnormalExitSurface {
|
|
@@ -64,7 +64,7 @@ fn detect_compaction(
|
|
|
64
64
|
"coordinator.compaction_observed",
|
|
65
65
|
json!({
|
|
66
66
|
"agent_id": fact.agent_id.as_str(),
|
|
67
|
-
"provider": provider.map(
|
|
67
|
+
"provider": provider.map(runtime_event_provider_name),
|
|
68
68
|
"team": team,
|
|
69
69
|
"compaction_count": current,
|
|
70
70
|
"stuck_loop": false,
|
|
@@ -82,7 +82,7 @@ fn detect_compaction(
|
|
|
82
82
|
"compaction_threshold_crossed.recommend_reset",
|
|
83
83
|
json!({
|
|
84
84
|
"agent_id": fact.agent_id.as_str(),
|
|
85
|
-
"provider": provider.map(
|
|
85
|
+
"provider": provider.map(runtime_event_provider_name),
|
|
86
86
|
"team": team,
|
|
87
87
|
"compaction_count": current,
|
|
88
88
|
"threshold": threshold,
|
|
@@ -239,7 +239,7 @@ fn detect_leader_api_error(
|
|
|
239
239
|
json!({
|
|
240
240
|
"leader_session_uuid": leader_session_uuid,
|
|
241
241
|
"error_class": error_class,
|
|
242
|
-
"provider": provider.map(
|
|
242
|
+
"provider": provider.map(runtime_event_provider_name),
|
|
243
243
|
"partial_response_streamed": partial_response_streamed,
|
|
244
244
|
"worker_dispatch_just_before": [],
|
|
245
245
|
"retry_count": 0,
|
|
@@ -545,7 +545,8 @@ fn leader_receiver_provider(receiver: Option<&Value>) -> Option<Provider> {
|
|
|
545
545
|
serde_json::from_value(Value::String(raw.to_string())).ok()
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
-
|
|
548
|
+
// Event-field grammar, not provider identity parsing.
|
|
549
|
+
fn runtime_event_provider_name(provider: Provider) -> &'static str {
|
|
549
550
|
match provider {
|
|
550
551
|
Provider::Claude => "claude",
|
|
551
552
|
Provider::ClaudeCode => "claude_code",
|