@team-agent/installer 0.5.106 → 0.5.108

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.
Files changed (41) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +1 -1
  4. package/crates/team-agent/src/cli/emit.rs +3 -3
  5. package/crates/team-agent/src/cli/mod.rs +87 -4
  6. package/crates/team-agent/src/cli/models.rs +252 -3
  7. package/crates/team-agent/src/cli/spec.rs +1 -1
  8. package/crates/team-agent/src/cli/status_port/brief.rs +155 -37
  9. package/crates/team-agent/src/cli/tests/base.rs +112 -225
  10. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +184 -0
  11. package/crates/team-agent/src/cli/tests/status_brief.rs +624 -0
  12. package/crates/team-agent/src/compiler/tests.rs +46 -59
  13. package/crates/team-agent/src/leader/tests/byte_findings.rs +1 -14
  14. package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +57 -173
  15. package/crates/team-agent/src/lifecycle/launch/pi_mcp_extension.js +226 -0
  16. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +0 -37
  17. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +0 -9
  18. package/crates/team-agent/src/lifecycle/tests/fixtures/pi_native_mcp_host.mjs +71 -0
  19. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +0 -66
  20. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +0 -392
  21. package/crates/team-agent/src/lifecycle/tests/lifecycle_lock.rs +0 -5
  22. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +2 -361
  23. package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +1 -16
  24. package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +20 -112
  25. package/crates/team-agent/src/lifecycle/tests/pi_fork_session_inheritance_red.rs +1 -12
  26. package/crates/team-agent/src/lifecycle/tests/pi_native_mcp_capability_red.rs +546 -0
  27. package/crates/team-agent/src/lifecycle/tests.rs +1 -0
  28. package/crates/team-agent/src/lifecycle/worker_command_context.rs +11 -54
  29. package/crates/team-agent/src/messaging/tests/wave2.rs +2 -302
  30. package/crates/team-agent/src/model/name_similarity.rs +52 -67
  31. package/crates/team-agent/src/model/task_graph.rs +37 -124
  32. package/crates/team-agent/src/model/yaml/tests.rs +46 -165
  33. package/crates/team-agent/src/provider/model_catalog.rs +166 -3
  34. package/crates/team-agent/src/provider/testdata/cursor-list-models-native.stdout +250 -0
  35. package/crates/team-agent/src/provider/testdata/grok-models-authenticated.stdout +9 -0
  36. package/crates/team-agent/src/provider/testdata/grok-models-unauthenticated.stdout +7 -0
  37. package/crates/team-agent/src/provider/tests/adapter.rs +0 -20
  38. package/crates/team-agent/src/provider/tests/classify.rs +106 -198
  39. package/crates/team-agent/src/tmux_backend/tests.rs +178 -501
  40. package/crates/team-agent/src/transport/tests/wire.rs +0 -55
  41. package/package.json +4 -4
@@ -1,13 +1,9 @@
1
1
  use super::launch_spawn::seed_healthy_coordinator;
2
2
  use super::*;
3
- use crate::cli::{
4
- cmd_doctor, cmd_send, cmd_status, lifecycle_port, CmdOutput, CmdResult, DoctorArgs,
5
- SendArgs, StatusArgs,
6
- };
3
+ use crate::cli::{cmd_doctor, CmdOutput, CmdResult, DoctorArgs};
7
4
  use crate::transport::test_support::OfflineTransport;
8
- use crate::transport::{PaneId, PaneInfo, SessionName, WindowName};
9
5
  use serde_json::{json, Map, Value};
10
- use std::collections::{BTreeMap, BTreeSet};
6
+ use std::collections::BTreeMap;
11
7
  use std::path::{Path, PathBuf};
12
8
  use std::sync::atomic::{AtomicU64, Ordering};
13
9
 
@@ -96,91 +92,6 @@ fn hermetic_caller_envs() -> &'static [&'static str] {
96
92
  ]
97
93
  }
98
94
 
