@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,25 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 零配置一键起队入口,编译角色目录、起全队、判嵌套层级并给出 attach 指引
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: quick_start
|
|
6
|
+
//! what: 由角色目录一键起队
|
|
7
|
+
//! - name: quick_start_in_workspace_with_display_and_backend
|
|
8
|
+
//! what: 带显示开关与后端选择的起队入口
|
|
9
|
+
//! - name: quick_start_with_transport_in_workspace_with_display
|
|
10
|
+
//! what: 起队的实体实现,含 leader pane 校验、层级门与已有 runtime 的早退
|
|
11
|
+
//! depends:
|
|
12
|
+
//! - crate::compiler
|
|
13
|
+
//! - crate::state::persist
|
|
14
|
+
//! - crate::transport_factory
|
|
15
|
+
//! - crate::lifecycle::launch::identity
|
|
16
|
+
//! - crate::lifecycle::launch::quick_start_transport
|
|
17
|
+
//! boundary:
|
|
18
|
+
//! - 只管初次起队,已有 runtime 时给出 restart 指引而不接管
|
|
19
|
+
//! - 显式指定非 tmux 后端时不静默退回 tmux,不可用就如实报错
|
|
20
|
+
//! - 团队嵌套超过两层直接拒绝
|
|
21
|
+
//! maturity: wired
|
|
22
|
+
//! ---
|
|
1
23
|
use std::collections::{BTreeMap, BTreeSet};
|
|
2
24
|
use std::path::{Path, PathBuf};
|
|
3
25
|
use std::process::Command;
|
|
@@ -14,6 +36,17 @@ use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest}
|
|
|
14
36
|
|
|
15
37
|
use super::*;
|
|
16
38
|
|
|
39
|
+
/// ---
|
|
40
|
+
/// purpose: 由角色目录推出 workspace 后一键起队
|
|
41
|
+
/// params:
|
|
42
|
+
/// agents_dir: 角色定义目录
|
|
43
|
+
/// name: 请求的团队名
|
|
44
|
+
/// yes: 免确认标志
|
|
45
|
+
/// team_id: 显式团队键,优先于 name
|
|
46
|
+
/// returns: 起队报告
|
|
47
|
+
/// errors: 透传实体实现的错误
|
|
48
|
+
/// contract_id: lifecycle.quick_start.entry
|
|
49
|
+
/// ---
|
|
17
50
|
/// `quick_start(agents_dir, name, yes, team_id)`(`diagnose/quick_start.py:18`)。
|
|
18
51
|
/// 面向用户的零配置入口:编译 team_dir → `launch` → autobind leader receiver → 起
|
|
19
52
|
/// coordinator → `wait_ready` 轮询就绪。归入 lifecycle module(不与 diagnose 混)。
|
|
@@ -27,6 +60,12 @@ pub fn quick_start(
|
|
|
27
60
|
quick_start_in_workspace(&workspace, agents_dir, name, yes, team_id)
|
|
28
61
|
}
|
|
29
62
|
|
|
63
|
+
/// ---
|
|
64
|
+
/// purpose: 在指定 workspace 起队,transport 优先复用调用方所在 tmux socket
|
|
65
|
+
/// returns: 起队报告
|
|
66
|
+
/// errors: 透传实体实现的错误
|
|
67
|
+
/// contract_id: lifecycle.quick_start.entry
|
|
68
|
+
/// ---
|
|
30
69
|
pub(crate) fn quick_start_in_workspace(
|
|
31
70
|
workspace: &Path,
|
|
32
71
|
agents_dir: &Path,
|
|
@@ -39,6 +78,14 @@ pub(crate) fn quick_start_in_workspace(
|
|
|
39
78
|
quick_start_with_transport_in_workspace(&workspace, agents_dir, name, yes, team_id, &transport)
|
|
40
79
|
}
|
|
41
80
|
|
|
81
|
+
/// ---
|
|
82
|
+
/// purpose: 带显示开关与后端字面量的起队入口
|
|
83
|
+
/// params:
|
|
84
|
+
/// open_display: 为假时把 spec 的显示后端改成 none
|
|
85
|
+
/// backend: 未给或为 tmux 时走既有 tmux 路径,其余字面量经 transport 工厂解析
|
|
86
|
+
/// returns: 起队报告
|
|
87
|
+
/// errors: 后端字面量不认识时返回 TeamSelect;工厂拒绝或后端不可用时如实报错,不退回 tmux
|
|
88
|
+
/// ---
|
|
42
89
|
/// 0.5.x Phase 1d Batch 2: quick-start with an optional
|
|
43
90
|
/// `--backend <tmux|conpty>` override. When `backend` is `None` or
|
|
44
91
|
/// `Some("tmux")` the transport is the same one the legacy entrypoint
|
|
@@ -182,6 +229,12 @@ pub fn quick_start_in_workspace_with_display_and_backend(
|
|
|
182
229
|
)
|
|
183
230
|
}
|
|
184
231
|
|
|
232
|
+
/// ---
|
|
233
|
+
/// purpose: 带注入 transport 的起队入口,由角色目录推 workspace
|
|
234
|
+
/// returns: 起队报告
|
|
235
|
+
/// errors: 透传实体实现的错误
|
|
236
|
+
/// contract_id: lifecycle.quick_start.entry
|
|
237
|
+
/// ---
|
|
185
238
|
pub(crate) fn quick_start_with_transport(
|
|
186
239
|
agents_dir: &Path,
|
|
187
240
|
name: Option<&str>,
|
|
@@ -193,6 +246,12 @@ pub(crate) fn quick_start_with_transport(
|
|
|
193
246
|
quick_start_with_transport_in_workspace(&workspace, agents_dir, name, yes, team_id, transport)
|
|
194
247
|
}
|
|
195
248
|
|
|
249
|
+
/// ---
|
|
250
|
+
/// purpose: 带注入 transport 与显式 workspace 的起队入口,默认开显示
|
|
251
|
+
/// returns: 起队报告
|
|
252
|
+
/// errors: 透传实体实现的错误
|
|
253
|
+
/// contract_id: lifecycle.quick_start.entry
|
|
254
|
+
/// ---
|
|
196
255
|
pub(crate) fn quick_start_with_transport_in_workspace(
|
|
197
256
|
workspace: &Path,
|
|
198
257
|
agents_dir: &Path,
|
|
@@ -206,6 +265,13 @@ pub(crate) fn quick_start_with_transport_in_workspace(
|
|
|
206
265
|
)
|
|
207
266
|
}
|
|
208
267
|
|
|
268
|
+
/// ---
|
|
269
|
+
/// purpose: 起队的实体实现,校验 leader pane、编译 spec、定团队键、判层级、必要时早退,然后起队并给 attach 指引
|
|
270
|
+
/// params:
|
|
271
|
+
/// open_display: 为假时把显示后端改成 none
|
|
272
|
+
/// returns: 起队报告,含 session 名与 attach 命令
|
|
273
|
+
/// errors: leader pane 环境无效返回 RequirementUnmet;角色目录不存在或编译失败返回 Compile;嵌套层级超限返回 RequirementUnmet;读 state 失败返回 StatePersist
|
|
274
|
+
/// ---
|
|
209
275
|
pub(crate) fn quick_start_with_transport_in_workspace_with_display(
|
|
210
276
|
workspace: &Path,
|
|
211
277
|
agents_dir: &Path,
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 起队路径上的 transport 选择、runtime state 里的 transport 标注与 attach 命令拼装
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: quick_start_tmux_backend
|
|
6
|
+
//! what: 优先复用调用方所在 tmux socket,否则按 workspace 派生
|
|
7
|
+
//! - name: annotate_runtime_transport
|
|
8
|
+
//! what: 把 transport 种类与来源写进 state,tmux 时再写 endpoint 三字段
|
|
9
|
+
//! - name: attach_commands_for_runtime_windows
|
|
10
|
+
//! what: 拼出用户可直接粘贴的 attach 命令
|
|
11
|
+
//! - name: attach_window_names_for_state_agents
|
|
12
|
+
//! what: 由 state 推出这些席位对应的窗口名
|
|
13
|
+
//! depends:
|
|
14
|
+
//! - crate::tmux_backend
|
|
15
|
+
//! - crate::transport::Transport
|
|
16
|
+
//! - crate::state::projection
|
|
17
|
+
//! - crate::event_log::EventLog
|
|
18
|
+
//! boundary:
|
|
19
|
+
//! - 只做选择与标注,不 spawn 也不 kill
|
|
20
|
+
//! - tmux 专有字段不写进非 tmux 后端的 state
|
|
21
|
+
//! - pane 标题设置失败只告警不阻断
|
|
22
|
+
//! maturity: wired
|
|
23
|
+
//! ---
|
|
1
24
|
use std::collections::{BTreeMap, BTreeSet};
|
|
2
25
|
use std::path::{Path, PathBuf};
|
|
3
26
|
use std::process::Command;
|
|
@@ -14,6 +37,10 @@ use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest}
|
|
|
14
37
|
|
|
15
38
|
use super::*;
|
|
16
39
|
|
|
40
|
+
/// ---
|
|
41
|
+
/// purpose: 定出 quick-start 使用的 tmux 后端
|
|
42
|
+
/// returns: 调用方环境里有 socket 就复用它,否则按 workspace 派生一个
|
|
43
|
+
/// ---
|
|
17
44
|
pub(crate) fn quick_start_tmux_backend(workspace: &Path) -> crate::tmux_backend::TmuxBackend {
|
|
18
45
|
if let Some(endpoint) = crate::tmux_backend::socket_name_from_tmux_env() {
|
|
19
46
|
crate::tmux_backend::TmuxBackend::for_tmux_endpoint(&endpoint)
|
|
@@ -22,6 +49,10 @@ pub(crate) fn quick_start_tmux_backend(workspace: &Path) -> crate::tmux_backend:
|
|
|
22
49
|
}
|
|
23
50
|
}
|
|
24
51
|
|
|
52
|
+
/// ---
|
|
53
|
+
/// purpose: 说明当前 transport 的 socket 是从哪来的
|
|
54
|
+
/// returns: leader_env 或 workspace;无 endpoint 或两者都不匹配时为 None
|
|
55
|
+
/// ---
|
|
25
56
|
pub(crate) fn selected_tmux_socket_source(
|
|
26
57
|
transport: &dyn Transport,
|
|
27
58
|
workspace: &Path,
|
|
@@ -36,6 +67,10 @@ pub(crate) fn selected_tmux_socket_source(
|
|
|
36
67
|
}
|
|
37
68
|
}
|
|
38
69
|
|
|
70
|
+
/// ---
|
|
71
|
+
/// purpose: 给 adaptive 布局的 pane 设置标题
|
|
72
|
+
/// returns: 失败只打印告警并写一条事件,不向调用方报错
|
|
73
|
+
/// ---
|
|
39
74
|
pub(crate) fn configure_adaptive_pane_title(
|
|
40
75
|
workspace: &Path,
|
|
41
76
|
transport: &dyn Transport,
|
|
@@ -66,6 +101,10 @@ pub(crate) fn configure_adaptive_pane_title(
|
|
|
66
101
|
}
|
|
67
102
|
}
|
|
68
103
|
|
|
104
|
+
/// ---
|
|
105
|
+
/// purpose: 把用户给的 workspace 归一成绝对路径
|
|
106
|
+
/// returns: 能 canonicalize 就用它;否则绝对路径原样返回,相对路径接到当前目录后
|
|
107
|
+
/// ---
|
|
69
108
|
pub(super) fn explicit_quick_start_workspace(workspace: &Path) -> PathBuf {
|
|
70
109
|
std::fs::canonicalize(workspace).unwrap_or_else(|_| {
|
|
71
110
|
if workspace.is_absolute() {
|
|
@@ -78,6 +117,13 @@ pub(super) fn explicit_quick_start_workspace(workspace: &Path) -> PathBuf {
|
|
|
78
117
|
})
|
|
79
118
|
}
|
|
80
119
|
|
|
120
|
+
/// ---
|
|
121
|
+
/// purpose: 把 transport 的种类与来源写进 runtime state
|
|
122
|
+
/// params:
|
|
123
|
+
/// state: 就地写入 transport 段
|
|
124
|
+
/// source: 来源说明,None 时记 unknown
|
|
125
|
+
/// returns: tmux 后端会再调 annotate_runtime_tmux_endpoint,非 tmux 不写 tmux 专有字段
|
|
126
|
+
/// ---
|
|
81
127
|
/// `quick_start` with an injected transport — tests inject a recording mock so the REAL spawn path
|
|
82
128
|
/// (launch dry_run=false → spawn_agents) is asserted without a live tmux; prod uses the real TmuxBackend.
|
|
83
129
|
/// Annotate `state.tmux_endpoint` / `state.tmux_socket` (and `tmux_socket_source`)
|
|
@@ -126,6 +172,12 @@ pub fn annotate_runtime_transport(
|
|
|
126
172
|
}
|
|
127
173
|
}
|
|
128
174
|
|
|
175
|
+
/// ---
|
|
176
|
+
/// purpose: 把 tmux endpoint、socket 与 socket 来源写进 state
|
|
177
|
+
/// params:
|
|
178
|
+
/// state: 就地写入
|
|
179
|
+
/// returns: transport 没有 endpoint 时不写;socket 名能解析成路径时写路径
|
|
180
|
+
/// ---
|
|
129
181
|
pub(crate) fn annotate_runtime_tmux_endpoint(
|
|
130
182
|
state: &mut serde_json::Value,
|
|
131
183
|
transport: &dyn Transport,
|
|
@@ -162,6 +214,12 @@ pub(crate) fn annotate_runtime_tmux_endpoint(
|
|
|
162
214
|
}
|
|
163
215
|
}
|
|
164
216
|
|
|
217
|
+
/// ---
|
|
218
|
+
/// purpose: 拼出这些窗口的 tmux attach 命令
|
|
219
|
+
/// params:
|
|
220
|
+
/// endpoint: 已知 endpoint;是绝对路径时直接拼 -S 形式,否则交给 tmux_backend 按 workspace 拼
|
|
221
|
+
/// returns: 与窗口一一对应的命令行
|
|
222
|
+
/// ---
|
|
165
223
|
pub(super) fn attach_commands_for_runtime_windows<'a>(
|
|
166
224
|
endpoint: Option<&str>,
|
|
167
225
|
workspace: &Path,
|
|
@@ -199,6 +257,10 @@ pub(super) fn attach_commands_for_runtime_windows<'a>(
|
|
|
199
257
|
attach
|
|
200
258
|
}
|
|
201
259
|
|
|
260
|
+
/// ---
|
|
261
|
+
/// purpose: 由本次起的席位推出窗口名
|
|
262
|
+
/// returns: 排序去重后的窗口名,席位没有布局窗口时用 agent id
|
|
263
|
+
/// ---
|
|
202
264
|
pub(super) fn started_attach_window_names(started: &[StartedAgent]) -> Vec<String> {
|
|
203
265
|
let mut windows = started
|
|
204
266
|
.iter()
|
|
@@ -215,6 +277,10 @@ pub(super) fn started_attach_window_names(started: &[StartedAgent]) -> Vec<Strin
|
|
|
215
277
|
windows
|
|
216
278
|
}
|
|
217
279
|
|
|
280
|
+
/// ---
|
|
281
|
+
/// purpose: 由 state 推出指定席位的窗口名
|
|
282
|
+
/// returns: 每个席位取 layout_window 再取 window,都没有时用 agent id;托管 leader 时追加 leader 窗口
|
|
283
|
+
/// ---
|
|
218
284
|
pub(crate) fn attach_window_names_for_state_agents<'a>(
|
|
219
285
|
state: &serde_json::Value,
|
|
220
286
|
agent_ids: impl IntoIterator<Item = &'a str>,
|
|
@@ -240,6 +306,10 @@ pub(crate) fn attach_window_names_for_state_agents<'a>(
|
|
|
240
306
|
attach_window_names_with_managed_leader(state, windows)
|
|
241
307
|
}
|
|
242
308
|
|
|
309
|
+
/// ---
|
|
310
|
+
/// purpose: 由 state 里的全部席位推出窗口名
|
|
311
|
+
/// returns: 排序去重后的窗口名,托管 leader 时含 leader 窗口
|
|
312
|
+
/// ---
|
|
243
313
|
pub(super) fn quick_start_attach_window_names(state: &serde_json::Value) -> Vec<String> {
|
|
244
314
|
let windows = state
|
|
245
315
|
.get("agents")
|
|
@@ -261,6 +331,10 @@ pub(super) fn quick_start_attach_window_names(state: &serde_json::Value) -> Vec<
|
|
|
261
331
|
attach_window_names_with_managed_leader(state, windows)
|
|
262
332
|
}
|
|
263
333
|
|
|
334
|
+
/// ---
|
|
335
|
+
/// purpose: 托管 leader 时补上 leader 窗口并排序去重
|
|
336
|
+
/// returns: 处理后的窗口名列表
|
|
337
|
+
/// ---
|
|
264
338
|
pub(super) fn attach_window_names_with_managed_leader(
|
|
265
339
|
state: &serde_json::Value,
|
|
266
340
|
mut windows: Vec<String>,
|
|
@@ -273,6 +347,10 @@ pub(super) fn attach_window_names_with_managed_leader(
|
|
|
273
347
|
windows
|
|
274
348
|
}
|
|
275
349
|
|
|
350
|
+
/// ---
|
|
351
|
+
/// purpose: 判断该 state 是否走托管 leader
|
|
352
|
+
/// returns: 由 state::projection 给出的判定
|
|
353
|
+
/// ---
|
|
276
354
|
pub(super) fn state_uses_managed_leader(state: &serde_json::Value) -> bool {
|
|
277
355
|
crate::state::projection::state_is_managed_leader(state)
|
|
278
356
|
}
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 起队后的就绪判定,含 worker 状态汇总与 leader 收件可达性
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: quick_start_worker_readiness
|
|
6
|
+
//! what: 汇总各席位状态给出就绪结论
|
|
7
|
+
//! - name: quick_start_session_capture_incomplete_agents
|
|
8
|
+
//! what: 列出会话捕获尚未收敛的席位
|
|
9
|
+
//! - name: launched_team_receiver_is_attached
|
|
10
|
+
//! what: 以宿主注册表为准判断 leader 收件是否已挂上
|
|
11
|
+
//! depends:
|
|
12
|
+
//! - crate::state::persist
|
|
13
|
+
//! - crate::session_capture
|
|
14
|
+
//! - crate::leader::registry
|
|
15
|
+
//! boundary:
|
|
16
|
+
//! - 可达性以宿主注册表为权威,workspace state 只是副本
|
|
17
|
+
//! - 判不出来一律当作未挂上,不乐观放行
|
|
18
|
+
//! maturity: wired
|
|
19
|
+
//! ---
|
|
1
20
|
//! unit-8 (Stage 3) — `lifecycle::launch::readiness` phase boundary.
|
|
2
21
|
//!
|
|
3
22
|
//! Dedicated home for coordinator-start + readiness-verdict computation.
|
|
@@ -20,6 +39,12 @@ use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest}
|
|
|
20
39
|
|
|
21
40
|
use super::*;
|
|
22
41
|
|
|
42
|
+
/// ---
|
|
43
|
+
/// purpose: 由 runtime state 汇总该团队的就绪结论
|
|
44
|
+
/// params:
|
|
45
|
+
/// team_key: 团队键,teams 表里没有时退到顶层 state
|
|
46
|
+
/// returns: 有非 running 席位时返回 Degraded 并列出它们,否则返回 PendingToolLoad
|
|
47
|
+
/// ---
|
|
23
48
|
pub(super) fn quick_start_worker_readiness(
|
|
24
49
|
workspace: &Path,
|
|
25
50
|
team_key: &str,
|
|
@@ -66,6 +91,10 @@ pub(super) fn quick_start_worker_readiness(
|
|
|
66
91
|
}
|
|
67
92
|
}
|
|
68
93
|
|
|
94
|
+
/// ---
|
|
95
|
+
/// purpose: 列出会话捕获尚未收敛的席位
|
|
96
|
+
/// returns: 席位 id 列表;读不出 state 时为空
|
|
97
|
+
/// ---
|
|
69
98
|
pub(super) fn quick_start_session_capture_incomplete_agents(
|
|
70
99
|
workspace: &Path,
|
|
71
100
|
team_key: &str,
|
|
@@ -81,48 +110,66 @@ pub(super) fn quick_start_session_capture_incomplete_agents(
|
|
|
81
110
|
crate::session_capture::incomplete_interacted_resumable_agent_ids(team_state)
|
|
82
111
|
}
|
|
83
112
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return crate::state::projection::state_is_external_leader(team_state);
|
|
113
|
+
/// ---
|
|
114
|
+
/// purpose: 判断该团队的 leader 收件端是否真的挂上了
|
|
115
|
+
/// returns: 注册表明确记为 attached 且 state 可读时为 true;未绑定或判不出一律 false
|
|
116
|
+
/// ---
|
|
117
|
+
/// Host registry is the deliverability authority. Workspace `state.json`
|
|
118
|
+
/// is only a copy. Detection failure is unbound, never attached.
|
|
119
|
+
pub fn launched_team_receiver_is_attached(workspace: &Path, team_key: &str) -> bool {
|
|
120
|
+
match registry_deliverability(workspace, team_key) {
|
|
121
|
+
RegistryDeliverability::Attached => {}
|
|
122
|
+
RegistryDeliverability::Unbound | RegistryDeliverability::Undecidable => return false,
|
|
95
123
|
}
|
|
96
|
-
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
leader_receiver_is_attached(team_state)
|
|
124
|
+
load_runtime_state(workspace).is_ok()
|
|
100
125
|
}
|
|
101
126
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
.is_some_and(|agents| {
|
|
107
|
-
!agents.is_empty()
|
|
108
|
-
&& agents.values().all(|agent| {
|
|
109
|
-
agent.get("model").and_then(serde_json::Value::as_str) == Some("fake")
|
|
110
|
-
})
|
|
111
|
-
})
|
|
127
|
+
enum RegistryDeliverability {
|
|
128
|
+
Attached,
|
|
129
|
+
Unbound,
|
|
130
|
+
Undecidable,
|
|
112
131
|
}
|
|
113
132
|
|
|
114
|
-
|
|
115
|
-
let Some(
|
|
116
|
-
return
|
|
133
|
+
fn registry_deliverability(workspace: &Path, team_key: &str) -> RegistryDeliverability {
|
|
134
|
+
let Some(dir) = crate::leader::registry::registry_dir() else {
|
|
135
|
+
return RegistryDeliverability::Undecidable;
|
|
117
136
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
let
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
if team_key.is_empty() {
|
|
138
|
+
return RegistryDeliverability::Unbound;
|
|
139
|
+
}
|
|
140
|
+
let hash = crate::leader::registry::workspace_hash(workspace);
|
|
141
|
+
let path = dir.join(format!("{hash}__{team_key}.json"));
|
|
142
|
+
match std::fs::read_to_string(&path) {
|
|
143
|
+
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
|
|
144
|
+
RegistryDeliverability::Unbound
|
|
145
|
+
}
|
|
146
|
+
Err(_) => RegistryDeliverability::Undecidable,
|
|
147
|
+
Ok(text) => {
|
|
148
|
+
match serde_json::from_str::<crate::leader::registry::LeaderRegistryEntry>(&text) {
|
|
149
|
+
Err(_) => RegistryDeliverability::Undecidable,
|
|
150
|
+
Ok(entry) => {
|
|
151
|
+
if entry.status != "attached" {
|
|
152
|
+
return RegistryDeliverability::Unbound;
|
|
153
|
+
}
|
|
154
|
+
if let Some(authorized) = entry
|
|
155
|
+
.channel
|
|
156
|
+
.get("authorized_team_workspace")
|
|
157
|
+
.and_then(serde_json::Value::as_str)
|
|
158
|
+
.filter(|value| !value.is_empty())
|
|
159
|
+
{
|
|
160
|
+
if !same_workspace_path(Path::new(authorized), workspace) {
|
|
161
|
+
return RegistryDeliverability::Unbound;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
RegistryDeliverability::Attached
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fn same_workspace_path(left: &Path, right: &Path) -> bool {
|
|
172
|
+
let left = std::fs::canonicalize(left).unwrap_or_else(|_| left.to_path_buf());
|
|
173
|
+
let right = std::fs::canonicalize(right).unwrap_or_else(|_| right.to_path_buf());
|
|
174
|
+
left == right
|
|
128
175
|
}
|
|
@@ -1,10 +1,37 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 把源席最新角色文件物化到 dynamic-role-files
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: materialize_latest_role
|
|
6
|
+
//! what: clone/add 用的角色落盘,以磁盘最新文件为准
|
|
7
|
+
//! boundary:
|
|
8
|
+
//! - 不负责 fork 窗口注入
|
|
9
|
+
//! - 不读 provider session 落盘
|
|
10
|
+
//! maturity: wired
|
|
11
|
+
//! ---
|
|
1
12
|
use std::path::{Path, PathBuf};
|
|
2
13
|
|
|
3
14
|
use crate::lifecycle::LifecycleError;
|
|
4
15
|
use crate::model::ids::AgentId;
|
|
5
16
|
use crate::model::yaml::{self, Value};
|
|
6
17
|
|
|
7
|
-
|
|
18
|
+
fn set_yaml_map_value(
|
|
19
|
+
value: &mut Value,
|
|
20
|
+
key: &str,
|
|
21
|
+
next: Value,
|
|
22
|
+
) -> Result<(), LifecycleError> {
|
|
23
|
+
let Value::Map(pairs) = value else {
|
|
24
|
+
return Err(LifecycleError::Compile(
|
|
25
|
+
"agent entry is not a map".to_string(),
|
|
26
|
+
));
|
|
27
|
+
};
|
|
28
|
+
if let Some((_, existing)) = pairs.iter_mut().find(|(k, _)| k == key) {
|
|
29
|
+
*existing = next;
|
|
30
|
+
} else {
|
|
31
|
+
pairs.push((key.to_string(), next));
|
|
32
|
+
}
|
|
33
|
+
Ok(())
|
|
34
|
+
}
|
|
8
35
|
|
|
9
36
|
pub(super) struct MaterializedRole {
|
|
10
37
|
path: PathBuf,
|
|
@@ -12,10 +39,17 @@ pub(super) struct MaterializedRole {
|
|
|
12
39
|
}
|
|
13
40
|
|
|
14
41
|
impl MaterializedRole {
|
|
42
|
+
/// ---
|
|
43
|
+
/// purpose: 取物化出来的角色文件路径
|
|
44
|
+
/// returns: 落盘路径
|
|
45
|
+
/// ---
|
|
15
46
|
pub(super) fn path(&self) -> &Path {
|
|
16
47
|
&self.path
|
|
17
48
|
}
|
|
18
49
|
|
|
50
|
+
/// ---
|
|
51
|
+
/// purpose: 标记该文件由调用方接管,Drop 时不再删除
|
|
52
|
+
/// ---
|
|
19
53
|
pub(super) fn keep(&mut self) {
|
|
20
54
|
self.keep = true;
|
|
21
55
|
}
|
|
@@ -29,6 +63,15 @@ impl Drop for MaterializedRole {
|
|
|
29
63
|
}
|
|
30
64
|
}
|
|
31
65
|
|
|
66
|
+
/// ---
|
|
67
|
+
/// purpose: 读源席最新角色文件,改名后物化到托管目录
|
|
68
|
+
/// params:
|
|
69
|
+
/// state: 用于找源席的 dynamic_role_file,找不到时退到 team 目录下的同名 md
|
|
70
|
+
/// as_agent_id: 新席位名,写进 front matter 的 name
|
|
71
|
+
/// label: 非空时覆盖 front matter 的 role
|
|
72
|
+
/// returns: 物化结果,未调用 keep 时 Drop 会删掉该文件
|
|
73
|
+
/// errors: 源文件缺失、未声明 name 或声明与源席不符时返回 Compile;目标已存在返回 RequirementUnmet;建目录或写盘失败返回 StatePersist
|
|
74
|
+
/// ---
|
|
32
75
|
pub(super) fn materialize_latest_role(
|
|
33
76
|
run_workspace: &Path,
|
|
34
77
|
team_dir: &Path,
|
|
@@ -86,49 +129,6 @@ pub(super) fn materialize_latest_role(
|
|
|
86
129
|
Ok(MaterializedRole { path, keep: false })
|
|
87
130
|
}
|
|
88
131
|
|
|
89
|
-
pub(super) fn clamp_materialized_role_to_leader(
|
|
90
|
-
materialized: &Path,
|
|
91
|
-
spec: &Value,
|
|
92
|
-
) -> Result<(), LifecycleError> {
|
|
93
|
-
let leader_tools = spec
|
|
94
|
-
.get("leader")
|
|
95
|
-
.and_then(|leader| leader.get("tools"))
|
|
96
|
-
.and_then(Value::as_list)
|
|
97
|
-
.map(|items| {
|
|
98
|
-
items
|
|
99
|
-
.iter()
|
|
100
|
-
.filter_map(Value::as_str)
|
|
101
|
-
.map(str::to_string)
|
|
102
|
-
.collect::<Vec<_>>()
|
|
103
|
-
})
|
|
104
|
-
.unwrap_or_default();
|
|
105
|
-
let (mut meta, body) = crate::compiler::read_front_matter(materialized)
|
|
106
|
-
.map_err(|error| LifecycleError::Compile(error.to_string()))?;
|
|
107
|
-
let requested = meta
|
|
108
|
-
.get("tools")
|
|
109
|
-
.and_then(Value::as_list)
|
|
110
|
-
.map(|items| {
|
|
111
|
-
items
|
|
112
|
-
.iter()
|
|
113
|
-
.filter_map(Value::as_str)
|
|
114
|
-
.map(str::to_string)
|
|
115
|
-
.collect::<Vec<_>>()
|
|
116
|
-
})
|
|
117
|
-
.unwrap_or_default();
|
|
118
|
-
let ceiling = crate::model::permissions::expand_tool_strings(&leader_tools)
|
|
119
|
-
.into_iter()
|
|
120
|
-
.collect::<std::collections::BTreeSet<_>>();
|
|
121
|
-
let effective = crate::model::permissions::expand_tool_strings(&requested)
|
|
122
|
-
.into_iter()
|
|
123
|
-
.filter(|tool| ceiling.contains(tool))
|
|
124
|
-
.map(Value::Str)
|
|
125
|
-
.collect::<Vec<_>>();
|
|
126
|
-
set_yaml_map_value(&mut meta, "tools", Value::List(effective))?;
|
|
127
|
-
let rendered = format!("---\n{}---\n\n{}", yaml::dumps(&meta), body);
|
|
128
|
-
std::fs::write(materialized, rendered.as_bytes())
|
|
129
|
-
.map_err(|error| LifecycleError::StatePersist(error.to_string()))
|
|
130
|
-
}
|
|
131
|
-
|
|
132
132
|
fn resolve_role_source(
|
|
133
133
|
run_workspace: &Path,
|
|
134
134
|
team_dir: &Path,
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 冷启路径的 worker spawn 执行器,按 spec 逐个起席并汇总启动结果
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: spawn_agents
|
|
6
|
+
//! what: 按 spec 顺序起各非 paused 席位,返回已起席位清单
|
|
7
|
+
//! - name: agent_id_spec_source_path
|
|
8
|
+
//! what: 尽力还原某席位角色文档的路径,用于审计事件
|
|
9
|
+
//! depends:
|
|
10
|
+
//! - crate::transport::Transport
|
|
11
|
+
//! - crate::provider
|
|
12
|
+
//! - crate::state::persist
|
|
13
|
+
//! - crate::lifecycle::profile_launch
|
|
14
|
+
//! - crate::lifecycle::worker_command_context
|
|
15
|
+
//! boundary:
|
|
16
|
+
//! - 只负责起席与收集结果,state 落盘由 state_projection 负责
|
|
17
|
+
//! - 启动提示按掉与 fast 模式启用都是尽力而为,失败不阻断
|
|
18
|
+
//! maturity: wired
|
|
19
|
+
//! ---
|
|
1
20
|
//!
|
|
2
21
|
//! unit-8 (Stage 3) — `lifecycle::launch::spawn` phase boundary.
|
|
3
22
|
//!
|
|
@@ -22,6 +41,14 @@ use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest}
|
|
|
22
41
|
|
|
23
42
|
use super::*;
|
|
24
43
|
|
|
44
|
+
/// ---
|
|
45
|
+
/// purpose: 按 spec 顺序起各席位
|
|
46
|
+
/// params:
|
|
47
|
+
/// spec_path: spec 路径,team 目录优先取 state 里的 team_dir
|
|
48
|
+
/// session_name: 目标 tmux session
|
|
49
|
+
/// returns: 已起席位清单,含目标 pane、启动模式、布局与显示信息;spawn 后 pane 已死的席位被跳过
|
|
50
|
+
/// errors: 命令拼装或 transport spawn 失败时返回 LifecycleError
|
|
51
|
+
/// ---
|
|
25
52
|
pub(super) fn spawn_agents(
|
|
26
53
|
workspace: &Path,
|
|
27
54
|
spec_path: &Path,
|
|
@@ -207,8 +234,12 @@ pub(super) fn spawn_agents(
|
|
|
207
234
|
)?;
|
|
208
235
|
}
|
|
209
236
|
fill_spawn_placeholders_full(&mut plan.argv, workspace, agent_id_raw, Some(&mcp_team_id));
|
|
210
|
-
let mut env =
|
|
211
|
-
|
|
237
|
+
let mut env = inherited_env_with_team_overrides(
|
|
238
|
+
workspace,
|
|
239
|
+
agent_id_raw,
|
|
240
|
+
Some(&mcp_team_id),
|
|
241
|
+
Some(auth_mode_env_value(auth_mode)),
|
|
242
|
+
);
|
|
212
243
|
apply_profile_launch_env(&mut env, &profile_launch);
|
|
213
244
|
apply_mcp_auto_approval_env(&mut env, &safety);
|
|
214
245
|
// Python providers.py:145 + launch/core.py:253 — fresh launch runs the worker
|
|
@@ -256,6 +287,31 @@ pub(super) fn spawn_agents(
|
|
|
256
287
|
);
|
|
257
288
|
}
|
|
258
289
|
}
|
|
290
|
+
// Cursor: role 经 .cursor/rules;MCP 身份必须写进 mcp.json env
|
|
291
|
+
// (不继承父进程 TEAM_AGENT_*)。--workspace 钉物理路径。
|
|
292
|
+
// mcp.json last-writer:同 workspace 第二 CursorAgent 拒绝。
|
|
293
|
+
if matches!(provider, Provider::CursorAgent) {
|
|
294
|
+
refuse_second_cursor_occupant(workspace, agent_id_raw, Some(spec))?;
|
|
295
|
+
apply_cursor_agent_rules_overlay(workspace, agent_id_raw, system_prompt.as_str())?;
|
|
296
|
+
apply_cursor_mcp_overlay(workspace, &mcp_config)?;
|
|
297
|
+
enable_cursor_workspace_mcp(workspace)?;
|
|
298
|
+
apply_cursor_workspace_physical_path(&mut plan.argv, workspace);
|
|
299
|
+
let proxy = apply_cursor_subscription_proxy_env(&mut env);
|
|
300
|
+
let event_log = crate::event_log::EventLog::new(workspace);
|
|
301
|
+
let _ = event_log.write(
|
|
302
|
+
"provider.cursor.proxy_presence",
|
|
303
|
+
serde_json::json!({
|
|
304
|
+
"agent_id": agent_id_raw,
|
|
305
|
+
"https_proxy": proxy.https_proxy,
|
|
306
|
+
"no_proxy": proxy.no_proxy,
|
|
307
|
+
}),
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
// Grok 无 --mcp-config;写 <cwd>/.grok/config.toml 让 CLI 读到 team-agent。
|
|
311
|
+
if matches!(provider, Provider::Grok) {
|
|
312
|
+
ensure_grok_login_and_folder_trust(workspace)?;
|
|
313
|
+
apply_grok_mcp_overlay(workspace, &mcp_config)?;
|
|
314
|
+
}
|
|
259
315
|
let spawn_epoch = u64::try_from(started.len()).unwrap_or(u64::MAX);
|
|
260
316
|
let spawned_at = spawn_timestamp_for_agent(u32::try_from(spawn_epoch).unwrap_or(u32::MAX));
|
|
261
317
|
// E6 层1 实证3 + 诊断留痕:落最终 worker argv(spawn 前的真实形态)。
|
|
@@ -401,6 +457,10 @@ pub(super) fn spawn_agents(
|
|
|
401
457
|
Ok(started)
|
|
402
458
|
}
|
|
403
459
|
|
|
460
|
+
/// ---
|
|
461
|
+
/// purpose: 尽力还原某席位角色文档的路径
|
|
462
|
+
/// returns: state 里有 team_dir 时用它下的 agents 目录,否则退到 workspace 下的当前团队目录
|
|
463
|
+
/// ---
|
|
404
464
|
/// 0.5.66 bypass 单源 §2.7:审计事件里角色 md 的 spec 来源路径(尽力解析,失败空串)。
|
|
405
465
|
pub(crate) fn agent_id_spec_source_path(workspace: &Path, agent_id: &str) -> String {
|
|
406
466
|
let team_dir = crate::state::persist::load_runtime_state(workspace)
|