@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,29 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 由 agent 的 profile 声明推出 provider 启动所需的环境覆盖、环境清除与命令覆写
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: prepare_provider_profile_launch
|
|
6
|
+
//! what: 从 YAML agent 节点算出 ProviderProfileLaunch
|
|
7
|
+
//! - name: prepare_provider_profile_launch_from_json
|
|
8
|
+
//! what: 从 runtime state 的 JSON agent 节点算出同一结果
|
|
9
|
+
//! - name: load_profile
|
|
10
|
+
//! what: 按查找顺序找到 profile env 文件并解析成键值
|
|
11
|
+
//! - name: provider_env_unsets
|
|
12
|
+
//! what: 该 provider 与 auth_mode 下必须清除的环境变量集,worker 与 leader 共用这一份
|
|
13
|
+
//! - name: profile_proxy_mode
|
|
14
|
+
//! what: 读出 profile 的代理模式,缺省 inherit
|
|
15
|
+
//! - name: parse_auth_mode
|
|
16
|
+
//! what: 把 auth_mode 字符串解析成枚举,未知值返回 None
|
|
17
|
+
//! depends:
|
|
18
|
+
//! - crate::provider
|
|
19
|
+
//! - crate::model::enums
|
|
20
|
+
//! - std::fs
|
|
21
|
+
//! boundary:
|
|
22
|
+
//! - 只读 profile 文件,不改写也不创建 profile
|
|
23
|
+
//! - 不把密钥值写进事件或错误消息
|
|
24
|
+
//! - provider 的环境清除集只此一份,leader 侧不得另建
|
|
25
|
+
//! maturity: wired
|
|
26
|
+
//! ---
|
|
1
27
|
use std::collections::{BTreeMap, BTreeSet};
|
|
2
28
|
use std::path::{Path, PathBuf};
|
|
3
29
|
|
|
@@ -37,6 +63,15 @@ struct AgentProfileInput {
|
|
|
37
63
|
model_source: Option<String>,
|
|
38
64
|
}
|
|
39
65
|
|
|
66
|
+
/// ---
|
|
67
|
+
/// purpose: 从 YAML agent 节点准备 provider 启动参数,profile 目录用默认查找顺序
|
|
68
|
+
/// params:
|
|
69
|
+
/// agent_id: agent 节点没写 id 时的兜底 id
|
|
70
|
+
/// mcp_config: 需要托管 MCP 配置时传入,否则 None
|
|
71
|
+
/// returns: 环境覆盖、环境清除、命令覆写与 claude 配置目录
|
|
72
|
+
/// errors: profile 找不到、必填键缺失或写 runtime env 文件失败时返回 LifecycleError
|
|
73
|
+
/// contract_id: lifecycle.profile_launch.prepare
|
|
74
|
+
/// ---
|
|
40
75
|
pub(crate) fn prepare_provider_profile_launch(
|
|
41
76
|
workspace: &Path,
|
|
42
77
|
agent_id: &str,
|
|
@@ -46,6 +81,14 @@ pub(crate) fn prepare_provider_profile_launch(
|
|
|
46
81
|
prepare_provider_profile_launch_with_profile_dir(workspace, agent_id, agent, None, mcp_config)
|
|
47
82
|
}
|
|
48
83
|
|
|
84
|
+
/// ---
|
|
85
|
+
/// purpose: 同上,但允许指定优先查找的 profile 目录
|
|
86
|
+
/// params:
|
|
87
|
+
/// profile_dir: 优先查找目录;agent 节点里的 _profile_dir 优先于它
|
|
88
|
+
/// returns: 环境覆盖、环境清除、命令覆写与 claude 配置目录
|
|
89
|
+
/// errors: 同 prepare_provider_profile_launch
|
|
90
|
+
/// contract_id: lifecycle.profile_launch.prepare
|
|
91
|
+
/// ---
|
|
49
92
|
pub(crate) fn prepare_provider_profile_launch_with_profile_dir(
|
|
50
93
|
workspace: &Path,
|
|
51
94
|
agent_id: &str,
|
|
@@ -94,6 +137,12 @@ pub(crate) fn prepare_provider_profile_launch_with_profile_dir(
|
|
|
94
137
|
prepare_profile_launch(workspace, input, mcp_config)
|
|
95
138
|
}
|
|
96
139
|
|
|
140
|
+
/// ---
|
|
141
|
+
/// purpose: 从 runtime state 的 JSON agent 节点准备 provider 启动参数
|
|
142
|
+
/// returns: 环境覆盖、环境清除、命令覆写与 claude 配置目录
|
|
143
|
+
/// errors: 同 prepare_provider_profile_launch
|
|
144
|
+
/// contract_id: lifecycle.profile_launch.prepare
|
|
145
|
+
/// ---
|
|
97
146
|
pub(crate) fn prepare_provider_profile_launch_from_json(
|
|
98
147
|
workspace: &Path,
|
|
99
148
|
agent_id: &str,
|
|
@@ -199,6 +248,14 @@ fn prepare_profile_launch(
|
|
|
199
248
|
})
|
|
200
249
|
}
|
|
201
250
|
|
|
251
|
+
/// ---
|
|
252
|
+
/// purpose: 按查找目录顺序定位 profile env 文件并解析成键值表
|
|
253
|
+
/// params:
|
|
254
|
+
/// name: profile 名,先找 name.env 再找 name.example.env
|
|
255
|
+
/// profile_dir: 优先查找目录,None 表示只用默认顺序
|
|
256
|
+
/// returns: 命中的文件路径与解析出的键值
|
|
257
|
+
/// errors: 所有目录都没命中返回 RequirementUnmet,读文件失败返回 StatePersist
|
|
258
|
+
/// ---
|
|
202
259
|
pub(crate) fn load_profile(
|
|
203
260
|
workspace: &Path,
|
|
204
261
|
name: &str,
|
|
@@ -424,11 +481,19 @@ fn provider_env_exports(
|
|
|
424
481
|
}
|
|
425
482
|
}
|
|
426
483
|
}
|
|
484
|
+
// grok/cursor_agent 一期无 profile exports(subscription 已登录态)。
|
|
485
|
+
// Cursor 订阅席的代理透传不在这里:profile 只服务 compatible-api。
|
|
486
|
+
// 订阅路径见 apply_cursor_subscription_proxy_env(只记有无,不记值)。
|
|
487
|
+
Provider::Grok | Provider::CursorAgent => {}
|
|
427
488
|
Provider::Fake => {}
|
|
428
489
|
}
|
|
429
490
|
exports
|
|
430
491
|
}
|
|
431
492
|
|
|
493
|
+
/// ---
|
|
494
|
+
/// purpose: 给出该 provider 与 auth_mode 下必须从子进程环境里清掉的变量名集
|
|
495
|
+
/// returns: 变量名集合;claude 系清掉会让子进程并入父会话的那组标识
|
|
496
|
+
/// ---
|
|
432
497
|
/// 0.4.x (CR C-1): exposed as `pub(crate)` so the leader launcher
|
|
433
498
|
/// (leader/start.rs) can reuse the SAME Claude/Codex/Copilot env-unset set
|
|
434
499
|
/// used by worker spawn. Single source of truth — adding a new provider env
|
|
@@ -497,6 +562,11 @@ pub(crate) fn provider_env_unsets(provider: Provider, auth_mode: AuthMode) -> BT
|
|
|
497
562
|
// C-7-1 cr verdict: 一期 subscription-only(已登录态),exports/unsets 空;
|
|
498
563
|
// BYOK(COPILOT_PROVIDER_*)二期立项时单独 cr verdict 再开。
|
|
499
564
|
Provider::Copilot => {}
|
|
565
|
+
// grok 无 env 需 unset。cursor 订阅席必须保留代理键(TUI 连云要用);
|
|
566
|
+
// `--print` 路径会因 https:// 方案撞 GLOBAL_AGENT,但主路径是 TUI。
|
|
567
|
+
// 禁止在这里 unset 代理。
|
|
568
|
+
Provider::Grok => {}
|
|
569
|
+
Provider::CursorAgent => {}
|
|
500
570
|
Provider::Fake => {}
|
|
501
571
|
}
|
|
502
572
|
unsets
|
|
@@ -893,6 +963,12 @@ fn compatible_api_network_exports(
|
|
|
893
963
|
.collect()
|
|
894
964
|
}
|
|
895
965
|
|
|
966
|
+
/// ---
|
|
967
|
+
/// purpose: 读出 profile 声明的代理模式
|
|
968
|
+
/// params:
|
|
969
|
+
/// values: 已解析的 profile 键值,先看 PROXY_MODE 再看 NETWORK_MODE
|
|
970
|
+
/// returns: 小写去空白后的模式串,两个键都没有时为 inherit
|
|
971
|
+
/// ---
|
|
896
972
|
pub(crate) fn profile_proxy_mode(values: &BTreeMap<String, String>) -> String {
|
|
897
973
|
values
|
|
898
974
|
.get("PROXY_MODE")
|
|
@@ -915,10 +991,18 @@ fn required_profile_keys(provider: Provider, auth_mode: AuthMode) -> &'static [&
|
|
|
915
991
|
// C-7-1 cr verdict: copilot 一期 subscription-only,无 BYOK required key 集合;
|
|
916
992
|
// 二期 BYOK 立项时填(向后兼容字段保留)。
|
|
917
993
|
Provider::Copilot => &[],
|
|
994
|
+
// 0.5.67: grok/cursor_agent 一期无 BYOK required key 集合 (同 copilot 精神)。
|
|
995
|
+
Provider::Grok | Provider::CursorAgent => &[],
|
|
918
996
|
Provider::Fake => &[],
|
|
919
997
|
}
|
|
920
998
|
}
|
|
921
999
|
|
|
1000
|
+
/// ---
|
|
1001
|
+
/// purpose: 取 profile 里某个规范键,取不到时按该键的已知别名回退
|
|
1002
|
+
/// params:
|
|
1003
|
+
/// key: 规范键名,API_KEY BASE_URL MODEL 各有一组别名,其余键不回退
|
|
1004
|
+
/// returns: 命中的非空值
|
|
1005
|
+
/// ---
|
|
922
1006
|
pub(crate) fn profile_value_or_alternate<'a>(
|
|
923
1007
|
values: &'a BTreeMap<String, String>,
|
|
924
1008
|
key: &str,
|
|
@@ -949,6 +1033,10 @@ pub(crate) fn profile_value_or_alternate<'a>(
|
|
|
949
1033
|
})
|
|
950
1034
|
}
|
|
951
1035
|
|
|
1036
|
+
/// ---
|
|
1037
|
+
/// purpose: 在两个显式给定的键之间取第一个非空值
|
|
1038
|
+
/// returns: 命中的非空值,两个都空则 None
|
|
1039
|
+
/// ---
|
|
952
1040
|
pub(crate) fn value_or_alternate<'a>(
|
|
953
1041
|
values: &'a BTreeMap<String, String>,
|
|
954
1042
|
primary: &str,
|
|
@@ -961,6 +1049,10 @@ pub(crate) fn value_or_alternate<'a>(
|
|
|
961
1049
|
.map(String::as_str)
|
|
962
1050
|
}
|
|
963
1051
|
|
|
1052
|
+
/// ---
|
|
1053
|
+
/// purpose: 取 profile 里声明的模型名
|
|
1054
|
+
/// returns: MODEL 的非空值,没有时取 ANTHROPIC_MODEL
|
|
1055
|
+
/// ---
|
|
964
1056
|
pub(crate) fn profile_model(values: &BTreeMap<String, String>) -> Option<&str> {
|
|
965
1057
|
values
|
|
966
1058
|
.get("MODEL")
|
|
@@ -982,6 +1074,12 @@ fn effective_profile_or_agent_model<'a>(
|
|
|
982
1074
|
|
|
983
1075
|
pub(crate) use crate::provider::wire::parse_provider;
|
|
984
1076
|
|
|
1077
|
+
/// ---
|
|
1078
|
+
/// purpose: 把 auth_mode 字符串解析成枚举
|
|
1079
|
+
/// params:
|
|
1080
|
+
/// raw: 只接受 subscription、official_api、compatible_api
|
|
1081
|
+
/// returns: 对应枚举;未知取值返回 None,由调用方决定兜底
|
|
1082
|
+
/// ---
|
|
985
1083
|
pub(crate) fn parse_auth_mode(raw: &str) -> Option<AuthMode> {
|
|
986
1084
|
match raw {
|
|
987
1085
|
"subscription" => Some(AuthMode::Subscription),
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 对 compatible_api 席位做发车前的 profile 连通性冒烟
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: profile_smoke_checks_for_agents_with_profile_dir
|
|
6
|
+
//! what: 对一组 agent 逐个冒烟,可指定优先查找的 profile 目录
|
|
7
|
+
//! - name: profile_smoke_checks_for_agents
|
|
8
|
+
//! what: 对一组 agent 逐个冒烟,返回逐个结果
|
|
9
|
+
//! - name: smoke_check_agent_profile
|
|
10
|
+
//! what: 单 agent 冒烟,非 compatible_api 直接判 not_required
|
|
11
|
+
//! - name: format_profile_smoke_failures
|
|
12
|
+
//! what: 把失败结果排成一行可读文本
|
|
13
|
+
//! depends:
|
|
14
|
+
//! - crate::lifecycle::profile_launch
|
|
15
|
+
//! - crate::provider::wire
|
|
16
|
+
//! boundary:
|
|
17
|
+
//! - 只读 profile 并发探测请求,不改 profile、不写 runtime state
|
|
18
|
+
//! - 不做重试与退避,超时即判失败
|
|
19
|
+
//! - 结果里不带密钥值
|
|
20
|
+
//! maturity: wired
|
|
21
|
+
//! ---
|
|
1
22
|
use std::collections::BTreeMap;
|
|
2
23
|
use std::io::Write as _;
|
|
3
24
|
use std::path::Path;
|
|
@@ -13,6 +34,13 @@ use crate::provider::wire::provider_wire;
|
|
|
13
34
|
|
|
14
35
|
pub(crate) const DEFAULT_PROFILE_SMOKE_TIMEOUT: Duration = Duration::from_secs(8);
|
|
15
36
|
|
|
37
|
+
/// ---
|
|
38
|
+
/// purpose: 用默认 profile 目录对一组 agent 逐个冒烟
|
|
39
|
+
/// params:
|
|
40
|
+
/// agents: spec 里的 agent 节点列表
|
|
41
|
+
/// timeout: 单次探测的超时
|
|
42
|
+
/// returns: 与入参同序的逐 agent 结果 JSON
|
|
43
|
+
/// ---
|
|
16
44
|
pub(crate) fn profile_smoke_checks_for_agents(
|
|
17
45
|
workspace: &Path,
|
|
18
46
|
agents: &[YamlValue],
|
|
@@ -21,6 +49,12 @@ pub(crate) fn profile_smoke_checks_for_agents(
|
|
|
21
49
|
profile_smoke_checks_for_agents_with_profile_dir(workspace, agents, None, timeout)
|
|
22
50
|
}
|
|
23
51
|
|
|
52
|
+
/// ---
|
|
53
|
+
/// purpose: 用指定 profile 目录对一组 agent 逐个冒烟
|
|
54
|
+
/// params:
|
|
55
|
+
/// profile_dir: 优先查找的 profile 目录,None 表示只用默认查找顺序
|
|
56
|
+
/// returns: 与入参同序的逐 agent 结果 JSON
|
|
57
|
+
/// ---
|
|
24
58
|
pub(crate) fn profile_smoke_checks_for_agents_with_profile_dir(
|
|
25
59
|
workspace: &Path,
|
|
26
60
|
agents: &[YamlValue],
|
|
@@ -33,6 +67,13 @@ pub(crate) fn profile_smoke_checks_for_agents_with_profile_dir(
|
|
|
33
67
|
.collect()
|
|
34
68
|
}
|
|
35
69
|
|
|
70
|
+
/// ---
|
|
71
|
+
/// purpose: 单个 agent 的 profile 冒烟
|
|
72
|
+
/// params:
|
|
73
|
+
/// agent: 单个 agent 节点,缺 provider 按 codex、缺 auth_mode 按 subscription
|
|
74
|
+
/// timeout: 单次 HTTP 探测的超时
|
|
75
|
+
/// returns: 结果 JSON。非 compatible_api 记 not_required,profile 里关掉冒烟记 skipped_by_profile,其余走真实请求
|
|
76
|
+
/// ---
|
|
36
77
|
pub(crate) fn smoke_check_agent_profile(
|
|
37
78
|
workspace: &Path,
|
|
38
79
|
agent: &YamlValue,
|
|
@@ -108,6 +149,10 @@ pub(crate) fn smoke_check_agent_profile(
|
|
|
108
149
|
result
|
|
109
150
|
}
|
|
110
151
|
|
|
152
|
+
/// ---
|
|
153
|
+
/// purpose: 把失败结果拼成一行人类可读的摘要
|
|
154
|
+
/// returns: 以分号分隔的 agent 与原因,缺字段时退化成 agent 与 failed
|
|
155
|
+
/// ---
|
|
111
156
|
pub(crate) fn format_profile_smoke_failures(failures: &[Value]) -> String {
|
|
112
157
|
failures
|
|
113
158
|
.iter()
|
|
@@ -187,7 +232,12 @@ impl SmokeTarget {
|
|
|
187
232
|
}
|
|
188
233
|
// C-7-1 cr verdict: copilot 一期 subscription-only,无 BYOK HTTP smoke
|
|
189
234
|
// 入口;同 GeminiCli/Fake 走 unsupported_provider_smoke_skipped。
|
|
190
|
-
|
|
235
|
+
// 0.5.67: grok/cursor_agent 同走 skipped (一期 subscription 已登录态)。
|
|
236
|
+
Provider::Copilot
|
|
237
|
+
| Provider::Grok
|
|
238
|
+
| Provider::CursorAgent
|
|
239
|
+
| Provider::GeminiCli
|
|
240
|
+
| Provider::Fake => {
|
|
191
241
|
Err("unsupported_provider_smoke_skipped")
|
|
192
242
|
}
|
|
193
243
|
}
|
|
@@ -1,9 +1,52 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 单席位的起、停与重置,全部在 workspace 级生命周期锁下串行
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: start_agent
|
|
6
|
+
//! what: 起或复活一席,按四象限定出 resume 或 fresh
|
|
7
|
+
//! - name: stop_agent
|
|
8
|
+
//! what: 停一席,按 pane、同角色残留 pane、窗口三级收敛并取排空证据
|
|
9
|
+
//! - name: reset_agent
|
|
10
|
+
//! what: 必须显式丢弃会话,停席后换血再起
|
|
11
|
+
//! - name: start_agent_at_paths
|
|
12
|
+
//! what: 起席的实体实现,owner 门、暂停判定、启动模式决策与落 state
|
|
13
|
+
//! - name: drain_old_pane_and_pid
|
|
14
|
+
//! what: 有界轮询等旧 pane 与旧进程消失,产出可核的证据载荷
|
|
15
|
+
//! depends:
|
|
16
|
+
//! - crate::lifecycle::lock
|
|
17
|
+
//! - crate::lifecycle::restart::selection
|
|
18
|
+
//! - crate::lifecycle::restart::remove
|
|
19
|
+
//! - crate::lifecycle::restart::team_state
|
|
20
|
+
//! - crate::lifecycle::launch
|
|
21
|
+
//! - crate::state::projection
|
|
22
|
+
//! - crate::state::persist
|
|
23
|
+
//! - crate::platform::process
|
|
24
|
+
//! - crate::lifecycle::profile_launch
|
|
25
|
+
//! - crate::lifecycle::worker_command_context
|
|
26
|
+
//! - crate::event_log::EventLog
|
|
27
|
+
//! boundary:
|
|
28
|
+
//! - 每个对外入口先取 workspace 级生命周期锁,本文件不做无锁写
|
|
29
|
+
//! - owner 门先于未知席位报错,权限判定不被存在性判定绕过
|
|
30
|
+
//! - reset 未显式丢弃会话时直接拒绝,不擅自换血
|
|
31
|
+
//! - 排空是有界等待,等不到只如实记录,不无限阻塞
|
|
32
|
+
//! maturity: wired
|
|
33
|
+
//! ---
|
|
1
34
|
use super::common::*;
|
|
2
35
|
use super::selection::decide_start_mode;
|
|
3
36
|
use super::team_state::write_team_state;
|
|
4
37
|
use super::*;
|
|
5
38
|
use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest};
|
|
6
39
|
|
|
40
|
+
/// ---
|
|
41
|
+
/// purpose: 起或复活一席的对外入口
|
|
42
|
+
/// params:
|
|
43
|
+
/// force: 窗口已存在时也重起
|
|
44
|
+
/// open_display: 是否开显示
|
|
45
|
+
/// allow_fresh: resume 依据缺失时允许改跑全新会话
|
|
46
|
+
/// returns: Running、Noop 或 Paused
|
|
47
|
+
/// errors: 选不到 team 返回 TeamSelect,owner 门不过返回 OwnerRefused,席位不存在返回 RequirementUnmet
|
|
48
|
+
/// contract_id: lifecycle.start_agent.entry
|
|
49
|
+
/// ---
|
|
7
50
|
/// `start_agent(workspace, agent_id, force, open_display, allow_fresh, team)`
|
|
8
51
|
/// (`lifecycle/start.py:72`)。`_runtime_lock("start-agent")` 下串行:resume-or-fresh
|
|
9
52
|
/// 决策、resume 窗口退出回退 fresh、起后投递 pending message、起 coordinator。
|
|
@@ -39,6 +82,12 @@ pub fn start_agent(
|
|
|
39
82
|
)
|
|
40
83
|
}
|
|
41
84
|
|
|
85
|
+
/// ---
|
|
86
|
+
/// purpose: 带注入 transport 的起席入口;未指定 team 且解析失败时退回把入参 workspace 当作两个路径
|
|
87
|
+
/// returns: Running、Noop 或 Paused
|
|
88
|
+
/// errors: 指定了 team 而解析失败时透传该错误,其余同 start_agent
|
|
89
|
+
/// contract_id: lifecycle.start_agent.entry
|
|
90
|
+
/// ---
|
|
42
91
|
/// `start_agent` with an injected transport — wires the single-worker resume/fresh spawn +
|
|
43
92
|
/// start_coordinator (rt-host-a sweep: was a stub returning RequirementUnmet at the spawn boundary).
|
|
44
93
|
#[allow(clippy::too_many_arguments)]
|
|
@@ -80,6 +129,14 @@ pub(crate) fn start_agent_with_transport(
|
|
|
80
129
|
)
|
|
81
130
|
}
|
|
82
131
|
|
|
132
|
+
/// ---
|
|
133
|
+
/// purpose: 起席的实体实现,读团队作用域 state、过 owner 门、定启动模式、spawn 并标记已起
|
|
134
|
+
/// params:
|
|
135
|
+
/// spec_workspace: spec 所在目录,用于补回席位的命令上下文
|
|
136
|
+
/// open_display: 当前实现未直接使用,仅参与自适应布局判定
|
|
137
|
+
/// returns: 席位暂停时直接返回 Paused,其余按启动模式返回 Running 或 Noop
|
|
138
|
+
/// errors: 读 state 失败返回 StatePersist,owner 门不过返回 OwnerRefused,席位不存在返回 RequirementUnmet
|
|
139
|
+
/// ---
|
|
83
140
|
#[allow(clippy::too_many_arguments)]
|
|
84
141
|
pub(crate) fn start_agent_at_paths(
|
|
85
142
|
workspace: &Path,
|
|
@@ -181,6 +238,31 @@ pub(crate) fn start_agent_at_paths(
|
|
|
181
238
|
);
|
|
182
239
|
}
|
|
183
240
|
let agent_live = agent_live && (!has_collision || noop_pane.is_some());
|
|
241
|
+
// 0.5.66 bypass 单源 §2.6 — dangerously_skip_permissions drift 挡板:
|
|
242
|
+
// Noop 路径不 spawn,若声明面(角色 md / spec)与运行面(as-launched)不一致,
|
|
243
|
+
// 拒 Noop 报明,防"改了 md 却以为生效、进程仍带旧 bypass 跑着"的暗门。
|
|
244
|
+
let file_bypass = agent
|
|
245
|
+
.get("dangerously_skip_permissions")
|
|
246
|
+
.and_then(serde_json::Value::as_bool);
|
|
247
|
+
let launched_bypass = raw_agent
|
|
248
|
+
.get("as_launched_dangerously_skip_permissions")
|
|
249
|
+
.and_then(serde_json::Value::as_bool);
|
|
250
|
+
if !force && agent_live && check_bypass_drift(file_bypass, launched_bypass) {
|
|
251
|
+
// 0.5.66 bypass 单源 §2.7:Noop drift 拒也写审计事件。
|
|
252
|
+
let _ = crate::event_log::EventLog::new(workspace).write(
|
|
253
|
+
"worker.spawn_dangerously_skip_permissions_drift_denied",
|
|
254
|
+
serde_json::json!({
|
|
255
|
+
"agent_id": agent_id.as_str(),
|
|
256
|
+
"file_value": file_bypass,
|
|
257
|
+
"launched_value": launched_bypass,
|
|
258
|
+
"action": "noop_denied_require_remove_add_or_force",
|
|
259
|
+
}),
|
|
260
|
+
);
|
|
261
|
+
return Err(LifecycleError::RequirementUnmet(format!(
|
|
262
|
+
"dangerously_skip_permissions drift for agent {agent_id}: file={file_bypass:?} but running process launched with {launched_bypass:?}. \
|
|
263
|
+
Run `remove-agent {agent_id}` then `add-agent {agent_id}` to apply, or `restart --force {agent_id}` to accept fresh spawn."
|
|
264
|
+
)));
|
|
265
|
+
}
|
|
184
266
|
if !force && agent_live {
|
|
185
267
|
let old_binding = pane_binding_snapshot(&raw_agent);
|
|
186
268
|
let refreshed_binding = noop_pane.as_ref().map(pane_binding_from_live);
|
|
@@ -257,7 +339,10 @@ pub(crate) fn start_agent_at_paths(
|
|
|
257
339
|
};
|
|
258
340
|
let into_existing_session =
|
|
259
341
|
session_live_or_default(transport, &session_name, session_name_present(&state));
|
|
260
|
-
let safety = crate::lifecycle::launch::
|
|
342
|
+
let safety = crate::lifecycle::launch::effective_runtime_config_for_worker_spawn_json(
|
|
343
|
+
&agent,
|
|
344
|
+
provider,
|
|
345
|
+
)?;
|
|
261
346
|
let layout_placement = if adaptive_layout {
|
|
262
347
|
crate::lifecycle::launch::adaptive_existing_placement_for_agent(
|
|
263
348
|
&state,
|
|
@@ -323,6 +408,30 @@ pub(crate) fn start_agent_at_paths(
|
|
|
323
408
|
&safety,
|
|
324
409
|
start_mode,
|
|
325
410
|
)?;
|
|
411
|
+
// 0.5.66 bypass 单源 §2.7:启动即带 bypass → 审计留痕。
|
|
412
|
+
if safety.enabled {
|
|
413
|
+
let state_agent = state
|
|
414
|
+
.get("agents")
|
|
415
|
+
.and_then(|v| v.get(agent_id.as_str()));
|
|
416
|
+
let _ = crate::event_log::EventLog::new(workspace).write(
|
|
417
|
+
"worker.spawn_dangerously_skip_permissions",
|
|
418
|
+
serde_json::json!({
|
|
419
|
+
"agent_id": agent_id.as_str(),
|
|
420
|
+
"role": state_agent
|
|
421
|
+
.and_then(|a| a.get("role"))
|
|
422
|
+
.and_then(serde_json::Value::as_str)
|
|
423
|
+
.unwrap_or_default(),
|
|
424
|
+
"provider": state_agent
|
|
425
|
+
.and_then(|a| a.get("provider"))
|
|
426
|
+
.and_then(serde_json::Value::as_str)
|
|
427
|
+
.unwrap_or_default(),
|
|
428
|
+
"trigger": "start-agent",
|
|
429
|
+
"spec_source_path": crate::lifecycle::launch::agent_id_spec_source_path(
|
|
430
|
+
workspace, agent_id.as_str(),
|
|
431
|
+
),
|
|
432
|
+
}),
|
|
433
|
+
);
|
|
434
|
+
}
|
|
326
435
|
// **0.3.24 add-agent socket drift fix**: keep `state.tmux_endpoint` /
|
|
327
436
|
// `state.tmux_socket` synchronized with the transport actually used for the
|
|
328
437
|
// spawn. Without this, add-agent / fork-agent could spawn to a socket that
|
|
@@ -736,6 +845,13 @@ fn tmux_start_mode_for_spawn(
|
|
|
736
845
|
// 0.4.6 Stage 1: reset/start clean-boundary helpers
|
|
737
846
|
// ═══════════════════════════════════════════════════════════════════════
|
|
738
847
|
|
|
848
|
+
/// ---
|
|
849
|
+
/// purpose: 有界轮询等待旧 pane 不可达且旧进程退出
|
|
850
|
+
/// params:
|
|
851
|
+
/// old_pane: 旧 pane,None 视作已死
|
|
852
|
+
/// old_pid: 旧进程号,None 视作已退出
|
|
853
|
+
/// returns: 证据载荷,含两者是否确认消失与实际等待毫秒数;到上限仍未消失也照实返回
|
|
854
|
+
/// ---
|
|
739
855
|
/// Bounded polling: wait for the old pane to become unreachable AND, when
|
|
740
856
|
/// possible, for the old pane_pid to exit. Returns a JSON evidence blob
|
|
741
857
|
/// recording the observed transition for events.
|
|
@@ -778,6 +894,10 @@ pub(super) fn drain_old_pane_and_pid(
|
|
|
778
894
|
})
|
|
779
895
|
}
|
|
780
896
|
|
|
897
|
+
/// ---
|
|
898
|
+
/// purpose: 尽力探测某进程号是否还活着
|
|
899
|
+
/// returns: 由平台层给出的判定
|
|
900
|
+
/// ---
|
|
781
901
|
/// Best-effort liveness probe for a pid.
|
|
782
902
|
///
|
|
783
903
|
/// 0.5.x Windows portability Batch 3: routes through
|
|
@@ -791,6 +911,10 @@ pub(super) fn pid_is_alive(pid: u32) -> bool {
|
|
|
791
911
|
crate::platform::process::pid_is_alive(pid)
|
|
792
912
|
}
|
|
793
913
|
|
|
914
|
+
/// ---
|
|
915
|
+
/// purpose: 从 state 里读该席位的 pane 进程号
|
|
916
|
+
/// returns: 存在且是无符号整数时返回它,否则 None
|
|
917
|
+
/// ---
|
|
794
918
|
/// Read state.agents[agent_id].pane_pid (u32) from the runtime state.
|
|
795
919
|
pub(super) fn state_pane_pid(state: &serde_json::Value, agent_id: &AgentId) -> Option<u32> {
|
|
796
920
|
state
|
|
@@ -801,6 +925,10 @@ pub(super) fn state_pane_pid(state: &serde_json::Value, agent_id: &AgentId) -> O
|
|
|
801
925
|
.map(|p| p as u32)
|
|
802
926
|
}
|
|
803
927
|
|
|
928
|
+
/// ---
|
|
929
|
+
/// purpose: 算出该席位下一次的 spawn 世代号
|
|
930
|
+
/// returns: 当前值加一,缺失时按 0 起算;世代号用于把捕获与事件归因到本次进程
|
|
931
|
+
/// ---
|
|
804
932
|
/// Read + increment state.agents[agent_id].spawn_epoch. The epoch is a
|
|
805
933
|
/// monotonic counter persisted with the agent row that uniquely tags each
|
|
806
934
|
/// fresh-start / reset / restart cycle. Subsequent capture / event /
|
|
@@ -817,6 +945,12 @@ pub(super) fn next_spawn_epoch(state: &serde_json::Value, agent_id: &AgentId) ->
|
|
|
817
945
|
current.saturating_add(1)
|
|
818
946
|
}
|
|
819
947
|
|
|
948
|
+
/// ---
|
|
949
|
+
/// purpose: 停一席的对外入口
|
|
950
|
+
/// returns: 停止报告
|
|
951
|
+
/// errors: 选不到 team 返回 TeamSelect,席位不存在返回 RequirementUnmet
|
|
952
|
+
/// contract_id: lifecycle.stop_agent.entry
|
|
953
|
+
/// ---
|
|
820
954
|
/// `stop_agent(workspace, agent_id, team)`(`lifecycle/operations.py:62`)。
|
|
821
955
|
/// owner-gate → kill window → **同时关显示** → 写 state。
|
|
822
956
|
pub fn stop_agent(
|
|
@@ -843,6 +977,12 @@ pub fn stop_agent(
|
|
|
843
977
|
)
|
|
844
978
|
}
|
|
845
979
|
|
|
980
|
+
/// ---
|
|
981
|
+
/// purpose: 带注入 transport 的停席入口
|
|
982
|
+
/// returns: 停止报告
|
|
983
|
+
/// errors: 同 stop_agent
|
|
984
|
+
/// contract_id: lifecycle.stop_agent.entry
|
|
985
|
+
/// ---
|
|
846
986
|
pub(crate) fn stop_agent_with_transport(
|
|
847
987
|
workspace: &Path,
|
|
848
988
|
agent_id: &AgentId,
|
|
@@ -867,6 +1007,11 @@ pub(crate) fn stop_agent_with_transport(
|
|
|
867
1007
|
)
|
|
868
1008
|
}
|
|
869
1009
|
|
|
1010
|
+
/// ---
|
|
1011
|
+
/// purpose: 停席的实体实现,解析席位、收敛杀 pane 与窗口、取排空证据后落 state
|
|
1012
|
+
/// returns: 停止报告;注意报告里的显示已关闭字段恒为真,不反映实际关显示结果
|
|
1013
|
+
/// errors: 席位解析不到实体返回 RequirementUnmet,写 state 失败返回 StatePersist
|
|
1014
|
+
/// ---
|
|
870
1015
|
pub(super) fn stop_agent_at_paths(
|
|
871
1016
|
workspace: &Path,
|
|
872
1017
|
spec_workspace: &Path,
|
|
@@ -1024,6 +1169,13 @@ pub(super) fn stop_agent_at_paths(
|
|
|
1024
1169
|
})
|
|
1025
1170
|
}
|
|
1026
1171
|
|
|
1172
|
+
/// ---
|
|
1173
|
+
/// purpose: 取团队作用域的投影 state,把选择拒绝转成带原因的错误
|
|
1174
|
+
/// params:
|
|
1175
|
+
/// team: 目标团队,None 时按唯一性选
|
|
1176
|
+
/// returns: 投影后的 state
|
|
1177
|
+
/// errors: 目标歧义或选不出时返回 TeamSelect 并在消息里带原因与细节,读取失败返回 StatePersist
|
|
1178
|
+
/// ---
|
|
1027
1179
|
/// golden `resolve_team_scoped_state` (state.py:243): returns the team-scoped projected state, or
|
|
1028
1180
|
/// surfaces the refusal dict (`team_target_ambiguous` / `team_target_unresolved`) as a typed error
|
|
1029
1181
|
/// BEFORE the owner gate / unknown-worker raise (operations.py:64-66). The lifecycle return types are
|
|
@@ -1078,6 +1230,12 @@ fn mark_agent_started(
|
|
|
1078
1230
|
// lifecycle/topology fields so absence == UNKNOWN until the next
|
|
1079
1231
|
// coordinator tick or pane fallback produces a post-spawn observation.
|
|
1080
1232
|
clear_agent_runtime_activity_observation(agent);
|
|
1233
|
+
// 0.5.66 bypass 单源 §2.6:fresh spawn 那一刻记录运行面 bypass 值,
|
|
1234
|
+
// 供下一次 restart 的 Noop drift 挡板比较。
|
|
1235
|
+
agent.insert(
|
|
1236
|
+
"as_launched_dangerously_skip_permissions".to_string(),
|
|
1237
|
+
serde_json::json!(safety.enabled),
|
|
1238
|
+
);
|
|
1081
1239
|
// S1-CAPTURE-001 (0.4.8, CR M3 provider-agnostic): on a Fresh /
|
|
1082
1240
|
// FreshAfterMissingRollout start, the prior session's authoritative
|
|
1083
1241
|
// capture tuple MUST be cleared before persist_command_plan_state
|
|
@@ -1193,6 +1351,14 @@ fn mark_agent_started(
|
|
|
1193
1351
|
Ok(())
|
|
1194
1352
|
}
|
|
1195
1353
|
|
|
1354
|
+
/// ---
|
|
1355
|
+
/// purpose: 重置一席的对外入口
|
|
1356
|
+
/// params:
|
|
1357
|
+
/// discard_session: 必须为真,否则直接返回拒绝
|
|
1358
|
+
/// returns: 重置结果,未显式丢弃会话时返回 Refused
|
|
1359
|
+
/// errors: 选不到 team 返回 TeamSelect,其余透传实体实现
|
|
1360
|
+
/// contract_id: lifecycle.reset_agent.entry
|
|
1361
|
+
/// ---
|
|
1196
1362
|
/// `reset_agent(workspace, agent_id, discard_session, open_display, team)`
|
|
1197
1363
|
/// (`lifecycle/operations.py:102`)。discard + 重起;**未传 discard_session → 拒绝**。
|
|
1198
1364
|
pub fn reset_agent(
|
|
@@ -1228,6 +1394,14 @@ pub fn reset_agent(
|
|
|
1228
1394
|
)
|
|
1229
1395
|
}
|
|
1230
1396
|
|
|
1397
|
+
/// ---
|
|
1398
|
+
/// purpose: 带注入 transport 的重置入口
|
|
1399
|
+
/// params:
|
|
1400
|
+
/// discard_session: 必须为真,否则直接返回拒绝
|
|
1401
|
+
/// returns: 重置结果
|
|
1402
|
+
/// errors: 同 reset_agent
|
|
1403
|
+
/// contract_id: lifecycle.reset_agent.entry
|
|
1404
|
+
/// ---
|
|
1231
1405
|
pub(crate) fn reset_agent_with_transport(
|
|
1232
1406
|
workspace: &Path,
|
|
1233
1407
|
agent_id: &AgentId,
|
|
@@ -1609,7 +1783,6 @@ fn write_start_agent_start_event(
|
|
|
1609
1783
|
let adapter = crate::provider::get_adapter(provider);
|
|
1610
1784
|
// Contract C / F6.4: event log must record the same context-aware argv that the
|
|
1611
1785
|
// actual spawn used — so the role/tools/MCP context appears in `start_agent.agent_start`.
|
|
1612
|
-
let safety = crate::lifecycle::launch::effective_runtime_config_for_worker_spawn()?;
|
|
1613
1786
|
let command_agent = crate::lifecycle::worker_command_context::WorkerCommandAgent::from_json(
|
|
1614
1787
|
agent,
|
|
1615
1788
|
Some(agent_id.as_str()),
|
|
@@ -1620,7 +1793,6 @@ fn write_start_agent_start_event(
|
|
|
1620
1793
|
let tools = crate::lifecycle::worker_command_context::resolved_tool_strings_for_command(
|
|
1621
1794
|
&command_agent,
|
|
1622
1795
|
provider,
|
|
1623
|
-
&safety,
|
|
1624
1796
|
)?;
|
|
1625
1797
|
let resolved_tool_refs: Vec<&str> = tools.iter().map(String::as_str).collect();
|
|
1626
1798
|
let mcp_config = adapter
|
|
@@ -1727,6 +1899,14 @@ fn write_stop_complete_event(
|
|
|
1727
1899
|
Ok(())
|
|
1728
1900
|
}
|
|
1729
1901
|
|
|
1902
|
+
/// ---
|
|
1903
|
+
/// purpose: 写一条停席排空证据事件
|
|
1904
|
+
/// params:
|
|
1905
|
+
/// target: 被停的目标描述
|
|
1906
|
+
/// drain: 排空证据载荷,其字段被并进事件顶层
|
|
1907
|
+
/// returns: 成功返回空值
|
|
1908
|
+
/// errors: 事件写入失败时返回 StatePersist
|
|
1909
|
+
/// ---
|
|
1730
1910
|
/// 0.4.6 Stage 1: drain evidence for the old pane+pid after stop. Fires
|
|
1731
1911
|
/// during reset/stop so operators can see whether the prior worker really
|
|
1732
1912
|
/// went away or stuck around inside the resp budget.
|
|
@@ -1831,6 +2011,17 @@ fn write_reset_complete_event(
|
|
|
1831
2011
|
Ok(())
|
|
1832
2012
|
}
|
|
1833
2013
|
|
|
2014
|
+
/// 0.5.66 bypass 单源 §2.6:声明面(file)与运行面(as-launched)是否 drift。
|
|
2015
|
+
/// 缺 as-launched(从未 spawn)= 视为未启动,不 drift(首次 Noop 无进程可比)。
|
|
2016
|
+
fn check_bypass_drift(file: Option<bool>, launched: Option<bool>) -> bool {
|
|
2017
|
+
match (file, launched) {
|
|
2018
|
+
(Some(file), Some(launched)) => file != launched,
|
|
2019
|
+
_ => false,
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
#[cfg(test)]
|
|
2024
|
+
|
|
1834
2025
|
#[cfg(test)]
|
|
1835
2026
|
mod tests {
|
|
1836
2027
|
use super::*;
|
|
@@ -1907,4 +2098,23 @@ mod tests {
|
|
|
1907
2098
|
"other-agent collision checks must also include tmux_socket"
|
|
1908
2099
|
);
|
|
1909
2100
|
}
|
|
2101
|
+
|
|
2102
|
+
// 0.5.66 bypass 单源 §4.1:Noop drift 检测——声明面与运行面不一致 → drift。
|
|
2103
|
+
#[test]
|
|
2104
|
+
fn test_restart_noop_denied_on_drift() {
|
|
2105
|
+
// file=true, launched=false → drift(改了 md 升 bypass 但进程没带)。
|
|
2106
|
+
assert!(check_bypass_drift(Some(true), Some(false)));
|
|
2107
|
+
// file=false, launched=true → drift(改了 md 撤 bypass 但进程还带着)。
|
|
2108
|
+
assert!(check_bypass_drift(Some(false), Some(true)));
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
// 0.5.66 bypass 单源 §4.1:无 drift 或从未 spawn(as-launched 缺)→ 不拒。
|
|
2112
|
+
#[test]
|
|
2113
|
+
fn test_restart_fresh_spawn_after_drift_uses_file_value() {
|
|
2114
|
+
assert!(!check_bypass_drift(Some(true), Some(true)));
|
|
2115
|
+
assert!(!check_bypass_drift(Some(false), Some(false)));
|
|
2116
|
+
// 从未 spawn:as-launched 缺失 → 不 drift(首次 Noop 无进程可比)。
|
|
2117
|
+
assert!(!check_bypass_drift(Some(true), None));
|
|
2118
|
+
assert!(!check_bypass_drift(None, None));
|
|
2119
|
+
}
|
|
1910
2120
|
}
|