@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,35 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: spec 的读取改写与 runtime state 初值物化,含 quick-start 的 owner 种入
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: initial_runtime_state
|
|
6
|
+
//! what: 由 spec 生成首份 runtime state 树,并种入 launched owner
|
|
7
|
+
//! - name: write_spec_atomic
|
|
8
|
+
//! what: 先写同目录临时文件再 rename,避免留下半截 spec
|
|
9
|
+
//! - name: spec_session_name
|
|
10
|
+
//! what: 取 runtime.session_name,缺省由 team 名派生
|
|
11
|
+
//! - name: spec_agents
|
|
12
|
+
//! what: 取 spec 里全部 agent id
|
|
13
|
+
//! - name: spec_routes
|
|
14
|
+
//! what: 对 spec 里每个 task 算出路由决策
|
|
15
|
+
//! - name: effective_runtime_config_for_worker_spawn
|
|
16
|
+
//! what: 由单个 agent 的 dangerously_skip_permissions 定出 bypass 审批结论
|
|
17
|
+
//! depends:
|
|
18
|
+
//! - crate::state::projection
|
|
19
|
+
//! - crate::state::identity
|
|
20
|
+
//! - crate::state::ownership
|
|
21
|
+
//! - crate::model::yaml
|
|
22
|
+
//! - crate::model::routing
|
|
23
|
+
//! - crate::provider::bypass_flags
|
|
24
|
+
//! - crate::lifecycle::launch::identity
|
|
25
|
+
//! - crate::lifecycle::launch::leader_context
|
|
26
|
+
//! - crate::lifecycle::launch::worker_env
|
|
27
|
+
//! boundary:
|
|
28
|
+
//! - 只生成与改写数据,不 spawn 进程也不开显示
|
|
29
|
+
//! - bypass 只认单个 agent 自己的声明,不从 runtime 配置或 leader argv 全队继承
|
|
30
|
+
//! - spec 写盘一律走原子替换,不做就地截断写
|
|
31
|
+
//! maturity: wired
|
|
32
|
+
//! ---
|
|
1
33
|
//!
|
|
2
34
|
//! unit-8 (Stage 3) — `lifecycle::launch::spec_state` phase boundary.
|
|
3
35
|
//!
|
|
@@ -40,6 +72,12 @@ use super::leader_context::{
|
|
|
40
72
|
};
|
|
41
73
|
use super::worker_env::spawn_timestamp;
|
|
42
74
|
|
|
75
|
+
/// ---
|
|
76
|
+
/// purpose: 由 spec 生成首份 runtime state 树
|
|
77
|
+
/// params:
|
|
78
|
+
/// team_key: 本团队的 runtime 键
|
|
79
|
+
/// returns: 含 spec_path、workspace、team_dir、session_name、leader、agents、tasks 与 display_backend 的 state;随后按环境种入 launched owner,种不到则种一份 unbound owner
|
|
80
|
+
/// ---
|
|
43
81
|
pub(super) fn initial_runtime_state(
|
|
44
82
|
spec: &Value,
|
|
45
83
|
spec_path: &Path,
|
|
@@ -113,6 +151,12 @@ pub(super) fn initial_runtime_state(
|
|
|
113
151
|
state
|
|
114
152
|
}
|
|
115
153
|
|
|
154
|
+
/// ---
|
|
155
|
+
/// purpose: 从环境里的 caller 身份种入 launched owner
|
|
156
|
+
/// params:
|
|
157
|
+
/// state: 待改写的 state,成功时写入 owner 与 leader receiver
|
|
158
|
+
/// returns: true 表示已种入,取不到 caller 身份或无 pane 时为 false
|
|
159
|
+
/// ---
|
|
116
160
|
pub(super) fn seed_launched_owner_from_env(state: &mut serde_json::Value) -> bool {
|
|
117
161
|
let team_id = crate::state::projection::team_state_key(state);
|
|
118
162
|
let Ok(caller) = crate::state::identity::caller_identity_from_env(
|
|
@@ -130,6 +174,13 @@ pub(super) fn seed_launched_owner_from_env(state: &mut serde_json::Value) -> boo
|
|
|
130
174
|
)
|
|
131
175
|
}
|
|
132
176
|
|
|
177
|
+
/// ---
|
|
178
|
+
/// purpose: 用给定的 caller 身份与 pane provider 查询函数种入 owner 与 leader receiver
|
|
179
|
+
/// params:
|
|
180
|
+
/// caller: 调用方身份,pane_id 为空则不种
|
|
181
|
+
/// lookup_pane_provider: 由 pane 反查 provider 的函数,便于测试替换
|
|
182
|
+
/// returns: true 表示已写入 owner 记录
|
|
183
|
+
/// ---
|
|
133
184
|
pub(super) fn seed_launched_owner_from_caller_with_provider_lookup(
|
|
134
185
|
state: &mut serde_json::Value,
|
|
135
186
|
caller: crate::state::owner_gate::CallerIdentity,
|
|
@@ -186,6 +237,10 @@ pub(super) fn seed_launched_owner_from_caller_with_provider_lookup(
|
|
|
186
237
|
true
|
|
187
238
|
}
|
|
188
239
|
|
|
240
|
+
/// ---
|
|
241
|
+
/// purpose: 判断环境里是否带有任一 leader 身份变量
|
|
242
|
+
/// returns: pane id、session uuid、其 override 或 provider 任一非空即 true
|
|
243
|
+
/// ---
|
|
189
244
|
pub(super) fn has_positive_caller_leader_env() -> bool {
|
|
190
245
|
env_nonempty("TEAM_AGENT_LEADER_PANE_ID")
|
|
191
246
|
|| env_nonempty("TEAM_AGENT_LEADER_SESSION_UUID")
|
|
@@ -193,12 +248,20 @@ pub(super) fn has_positive_caller_leader_env() -> bool {
|
|
|
193
248
|
|| env_nonempty("TEAM_AGENT_LEADER_PROVIDER")
|
|
194
249
|
}
|
|
195
250
|
|
|
251
|
+
/// ---
|
|
252
|
+
/// purpose: 判断某环境变量存在且非空
|
|
253
|
+
/// returns: 存在且非空为 true
|
|
254
|
+
/// ---
|
|
196
255
|
pub(super) fn env_nonempty(key: &str) -> bool {
|
|
197
256
|
std::env::var(key)
|
|
198
257
|
.ok()
|
|
199
258
|
.is_some_and(|value| !value.is_empty())
|
|
200
259
|
}
|
|
201
260
|
|
|
261
|
+
/// ---
|
|
262
|
+
/// purpose: 把 spec 的 tasks 列表转成 JSON 数组
|
|
263
|
+
/// returns: 转换后的数组,没有 tasks 时为空数组
|
|
264
|
+
/// ---
|
|
202
265
|
pub(super) fn spec_tasks_json(spec: &Value) -> serde_json::Value {
|
|
203
266
|
spec.get("tasks")
|
|
204
267
|
.and_then(Value::as_list)
|
|
@@ -206,6 +269,10 @@ pub(super) fn spec_tasks_json(spec: &Value) -> serde_json::Value {
|
|
|
206
269
|
.unwrap_or_else(|| serde_json::json!([]))
|
|
207
270
|
}
|
|
208
271
|
|
|
272
|
+
/// ---
|
|
273
|
+
/// purpose: 把 YAML 值递归转成等价 JSON 值
|
|
274
|
+
/// returns: 结构与标量一一对应的 JSON
|
|
275
|
+
/// ---
|
|
209
276
|
pub(super) fn yaml_value_to_json(value: &Value) -> serde_json::Value {
|
|
210
277
|
match value {
|
|
211
278
|
Value::Null => serde_json::Value::Null,
|
|
@@ -226,6 +293,11 @@ pub(super) fn yaml_value_to_json(value: &Value) -> serde_json::Value {
|
|
|
226
293
|
}
|
|
227
294
|
}
|
|
228
295
|
|
|
296
|
+
/// ---
|
|
297
|
+
/// purpose: 原子写 spec,先写带进程号后缀的临时文件再 rename 覆盖
|
|
298
|
+
/// returns: 成功返回空值
|
|
299
|
+
/// errors: 建父目录、写临时文件或 rename 失败时返回 StatePersist;rename 失败会删掉临时文件且原 spec 不动
|
|
300
|
+
/// ---
|
|
229
301
|
///
|
|
230
302
|
/// Set `runtime.session_name` on the compiled spec to `session_name`, creating the
|
|
231
303
|
/// `runtime` map and/or the `session_name` entry if absent. Used by quick-start to
|
|
@@ -252,10 +324,18 @@ pub(crate) fn write_spec_atomic(spec_path: &Path, spec: &Value) -> Result<(), Li
|
|
|
252
324
|
Ok(())
|
|
253
325
|
}
|
|
254
326
|
|
|
327
|
+
/// ---
|
|
328
|
+
/// purpose: 就地把 spec 的 runtime.session_name 改成给定值
|
|
329
|
+
/// ---
|
|
255
330
|
pub(crate) fn override_spec_session_name(spec: &mut Value, session_name: &str) {
|
|
256
331
|
override_spec_runtime_str(spec, "session_name", session_name);
|
|
257
332
|
}
|
|
258
333
|
|
|
334
|
+
/// ---
|
|
335
|
+
/// purpose: 就地把 spec 里 team.workspace 与各 agent 的 working_directory 改成给定路径
|
|
336
|
+
/// params:
|
|
337
|
+
/// spec: 非 Map 时不动;只改已存在的字段,不新建
|
|
338
|
+
/// ---
|
|
259
339
|
pub(crate) fn override_spec_workspace(spec: &mut Value, workspace: &Path) {
|
|
260
340
|
let workspace_s = workspace.to_string_lossy().to_string();
|
|
261
341
|
let Value::Map(root) = spec else { return };
|
|
@@ -276,10 +356,19 @@ pub(crate) fn override_spec_workspace(spec: &mut Value, workspace: &Path) {
|
|
|
276
356
|
}
|
|
277
357
|
}
|
|
278
358
|
|
|
359
|
+
/// ---
|
|
360
|
+
/// purpose: 就地把 spec 的 runtime.display_backend 改成给定值
|
|
361
|
+
/// ---
|
|
279
362
|
pub(super) fn override_spec_display_backend(spec: &mut Value, display_backend: &str) {
|
|
280
363
|
override_spec_runtime_str(spec, "display_backend", display_backend);
|
|
281
364
|
}
|
|
282
365
|
|
|
366
|
+
/// ---
|
|
367
|
+
/// purpose: 就地写 spec 的 runtime 段下某个字符串字段
|
|
368
|
+
/// params:
|
|
369
|
+
/// spec: 非 Map 时不动
|
|
370
|
+
/// returns: runtime 段缺失则新建,已存在同名键则覆盖,runtime 是非 Map 值则整段替换
|
|
371
|
+
/// ---
|
|
283
372
|
pub(super) fn override_spec_runtime_str(spec: &mut Value, key: &str, value: &str) {
|
|
284
373
|
let Value::Map(root) = spec else { return };
|
|
285
374
|
let runtime_slot = root
|
|
@@ -306,6 +395,10 @@ pub(super) fn override_spec_runtime_str(spec: &mut Value, key: &str, value: &str
|
|
|
306
395
|
}
|
|
307
396
|
}
|
|
308
397
|
|
|
398
|
+
/// ---
|
|
399
|
+
/// purpose: 定出本 spec 的 tmux session 名
|
|
400
|
+
/// returns: runtime.session_name 的非空值;缺失时由 team.name 派生,team 名也缺失时用 agent 兜底
|
|
401
|
+
/// ---
|
|
309
402
|
pub(super) fn spec_session_name(spec: &Value) -> SessionName {
|
|
310
403
|
if let Some(name) = spec
|
|
311
404
|
.get("runtime")
|
|
@@ -325,6 +418,10 @@ pub(super) fn spec_session_name(spec: &Value) -> SessionName {
|
|
|
325
418
|
SessionName::new(format!("team-{team_name}"))
|
|
326
419
|
}
|
|
327
420
|
|
|
421
|
+
/// ---
|
|
422
|
+
/// purpose: 把 spec_session_name 以更宽可见性转出,供 layout 复用同一份实现
|
|
423
|
+
/// returns: 同 spec_session_name
|
|
424
|
+
/// ---
|
|
328
425
|
///
|
|
329
426
|
/// 0.3.28 layout step 1: pub re-export of `spec_session_name` for the new
|
|
330
427
|
/// `layout::sessions::worker_session_name` to delegate to. Single underlying
|
|
@@ -333,6 +430,10 @@ pub fn worker_session_name_pub(spec: &Value) -> SessionName {
|
|
|
333
430
|
spec_session_name(spec)
|
|
334
431
|
}
|
|
335
432
|
|
|
433
|
+
/// ---
|
|
434
|
+
/// purpose: 取出 spec 里全部 agent id
|
|
435
|
+
/// returns: 按 spec 顺序的 agent id,无 id 字段的条目被跳过
|
|
436
|
+
/// ---
|
|
336
437
|
pub(super) fn spec_agents(spec: &Value) -> Vec<AgentId> {
|
|
337
438
|
spec_agent_values(spec)
|
|
338
439
|
.into_iter()
|
|
@@ -340,6 +441,10 @@ pub(super) fn spec_agents(spec: &Value) -> Vec<AgentId> {
|
|
|
340
441
|
.collect()
|
|
341
442
|
}
|
|
342
443
|
|
|
444
|
+
/// ---
|
|
445
|
+
/// purpose: 取出 spec 里全部 agent id 的集合,便于成员判定
|
|
446
|
+
/// returns: 有序集合
|
|
447
|
+
/// ---
|
|
343
448
|
///
|
|
344
449
|
/// Bug 1 (0.4.2): expose spec agent id set so the restart path can filter
|
|
345
450
|
/// state.agents to only the agents currently defined in the rebuilt spec.
|
|
@@ -351,6 +456,10 @@ pub(crate) fn spec_agent_id_set(spec: &Value) -> std::collections::BTreeSet<Stri
|
|
|
351
456
|
.collect()
|
|
352
457
|
}
|
|
353
458
|
|
|
459
|
+
/// ---
|
|
460
|
+
/// purpose: 取出 spec 的 agents 列表原始节点
|
|
461
|
+
/// returns: 节点引用列表,字段缺失或类型不对时为空
|
|
462
|
+
/// ---
|
|
354
463
|
pub(super) fn spec_agent_values(spec: &Value) -> Vec<&Value> {
|
|
355
464
|
spec.get("agents")
|
|
356
465
|
.and_then(Value::as_list)
|
|
@@ -358,6 +467,10 @@ pub(super) fn spec_agent_values(spec: &Value) -> Vec<&Value> {
|
|
|
358
467
|
.unwrap_or_default()
|
|
359
468
|
}
|
|
360
469
|
|
|
470
|
+
/// ---
|
|
471
|
+
/// purpose: 对 spec 里每个 task 算出路由决策
|
|
472
|
+
/// returns: 每个 task 的选中 agent 与理由,manual_override 恒为 false
|
|
473
|
+
/// ---
|
|
361
474
|
pub(super) fn spec_routes(spec: &Value) -> Vec<RoutingDecision> {
|
|
362
475
|
spec.get("tasks")
|
|
363
476
|
.and_then(Value::as_list)
|
|
@@ -378,6 +491,10 @@ pub(super) fn spec_routes(spec: &Value) -> Vec<RoutingDecision> {
|
|
|
378
491
|
.unwrap_or_default()
|
|
379
492
|
}
|
|
380
493
|
|
|
494
|
+
/// ---
|
|
495
|
+
/// purpose: 定出默认承接人
|
|
496
|
+
/// returns: routing.default_assignee,缺失时取第一个 agent
|
|
497
|
+
/// ---
|
|
381
498
|
pub(super) fn spec_default_assignee(spec: &Value) -> Option<AgentId> {
|
|
382
499
|
spec.get("routing")
|
|
383
500
|
.and_then(|v| v.get("default_assignee"))
|
|
@@ -386,6 +503,12 @@ pub(super) fn spec_default_assignee(spec: &Value) -> Option<AgentId> {
|
|
|
386
503
|
.or_else(|| spec_agents(spec).into_iter().next())
|
|
387
504
|
}
|
|
388
505
|
|
|
506
|
+
/// ---
|
|
507
|
+
/// purpose: 由单个 YAML agent 的 dangerously_skip_permissions 定出 bypass 审批结论
|
|
508
|
+
/// returns: 字段为 true 时 enabled 且来源记 RuntimeConfig,并带上该 provider 的 bypass argv;否则 Disabled
|
|
509
|
+
/// errors: 当前实现不产生 Err
|
|
510
|
+
/// contract_id: lifecycle.spec_state.effective_runtime_config
|
|
511
|
+
/// ---
|
|
389
512
|
/// 0.5.66 bypass 单源:从**单个 agent**(yaml spec)的 `dangerously_skip_permissions` 构造
|
|
390
513
|
/// `DangerousApproval`(取代旧"runtime config + leader argv"全队一份)。
|
|
391
514
|
/// true → source=RuntimeConfig(角色声明即用户明确同意,coordinator 的
|
|
@@ -423,6 +546,12 @@ pub(crate) fn effective_runtime_config_for_worker_spawn(
|
|
|
423
546
|
})
|
|
424
547
|
}
|
|
425
548
|
|
|
549
|
+
/// ---
|
|
550
|
+
/// purpose: 同上,但读的是 runtime state 里的 JSON agent 节点
|
|
551
|
+
/// returns: 同 YAML 版
|
|
552
|
+
/// errors: 当前实现不产生 Err
|
|
553
|
+
/// contract_id: lifecycle.spec_state.effective_runtime_config
|
|
554
|
+
/// ---
|
|
426
555
|
/// 0.5.66 bypass 单源:restart/state 路径的 serde_json 版(agent 来自 state JSON)。
|
|
427
556
|
pub(crate) fn effective_runtime_config_for_worker_spawn_json(
|
|
428
557
|
agent: &serde_json::Value,
|
|
@@ -457,6 +586,10 @@ pub(crate) fn effective_runtime_config_for_worker_spawn_json(
|
|
|
457
586
|
})
|
|
458
587
|
}
|
|
459
588
|
|
|
589
|
+
/// ---
|
|
590
|
+
/// purpose: 给出 provider 的稳定 wire 名
|
|
591
|
+
/// returns: 与 spec 写法一致的小写下划线名
|
|
592
|
+
/// ---
|
|
460
593
|
pub(crate) fn provider_display_str(provider: Provider) -> &'static str {
|
|
461
594
|
match provider {
|
|
462
595
|
Provider::Claude => "claude",
|
|
@@ -464,10 +597,16 @@ pub(crate) fn provider_display_str(provider: Provider) -> &'static str {
|
|
|
464
597
|
Provider::Codex => "codex",
|
|
465
598
|
Provider::Copilot => "copilot",
|
|
466
599
|
Provider::GeminiCli => "gemini_cli",
|
|
600
|
+
Provider::Grok => "grok",
|
|
601
|
+
Provider::CursorAgent => "cursor_agent",
|
|
467
602
|
Provider::Fake => "fake",
|
|
468
603
|
}
|
|
469
604
|
}
|
|
470
605
|
|
|
606
|
+
/// ---
|
|
607
|
+
/// purpose: 由 team 目录推出 workspace 根
|
|
608
|
+
/// returns: 解析成功用解析值,失败时退到 team 目录的父目录,没有父目录则用 team 目录本身
|
|
609
|
+
/// ---
|
|
471
610
|
pub(super) fn team_workspace(team_dir: &Path) -> PathBuf {
|
|
472
611
|
crate::model::paths::team_workspace(team_dir).unwrap_or_else(|_| {
|
|
473
612
|
team_dir
|
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 冷启后把席位实况投影进 runtime state,并做团队级合并、投影与 owner 清洗
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: persist_spawn_agent_state
|
|
6
|
+
//! what: 按 spawn 结果与 tmux 实况写出各席位状态,分 running/paused/spawn_failed
|
|
7
|
+
//! - name: save_launched_team_state_for_key
|
|
8
|
+
//! what: 把本次团队状态并入既有 state,投影后经 repository 落盘
|
|
9
|
+
//! - name: merge_workspace_team_state_with_key
|
|
10
|
+
//! what: 以显式团队键把本次状态并进 teams 表,不丢既有团队
|
|
11
|
+
//! - name: drop_worker_pane_seeded_owner
|
|
12
|
+
//! what: 识别出被 worker pane 误种的 owner 并改种 unbound owner
|
|
13
|
+
//! depends:
|
|
14
|
+
//! - crate::state::repository
|
|
15
|
+
//! - crate::state::projection
|
|
16
|
+
//! - crate::state::persist
|
|
17
|
+
//! - crate::state::ownership
|
|
18
|
+
//! - crate::tmux_backend
|
|
19
|
+
//! - crate::lifecycle::launch::agent_state
|
|
20
|
+
//! - crate::lifecycle::launch::identity
|
|
21
|
+
//! - crate::lifecycle::launch::leader_context
|
|
22
|
+
//! - crate::lifecycle::launch::spec_state
|
|
23
|
+
//! - crate::lifecycle::launch::worker_env
|
|
24
|
+
//! boundary:
|
|
25
|
+
//! - 只写 state,不 spawn、不 kill、不开显示
|
|
26
|
+
//! - 落盘一律经 StateRepository 带写意图,不直接改文件
|
|
27
|
+
//! - owner 归属判不准时倒向 unbound,不认领他队的 pane
|
|
28
|
+
//! maturity: wired
|
|
29
|
+
//! ---
|
|
1
30
|
//!
|
|
2
31
|
use std::collections::{BTreeMap, BTreeSet};
|
|
3
32
|
use std::path::{Path, PathBuf};
|
|
@@ -21,6 +50,14 @@ use super::leader_context::{
|
|
|
21
50
|
use super::spec_state::{has_positive_caller_leader_env, spec_agent_values};
|
|
22
51
|
use super::worker_env::{agent_is_paused, spawn_timestamp_for_agent};
|
|
23
52
|
|
|
53
|
+
/// ---
|
|
54
|
+
/// purpose: 把本次 spawn 的结果写成 runtime state 里的各席位状态
|
|
55
|
+
/// params:
|
|
56
|
+
/// started: 本次 spawn 成功返回的席位
|
|
57
|
+
/// transport: 用于查活窗口与 pane pid
|
|
58
|
+
/// returns: 成功返回空值。spec 里 paused 的席位记 paused,spawn 后窗口不在的记 spawn_failed,其余记 running
|
|
59
|
+
/// errors: 读或写 runtime state 失败时返回 StatePersist
|
|
60
|
+
/// ---
|
|
24
61
|
pub(super) fn persist_spawn_agent_state(
|
|
25
62
|
workspace: &Path,
|
|
26
63
|
spec_path: &Path,
|
|
@@ -148,6 +185,10 @@ pub(super) fn persist_spawn_agent_state(
|
|
|
148
185
|
save_launched_team_state_for_key(workspace, &state, Some(&team_id), None)
|
|
149
186
|
}
|
|
150
187
|
|
|
188
|
+
/// ---
|
|
189
|
+
/// purpose: 给出各已起席位对应 pane 的进程号
|
|
190
|
+
/// returns: agent_id 到 pane pid 的映射,查不到的席位不出现
|
|
191
|
+
/// ---
|
|
151
192
|
pub(super) fn pane_pids_by_started_agent(
|
|
152
193
|
transport: &dyn Transport,
|
|
153
194
|
started: &[StartedAgent],
|
|
@@ -165,6 +206,10 @@ pub(super) fn pane_pids_by_started_agent(
|
|
|
165
206
|
.collect()
|
|
166
207
|
}
|
|
167
208
|
|
|
209
|
+
/// ---
|
|
210
|
+
/// purpose: 由 agent id 找出它这次 spawn 的目标 pane
|
|
211
|
+
/// returns: 命中的 pane 串,找不到时为空串
|
|
212
|
+
/// ---
|
|
168
213
|
pub(super) fn agent_id_to_pane_id<'a>(started: &'a [StartedAgent], agent_id: &str) -> &'a str {
|
|
169
214
|
started
|
|
170
215
|
.iter()
|
|
@@ -173,6 +218,12 @@ pub(super) fn agent_id_to_pane_id<'a>(started: &'a [StartedAgent], agent_id: &st
|
|
|
173
218
|
.unwrap_or("")
|
|
174
219
|
}
|
|
175
220
|
|
|
221
|
+
/// ---
|
|
222
|
+
/// purpose: 不带显式团队键地保存本次团队状态
|
|
223
|
+
/// returns: 成功返回空值
|
|
224
|
+
/// errors: 落盘失败时返回 StatePersist
|
|
225
|
+
/// contract_id: lifecycle.state_projection.save_launched_team_state
|
|
226
|
+
/// ---
|
|
176
227
|
pub(super) fn save_launched_team_state(
|
|
177
228
|
workspace: &Path,
|
|
178
229
|
launched: &serde_json::Value,
|
|
@@ -180,6 +231,15 @@ pub(super) fn save_launched_team_state(
|
|
|
180
231
|
save_launched_team_state_for_key(workspace, launched, None, None)
|
|
181
232
|
}
|
|
182
233
|
|
|
234
|
+
/// ---
|
|
235
|
+
/// purpose: 保存本次团队状态的实体实现,含团队键钉死、owner 清洗、合并与投影
|
|
236
|
+
/// params:
|
|
237
|
+
/// team_key: 显式团队键,为空则由 state 推算
|
|
238
|
+
/// added_agent_id: 本次新增席位 id,写进写意图
|
|
239
|
+
/// returns: 成功返回空值
|
|
240
|
+
/// errors: 落盘失败时返回 StatePersist
|
|
241
|
+
/// contract_id: lifecycle.state_projection.save_launched_team_state
|
|
242
|
+
/// ---
|
|
183
243
|
pub(super) fn save_launched_team_state_for_key(
|
|
184
244
|
workspace: &Path,
|
|
185
245
|
launched: &serde_json::Value,
|
|
@@ -240,6 +300,11 @@ pub(super) fn save_launched_team_state_for_key(
|
|
|
240
300
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))
|
|
241
301
|
}
|
|
242
302
|
|
|
303
|
+
/// ---
|
|
304
|
+
/// purpose: 本次状态没写 leader 拓扑字段时,从既有状态里补回来
|
|
305
|
+
/// params:
|
|
306
|
+
/// launched: 就地补 is_external_leader 与 leader_client,已有同名键不覆盖
|
|
307
|
+
/// ---
|
|
243
308
|
pub(super) fn preserve_existing_leader_topology(
|
|
244
309
|
existing: &serde_json::Value,
|
|
245
310
|
launched_key: &str,
|
|
@@ -262,6 +327,11 @@ pub(super) fn preserve_existing_leader_topology(
|
|
|
262
327
|
}
|
|
263
328
|
}
|
|
264
329
|
|
|
330
|
+
/// ---
|
|
331
|
+
/// purpose: 无 leader 身份环境且 owner pane 形如首席 pane 时改种 unbound owner
|
|
332
|
+
/// params:
|
|
333
|
+
/// launched: 就地改写
|
|
334
|
+
/// ---
|
|
265
335
|
pub(super) fn drop_bare_worker_seeded_owner(launched: &mut serde_json::Value, launched_key: &str) {
|
|
266
336
|
if has_positive_caller_leader_env() {
|
|
267
337
|
return;
|
|
@@ -276,6 +346,10 @@ pub(super) fn drop_bare_worker_seeded_owner(launched: &mut serde_json::Value, la
|
|
|
276
346
|
}
|
|
277
347
|
}
|
|
278
348
|
|
|
349
|
+
/// ---
|
|
350
|
+
/// purpose: 以显式团队键把本次状态并进 teams 表
|
|
351
|
+
/// returns: 合并后的 state;既有顶层 session 名为空或团队键相同时保留既有 teams 并覆盖本键,否则以既有状态为底再补进本次团队
|
|
352
|
+
/// ---
|
|
279
353
|
pub(super) fn merge_workspace_team_state_with_key(
|
|
280
354
|
existing: &serde_json::Value,
|
|
281
355
|
launched: &serde_json::Value,
|
|
@@ -360,6 +434,11 @@ mod merge_workspace_team_state_with_key_tests {
|
|
|
360
434
|
}
|
|
361
435
|
}
|
|
362
436
|
|
|
437
|
+
/// ---
|
|
438
|
+
/// purpose: 把 teams 表里本团队的绑定字段提到顶层
|
|
439
|
+
/// params:
|
|
440
|
+
/// launched: 就地补 leader_receiver、team_owner 与 owner_epoch,已有同名键不覆盖
|
|
441
|
+
/// ---
|
|
363
442
|
pub(super) fn promote_launched_binding_from_team_entry(
|
|
364
443
|
launched: &mut serde_json::Value,
|
|
365
444
|
launched_key: &str,
|
|
@@ -383,6 +462,11 @@ pub(super) fn promote_launched_binding_from_team_entry(
|
|
|
383
462
|
}
|
|
384
463
|
}
|
|
385
464
|
|
|
465
|
+
/// ---
|
|
466
|
+
/// purpose: 顶层 owner 的 pane 不像真实 pane 时把顶层绑定字段整组删掉
|
|
467
|
+
/// params:
|
|
468
|
+
/// state: 就地删 leader_receiver、team_owner 与 owner_epoch
|
|
469
|
+
/// ---
|
|
386
470
|
pub(super) fn drop_unbound_top_level_owner(state: &mut serde_json::Value) {
|
|
387
471
|
let pane = state
|
|
388
472
|
.get("team_owner")
|
|
@@ -399,6 +483,12 @@ pub(super) fn drop_unbound_top_level_owner(state: &mut serde_json::Value) {
|
|
|
399
483
|
}
|
|
400
484
|
}
|
|
401
485
|
|
|
486
|
+
/// ---
|
|
487
|
+
/// purpose: 本次 owner 的 pane 已属于别的团队时,改种 unbound owner 或整组删除
|
|
488
|
+
/// params:
|
|
489
|
+
/// existing: 既有 state,用于判断 pane 归属
|
|
490
|
+
/// launched: 就地改写
|
|
491
|
+
/// ---
|
|
402
492
|
pub(super) fn drop_foreign_seeded_owner(
|
|
403
493
|
existing: &serde_json::Value,
|
|
404
494
|
launched_key: &str,
|
|
@@ -434,6 +524,13 @@ pub(super) fn drop_foreign_seeded_owner(
|
|
|
434
524
|
}
|
|
435
525
|
}
|
|
436
526
|
|
|
527
|
+
/// ---
|
|
528
|
+
/// purpose: 识别 owner 是被 worker 自己的 pane 误种的情形并改种 unbound owner
|
|
529
|
+
/// params:
|
|
530
|
+
/// started: 本次起的席位,用于判断 pane 是否属于 worker
|
|
531
|
+
/// worker_tmux_socket: worker 侧 socket 名,与调用方 socket 比对
|
|
532
|
+
/// returns: 只有在无 leader 身份环境、pane 与调用方 TMUX_PANE 相同且看起来是 worker pane 时才改种
|
|
533
|
+
/// ---
|
|
437
534
|
pub(super) fn drop_worker_pane_seeded_owner(
|
|
438
535
|
launched: &mut serde_json::Value,
|
|
439
536
|
launched_key: &str,
|
|
@@ -466,6 +563,10 @@ pub(super) fn drop_worker_pane_seeded_owner(
|
|
|
466
563
|
}
|
|
467
564
|
}
|
|
468
565
|
|
|
566
|
+
/// ---
|
|
567
|
+
/// purpose: 判断某 pane 是否像本次起的 worker 的 pane
|
|
568
|
+
/// returns: 形如首席 pane,或与任一席位目标互为前缀时为 true
|
|
569
|
+
/// ---
|
|
469
570
|
pub(super) fn seeded_pane_looks_like_worker(pane: &str, started: &[StartedAgent]) -> bool {
|
|
470
571
|
pane.ends_with("-first")
|
|
471
572
|
|| started.iter().any(|agent| {
|
|
@@ -475,6 +576,10 @@ pub(super) fn seeded_pane_looks_like_worker(pane: &str, started: &[StartedAgent]
|
|
|
475
576
|
})
|
|
476
577
|
}
|
|
477
578
|
|
|
579
|
+
/// ---
|
|
580
|
+
/// purpose: 给出 worker 侧使用的 tmux socket 名
|
|
581
|
+
/// returns: transport 是 tmux 后端时按 workspace 推出 socket 名,否则 None
|
|
582
|
+
/// ---
|
|
478
583
|
pub(super) fn launched_worker_tmux_socket(
|
|
479
584
|
transport: &dyn Transport,
|
|
480
585
|
workspace: &Path,
|
|
@@ -486,6 +591,10 @@ pub(super) fn launched_worker_tmux_socket(
|
|
|
486
591
|
}
|
|
487
592
|
}
|
|
488
593
|
|
|
594
|
+
/// ---
|
|
595
|
+
/// purpose: 判断两侧 socket 是否一致或不可判
|
|
596
|
+
/// returns: 两侧都已知时比相等;调用方已知而 worker 未知时为 false;调用方未知时一律为 true
|
|
597
|
+
/// ---
|
|
489
598
|
pub(super) fn tmux_sockets_match_or_unknown(
|
|
490
599
|
caller_socket: Option<&str>,
|
|
491
600
|
worker_socket: Option<&str>,
|