99
- #[test]
100
- #[ignore = "legacy display-backed phase golden fixture superseded by silent tmux runtime"]
101
- #[serial_test::serial(env)]
102
- fn phase_b_golden_events_state_status_zero_drift() {
103
- let baseline = phase_fixture_path("phase_b").join("golden.json");
104
- let actual = run_phase_golden(PhaseGolden {
105
- phase: "phase_b",
106
- team_key: "teamdir",
107
- lifecycle_op: phase_b_reset_discard_session,
108
- });
109
- let expected = load_phase_golden(&baseline);
110
- assert_eq!(
111
- actual, expected,
112
- "phase B golden drift; surgical key edits only — do not regenerate the file"
113
- );
114
- }
115
-
116
- #[test]
117
- #[ignore = "legacy display-backed phase golden fixture superseded by silent tmux runtime"]
118
- #[serial_test::serial(env)]
119
- fn phase_c_golden_events_state_status_zero_drift() {
120
- let baseline = phase_fixture_path("phase_c").join("golden.json");
121
- let actual = run_phase_golden(PhaseGolden {
122
- phase: "phase_c",
123
- team_key: "teamdir",
124
- lifecycle_op: phase_b_reset_discard_session,
125
- });
126
- let expected = load_phase_golden(&baseline);
127
- assert_eq!(
128
- actual, expected,
129
- "phase C golden drift; surgical key edits only — do not regenerate the file"
130
- );
131
- }
132
-
133
- #[test]
134
- #[ignore = "legacy display-backed phase golden fixture superseded by silent tmux runtime"]
135
- #[serial_test::serial(env)]
136
- fn phase_d_golden_events_state_status_zero_drift() {
137
- let baseline = phase_fixture_path("phase_d").join("golden.json");
138
- let actual = run_phase_golden(PhaseGolden {
139
- phase: "phase_d",
140
- team_key: "teamdir",
141
- lifecycle_op: phase_b_reset_discard_session,
142
- });
143
- let expected = load_phase_golden(&baseline);
144
- assert_eq!(
145
- actual, expected,
146
- "phase D golden drift; surgical key edits only — do not regenerate the file"
147
- );
148
- }
149
-
150
- #[test]
151
- #[ignore = "legacy display-backed phase golden fixture superseded by silent tmux runtime"]
152
- #[serial_test::serial(env)]
153
- fn phase_e_golden_events_state_status_zero_drift() {
154
- let baseline = phase_fixture_path("phase_e").join("golden.json");
155
- let actual = run_phase_golden(PhaseGolden {
156
- phase: "phase_e",
157
- team_key: "teamdir",
158
- lifecycle_op: phase_b_reset_discard_session,
159
- });
160
- let expected = load_phase_golden(&baseline);
161
- assert_eq!(
162
- actual, expected,
163
- "phase E golden drift; surgical key edits only — do not regenerate the file"
164
- );
165
- }
166
-
167
- #[test]
168
- #[ignore = "legacy display-backed phase golden fixture superseded by silent tmux runtime"]
169
- #[serial_test::serial(env)]
170
- fn phase_f_golden_events_state_status_zero_drift() {
171
- let baseline = phase_fixture_path("phase_f").join("golden.json");
172
- let actual = run_phase_golden(PhaseGolden {
173
- phase: "phase_f",
174
- team_key: "teamdir",
175
- lifecycle_op: phase_b_reset_discard_session,
176
- });
177
- let expected = load_phase_golden(&baseline);
178
- assert_eq!(
179
- actual, expected,
180
- "phase F golden drift; surgical key edits only — do not regenerate the file"
181
- );
182
- }
183
-
184
95
  #[test]
185
96
  #[serial_test::serial(env)]
186
97
  fn phase_golden_unbound_not_ready_requires_claim_leader() {
@@ -221,202 +132,6 @@ fn phase_golden_unbound_not_ready_requires_claim_leader() {
221
132
  );
222
133
  }
223
134
 
