@team-agent/installer 0.5.65 → 0.5.68
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 +8 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +6 -0
- package/crates/team-agent/src/cli/adapters.rs +6 -1
- package/crates/team-agent/src/cli/diagnose.rs +82 -0
- package/crates/team-agent/src/cli/emit.rs +3 -3
- package/crates/team-agent/src/cli/grok_slot.rs +299 -0
- package/crates/team-agent/src/cli/leader.rs +21 -7
- package/crates/team-agent/src/cli/leaders.rs +125 -1
- package/crates/team-agent/src/cli/mod.rs +20 -0
- package/crates/team-agent/src/cli/send/presentation.rs +7 -1
- package/crates/team-agent/src/cli/spec.rs +2 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
- package/crates/team-agent/src/cli/tests/base.rs +26 -3
- package/crates/team-agent/src/cli/tests/compile.rs +1 -1
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
- package/crates/team-agent/src/cli/tests/mod.rs +2 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
- package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
- package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
- package/crates/team-agent/src/communication_mode/mod.rs +6 -4
- package/crates/team-agent/src/compiler/tests.rs +23 -20
- package/crates/team-agent/src/compiler.rs +84 -5
- package/crates/team-agent/src/conpty/backend.rs +16 -0
- package/crates/team-agent/src/coordinator/backoff.rs +55 -0
- package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
- package/crates/team-agent/src/coordinator/health.rs +173 -0
- package/crates/team-agent/src/coordinator/mod.rs +31 -0
- package/crates/team-agent/src/coordinator/orphan.rs +31 -0
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
- package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
- package/crates/team-agent/src/coordinator/tick.rs +133 -22
- package/crates/team-agent/src/coordinator/types.rs +49 -0
- package/crates/team-agent/src/db/message_store.rs +39 -5
- package/crates/team-agent/src/layout/worker_env.rs +20 -3
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
- package/crates/team-agent/src/leader/registry.rs +65 -0
- package/crates/team-agent/src/leader/start.rs +1080 -58
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/display.rs +56 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
- package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
- package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
- package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
- package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
- package/crates/team-agent/src/lifecycle/launch.rs +209 -43
- package/crates/team-agent/src/lifecycle/lock.rs +42 -1
- package/crates/team-agent/src/lifecycle/mod.rs +11 -0
- package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
- package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
- package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
- package/crates/team-agent/src/lifecycle/restart.rs +29 -0
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
- package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
- package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
- package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
- package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
- package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
- package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
- package/crates/team-agent/src/lifecycle/tests.rs +23 -15
- package/crates/team-agent/src/lifecycle/types.rs +61 -2
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
- package/crates/team-agent/src/mcp_server/tests.rs +196 -2
- package/crates/team-agent/src/messaging/delivery.rs +405 -40
- package/crates/team-agent/src/messaging/helpers.rs +2 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
- package/crates/team-agent/src/messaging/results.rs +4 -0
- package/crates/team-agent/src/messaging/send.rs +34 -0
- package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
- package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/types.rs +4 -1
- package/crates/team-agent/src/model/enums.rs +14 -5
- package/crates/team-agent/src/model/permissions.rs +3 -0
- package/crates/team-agent/src/model/spec.rs +129 -6
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
- package/crates/team-agent/src/os_probe.rs +73 -2
- package/crates/team-agent/src/provider/adapter.rs +231 -4
- package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
- package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
- package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
- package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
- package/crates/team-agent/src/provider/classify.rs +4 -2
- package/crates/team-agent/src/provider/faults.rs +2 -1
- package/crates/team-agent/src/provider/mod.rs +13 -0
- package/crates/team-agent/src/provider/session/capture.rs +158 -20
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
- package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
- package/crates/team-agent/src/provider/session/mod.rs +18 -0
- package/crates/team-agent/src/provider/session/resume.rs +57 -0
- package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
- package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
- package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
- package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
- package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
- package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
- package/crates/team-agent/src/provider/session_scan.rs +8 -0
- package/crates/team-agent/src/provider/submit_now.rs +94 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
- package/crates/team-agent/src/provider/types.rs +4 -2
- package/crates/team-agent/src/provider/wire.rs +23 -1
- package/crates/team-agent/src/state/persist.rs +301 -8
- package/crates/team-agent/src/state/repository.rs +5 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
- package/crates/team-agent/src/tmux_backend.rs +1083 -75
- package/crates/team-agent/src/transport/test_support.rs +20 -0
- package/crates/team-agent/src/transport/tests/wire.rs +28 -2
- package/crates/team-agent/src/transport.rs +265 -1
- package/npm/install.mjs +129 -73
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +33 -238
- package/skills/team-agent/command-coverage.json +31 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: restart 与 start 共用的底座,含 spawn 执行、transport 解析、state 标记与各类读取判定
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: spawn_agent_window
|
|
6
|
+
//! what: 为一个席位拼命令并在目标 session 里开窗或分格起进程
|
|
7
|
+
//! - name: lifecycle_worker_tmux_backend_for_selected_state
|
|
8
|
+
//! what: 按团队已持久化的 endpoint 解析 tmux 后端,非 tmux 状态直接拒绝
|
|
9
|
+
//! - name: save_restart_projected_state
|
|
10
|
+
//! what: 同步团队投影后经 repository 带写意图落盘
|
|
11
|
+
//! - name: resume_backing_probe_for_agent
|
|
12
|
+
//! what: 探测该席位的 resume 依据是否在盘上,并记下所有查过的路径
|
|
13
|
+
//! - name: converge_missing_provider_sessions
|
|
14
|
+
//! what: 有界等待会话捕获收敛,并逐轮写进度事件
|
|
15
|
+
//! - name: mark_agent_stopped
|
|
16
|
+
//! what: 把席位标记为已停并清掉 window 与 pane_id 字段
|
|
17
|
+
//! depends:
|
|
18
|
+
//! - crate::transport::Transport
|
|
19
|
+
//! - crate::tmux_backend
|
|
20
|
+
//! - crate::transport_factory
|
|
21
|
+
//! - crate::provider
|
|
22
|
+
//! - crate::session_capture
|
|
23
|
+
//! - crate::state::projection
|
|
24
|
+
//! - crate::state::repository
|
|
25
|
+
//! - crate::state::persist
|
|
26
|
+
//! - crate::coordinator::health
|
|
27
|
+
//! - crate::event_log::EventLog
|
|
28
|
+
//! - crate::lifecycle::launch
|
|
29
|
+
//! - crate::lifecycle::profile_launch
|
|
30
|
+
//! - crate::lifecycle::worker_command_context
|
|
31
|
+
//! - crate::lifecycle::restart::selection
|
|
32
|
+
//! boundary:
|
|
33
|
+
//! - 状态是 conpty 时 tmux 专用解析器直接报错,不静默降级到 tmux
|
|
34
|
+
//! - 探测类判定失败一律倒向保守值,不把不确定当成存活
|
|
35
|
+
//! - 清活动观测只删观测字段,不动生命周期与拓扑字段
|
|
36
|
+
//! - 收敛与排空都是有界等待,不无限阻塞
|
|
37
|
+
//! maturity: wired
|
|
38
|
+
//! ---
|
|
1
39
|
use super::*;
|
|
2
40
|
|
|
3
41
|
pub(super) struct SpawnedAgentWindow {
|
|
@@ -23,6 +61,12 @@ pub(super) struct SameRoleCohortTarget {
|
|
|
23
61
|
}
|
|
24
62
|
|
|
25
63
|
impl SameRoleCohortTarget {
|
|
64
|
+
/// ---
|
|
65
|
+
/// purpose: 构造一个同角色同批目标
|
|
66
|
+
/// params:
|
|
67
|
+
/// window: 该席位的窗口名
|
|
68
|
+
/// returns: 未带期望 pane 的目标
|
|
69
|
+
/// ---
|
|
26
70
|
pub(super) fn new(agent_id: &AgentId, window: &str) -> Self {
|
|
27
71
|
Self {
|
|
28
72
|
agent_id: agent_id.as_str().to_string(),
|
|
@@ -31,16 +75,28 @@ impl SameRoleCohortTarget {
|
|
|
31
75
|
}
|
|
32
76
|
}
|
|
33
77
|
|
|
78
|
+
/// ---
|
|
79
|
+
/// purpose: 给目标补上期望的旧 pane
|
|
80
|
+
/// returns: 带期望 pane 的目标
|
|
81
|
+
/// ---
|
|
34
82
|
pub(super) fn with_expected_pane_id(mut self, pane_id: Option<&str>) -> Self {
|
|
35
83
|
self.expected_pane_id = pane_id.map(ToString::to_string);
|
|
36
84
|
self
|
|
37
85
|
}
|
|
38
86
|
}
|
|
39
87
|
|
|
88
|
+
/// ---
|
|
89
|
+
/// purpose: 判断该窗口是不是按席位命名的独立窗口
|
|
90
|
+
/// returns: 窗口名等于席位名且不是规范布局窗口时为 true
|
|
91
|
+
/// ---
|
|
40
92
|
pub(super) fn is_per_agent_cohort_window(window: &str, agent_id: &AgentId) -> bool {
|
|
41
93
|
window == agent_id.as_str() && !crate::lifecycle::launch::is_adaptive_layout_window_pub(window)
|
|
42
94
|
}
|
|
43
95
|
|
|
96
|
+
/// ---
|
|
97
|
+
/// purpose: spawn 之前检查同角色同批是否已有残留
|
|
98
|
+
/// returns: 期望基数为 0;不满足时给出可读的拒绝说明,满足则 None
|
|
99
|
+
/// ---
|
|
44
100
|
pub(super) fn same_role_cohort_pre_spawn_error(
|
|
45
101
|
transport: &dyn crate::transport::Transport,
|
|
46
102
|
session_name: &SessionName,
|
|
@@ -50,6 +106,10 @@ pub(super) fn same_role_cohort_pre_spawn_error(
|
|
|
50
106
|
same_role_cohort_error(transport, session_name, operation, targets, 0, true)
|
|
51
107
|
}
|
|
52
108
|
|
|
109
|
+
/// ---
|
|
110
|
+
/// purpose: spawn 之后检查同角色同批是否恰好只剩一个
|
|
111
|
+
/// returns: 期望基数为 1;不满足时给出可读的拒绝说明,满足则 None
|
|
112
|
+
/// ---
|
|
53
113
|
pub(super) fn same_role_cohort_exactly_one_error(
|
|
54
114
|
transport: &dyn crate::transport::Transport,
|
|
55
115
|
session_name: &SessionName,
|
|
@@ -59,6 +119,11 @@ pub(super) fn same_role_cohort_exactly_one_error(
|
|
|
59
119
|
same_role_cohort_error(transport, session_name, operation, targets, 1, false)
|
|
60
120
|
}
|
|
61
121
|
|
|
122
|
+
/// ---
|
|
123
|
+
/// purpose: 杀掉这些目标记录的旧 pane
|
|
124
|
+
/// returns: 全部处理完返回空值;目标没有期望 pane 或该 pane 明确不存在时跳过
|
|
125
|
+
/// errors: 杀 pane 失败时返回带席位、窗口与 pane 的错误串
|
|
126
|
+
/// ---
|
|
62
127
|
pub(super) fn retire_expected_same_role_cohorts(
|
|
63
128
|
transport: &dyn crate::transport::Transport,
|
|
64
129
|
operation: &str,
|
|
@@ -175,6 +240,17 @@ fn same_role_cohort_error(
|
|
|
175
240
|
None
|
|
176
241
|
}
|
|
177
242
|
|
|
243
|
+
/// ---
|
|
244
|
+
/// purpose: 为一个席位拼出命令并在目标 session 里起进程
|
|
245
|
+
/// params:
|
|
246
|
+
/// resume_session_id: 给出且该 provider 支持 resume 时按 resume 起,否则丢弃它全新起
|
|
247
|
+
/// into_existing_session: 目标 session 已存在时开新窗口,否则新建 session
|
|
248
|
+
/// layout_placement: 有布局位置时按布局开窗或分格
|
|
249
|
+
/// spawn_cwd_override: 覆盖工作目录
|
|
250
|
+
/// owner_team_id_override: 显式指定写进 worker 环境的 owner team,缺省时退回席位行与顶层活跃键
|
|
251
|
+
/// returns: spawn 结果、时间戳、命令计划、profile 启动参数、布局位置与实际使用的 owner team
|
|
252
|
+
/// errors: 命令拼装、profile 准备或 transport spawn 失败时返回 LifecycleError
|
|
253
|
+
/// ---
|
|
178
254
|
#[allow(clippy::too_many_arguments)]
|
|
179
255
|
pub(super) fn spawn_agent_window(
|
|
180
256
|
workspace: &Path,
|
|
@@ -213,7 +289,9 @@ pub(super) fn spawn_agent_window(
|
|
|
213
289
|
let safety = if let Some(safety) = safety {
|
|
214
290
|
safety
|
|
215
291
|
} else {
|
|
216
|
-
detected_safety = crate::lifecycle::launch::
|
|
292
|
+
detected_safety = crate::lifecycle::launch::effective_runtime_config_for_worker_spawn_json(
|
|
293
|
+
agent, provider,
|
|
294
|
+
)?;
|
|
217
295
|
&detected_safety
|
|
218
296
|
};
|
|
219
297
|
let command_agent = crate::lifecycle::worker_command_context::WorkerCommandAgent::from_json(
|
|
@@ -226,7 +304,6 @@ pub(super) fn spawn_agent_window(
|
|
|
226
304
|
let tools = crate::lifecycle::worker_command_context::resolved_tool_strings_for_command(
|
|
227
305
|
&command_agent,
|
|
228
306
|
provider,
|
|
229
|
-
safety,
|
|
230
307
|
)?;
|
|
231
308
|
let resolved_tool_refs: Vec<&str> = tools.iter().map(String::as_str).collect();
|
|
232
309
|
// owner_team_id resolution priority (Issue 2 fix):
|
|
@@ -325,6 +402,7 @@ pub(super) fn spawn_agent_window(
|
|
|
325
402
|
workspace,
|
|
326
403
|
agent_id.as_str(),
|
|
327
404
|
team_id.as_deref(),
|
|
405
|
+
Some(crate::lifecycle::launch::auth_mode_env_value(auth_mode)),
|
|
328
406
|
);
|
|
329
407
|
crate::lifecycle::launch::apply_profile_launch_env(&mut env, &profile_launch);
|
|
330
408
|
crate::lifecycle::launch::apply_mcp_auto_approval_env(&mut env, safety);
|
|
@@ -336,6 +414,27 @@ pub(super) fn spawn_agent_window(
|
|
|
336
414
|
&mut env,
|
|
337
415
|
)?;
|
|
338
416
|
}
|
|
417
|
+
// 0.5.67 Cursor 方案 1 变体: role 经 workspace rules 文件注入 (不 argv)。
|
|
418
|
+
if provider == crate::provider::Provider::CursorAgent {
|
|
419
|
+
crate::lifecycle::launch::refuse_second_cursor_occupant(
|
|
420
|
+
workspace,
|
|
421
|
+
agent_id.as_str(),
|
|
422
|
+
None,
|
|
423
|
+
)?;
|
|
424
|
+
crate::lifecycle::launch::apply_cursor_agent_rules_overlay(
|
|
425
|
+
workspace,
|
|
426
|
+
agent_id.as_str(),
|
|
427
|
+
&system_prompt,
|
|
428
|
+
)?;
|
|
429
|
+
crate::lifecycle::launch::apply_cursor_mcp_overlay(workspace, &mcp_config)?;
|
|
430
|
+
crate::lifecycle::launch::enable_cursor_workspace_mcp(workspace)?;
|
|
431
|
+
crate::lifecycle::launch::apply_cursor_workspace_physical_path(&mut plan.argv, workspace);
|
|
432
|
+
crate::lifecycle::launch::apply_cursor_subscription_proxy_env(&mut env);
|
|
433
|
+
}
|
|
434
|
+
if provider == crate::provider::Provider::Grok {
|
|
435
|
+
crate::lifecycle::launch::ensure_grok_login_and_folder_trust(workspace)?;
|
|
436
|
+
crate::lifecycle::launch::apply_grok_mcp_overlay(workspace, &mcp_config)?;
|
|
437
|
+
}
|
|
339
438
|
// 0.3.28 Step 3: per Python parity, worker spawn cwd is ALWAYS `workspace`.
|
|
340
439
|
// The persisted-state `agent.spawn_cwd` override is ignored (it was a
|
|
341
440
|
// Rust-only extension that drifted to `.team/runtime/<team_key>/` after
|
|
@@ -558,6 +657,10 @@ fn is_structural_startup_prompt_error(error: &str) -> bool {
|
|
|
558
657
|
.any(|needle| lower.contains(needle))
|
|
559
658
|
}
|
|
560
659
|
|
|
660
|
+
/// ---
|
|
661
|
+
/// purpose: 用 spec 里的同名 agent 补回 state 席位行缺的命令上下文字段
|
|
662
|
+
/// returns: 合并后的席位行;spec 读不到或找不到该 agent 时原样返回
|
|
663
|
+
/// ---
|
|
561
664
|
pub(super) fn rehydrate_agent_command_context_from_spec(
|
|
562
665
|
spec_workspace: &Path,
|
|
563
666
|
agent_id: &AgentId,
|
|
@@ -591,6 +694,8 @@ fn merge_command_context_fields(
|
|
|
591
694
|
"effort",
|
|
592
695
|
"profile",
|
|
593
696
|
"permission_mode",
|
|
697
|
+
// 0.5.66 bypass 单源:rehydrate 同步合入,restart 的 safety 构造读到新值。
|
|
698
|
+
"dangerously_skip_permissions",
|
|
594
699
|
] {
|
|
595
700
|
if let Some(value) = spec_agent.get(field).and_then(yaml_value_to_json) {
|
|
596
701
|
obj.insert(field.to_string(), value);
|
|
@@ -648,6 +753,13 @@ fn window_present_in_live(
|
|
|
648
753
|
false
|
|
649
754
|
}
|
|
650
755
|
|
|
756
|
+
/// ---
|
|
757
|
+
/// purpose: 起该 workspace 的 coordinator
|
|
758
|
+
/// params:
|
|
759
|
+
/// team_key: 传给 coordinator 的团队键
|
|
760
|
+
/// returns: 启动摘要
|
|
761
|
+
/// errors: 启动失败时返回 StatePersist
|
|
762
|
+
/// ---
|
|
651
763
|
pub(super) fn start_coordinator_for_workspace(
|
|
652
764
|
workspace: &Path,
|
|
653
765
|
team_key: Option<&str>,
|
|
@@ -658,6 +770,13 @@ pub(super) fn start_coordinator_for_workspace(
|
|
|
658
770
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))
|
|
659
771
|
}
|
|
660
772
|
|
|
773
|
+
/// ---
|
|
774
|
+
/// purpose: 按团队持久化的 tmux endpoint 解析出后端,让 restart、add 与 fork 落在同一 socket
|
|
775
|
+
/// params:
|
|
776
|
+
/// team: 目标团队,None 时按唯一性选
|
|
777
|
+
/// returns: 绑定到该 endpoint 的 tmux 后端;冷 workspace 无持久 endpoint 时退到按 workspace 派生
|
|
778
|
+
/// errors: 团队目标歧义或未解析返回 TeamSelect;state 声明后端是 conpty 时也返回 TeamSelect 拒绝,不降级
|
|
779
|
+
/// ---
|
|
661
780
|
/// State-aware tmux backend resolver. Reads the team's persisted
|
|
662
781
|
/// `tmux_endpoint` (set at `team-agent launch` time and shared across
|
|
663
782
|
/// restart/add-agent/fork-agent) and constructs a TmuxBackend on THAT socket,
|
|
@@ -732,6 +851,11 @@ pub(crate) fn lifecycle_worker_tmux_backend_for_selected_state(
|
|
|
732
851
|
.unwrap_or_else(|| crate::tmux_backend::TmuxBackend::for_workspace(run_workspace)))
|
|
733
852
|
}
|
|
734
853
|
|
|
854
|
+
/// ---
|
|
855
|
+
/// purpose: 与上面同样的团队选择语义,但返回工厂解析出的通用 transport
|
|
856
|
+
/// returns: 已解析的 transport,含后端种类、来源与提示
|
|
857
|
+
/// errors: 团队选择失败或工厂拒绝时返回 TeamSelect,读 state 失败返回 StatePersist
|
|
858
|
+
/// ---
|
|
735
859
|
/// 0.5.x Phase 1d Batch 1: new generic-typed lifecycle resolver.
|
|
736
860
|
///
|
|
737
861
|
/// Same team-selection semantics as the legacy tmux-typed variant, but
|
|
@@ -770,6 +894,11 @@ pub(crate) fn lifecycle_worker_transport_for_selected_state(
|
|
|
770
894
|
.map_err(|e| LifecycleError::TeamSelect(e.to_string()))
|
|
771
895
|
}
|
|
772
896
|
|
|
897
|
+
/// ---
|
|
898
|
+
/// purpose: 由已取到的 state 解析 tmux 后端与它的来源
|
|
899
|
+
/// returns: 后端与 endpoint 来源
|
|
900
|
+
/// errors: state 声明后端是 conpty 时返回 TeamSelect
|
|
901
|
+
/// ---
|
|
773
902
|
pub(super) fn lifecycle_worker_tmux_backend_selection_for_state(
|
|
774
903
|
run_workspace: &Path,
|
|
775
904
|
state: &serde_json::Value,
|
|
@@ -791,6 +920,10 @@ pub(super) fn lifecycle_worker_tmux_backend_selection_for_state(
|
|
|
791
920
|
)
|
|
792
921
|
}
|
|
793
922
|
|
|
923
|
+
/// ---
|
|
924
|
+
/// purpose: 由已取到的 state 直接给出 tmux 后端
|
|
925
|
+
/// returns: 绑定到该 state endpoint 的后端,缺失时按 workspace 派生
|
|
926
|
+
/// ---
|
|
794
927
|
pub(super) fn lifecycle_worker_tmux_backend_for_state(
|
|
795
928
|
run_workspace: &Path,
|
|
796
929
|
state: &serde_json::Value,
|
|
@@ -799,13 +932,14 @@ pub(super) fn lifecycle_worker_tmux_backend_for_state(
|
|
|
799
932
|
.backend
|
|
800
933
|
}
|
|
801
934
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
935
|
+
/// ---
|
|
936
|
+
/// purpose: 同步团队投影后落盘 restart 结果
|
|
937
|
+
/// params:
|
|
938
|
+
/// topology_authority_agent_ids: 本次以内存值为拓扑权威的席位
|
|
939
|
+
/// returns: 成功返回空值
|
|
940
|
+
/// errors: 落盘失败时返回 StatePersist
|
|
941
|
+
/// contract_id: lifecycle.common.save_restart_projected_state
|
|
942
|
+
/// ---
|
|
809
943
|
pub(super) fn save_restart_projected_state(
|
|
810
944
|
workspace: &Path,
|
|
811
945
|
state: &mut serde_json::Value,
|
|
@@ -821,6 +955,14 @@ pub(super) fn save_restart_projected_state(
|
|
|
821
955
|
)
|
|
822
956
|
}
|
|
823
957
|
|
|
958
|
+
/// ---
|
|
959
|
+
/// purpose: 同上,并可指定哪些席位跳过会话捕获回填
|
|
960
|
+
/// params:
|
|
961
|
+
/// skip_capture_backfill_agent_ids: 跳过回填的席位
|
|
962
|
+
/// returns: 成功返回空值
|
|
963
|
+
/// errors: 落盘失败时返回 StatePersist
|
|
964
|
+
/// contract_id: lifecycle.common.save_restart_projected_state
|
|
965
|
+
/// ---
|
|
824
966
|
pub(super) fn save_restart_projected_state_with_capture_backfill_skip(
|
|
825
967
|
workspace: &Path,
|
|
826
968
|
state: &mut serde_json::Value,
|
|
@@ -829,17 +971,22 @@ pub(super) fn save_restart_projected_state_with_capture_backfill_skip(
|
|
|
829
971
|
topology_authority_agent_ids: &[&str],
|
|
830
972
|
) -> Result<(), LifecycleError> {
|
|
831
973
|
sync_restart_team_projections(state, team_key);
|
|
832
|
-
crate::state::repository::StateRepository::new(workspace)
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
974
|
+
crate::state::repository::StateRepository::new(workspace)
|
|
975
|
+
.save(
|
|
976
|
+
crate::state::repository::StateWriteIntent::RestartTeam {
|
|
977
|
+
team_key,
|
|
978
|
+
topology_authority_agent_ids,
|
|
979
|
+
skip_capture_backfill_agent_ids,
|
|
980
|
+
},
|
|
981
|
+
state,
|
|
982
|
+
)
|
|
983
|
+
.map_err(|e| LifecycleError::StatePersist(e.to_string()))
|
|
841
984
|
}
|
|
842
985
|
|
|
986
|
+
/// ---
|
|
987
|
+
/// purpose: 定出本次投影使用的团队键
|
|
988
|
+
/// returns: 显式 team 优先,其次 state 里的活跃键,最后由 state 推算
|
|
989
|
+
/// ---
|
|
843
990
|
pub(super) fn restart_projection_team_key(state: &serde_json::Value, team: Option<&str>) -> String {
|
|
844
991
|
team.filter(|key| !key.is_empty())
|
|
845
992
|
.map(str::to_string)
|
|
@@ -853,6 +1000,12 @@ pub(super) fn restart_projection_team_key(state: &serde_json::Value, team: Optio
|
|
|
853
1000
|
.unwrap_or_else(|| crate::state::projection::team_state_key(state))
|
|
854
1001
|
}
|
|
855
1002
|
|
|
1003
|
+
/// ---
|
|
1004
|
+
/// purpose: 把当前顶层状态压实后写回 teams 表
|
|
1005
|
+
/// params:
|
|
1006
|
+
/// state: 就地改写;显式团队键允许覆盖或新建,别名键只在盘上已有且身份不冲突时才写
|
|
1007
|
+
/// returns: teams 表缺失或为空时不动
|
|
1008
|
+
/// ---
|
|
856
1009
|
pub(super) fn sync_restart_team_projections(state: &mut serde_json::Value, team_key: &str) {
|
|
857
1010
|
let Some(teams) = state.get("teams").and_then(serde_json::Value::as_object) else {
|
|
858
1011
|
return;
|
|
@@ -933,6 +1086,10 @@ fn json_team_identity_matches(existing: &serde_json::Value, compact: &serde_json
|
|
|
933
1086
|
true
|
|
934
1087
|
}
|
|
935
1088
|
|
|
1089
|
+
/// ---
|
|
1090
|
+
/// purpose: 取 state 里的 session 名
|
|
1091
|
+
/// returns: 非空的 session_name,缺失时退到默认名
|
|
1092
|
+
/// ---
|
|
936
1093
|
pub(super) fn state_session_name(state: &serde_json::Value) -> SessionName {
|
|
937
1094
|
state
|
|
938
1095
|
.get("session_name")
|
|
@@ -942,6 +1099,10 @@ pub(super) fn state_session_name(state: &serde_json::Value) -> SessionName {
|
|
|
942
1099
|
.unwrap_or_else(|| SessionName::new("team-agent"))
|
|
943
1100
|
}
|
|
944
1101
|
|
|
1102
|
+
/// ---
|
|
1103
|
+
/// purpose: 判断 state 里是否记了非空 session 名
|
|
1104
|
+
/// returns: 记了则 true
|
|
1105
|
+
/// ---
|
|
945
1106
|
pub(super) fn session_name_present(state: &serde_json::Value) -> bool {
|
|
946
1107
|
state
|
|
947
1108
|
.get("session_name")
|
|
@@ -950,6 +1111,12 @@ pub(super) fn session_name_present(state: &serde_json::Value) -> bool {
|
|
|
950
1111
|
.unwrap_or(false)
|
|
951
1112
|
}
|
|
952
1113
|
|
|
1114
|
+
/// ---
|
|
1115
|
+
/// purpose: 探测 session 是否存活
|
|
1116
|
+
/// params:
|
|
1117
|
+
/// default: 探测本身 panic 时采用的兜底判定
|
|
1118
|
+
/// returns: transport 明确回答时用它;返回错误时判为不存活;探测 panic 时用兜底值
|
|
1119
|
+
/// ---
|
|
953
1120
|
pub(super) fn session_live_or_default(
|
|
954
1121
|
transport: &dyn crate::transport::Transport,
|
|
955
1122
|
session_name: &SessionName,
|
|
@@ -964,6 +1131,10 @@ pub(super) fn session_live_or_default(
|
|
|
964
1131
|
}
|
|
965
1132
|
}
|
|
966
1133
|
|
|
1134
|
+
/// ---
|
|
1135
|
+
/// purpose: 取席位行的 provider
|
|
1136
|
+
/// returns: 解析成功用它,缺失或不认识时退到 codex
|
|
1137
|
+
/// ---
|
|
967
1138
|
pub(super) fn agent_provider(agent: &serde_json::Value) -> Provider {
|
|
968
1139
|
agent
|
|
969
1140
|
.get("provider")
|
|
@@ -972,6 +1143,10 @@ pub(super) fn agent_provider(agent: &serde_json::Value) -> Provider {
|
|
|
972
1143
|
.unwrap_or(Provider::Codex)
|
|
973
1144
|
}
|
|
974
1145
|
|
|
1146
|
+
/// ---
|
|
1147
|
+
/// purpose: 取席位行的 auth_mode
|
|
1148
|
+
/// returns: 解析成功用它,缺失或不认识时退到 subscription
|
|
1149
|
+
/// ---
|
|
975
1150
|
pub(super) fn agent_auth_mode(agent: &serde_json::Value) -> AuthMode {
|
|
976
1151
|
agent
|
|
977
1152
|
.get("auth_mode")
|
|
@@ -980,6 +1155,10 @@ pub(super) fn agent_auth_mode(agent: &serde_json::Value) -> AuthMode {
|
|
|
980
1155
|
.unwrap_or(AuthMode::Subscription)
|
|
981
1156
|
}
|
|
982
1157
|
|
|
1158
|
+
/// ---
|
|
1159
|
+
/// purpose: 取席位行记录的会话 id
|
|
1160
|
+
/// returns: 非空时返回,否则 None
|
|
1161
|
+
/// ---
|
|
983
1162
|
pub(super) fn agent_session_id(agent: &serde_json::Value) -> Option<SessionId> {
|
|
984
1163
|
agent
|
|
985
1164
|
.get("session_id")
|
|
@@ -988,6 +1167,10 @@ pub(super) fn agent_session_id(agent: &serde_json::Value) -> Option<SessionId> {
|
|
|
988
1167
|
.map(SessionId::new)
|
|
989
1168
|
}
|
|
990
1169
|
|
|
1170
|
+
/// ---
|
|
1171
|
+
/// purpose: 取席位行记录的 rollout 路径
|
|
1172
|
+
/// returns: 非空时返回,否则 None
|
|
1173
|
+
/// ---
|
|
991
1174
|
pub(super) fn agent_rollout_path(agent: &serde_json::Value) -> Option<RolloutPath> {
|
|
992
1175
|
agent
|
|
993
1176
|
.get("rollout_path")
|
|
@@ -996,6 +1179,10 @@ pub(super) fn agent_rollout_path(agent: &serde_json::Value) -> Option<RolloutPat
|
|
|
996
1179
|
.map(RolloutPath::new)
|
|
997
1180
|
}
|
|
998
1181
|
|
|
1182
|
+
/// ---
|
|
1183
|
+
/// purpose: 判断该席位的 resume 依据是否存在
|
|
1184
|
+
/// returns: 探测结果里的存在位
|
|
1185
|
+
/// ---
|
|
999
1186
|
pub(super) fn resume_backing_exists_for_agent(
|
|
1000
1187
|
workspace: &Path,
|
|
1001
1188
|
agent_id: &AgentId,
|
|
@@ -1035,6 +1222,13 @@ pub(crate) struct SessionIdentityProbeResult {
|
|
|
1035
1222
|
pub rollout_path: Option<PathBuf>,
|
|
1036
1223
|
}
|
|
1037
1224
|
|
|
1225
|
+
/// ---
|
|
1226
|
+
/// purpose: 探测 rollout 文件里嵌的席位身份是否与本席位一致
|
|
1227
|
+
/// params:
|
|
1228
|
+
/// _provider: 未参与判定
|
|
1229
|
+
/// rollout_path: 没有路径时三项都返回未知
|
|
1230
|
+
/// returns: 一致性判定、读到的嵌入席位名与实际探测路径;读不出嵌入身份时一致性为未知
|
|
1231
|
+
/// ---
|
|
1038
1232
|
pub(crate) fn session_identity_probe_for_agent(
|
|
1039
1233
|
agent_id: &AgentId,
|
|
1040
1234
|
_provider: Provider,
|
|
@@ -1059,6 +1253,10 @@ pub(crate) fn session_identity_probe_for_agent(
|
|
|
1059
1253
|
}
|
|
1060
1254
|
}
|
|
1061
1255
|
|
|
1256
|
+
/// ---
|
|
1257
|
+
/// purpose: 按 provider 分支探测 resume 依据,并记录所有查过的路径
|
|
1258
|
+
/// returns: 存在位与查过的路径列表;持久化的 rollout 路径即使不存在也记进列表
|
|
1259
|
+
/// ---
|
|
1062
1260
|
pub(super) fn resume_backing_probe_for_agent(
|
|
1063
1261
|
workspace: &Path,
|
|
1064
1262
|
agent_id: &AgentId,
|
|
@@ -1110,6 +1308,47 @@ pub(super) fn resume_backing_probe_for_agent(
|
|
|
1110
1308
|
}
|
|
1111
1309
|
copilot_session_store_has_session(session_id.as_str())
|
|
1112
1310
|
}
|
|
1311
|
+
Provider::Grok => {
|
|
1312
|
+
let spawn_cwd = agent
|
|
1313
|
+
.get("spawn_cwd")
|
|
1314
|
+
.and_then(serde_json::Value::as_str)
|
|
1315
|
+
.map(PathBuf::from)
|
|
1316
|
+
.unwrap_or_else(|| workspace.to_path_buf());
|
|
1317
|
+
match crate::provider::session_scan::grok::grok_session_dir(
|
|
1318
|
+
&spawn_cwd,
|
|
1319
|
+
session_id.as_str(),
|
|
1320
|
+
) {
|
|
1321
|
+
Some(dir) => {
|
|
1322
|
+
checked_paths.push(dir.clone());
|
|
1323
|
+
crate::provider::session_scan::grok::grok_session_archive_present(&dir)
|
|
1324
|
+
}
|
|
1325
|
+
None => false,
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
Provider::CursorAgent => {
|
|
1329
|
+
let spawn_cwd = agent
|
|
1330
|
+
.get("spawn_cwd")
|
|
1331
|
+
.and_then(serde_json::Value::as_str)
|
|
1332
|
+
.map(PathBuf::from)
|
|
1333
|
+
.unwrap_or_else(|| workspace.to_path_buf());
|
|
1334
|
+
let rollout_ok = rollout_path.is_some_and(|path| {
|
|
1335
|
+
crate::provider::session_scan::cursor::cursor_session_archive_present(path.as_path())
|
|
1336
|
+
});
|
|
1337
|
+
let discovered = crate::provider::session_scan::cursor::cursor_session_dir_for_cwd(
|
|
1338
|
+
session_id.as_str(),
|
|
1339
|
+
&spawn_cwd,
|
|
1340
|
+
);
|
|
1341
|
+
if let Some(dir) = discovered.as_ref() {
|
|
1342
|
+
checked_paths.push(dir.clone());
|
|
1343
|
+
}
|
|
1344
|
+
if let Some(home) = std::env::var_os("HOME").map(PathBuf::from) {
|
|
1345
|
+
checked_paths.push(home.join(".cursor").join("chats"));
|
|
1346
|
+
}
|
|
1347
|
+
rollout_ok
|
|
1348
|
+
|| discovered.as_ref().is_some_and(|dir| {
|
|
1349
|
+
crate::provider::session_scan::cursor::cursor_session_archive_present(dir)
|
|
1350
|
+
})
|
|
1351
|
+
}
|
|
1113
1352
|
Provider::GeminiCli | Provider::Fake => false,
|
|
1114
1353
|
};
|
|
1115
1354
|
|
|
@@ -1124,10 +1363,20 @@ pub(super) fn resume_backing_probe_for_agent(
|
|
|
1124
1363
|
}
|
|
1125
1364
|
}
|
|
1126
1365
|
|
|
1366
|
+
/// ---
|
|
1367
|
+
/// purpose: 判断该 provider 是否支持 resume
|
|
1368
|
+
/// returns: 由 provider 适配器的能力位给出
|
|
1369
|
+
/// contract_id: lifecycle.common.provider_supports_resume
|
|
1370
|
+
/// ---
|
|
1127
1371
|
pub(super) fn provider_supports_resume(provider: Provider) -> bool {
|
|
1128
1372
|
crate::provider::get_adapter(provider).caps().resume
|
|
1129
1373
|
}
|
|
1130
1374
|
|
|
1375
|
+
/// ---
|
|
1376
|
+
/// purpose: 由 provider wire 名判断是否支持 resume
|
|
1377
|
+
/// returns: 名字不认识时为 false
|
|
1378
|
+
/// contract_id: lifecycle.common.provider_supports_resume
|
|
1379
|
+
/// ---
|
|
1131
1380
|
pub(super) fn provider_wire_supports_resume(provider: &str) -> bool {
|
|
1132
1381
|
parse_provider(provider)
|
|
1133
1382
|
.map(provider_supports_resume)
|
|
@@ -1303,6 +1552,13 @@ fn copilot_session_store_has_session(session_id: &str) -> bool {
|
|
|
1303
1552
|
.is_ok()
|
|
1304
1553
|
}
|
|
1305
1554
|
|
|
1555
|
+
/// ---
|
|
1556
|
+
/// purpose: 对缺会话的席位做一次捕获尝试
|
|
1557
|
+
/// params:
|
|
1558
|
+
/// state: 就地改写
|
|
1559
|
+
/// returns: 本次是否改动了 state
|
|
1560
|
+
/// errors: 捕获过程出错时返回 Provider
|
|
1561
|
+
/// ---
|
|
1306
1562
|
pub(crate) fn refresh_missing_provider_sessions(
|
|
1307
1563
|
state: &mut serde_json::Value,
|
|
1308
1564
|
) -> Result<bool, LifecycleError> {
|
|
@@ -1316,6 +1572,15 @@ pub(crate) fn refresh_missing_provider_sessions(
|
|
|
1316
1572
|
.map_err(|e| LifecycleError::Provider(e.to_string()))
|
|
1317
1573
|
}
|
|
1318
1574
|
|
|
1575
|
+
/// ---
|
|
1576
|
+
/// purpose: 有界等待缺会话席位收敛,并逐轮写进度事件
|
|
1577
|
+
/// params:
|
|
1578
|
+
/// deadline: 等待上限
|
|
1579
|
+
/// poll_interval: 轮询间隔
|
|
1580
|
+
/// allow_fresh: 只写进事件载荷,不改变收敛判定
|
|
1581
|
+
/// returns: 收敛结论
|
|
1582
|
+
/// errors: 收敛过程出错时返回 StatePersist
|
|
1583
|
+
/// ---
|
|
1319
1584
|
pub(crate) fn converge_missing_provider_sessions(
|
|
1320
1585
|
state: &mut serde_json::Value,
|
|
1321
1586
|
deadline: std::time::Duration,
|
|
@@ -1361,6 +1626,10 @@ fn write_session_convergence_progress_event(
|
|
|
1361
1626
|
.map_err(|e| e.to_string())
|
|
1362
1627
|
}
|
|
1363
1628
|
|
|
1629
|
+
/// ---
|
|
1630
|
+
/// purpose: 列出必须等到会话收敛才能 restart 的席位
|
|
1631
|
+
/// returns: 排序后的席位 id;只保留无会话、状态为 running 且确有待保留上下文的席位,从未捕获过的席位不进入
|
|
1632
|
+
/// ---
|
|
1364
1633
|
pub(crate) fn restart_required_missing_session_agent_ids(state: &serde_json::Value) -> Vec<String> {
|
|
1365
1634
|
let mut missing = crate::session_capture::incomplete_resumable_agent_ids(state)
|
|
1366
1635
|
.into_iter()
|
|
@@ -1400,6 +1669,10 @@ pub(crate) fn restart_required_missing_session_agent_ids(state: &serde_json::Val
|
|
|
1400
1669
|
missing.sort();
|
|
1401
1670
|
missing
|
|
1402
1671
|
}
|
|
1672
|
+
/// ---
|
|
1673
|
+
/// purpose: 取该席位的窗口名
|
|
1674
|
+
/// returns: 席位行里的非空 window,缺失时用席位 id
|
|
1675
|
+
/// ---
|
|
1403
1676
|
pub(super) fn agent_window(agent: &serde_json::Value, agent_id: &AgentId) -> String {
|
|
1404
1677
|
agent
|
|
1405
1678
|
.get("window")
|
|
@@ -1411,6 +1684,10 @@ pub(super) fn agent_window(agent: &serde_json::Value, agent_id: &AgentId) -> Str
|
|
|
1411
1684
|
|
|
1412
1685
|
pub(super) use crate::provider::wire::{parse_provider, provider_wire};
|
|
1413
1686
|
|
|
1687
|
+
/// ---
|
|
1688
|
+
/// purpose: 把 auth_mode 字符串解析成枚举
|
|
1689
|
+
/// returns: 只认三种取值,未知返回 None
|
|
1690
|
+
/// ---
|
|
1414
1691
|
pub(super) fn parse_auth_mode(raw: &str) -> Option<AuthMode> {
|
|
1415
1692
|
match raw {
|
|
1416
1693
|
"subscription" => Some(AuthMode::Subscription),
|
|
@@ -1420,6 +1697,11 @@ pub(super) fn parse_auth_mode(raw: &str) -> Option<AuthMode> {
|
|
|
1420
1697
|
}
|
|
1421
1698
|
}
|
|
1422
1699
|
|
|
1700
|
+
/// ---
|
|
1701
|
+
/// purpose: 从给定目录读出 team spec
|
|
1702
|
+
/// returns: 解析后的 YAML
|
|
1703
|
+
/// errors: 文件不存在返回 TeamSelect,读文件或解析失败返回 Compile
|
|
1704
|
+
/// ---
|
|
1423
1705
|
pub(super) fn load_team_spec(workspace: &Path) -> Result<YamlValue, LifecycleError> {
|
|
1424
1706
|
let spec_path = workspace.join("team.spec.yaml");
|
|
1425
1707
|
if !spec_path.exists() {
|
|
@@ -1433,6 +1715,10 @@ pub(super) fn load_team_spec(workspace: &Path) -> Result<YamlValue, LifecycleErr
|
|
|
1433
1715
|
yaml::loads(&text).map_err(|e| LifecycleError::Compile(e.to_string()))
|
|
1434
1716
|
}
|
|
1435
1717
|
|
|
1718
|
+
/// ---
|
|
1719
|
+
/// purpose: 在 spec 的 agents 列表里找该席位
|
|
1720
|
+
/// returns: 命中的节点;该 id 其实是 leader 时返回 None
|
|
1721
|
+
/// ---
|
|
1436
1722
|
pub(super) fn find_spec_agent<'a>(
|
|
1437
1723
|
spec: &'a YamlValue,
|
|
1438
1724
|
agent_id: &AgentId,
|
|
@@ -1455,10 +1741,18 @@ pub(super) fn find_spec_agent<'a>(
|
|
|
1455
1741
|
})
|
|
1456
1742
|
}
|
|
1457
1743
|
|
|
1744
|
+
/// ---
|
|
1745
|
+
/// purpose: 构造未知席位的错误
|
|
1746
|
+
/// returns: 带席位 id 的 RequirementUnmet
|
|
1747
|
+
/// ---
|
|
1458
1748
|
pub(super) fn unknown_worker(agent_id: &AgentId) -> LifecycleError {
|
|
1459
1749
|
LifecycleError::RequirementUnmet(format!("unknown worker agent id: {agent_id}"))
|
|
1460
1750
|
}
|
|
1461
1751
|
|
|
1752
|
+
/// ---
|
|
1753
|
+
/// purpose: 定出 session 名,state 缺失时回落到 spec
|
|
1754
|
+
/// returns: 依次取 state 的 session_name、spec 的 runtime.session_name、由 team 名派生,最后用默认名
|
|
1755
|
+
/// ---
|
|
1462
1756
|
pub(super) fn state_session_name_from_spec(
|
|
1463
1757
|
state: &serde_json::Value,
|
|
1464
1758
|
spec: &YamlValue,
|
|
@@ -1483,6 +1777,14 @@ pub(super) fn state_session_name_from_spec(
|
|
|
1483
1777
|
.unwrap_or_else(|| SessionName::new("team-agent"))
|
|
1484
1778
|
}
|
|
1485
1779
|
|
|
1780
|
+
/// ---
|
|
1781
|
+
/// purpose: 把席位标记为已停并清掉 window 与 pane_id 字段(pane_pid 等其余字段保留)
|
|
1782
|
+
/// params:
|
|
1783
|
+
/// state: 就地改写,非对象时先重置成空对象
|
|
1784
|
+
/// spec_agent: 提供 provider 等最小投影
|
|
1785
|
+
/// returns: 成功返回空值
|
|
1786
|
+
/// errors: state 结构不是对象时返回 StatePersist
|
|
1787
|
+
/// ---
|
|
1486
1788
|
pub(super) fn mark_agent_stopped(
|
|
1487
1789
|
state: &mut serde_json::Value,
|
|
1488
1790
|
agent_id: &AgentId,
|
|
@@ -1532,6 +1834,13 @@ pub(super) fn mark_agent_stopped(
|
|
|
1532
1834
|
Ok(())
|
|
1533
1835
|
}
|
|
1534
1836
|
|
|
1837
|
+
/// ---
|
|
1838
|
+
/// purpose: 窗口本就存在而未新起进程时,把席位标记为运行中
|
|
1839
|
+
/// params:
|
|
1840
|
+
/// pane: 探到的现有 pane,用于写 pane id 与进程号
|
|
1841
|
+
/// returns: 成功返回空值
|
|
1842
|
+
/// errors: state 结构不是对象时返回 StatePersist
|
|
1843
|
+
/// ---
|
|
1535
1844
|
pub(super) fn mark_agent_running_noop(
|
|
1536
1845
|
state: &mut serde_json::Value,
|
|
1537
1846
|
agent_id: &AgentId,
|
|
@@ -1590,6 +1899,11 @@ pub(super) fn mark_agent_running_noop(
|
|
|
1590
1899
|
Ok(())
|
|
1591
1900
|
}
|
|
1592
1901
|
|
|
1902
|
+
/// ---
|
|
1903
|
+
/// purpose: 写一条起席无操作事件
|
|
1904
|
+
/// returns: 成功返回空值
|
|
1905
|
+
/// errors: 事件写入失败时返回 StatePersist
|
|
1906
|
+
/// ---
|
|
1593
1907
|
pub(super) fn write_start_agent_noop_event(
|
|
1594
1908
|
workspace: &Path,
|
|
1595
1909
|
agent_id: &AgentId,
|
|
@@ -1609,6 +1923,10 @@ pub(super) fn write_start_agent_noop_event(
|
|
|
1609
1923
|
Ok(())
|
|
1610
1924
|
}
|
|
1611
1925
|
|
|
1926
|
+
/// ---
|
|
1927
|
+
/// purpose: 判断某 session 里是否存在该窗口
|
|
1928
|
+
/// returns: 明确列出该窗口才为 true;列窗口出错或 panic 时为 false
|
|
1929
|
+
/// ---
|
|
1612
1930
|
pub(super) fn window_exists(
|
|
1613
1931
|
transport: &dyn crate::transport::Transport,
|
|
1614
1932
|
session_name: &SessionName,
|
|
@@ -1622,6 +1940,11 @@ pub(super) fn window_exists(
|
|
|
1622
1940
|
}
|
|
1623
1941
|
}
|
|
1624
1942
|
|
|
1943
|
+
/// ---
|
|
1944
|
+
/// purpose: 在 state 里把该席位的显示标记为已关
|
|
1945
|
+
/// params:
|
|
1946
|
+
/// state: 就地改写;只有 ghostty 工作区后端会被改写状态与标题,其余后端不动
|
|
1947
|
+
/// ---
|
|
1625
1948
|
pub(super) fn close_agent_display(state: &mut serde_json::Value, agent_id: &AgentId) {
|
|
1626
1949
|
let Some(display) = state
|
|
1627
1950
|
.get_mut("agents")
|
|
@@ -1649,6 +1972,13 @@ pub(super) fn close_agent_display(state: &mut serde_json::Value, agent_id: &Agen
|
|
|
1649
1972
|
}
|
|
1650
1973
|
}
|
|
1651
1974
|
|
|
1975
|
+
/// ---
|
|
1976
|
+
/// purpose: 丢弃该席位的会话捕获字段并标记为已停
|
|
1977
|
+
/// params:
|
|
1978
|
+
/// state: 就地改写;只删会话捕获相关字段,工作目录等状态字段保留
|
|
1979
|
+
/// returns: 成功返回空值
|
|
1980
|
+
/// errors: 席位不存在返回 RequirementUnmet,席位行不是对象返回 StatePersist
|
|
1981
|
+
/// ---
|
|
1652
1982
|
pub(super) fn discard_agent_session_fields(
|
|
1653
1983
|
state: &mut serde_json::Value,
|
|
1654
1984
|
agent_id: &AgentId,
|
|
@@ -1696,6 +2026,10 @@ pub(super) fn discard_agent_session_fields(
|
|
|
1696
2026
|
Ok(())
|
|
1697
2027
|
}
|
|
1698
2028
|
|
|
2029
|
+
/// ---
|
|
2030
|
+
/// purpose: 判断该席位是否在运行
|
|
2031
|
+
/// returns: 状态为 running 或 busy 直接为真;其余状态都退到按 session 与窗口存在性判定
|
|
2032
|
+
/// ---
|
|
1699
2033
|
pub(super) fn agent_is_running(
|
|
1700
2034
|
state: &serde_json::Value,
|
|
1701
2035
|
agent_id: &AgentId,
|
|
@@ -1727,6 +2061,10 @@ pub(super) fn agent_is_running(
|
|
|
1727
2061
|
window_exists(transport, &session_name, window)
|
|
1728
2062
|
}
|
|
1729
2063
|
|
|
2064
|
+
/// ---
|
|
2065
|
+
/// purpose: 判断该席位是不是动态生成的
|
|
2066
|
+
/// returns: state 里记了动态角色文件,或 spec 里标了来源席位时为 true
|
|
2067
|
+
/// ---
|
|
1730
2068
|
pub(super) fn is_dynamic_agent(
|
|
1731
2069
|
state: &serde_json::Value,
|
|
1732
2070
|
spec_agent: &YamlValue,
|
|
@@ -1745,6 +2083,13 @@ pub(super) fn is_dynamic_agent(
|
|
|
1745
2083
|
.is_some_and(|s| !s.is_empty())
|
|
1746
2084
|
}
|
|
1747
2085
|
|
|
2086
|
+
/// ---
|
|
2087
|
+
/// purpose: 在 spawn 之前预判 tmux 的起法,供审计事件记录
|
|
2088
|
+
/// params:
|
|
2089
|
+
/// layout_placement: 有布局位置时按是否起新窗口区分
|
|
2090
|
+
/// into_existing_session: 目标 session 已存在
|
|
2091
|
+
/// returns: new-session、new-window 或 split-window
|
|
2092
|
+
/// ---
|
|
1748
2093
|
/// 0.4.6 Stage 2: predict the tmux start mode BEFORE the spawn call, so
|
|
1749
2094
|
/// the `provider.worker.spawn_argv` event can record what the spawn will
|
|
1750
2095
|
/// actually do. Same logic as `tmux_start_mode_for_spawn` in agent.rs
|
|
@@ -1771,6 +2116,10 @@ pub(super) fn predict_tmux_start_mode(
|
|
|
1771
2116
|
}
|
|
1772
2117
|
}
|
|
1773
2118
|
|
|
2119
|
+
/// ---
|
|
2120
|
+
/// purpose: 从盘上读该席位当前的 spawn 世代号
|
|
2121
|
+
/// returns: 读到的值;读不出 state 或字段缺失时为 0
|
|
2122
|
+
/// ---
|
|
1774
2123
|
/// 0.4.6 Stage 2: read state.agents[agent_id].spawn_epoch from disk to
|
|
1775
2124
|
/// stamp the spawn_argv event with the current cohort identifier. Returns
|
|
1776
2125
|
/// 0 if the agent row / field is missing.
|
|
@@ -1786,6 +2135,12 @@ pub(super) fn state_spawn_epoch_for_agent(workspace: &Path, agent_id: &AgentId)
|
|
|
1786
2135
|
.unwrap_or(0)
|
|
1787
2136
|
}
|
|
1788
2137
|
|
|
2138
|
+
/// ---
|
|
2139
|
+
/// purpose: 新起进程后清掉该席位的活动观测字段
|
|
2140
|
+
/// params:
|
|
2141
|
+
/// agent: 就地删除观测字段;生命周期与拓扑字段一概不动
|
|
2142
|
+
/// returns: 清完之后观测缺失表示未知,不等于空闲
|
|
2143
|
+
/// ---
|
|
1789
2144
|
/// 0.5.32 (`.team/artifacts/restart-resumed-stale-activity-locate.md` §5):
|
|
1790
2145
|
/// clear the per-agent turn/activity observation set on a successful new
|
|
1791
2146
|
/// worker process cohort. Called from `mark_agent_started` /
|