@team-agent/installer 0.5.113 → 0.5.114
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 +252 -84
- package/Cargo.toml +3 -1
- package/crates/team-agent/Cargo.toml +3 -0
- package/crates/team-agent/src/cli/adapters.rs +28 -15
- package/crates/team-agent/src/cli/diagnose.rs +36 -13
- package/crates/team-agent/src/cli/emit.rs +55 -20
- package/crates/team-agent/src/cli/leader.rs +16 -0
- package/crates/team-agent/src/cli/leader_prompt.rs +261 -0
- package/crates/team-agent/src/cli/mod.rs +152 -8
- package/crates/team-agent/src/cli/models.rs +68 -18
- package/crates/team-agent/src/cli/spec.rs +7 -1
- package/crates/team-agent/src/cli/status_port/brief.rs +268 -101
- package/crates/team-agent/src/cli/tests/base.rs +88 -0
- package/crates/team-agent/src/cli/tests/status_send.rs +2 -0
- package/crates/team-agent/src/compiler.rs +66 -18
- package/crates/team-agent/src/contract_runtime/backend.rs +646 -0
- package/crates/team-agent/src/contract_runtime/config.rs +309 -0
- package/crates/team-agent/src/contract_runtime/discovery.rs +237 -0
- package/crates/team-agent/src/contract_runtime/forward.rs +229 -0
- package/crates/team-agent/src/contract_runtime/framework.rs +261 -0
- package/crates/team-agent/src/contract_runtime/mcp.rs +396 -0
- package/crates/team-agent/src/contract_runtime/mod.rs +14 -0
- package/crates/team-agent/src/contract_runtime/prompt.rs +216 -0
- package/crates/team-agent/src/contract_runtime/pump.rs +207 -0
- package/crates/team-agent/src/contract_runtime/registry.rs +30 -0
- package/crates/team-agent/src/coordinator/tick.rs +26 -8
- package/crates/team-agent/src/leader/mod.rs +1 -0
- package/crates/team-agent/src/leader/prompt.rs +906 -0
- package/crates/team-agent/src/leader/start.rs +260 -16
- package/crates/team-agent/src/leader/tests/mod.rs +2 -0
- package/crates/team-agent/src/leader/tests/prompt_contract.rs +268 -0
- package/crates/team-agent/src/leader/types.rs +6 -0
- package/crates/team-agent/src/lib.rs +4 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +472 -388
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +38 -9
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +17 -0
- package/crates/team-agent/src/lifecycle/launch.rs +39 -28
- package/crates/team-agent/src/lifecycle/types.rs +4 -1
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +37 -27
- package/crates/team-agent/src/main.rs +6 -0
- package/crates/team-agent/src/messaging/delivery.rs +50 -1
- package/crates/team-agent/src/messaging/send.rs +10 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +131 -0
- package/crates/team-agent/src/model/spec.rs +24 -12
- package/crates/team-agent/src/state/persist.rs +73 -0
- package/package.json +4 -4
|
@@ -66,6 +66,7 @@ pub mod leaders;
|
|
|
66
66
|
pub mod models;
|
|
67
67
|
pub mod named_address;
|
|
68
68
|
pub mod profile;
|
|
69
|
+
pub(crate) mod leader_prompt;
|
|
69
70
|
pub(crate) mod route;
|
|
70
71
|
pub mod send;
|
|
71
72
|
pub(crate) mod spec;
|
|
@@ -172,12 +173,18 @@ pub mod lifecycle_port {
|
|
|
172
173
|
Err(crate::leader::start::PrepareLeaderStartError::PaneAuthorityRefused(refusal)) => {
|
|
173
174
|
return Ok(pane_authority_refusal_value(provider, attach, &refusal));
|
|
174
175
|
}
|
|
176
|
+
Err(crate::leader::start::PrepareLeaderStartError::Leader(
|
|
177
|
+
crate::leader::LeaderError::Prompt(error),
|
|
178
|
+
)) => return Ok(error.report()),
|
|
175
179
|
Err(crate::leader::start::PrepareLeaderStartError::Leader(error)) => {
|
|
176
180
|
return Err(CliError::Runtime(error.to_string()));
|
|
177
181
|
}
|
|
178
182
|
};
|
|
179
|
-
let outcome = crate::leader::start::execute_prepared_leader_start(&prepared, cwd)
|
|
180
|
-
|
|
183
|
+
let outcome = match crate::leader::start::execute_prepared_leader_start(&prepared, cwd) {
|
|
184
|
+
Ok(outcome) => outcome,
|
|
185
|
+
Err(crate::leader::LeaderError::Prompt(error)) => return Ok(error.report()),
|
|
186
|
+
Err(error) => return Err(CliError::Runtime(error.to_string())),
|
|
187
|
+
};
|
|
181
188
|
let plan = prepared.plan();
|
|
182
189
|
let ok = match outcome.status {
|
|
183
190
|
crate::leader::LeaderLaunchStatus::Exited => outcome.exit_code == Some(0),
|
|
@@ -1297,6 +1304,16 @@ pub mod lifecycle_port {
|
|
|
1297
1304
|
Some(state) => state,
|
|
1298
1305
|
None => shutdown_state_for_team(&run_workspace, team)?,
|
|
1299
1306
|
};
|
|
1307
|
+
#[cfg(unix)]
|
|
1308
|
+
if crate::contract_runtime::framework::has_contract(&state) {
|
|
1309
|
+
crate::contract_runtime::framework::stop_selected(&run_workspace, &state).map_err(
|
|
1310
|
+
|error| {
|
|
1311
|
+
CliError::Runtime(format!(
|
|
1312
|
+
"native shutdown failed; owned resources preserved: {error}"
|
|
1313
|
+
))
|
|
1314
|
+
},
|
|
1315
|
+
)?;
|
|
1316
|
+
}
|
|
1300
1317
|
deadline.check("refresh_provider_sessions")?;
|
|
1301
1318
|
let captured_missing_sessions =
|
|
1302
1319
|
crate::lifecycle::restart::refresh_missing_provider_sessions(&mut state)
|
|
@@ -3108,6 +3125,10 @@ pub mod lifecycle_port {
|
|
|
3108
3125
|
team: Option<&str>,
|
|
3109
3126
|
session_converge_deadline_ms: Option<u64>,
|
|
3110
3127
|
) -> Result<Value, CliError> {
|
|
3128
|
+
#[cfg(unix)]
|
|
3129
|
+
crate::contract_runtime::framework::require_legacy_lifecycle(
|
|
3130
|
+
workspace, team, None, "restart",
|
|
3131
|
+
)?;
|
|
3111
3132
|
match crate::lifecycle::restart_with_session_convergence_deadline(
|
|
3112
3133
|
workspace,
|
|
3113
3134
|
allow_fresh,
|
|
@@ -3128,6 +3149,19 @@ pub mod lifecycle_port {
|
|
|
3128
3149
|
team: Option<&str>,
|
|
3129
3150
|
role_config: &crate::lifecycle::role_config::RoleConfigPatch,
|
|
3130
3151
|
) -> Result<Value, CliError> {
|
|
3152
|
+
#[cfg(unix)]
|
|
3153
|
+
{
|
|
3154
|
+
crate::contract_runtime::framework::require_legacy_lifecycle(
|
|
3155
|
+
workspace,
|
|
3156
|
+
team,
|
|
3157
|
+
Some(agent),
|
|
3158
|
+
"start-agent",
|
|
3159
|
+
)?;
|
|
3160
|
+
crate::contract_runtime::framework::require_legacy_role(
|
|
3161
|
+
None,
|
|
3162
|
+
role_config.provider.as_deref(),
|
|
3163
|
+
)?;
|
|
3164
|
+
}
|
|
3131
3165
|
let _ = force; // Never grants authority to stop an already-running seat.
|
|
3132
3166
|
let agent_id = crate::model::ids::AgentId::new(agent);
|
|
3133
3167
|
match crate::lifecycle::role_config::start_agent_from_role(
|
|
@@ -3195,6 +3229,13 @@ pub mod lifecycle_port {
|
|
|
3195
3229
|
team: Option<&str>,
|
|
3196
3230
|
) -> Result<Value, CliError> {
|
|
3197
3231
|
let agent_id = crate::model::ids::AgentId::new(agent);
|
|
3232
|
+
#[cfg(unix)]
|
|
3233
|
+
crate::contract_runtime::framework::require_legacy_lifecycle(
|
|
3234
|
+
workspace,
|
|
3235
|
+
team,
|
|
3236
|
+
Some(agent),
|
|
3237
|
+
"stop-agent",
|
|
3238
|
+
)?;
|
|
3198
3239
|
match crate::lifecycle::stop_agent(workspace, &agent_id, team) {
|
|
3199
3240
|
Ok(report) => Ok(json!({"ok": true, "agent_id": agent, "stopped": report.stopped})),
|
|
3200
3241
|
Err(e) => Ok(error_value(e)),
|
|
@@ -3209,6 +3250,13 @@ pub mod lifecycle_port {
|
|
|
3209
3250
|
team: Option<&str>,
|
|
3210
3251
|
) -> Result<Value, CliError> {
|
|
3211
3252
|
let agent_id = crate::model::ids::AgentId::new(agent);
|
|
3253
|
+
#[cfg(unix)]
|
|
3254
|
+
crate::contract_runtime::framework::require_legacy_lifecycle(
|
|
3255
|
+
workspace,
|
|
3256
|
+
team,
|
|
3257
|
+
Some(agent),
|
|
3258
|
+
"reset-agent",
|
|
3259
|
+
)?;
|
|
3212
3260
|
match crate::lifecycle::reset_agent(workspace, &agent_id, discard_session, true, team) {
|
|
3213
3261
|
Ok(crate::lifecycle::ResetAgentOutcome::Reset {
|
|
3214
3262
|
env,
|
|
@@ -3256,6 +3304,19 @@ pub mod lifecycle_port {
|
|
|
3256
3304
|
) -> Result<Value, CliError> {
|
|
3257
3305
|
let agent_id = crate::model::ids::AgentId::new(agent);
|
|
3258
3306
|
let source = (!role_file.is_empty()).then(|| Path::new(role_file));
|
|
3307
|
+
#[cfg(unix)]
|
|
3308
|
+
{
|
|
3309
|
+
crate::contract_runtime::framework::require_legacy_lifecycle(
|
|
3310
|
+
workspace,
|
|
3311
|
+
team,
|
|
3312
|
+
Some(agent),
|
|
3313
|
+
"add-agent",
|
|
3314
|
+
)?;
|
|
3315
|
+
crate::contract_runtime::framework::require_legacy_role(
|
|
3316
|
+
source,
|
|
3317
|
+
role_config.provider.as_deref(),
|
|
3318
|
+
)?;
|
|
3319
|
+
}
|
|
3259
3320
|
match crate::lifecycle::role_config::add_agent_from_role(
|
|
3260
3321
|
workspace,
|
|
3261
3322
|
&agent_id,
|
|
@@ -3308,6 +3369,13 @@ pub mod lifecycle_port {
|
|
|
3308
3369
|
) -> Result<Value, CliError> {
|
|
3309
3370
|
let source = crate::model::ids::AgentId::new(source_agent);
|
|
3310
3371
|
let dest = crate::model::ids::AgentId::new(as_agent_id);
|
|
3372
|
+
#[cfg(unix)]
|
|
3373
|
+
crate::contract_runtime::framework::require_legacy_lifecycle(
|
|
3374
|
+
workspace,
|
|
3375
|
+
team,
|
|
3376
|
+
Some(source_agent),
|
|
3377
|
+
"fork-agent",
|
|
3378
|
+
)?;
|
|
3311
3379
|
match crate::lifecycle::fork_agent(workspace, &source, &dest, label, true, team) {
|
|
3312
3380
|
Ok(report) => {
|
|
3313
3381
|
let mut value = json!({
|
|
@@ -3345,6 +3413,13 @@ pub mod lifecycle_port {
|
|
|
3345
3413
|
) -> Result<Value, CliError> {
|
|
3346
3414
|
let source = crate::model::ids::AgentId::new(source_agent);
|
|
3347
3415
|
let dest = crate::model::ids::AgentId::new(as_agent_id);
|
|
3416
|
+
#[cfg(unix)]
|
|
3417
|
+
crate::contract_runtime::framework::require_legacy_lifecycle(
|
|
3418
|
+
workspace,
|
|
3419
|
+
team,
|
|
3420
|
+
Some(source_agent),
|
|
3421
|
+
"clone-agent",
|
|
3422
|
+
)?;
|
|
3348
3423
|
match crate::lifecycle::clone_agent(workspace, &source, &dest, label, true, team) {
|
|
3349
3424
|
Ok(report) => Ok(json!({
|
|
3350
3425
|
"ok": true,
|
|
@@ -3370,6 +3445,13 @@ pub mod lifecycle_port {
|
|
|
3370
3445
|
team: Option<&str>,
|
|
3371
3446
|
) -> Result<Value, CliError> {
|
|
3372
3447
|
let agent_id = crate::model::ids::AgentId::new(agent);
|
|
3448
|
+
#[cfg(unix)]
|
|
3449
|
+
crate::contract_runtime::framework::require_legacy_lifecycle(
|
|
3450
|
+
workspace,
|
|
3451
|
+
team,
|
|
3452
|
+
Some(agent),
|
|
3453
|
+
"remove-agent",
|
|
3454
|
+
)?;
|
|
3373
3455
|
match crate::lifecycle::remove_agent_flag_requirements(workspace, &agent_id, team) {
|
|
3374
3456
|
Ok(requirements) => {
|
|
3375
3457
|
if !remove_agent_missing_flags(from_spec, confirm, force, &requirements).is_empty()
|
|
@@ -3712,7 +3794,13 @@ pub mod lifecycle_port {
|
|
|
3712
3794
|
// tool-set load has not been confirmed yet (PendingToolLoad).
|
|
3713
3795
|
let incomplete_session_capture_agents =
|
|
3714
3796
|
launch.session_capture_incomplete_agents.clone();
|
|
3715
|
-
let
|
|
3797
|
+
let agent_ids: Vec<_> = launch
|
|
3798
|
+
.started
|
|
3799
|
+
.iter()
|
|
3800
|
+
.map(|agent| agent.agent_id.as_str())
|
|
3801
|
+
.chain(launch.contract_started.iter().map(|id| id.as_str()))
|
|
3802
|
+
.collect();
|
|
3803
|
+
let all_spawned = !agent_ids.is_empty();
|
|
3716
3804
|
let leader_receiver_attached = launch.leader_receiver_attached;
|
|
3717
3805
|
let all_resumable_have_session = incomplete_session_capture_agents.is_empty();
|
|
3718
3806
|
let all_workers_spawned = all_spawned;
|
|
@@ -3840,7 +3928,7 @@ pub mod lifecycle_port {
|
|
|
3840
3928
|
"ready": readiness_json.get("ready").cloned().unwrap_or(Value::Bool(false)),
|
|
3841
3929
|
"session_name": session_name.as_str(),
|
|
3842
3930
|
"team": team,
|
|
3843
|
-
"agent_ids":
|
|
3931
|
+
"agent_ids": agent_ids,
|
|
3844
3932
|
"dry_run": launch.dry_run,
|
|
3845
3933
|
"next_actions": next_actions,
|
|
3846
3934
|
"attach_commands": attach_commands,
|
|
@@ -3961,9 +4049,8 @@ pub mod lifecycle_port {
|
|
|
3961
4049
|
.is_some());
|
|
3962
4050
|
}
|
|
3963
4051
|
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
let value = quick_start_value(crate::lifecycle::QuickStartReport::Ready {
|
|
4052
|
+
fn pending_tool_load_report() -> crate::lifecycle::QuickStartReport {
|
|
4053
|
+
crate::lifecycle::QuickStartReport::Ready {
|
|
3967
4054
|
session_name: crate::transport::SessionName::new("team-demo"),
|
|
3968
4055
|
launch: Box::new(crate::lifecycle::LaunchReport {
|
|
3969
4056
|
session_name: crate::transport::SessionName::new("team-demo"),
|
|
@@ -3979,6 +4066,7 @@ pub mod lifecycle_port {
|
|
|
3979
4066
|
provider_projects_root: None,
|
|
3980
4067
|
managed_mcp_config: false,
|
|
3981
4068
|
}],
|
|
4069
|
+
contract_started: Vec::new(),
|
|
3982
4070
|
dry_run: false,
|
|
3983
4071
|
tmux_endpoint: None,
|
|
3984
4072
|
routes: Vec::new(),
|
|
@@ -3991,7 +4079,63 @@ pub mod lifecycle_port {
|
|
|
3991
4079
|
attach_commands: Vec::new(),
|
|
3992
4080
|
worker_readiness: crate::lifecycle::QuickStartReadiness::PendingToolLoad,
|
|
3993
4081
|
team: "team-demo".to_string(),
|
|
3994
|
-
}
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
|
|
4085
|
+
#[test]
|
|
4086
|
+
fn contract_start_receipts_join_summary_without_bypassing_leader_binding() {
|
|
4087
|
+
for (legacy, contract) in [(false, true), (true, true), (true, false), (false, false)] {
|
|
4088
|
+
for bound in [false, true] {
|
|
4089
|
+
let mut report = pending_tool_load_report();
|
|
4090
|
+
let crate::lifecycle::QuickStartReport::Ready { launch, .. } = &mut report else {
|
|
4091
|
+
panic!("ready fixture required");
|
|
4092
|
+
};
|
|
4093
|
+
if !legacy {
|
|
4094
|
+
launch.started.clear();
|
|
4095
|
+
}
|
|
4096
|
+
if contract {
|
|
4097
|
+
launch
|
|
4098
|
+
.contract_started
|
|
4099
|
+
.push(crate::model::ids::AgentId::new("kiro-worker"));
|
|
4100
|
+
}
|
|
4101
|
+
launch.leader_receiver_attached = bound;
|
|
4102
|
+
launch.leader_bind_stage = (!bound).then(|| "caller_pane".into());
|
|
4103
|
+
launch.leader_bind_reason = (!bound).then(|| "caller_pane_missing".into());
|
|
4104
|
+
assert_eq!(launch.started.len(), usize::from(legacy));
|
|
4105
|
+
let expected: Vec<_> = [(legacy, "worker"), (contract, "kiro-worker")]
|
|
4106
|
+
.into_iter()
|
|
4107
|
+
.filter_map(|(started, id)| started.then_some(id))
|
|
4108
|
+
.collect();
|
|
4109
|
+
let value = quick_start_value(report);
|
|
4110
|
+
assert_eq!(value["agent_ids"], json!(expected));
|
|
4111
|
+
assert_eq!(
|
|
4112
|
+
value["readiness"]["all_workers_spawned"],
|
|
4113
|
+
json!(legacy || contract)
|
|
4114
|
+
);
|
|
4115
|
+
assert_eq!(value["readiness"]["all_spawned"], json!(legacy || contract));
|
|
4116
|
+
assert_eq!(value["readiness"]["leader_receiver_attached"], json!(bound));
|
|
4117
|
+
assert_eq!(value["ok"], json!((legacy || contract) && bound));
|
|
4118
|
+
assert_eq!(value["ready"], json!((legacy || contract) && bound));
|
|
4119
|
+
if !bound {
|
|
4120
|
+
assert_eq!(value["status"], "leader_binding_incomplete");
|
|
4121
|
+
assert_eq!(value["reason"], "caller_pane_missing");
|
|
4122
|
+
} else {
|
|
4123
|
+
assert_eq!(value["status"], "pending_tool_load");
|
|
4124
|
+
}
|
|
4125
|
+
let mut compact = value.clone();
|
|
4126
|
+
compact_quick_start_value(&mut compact);
|
|
4127
|
+
assert_eq!(
|
|
4128
|
+
compact["worker_readiness"]["all_workers_spawned"],
|
|
4129
|
+
json!(legacy || contract)
|
|
4130
|
+
);
|
|
4131
|
+
assert_eq!(compact["ready"], value["ready"]);
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
#[test]
|
|
4137
|
+
fn pending_tool_load_success_output_explains_bound_send_without_doctor() {
|
|
4138
|
+
let value = quick_start_value(pending_tool_load_report());
|
|
3995
4139
|
assert_eq!(
|
|
3996
4140
|
value.get("summary").and_then(Value::as_str),
|
|
3997
4141
|
Some("team started; leader bound; send a task next (worker tool load unverified): team-demo")
|
|
@@ -22,6 +22,10 @@ enum CatalogFormat {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
pub fn cmd_models(args: &ModelsArgs) -> Result<CmdResult, CliError> {
|
|
25
|
+
#[cfg(unix)]
|
|
26
|
+
if crate::contract_runtime::registry::descriptor(&args.provider).is_some() {
|
|
27
|
+
return crate::contract_runtime::discovery::models(args);
|
|
28
|
+
}
|
|
25
29
|
let records = match crate::provider::model_catalog::discover_model_catalog(&args.provider) {
|
|
26
30
|
Ok(records) => records,
|
|
27
31
|
Err(error) => {
|
|
@@ -36,7 +40,12 @@ pub fn cmd_models(args: &ModelsArgs) -> Result<CmdResult, CliError> {
|
|
|
36
40
|
fn render_catalog(args: &ModelsArgs, all: Vec<ModelRecord>) -> Result<CmdResult, CliError> {
|
|
37
41
|
let visible = args.search.as_deref().map_or_else(
|
|
38
42
|
|| all.clone(),
|
|
39
|
-
|search|
|
|
43
|
+
|search| {
|
|
44
|
+
all.iter()
|
|
45
|
+
.filter(|model| crate::provider::model_catalog::model_matches(model, search))
|
|
46
|
+
.cloned()
|
|
47
|
+
.collect()
|
|
48
|
+
},
|
|
40
49
|
);
|
|
41
50
|
let current = if args.provider == "pi" {
|
|
42
51
|
current_role_model(&all.iter().map(|model| model.id.clone()).collect::<Vec<_>>())
|
|
@@ -66,14 +75,31 @@ fn render_catalog(args: &ModelsArgs, all: Vec<ModelRecord>) -> Result<CmdResult,
|
|
|
66
75
|
preserve_json_order: true,
|
|
67
76
|
})
|
|
68
77
|
} else {
|
|
69
|
-
let mut lines = vec![format!(
|
|
78
|
+
let mut lines = vec![format!(
|
|
79
|
+
"models.v1 | {provider} models (copyable model_id):"
|
|
80
|
+
)];
|
|
70
81
|
lines.push("PROVIDER VENDOR MODEL_ID DISPLAY_NAME CURRENT DEFAULT".to_string());
|
|
71
|
-
lines.extend(visible.iter().map(|model|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
lines.extend(visible.iter().map(|model| {
|
|
83
|
+
format!(
|
|
84
|
+
"{} {} {} {} {} {}",
|
|
85
|
+
model.provider,
|
|
86
|
+
model.vendor,
|
|
87
|
+
model.id,
|
|
88
|
+
model.display_name,
|
|
89
|
+
if args.provider == "pi" {
|
|
90
|
+
(current.as_deref() == Some(model.id.as_str())).to_string()
|
|
91
|
+
} else {
|
|
92
|
+
"—".to_string()
|
|
93
|
+
},
|
|
94
|
+
model
|
|
95
|
+
.default
|
|
96
|
+
.map_or_else(|| "—".to_string(), |value| value.to_string()),
|
|
97
|
+
)
|
|
98
|
+
}));
|
|
99
|
+
lines.push(format!(
|
|
100
|
+
"auth: ok (catalog_visibility); {} model(s)",
|
|
101
|
+
visible.len()
|
|
102
|
+
));
|
|
77
103
|
if visible.is_empty() {
|
|
78
104
|
lines.push("No models matched --search/query; rerun without a search to list the full catalog.".to_string());
|
|
79
105
|
}
|
|
@@ -83,12 +109,23 @@ fn render_catalog(args: &ModelsArgs, all: Vec<ModelRecord>) -> Result<CmdResult,
|
|
|
83
109
|
|
|
84
110
|
/// Existing injectable Pi boundary retained for deterministic command/timeout tests.
|
|
85
111
|
#[cfg(test)]
|
|
86
|
-
fn cmd_models_with(
|
|
112
|
+
fn cmd_models_with(
|
|
113
|
+
args: &ModelsArgs,
|
|
114
|
+
program: &Path,
|
|
115
|
+
timeout: Duration,
|
|
116
|
+
max_bytes: u64,
|
|
117
|
+
) -> Result<CmdResult, CliError> {
|
|
87
118
|
cmd_models_with_format(args, program, CatalogFormat::Pi, timeout, max_bytes)
|
|
88
119
|
}
|
|
89
120
|
|
|
90
121
|
#[cfg(test)]
|
|
91
|
-
fn cmd_models_with_format(
|
|
122
|
+
fn cmd_models_with_format(
|
|
123
|
+
args: &ModelsArgs,
|
|
124
|
+
program: &Path,
|
|
125
|
+
format: CatalogFormat,
|
|
126
|
+
timeout: Duration,
|
|
127
|
+
max_bytes: u64,
|
|
128
|
+
) -> Result<CmdResult, CliError> {
|
|
92
129
|
let bytes = match run_catalog(program, timeout, max_bytes) {
|
|
93
130
|
Ok(bytes) => bytes,
|
|
94
131
|
Err(message) => return Ok(failure(args, &message)),
|
|
@@ -102,7 +139,10 @@ fn cmd_models_with_format(args: &ModelsArgs, program: &Path, format: CatalogForm
|
|
|
102
139
|
|
|
103
140
|
#[cfg(test)]
|
|
104
141
|
fn parse_catalog(bytes: &[u8], format: CatalogFormat) -> Result<Vec<ModelRecord>, String> {
|
|
105
|
-
let provider = match format {
|
|
142
|
+
let provider = match format {
|
|
143
|
+
CatalogFormat::Pi => "pi",
|
|
144
|
+
CatalogFormat::CursorAgent => "cursor_agent",
|
|
145
|
+
};
|
|
106
146
|
let result = match format {
|
|
107
147
|
CatalogFormat::Pi => crate::provider::model_catalog::parse_pi_catalog(bytes),
|
|
108
148
|
CatalogFormat::CursorAgent => crate::provider::model_catalog::parse_cursor_catalog(bytes),
|
|
@@ -124,7 +164,12 @@ fn model_matches(model: &ModelRecord, search: &str) -> bool {
|
|
|
124
164
|
#[cfg(test)]
|
|
125
165
|
fn current_model_for(format: CatalogFormat, models: &[ModelRecord]) -> Option<String> {
|
|
126
166
|
match format {
|
|
127
|
-
CatalogFormat::Pi => current_role_model(
|
|
167
|
+
CatalogFormat::Pi => current_role_model(
|
|
168
|
+
&models
|
|
169
|
+
.iter()
|
|
170
|
+
.map(|model| model.id.clone())
|
|
171
|
+
.collect::<Vec<_>>(),
|
|
172
|
+
),
|
|
128
173
|
CatalogFormat::CursorAgent => None,
|
|
129
174
|
}
|
|
130
175
|
}
|
|
@@ -148,8 +193,14 @@ fn failure(args: &ModelsArgs, message: &str) -> CmdResult {
|
|
|
148
193
|
fn failure_with_action(args: &ModelsArgs, message: &str, action: &str) -> CmdResult {
|
|
149
194
|
let value = json!({ "schema_version": "models.v1", "ok": false, "provider": args.provider, "auth": "not_ready", "auth_basis": "catalog_visibility", "models": Value::Array(Vec::new()), "current_role_model": Value::Null, "error": message, "action": action });
|
|
150
195
|
if args.json {
|
|
151
|
-
let text = serde_json::to_string_pretty(&value)
|
|
152
|
-
|
|
196
|
+
let text = serde_json::to_string_pretty(&value)
|
|
197
|
+
.unwrap_or_else(|_| "{\"schema_version\":\"models.v1\",\"ok\":false}".to_string());
|
|
198
|
+
CmdResult {
|
|
199
|
+
output: CmdOutput::Human(text),
|
|
200
|
+
exit: ExitCode::Error,
|
|
201
|
+
as_json: false,
|
|
202
|
+
preserve_json_order: true,
|
|
203
|
+
}
|
|
153
204
|
} else {
|
|
154
205
|
CmdResult { output: CmdOutput::Human(format!("models.v1 | error: {message}\naction: {action}\nauth: not_ready (catalog_visibility)")), exit: ExitCode::Error, as_json: false, preserve_json_order: false }
|
|
155
206
|
}
|
|
@@ -1271,10 +1322,9 @@ mod tests {
|
|
|
1271
1322
|
),
|
|
1272
1323
|
ExitCode::Error
|
|
1273
1324
|
);
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
);
|
|
1325
|
+
let usage = "team-agent models [--provider pi|cursor_agent|codex|claude|claude_code|grok|kiro] [QUERY|--search TEXT] [--json]";
|
|
1326
|
+
assert_eq!(crate::cli::spec::command_spec("models").unwrap().usage, usage);
|
|
1327
|
+
assert!(crate::cli::emit::command_help(Some("models")).contains(usage));
|
|
1278
1328
|
}
|
|
1279
1329
|
|
|
1280
1330
|
#[test]
|
|
@@ -36,6 +36,7 @@ pub(crate) enum DispatchKind {
|
|
|
36
36
|
Send,
|
|
37
37
|
Status,
|
|
38
38
|
Models,
|
|
39
|
+
KiroLeader,
|
|
39
40
|
Inbox,
|
|
40
41
|
Restart,
|
|
41
42
|
Shutdown,
|
|
@@ -50,6 +51,7 @@ pub(crate) enum DispatchKind {
|
|
|
50
51
|
Doctor,
|
|
51
52
|
Approvals,
|
|
52
53
|
Route,
|
|
54
|
+
LeaderPrompt,
|
|
53
55
|
Profile,
|
|
54
56
|
InstallSkill,
|
|
55
57
|
ClaimLeader,
|
|
@@ -61,6 +63,7 @@ pub(crate) const ALL_DISPATCH_KINDS: &[DispatchKind] = &[
|
|
|
61
63
|
DispatchKind::Send,
|
|
62
64
|
DispatchKind::Status,
|
|
63
65
|
DispatchKind::Models,
|
|
66
|
+
DispatchKind::KiroLeader,
|
|
64
67
|
DispatchKind::Inbox,
|
|
65
68
|
DispatchKind::Restart,
|
|
66
69
|
DispatchKind::Shutdown,
|
|
@@ -75,6 +78,7 @@ pub(crate) const ALL_DISPATCH_KINDS: &[DispatchKind] = &[
|
|
|
75
78
|
DispatchKind::Doctor,
|
|
76
79
|
DispatchKind::Approvals,
|
|
77
80
|
DispatchKind::Route,
|
|
81
|
+
DispatchKind::LeaderPrompt,
|
|
78
82
|
DispatchKind::Profile,
|
|
79
83
|
DispatchKind::InstallSkill,
|
|
80
84
|
DispatchKind::ClaimLeader,
|
|
@@ -109,7 +113,7 @@ pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
|
|
|
109
113
|
CommandSpec { name: "quick-start", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::QuickStart), summary: "Create a team from role files; use restart for an initialized team", usage: "team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--team TEAM] [--name NAME] [--team-id TEAM] [--yes] [--backend tmux|conpty] [--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
110
114
|
CommandSpec { name: "send", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Send), summary: "Send a task to a specified agent", usage: "team-agent send <agent> MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--mailbox] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: Some("a future compatibility release"), action: Some("Use team-agent send <agent> 'task message'; select scope with --workspace and --team"), governance: None },
|
|
111
115
|
CommandSpec { name: "status", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Status), summary: "Check agent status without reading conversation content", usage: "team-agent status [<agent>] [--workspace WORKSPACE] [--team TEAM] [--json] [--summary|--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
112
|
-
CommandSpec { name: "models", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Models), summary: "Discover model names supported by the native provider", usage: "team-agent models [--provider pi|cursor_agent|codex|claude|claude_code|grok] [QUERY|--search TEXT] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
116
|
+
CommandSpec { name: "models", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Models), summary: "Discover model names supported by the native provider", usage: "team-agent models [--provider pi|cursor_agent|codex|claude|claude_code|grok|kiro] [QUERY|--search TEXT] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
113
117
|
CommandSpec { name: "inbox", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Inbox), summary: "Read agent replies", usage: "team-agent inbox <agent> [-n N|--limit N] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
114
118
|
CommandSpec { name: "restart", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Restart), summary: "Restart an existing team using saved sessions when available", usage: "team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
115
119
|
CommandSpec { name: "shutdown", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Shutdown), summary: "Shut down the selected team and report residual resources", usage: "team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
@@ -124,11 +128,13 @@ pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
|
|
|
124
128
|
CommandSpec { name: "doctor", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Doctor), summary: "Diagnose a team and suggest recovery; read-only by default", usage: "team-agent doctor [SPEC] [--workspace WORKSPACE] [--team TEAM] [--comms] [--gate orphans|comms] [--fix] [--fix-schema] [--cleanup-orphans] [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
125
129
|
CommandSpec { name: "approvals", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Approvals), summary: "List pending agent approvals", usage: "team-agent approvals [<agent>] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
126
130
|
CommandSpec { name: "route", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Route), summary: "Configure additional provider launch arguments", usage: "team-agent route [status|enable|disable|show [TOOL]|set TOOL -- ARG...|add TOOL -- ARG...|clear TOOL] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
131
|
+
CommandSpec { name: "leader-prompt", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::LeaderPrompt), summary: "Persist supplemental instructions for new Leaders only", usage: "team-agent leader-prompt [show|set|append|clear] [--json] [--file PATH | -- TEXT]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
127
132
|
CommandSpec { name: "profile", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Profile), summary: "Manage authentication and proxy profiles", usage: "team-agent profile init|doctor|show NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--proxy-mode direct|inherit] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
128
133
|
CommandSpec { name: "install-skill", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::InstallSkill), summary: "Optionally install an existing public operation skill", usage: "team-agent install-skill (--source DIR|--uninstall) [--target codex|claude|copilot|all] [--dest DIR] [--dry-run] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
129
134
|
CommandSpec { name: "claim-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::ClaimLeader), summary: "Register the current terminal as leader only when diagnosis recommends it", usage: "team-agent claim-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
130
135
|
CommandSpec { name: "takeover", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::Takeover), summary: "Take over an existing team only after diagnosis and confirmation", usage: "team-agent takeover [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
131
136
|
CommandSpec { name: "attach-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::AttachLeader), summary: "Attach a verified leader terminal only when diagnosis recommends it", usage: "team-agent attach-leader [--workspace WORKSPACE] [--team TEAM] --pane PANE --provider TOOL [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
137
|
+
CommandSpec { name: "kiro", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::KiroLeader), summary: "Check Kiro leader launch admission (currently refused)", usage: "team-agent kiro [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
132
138
|
CommandSpec { name: "pi", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "pi" }, summary: "Open a leader session with Pi", usage: "team-agent pi [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
133
139
|
CommandSpec { name: "codex", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "codex" }, summary: "Open a leader session with Codex", usage: "team-agent codex [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
134
140
|
CommandSpec { name: "claude", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "claude" }, summary: "Open a leader session with Claude Code", usage: "team-agent claude [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|