224
- #[derive(Clone, Copy)]
225
- struct PhaseGolden {
226
- phase: &'static str,
227
- team_key: &'static str,
228
- lifecycle_op: fn(&Path, &OfflineTransport, &'static str) -> Value,
229
- }
230
-
231
- fn run_phase_golden(spec: PhaseGolden) -> Value {
232
- let hermetic = HermeticTestEnv::enter(spec.phase);
233
- let _permission_mode = EnvVarGuard::set(ANCESTRY_ENV, "[]");
234
- let team = two_worker_team_dir(&hermetic);
235
- let workspace = team.parent().expect("workspace").to_path_buf();
236
- seed_healthy_coordinator(&workspace);
237
- let _caller_pane = EnvVarGuard::set("TMUX_PANE", "%caller");
238
- let launch_transport = codex_ready_transport().with_targets(vec![PaneInfo {
239
- pane_id: PaneId::new("%caller"),
240
- session: SessionName::new("caller"),
241
- window_index: Some(0),
242
- window_name: Some(WindowName::new("caller")),
243
- pane_index: Some(0),
244
- tty: None,
245
- current_command: Some("codex".to_string()),
246
- current_path: Some(workspace.clone()),
247
- active: true,
248
- pane_pid: None,
249
- leader_env: BTreeMap::new(),
250
- }])
251
- .with_pane_current_command("%caller", "codex");
252
- let quick_start = quick_start_with_transport_in_workspace(
253
- &workspace,
254
- &team,
255
- None,
256
- true,
257
- None,
258
- &launch_transport,
259
- );
260
- let status_compact = cmd_status(&StatusArgs {
261
- agent: None,
262
- workspace: workspace.clone(),
263
- detail: false,
264
- summary: false,
265
- json: true,
266
- team: Some(spec.team_key.to_string()),
267
- });
268
- let status_detail = cmd_status(&StatusArgs {
269
- agent: None,
270
- workspace: workspace.clone(),
271
- detail: true,
272
- summary: false,
273
- json: true,
274
- team: Some(spec.team_key.to_string()),
275
- });
276
- let send = cmd_send(&SendArgs {
277
- target: Some("w1".to_string()),
278
- message: vec!["phase".to_string(), "golden".to_string()],
279
- targets: None,
280
- workspace: workspace.clone(),
281
- team: Some(spec.team_key.to_string()),
282
- task: None,
283
- sender: crate::messaging::TrustedSender::leader(),
284
- no_ack: true,
285
- no_wait: true,
286
- watch_result: false,
287
- timeout: 0.1,
288
- confirm_human: false,
289
- json: true,
290
- message_id: Some("phase-golden-message".to_string()),
291
- presentation: crate::messaging::presentation::PresentationRequest::default(),
292
- pane: None,
293
- to_name: None,
294
- to_leader: None,
295
- });
296
- let runtime_state = crate::state::persist::load_runtime_state(&workspace)
297
- .expect("quick-start state written before shutdown fixture");
298
- let generation = runtime_state
299
- .get("generation")
300
- .and_then(Value::as_str)
301
- .or_else(|| {
302
- runtime_state
303
- .get("agents")
304
- .and_then(Value::as_object)
305
- .and_then(|agents| {
306
- agents.values().find_map(|agent| {
307
- agent.get("spawned_at").and_then(Value::as_str)
308
- })
309
- })
310
- })
311
- .or_else(|| {
312
- runtime_state
313
- .pointer("/teams/teamdir/agents")
314
- .and_then(Value::as_object)
315
- .and_then(|agents| {
316
- agents.values().find_map(|agent| {
317
- agent.get("spawned_at").and_then(Value::as_str)
318
- })
319
- })
320
- })
321
- .unwrap_or("phase-golden");
322
- let lifecycle_transport = codex_ready_transport()
323
- .with_session_present(true)
324
- .with_windows(vec![WindowName::new("w1"), WindowName::new("w2")])
325
- .with_targets(vec![PaneInfo {
326
- pane_id: PaneId::new("%w1"),
327
- session: SessionName::new("team-phasegolden"),
328
- window_index: Some(0),
329
- window_name: Some(WindowName::new("w1")),
330
- pane_index: Some(0),
331
- tty: None,
332
- current_command: Some("codex".to_string()),
333
- current_path: Some(workspace.clone()),
334
- active: true,
335
- pane_pid: None,
336
- leader_env: BTreeMap::new(),
337
- }])
338
- .with_session_owner(&workspace, "teamdir", generation);
339
- let lifecycle = (spec.lifecycle_op)(&workspace, &lifecycle_transport, spec.team_key);
340
- let shutdown = lifecycle_port::shutdown_with_transport(
341
- &workspace,
342
- true,
343
- Some(spec.team_key),
344
- &lifecycle_transport,
345
- );
346
-
347
- let raw = json!({
348
- "phase": spec.phase,
349
- "script": [
350
- {
351
- "step": "quick-start",
352
- "exit_code": if quick_start.is_ok() { 0 } else { 1 },
353
- "output": quick_start_value(quick_start),
354
- },
355
- { "step": "status-json", "result": cmd_value(status_compact) },
356
- { "step": "status-detail-json", "result": cmd_value(status_detail) },
357
- { "step": "send", "result": cmd_value(send) },
358
- { "step": "phase-lifecycle-op", "output": lifecycle },
359
- {
360
- "step": "shutdown-keep-logs",
361
- "exit_code": if shutdown.is_ok() { 0 } else { 1 },
362
- "output": match shutdown {
363
- Ok(value) => value,
364
- Err(error) => json!({"ok": false, "error": error.to_string()}),
365
- },
366
- },
367
- ],
368
- "events_jsonl": read_events(&workspace),
369
- "state_json": read_state(&workspace),
370
- "transport": {
371
- "spawns": lifecycle_transport.spawn_window_records(),
372
- },
373
- });
374
-
375
- let mut ctx = NormalizeCtx::new(&workspace);
376
- normalize_value(raw, &mut ctx, None)
377
- }
378
-
379
- fn phase_b_reset_discard_session(
380
- workspace: &Path,
381
- transport: &OfflineTransport,
382
- team_key: &'static str,
383
- ) -> Value {
384
- match crate::lifecycle::reset_agent_with_transport(
385
- workspace,
386
- &aid("w1"),
387
- true,
388
- false,
389
- Some(team_key),
390
- transport,
391
- ) {
392
- Ok(ResetAgentOutcome::Reset {
393
- start_mode,
394
- discarded_session_id,
395
- session_id,
396
- new_session_id,
397
- ..
398
- }) => json!({
399
- "ok": true,
400
- "operation": "reset-agent --discard-session",
401
- "status": "running",
402
- "start_mode": format!("{start_mode:?}"),
403
- "discarded_session_id": discarded_session_id.map(|id| id.as_str().to_string()),
404
- "session_id": session_id.map(|id| id.as_str().to_string()),
405
- "new_session_id": new_session_id.map(|id| id.as_str().to_string()),
406
- }),
407
- Ok(other) => json!({
408
- "ok": true,
409
- "operation": "reset-agent --discard-session",
410
- "status": format!("{other:?}"),
411
- }),
412
- Err(error) => json!({
413
- "ok": false,
414
- "operation": "reset-agent --discard-session",
415
- "error": error.to_string(),
416
- }),
417
- }
418
- }
419
-
420
135
  fn two_worker_team_dir(hermetic: &HermeticTestEnv) -> PathBuf {
421
136
  let team = hermetic.workspace("team").join("teamdir");
422
137
  std::fs::create_dir_all(team.join("agents")).unwrap();
@@ -445,40 +160,6 @@ fn role_doc(id: &str) -> String {
445
160
  )
446
161
  }
447
162
 
448
- fn quick_start_value(result: Result<QuickStartReport, LifecycleError>) -> Value {
449
- match result {
450
- Ok(QuickStartReport::Ready {
451
- session_name,
452
- launch,
453
- worker_readiness,
454
- ..
455
- }) => {
456
- let agents = launch
457
- .started
458
- .iter()
459
- .map(|started| {
460
- json!({
461
- "agent_id": started.agent_id.as_str(),
462
- "target": started.target,
463
- "start_mode": format!("{:?}", started.start_mode),
464
- "session_id": started.session_id.as_ref().map(|id| id.as_str().to_string()),
465
- "rollout_path": started.rollout_path.as_ref().map(|path| path.as_path().to_string_lossy().to_string()),
466
- })
467
- })
468
- .collect::<Vec<_>>();
469
- json!({
470
- "ok": true,
471
- "status": "ready",
472
- "session_name": session_name.as_str(),
473
- "worker_readiness": format!("{worker_readiness:?}"),
474
- "started": agents,
475
- })
476
- }
477
- Ok(other) => json!({"ok": false, "status": format!("{other:?}")}),
478
- Err(error) => json!({"ok": false, "error": error.to_string()}),
479
- }
480
- }
481
-
482
163
  fn cmd_value(result: Result<CmdResult, crate::cli::CliError>) -> Value {
483
164
  match result {
484
165
  Ok(cmd) => json!({
@@ -496,46 +177,6 @@ fn cmd_value(result: Result<CmdResult, crate::cli::CliError>) -> Value {
496
177
  }
497
178
  }
498
179
 
499
- fn read_events(workspace: &Path) -> Vec<Value> {
500
- let path = crate::model::paths::logs_dir(workspace).join("events.jsonl");
501
- std::fs::read_to_string(path)
502
- .unwrap_or_default()
503
- .lines()
504
- .filter_map(|line| serde_json::from_str(line).ok())
505
- .collect()
506
- }
507
-
508
- fn read_state(workspace: &Path) -> Value {
509
- crate::state::persist::load_runtime_state(workspace).unwrap_or_else(|_| json!(null))
510
- }
511
-
512
- fn phase_fixture_path(phase: &str) -> PathBuf {
513
- PathBuf::from(env!("CARGO_MANIFEST_DIR"))
514
- .join("tests")
515
- .join("fixtures")
516
- .join("0_5_0_phase_golden")
517
- .join(phase)
518
- }
519
-
520
- fn load_phase_golden(path: &Path) -> Value {
521
- let text = std::fs::read_to_string(path)
522
- .unwrap_or_else(|e| panic!("missing golden baseline {}: {e}", path.display()));
523
- let stripped = strip_full_line_json_comments(&text);
524
- serde_json::from_str(&stripped).unwrap_or_else(|e| {
525
- panic!(
526
- "parse golden baseline {} after stripping // review comments: {e}",
527
- path.display()
528
- )
529
- })
530
- }
531
-
532
- fn strip_full_line_json_comments(text: &str) -> String {
533
- text.lines()
534
- .filter(|line| !line.trim_start().starts_with("//"))
535
- .collect::<Vec<_>>()
536
- .join("\n")
537
- }
538
-
539
180
  fn run_diagnose_after_quick_start() -> Value {
540
181
  let hermetic = HermeticTestEnv::enter("tombstone-claim-leader");
541
182
  let _permission_mode = EnvVarGuard::set(ANCESTRY_ENV, "[]");
@@ -371,16 +371,11 @@ fn pi_leader_and_teammate_body(hermetic: &HermeticTestEnv) {
371
371
 
372
372
  let pi_provider_root = hermetic.workspace("pi-add-provider");
373
373
  let pi_bin = pi_provider_root.join("bin");
374
- let pi_package = pi_provider_root.join("pi-mcp-adapter");
375
374
  std::fs::create_dir_all(&pi_bin).expect("create Pi test bin");
376
- std::fs::create_dir_all(&pi_package).expect("create Pi test adapter");
377
375
  let pi_real = pi_provider_root.join("pi-cli");
378
376
  std::fs::write(
379
377
  &pi_real,
380
- format!(
381
- "#!/bin/sh\ncase \"$1\" in\n --version) printf '0.84.4\\n' ;;\n --list-models) printf 'provider model\\nteam-agent qwen3.8-27b\\n' ;;\n list) printf 'npm:pi-mcp-adapter\\n{}\\n' ;;\n *) exit 64 ;;\nesac\n",
382
- pi_package.display()
383
- ),
378
+ "#!/bin/sh\ncase \"$1\" in\n --version) printf '0.84.4\\n' ;;\n --list-models) printf 'provider model\\nteam-agent qwen3.8-27b\\n' ;;\n list) exit 64 ;;\n *) exit 64 ;;\nesac\n",
384
379
  )
385
380
  .expect("write protocol-capable Pi test executable");
386
381
  std::fs::set_permissions(
@@ -389,16 +384,6 @@ fn pi_leader_and_teammate_body(hermetic: &HermeticTestEnv) {
389
384
  )
390
385
  .expect("make Pi test executable");
391
386
  std::os::unix::fs::symlink(&pi_real, pi_bin.join("pi")).expect("link Pi test executable");
392
- std::fs::write(
393
- pi_package.join("package.json"),
394
- br#"{"name":"pi-mcp-adapter","version":"2.30.0","pi":{"extensions":["./index.ts"]}}"#,
395
- )
396
- .expect("write Pi test adapter package");
397
- std::fs::write(
398
- pi_package.join("index.ts"),
399
- b"export const createMcpAdapter = () => {};\n",
400
- )
401
- .expect("write Pi test adapter entry");
402
387
  let _pi_path = EnvVarGuard::set("PATH", pi_bin.as_os_str());
403
388
  let pi_success_role = "---\nname: mate\nrole: Pi Dynamic Worker\nprovider: pi\nmodel: team-agent/qwen3.8-27b\nauth_mode: subscription\neffort: max\ntools:\n - mcp_team\ndangerously_skip_permissions: true\n---\n\ndynamic pi\n";
404
389
  let (pi_success_team, pi_success_role_path) =