@team-agent/installer 0.5.77 → 0.5.80
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 +161 -29
- package/crates/team-agent/src/cli/diagnose.rs +333 -68
- package/crates/team-agent/src/cli/emit.rs +24 -8
- package/crates/team-agent/src/cli/mod.rs +543 -98
- package/crates/team-agent/src/cli/models.rs +393 -32
- package/crates/team-agent/src/cli/named_address.rs +19 -5
- package/crates/team-agent/src/cli/profile.rs +35 -4
- package/crates/team-agent/src/cli/spec.rs +7 -2
- package/crates/team-agent/src/cli/status_port/compact.rs +12 -8
- package/crates/team-agent/src/cli/status_port/runtime.rs +3 -1
- package/crates/team-agent/src/cli/status_port/snapshot.rs +15 -12
- package/crates/team-agent/src/cli/status_port/store.rs +1 -1
- package/crates/team-agent/src/cli/status_port/tests.rs +37 -1
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +62 -8
- package/crates/team-agent/src/cli/tests/named_address.rs +70 -0
- package/crates/team-agent/src/cli/tests/verb_profile.rs +81 -6
- package/crates/team-agent/src/cli/types.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +122 -2
- package/crates/team-agent/src/leader/lease.rs +492 -33
- package/crates/team-agent/src/leader/owner_bind.rs +40 -12
- package/crates/team-agent/src/leader/rediscover/tests.rs +65 -0
- package/crates/team-agent/src/leader/rediscover.rs +36 -6
- package/crates/team-agent/src/leader/start.rs +197 -88
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +15 -9
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +28 -227
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +0 -18
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +94 -227
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +59 -10
- package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +86 -23
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +1449 -282
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +349 -4
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +11 -11
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +35 -3
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +16 -0
- package/crates/team-agent/src/lifecycle/launch.rs +30 -7
- package/crates/team-agent/src/lifecycle/profile_launch.rs +37 -3
- package/crates/team-agent/src/lifecycle/restart/agent.rs +112 -100
- package/crates/team-agent/src/lifecycle/restart/common.rs +82 -52
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +996 -66
- package/crates/team-agent/src/lifecycle/restart.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +15 -0
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +5 -0
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +11 -13
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +119 -4
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +12 -0
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +22 -0
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +12 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +265 -198
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +14 -30
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/fixtures/pi_list_models_spaced_ids.stdout.txt +9 -0
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +2 -5
- package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +10 -0
- package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +58 -35
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +10 -0
- package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +9 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -2
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +0 -168
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +145 -212
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +16 -2
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +54 -9
- package/crates/team-agent/src/lifecycle/tests/pi_catalog_red.rs +71 -0
- package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +78 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +51 -10
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +50 -7
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +137 -0
- package/crates/team-agent/src/lifecycle/types.rs +8 -0
- package/crates/team-agent/src/mcp_server/wire.rs +140 -11
- package/crates/team-agent/src/messaging/delivery.rs +221 -80
- package/crates/team-agent/src/messaging/leader_channel.rs +56 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +127 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +10 -2
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +2 -5
- package/crates/team-agent/src/provider/session_scan/common/tests.rs +204 -0
- package/crates/team-agent/src/provider/session_scan/common.rs +24 -2
- package/crates/team-agent/src/state/abnormal_watch/tests/mod.rs +248 -0
- package/crates/team-agent/src/state/abnormal_watch.rs +144 -0
- package/crates/team-agent/src/state/mod.rs +1 -0
- package/crates/team-agent/src/state/persist.rs +102 -4
- package/crates/team-agent/src/state/projection.rs +7 -1
- package/crates/team-agent/src/state/repository.rs +29 -0
- package/crates/team-agent/src/test_support/models_timeout_fixture.rs +43 -21
- package/crates/team-agent/src/tmux_backend/tests.rs +149 -4
- package/crates/team-agent/src/tmux_backend.rs +239 -127
- package/crates/team-agent/src/transport.rs +78 -0
- package/crates/team-agent/src/transport_factory.rs +53 -0
- package/package.json +4 -5
- package/skills/team-agent/SKILL.md +45 -17
- package/skills/team-agent/command-coverage.json +4 -29
- package/npm/check-skill-links.mjs +0 -88
- package/skills/team-agent/references/team-agent-operator.md +0 -672
|
@@ -333,7 +333,7 @@ pub(crate) fn start_agent_at_paths(
|
|
|
333
333
|
)));
|
|
334
334
|
}
|
|
335
335
|
let spawn_session_id = if matches!(start_mode, StartMode::Resumed) {
|
|
336
|
-
session_id.
|
|
336
|
+
session_id.clone()
|
|
337
337
|
} else {
|
|
338
338
|
None
|
|
339
339
|
};
|
|
@@ -375,7 +375,7 @@ pub(crate) fn start_agent_at_paths(
|
|
|
375
375
|
&session_name,
|
|
376
376
|
agent_id,
|
|
377
377
|
&agent,
|
|
378
|
-
spawn_session_id,
|
|
378
|
+
spawn_session_id.as_ref(),
|
|
379
379
|
into_existing_session,
|
|
380
380
|
transport,
|
|
381
381
|
Some(&safety),
|
|
@@ -390,106 +390,95 @@ pub(crate) fn start_agent_at_paths(
|
|
|
390
390
|
&session_name,
|
|
391
391
|
&spawn.spawn.window,
|
|
392
392
|
) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
return Err(error);
|
|
393
|
+
return Err(rollback_spawned_pane_keep_error(
|
|
394
|
+
transport,
|
|
395
|
+
&spawn.spawn.pane_id,
|
|
396
|
+
error,
|
|
397
|
+
));
|
|
400
398
|
}
|
|
401
399
|
let actual_spawn_window = spawn.spawn.window.as_str().to_string();
|
|
402
|
-
let
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
// **0.3.24 add-agent socket drift fix**: keep `state.tmux_endpoint` /
|
|
435
|
-
// `state.tmux_socket` synchronized with the transport actually used for the
|
|
436
|
-
// spawn. Without this, add-agent / fork-agent could spawn to a socket that
|
|
437
|
-
// never gets persisted, and the next coordinator tick would re-resolve to
|
|
438
|
-
// the workspace-hash socket and lose the new pane. Annotation runs inside
|
|
439
|
-
// the same `save_restart_projected_state` window — no parallel "annotate
|
|
440
|
-
// after spawn" race with coordinator and no double source of truth.
|
|
441
|
-
crate::lifecycle::launch::annotate_runtime_tmux_endpoint(&mut state, transport, workspace);
|
|
442
|
-
let team_key = restart_projection_team_key(&state, team);
|
|
443
|
-
// 0.5.32 (`.team/artifacts/restart-resumed-stale-activity-locate.md` §5):
|
|
444
|
-
// clear the matching `agent_health` observation on the new spawn cohort
|
|
445
|
-
// so five-line status summary and status --json do not surface a stale
|
|
446
|
-
// WORKING row from the pre-shutdown process. Best-effort: DB failure
|
|
447
|
-
// must not fail the start-agent path.
|
|
448
|
-
let _ = crate::db::agent_health_capture::clear_agent_health_observation(
|
|
449
|
-
workspace, &team_key, agent_id,
|
|
400
|
+
let spawned = (|| {
|
|
401
|
+
mark_agent_started(
|
|
402
|
+
&mut state,
|
|
403
|
+
agent_id,
|
|
404
|
+
&actual_spawn_window,
|
|
405
|
+
&spawn,
|
|
406
|
+
transport,
|
|
407
|
+
&safety,
|
|
408
|
+
start_mode,
|
|
409
|
+
)?;
|
|
410
|
+
// 0.5.66 bypass 单源 §2.7:启动即带 bypass → 审计留痕。
|
|
411
|
+
if safety.enabled {
|
|
412
|
+
let state_agent = state
|
|
413
|
+
.get("agents")
|
|
414
|
+
.and_then(|v| v.get(agent_id.as_str()));
|
|
415
|
+
let _ = crate::event_log::EventLog::new(workspace).write(
|
|
416
|
+
"worker.spawn_dangerously_skip_permissions",
|
|
417
|
+
serde_json::json!({
|
|
418
|
+
"agent_id": agent_id.as_str(),
|
|
419
|
+
"role": state_agent
|
|
420
|
+
.and_then(|a| a.get("role"))
|
|
421
|
+
.and_then(serde_json::Value::as_str)
|
|
422
|
+
.unwrap_or_default(),
|
|
423
|
+
"provider": state_agent
|
|
424
|
+
.and_then(|a| a.get("provider"))
|
|
425
|
+
.and_then(serde_json::Value::as_str)
|
|
426
|
+
.unwrap_or_default(),
|
|
427
|
+
"trigger": "start-agent",
|
|
428
|
+
"spec_source_path": crate::lifecycle::launch::agent_id_spec_source_path(
|
|
429
|
+
workspace, agent_id.as_str(),
|
|
430
|
+
),
|
|
431
|
+
}),
|
|
450
432
|
);
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
tmux_start_mode_for_spawn(&spawn, into_existing_session),
|
|
477
|
-
)?;
|
|
478
|
-
replay_worker_target_missing_messages(workspace, agent_id, &team_key, &state, transport)?;
|
|
479
|
-
Ok(start_coordinator_for_workspace(workspace, Some(&team_key))?.ok)
|
|
480
|
-
})();
|
|
481
|
-
let coordinator_started = match post_spawn {
|
|
482
|
-
Ok(started) => started,
|
|
483
|
-
Err(error) => {
|
|
484
|
-
if let Err(rollback_error) = transport.kill_pane(&spawn.spawn.pane_id) {
|
|
485
|
-
return Err(LifecycleError::RequirementUnmet(format!(
|
|
486
|
-
"{error}; failed to roll back spawned pane {}: {rollback_error}",
|
|
487
|
-
spawn.spawn.pane_id.as_str()
|
|
488
|
-
)));
|
|
489
|
-
}
|
|
490
|
-
return Err(error);
|
|
491
|
-
}
|
|
433
|
+
}
|
|
434
|
+
// **0.3.24 add-agent socket drift fix**: keep `state.tmux_endpoint` /
|
|
435
|
+
// `state.tmux_socket` synchronized with the transport actually used for the
|
|
436
|
+
// spawn. Without this, add-agent / fork-agent could spawn to a socket that
|
|
437
|
+
// never gets persisted, and the next coordinator tick would re-resolve to
|
|
438
|
+
// the workspace-hash socket and lose the new pane. Annotation runs inside
|
|
439
|
+
// the same `save_restart_projected_state` window — no parallel "annotate
|
|
440
|
+
// after spawn" race with coordinator and no double source of truth.
|
|
441
|
+
crate::lifecycle::launch::annotate_runtime_tmux_endpoint(&mut state, transport, workspace);
|
|
442
|
+
let team_key = restart_projection_team_key(&state, team);
|
|
443
|
+
// 0.5.32 (`.team/artifacts/restart-resumed-stale-activity-locate.md` §5):
|
|
444
|
+
// clear the matching `agent_health` observation on the new spawn cohort
|
|
445
|
+
// so five-line status summary and status --json do not surface a stale
|
|
446
|
+
// WORKING row from the pre-shutdown process. Best-effort: DB failure
|
|
447
|
+
// must not fail the start-agent path.
|
|
448
|
+
let _ = crate::db::agent_health_capture::clear_agent_health_observation(
|
|
449
|
+
workspace, &team_key, agent_id,
|
|
450
|
+
);
|
|
451
|
+
let skip_capture_backfill = if matches!(
|
|
452
|
+
start_mode,
|
|
453
|
+
StartMode::Fresh | StartMode::FreshAfterMissingRollout
|
|
454
|
+
) {
|
|
455
|
+
vec![agent_id.as_str()]
|
|
456
|
+
} else {
|
|
457
|
+
Vec::new()
|
|
492
458
|
};
|
|
459
|
+
persist_selected_receiver_session_name(&mut state, &session_name);
|
|
460
|
+
save_restart_projected_state_with_capture_backfill_skip(
|
|
461
|
+
workspace,
|
|
462
|
+
&mut state,
|
|
463
|
+
&team_key,
|
|
464
|
+
&skip_capture_backfill,
|
|
465
|
+
&[agent_id.as_str()],
|
|
466
|
+
)?;
|
|
467
|
+
write_start_agent_start_event(
|
|
468
|
+
workspace,
|
|
469
|
+
agent_id,
|
|
470
|
+
&agent,
|
|
471
|
+
&spawn.plan,
|
|
472
|
+
provider,
|
|
473
|
+
start_mode,
|
|
474
|
+
&session_name,
|
|
475
|
+
&actual_spawn_window,
|
|
476
|
+
spawn_session_id.as_ref(),
|
|
477
|
+
tmux_start_mode_for_spawn(&spawn, into_existing_session),
|
|
478
|
+
)?;
|
|
479
|
+
replay_worker_target_missing_messages(workspace, agent_id, &team_key, &state, transport)?;
|
|
480
|
+
let coordinator = start_coordinator_for_workspace(workspace, Some(&team_key))?;
|
|
481
|
+
let coordinator_started = coordinator.ok;
|
|
493
482
|
Ok(StartAgentOutcome::Running {
|
|
494
483
|
env: AgentActionEnvelope {
|
|
495
484
|
agent_id: agent_id.clone(),
|
|
@@ -502,6 +491,29 @@ pub(crate) fn start_agent_at_paths(
|
|
|
502
491
|
new_session_id: spawn.plan.expected_session_id.clone(),
|
|
503
492
|
rollout_path,
|
|
504
493
|
})
|
|
494
|
+
})();
|
|
495
|
+
match spawned {
|
|
496
|
+
Ok(outcome) => Ok(outcome),
|
|
497
|
+
Err(error) => Err(rollback_spawned_pane_keep_error(
|
|
498
|
+
transport,
|
|
499
|
+
&spawn.spawn.pane_id,
|
|
500
|
+
error,
|
|
501
|
+
)),
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
fn rollback_spawned_pane_keep_error(
|
|
506
|
+
transport: &dyn crate::transport::Transport,
|
|
507
|
+
pane: &crate::transport::PaneId,
|
|
508
|
+
error: LifecycleError,
|
|
509
|
+
) -> LifecycleError {
|
|
510
|
+
match transport.kill_pane(pane) {
|
|
511
|
+
Ok(()) => error,
|
|
512
|
+
Err(rollback_error) => LifecycleError::Transport(format!(
|
|
513
|
+
"{error}; failed to roll back spawned pane {}: {rollback_error}",
|
|
514
|
+
pane.as_str()
|
|
515
|
+
)),
|
|
516
|
+
}
|
|
505
517
|
}
|
|
506
518
|
|
|
507
519
|
fn replay_worker_target_missing_messages(
|
|
@@ -1783,8 +1795,8 @@ fn write_start_agent_start_event(
|
|
|
1783
1795
|
workspace: &Path,
|
|
1784
1796
|
agent_id: &AgentId,
|
|
1785
1797
|
agent: &serde_json::Value,
|
|
1798
|
+
spawn_plan: &crate::provider::CommandPlan,
|
|
1786
1799
|
provider: crate::provider::Provider,
|
|
1787
|
-
plan: &crate::provider::CommandPlan,
|
|
1788
1800
|
start_mode: StartMode,
|
|
1789
1801
|
session_name: &SessionName,
|
|
1790
1802
|
window: &str,
|
|
@@ -1802,7 +1814,7 @@ fn write_start_agent_start_event(
|
|
|
1802
1814
|
"session": session_name.as_str(),
|
|
1803
1815
|
"window": window,
|
|
1804
1816
|
"tmux_start_mode": tmux_start_mode,
|
|
1805
|
-
"command":
|
|
1817
|
+
"command": spawn_plan.argv,
|
|
1806
1818
|
"mcp_config": agent.get("mcp_config").cloned().unwrap_or(serde_json::Value::Null),
|
|
1807
1819
|
}),
|
|
1808
1820
|
)
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
//! maturity: wired
|
|
38
38
|
//! ---
|
|
39
39
|
use super::*;
|
|
40
|
-
use crate::transport::Transport;
|
|
41
40
|
|
|
42
41
|
pub(super) struct SpawnedAgentWindow {
|
|
43
42
|
pub spawn: crate::transport::SpawnResult,
|
|
@@ -341,7 +340,7 @@ pub(super) fn spawn_agent_window(
|
|
|
341
340
|
agent_id.as_str(),
|
|
342
341
|
team_id.as_deref().unwrap_or(""),
|
|
343
342
|
);
|
|
344
|
-
let mcp_config_path = if provider == Provider::Pi {
|
|
343
|
+
let mcp_config_path = if provider == crate::provider::Provider::Pi {
|
|
345
344
|
None
|
|
346
345
|
} else {
|
|
347
346
|
Some(crate::lifecycle::launch::write_worker_mcp_config(
|
|
@@ -379,19 +378,19 @@ pub(super) fn spawn_agent_window(
|
|
|
379
378
|
agent_id_hint: Some(agent_id.as_str()),
|
|
380
379
|
effort: restart_effort,
|
|
381
380
|
};
|
|
382
|
-
let pi_spawn_cwd = (provider == Provider::Pi)
|
|
381
|
+
let pi_spawn_cwd = (provider == crate::provider::Provider::Pi)
|
|
383
382
|
.then(|| {
|
|
384
383
|
agent
|
|
385
384
|
.get("spawn_cwd")
|
|
386
385
|
.and_then(serde_json::Value::as_str)
|
|
387
386
|
.filter(|value| !value.is_empty())
|
|
388
|
-
.map(PathBuf::from)
|
|
387
|
+
.map(std::path::PathBuf::from)
|
|
389
388
|
})
|
|
390
389
|
.flatten();
|
|
391
390
|
let spawn_cwd = spawn_cwd_override
|
|
392
391
|
.or(pi_spawn_cwd.as_deref())
|
|
393
392
|
.unwrap_or(workspace);
|
|
394
|
-
let mut plan = if provider == Provider::Pi {
|
|
393
|
+
let mut plan = if provider == crate::provider::Provider::Pi {
|
|
395
394
|
let request = crate::lifecycle::launch::pi_mcp::PiMaterializeRequest {
|
|
396
395
|
workspace,
|
|
397
396
|
team_id: team_id.as_deref().unwrap_or(""),
|
|
@@ -477,41 +476,33 @@ pub(super) fn spawn_agent_window(
|
|
|
477
476
|
agent_id.as_str(),
|
|
478
477
|
&system_prompt,
|
|
479
478
|
)?;
|
|
480
|
-
crate::lifecycle::launch::
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
479
|
+
let project = crate::lifecycle::launch::prepare_cursor_seat_mcp(
|
|
480
|
+
workspace,
|
|
481
|
+
agent_id.as_str(),
|
|
482
|
+
&mcp_config,
|
|
483
|
+
)?;
|
|
484
|
+
crate::lifecycle::launch::enable_cursor_workspace_mcp_with_profile(
|
|
485
|
+
workspace,
|
|
486
|
+
project.as_deref(),
|
|
487
|
+
Some(&profile_launch),
|
|
488
|
+
)?;
|
|
490
489
|
crate::lifecycle::launch::apply_cursor_spawn_workspace_pointers(
|
|
491
490
|
&mut plan.argv,
|
|
492
491
|
workspace,
|
|
493
492
|
agent_id.as_str(),
|
|
494
493
|
)?;
|
|
495
|
-
crate::lifecycle::launch::
|
|
494
|
+
crate::lifecycle::launch::apply_cursor_subscription_proxy_env_with_profile(
|
|
495
|
+
&mut env,
|
|
496
|
+
&profile_launch,
|
|
497
|
+
);
|
|
496
498
|
}
|
|
497
499
|
if provider == crate::provider::Provider::Grok {
|
|
498
500
|
crate::lifecycle::launch::ensure_grok_login_and_folder_trust(workspace)?;
|
|
499
501
|
crate::lifecycle::launch::apply_grok_mcp_overlay(workspace, &mcp_config)?;
|
|
500
502
|
}
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
//
|
|
504
|
-
// rebuild.rs:138 — root cause of E56). The `spawn_cwd_override` parameter
|
|
505
|
-
// is still honoured for callers that need an explicit cwd (e.g. spec
|
|
506
|
-
// YAML-resolved cwd at first launch in `lifecycle/launch.rs`), but
|
|
507
|
-
// restart never passes it (see commit 71864c0 which fixed rebuild.rs:297
|
|
508
|
-
// to stop pinning `.team/runtime/<team_key>/`).
|
|
509
|
-
//
|
|
510
|
-
// NOTE: Step 4 will thread the YAML spec down to here so we can honour
|
|
511
|
-
// a per-agent YAML `spawn_cwd` field if one is set. Until then, override
|
|
512
|
-
// > workspace; state-based override is silently dropped for existing
|
|
513
|
-
// providers. Pi is the narrow exception because exact header/path resume
|
|
514
|
-
// must preserve the captured cwd or refuse.
|
|
503
|
+
// Non-Pi spawn cwd is override-or-workspace (E56: ignore drifted state
|
|
504
|
+
// `.team/runtime/<team_key>/`). Pi resume uses captured agent.spawn_cwd
|
|
505
|
+
// already resolved above so exact-path header checks match capture.
|
|
515
506
|
// 0.4.x provider effort MVP step 9: scrub CLAUDE_EFFORT for Claude
|
|
516
507
|
// worker spawn so a parent shell env cannot silently override the
|
|
517
508
|
// framework's effort decision.
|
|
@@ -1158,34 +1149,19 @@ fn json_team_identity_matches(existing: &serde_json::Value, compact: &serde_json
|
|
|
1158
1149
|
/// returns: 非空的 session_name,缺失时退到默认名
|
|
1159
1150
|
/// ---
|
|
1160
1151
|
pub(super) fn state_session_name(state: &serde_json::Value) -> SessionName {
|
|
1161
|
-
worker_session_name_from_state(state)
|
|
1162
|
-
.map(SessionName::new)
|
|
1163
|
-
.unwrap_or_else(|| SessionName::new("team-agent"))
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
fn worker_session_name_from_state(state: &serde_json::Value) -> Option<&str> {
|
|
1167
1152
|
state
|
|
1168
1153
|
.get("session_name")
|
|
1169
1154
|
.and_then(|v| v.as_str())
|
|
1170
1155
|
.filter(|s| !s.is_empty())
|
|
1171
1156
|
.or_else(|| {
|
|
1172
|
-
let team_key = state
|
|
1173
|
-
.get("active_team_key")
|
|
1174
|
-
.and_then(serde_json::Value::as_str)
|
|
1175
|
-
.filter(|team_key| !team_key.is_empty())?;
|
|
1176
1157
|
state
|
|
1177
|
-
.get("
|
|
1178
|
-
.get(
|
|
1179
|
-
.
|
|
1180
|
-
.
|
|
1181
|
-
.filter(|session| !session.is_empty())
|
|
1182
|
-
})
|
|
1183
|
-
.or_else(|| {
|
|
1184
|
-
state
|
|
1185
|
-
.pointer("/leader_receiver/session_name")
|
|
1186
|
-
.and_then(serde_json::Value::as_str)
|
|
1187
|
-
.filter(|session| !session.is_empty())
|
|
1158
|
+
.get("leader_receiver")
|
|
1159
|
+
.and_then(|receiver| receiver.get("session_name"))
|
|
1160
|
+
.and_then(|v| v.as_str())
|
|
1161
|
+
.filter(|s| !s.is_empty())
|
|
1188
1162
|
})
|
|
1163
|
+
.map(SessionName::new)
|
|
1164
|
+
.unwrap_or_else(|| SessionName::new("team-agent"))
|
|
1189
1165
|
}
|
|
1190
1166
|
|
|
1191
1167
|
/// ---
|
|
@@ -1193,7 +1169,40 @@ fn worker_session_name_from_state(state: &serde_json::Value) -> Option<&str> {
|
|
|
1193
1169
|
/// returns: 记了则 true
|
|
1194
1170
|
/// ---
|
|
1195
1171
|
pub(super) fn session_name_present(state: &serde_json::Value) -> bool {
|
|
1196
|
-
|
|
1172
|
+
state
|
|
1173
|
+
.get("session_name")
|
|
1174
|
+
.and_then(|v| v.as_str())
|
|
1175
|
+
.map(|s| !s.is_empty())
|
|
1176
|
+
.unwrap_or(false)
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/// Persist the session actually used for spawn when the selected team only
|
|
1180
|
+
/// had it on `leader_receiver`. Send/status route on `session_name`; an
|
|
1181
|
+
/// explicit null must stay a miss and must not rewrite a sibling team.
|
|
1182
|
+
pub(super) fn persist_selected_receiver_session_name(
|
|
1183
|
+
state: &mut serde_json::Value,
|
|
1184
|
+
session: &SessionName,
|
|
1185
|
+
) {
|
|
1186
|
+
if session_name_present(state) {
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
let Some(from_receiver) = state
|
|
1190
|
+
.get("leader_receiver")
|
|
1191
|
+
.and_then(|receiver| receiver.get("session_name"))
|
|
1192
|
+
.and_then(serde_json::Value::as_str)
|
|
1193
|
+
.filter(|name| !name.is_empty())
|
|
1194
|
+
else {
|
|
1195
|
+
return;
|
|
1196
|
+
};
|
|
1197
|
+
if from_receiver != session.as_str() {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if let Some(object) = state.as_object_mut() {
|
|
1201
|
+
object.insert(
|
|
1202
|
+
"session_name".to_string(),
|
|
1203
|
+
serde_json::json!(session.as_str()),
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1197
1206
|
}
|
|
1198
1207
|
|
|
1199
1208
|
/// ---
|
|
@@ -2291,6 +2300,27 @@ mod e36_transcript_backing_tests {
|
|
|
2291
2300
|
}
|
|
2292
2301
|
}
|
|
2293
2302
|
|
|
2303
|
+
#[test]
|
|
2304
|
+
fn agent_rollout_path_keeps_captured_exact_session_file() {
|
|
2305
|
+
// Live T1 public session.captured (events-before.jsonl / events-after-t1.jsonl):
|
|
2306
|
+
// rollout_path was already assigned before shutdown; resume must not
|
|
2307
|
+
// invent a different file or drop the captured path.
|
|
2308
|
+
let captured = "/Volumes/nvme/tmp/team-agent-live-0578-r2-3087abc5/.team/runtime/pi/claim-live-0578-r2/echo/sessions/2026-09-09T15-53-03-460Z_0dac7250-aa88-45be-aa3e-03a6833f9552.jsonl";
|
|
2309
|
+
let agent = serde_json::json!({
|
|
2310
|
+
"session_id": "0dac7250-aa88-45be-aa3e-03a6833f9552",
|
|
2311
|
+
"rollout_path": captured,
|
|
2312
|
+
});
|
|
2313
|
+
let path = agent_rollout_path(&agent).expect("captured exact path");
|
|
2314
|
+
assert_eq!(path.as_path().to_string_lossy(), captured);
|
|
2315
|
+
assert!(
|
|
2316
|
+
agent_rollout_path(&serde_json::json!({
|
|
2317
|
+
"session_id": "0dac7250-aa88-45be-aa3e-03a6833f9552",
|
|
2318
|
+
}))
|
|
2319
|
+
.is_none(),
|
|
2320
|
+
"session_id alone is not an exact session path"
|
|
2321
|
+
);
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2294
2324
|
#[test]
|
|
2295
2325
|
fn convergence_event_uses_central_scrub_and_preserves_required_failure() {
|
|
2296
2326
|
let scratch = ScratchDir::new("event-log-central");
|