@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,28 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: worker 进程的环境变量与 argv 占位符处理,以及各 provider 的角色注入落盘
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: inherited_env_with_team_overrides
|
|
6
|
+
//! what: 由当前进程环境按白名单派生 worker spawn 环境,并叠加 team 身份
|
|
7
|
+
//! - name: apply_mcp_auto_approval_env
|
|
8
|
+
//! what: 按 bypass 结论写或清 MCP 自动放行相关变量
|
|
9
|
+
//! - name: apply_copilot_instructions_overlay
|
|
10
|
+
//! what: 把角色提示词写成 per-agent 的 AGENTS.md 并指向它
|
|
11
|
+
//! - name: apply_cursor_agent_rules_overlay
|
|
12
|
+
//! what: 把角色提示词写成 workspace 内 per-agent 的 cursor rules 文件
|
|
13
|
+
//! - name: fill_spawn_placeholders_full
|
|
14
|
+
//! what: 替换 argv 里的 workspace、agent_id 与 team_id 占位符
|
|
15
|
+
//! depends:
|
|
16
|
+
//! - crate::layout::worker_env
|
|
17
|
+
//! - crate::event_log::EventLog
|
|
18
|
+
//! - crate::provider
|
|
19
|
+
//! - std::fs
|
|
20
|
+
//! boundary:
|
|
21
|
+
//! - 不写用户全局配置,角色注入一律落在 workspace 或 runtime 目录下的 per-agent 路径
|
|
22
|
+
//! - 环境里的代理与凭据值只允许记存在与长度,不落值
|
|
23
|
+
//! - 不 spawn 进程,只准备环境与 argv
|
|
24
|
+
//! maturity: wired
|
|
25
|
+
//! ---
|
|
1
26
|
use std::collections::{BTreeMap, BTreeSet};
|
|
2
27
|
use std::path::{Path, PathBuf};
|
|
3
28
|
use std::process::Command;
|
|
@@ -14,10 +39,18 @@ use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest}
|
|
|
14
39
|
|
|
15
40
|
use super::*;
|
|
16
41
|
|
|
42
|
+
/// ---
|
|
43
|
+
/// purpose: 判断 spec 里的该 agent 是否被标为 paused
|
|
44
|
+
/// returns: paused 字段确为真时 true
|
|
45
|
+
/// ---
|
|
17
46
|
pub(super) fn agent_is_paused(agent: &Value) -> bool {
|
|
18
47
|
matches!(agent.get("paused"), Some(Value::Bool(true)))
|
|
19
48
|
}
|
|
20
49
|
|
|
50
|
+
/// ---
|
|
51
|
+
/// purpose: 给出 spawn 时间戳
|
|
52
|
+
/// returns: 测试固定时间戳环境变量优先,否则取当前 UTC 时间的微秒精度串
|
|
53
|
+
/// ---
|
|
21
54
|
pub(crate) fn spawn_timestamp() -> String {
|
|
22
55
|
match std::env::var("TEAM_AGENT_TEST_FIXED_SPAWNED_AT") {
|
|
23
56
|
Ok(value) => value,
|
|
@@ -27,6 +60,12 @@ pub(crate) fn spawn_timestamp() -> String {
|
|
|
27
60
|
}
|
|
28
61
|
}
|
|
29
62
|
|
|
63
|
+
/// ---
|
|
64
|
+
/// purpose: 给出带偏移的 spawn 时间戳,用于同批席位间保持先后次序
|
|
65
|
+
/// params:
|
|
66
|
+
/// offset_micros: 相对基准时间的微秒偏移,为 0 时等同 spawn_timestamp
|
|
67
|
+
/// returns: 固定时间戳可解析时加上偏移,否则原样返回固定值;无固定值时取当前时间
|
|
68
|
+
/// ---
|
|
30
69
|
pub(super) fn spawn_timestamp_for_agent(offset_micros: u32) -> String {
|
|
31
70
|
if offset_micros == 0 {
|
|
32
71
|
return spawn_timestamp();
|
|
@@ -44,10 +83,17 @@ pub(super) fn spawn_timestamp_for_agent(offset_micros: u32) -> String {
|
|
|
44
83
|
}
|
|
45
84
|
}
|
|
46
85
|
|
|
86
|
+
/// ---
|
|
87
|
+
/// purpose: 替换 argv 里的 workspace 与 agent_id 占位符,不带 team_id
|
|
88
|
+
/// ---
|
|
47
89
|
pub(crate) fn fill_spawn_placeholders(argv: &mut [String], workspace: &Path, agent_id: &str) {
|
|
48
90
|
fill_spawn_placeholders_full(argv, workspace, agent_id, None);
|
|
49
91
|
}
|
|
50
92
|
|
|
93
|
+
/// ---
|
|
94
|
+
/// purpose: 给出 auth_mode 写进环境变量时的稳定字符串
|
|
95
|
+
/// returns: subscription、official_api 或 compatible_api
|
|
96
|
+
/// ---
|
|
51
97
|
/// #229 B-layer worker env contract (`worker_spawn_inherits_parent_process_env_for_proxy_and_ca`):
|
|
52
98
|
/// every worker `transport.spawn_first/into` MUST receive an env map that is the **complete**
|
|
53
99
|
/// `team-agent` process environ (so the child sees the user's PATH ordering, HTTP_PROXY /
|
|
@@ -59,10 +105,26 @@ pub(crate) fn fill_spawn_placeholders(argv: &mut [String], workspace: &Path, age
|
|
|
59
105
|
///
|
|
60
106
|
/// `TMUX` / `TMUX_PANE` are stripped because they bind the inherited shell to the **launching**
|
|
61
107
|
/// tmux pane; leaving them in would point worker-side tmux integrations at the wrong pane.
|
|
108
|
+
pub(crate) fn auth_mode_env_value(mode: AuthMode) -> &'static str {
|
|
109
|
+
match mode {
|
|
110
|
+
AuthMode::Subscription => "subscription",
|
|
111
|
+
AuthMode::OfficialApi => "official_api",
|
|
112
|
+
AuthMode::CompatibleApi => "compatible_api",
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// ---
|
|
117
|
+
/// purpose: 由当前进程环境派生 worker 的 spawn 环境
|
|
118
|
+
/// params:
|
|
119
|
+
/// team_id: 作为 owner team id 重新注入给 worker
|
|
120
|
+
/// auth_mode: 写进环境的 auth_mode 字符串
|
|
121
|
+
/// returns: 白名单过滤后的环境,leader 身份类变量与 TMUX 绑定变量已被剥离
|
|
122
|
+
/// ---
|
|
62
123
|
pub(crate) fn inherited_env_with_team_overrides(
|
|
63
124
|
workspace: &Path,
|
|
64
125
|
agent_id: &str,
|
|
65
126
|
team_id: Option<&str>,
|
|
127
|
+
auth_mode: Option<&str>,
|
|
66
128
|
) -> BTreeMap<String, String> {
|
|
67
129
|
// 0.3.28 Step 3: delegate to `layout::worker_env::worker_spawn_env` which
|
|
68
130
|
// implements Python's whitelist semantics (`providers.py:130-145`). The
|
|
@@ -77,9 +139,22 @@ pub(crate) fn inherited_env_with_team_overrides(
|
|
|
77
139
|
// * Strips `COPILOT_DISABLE_TERMINAL_TITLE` (re-injected per-agent by
|
|
78
140
|
// `apply_copilot_instructions_overlay` based on the WORKER's provider).
|
|
79
141
|
// * Strips `TMUX` / `TMUX_PANE` (would attach worker to leader's pane).
|
|
80
|
-
crate::layout::worker_env::worker_spawn_env(
|
|
142
|
+
crate::layout::worker_env::worker_spawn_env(
|
|
143
|
+
std::env::vars(),
|
|
144
|
+
workspace,
|
|
145
|
+
agent_id,
|
|
146
|
+
team_id,
|
|
147
|
+
auth_mode,
|
|
148
|
+
)
|
|
81
149
|
}
|
|
82
150
|
|
|
151
|
+
/// ---
|
|
152
|
+
/// purpose: 按 bypass 结论设置或清除 MCP 自动放行相关环境变量
|
|
153
|
+
/// params:
|
|
154
|
+
/// env: 就地改写的环境表,六个相关键先被无条件清掉
|
|
155
|
+
/// safety: 只有来源为 leader 进程且确为继承而来时才写入放行变量
|
|
156
|
+
/// returns: 不满足条件时只写一个关闭标记
|
|
157
|
+
/// ---
|
|
83
158
|
pub(crate) fn apply_mcp_auto_approval_env(
|
|
84
159
|
env: &mut BTreeMap<String, String>,
|
|
85
160
|
safety: &DangerousApproval,
|
|
@@ -128,6 +203,14 @@ pub(crate) fn apply_mcp_auto_approval_env(
|
|
|
128
203
|
}
|
|
129
204
|
}
|
|
130
205
|
|
|
206
|
+
/// ---
|
|
207
|
+
/// purpose: 把角色提示词写进 per-agent 的 copilot 指令目录并指向它
|
|
208
|
+
/// params:
|
|
209
|
+
/// system_prompt: 写入 AGENTS.md 的正文
|
|
210
|
+
/// env: 就地写入指令目录与禁用终端标题的变量
|
|
211
|
+
/// returns: 成功返回空值
|
|
212
|
+
/// errors: 建目录或写文件失败时返回 StatePersist,不静默吞
|
|
213
|
+
/// ---
|
|
131
214
|
/// BUG / B2 灵魂件 + C-1-2 + C-6-1 cr verdict — Copilot per-worker AGENTS.md
|
|
132
215
|
/// 写入 + `COPILOT_CUSTOM_INSTRUCTIONS_DIRS` 注入。
|
|
133
216
|
///
|
|
@@ -172,6 +255,50 @@ pub(crate) fn apply_copilot_instructions_overlay(
|
|
|
172
255
|
Ok(())
|
|
173
256
|
}
|
|
174
257
|
|
|
258
|
+
/// ---
|
|
259
|
+
/// purpose: 把角色提示词写成 workspace 内 per-agent 的 cursor rules 文件
|
|
260
|
+
/// params:
|
|
261
|
+
/// system_prompt: 写入 rules 正文,文件头声明 alwaysApply
|
|
262
|
+
/// returns: 成功返回空值
|
|
263
|
+
/// errors: 建目录或写文件失败时返回 StatePersist
|
|
264
|
+
/// ---
|
|
265
|
+
/// 0.5.67 Cursor append-system-prompt 方案 1 变体(冒烟 PASS, see
|
|
266
|
+
/// .team/artifacts/0.5.67-cursor-rules-smoke.md)— Cursor `agent` CLI 无
|
|
267
|
+
/// append/rule CLI flag, 只从 workspace 静态 rules 文件读:
|
|
268
|
+
/// `.cursor/rules/**/*.mdc` (alwaysApply: true) / `AGENTS.md` / `.cursorrules`。
|
|
269
|
+
///
|
|
270
|
+
/// 这里在 launch 路径写 `<workspace>/.cursor/rules/team-agent-role-<agent_id>.mdc`
|
|
271
|
+
/// 并把 role(= compile_worker_system_prompt 输出)放进去, argv 只带
|
|
272
|
+
/// `--workspace <workspace>`(cursor_agent.rs)。cursor CLI 读取该目录即加载 role。
|
|
273
|
+
///
|
|
274
|
+
/// 目录布局为 workspace 内 `.cursor/rules/`(Cursor 唯一 rules 通道; 无法像 copilot
|
|
275
|
+
/// 那样放 `.team/runtime/` —— 那是 Cursor 不读的路径)。文件名含 `<agent_id>`
|
|
276
|
+
/// 保证 per-agent 隔离, 不互相污染。
|
|
277
|
+
pub(crate) fn apply_cursor_agent_rules_overlay(
|
|
278
|
+
workspace: &Path,
|
|
279
|
+
agent_id: &str,
|
|
280
|
+
system_prompt: &str,
|
|
281
|
+
) -> Result<(), LifecycleError> {
|
|
282
|
+
let dir = workspace.join(".cursor").join("rules");
|
|
283
|
+
std::fs::create_dir_all(&dir)
|
|
284
|
+
.map_err(|e| LifecycleError::StatePersist(format!("{}: {e}", dir.display())))?;
|
|
285
|
+
let rules = dir.join(format!("team-agent-role-{agent_id}.mdc"));
|
|
286
|
+
let content = format!(
|
|
287
|
+
"---\ndescription: Team Agent role injection (auto-generated)\nalwaysApply: true\n---\n\n{system_prompt}\n"
|
|
288
|
+
);
|
|
289
|
+
std::fs::write(&rules, content.as_bytes())
|
|
290
|
+
.map_err(|e| LifecycleError::StatePersist(format!("{}: {e}", rules.display())))?;
|
|
291
|
+
Ok(())
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/// ---
|
|
295
|
+
/// purpose: 扫出 copilot 侧残留的 MCP server,对非本框架的逐个追加禁用参数
|
|
296
|
+
/// params:
|
|
297
|
+
/// argv: 就地追加禁用参数
|
|
298
|
+
/// log_dir: 残留清单落盘目录
|
|
299
|
+
/// returns: 成功返回空值;copilot 命令不可用或退出码非零时只记录不阻断
|
|
300
|
+
/// errors: 写残留清单文件失败时返回 StatePersist
|
|
301
|
+
/// ---
|
|
175
302
|
/// C-3-2/C-3-3 cr verdict v2 — Copilot spawn 前调 `copilot mcp list` 扫用户全局
|
|
176
303
|
/// `~/.copilot/mcp-config.json` 与 workspace `.mcp.json` 的 MCP 残留;对每个非
|
|
177
304
|
/// `team_orchestrator` server 追加 `--disable-mcp-server <name>`(main-help:72-73)
|
|
@@ -246,6 +373,12 @@ pub(super) fn apply_copilot_mcp_residual_disables(
|
|
|
246
373
|
Ok(())
|
|
247
374
|
}
|
|
248
375
|
|
|
376
|
+
/// ---
|
|
377
|
+
/// purpose: 从 copilot mcp list 输出里解析 server 名集合
|
|
378
|
+
/// params:
|
|
379
|
+
/// text: 命令原始输出
|
|
380
|
+
/// returns: server 名列表;遇到无配置的提示行立即返回空;只在 servers 段内取缩进行首 token
|
|
381
|
+
/// ---
|
|
249
382
|
/// 解析 `copilot mcp list` 输出取 server 名集合(te 真机实证 v2,1.0.59 形态):
|
|
250
383
|
/// ```text
|
|
251
384
|
/// User servers:
|
|
@@ -318,6 +451,65 @@ pub(super) fn parse_copilot_mcp_list_server_names(text: &str) -> Vec<String> {
|
|
|
318
451
|
out
|
|
319
452
|
}
|
|
320
453
|
|
|
454
|
+
/// Subscription seats inherit parent env, but that is implicit. Cursor cloud
|
|
455
|
+
/// on this host is blocked without a proxy; `profile_launch` copies these
|
|
456
|
+
/// keys only on the compatible-api path. Copy them explicitly here so a
|
|
457
|
+
/// subscription cursor seat sees the same keys. Values may contain
|
|
458
|
+
/// credentials — callers must only log presence/length, never the value.
|
|
459
|
+
const CURSOR_PROXY_KEYS: &[&str] = &[
|
|
460
|
+
"HTTPS_PROXY",
|
|
461
|
+
"HTTP_PROXY",
|
|
462
|
+
"ALL_PROXY",
|
|
463
|
+
"NO_PROXY",
|
|
464
|
+
"https_proxy",
|
|
465
|
+
"http_proxy",
|
|
466
|
+
"all_proxy",
|
|
467
|
+
"no_proxy",
|
|
468
|
+
];
|
|
469
|
+
|
|
470
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
471
|
+
pub struct CursorProxyPresence {
|
|
472
|
+
pub https_proxy: bool,
|
|
473
|
+
pub no_proxy: bool,
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/// ---
|
|
477
|
+
/// purpose: 给订阅制 cursor 席位显式补上父进程里的代理变量
|
|
478
|
+
/// params:
|
|
479
|
+
/// env: 就地补键,已有同名键不覆盖
|
|
480
|
+
/// returns: 记录是否存在 https 代理与 no_proxy,只记存在与否不记值
|
|
481
|
+
/// ---
|
|
482
|
+
pub fn apply_cursor_subscription_proxy_env(
|
|
483
|
+
env: &mut BTreeMap<String, String>,
|
|
484
|
+
) -> CursorProxyPresence {
|
|
485
|
+
let mut https_proxy = false;
|
|
486
|
+
let mut no_proxy = false;
|
|
487
|
+
for key in CURSOR_PROXY_KEYS {
|
|
488
|
+
let Ok(value) = std::env::var(key) else {
|
|
489
|
+
continue;
|
|
490
|
+
};
|
|
491
|
+
if value.is_empty() {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
if key.eq_ignore_ascii_case("HTTPS_PROXY") {
|
|
495
|
+
https_proxy = true;
|
|
496
|
+
}
|
|
497
|
+
if key.eq_ignore_ascii_case("NO_PROXY") {
|
|
498
|
+
no_proxy = true;
|
|
499
|
+
}
|
|
500
|
+
env.entry((*key).to_string()).or_insert(value);
|
|
501
|
+
}
|
|
502
|
+
CursorProxyPresence {
|
|
503
|
+
https_proxy,
|
|
504
|
+
no_proxy,
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/// ---
|
|
509
|
+
/// purpose: 把 profile 推出的环境清除与覆盖套用到 spawn 环境上
|
|
510
|
+
/// params:
|
|
511
|
+
/// env: 先按 env_unset 删键,再并入 env_overlay
|
|
512
|
+
/// ---
|
|
321
513
|
pub(crate) fn apply_profile_launch_env(
|
|
322
514
|
env: &mut BTreeMap<String, String>,
|
|
323
515
|
profile_launch: &crate::provider::ProviderProfileLaunch,
|
|
@@ -328,6 +520,11 @@ pub(crate) fn apply_profile_launch_env(
|
|
|
328
520
|
env.extend(profile_launch.env_overlay.clone());
|
|
329
521
|
}
|
|
330
522
|
|
|
523
|
+
/// ---
|
|
524
|
+
/// purpose: 把已 spawn 席位的 profile 相关信息写进该席位的 state 节点
|
|
525
|
+
/// params:
|
|
526
|
+
/// state: 就地写入待定 session id、projects 根与 profile_launch 摘要
|
|
527
|
+
/// ---
|
|
331
528
|
pub(super) fn persist_started_agent_plan_state(
|
|
332
529
|
state: &mut serde_json::Map<String, serde_json::Value>,
|
|
333
530
|
started_agent: &StartedAgent,
|
|
@@ -362,6 +559,12 @@ pub(super) fn persist_started_agent_plan_state(
|
|
|
362
559
|
}
|
|
363
560
|
}
|
|
364
561
|
|
|
562
|
+
/// ---
|
|
563
|
+
/// purpose: 把命令计划与 profile 推出的信息写进该席位的 state 节点
|
|
564
|
+
/// params:
|
|
565
|
+
/// plan: 命令计划,其 projects 根优先于 profile 给出的
|
|
566
|
+
/// profile_launch: profile 推出的启动参数
|
|
567
|
+
/// ---
|
|
365
568
|
pub(crate) fn persist_command_plan_state(
|
|
366
569
|
state: &mut serde_json::Map<String, serde_json::Value>,
|
|
367
570
|
plan: &crate::provider::CommandPlan,
|
|
@@ -399,6 +602,10 @@ pub(crate) fn persist_command_plan_state(
|
|
|
399
602
|
}
|
|
400
603
|
}
|
|
401
604
|
|
|
605
|
+
/// ---
|
|
606
|
+
/// purpose: 判断名字是否是合法的 POSIX shell 标识符
|
|
607
|
+
/// returns: 首字符为字母或下划线且其余为字母数字下划线时为 true
|
|
608
|
+
/// ---
|
|
402
609
|
pub(super) fn is_posix_shell_identifier(name: &str) -> bool {
|
|
403
610
|
let mut chars = name.chars();
|
|
404
611
|
match chars.next() {
|
|
@@ -408,6 +615,12 @@ pub(super) fn is_posix_shell_identifier(name: &str) -> bool {
|
|
|
408
615
|
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
|
|
409
616
|
}
|
|
410
617
|
|
|
618
|
+
/// ---
|
|
619
|
+
/// purpose: 替换 argv 里的 workspace、agent_id 与 team_id 占位符
|
|
620
|
+
/// params:
|
|
621
|
+
/// argv: 就地改写;整串等于占位符时整体替换,否则按子串替换
|
|
622
|
+
/// team_id: None 时按空串替换
|
|
623
|
+
/// ---
|
|
411
624
|
/// Same as [`fill_spawn_placeholders`] plus `{team_id}` substitution everywhere it
|
|
412
625
|
/// appears as a SUBSTRING (the MCP config encodes it as `mcp_servers.team_orchestrator
|
|
413
626
|
/// .env.TEAM_AGENT_OWNER_TEAM_ID="{team_id}"`, embedded inside `-c key=value` strings,
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 冷启、add/clone/fork 入口拼装
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: fork_agent
|
|
6
|
+
//! what: 成员分身入口,grok 走窗口内命令
|
|
7
|
+
//! boundary:
|
|
8
|
+
//! - 不读 provider 会话落盘
|
|
9
|
+
//! maturity: wired
|
|
10
|
+
//! ---
|
|
1
11
|
//!
|
|
2
12
|
//! lifecycle::launch —— 冷启 / quick-start / 危险审批探测 + add/fork / plan 起步与推进。
|
|
3
13
|
|
|
@@ -18,6 +28,16 @@ use super::*;
|
|
|
18
28
|
|
|
19
29
|
// ── lifecycle::launch —— 冷启 / quick-start / 危险审批探测 ──────────────────
|
|
20
30
|
|
|
31
|
+
/// ---
|
|
32
|
+
/// purpose: 冷启全队的默认入口,按 spec 所在目录绑定 per-team tmux socket 后转 launch_with_transport
|
|
33
|
+
/// params:
|
|
34
|
+
/// spec_path: 已编译 spec 的路径,其父目录即 team 目录
|
|
35
|
+
/// dry_run: 只出路由与权限,不 spawn 也不落盘
|
|
36
|
+
/// auto_approve: 当前为 no-op
|
|
37
|
+
/// skip_profile_smoke: 当前为 no-op
|
|
38
|
+
/// returns: LaunchReport
|
|
39
|
+
/// errors: 透传 launch_with_transport 的错误
|
|
40
|
+
/// ---
|
|
21
41
|
///
|
|
22
42
|
/// `launch(spec_path, dry_run, auto_approve, skip_profile_smoke)`(`launch/core.py:29`)。
|
|
23
43
|
/// 冷启全队:路由 tasks、resolve 权限/危险审批门、session 冲突检查(冲突 →
|
|
@@ -42,6 +62,13 @@ pub fn launch(
|
|
|
42
62
|
)
|
|
43
63
|
}
|
|
44
64
|
|
|
65
|
+
/// ---
|
|
66
|
+
/// purpose: 由 spec 路径推出 workspace,再转 launch_with_transport_in_workspace
|
|
67
|
+
/// params:
|
|
68
|
+
/// transport: 已绑定 team socket 的 transport
|
|
69
|
+
/// returns: LaunchReport
|
|
70
|
+
/// errors: 透传 launch_with_transport_in_workspace 的错误
|
|
71
|
+
/// ---
|
|
45
72
|
pub(crate) fn launch_with_transport(
|
|
46
73
|
spec_path: &Path,
|
|
47
74
|
dry_run: bool,
|
|
@@ -61,6 +88,17 @@ pub(crate) fn launch_with_transport(
|
|
|
61
88
|
)
|
|
62
89
|
}
|
|
63
90
|
|
|
91
|
+
/// ---
|
|
92
|
+
/// purpose: 冷启全队的实体实现,编译 spec、检查 session 冲突、按序 spawn、落 state、出报告
|
|
93
|
+
/// params:
|
|
94
|
+
/// workspace: run workspace 根,事件与 state 都落在这里
|
|
95
|
+
/// spec_path: 已编译 spec 的路径
|
|
96
|
+
/// dry_run: 为真时不 spawn 不落盘,started 为空
|
|
97
|
+
/// auto_approve: 未使用
|
|
98
|
+
/// skip_profile_smoke: 未使用
|
|
99
|
+
/// returns: LaunchReport,其中 leader_receiver_attached 恒 false、session_capture_incomplete_agents 恒空
|
|
100
|
+
/// errors: spec 不存在或解析失败返回 Compile,tmux session 已存在返回 SessionConflict,spawn 与落 state 的错误透传
|
|
101
|
+
/// ---
|
|
64
102
|
pub fn launch_with_transport_in_workspace(
|
|
65
103
|
workspace: &Path,
|
|
66
104
|
spec_path: &Path,
|
|
@@ -71,8 +109,8 @@ pub fn launch_with_transport_in_workspace(
|
|
|
71
109
|
) -> Result<LaunchReport, LifecycleError> {
|
|
72
110
|
let _ = skip_profile_smoke;
|
|
73
111
|
let _ = auto_approve; // 0.5.66: bypass 单源后无 runtime.dangerous_auto_approve,`--yes` 为 no-op。
|
|
74
|
-
|
|
75
|
-
|
|
112
|
+
// 0.5.38 (`.team/artifacts/startup-latency-locate.md` §5): launch.phase
|
|
113
|
+
// timer with monotonic `elapsed_ms` for latency triage.
|
|
76
114
|
let phase_timer = crate::lifecycle::restart::RestartPhaseTimer::start();
|
|
77
115
|
if !spec_path.exists() {
|
|
78
116
|
return Err(LifecycleError::Compile(format!(
|
|
@@ -107,13 +145,7 @@ pub fn launch_with_transport_in_workspace(
|
|
|
107
145
|
Vec::new()
|
|
108
146
|
} else {
|
|
109
147
|
phase_timer.emit(workspace, "launch.phase", "spawn_all");
|
|
110
|
-
let started = spawn_agents(
|
|
111
|
-
workspace,
|
|
112
|
-
spec_path,
|
|
113
|
-
&spec,
|
|
114
|
-
&session_name,
|
|
115
|
-
transport,
|
|
116
|
-
)?;
|
|
148
|
+
let started = spawn_agents(workspace, spec_path, &spec, &session_name, transport)?;
|
|
117
149
|
persist_spawn_agent_state(
|
|
118
150
|
workspace,
|
|
119
151
|
spec_path,
|
|
@@ -224,8 +256,8 @@ mod plan;
|
|
|
224
256
|
pub(crate) use plan::{handle_report_result, start_plan};
|
|
225
257
|
|
|
226
258
|
pub mod spawn;
|
|
227
|
-
pub(super) use spawn::*;
|
|
228
259
|
pub(crate) use spawn::agent_id_spec_source_path;
|
|
260
|
+
pub(super) use spawn::*;
|
|
229
261
|
|
|
230
262
|
mod layout;
|
|
231
263
|
pub(super) use layout::*;
|
|
@@ -260,20 +292,38 @@ pub(crate) use agent_state::{
|
|
|
260
292
|
persist_effective_approval_policy_from_agent,
|
|
261
293
|
};
|
|
262
294
|
|
|
295
|
+
mod grok_per_seat;
|
|
296
|
+
pub(crate) use grok_per_seat::{
|
|
297
|
+
is_per_seat_env_key, non_per_seat_env_in_tables, per_seat_keys_in_toml,
|
|
298
|
+
strip_per_seat_keys_from_toml,
|
|
299
|
+
};
|
|
300
|
+
|
|
263
301
|
mod mcp_config;
|
|
302
|
+
pub use mcp_config::apply_grok_mcp_overlay;
|
|
264
303
|
pub(super) use mcp_config::*;
|
|
265
304
|
pub(crate) use mcp_config::{
|
|
266
|
-
|
|
305
|
+
ensure_grok_login_and_folder_trust, grok_shared_cwd_error, point_native_mcp_config_at_file,
|
|
306
|
+
reconcile_grok_toml_per_seat_keys, resolve_mcp_config, write_worker_mcp_config,
|
|
267
307
|
write_worker_mcp_config_for_provider,
|
|
268
308
|
};
|
|
269
309
|
|
|
310
|
+
mod cursor_mcp;
|
|
311
|
+
pub use cursor_mcp::{
|
|
312
|
+
apply_cursor_mcp_overlay, apply_cursor_workspace_physical_path, cursor_mcp_enable_argv,
|
|
313
|
+
enable_cursor_workspace_mcp, physical_workspace_path, refuse_second_cursor_occupant,
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
mod cursor_create_chat;
|
|
317
|
+
|
|
270
318
|
mod worker_env;
|
|
271
319
|
pub(super) use worker_env::*;
|
|
272
320
|
pub(crate) use worker_env::{
|
|
273
|
-
apply_copilot_instructions_overlay,
|
|
321
|
+
apply_copilot_instructions_overlay, apply_cursor_agent_rules_overlay,
|
|
322
|
+
apply_mcp_auto_approval_env, apply_profile_launch_env, auth_mode_env_value,
|
|
274
323
|
fill_spawn_placeholders, fill_spawn_placeholders_full, inherited_env_with_team_overrides,
|
|
275
324
|
persist_command_plan_state, spawn_timestamp,
|
|
276
325
|
};
|
|
326
|
+
pub use worker_env::{apply_cursor_subscription_proxy_env, CursorProxyPresence};
|
|
277
327
|
|
|
278
328
|
mod identity;
|
|
279
329
|
pub(super) use identity::*;
|
|
@@ -300,7 +350,7 @@ pub(crate) use quick_start_transport::{
|
|
|
300
350
|
};
|
|
301
351
|
|
|
302
352
|
pub mod readiness;
|
|
303
|
-
pub
|
|
353
|
+
pub use readiness::launched_team_receiver_is_attached;
|
|
304
354
|
pub(super) use readiness::*;
|
|
305
355
|
|
|
306
356
|
mod add_agent;
|
|
@@ -313,19 +363,12 @@ pub(crate) use add_agent_state::inject_agent_into_spec;
|
|
|
313
363
|
pub(super) use add_agent_state::*;
|
|
314
364
|
|
|
315
365
|
mod fork_agent;
|
|
316
|
-
pub use fork_agent::fork_agent_with_transport;
|
|
317
366
|
pub(super) use fork_agent::*;
|
|
367
|
+
pub use fork_agent::{fork_agent_with_transport, in_window_fork, in_window_fork_command};
|
|
318
368
|
|
|
319
369
|
mod fork_entry;
|
|
320
370
|
pub use fork_entry::fork_agent;
|
|
321
371
|
|
|
322
|
-
mod fork_state;
|
|
323
|
-
pub(super) use fork_state::*;
|
|
324
|
-
|
|
325
|
-
mod fork_finalize;
|
|
326
|
-
pub(super) use fork_finalize::*;
|
|
327
|
-
pub(crate) use fork_finalize::{finalize_pending_fork_capture, ContextForkFinalized};
|
|
328
|
-
|
|
329
372
|
mod role_source;
|
|
330
373
|
pub(super) use role_source::*;
|
|
331
374
|
|
|
@@ -337,9 +380,7 @@ pub(super) use ownership::*;
|
|
|
337
380
|
pub(crate) use ownership::{ensure_owner_allowed, ensure_owner_allowed_for_state, state_path};
|
|
338
381
|
|
|
339
382
|
pub mod spec_state;
|
|
340
|
-
pub
|
|
341
|
-
override_spec_session_name, override_spec_workspace, spec_agent_id_set, write_spec_atomic,
|
|
342
|
-
};
|
|
383
|
+
pub use spec_state::worker_session_name_pub;
|
|
343
384
|
pub(crate) use spec_state::{
|
|
344
385
|
effective_runtime_config_for_worker_spawn, effective_runtime_config_for_worker_spawn_json,
|
|
345
386
|
};
|
|
@@ -350,7 +391,9 @@ use spec_state::{
|
|
|
350
391
|
spec_agent_values, spec_agents, spec_default_assignee, spec_routes, spec_session_name,
|
|
351
392
|
spec_tasks_json, team_workspace, yaml_value_to_json,
|
|
352
393
|
};
|
|
353
|
-
pub use spec_state::
|
|
394
|
+
pub(crate) use spec_state::{
|
|
395
|
+
override_spec_session_name, override_spec_workspace, spec_agent_id_set, write_spec_atomic,
|
|
396
|
+
};
|
|
354
397
|
|
|
355
398
|
#[cfg(test)]
|
|
356
399
|
mod tests {
|
|
@@ -361,10 +404,8 @@ mod tests {
|
|
|
361
404
|
// leader argv 带 bypass flag + 无角色声明 true → 写 warning 事件。
|
|
362
405
|
#[test]
|
|
363
406
|
fn test_leader_flag_present_but_no_role_true_writes_warning_event() {
|
|
364
|
-
let ws =
|
|
365
|
-
"ta-bypass-warning-test-{}",
|
|
366
|
-
std::process::id()
|
|
367
|
-
));
|
|
407
|
+
let ws =
|
|
408
|
+
std::env::temp_dir().join(format!("ta-bypass-warning-test-{}", std::process::id()));
|
|
368
409
|
let _ = std::fs::remove_dir_all(&ws);
|
|
369
410
|
std::fs::create_dir_all(&ws).unwrap();
|
|
370
411
|
let spec = yaml::loads(
|
|
@@ -415,4 +456,33 @@ mod tests {
|
|
|
415
456
|
);
|
|
416
457
|
let _ = std::fs::remove_dir_all(&ws);
|
|
417
458
|
}
|
|
459
|
+
|
|
460
|
+
#[test]
|
|
461
|
+
fn team_agent_force_flag_does_not_look_like_bypass() {
|
|
462
|
+
let ws =
|
|
463
|
+
std::env::temp_dir().join(format!("ta-bypass-force-not-bypass-{}", std::process::id()));
|
|
464
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
465
|
+
std::fs::create_dir_all(&ws).unwrap();
|
|
466
|
+
let spec = yaml::loads(
|
|
467
|
+
"version: 1\nteam: {}\nleader: {}\nagents:\n - id: a\n role: r\n provider: fake\n dangerously_skip_permissions: false\n",
|
|
468
|
+
)
|
|
469
|
+
.unwrap();
|
|
470
|
+
let argv = vec![
|
|
471
|
+
"team-agent".to_string(),
|
|
472
|
+
"remove-agent".to_string(),
|
|
473
|
+
"x".to_string(),
|
|
474
|
+
"--confirm".to_string(),
|
|
475
|
+
"--force".to_string(),
|
|
476
|
+
];
|
|
477
|
+
emit_dangerous_flag_no_role_declared_warning_with_argv(&ws, &spec, &argv);
|
|
478
|
+
let events = crate::event_log::EventLog::new(&ws).tail(0).unwrap();
|
|
479
|
+
assert!(
|
|
480
|
+
!events.iter().any(|event| {
|
|
481
|
+
event.get("event").and_then(serde_json::Value::as_str)
|
|
482
|
+
== Some("dangerously_flag_present_but_no_role_declared")
|
|
483
|
+
}),
|
|
484
|
+
"team-agent --force must not emit bypass warning; events={events:?}"
|
|
485
|
+
);
|
|
486
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
487
|
+
}
|
|
418
488
|
}
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: workspace 级 agent-lifecycle 互斥锁,让同一 workspace 的席位增删起停串行
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: acquire_agent_lifecycle_lock
|
|
6
|
+
//! what: 轮询抢锁,成功返回持有到 Drop 的 guard,超时返回 LifecycleLockTimeout
|
|
7
|
+
//! - name: agent_lifecycle_lock_path
|
|
8
|
+
//! what: 给出该 workspace 的锁文件路径
|
|
9
|
+
//! depends:
|
|
10
|
+
//! - crate::platform::file_lock
|
|
11
|
+
//! - crate::event_log::EventLog
|
|
12
|
+
//! - crate::model::paths
|
|
13
|
+
//! boundary:
|
|
14
|
+
//! - 不锁 pane 输入通道,那是 pane_input_lock
|
|
15
|
+
//! - 不做跨 workspace 互斥,锁作用域就是一个 workspace
|
|
16
|
+
//! - 拿不到锁只超时报错,不强抢也不清理他人锁文件
|
|
17
|
+
//! - 锁文件内容只作观测用,不作为持有权判据
|
|
18
|
+
//! maturity: wired
|
|
19
|
+
//! ---
|
|
1
20
|
use std::io::{Seek, Write};
|
|
2
21
|
use std::path::{Path, PathBuf};
|
|
3
22
|
use std::time::{Duration, Instant};
|
|
@@ -17,10 +36,16 @@ pub(crate) struct LifecycleLockRequest<'a> {
|
|
|
17
36
|
}
|
|
18
37
|
|
|
19
38
|
pub(crate) struct LifecycleLockGuard {
|
|
20
|
-
#[allow(dead_code)]
|
|
21
39
|
file: std::fs::File,
|
|
22
40
|
}
|
|
23
41
|
|
|
42
|
+
/// ---
|
|
43
|
+
/// purpose: 抢 workspace 级 agent-lifecycle 锁,默认 30 秒超时、5 秒起写 lock_held_long 事件
|
|
44
|
+
/// params:
|
|
45
|
+
/// request: 携带 workspace、操作名与可选 team/agent,仅用于锁元数据与事件
|
|
46
|
+
/// returns: 持有锁的 guard,Drop 时解锁
|
|
47
|
+
/// errors: 建目录或打开锁文件失败返回 StatePersist,等待超时返回 LifecycleLockTimeout
|
|
48
|
+
/// ---
|
|
24
49
|
pub(crate) fn acquire_agent_lifecycle_lock(
|
|
25
50
|
request: LifecycleLockRequest<'_>,
|
|
26
51
|
) -> Result<LifecycleLockGuard, LifecycleError> {
|
|
@@ -35,6 +60,14 @@ pub(crate) fn acquire_agent_lifecycle_lock(
|
|
|
35
60
|
)
|
|
36
61
|
}
|
|
37
62
|
|
|
63
|
+
/// ---
|
|
64
|
+
/// purpose: 以显式超时与告警阈值抢同一把锁,仅测试构建可见
|
|
65
|
+
/// params:
|
|
66
|
+
/// timeout: 放弃等待的上限
|
|
67
|
+
/// held_long: 超过它写一次 lock_held_long 事件
|
|
68
|
+
/// returns: 持有锁的 guard
|
|
69
|
+
/// errors: 同 acquire_agent_lifecycle_lock
|
|
70
|
+
/// ---
|
|
38
71
|
#[cfg(test)]
|
|
39
72
|
pub(crate) fn acquire_agent_lifecycle_lock_for_test(
|
|
40
73
|
request: LifecycleLockRequest<'_>,
|
|
@@ -55,6 +88,10 @@ pub(crate) struct LifecycleLockDeadlineOverrideGuard {
|
|
|
55
88
|
previous: Option<(Duration, Duration)>,
|
|
56
89
|
}
|
|
57
90
|
|
|
91
|
+
/// ---
|
|
92
|
+
/// purpose: 线程内改写默认锁超时与告警阈值,仅测试构建可见
|
|
93
|
+
/// returns: guard,Drop 时恢复上一次的取值
|
|
94
|
+
/// ---
|
|
58
95
|
#[cfg(test)]
|
|
59
96
|
pub(crate) fn override_agent_lifecycle_lock_deadlines_for_test(
|
|
60
97
|
timeout: Duration,
|
|
@@ -145,6 +182,10 @@ fn acquire_agent_lifecycle_lock_with_deadlines(
|
|
|
145
182
|
}
|
|
146
183
|
}
|
|
147
184
|
|
|
185
|
+
/// ---
|
|
186
|
+
/// purpose: 给出该 workspace 的 agent-lifecycle 锁文件路径
|
|
187
|
+
/// returns: runtime 目录下的 agent-lifecycle.lock
|
|
188
|
+
/// ---
|
|
148
189
|
pub(crate) fn agent_lifecycle_lock_path(workspace: &Path) -> PathBuf {
|
|
149
190
|
crate::model::paths::runtime_dir(workspace).join(format!("{AGENT_LIFECYCLE_LOCK_NAME}.lock"))
|
|
150
191
|
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: team 进程级生命周期编排(起停加减 clone/fork)
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: launch
|
|
6
|
+
//! what: 冷启与成员生命周期入口
|
|
7
|
+
//! boundary:
|
|
8
|
+
//! - 不拥有物理注入,只调用 transport
|
|
9
|
+
//! maturity: wired
|
|
10
|
+
//! ---
|
|
1
11
|
//!
|
|
2
12
|
//! step 13 · lifecycle — team 进程级生命周期编排器(ROUND-0.5 骨架)。
|
|
3
13
|
//!
|
|
@@ -46,6 +56,7 @@ pub mod display;
|
|
|
46
56
|
pub mod helpers;
|
|
47
57
|
pub mod launch;
|
|
48
58
|
pub(crate) mod lock;
|
|
59
|
+
pub(crate) mod pane_input_lock;
|
|
49
60
|
pub(crate) mod profile_launch;
|
|
50
61
|
pub(crate) mod profile_smoke;
|
|
51
62
|
pub mod restart;
|