@team-agent/installer 0.5.65 → 0.5.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +8 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +6 -0
- package/crates/team-agent/src/cli/adapters.rs +6 -1
- package/crates/team-agent/src/cli/diagnose.rs +82 -0
- package/crates/team-agent/src/cli/emit.rs +3 -3
- package/crates/team-agent/src/cli/grok_slot.rs +299 -0
- package/crates/team-agent/src/cli/leader.rs +21 -7
- package/crates/team-agent/src/cli/leaders.rs +125 -1
- package/crates/team-agent/src/cli/mod.rs +20 -0
- package/crates/team-agent/src/cli/send/presentation.rs +7 -1
- package/crates/team-agent/src/cli/spec.rs +2 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
- package/crates/team-agent/src/cli/tests/base.rs +26 -3
- package/crates/team-agent/src/cli/tests/compile.rs +1 -1
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
- package/crates/team-agent/src/cli/tests/mod.rs +2 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
- package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
- package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
- package/crates/team-agent/src/communication_mode/mod.rs +6 -4
- package/crates/team-agent/src/compiler/tests.rs +23 -20
- package/crates/team-agent/src/compiler.rs +84 -5
- package/crates/team-agent/src/conpty/backend.rs +16 -0
- package/crates/team-agent/src/coordinator/backoff.rs +55 -0
- package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
- package/crates/team-agent/src/coordinator/health.rs +173 -0
- package/crates/team-agent/src/coordinator/mod.rs +31 -0
- package/crates/team-agent/src/coordinator/orphan.rs +31 -0
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
- package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
- package/crates/team-agent/src/coordinator/tick.rs +133 -22
- package/crates/team-agent/src/coordinator/types.rs +49 -0
- package/crates/team-agent/src/db/message_store.rs +39 -5
- package/crates/team-agent/src/layout/worker_env.rs +20 -3
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
- package/crates/team-agent/src/leader/registry.rs +65 -0
- package/crates/team-agent/src/leader/start.rs +1080 -58
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/display.rs +56 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
- package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
- package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
- package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
- package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
- package/crates/team-agent/src/lifecycle/launch.rs +209 -43
- package/crates/team-agent/src/lifecycle/lock.rs +42 -1
- package/crates/team-agent/src/lifecycle/mod.rs +11 -0
- package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
- package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
- package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
- package/crates/team-agent/src/lifecycle/restart.rs +29 -0
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
- package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
- package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
- package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
- package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
- package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
- package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
- package/crates/team-agent/src/lifecycle/tests.rs +23 -15
- package/crates/team-agent/src/lifecycle/types.rs +61 -2
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
- package/crates/team-agent/src/mcp_server/tests.rs +196 -2
- package/crates/team-agent/src/messaging/delivery.rs +405 -40
- package/crates/team-agent/src/messaging/helpers.rs +2 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
- package/crates/team-agent/src/messaging/results.rs +4 -0
- package/crates/team-agent/src/messaging/send.rs +34 -0
- package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
- package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/types.rs +4 -1
- package/crates/team-agent/src/model/enums.rs +14 -5
- package/crates/team-agent/src/model/permissions.rs +3 -0
- package/crates/team-agent/src/model/spec.rs +129 -6
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
- package/crates/team-agent/src/os_probe.rs +73 -2
- package/crates/team-agent/src/provider/adapter.rs +231 -4
- package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
- package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
- package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
- package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
- package/crates/team-agent/src/provider/classify.rs +4 -2
- package/crates/team-agent/src/provider/faults.rs +2 -1
- package/crates/team-agent/src/provider/mod.rs +13 -0
- package/crates/team-agent/src/provider/session/capture.rs +158 -20
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
- package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
- package/crates/team-agent/src/provider/session/mod.rs +18 -0
- package/crates/team-agent/src/provider/session/resume.rs +57 -0
- package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
- package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
- package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
- package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
- package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
- package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
- package/crates/team-agent/src/provider/session_scan.rs +8 -0
- package/crates/team-agent/src/provider/submit_now.rs +94 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
- package/crates/team-agent/src/provider/types.rs +4 -2
- package/crates/team-agent/src/provider/wire.rs +23 -1
- package/crates/team-agent/src/state/persist.rs +301 -8
- package/crates/team-agent/src/state/repository.rs +5 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
- package/crates/team-agent/src/tmux_backend.rs +1083 -75
- package/crates/team-agent/src/transport/test_support.rs +20 -0
- package/crates/team-agent/src/transport/tests/wire.rs +28 -2
- package/crates/team-agent/src/transport.rs +265 -1
- package/npm/install.mjs +129 -73
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +33 -238
- package/skills/team-agent/command-coverage.json +31 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
|
@@ -1,3 +1,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,12 +41,19 @@ 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,
|
|
28
55
|
spec: &Value,
|
|
29
56
|
session_name: &SessionName,
|
|
30
|
-
safety: &DangerousApproval,
|
|
31
57
|
transport: &dyn Transport,
|
|
32
58
|
) -> Result<Vec<StartedAgent>, LifecycleError> {
|
|
33
59
|
// E5 解耦:team_dir(角色定义 + profiles 所在)≠ spec_path.parent()(spec 已迁出到 .team/runtime)。
|
|
@@ -100,9 +126,10 @@ pub(super) fn spawn_agents(
|
|
|
100
126
|
let tools = crate::lifecycle::worker_command_context::resolved_tool_strings_for_command(
|
|
101
127
|
&command_agent,
|
|
102
128
|
provider,
|
|
103
|
-
safety,
|
|
104
129
|
)?;
|
|
105
130
|
let resolved_tool_refs: Vec<&str> = tools.iter().map(String::as_str).collect();
|
|
131
|
+
// 0.5.66 bypass 单源:per-agent safety 只服务 env 注入与审计,不参与 argv 决策。
|
|
132
|
+
let safety = effective_runtime_config_for_worker_spawn(agent, provider)?;
|
|
106
133
|
let mcp_team_id = runtime_team_key_for_spec(spec_path, spec, session_name);
|
|
107
134
|
let mcp_config = adapter
|
|
108
135
|
.mcp_config(auth_mode)
|
|
@@ -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 前的真实形态)。
|
|
@@ -283,6 +339,25 @@ pub(super) fn spawn_agents(
|
|
|
283
339
|
"spawn_epoch": spawn_epoch,
|
|
284
340
|
})),
|
|
285
341
|
);
|
|
342
|
+
// 0.5.66 bypass 单源 §2.7:最终 argv 含 bypass flag → 审计留痕。
|
|
343
|
+
if matches!(
|
|
344
|
+
agent.get("dangerously_skip_permissions"),
|
|
345
|
+
Some(Value::Bool(true))
|
|
346
|
+
) {
|
|
347
|
+
let _ = event_log.write(
|
|
348
|
+
"worker.spawn_dangerously_skip_permissions",
|
|
349
|
+
serde_json::json!({
|
|
350
|
+
"agent_id": agent_id_raw,
|
|
351
|
+
"role": agent
|
|
352
|
+
.get("role")
|
|
353
|
+
.and_then(Value::as_str)
|
|
354
|
+
.unwrap_or_default(),
|
|
355
|
+
"provider": provider,
|
|
356
|
+
"trigger": "launch",
|
|
357
|
+
"spec_source_path": agent_id_spec_source_path(workspace, agent_id_raw),
|
|
358
|
+
}),
|
|
359
|
+
);
|
|
360
|
+
}
|
|
286
361
|
}
|
|
287
362
|
// 0.3.28 Step 4b: replaced the `adaptive_layout_plan` 3-pane tiling
|
|
288
363
|
// with Python-parity 1-window-per-agent placement. Window name =
|
|
@@ -381,3 +456,25 @@ pub(super) fn spawn_agents(
|
|
|
381
456
|
}
|
|
382
457
|
Ok(started)
|
|
383
458
|
}
|
|
459
|
+
|
|
460
|
+
/// ---
|
|
461
|
+
/// purpose: 尽力还原某席位角色文档的路径
|
|
462
|
+
/// returns: state 里有 team_dir 时用它下的 agents 目录,否则退到 workspace 下的当前团队目录
|
|
463
|
+
/// ---
|
|
464
|
+
/// 0.5.66 bypass 单源 §2.7:审计事件里角色 md 的 spec 来源路径(尽力解析,失败空串)。
|
|
465
|
+
pub(crate) fn agent_id_spec_source_path(workspace: &Path, agent_id: &str) -> String {
|
|
466
|
+
let team_dir = crate::state::persist::load_runtime_state(workspace)
|
|
467
|
+
.ok()
|
|
468
|
+
.and_then(|state| {
|
|
469
|
+
state
|
|
470
|
+
.get("team_dir")
|
|
471
|
+
.and_then(serde_json::Value::as_str)
|
|
472
|
+
.filter(|s| !s.is_empty())
|
|
473
|
+
.map(PathBuf::from)
|
|
474
|
+
});
|
|
475
|
+
team_dir
|
|
476
|
+
.map(|dir| dir.join("agents").join(format!("{agent_id}.md")))
|
|
477
|
+
.unwrap_or_else(|| workspace.join(".team/current/agents").join(format!("{agent_id}.md")))
|
|
478
|
+
.display()
|
|
479
|
+
.to_string()
|
|
480
|
+
}
|
|
@@ -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
|
//!
|
|
@@ -33,7 +65,6 @@ use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
|
|
|
33
65
|
|
|
34
66
|
use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest};
|
|
35
67
|
|
|
36
|
-
use super::approval::detect_dangerous_approval;
|
|
37
68
|
use super::identity::spec_display_backend;
|
|
38
69
|
use super::leader_context::{
|
|
39
70
|
attributed_provider_for_pane_across_tmux_sockets, caller_provider_for_seed_with_lookup,
|
|
@@ -41,6 +72,12 @@ use super::leader_context::{
|
|
|
41
72
|
};
|
|
42
73
|
use super::worker_env::spawn_timestamp;
|
|
43
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
|
+
/// ---
|
|
44
81
|
pub(super) fn initial_runtime_state(
|
|
45
82
|
spec: &Value,
|
|
46
83
|
spec_path: &Path,
|
|
@@ -114,6 +151,12 @@ pub(super) fn initial_runtime_state(
|
|
|
114
151
|
state
|
|
115
152
|
}
|
|
116
153
|
|
|
154
|
+
/// ---
|
|
155
|
+
/// purpose: 从环境里的 caller 身份种入 launched owner
|
|
156
|
+
/// params:
|
|
157
|
+
/// state: 待改写的 state,成功时写入 owner 与 leader receiver
|
|
158
|
+
/// returns: true 表示已种入,取不到 caller 身份或无 pane 时为 false
|
|
159
|
+
/// ---
|
|
117
160
|
pub(super) fn seed_launched_owner_from_env(state: &mut serde_json::Value) -> bool {
|
|
118
161
|
let team_id = crate::state::projection::team_state_key(state);
|
|
119
162
|
let Ok(caller) = crate::state::identity::caller_identity_from_env(
|
|
@@ -131,6 +174,13 @@ pub(super) fn seed_launched_owner_from_env(state: &mut serde_json::Value) -> boo
|
|
|
131
174
|
)
|
|
132
175
|
}
|
|
133
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
|
+
/// ---
|
|
134
184
|
pub(super) fn seed_launched_owner_from_caller_with_provider_lookup(
|
|
135
185
|
state: &mut serde_json::Value,
|
|
136
186
|
caller: crate::state::owner_gate::CallerIdentity,
|
|
@@ -187,6 +237,10 @@ pub(super) fn seed_launched_owner_from_caller_with_provider_lookup(
|
|
|
187
237
|
true
|
|
188
238
|
}
|
|
189
239
|
|
|
240
|
+
/// ---
|
|
241
|
+
/// purpose: 判断环境里是否带有任一 leader 身份变量
|
|
242
|
+
/// returns: pane id、session uuid、其 override 或 provider 任一非空即 true
|
|
243
|
+
/// ---
|
|
190
244
|
pub(super) fn has_positive_caller_leader_env() -> bool {
|
|
191
245
|
env_nonempty("TEAM_AGENT_LEADER_PANE_ID")
|
|
192
246
|
|| env_nonempty("TEAM_AGENT_LEADER_SESSION_UUID")
|
|
@@ -194,12 +248,20 @@ pub(super) fn has_positive_caller_leader_env() -> bool {
|
|
|
194
248
|
|| env_nonempty("TEAM_AGENT_LEADER_PROVIDER")
|
|
195
249
|
}
|
|
196
250
|
|
|
251
|
+
/// ---
|
|
252
|
+
/// purpose: 判断某环境变量存在且非空
|
|
253
|
+
/// returns: 存在且非空为 true
|
|
254
|
+
/// ---
|
|
197
255
|
pub(super) fn env_nonempty(key: &str) -> bool {
|
|
198
256
|
std::env::var(key)
|
|
199
257
|
.ok()
|
|
200
258
|
.is_some_and(|value| !value.is_empty())
|
|
201
259
|
}
|
|
202
260
|
|
|
261
|
+
/// ---
|
|
262
|
+
/// purpose: 把 spec 的 tasks 列表转成 JSON 数组
|
|
263
|
+
/// returns: 转换后的数组,没有 tasks 时为空数组
|
|
264
|
+
/// ---
|
|
203
265
|
pub(super) fn spec_tasks_json(spec: &Value) -> serde_json::Value {
|
|
204
266
|
spec.get("tasks")
|
|
205
267
|
.and_then(Value::as_list)
|
|
@@ -207,6 +269,10 @@ pub(super) fn spec_tasks_json(spec: &Value) -> serde_json::Value {
|
|
|
207
269
|
.unwrap_or_else(|| serde_json::json!([]))
|
|
208
270
|
}
|
|
209
271
|
|
|
272
|
+
/// ---
|
|
273
|
+
/// purpose: 把 YAML 值递归转成等价 JSON 值
|
|
274
|
+
/// returns: 结构与标量一一对应的 JSON
|
|
275
|
+
/// ---
|
|
210
276
|
pub(super) fn yaml_value_to_json(value: &Value) -> serde_json::Value {
|
|
211
277
|
match value {
|
|
212
278
|
Value::Null => serde_json::Value::Null,
|
|
@@ -227,6 +293,11 @@ pub(super) fn yaml_value_to_json(value: &Value) -> serde_json::Value {
|
|
|
227
293
|
}
|
|
228
294
|
}
|
|
229
295
|
|
|
296
|
+
/// ---
|
|
297
|
+
/// purpose: 原子写 spec,先写带进程号后缀的临时文件再 rename 覆盖
|
|
298
|
+
/// returns: 成功返回空值
|
|
299
|
+
/// errors: 建父目录、写临时文件或 rename 失败时返回 StatePersist;rename 失败会删掉临时文件且原 spec 不动
|
|
300
|
+
/// ---
|
|
230
301
|
///
|
|
231
302
|
/// Set `runtime.session_name` on the compiled spec to `session_name`, creating the
|
|
232
303
|
/// `runtime` map and/or the `session_name` entry if absent. Used by quick-start to
|
|
@@ -253,10 +324,18 @@ pub(crate) fn write_spec_atomic(spec_path: &Path, spec: &Value) -> Result<(), Li
|
|
|
253
324
|
Ok(())
|
|
254
325
|
}
|
|
255
326
|
|
|
327
|
+
/// ---
|
|
328
|
+
/// purpose: 就地把 spec 的 runtime.session_name 改成给定值
|
|
329
|
+
/// ---
|
|
256
330
|
pub(crate) fn override_spec_session_name(spec: &mut Value, session_name: &str) {
|
|
257
331
|
override_spec_runtime_str(spec, "session_name", session_name);
|
|
258
332
|
}
|
|
259
333
|
|
|
334
|
+
/// ---
|
|
335
|
+
/// purpose: 就地把 spec 里 team.workspace 与各 agent 的 working_directory 改成给定路径
|
|
336
|
+
/// params:
|
|
337
|
+
/// spec: 非 Map 时不动;只改已存在的字段,不新建
|
|
338
|
+
/// ---
|
|
260
339
|
pub(crate) fn override_spec_workspace(spec: &mut Value, workspace: &Path) {
|
|
261
340
|
let workspace_s = workspace.to_string_lossy().to_string();
|
|
262
341
|
let Value::Map(root) = spec else { return };
|
|
@@ -277,10 +356,19 @@ pub(crate) fn override_spec_workspace(spec: &mut Value, workspace: &Path) {
|
|
|
277
356
|
}
|
|
278
357
|
}
|
|
279
358
|
|
|
359
|
+
/// ---
|
|
360
|
+
/// purpose: 就地把 spec 的 runtime.display_backend 改成给定值
|
|
361
|
+
/// ---
|
|
280
362
|
pub(super) fn override_spec_display_backend(spec: &mut Value, display_backend: &str) {
|
|
281
363
|
override_spec_runtime_str(spec, "display_backend", display_backend);
|
|
282
364
|
}
|
|
283
365
|
|
|
366
|
+
/// ---
|
|
367
|
+
/// purpose: 就地写 spec 的 runtime 段下某个字符串字段
|
|
368
|
+
/// params:
|
|
369
|
+
/// spec: 非 Map 时不动
|
|
370
|
+
/// returns: runtime 段缺失则新建,已存在同名键则覆盖,runtime 是非 Map 值则整段替换
|
|
371
|
+
/// ---
|
|
284
372
|
pub(super) fn override_spec_runtime_str(spec: &mut Value, key: &str, value: &str) {
|
|
285
373
|
let Value::Map(root) = spec else { return };
|
|
286
374
|
let runtime_slot = root
|
|
@@ -307,6 +395,10 @@ pub(super) fn override_spec_runtime_str(spec: &mut Value, key: &str, value: &str
|
|
|
307
395
|
}
|
|
308
396
|
}
|
|
309
397
|
|
|
398
|
+
/// ---
|
|
399
|
+
/// purpose: 定出本 spec 的 tmux session 名
|
|
400
|
+
/// returns: runtime.session_name 的非空值;缺失时由 team.name 派生,team 名也缺失时用 agent 兜底
|
|
401
|
+
/// ---
|
|
310
402
|
pub(super) fn spec_session_name(spec: &Value) -> SessionName {
|
|
311
403
|
if let Some(name) = spec
|
|
312
404
|
.get("runtime")
|
|
@@ -326,6 +418,10 @@ pub(super) fn spec_session_name(spec: &Value) -> SessionName {
|
|
|
326
418
|
SessionName::new(format!("team-{team_name}"))
|
|
327
419
|
}
|
|
328
420
|
|
|
421
|
+
/// ---
|
|
422
|
+
/// purpose: 把 spec_session_name 以更宽可见性转出,供 layout 复用同一份实现
|
|
423
|
+
/// returns: 同 spec_session_name
|
|
424
|
+
/// ---
|
|
329
425
|
///
|
|
330
426
|
/// 0.3.28 layout step 1: pub re-export of `spec_session_name` for the new
|
|
331
427
|
/// `layout::sessions::worker_session_name` to delegate to. Single underlying
|
|
@@ -334,6 +430,10 @@ pub fn worker_session_name_pub(spec: &Value) -> SessionName {
|
|
|
334
430
|
spec_session_name(spec)
|
|
335
431
|
}
|
|
336
432
|
|
|
433
|
+
/// ---
|
|
434
|
+
/// purpose: 取出 spec 里全部 agent id
|
|
435
|
+
/// returns: 按 spec 顺序的 agent id,无 id 字段的条目被跳过
|
|
436
|
+
/// ---
|
|
337
437
|
pub(super) fn spec_agents(spec: &Value) -> Vec<AgentId> {
|
|
338
438
|
spec_agent_values(spec)
|
|
339
439
|
.into_iter()
|
|
@@ -341,6 +441,10 @@ pub(super) fn spec_agents(spec: &Value) -> Vec<AgentId> {
|
|
|
341
441
|
.collect()
|
|
342
442
|
}
|
|
343
443
|
|
|
444
|
+
/// ---
|
|
445
|
+
/// purpose: 取出 spec 里全部 agent id 的集合,便于成员判定
|
|
446
|
+
/// returns: 有序集合
|
|
447
|
+
/// ---
|
|
344
448
|
///
|
|
345
449
|
/// Bug 1 (0.4.2): expose spec agent id set so the restart path can filter
|
|
346
450
|
/// state.agents to only the agents currently defined in the rebuilt spec.
|
|
@@ -352,6 +456,10 @@ pub(crate) fn spec_agent_id_set(spec: &Value) -> std::collections::BTreeSet<Stri
|
|
|
352
456
|
.collect()
|
|
353
457
|
}
|
|
354
458
|
|
|
459
|
+
/// ---
|
|
460
|
+
/// purpose: 取出 spec 的 agents 列表原始节点
|
|
461
|
+
/// returns: 节点引用列表,字段缺失或类型不对时为空
|
|
462
|
+
/// ---
|
|
355
463
|
pub(super) fn spec_agent_values(spec: &Value) -> Vec<&Value> {
|
|
356
464
|
spec.get("agents")
|
|
357
465
|
.and_then(Value::as_list)
|
|
@@ -359,6 +467,10 @@ pub(super) fn spec_agent_values(spec: &Value) -> Vec<&Value> {
|
|
|
359
467
|
.unwrap_or_default()
|
|
360
468
|
}
|
|
361
469
|
|
|
470
|
+
/// ---
|
|
471
|
+
/// purpose: 对 spec 里每个 task 算出路由决策
|
|
472
|
+
/// returns: 每个 task 的选中 agent 与理由,manual_override 恒为 false
|
|
473
|
+
/// ---
|
|
362
474
|
pub(super) fn spec_routes(spec: &Value) -> Vec<RoutingDecision> {
|
|
363
475
|
spec.get("tasks")
|
|
364
476
|
.and_then(Value::as_list)
|
|
@@ -379,6 +491,10 @@ pub(super) fn spec_routes(spec: &Value) -> Vec<RoutingDecision> {
|
|
|
379
491
|
.unwrap_or_default()
|
|
380
492
|
}
|
|
381
493
|
|
|
494
|
+
/// ---
|
|
495
|
+
/// purpose: 定出默认承接人
|
|
496
|
+
/// returns: routing.default_assignee,缺失时取第一个 agent
|
|
497
|
+
/// ---
|
|
382
498
|
pub(super) fn spec_default_assignee(spec: &Value) -> Option<AgentId> {
|
|
383
499
|
spec.get("routing")
|
|
384
500
|
.and_then(|v| v.get("default_assignee"))
|
|
@@ -387,66 +503,110 @@ pub(super) fn spec_default_assignee(spec: &Value) -> Option<AgentId> {
|
|
|
387
503
|
.or_else(|| spec_agents(spec).into_iter().next())
|
|
388
504
|
}
|
|
389
505
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
+
/// ---
|
|
512
|
+
/// 0.5.66 bypass 单源:从**单个 agent**(yaml spec)的 `dangerously_skip_permissions` 构造
|
|
513
|
+
/// `DangerousApproval`(取代旧"runtime config + leader argv"全队一份)。
|
|
514
|
+
/// true → source=RuntimeConfig(角色声明即用户明确同意,coordinator 的
|
|
515
|
+
/// `explicit_yes_confirmed` 自动放行);false → Disabled。
|
|
516
|
+
pub(crate) fn effective_runtime_config_for_worker_spawn(
|
|
517
|
+
agent: &Value,
|
|
518
|
+
provider: Provider,
|
|
519
|
+
) -> Result<DangerousApproval, LifecycleError> {
|
|
520
|
+
let enabled = matches!(
|
|
521
|
+
agent.get("dangerously_skip_permissions"),
|
|
522
|
+
Some(Value::Bool(true))
|
|
523
|
+
);
|
|
524
|
+
if !enabled {
|
|
525
|
+
return Ok(DangerousApproval {
|
|
526
|
+
enabled: false,
|
|
527
|
+
source: DangerousApprovalSource::Disabled,
|
|
400
528
|
inherited: false,
|
|
401
529
|
provider: None,
|
|
402
530
|
flag: None,
|
|
403
|
-
worker_capability_above_leader:
|
|
404
|
-
ancestry_binary_name:
|
|
531
|
+
worker_capability_above_leader: false,
|
|
532
|
+
ancestry_binary_name: None,
|
|
405
533
|
unexpected_binary: false,
|
|
406
|
-
})
|
|
407
|
-
} else {
|
|
408
|
-
Ok(detect_dangerous_approval()?)
|
|
534
|
+
});
|
|
409
535
|
}
|
|
536
|
+
let flag = crate::provider::bypass_flags::provider_bypass_flag(provider);
|
|
537
|
+
Ok(DangerousApproval {
|
|
538
|
+
enabled: true,
|
|
539
|
+
source: DangerousApprovalSource::RuntimeConfig,
|
|
540
|
+
inherited: false,
|
|
541
|
+
provider: Some(provider_display_str(provider).to_string()),
|
|
542
|
+
flag: flag.map(str::to_string),
|
|
543
|
+
worker_capability_above_leader: false,
|
|
544
|
+
ancestry_binary_name: None,
|
|
545
|
+
unexpected_binary: false,
|
|
546
|
+
})
|
|
410
547
|
}
|
|
411
548
|
|
|
412
|
-
|
|
549
|
+
/// ---
|
|
550
|
+
/// purpose: 同上,但读的是 runtime state 里的 JSON agent 节点
|
|
551
|
+
/// returns: 同 YAML 版
|
|
552
|
+
/// errors: 当前实现不产生 Err
|
|
553
|
+
/// contract_id: lifecycle.spec_state.effective_runtime_config
|
|
554
|
+
/// ---
|
|
555
|
+
/// 0.5.66 bypass 单源:restart/state 路径的 serde_json 版(agent 来自 state JSON)。
|
|
556
|
+
pub(crate) fn effective_runtime_config_for_worker_spawn_json(
|
|
557
|
+
agent: &serde_json::Value,
|
|
558
|
+
provider: Provider,
|
|
413
559
|
) -> Result<DangerousApproval, LifecycleError> {
|
|
414
|
-
|
|
560
|
+
let enabled = agent
|
|
561
|
+
.get("dangerously_skip_permissions")
|
|
562
|
+
.and_then(serde_json::Value::as_bool)
|
|
563
|
+
.unwrap_or(false);
|
|
564
|
+
if !enabled {
|
|
565
|
+
return Ok(DangerousApproval {
|
|
566
|
+
enabled: false,
|
|
567
|
+
source: DangerousApprovalSource::Disabled,
|
|
568
|
+
inherited: false,
|
|
569
|
+
provider: None,
|
|
570
|
+
flag: None,
|
|
571
|
+
worker_capability_above_leader: false,
|
|
572
|
+
ancestry_binary_name: None,
|
|
573
|
+
unexpected_binary: false,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
let flag = crate::provider::bypass_flags::provider_bypass_flag(provider);
|
|
577
|
+
Ok(DangerousApproval {
|
|
578
|
+
enabled: true,
|
|
579
|
+
source: DangerousApprovalSource::RuntimeConfig,
|
|
580
|
+
inherited: false,
|
|
581
|
+
provider: Some(provider_display_str(provider).to_string()),
|
|
582
|
+
flag: flag.map(str::to_string),
|
|
583
|
+
worker_capability_above_leader: false,
|
|
584
|
+
ancestry_binary_name: None,
|
|
585
|
+
unexpected_binary: false,
|
|
586
|
+
})
|
|
415
587
|
}
|
|
416
588
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
"ancestry_binary_name": safety.ancestry_binary_name,
|
|
432
|
-
}),
|
|
433
|
-
)
|
|
434
|
-
.map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
|
|
435
|
-
if safety.unexpected_binary {
|
|
436
|
-
crate::event_log::EventLog::new(workspace)
|
|
437
|
-
.write(
|
|
438
|
-
"dangerous_flag_in_unexpected_binary",
|
|
439
|
-
serde_json::json!({
|
|
440
|
-
"provider": safety.provider,
|
|
441
|
-
"flag": safety.flag,
|
|
442
|
-
"ancestry_binary_name": safety.ancestry_binary_name,
|
|
443
|
-
}),
|
|
444
|
-
)
|
|
445
|
-
.map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
|
|
589
|
+
/// ---
|
|
590
|
+
/// purpose: 给出 provider 的稳定 wire 名
|
|
591
|
+
/// returns: 与 spec 写法一致的小写下划线名
|
|
592
|
+
/// ---
|
|
593
|
+
pub(crate) fn provider_display_str(provider: Provider) -> &'static str {
|
|
594
|
+
match provider {
|
|
595
|
+
Provider::Claude => "claude",
|
|
596
|
+
Provider::ClaudeCode => "claude_code",
|
|
597
|
+
Provider::Codex => "codex",
|
|
598
|
+
Provider::Copilot => "copilot",
|
|
599
|
+
Provider::GeminiCli => "gemini_cli",
|
|
600
|
+
Provider::Grok => "grok",
|
|
601
|
+
Provider::CursorAgent => "cursor_agent",
|
|
602
|
+
Provider::Fake => "fake",
|
|
446
603
|
}
|
|
447
|
-
Ok(())
|
|
448
604
|
}
|
|
449
605
|
|
|
606
|
+
/// ---
|
|
607
|
+
/// purpose: 由 team 目录推出 workspace 根
|
|
608
|
+
/// returns: 解析成功用解析值,失败时退到 team 目录的父目录,没有父目录则用 team 目录本身
|
|
609
|
+
/// ---
|
|
450
610
|
pub(super) fn team_workspace(team_dir: &Path) -> PathBuf {
|
|
451
611
|
crate::model::paths::team_workspace(team_dir).unwrap_or_else(|_| {
|
|
452
612
|
team_dir
|
|
@@ -455,3 +615,39 @@ pub(super) fn team_workspace(team_dir: &Path) -> PathBuf {
|
|
|
455
615
|
.unwrap_or_else(|| team_dir.to_path_buf())
|
|
456
616
|
})
|
|
457
617
|
}
|
|
618
|
+
|
|
619
|
+
#[cfg(test)]
|
|
620
|
+
mod tests {
|
|
621
|
+
#![allow(clippy::unwrap_used)]
|
|
622
|
+
use super::*;
|
|
623
|
+
use crate::model::enums::Provider;
|
|
624
|
+
|
|
625
|
+
fn yaml_agent(bypass: bool) -> Value {
|
|
626
|
+
crate::model::yaml::loads(&format!(
|
|
627
|
+
"id: a\nrole: r\nprovider: codex\ndangerously_skip_permissions: {bypass}\n"
|
|
628
|
+
))
|
|
629
|
+
.unwrap()
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// 0.5.66 bypass 单源 §4.1:true → enabled + source=runtime_config(角色声明即明确同意)。
|
|
633
|
+
#[test]
|
|
634
|
+
fn test_effective_safety_from_true_field() {
|
|
635
|
+
let safety =
|
|
636
|
+
effective_runtime_config_for_worker_spawn(&yaml_agent(true), Provider::Codex).unwrap();
|
|
637
|
+
assert!(safety.enabled);
|
|
638
|
+
assert_eq!(safety.source, DangerousApprovalSource::RuntimeConfig);
|
|
639
|
+
assert_eq!(
|
|
640
|
+
safety.flag.as_deref(),
|
|
641
|
+
Some("--dangerously-bypass-approvals-and-sandbox")
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// 0.5.66 bypass 单源 §4.1:false → Disabled。
|
|
646
|
+
#[test]
|
|
647
|
+
fn test_effective_safety_from_false_field() {
|
|
648
|
+
let safety =
|
|
649
|
+
effective_runtime_config_for_worker_spawn(&yaml_agent(false), Provider::Codex).unwrap();
|
|
650
|
+
assert!(!safety.enabled);
|
|
651
|
+
assert_eq!(safety.source, DangerousApprovalSource::Disabled);
|
|
652
|
+
}
|
|
653
|
+
}
|