@team-agent/installer 0.5.14 → 0.5.15
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/lifecycle/restart/agent.rs +1 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +25 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +98 -7
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +8 -1
- package/crates/team-agent/src/tmux_backend.rs +28 -0
- package/package.json +4 -4
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -26,6 +26,7 @@ pub(super) fn spawn_agent_window(
|
|
|
26
26
|
safety: Option<&DangerousApproval>,
|
|
27
27
|
layout_placement: Option<&crate::lifecycle::launch::LayoutPlacement>,
|
|
28
28
|
spawn_cwd_override: Option<&Path>,
|
|
29
|
+
tmux_endpoint_source: Option<&str>,
|
|
29
30
|
// Issue 2 (Round 3b gate review §6): explicit owner_team_id override.
|
|
30
31
|
// When `Some`, callers (restart/rebuild.rs, restart/agent.rs) thread the
|
|
31
32
|
// resolved `selected.team_key` through here so the worker's MCP env /
|
|
@@ -205,6 +206,7 @@ pub(super) fn spawn_agent_window(
|
|
|
205
206
|
let tmux_start_mode_pre_spawn =
|
|
206
207
|
predict_tmux_start_mode(layout_placement, into_existing_session);
|
|
207
208
|
let spawn_epoch = state_spawn_epoch_for_agent(workspace, agent_id);
|
|
209
|
+
let tmux_endpoint = transport.tmux_endpoint();
|
|
208
210
|
let event_log = crate::event_log::EventLog::new(workspace);
|
|
209
211
|
let _ = event_log.write(
|
|
210
212
|
"provider.worker.spawn_argv",
|
|
@@ -220,6 +222,8 @@ pub(super) fn spawn_agent_window(
|
|
|
220
222
|
"tmux_start_mode": tmux_start_mode_pre_spawn,
|
|
221
223
|
"spawn_epoch": spawn_epoch,
|
|
222
224
|
"source": "restart",
|
|
225
|
+
"tmux_endpoint": tmux_endpoint,
|
|
226
|
+
"tmux_endpoint_source": tmux_endpoint_source.unwrap_or("transport"),
|
|
223
227
|
}),
|
|
224
228
|
);
|
|
225
229
|
}
|
|
@@ -564,6 +568,27 @@ pub(crate) fn lifecycle_worker_transport_for_selected_state(
|
|
|
564
568
|
.map_err(|e| LifecycleError::TeamSelect(e.to_string()))
|
|
565
569
|
}
|
|
566
570
|
|
|
571
|
+
pub(super) fn lifecycle_worker_tmux_backend_selection_for_state(
|
|
572
|
+
run_workspace: &Path,
|
|
573
|
+
state: &serde_json::Value,
|
|
574
|
+
) -> Result<crate::tmux_backend::RuntimeTmuxBackendSelection, LifecycleError> {
|
|
575
|
+
if let Some(kind) = state.pointer("/transport/kind").and_then(|v| v.as_str()) {
|
|
576
|
+
if kind.eq_ignore_ascii_case("conpty") {
|
|
577
|
+
return Err(LifecycleError::TeamSelect(format!(
|
|
578
|
+
"backend_kind_mismatch: state.transport.kind={kind:?} but the legacy \
|
|
579
|
+
tmux-typed lifecycle resolver was invoked; caller must migrate to \
|
|
580
|
+
`lifecycle_worker_transport_for_selected_state` (Phase 1d Batch 2/3)"
|
|
581
|
+
)));
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
Ok(
|
|
585
|
+
crate::tmux_backend::tmux_backend_for_runtime_state_or_workspace(
|
|
586
|
+
run_workspace,
|
|
587
|
+
Some(state),
|
|
588
|
+
),
|
|
589
|
+
)
|
|
590
|
+
}
|
|
591
|
+
|
|
567
592
|
pub(super) fn lifecycle_worker_tmux_backend_for_state(
|
|
568
593
|
run_workspace: &Path,
|
|
569
594
|
state: &serde_json::Value,
|
|
@@ -23,16 +23,51 @@ pub fn restart_with_session_convergence_deadline(
|
|
|
23
23
|
team: Option<&str>,
|
|
24
24
|
session_converge_deadline_ms: Option<u64>,
|
|
25
25
|
) -> Result<RestartReport, LifecycleError> {
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
workspace,
|
|
26
|
+
let context = resolve_restart_context(workspace, team)?;
|
|
27
|
+
restart_with_selected_team_and_transport(
|
|
28
|
+
context.selected,
|
|
30
29
|
allow_fresh,
|
|
31
|
-
|
|
32
|
-
&transport,
|
|
30
|
+
&context.transport,
|
|
33
31
|
session_converge_deadline_ms,
|
|
34
32
|
None,
|
|
33
|
+
Some(context.tmux_endpoint_source),
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
struct ResolvedRestartContext {
|
|
38
|
+
selected: crate::state::selector::SelectedTeam,
|
|
39
|
+
transport: crate::tmux_backend::TmuxBackend,
|
|
40
|
+
tmux_endpoint_source: &'static str,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fn resolve_restart_context(
|
|
44
|
+
workspace: &Path,
|
|
45
|
+
team: Option<&str>,
|
|
46
|
+
) -> Result<ResolvedRestartContext, LifecycleError> {
|
|
47
|
+
let resolved_ws = crate::model::paths::canonical_run_workspace(workspace)
|
|
48
|
+
.map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
|
|
49
|
+
if crate::lifecycle::restart::input_has_no_local_team_context(&resolved_ws) {
|
|
50
|
+
return Err(LifecycleError::TeamSelect(format!(
|
|
51
|
+
"active team spec not found: input_workspace={} expected_runtime_dir={}",
|
|
52
|
+
workspace.display(),
|
|
53
|
+
crate::model::paths::runtime_dir(&resolved_ws).display()
|
|
54
|
+
)));
|
|
55
|
+
}
|
|
56
|
+
let selected = crate::state::selector::resolve_active_team(
|
|
57
|
+
workspace,
|
|
58
|
+
team,
|
|
59
|
+
crate::state::selector::SelectorMode::RequireSpec,
|
|
35
60
|
)
|
|
61
|
+
.map_err(|e| LifecycleError::TeamSelect(e.to_string()))?;
|
|
62
|
+
let transport_selection = lifecycle_worker_tmux_backend_selection_for_state(
|
|
63
|
+
&selected.run_workspace,
|
|
64
|
+
&selected.state,
|
|
65
|
+
)?;
|
|
66
|
+
Ok(ResolvedRestartContext {
|
|
67
|
+
selected,
|
|
68
|
+
transport: transport_selection.backend,
|
|
69
|
+
tmux_endpoint_source: transport_selection.tmux_endpoint_source.as_str(),
|
|
70
|
+
})
|
|
36
71
|
}
|
|
37
72
|
|
|
38
73
|
/// `restart` with an injected transport (tests: recording mock; prod: real TmuxBackend). The Route-B
|
|
@@ -129,6 +164,24 @@ pub fn restart_with_transport_with_session_convergence_deadline(
|
|
|
129
164
|
expected.display()
|
|
130
165
|
)));
|
|
131
166
|
}
|
|
167
|
+
restart_with_selected_team_and_transport(
|
|
168
|
+
selected,
|
|
169
|
+
allow_fresh,
|
|
170
|
+
transport,
|
|
171
|
+
session_converge_deadline_ms,
|
|
172
|
+
readiness_deadline_ms,
|
|
173
|
+
None,
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
fn restart_with_selected_team_and_transport(
|
|
178
|
+
selected: crate::state::selector::SelectedTeam,
|
|
179
|
+
allow_fresh: bool,
|
|
180
|
+
transport: &dyn crate::transport::Transport,
|
|
181
|
+
session_converge_deadline_ms: Option<u64>,
|
|
182
|
+
readiness_deadline_ms: Option<u64>,
|
|
183
|
+
tmux_endpoint_source: Option<&str>,
|
|
184
|
+
) -> Result<RestartReport, LifecycleError> {
|
|
132
185
|
let lifecycle_lock = acquire_agent_lifecycle_lock(LifecycleLockRequest {
|
|
133
186
|
workspace: &selected.run_workspace,
|
|
134
187
|
operation: "restart",
|
|
@@ -381,6 +434,13 @@ pub fn restart_with_transport_with_session_convergence_deadline(
|
|
|
381
434
|
&raw_agent,
|
|
382
435
|
);
|
|
383
436
|
if has_endpoint_convergence_marker(&state) && is_fake_model_harness_agent(&agent) {
|
|
437
|
+
write_fake_harness_spawn_argv_event(
|
|
438
|
+
&selected.run_workspace,
|
|
439
|
+
decision,
|
|
440
|
+
&agent,
|
|
441
|
+
transport,
|
|
442
|
+
tmux_endpoint_source,
|
|
443
|
+
);
|
|
384
444
|
mark_fake_harness_agent_respawned(
|
|
385
445
|
&mut state,
|
|
386
446
|
&decision.agent_id,
|
|
@@ -438,6 +498,7 @@ pub fn restart_with_transport_with_session_convergence_deadline(
|
|
|
438
498
|
Some(&safety),
|
|
439
499
|
layout_placement.as_ref(),
|
|
440
500
|
None,
|
|
501
|
+
tmux_endpoint_source,
|
|
441
502
|
// Issue 2 (Round 3b gate review §6): thread the resolved
|
|
442
503
|
// selected.team_key so the worker MCP env carries the right
|
|
443
504
|
// owner_team_id even when top-level active_team_key is stale.
|
|
@@ -783,7 +844,14 @@ pub fn restart_with_transport_with_session_convergence_deadline(
|
|
|
783
844
|
restart_readiness_poll_interval(),
|
|
784
845
|
)?;
|
|
785
846
|
let attach_commands =
|
|
786
|
-
crate::tmux_backend::
|
|
847
|
+
crate::tmux_backend::attach_command_for_transport_session(transport, &session_name)
|
|
848
|
+
.or_else(|| {
|
|
849
|
+
crate::tmux_backend::attach_command_for_runtime_state_session_or_workspace(
|
|
850
|
+
&selected.run_workspace,
|
|
851
|
+
Some(&state),
|
|
852
|
+
&session_name,
|
|
853
|
+
)
|
|
854
|
+
})
|
|
787
855
|
.into_iter()
|
|
788
856
|
.collect::<Vec<_>>();
|
|
789
857
|
let mut next_actions = Vec::new();
|
|
@@ -1719,6 +1787,29 @@ fn mark_fake_harness_agent_respawned(
|
|
|
1719
1787
|
agent.insert("owner_team_id".to_string(), serde_json::json!(team_key));
|
|
1720
1788
|
}
|
|
1721
1789
|
|
|
1790
|
+
fn write_fake_harness_spawn_argv_event(
|
|
1791
|
+
workspace: &Path,
|
|
1792
|
+
decision: &RestartedAgent,
|
|
1793
|
+
agent: &serde_json::Value,
|
|
1794
|
+
transport: &dyn crate::transport::Transport,
|
|
1795
|
+
tmux_endpoint_source: Option<&str>,
|
|
1796
|
+
) {
|
|
1797
|
+
let _ = crate::event_log::EventLog::new(workspace).write(
|
|
1798
|
+
"provider.worker.spawn_argv",
|
|
1799
|
+
serde_json::json!({
|
|
1800
|
+
"agent_id": decision.agent_id.as_str(),
|
|
1801
|
+
"provider": agent_provider(agent),
|
|
1802
|
+
"argv": [],
|
|
1803
|
+
"session_id_in_argv": null,
|
|
1804
|
+
"expected_session_id": decision.session_id.as_ref().map(|s| s.as_str()),
|
|
1805
|
+
"tmux_start_mode": "fake_harness",
|
|
1806
|
+
"source": "restart",
|
|
1807
|
+
"tmux_endpoint": transport.tmux_endpoint(),
|
|
1808
|
+
"tmux_endpoint_source": tmux_endpoint_source.unwrap_or("transport"),
|
|
1809
|
+
}),
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1722
1813
|
fn restart_failed_agent(
|
|
1723
1814
|
decision: &RestartedAgent,
|
|
1724
1815
|
phase: impl Into<String>,
|
|
@@ -524,7 +524,7 @@ fn normalize_string(text: String, ctx: &mut NormalizeCtx, key: Option<&str>) ->
|
|
|
524
524
|
if key_is_timestamp(key) {
|
|
525
525
|
return json!("<TS>");
|
|
526
526
|
}
|
|
527
|
-
if key.contains("endpoint") {
|
|
527
|
+
if key.contains("endpoint") && value_looks_like_endpoint_path(&text) {
|
|
528
528
|
return json!("<SOCKET>");
|
|
529
529
|
}
|
|
530
530
|
if key_is_id(key, &text) {
|
|
@@ -557,6 +557,13 @@ fn normalize_path_string(text: &str, ctx: &NormalizeCtx) -> String {
|
|
|
557
557
|
normalize_socket_token(&out)
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
+
fn value_looks_like_endpoint_path(text: &str) -> bool {
|
|
561
|
+
text.starts_with('/')
|
|
562
|
+
|| text.starts_with("ta-")
|
|
563
|
+
|| text.contains("/tmux-")
|
|
564
|
+
|| text.contains("tmux-")
|
|
565
|
+
}
|
|
566
|
+
|
|
560
567
|
fn normalize_team_agent_binary_path(text: &str) -> String {
|
|
561
568
|
let mut out = text.to_string();
|
|
562
569
|
// 0.5.0 hermetic 教训「环境路径类 token 化」的直接延伸
|
|
@@ -566,6 +566,14 @@ pub(crate) fn attach_command_for_session(
|
|
|
566
566
|
))
|
|
567
567
|
}
|
|
568
568
|
|
|
569
|
+
pub(crate) fn attach_command_for_transport_session(
|
|
570
|
+
transport: &dyn crate::transport::Transport,
|
|
571
|
+
session_name: &SessionName,
|
|
572
|
+
) -> Option<String> {
|
|
573
|
+
let endpoint = transport.tmux_endpoint()?;
|
|
574
|
+
Some(attach_command_for_endpoint_session(&endpoint, session_name))
|
|
575
|
+
}
|
|
576
|
+
|
|
569
577
|
/// Bug #7 (prerelease 0.4.0 gate review §6): when the runtime state carries a
|
|
570
578
|
/// persisted `tmux_endpoint` / `tmux_socket` (e.g. `/private/tmp/tmux-501/default`),
|
|
571
579
|
/// the attach command MUST point at THAT endpoint, not the workspace-hash
|
|
@@ -591,6 +599,26 @@ pub(crate) fn attach_command_for_runtime_state_or_workspace(
|
|
|
591
599
|
attach_command_for_workspace(workspace, session_name, window_name)
|
|
592
600
|
}
|
|
593
601
|
|
|
602
|
+
pub(crate) fn attach_command_for_runtime_state_session_or_workspace(
|
|
603
|
+
workspace: &Path,
|
|
604
|
+
state: Option<&serde_json::Value>,
|
|
605
|
+
session_name: &SessionName,
|
|
606
|
+
) -> Option<String> {
|
|
607
|
+
if let Some((endpoint, _source)) = runtime_tmux_endpoint_from_state(state) {
|
|
608
|
+
return Some(attach_command_for_endpoint_session(endpoint, session_name));
|
|
609
|
+
}
|
|
610
|
+
attach_command_for_session(workspace, session_name)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
fn attach_command_for_endpoint_session(endpoint: &str, session_name: &SessionName) -> String {
|
|
614
|
+
let flag = if Path::new(endpoint).is_absolute() {
|
|
615
|
+
"-S"
|
|
616
|
+
} else {
|
|
617
|
+
"-L"
|
|
618
|
+
};
|
|
619
|
+
format!("tmux {flag} {endpoint} attach -t {}", session_name.as_str())
|
|
620
|
+
}
|
|
621
|
+
|
|
594
622
|
pub(crate) fn attach_commands_for_windows<'a>(
|
|
595
623
|
workspace: &Path,
|
|
596
624
|
session_name: &SessionName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.15",
|
|
4
4
|
"description": "npx installer for Team Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"team-agent-installer": "npm/install.mjs"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@team-agent/cli-darwin-arm64": "0.5.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.5.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.5.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.5.15",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.15",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.15"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|