@team-agent/installer 0.5.66 → 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 +1 -0
- package/crates/team-agent/src/cli/adapters.rs +5 -0
- 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/missing_subcommands.rs +129 -19
- 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/communication_mode/mod.rs +6 -4
- package/crates/team-agent/src/compiler.rs +59 -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 +920 -63
- 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 -12
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +62 -0
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +59 -0
- 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 -456
- 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 +62 -2
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +139 -0
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -0
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
- package/crates/team-agent/src/lifecycle/launch.rs +99 -29
- 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 +124 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +369 -11
- 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 +106 -0
- 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/agent_ops.rs +2 -3
- 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 +150 -157
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/core.rs +4 -0
- 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/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/lane_ops.rs +63 -38
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +3 -2
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +16 -8
- 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 +20 -19
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +97 -61
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +2 -0
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +40 -7
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +59 -1
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
- package/crates/team-agent/src/lifecycle/tests.rs +23 -13
- package/crates/team-agent/src/lifecycle/types.rs +61 -0
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +85 -11
- 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/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/os_probe.rs +73 -2
- package/crates/team-agent/src/provider/adapter.rs +231 -4
- 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 +46 -2
- 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 +11 -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 +1064 -75
- package/crates/team-agent/src/transport/tests/wire.rs +28 -2
- package/crates/team-agent/src/transport.rs +248 -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/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -488
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
|
@@ -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,
|
|
@@ -214,8 +290,7 @@ pub(super) fn spawn_agent_window(
|
|
|
214
290
|
safety
|
|
215
291
|
} else {
|
|
216
292
|
detected_safety = crate::lifecycle::launch::effective_runtime_config_for_worker_spawn_json(
|
|
217
|
-
agent,
|
|
218
|
-
provider,
|
|
293
|
+
agent, provider,
|
|
219
294
|
)?;
|
|
220
295
|
&detected_safety
|
|
221
296
|
};
|
|
@@ -327,6 +402,7 @@ pub(super) fn spawn_agent_window(
|
|
|
327
402
|
workspace,
|
|
328
403
|
agent_id.as_str(),
|
|
329
404
|
team_id.as_deref(),
|
|
405
|
+
Some(crate::lifecycle::launch::auth_mode_env_value(auth_mode)),
|
|
330
406
|
);
|
|
331
407
|
crate::lifecycle::launch::apply_profile_launch_env(&mut env, &profile_launch);
|
|
332
408
|
crate::lifecycle::launch::apply_mcp_auto_approval_env(&mut env, safety);
|
|
@@ -338,6 +414,27 @@ pub(super) fn spawn_agent_window(
|
|
|
338
414
|
&mut env,
|
|
339
415
|
)?;
|
|
340
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
|
+
}
|
|
341
438
|
// 0.3.28 Step 3: per Python parity, worker spawn cwd is ALWAYS `workspace`.
|
|
342
439
|
// The persisted-state `agent.spawn_cwd` override is ignored (it was a
|
|
343
440
|
// Rust-only extension that drifted to `.team/runtime/<team_key>/` after
|
|
@@ -560,6 +657,10 @@ fn is_structural_startup_prompt_error(error: &str) -> bool {
|
|
|
560
657
|
.any(|needle| lower.contains(needle))
|
|
561
658
|
}
|
|
562
659
|
|
|
660
|
+
/// ---
|
|
661
|
+
/// purpose: 用 spec 里的同名 agent 补回 state 席位行缺的命令上下文字段
|
|
662
|
+
/// returns: 合并后的席位行;spec 读不到或找不到该 agent 时原样返回
|
|
663
|
+
/// ---
|
|
563
664
|
pub(super) fn rehydrate_agent_command_context_from_spec(
|
|
564
665
|
spec_workspace: &Path,
|
|
565
666
|
agent_id: &AgentId,
|
|
@@ -652,6 +753,13 @@ fn window_present_in_live(
|
|
|
652
753
|
false
|
|
653
754
|
}
|
|
654
755
|
|
|
756
|
+
/// ---
|
|
757
|
+
/// purpose: 起该 workspace 的 coordinator
|
|
758
|
+
/// params:
|
|
759
|
+
/// team_key: 传给 coordinator 的团队键
|
|
760
|
+
/// returns: 启动摘要
|
|
761
|
+
/// errors: 启动失败时返回 StatePersist
|
|
762
|
+
/// ---
|
|
655
763
|
pub(super) fn start_coordinator_for_workspace(
|
|
656
764
|
workspace: &Path,
|
|
657
765
|
team_key: Option<&str>,
|
|
@@ -662,6 +770,13 @@ pub(super) fn start_coordinator_for_workspace(
|
|
|
662
770
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))
|
|
663
771
|
}
|
|
664
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
|
+
/// ---
|
|
665
780
|
/// State-aware tmux backend resolver. Reads the team's persisted
|
|
666
781
|
/// `tmux_endpoint` (set at `team-agent launch` time and shared across
|
|
667
782
|
/// restart/add-agent/fork-agent) and constructs a TmuxBackend on THAT socket,
|
|
@@ -736,6 +851,11 @@ pub(crate) fn lifecycle_worker_tmux_backend_for_selected_state(
|
|
|
736
851
|
.unwrap_or_else(|| crate::tmux_backend::TmuxBackend::for_workspace(run_workspace)))
|
|
737
852
|
}
|
|
738
853
|
|
|
854
|
+
/// ---
|
|
855
|
+
/// purpose: 与上面同样的团队选择语义,但返回工厂解析出的通用 transport
|
|
856
|
+
/// returns: 已解析的 transport,含后端种类、来源与提示
|
|
857
|
+
/// errors: 团队选择失败或工厂拒绝时返回 TeamSelect,读 state 失败返回 StatePersist
|
|
858
|
+
/// ---
|
|
739
859
|
/// 0.5.x Phase 1d Batch 1: new generic-typed lifecycle resolver.
|
|
740
860
|
///
|
|
741
861
|
/// Same team-selection semantics as the legacy tmux-typed variant, but
|
|
@@ -774,6 +894,11 @@ pub(crate) fn lifecycle_worker_transport_for_selected_state(
|
|
|
774
894
|
.map_err(|e| LifecycleError::TeamSelect(e.to_string()))
|
|
775
895
|
}
|
|
776
896
|
|
|
897
|
+
/// ---
|
|
898
|
+
/// purpose: 由已取到的 state 解析 tmux 后端与它的来源
|
|
899
|
+
/// returns: 后端与 endpoint 来源
|
|
900
|
+
/// errors: state 声明后端是 conpty 时返回 TeamSelect
|
|
901
|
+
/// ---
|
|
777
902
|
pub(super) fn lifecycle_worker_tmux_backend_selection_for_state(
|
|
778
903
|
run_workspace: &Path,
|
|
779
904
|
state: &serde_json::Value,
|
|
@@ -795,6 +920,10 @@ pub(super) fn lifecycle_worker_tmux_backend_selection_for_state(
|
|
|
795
920
|
)
|
|
796
921
|
}
|
|
797
922
|
|
|
923
|
+
/// ---
|
|
924
|
+
/// purpose: 由已取到的 state 直接给出 tmux 后端
|
|
925
|
+
/// returns: 绑定到该 state endpoint 的后端,缺失时按 workspace 派生
|
|
926
|
+
/// ---
|
|
798
927
|
pub(super) fn lifecycle_worker_tmux_backend_for_state(
|
|
799
928
|
run_workspace: &Path,
|
|
800
929
|
state: &serde_json::Value,
|
|
@@ -803,6 +932,14 @@ pub(super) fn lifecycle_worker_tmux_backend_for_state(
|
|
|
803
932
|
.backend
|
|
804
933
|
}
|
|
805
934
|
|
|
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
|
+
/// ---
|
|
806
943
|
pub(super) fn save_restart_projected_state(
|
|
807
944
|
workspace: &Path,
|
|
808
945
|
state: &mut serde_json::Value,
|
|
@@ -818,6 +955,14 @@ pub(super) fn save_restart_projected_state(
|
|
|
818
955
|
)
|
|
819
956
|
}
|
|
820
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
|
+
/// ---
|
|
821
966
|
pub(super) fn save_restart_projected_state_with_capture_backfill_skip(
|
|
822
967
|
workspace: &Path,
|
|
823
968
|
state: &mut serde_json::Value,
|
|
@@ -826,17 +971,22 @@ pub(super) fn save_restart_projected_state_with_capture_backfill_skip(
|
|
|
826
971
|
topology_authority_agent_ids: &[&str],
|
|
827
972
|
) -> Result<(), LifecycleError> {
|
|
828
973
|
sync_restart_team_projections(state, team_key);
|
|
829
|
-
crate::state::repository::StateRepository::new(workspace)
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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()))
|
|
838
984
|
}
|
|
839
985
|
|
|
986
|
+
/// ---
|
|
987
|
+
/// purpose: 定出本次投影使用的团队键
|
|
988
|
+
/// returns: 显式 team 优先,其次 state 里的活跃键,最后由 state 推算
|
|
989
|
+
/// ---
|
|
840
990
|
pub(super) fn restart_projection_team_key(state: &serde_json::Value, team: Option<&str>) -> String {
|
|
841
991
|
team.filter(|key| !key.is_empty())
|
|
842
992
|
.map(str::to_string)
|
|
@@ -850,6 +1000,12 @@ pub(super) fn restart_projection_team_key(state: &serde_json::Value, team: Optio
|
|
|
850
1000
|
.unwrap_or_else(|| crate::state::projection::team_state_key(state))
|
|
851
1001
|
}
|
|
852
1002
|
|
|
1003
|
+
/// ---
|
|
1004
|
+
/// purpose: 把当前顶层状态压实后写回 teams 表
|
|
1005
|
+
/// params:
|
|
1006
|
+
/// state: 就地改写;显式团队键允许覆盖或新建,别名键只在盘上已有且身份不冲突时才写
|
|
1007
|
+
/// returns: teams 表缺失或为空时不动
|
|
1008
|
+
/// ---
|
|
853
1009
|
pub(super) fn sync_restart_team_projections(state: &mut serde_json::Value, team_key: &str) {
|
|
854
1010
|
let Some(teams) = state.get("teams").and_then(serde_json::Value::as_object) else {
|
|
855
1011
|
return;
|
|
@@ -930,6 +1086,10 @@ fn json_team_identity_matches(existing: &serde_json::Value, compact: &serde_json
|
|
|
930
1086
|
true
|
|
931
1087
|
}
|
|
932
1088
|
|
|
1089
|
+
/// ---
|
|
1090
|
+
/// purpose: 取 state 里的 session 名
|
|
1091
|
+
/// returns: 非空的 session_name,缺失时退到默认名
|
|
1092
|
+
/// ---
|
|
933
1093
|
pub(super) fn state_session_name(state: &serde_json::Value) -> SessionName {
|
|
934
1094
|
state
|
|
935
1095
|
.get("session_name")
|
|
@@ -939,6 +1099,10 @@ pub(super) fn state_session_name(state: &serde_json::Value) -> SessionName {
|
|
|
939
1099
|
.unwrap_or_else(|| SessionName::new("team-agent"))
|
|
940
1100
|
}
|
|
941
1101
|
|
|
1102
|
+
/// ---
|
|
1103
|
+
/// purpose: 判断 state 里是否记了非空 session 名
|
|
1104
|
+
/// returns: 记了则 true
|
|
1105
|
+
/// ---
|
|
942
1106
|
pub(super) fn session_name_present(state: &serde_json::Value) -> bool {
|
|
943
1107
|
state
|
|
944
1108
|
.get("session_name")
|
|
@@ -947,6 +1111,12 @@ pub(super) fn session_name_present(state: &serde_json::Value) -> bool {
|
|
|
947
1111
|
.unwrap_or(false)
|
|
948
1112
|
}
|
|
949
1113
|
|
|
1114
|
+
/// ---
|
|
1115
|
+
/// purpose: 探测 session 是否存活
|
|
1116
|
+
/// params:
|
|
1117
|
+
/// default: 探测本身 panic 时采用的兜底判定
|
|
1118
|
+
/// returns: transport 明确回答时用它;返回错误时判为不存活;探测 panic 时用兜底值
|
|
1119
|
+
/// ---
|
|
950
1120
|
pub(super) fn session_live_or_default(
|
|
951
1121
|
transport: &dyn crate::transport::Transport,
|
|
952
1122
|
session_name: &SessionName,
|
|
@@ -961,6 +1131,10 @@ pub(super) fn session_live_or_default(
|
|
|
961
1131
|
}
|
|
962
1132
|
}
|
|
963
1133
|
|
|
1134
|
+
/// ---
|
|
1135
|
+
/// purpose: 取席位行的 provider
|
|
1136
|
+
/// returns: 解析成功用它,缺失或不认识时退到 codex
|
|
1137
|
+
/// ---
|
|
964
1138
|
pub(super) fn agent_provider(agent: &serde_json::Value) -> Provider {
|
|
965
1139
|
agent
|
|
966
1140
|
.get("provider")
|
|
@@ -969,6 +1143,10 @@ pub(super) fn agent_provider(agent: &serde_json::Value) -> Provider {
|
|
|
969
1143
|
.unwrap_or(Provider::Codex)
|
|
970
1144
|
}
|
|
971
1145
|
|
|
1146
|
+
/// ---
|
|
1147
|
+
/// purpose: 取席位行的 auth_mode
|
|
1148
|
+
/// returns: 解析成功用它,缺失或不认识时退到 subscription
|
|
1149
|
+
/// ---
|
|
972
1150
|
pub(super) fn agent_auth_mode(agent: &serde_json::Value) -> AuthMode {
|
|
973
1151
|
agent
|
|
974
1152
|
.get("auth_mode")
|
|
@@ -977,6 +1155,10 @@ pub(super) fn agent_auth_mode(agent: &serde_json::Value) -> AuthMode {
|
|
|
977
1155
|
.unwrap_or(AuthMode::Subscription)
|
|
978
1156
|
}
|
|
979
1157
|
|
|
1158
|
+
/// ---
|
|
1159
|
+
/// purpose: 取席位行记录的会话 id
|
|
1160
|
+
/// returns: 非空时返回,否则 None
|
|
1161
|
+
/// ---
|
|
980
1162
|
pub(super) fn agent_session_id(agent: &serde_json::Value) -> Option<SessionId> {
|
|
981
1163
|
agent
|
|
982
1164
|
.get("session_id")
|
|
@@ -985,6 +1167,10 @@ pub(super) fn agent_session_id(agent: &serde_json::Value) -> Option<SessionId> {
|
|
|
985
1167
|
.map(SessionId::new)
|
|
986
1168
|
}
|
|
987
1169
|
|
|
1170
|
+
/// ---
|
|
1171
|
+
/// purpose: 取席位行记录的 rollout 路径
|
|
1172
|
+
/// returns: 非空时返回,否则 None
|
|
1173
|
+
/// ---
|
|
988
1174
|
pub(super) fn agent_rollout_path(agent: &serde_json::Value) -> Option<RolloutPath> {
|
|
989
1175
|
agent
|
|
990
1176
|
.get("rollout_path")
|
|
@@ -993,6 +1179,10 @@ pub(super) fn agent_rollout_path(agent: &serde_json::Value) -> Option<RolloutPat
|
|
|
993
1179
|
.map(RolloutPath::new)
|
|
994
1180
|
}
|
|
995
1181
|
|
|
1182
|
+
/// ---
|
|
1183
|
+
/// purpose: 判断该席位的 resume 依据是否存在
|
|
1184
|
+
/// returns: 探测结果里的存在位
|
|
1185
|
+
/// ---
|
|
996
1186
|
pub(super) fn resume_backing_exists_for_agent(
|
|
997
1187
|
workspace: &Path,
|
|
998
1188
|
agent_id: &AgentId,
|
|
@@ -1032,6 +1222,13 @@ pub(crate) struct SessionIdentityProbeResult {
|
|
|
1032
1222
|
pub rollout_path: Option<PathBuf>,
|
|
1033
1223
|
}
|
|
1034
1224
|
|
|
1225
|
+
/// ---
|
|
1226
|
+
/// purpose: 探测 rollout 文件里嵌的席位身份是否与本席位一致
|
|
1227
|
+
/// params:
|
|
1228
|
+
/// _provider: 未参与判定
|
|
1229
|
+
/// rollout_path: 没有路径时三项都返回未知
|
|
1230
|
+
/// returns: 一致性判定、读到的嵌入席位名与实际探测路径;读不出嵌入身份时一致性为未知
|
|
1231
|
+
/// ---
|
|
1035
1232
|
pub(crate) fn session_identity_probe_for_agent(
|
|
1036
1233
|
agent_id: &AgentId,
|
|
1037
1234
|
_provider: Provider,
|
|
@@ -1056,6 +1253,10 @@ pub(crate) fn session_identity_probe_for_agent(
|
|
|
1056
1253
|
}
|
|
1057
1254
|
}
|
|
1058
1255
|
|
|
1256
|
+
/// ---
|
|
1257
|
+
/// purpose: 按 provider 分支探测 resume 依据,并记录所有查过的路径
|
|
1258
|
+
/// returns: 存在位与查过的路径列表;持久化的 rollout 路径即使不存在也记进列表
|
|
1259
|
+
/// ---
|
|
1059
1260
|
pub(super) fn resume_backing_probe_for_agent(
|
|
1060
1261
|
workspace: &Path,
|
|
1061
1262
|
agent_id: &AgentId,
|
|
@@ -1107,6 +1308,47 @@ pub(super) fn resume_backing_probe_for_agent(
|
|
|
1107
1308
|
}
|
|
1108
1309
|
copilot_session_store_has_session(session_id.as_str())
|
|
1109
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
|
+
}
|
|
1110
1352
|
Provider::GeminiCli | Provider::Fake => false,
|
|
1111
1353
|
};
|
|
1112
1354
|
|
|
@@ -1121,10 +1363,20 @@ pub(super) fn resume_backing_probe_for_agent(
|
|
|
1121
1363
|
}
|
|
1122
1364
|
}
|
|
1123
1365
|
|
|
1366
|
+
/// ---
|
|
1367
|
+
/// purpose: 判断该 provider 是否支持 resume
|
|
1368
|
+
/// returns: 由 provider 适配器的能力位给出
|
|
1369
|
+
/// contract_id: lifecycle.common.provider_supports_resume
|
|
1370
|
+
/// ---
|
|
1124
1371
|
pub(super) fn provider_supports_resume(provider: Provider) -> bool {
|
|
1125
1372
|
crate::provider::get_adapter(provider).caps().resume
|
|
1126
1373
|
}
|
|
1127
1374
|
|
|
1375
|
+
/// ---
|
|
1376
|
+
/// purpose: 由 provider wire 名判断是否支持 resume
|
|
1377
|
+
/// returns: 名字不认识时为 false
|
|
1378
|
+
/// contract_id: lifecycle.common.provider_supports_resume
|
|
1379
|
+
/// ---
|
|
1128
1380
|
pub(super) fn provider_wire_supports_resume(provider: &str) -> bool {
|
|
1129
1381
|
parse_provider(provider)
|
|
1130
1382
|
.map(provider_supports_resume)
|
|
@@ -1300,6 +1552,13 @@ fn copilot_session_store_has_session(session_id: &str) -> bool {
|
|
|
1300
1552
|
.is_ok()
|
|
1301
1553
|
}
|
|
1302
1554
|
|
|
1555
|
+
/// ---
|
|
1556
|
+
/// purpose: 对缺会话的席位做一次捕获尝试
|
|
1557
|
+
/// params:
|
|
1558
|
+
/// state: 就地改写
|
|
1559
|
+
/// returns: 本次是否改动了 state
|
|
1560
|
+
/// errors: 捕获过程出错时返回 Provider
|
|
1561
|
+
/// ---
|
|
1303
1562
|
pub(crate) fn refresh_missing_provider_sessions(
|
|
1304
1563
|
state: &mut serde_json::Value,
|
|
1305
1564
|
) -> Result<bool, LifecycleError> {
|
|
@@ -1313,6 +1572,15 @@ pub(crate) fn refresh_missing_provider_sessions(
|
|
|
1313
1572
|
.map_err(|e| LifecycleError::Provider(e.to_string()))
|
|
1314
1573
|
}
|
|
1315
1574
|
|
|
1575
|
+
/// ---
|
|
1576
|
+
/// purpose: 有界等待缺会话席位收敛,并逐轮写进度事件
|
|
1577
|
+
/// params:
|
|
1578
|
+
/// deadline: 等待上限
|
|
1579
|
+
/// poll_interval: 轮询间隔
|
|
1580
|
+
/// allow_fresh: 只写进事件载荷,不改变收敛判定
|
|
1581
|
+
/// returns: 收敛结论
|
|
1582
|
+
/// errors: 收敛过程出错时返回 StatePersist
|
|
1583
|
+
/// ---
|
|
1316
1584
|
pub(crate) fn converge_missing_provider_sessions(
|
|
1317
1585
|
state: &mut serde_json::Value,
|
|
1318
1586
|
deadline: std::time::Duration,
|
|
@@ -1358,6 +1626,10 @@ fn write_session_convergence_progress_event(
|
|
|
1358
1626
|
.map_err(|e| e.to_string())
|
|
1359
1627
|
}
|
|
1360
1628
|
|
|
1629
|
+
/// ---
|
|
1630
|
+
/// purpose: 列出必须等到会话收敛才能 restart 的席位
|
|
1631
|
+
/// returns: 排序后的席位 id;只保留无会话、状态为 running 且确有待保留上下文的席位,从未捕获过的席位不进入
|
|
1632
|
+
/// ---
|
|
1361
1633
|
pub(crate) fn restart_required_missing_session_agent_ids(state: &serde_json::Value) -> Vec<String> {
|
|
1362
1634
|
let mut missing = crate::session_capture::incomplete_resumable_agent_ids(state)
|
|
1363
1635
|
.into_iter()
|
|
@@ -1397,6 +1669,10 @@ pub(crate) fn restart_required_missing_session_agent_ids(state: &serde_json::Val
|
|
|
1397
1669
|
missing.sort();
|
|
1398
1670
|
missing
|
|
1399
1671
|
}
|
|
1672
|
+
/// ---
|
|
1673
|
+
/// purpose: 取该席位的窗口名
|
|
1674
|
+
/// returns: 席位行里的非空 window,缺失时用席位 id
|
|
1675
|
+
/// ---
|
|
1400
1676
|
pub(super) fn agent_window(agent: &serde_json::Value, agent_id: &AgentId) -> String {
|
|
1401
1677
|
agent
|
|
1402
1678
|
.get("window")
|
|
@@ -1408,6 +1684,10 @@ pub(super) fn agent_window(agent: &serde_json::Value, agent_id: &AgentId) -> Str
|
|
|
1408
1684
|
|
|
1409
1685
|
pub(super) use crate::provider::wire::{parse_provider, provider_wire};
|
|
1410
1686
|
|
|
1687
|
+
/// ---
|
|
1688
|
+
/// purpose: 把 auth_mode 字符串解析成枚举
|
|
1689
|
+
/// returns: 只认三种取值,未知返回 None
|
|
1690
|
+
/// ---
|
|
1411
1691
|
pub(super) fn parse_auth_mode(raw: &str) -> Option<AuthMode> {
|
|
1412
1692
|
match raw {
|
|
1413
1693
|
"subscription" => Some(AuthMode::Subscription),
|
|
@@ -1417,6 +1697,11 @@ pub(super) fn parse_auth_mode(raw: &str) -> Option<AuthMode> {
|
|
|
1417
1697
|
}
|
|
1418
1698
|
}
|
|
1419
1699
|
|
|
1700
|
+
/// ---
|
|
1701
|
+
/// purpose: 从给定目录读出 team spec
|
|
1702
|
+
/// returns: 解析后的 YAML
|
|
1703
|
+
/// errors: 文件不存在返回 TeamSelect,读文件或解析失败返回 Compile
|
|
1704
|
+
/// ---
|
|
1420
1705
|
pub(super) fn load_team_spec(workspace: &Path) -> Result<YamlValue, LifecycleError> {
|
|
1421
1706
|
let spec_path = workspace.join("team.spec.yaml");
|
|
1422
1707
|
if !spec_path.exists() {
|
|
@@ -1430,6 +1715,10 @@ pub(super) fn load_team_spec(workspace: &Path) -> Result<YamlValue, LifecycleErr
|
|
|
1430
1715
|
yaml::loads(&text).map_err(|e| LifecycleError::Compile(e.to_string()))
|
|
1431
1716
|
}
|
|
1432
1717
|
|
|
1718
|
+
/// ---
|
|
1719
|
+
/// purpose: 在 spec 的 agents 列表里找该席位
|
|
1720
|
+
/// returns: 命中的节点;该 id 其实是 leader 时返回 None
|
|
1721
|
+
/// ---
|
|
1433
1722
|
pub(super) fn find_spec_agent<'a>(
|
|
1434
1723
|
spec: &'a YamlValue,
|
|
1435
1724
|
agent_id: &AgentId,
|
|
@@ -1452,10 +1741,18 @@ pub(super) fn find_spec_agent<'a>(
|
|
|
1452
1741
|
})
|
|
1453
1742
|
}
|
|
1454
1743
|
|
|
1744
|
+
/// ---
|
|
1745
|
+
/// purpose: 构造未知席位的错误
|
|
1746
|
+
/// returns: 带席位 id 的 RequirementUnmet
|
|
1747
|
+
/// ---
|
|
1455
1748
|
pub(super) fn unknown_worker(agent_id: &AgentId) -> LifecycleError {
|
|
1456
1749
|
LifecycleError::RequirementUnmet(format!("unknown worker agent id: {agent_id}"))
|
|
1457
1750
|
}
|
|
1458
1751
|
|
|
1752
|
+
/// ---
|
|
1753
|
+
/// purpose: 定出 session 名,state 缺失时回落到 spec
|
|
1754
|
+
/// returns: 依次取 state 的 session_name、spec 的 runtime.session_name、由 team 名派生,最后用默认名
|
|
1755
|
+
/// ---
|
|
1459
1756
|
pub(super) fn state_session_name_from_spec(
|
|
1460
1757
|
state: &serde_json::Value,
|
|
1461
1758
|
spec: &YamlValue,
|
|
@@ -1480,6 +1777,14 @@ pub(super) fn state_session_name_from_spec(
|
|
|
1480
1777
|
.unwrap_or_else(|| SessionName::new("team-agent"))
|
|
1481
1778
|
}
|
|
1482
1779
|
|
|
1780
|
+
/// ---
|
|
1781
|
+
/// purpose: 把席位标记为已停并清掉 window 与 pane_id 字段(pane_pid 等其余字段保留)
|
|
1782
|
+
/// params:
|
|
1783
|
+
/// state: 就地改写,非对象时先重置成空对象
|
|
1784
|
+
/// spec_agent: 提供 provider 等最小投影
|
|
1785
|
+
/// returns: 成功返回空值
|
|
1786
|
+
/// errors: state 结构不是对象时返回 StatePersist
|
|
1787
|
+
/// ---
|
|
1483
1788
|
pub(super) fn mark_agent_stopped(
|
|
1484
1789
|
state: &mut serde_json::Value,
|
|
1485
1790
|
agent_id: &AgentId,
|
|
@@ -1529,6 +1834,13 @@ pub(super) fn mark_agent_stopped(
|
|
|
1529
1834
|
Ok(())
|
|
1530
1835
|
}
|
|
1531
1836
|
|
|
1837
|
+
/// ---
|
|
1838
|
+
/// purpose: 窗口本就存在而未新起进程时,把席位标记为运行中
|
|
1839
|
+
/// params:
|
|
1840
|
+
/// pane: 探到的现有 pane,用于写 pane id 与进程号
|
|
1841
|
+
/// returns: 成功返回空值
|
|
1842
|
+
/// errors: state 结构不是对象时返回 StatePersist
|
|
1843
|
+
/// ---
|
|
1532
1844
|
pub(super) fn mark_agent_running_noop(
|
|
1533
1845
|
state: &mut serde_json::Value,
|
|
1534
1846
|
agent_id: &AgentId,
|
|
@@ -1587,6 +1899,11 @@ pub(super) fn mark_agent_running_noop(
|
|
|
1587
1899
|
Ok(())
|
|
1588
1900
|
}
|
|
1589
1901
|
|
|
1902
|
+
/// ---
|
|
1903
|
+
/// purpose: 写一条起席无操作事件
|
|
1904
|
+
/// returns: 成功返回空值
|
|
1905
|
+
/// errors: 事件写入失败时返回 StatePersist
|
|
1906
|
+
/// ---
|
|
1590
1907
|
pub(super) fn write_start_agent_noop_event(
|
|
1591
1908
|
workspace: &Path,
|
|
1592
1909
|
agent_id: &AgentId,
|
|
@@ -1606,6 +1923,10 @@ pub(super) fn write_start_agent_noop_event(
|
|
|
1606
1923
|
Ok(())
|
|
1607
1924
|
}
|
|
1608
1925
|
|
|
1926
|
+
/// ---
|
|
1927
|
+
/// purpose: 判断某 session 里是否存在该窗口
|
|
1928
|
+
/// returns: 明确列出该窗口才为 true;列窗口出错或 panic 时为 false
|
|
1929
|
+
/// ---
|
|
1609
1930
|
pub(super) fn window_exists(
|
|
1610
1931
|
transport: &dyn crate::transport::Transport,
|
|
1611
1932
|
session_name: &SessionName,
|
|
@@ -1619,6 +1940,11 @@ pub(super) fn window_exists(
|
|
|
1619
1940
|
}
|
|
1620
1941
|
}
|
|
1621
1942
|
|
|
1943
|
+
/// ---
|
|
1944
|
+
/// purpose: 在 state 里把该席位的显示标记为已关
|
|
1945
|
+
/// params:
|
|
1946
|
+
/// state: 就地改写;只有 ghostty 工作区后端会被改写状态与标题,其余后端不动
|
|
1947
|
+
/// ---
|
|
1622
1948
|
pub(super) fn close_agent_display(state: &mut serde_json::Value, agent_id: &AgentId) {
|
|
1623
1949
|
let Some(display) = state
|
|
1624
1950
|
.get_mut("agents")
|
|
@@ -1646,6 +1972,13 @@ pub(super) fn close_agent_display(state: &mut serde_json::Value, agent_id: &Agen
|
|
|
1646
1972
|
}
|
|
1647
1973
|
}
|
|
1648
1974
|
|
|
1975
|
+
/// ---
|
|
1976
|
+
/// purpose: 丢弃该席位的会话捕获字段并标记为已停
|
|
1977
|
+
/// params:
|
|
1978
|
+
/// state: 就地改写;只删会话捕获相关字段,工作目录等状态字段保留
|
|
1979
|
+
/// returns: 成功返回空值
|
|
1980
|
+
/// errors: 席位不存在返回 RequirementUnmet,席位行不是对象返回 StatePersist
|
|
1981
|
+
/// ---
|
|
1649
1982
|
pub(super) fn discard_agent_session_fields(
|
|
1650
1983
|
state: &mut serde_json::Value,
|
|
1651
1984
|
agent_id: &AgentId,
|
|
@@ -1693,6 +2026,10 @@ pub(super) fn discard_agent_session_fields(
|
|
|
1693
2026
|
Ok(())
|
|
1694
2027
|
}
|
|
1695
2028
|
|
|
2029
|
+
/// ---
|
|
2030
|
+
/// purpose: 判断该席位是否在运行
|
|
2031
|
+
/// returns: 状态为 running 或 busy 直接为真;其余状态都退到按 session 与窗口存在性判定
|
|
2032
|
+
/// ---
|
|
1696
2033
|
pub(super) fn agent_is_running(
|
|
1697
2034
|
state: &serde_json::Value,
|
|
1698
2035
|
agent_id: &AgentId,
|
|
@@ -1724,6 +2061,10 @@ pub(super) fn agent_is_running(
|
|
|
1724
2061
|
window_exists(transport, &session_name, window)
|
|
1725
2062
|
}
|
|
1726
2063
|
|
|
2064
|
+
/// ---
|
|
2065
|
+
/// purpose: 判断该席位是不是动态生成的
|
|
2066
|
+
/// returns: state 里记了动态角色文件,或 spec 里标了来源席位时为 true
|
|
2067
|
+
/// ---
|
|
1727
2068
|
pub(super) fn is_dynamic_agent(
|
|
1728
2069
|
state: &serde_json::Value,
|
|
1729
2070
|
spec_agent: &YamlValue,
|
|
@@ -1742,6 +2083,13 @@ pub(super) fn is_dynamic_agent(
|
|
|
1742
2083
|
.is_some_and(|s| !s.is_empty())
|
|
1743
2084
|
}
|
|
1744
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
|
+
/// ---
|
|
1745
2093
|
/// 0.4.6 Stage 2: predict the tmux start mode BEFORE the spawn call, so
|
|
1746
2094
|
/// the `provider.worker.spawn_argv` event can record what the spawn will
|
|
1747
2095
|
/// actually do. Same logic as `tmux_start_mode_for_spawn` in agent.rs
|
|
@@ -1768,6 +2116,10 @@ pub(super) fn predict_tmux_start_mode(
|
|
|
1768
2116
|
}
|
|
1769
2117
|
}
|
|
1770
2118
|
|
|
2119
|
+
/// ---
|
|
2120
|
+
/// purpose: 从盘上读该席位当前的 spawn 世代号
|
|
2121
|
+
/// returns: 读到的值;读不出 state 或字段缺失时为 0
|
|
2122
|
+
/// ---
|
|
1771
2123
|
/// 0.4.6 Stage 2: read state.agents[agent_id].spawn_epoch from disk to
|
|
1772
2124
|
/// stamp the spawn_argv event with the current cohort identifier. Returns
|
|
1773
2125
|
/// 0 if the agent row / field is missing.
|
|
@@ -1783,6 +2135,12 @@ pub(super) fn state_spawn_epoch_for_agent(workspace: &Path, agent_id: &AgentId)
|
|
|
1783
2135
|
.unwrap_or(0)
|
|
1784
2136
|
}
|
|
1785
2137
|
|
|
2138
|
+
/// ---
|
|
2139
|
+
/// purpose: 新起进程后清掉该席位的活动观测字段
|
|
2140
|
+
/// params:
|
|
2141
|
+
/// agent: 就地删除观测字段;生命周期与拓扑字段一概不动
|
|
2142
|
+
/// returns: 清完之后观测缺失表示未知,不等于空闲
|
|
2143
|
+
/// ---
|
|
1786
2144
|
/// 0.5.32 (`.team/artifacts/restart-resumed-stale-activity-locate.md` §5):
|
|
1787
2145
|
/// clear the per-agent turn/activity observation set on a successful new
|
|
1788
2146
|
/// worker process cohort. Called from `mark_agent_started` /
|