@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
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: pane 输入通道互斥锁,让同一 pane 上的写入串行化
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: acquire_or_proceed
|
|
6
|
+
//! what: 取到锁则持有到 Drop;超时或锁不可用则告警后放行,不无限等待
|
|
7
|
+
//! depends:
|
|
8
|
+
//! - crate::platform::file_lock
|
|
9
|
+
//! - crate::event_log::EventLog
|
|
10
|
+
//! boundary:
|
|
11
|
+
//! - 不锁席位生命周期(那是 lock.rs)
|
|
12
|
+
//! - 不重粘文本、不加自动兜底重投;重试只重按回车由既有 inject 路径负责
|
|
13
|
+
//! - 不在投递层发明临时持有态
|
|
14
|
+
//! maturity: wired
|
|
15
|
+
//! ---
|
|
16
|
+
|
|
17
|
+
use std::path::{Path, PathBuf};
|
|
18
|
+
use std::time::{Duration, Instant};
|
|
19
|
+
|
|
20
|
+
use crate::event_log::EventLog;
|
|
21
|
+
use crate::model::paths::runtime_dir;
|
|
22
|
+
|
|
23
|
+
/// Hard upper bound for waiting on the pane input lock. Constant, not a magic number in a loop.
|
|
24
|
+
pub(crate) const PANE_INPUT_LOCK_TIMEOUT: Duration = Duration::from_millis(200);
|
|
25
|
+
|
|
26
|
+
pub(crate) const PANE_INPUT_LOCK_NAME: &str = "pane-input";
|
|
27
|
+
|
|
28
|
+
/// Event / stderr token. A5 greps this exact string.
|
|
29
|
+
pub(crate) const PANE_INPUT_LOCK_TIMEOUT_EVENT: &str = "pane_input_lock.timeout";
|
|
30
|
+
|
|
31
|
+
pub(crate) struct PaneInputLockRequest<'a> {
|
|
32
|
+
pub workspace: Option<&'a Path>,
|
|
33
|
+
pub target_key: &'a str,
|
|
34
|
+
pub operation: &'static str,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub(crate) struct PaneInputLockGuard {
|
|
38
|
+
file: Option<std::fs::File>,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// ---
|
|
42
|
+
/// purpose: 取 pane 输入锁;拿不到或超时则告警后放行,绝不无限等待
|
|
43
|
+
/// params:
|
|
44
|
+
/// request: 携带可选 workspace、目标 pane 键与操作名
|
|
45
|
+
/// returns: Some 表示已持锁,None 表示无 workspace 可锁或已超时放行
|
|
46
|
+
/// ---
|
|
47
|
+
/// Acquire the pane lock or proceed after the hard timeout.
|
|
48
|
+
///
|
|
49
|
+
/// On timeout / lock I/O failure: emit [`PANE_INPUT_LOCK_TIMEOUT_EVENT`] (stderr + events.jsonl)
|
|
50
|
+
/// and return `None` so the caller still writes. Relief messages must not wait forever.
|
|
51
|
+
pub(crate) fn acquire_or_proceed(request: PaneInputLockRequest<'_>) -> Option<PaneInputLockGuard> {
|
|
52
|
+
let timeout = PANE_INPUT_LOCK_TIMEOUT;
|
|
53
|
+
let Some(workspace) = request.workspace else {
|
|
54
|
+
// No workspace (some unit backends): nothing to lock against. Not an alarm.
|
|
55
|
+
return None;
|
|
56
|
+
};
|
|
57
|
+
acquire_file_lock(workspace, &request, timeout)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn acquire_file_lock(
|
|
61
|
+
workspace: &Path,
|
|
62
|
+
request: &PaneInputLockRequest<'_>,
|
|
63
|
+
timeout: Duration,
|
|
64
|
+
) -> Option<PaneInputLockGuard> {
|
|
65
|
+
let path = pane_input_lock_path(workspace, request.target_key);
|
|
66
|
+
if let Some(parent) = path.parent() {
|
|
67
|
+
if std::fs::create_dir_all(parent).is_err() {
|
|
68
|
+
emit_timeout_alarm(request, timeout, Duration::ZERO, "lock_dir");
|
|
69
|
+
return None;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let file = match std::fs::OpenOptions::new()
|
|
73
|
+
.create(true)
|
|
74
|
+
.read(true)
|
|
75
|
+
.write(true)
|
|
76
|
+
.truncate(false)
|
|
77
|
+
.open(&path)
|
|
78
|
+
{
|
|
79
|
+
Ok(file) => file,
|
|
80
|
+
Err(_) => {
|
|
81
|
+
emit_timeout_alarm(request, timeout, Duration::ZERO, "lock_open");
|
|
82
|
+
return None;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
let started = Instant::now();
|
|
86
|
+
loop {
|
|
87
|
+
match crate::platform::file_lock::try_lock_once_nonblocking(&file) {
|
|
88
|
+
Ok(true) => {
|
|
89
|
+
return Some(PaneInputLockGuard { file: Some(file) });
|
|
90
|
+
}
|
|
91
|
+
Ok(false) => {}
|
|
92
|
+
Err(_) => {
|
|
93
|
+
emit_timeout_alarm(request, timeout, started.elapsed(), "lock_io");
|
|
94
|
+
return None;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
let elapsed = started.elapsed();
|
|
98
|
+
if elapsed >= timeout {
|
|
99
|
+
emit_timeout_alarm(request, timeout, elapsed, "timeout");
|
|
100
|
+
return None;
|
|
101
|
+
}
|
|
102
|
+
std::thread::sleep(std::cmp::min(
|
|
103
|
+
Duration::from_millis(10),
|
|
104
|
+
timeout.saturating_sub(elapsed),
|
|
105
|
+
));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fn pane_input_lock_path(workspace: &Path, target_key: &str) -> PathBuf {
|
|
110
|
+
let safe: String = target_key
|
|
111
|
+
.chars()
|
|
112
|
+
.map(|c| {
|
|
113
|
+
if c.is_ascii_alphanumeric() {
|
|
114
|
+
c
|
|
115
|
+
} else {
|
|
116
|
+
'_'
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
.collect();
|
|
120
|
+
runtime_dir(workspace)
|
|
121
|
+
.join(PANE_INPUT_LOCK_NAME)
|
|
122
|
+
.join(format!("{safe}.lock"))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
fn emit_timeout_alarm(
|
|
126
|
+
request: &PaneInputLockRequest<'_>,
|
|
127
|
+
timeout: Duration,
|
|
128
|
+
waited: Duration,
|
|
129
|
+
reason: &str,
|
|
130
|
+
) {
|
|
131
|
+
// A5 watches this exact token on stderr. Commenting this line must turn A5 red.
|
|
132
|
+
eprintln!(
|
|
133
|
+
"{PANE_INPUT_LOCK_TIMEOUT_EVENT} target={} operation={} waited_ms={} timeout_ms={} reason={reason} proceeding",
|
|
134
|
+
request.target_key,
|
|
135
|
+
request.operation,
|
|
136
|
+
waited.as_millis(),
|
|
137
|
+
timeout.as_millis()
|
|
138
|
+
);
|
|
139
|
+
if let Some(workspace) = request.workspace {
|
|
140
|
+
let _ = EventLog::new(workspace).write(
|
|
141
|
+
PANE_INPUT_LOCK_TIMEOUT_EVENT,
|
|
142
|
+
serde_json::json!({
|
|
143
|
+
"lock_name": PANE_INPUT_LOCK_NAME,
|
|
144
|
+
"target": request.target_key,
|
|
145
|
+
"operation": request.operation,
|
|
146
|
+
"waited_ms": waited.as_millis() as u64,
|
|
147
|
+
"timeout_ms": timeout.as_millis() as u64,
|
|
148
|
+
"reason": reason,
|
|
149
|
+
"disposition": "proceed",
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
impl Drop for PaneInputLockGuard {
|
|
156
|
+
fn drop(&mut self) {
|
|
157
|
+
if let Some(file) = self.file.take() {
|
|
158
|
+
let _ = crate::platform::file_lock::unlock(&file);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -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,
|
|
@@ -788,6 +845,13 @@ fn tmux_start_mode_for_spawn(
|
|
|
788
845
|
// 0.4.6 Stage 1: reset/start clean-boundary helpers
|
|
789
846
|
// ═══════════════════════════════════════════════════════════════════════
|
|
790
847
|
|
|
848
|
+
/// ---
|
|
849
|
+
/// purpose: 有界轮询等待旧 pane 不可达且旧进程退出
|
|
850
|
+
/// params:
|
|
851
|
+
/// old_pane: 旧 pane,None 视作已死
|
|
852
|
+
/// old_pid: 旧进程号,None 视作已退出
|
|
853
|
+
/// returns: 证据载荷,含两者是否确认消失与实际等待毫秒数;到上限仍未消失也照实返回
|
|
854
|
+
/// ---
|
|
791
855
|
/// Bounded polling: wait for the old pane to become unreachable AND, when
|
|
792
856
|
/// possible, for the old pane_pid to exit. Returns a JSON evidence blob
|
|
793
857
|
/// recording the observed transition for events.
|
|
@@ -830,6 +894,10 @@ pub(super) fn drain_old_pane_and_pid(
|
|
|
830
894
|
})
|
|
831
895
|
}
|
|
832
896
|
|
|
897
|
+
/// ---
|
|
898
|
+
/// purpose: 尽力探测某进程号是否还活着
|
|
899
|
+
/// returns: 由平台层给出的判定
|
|
900
|
+
/// ---
|
|
833
901
|
/// Best-effort liveness probe for a pid.
|
|
834
902
|
///
|
|
835
903
|
/// 0.5.x Windows portability Batch 3: routes through
|
|
@@ -843,6 +911,10 @@ pub(super) fn pid_is_alive(pid: u32) -> bool {
|
|
|
843
911
|
crate::platform::process::pid_is_alive(pid)
|
|
844
912
|
}
|
|
845
913
|
|
|
914
|
+
/// ---
|
|
915
|
+
/// purpose: 从 state 里读该席位的 pane 进程号
|
|
916
|
+
/// returns: 存在且是无符号整数时返回它,否则 None
|
|
917
|
+
/// ---
|
|
846
918
|
/// Read state.agents[agent_id].pane_pid (u32) from the runtime state.
|
|
847
919
|
pub(super) fn state_pane_pid(state: &serde_json::Value, agent_id: &AgentId) -> Option<u32> {
|
|
848
920
|
state
|
|
@@ -853,6 +925,10 @@ pub(super) fn state_pane_pid(state: &serde_json::Value, agent_id: &AgentId) -> O
|
|
|
853
925
|
.map(|p| p as u32)
|
|
854
926
|
}
|
|
855
927
|
|
|
928
|
+
/// ---
|
|
929
|
+
/// purpose: 算出该席位下一次的 spawn 世代号
|
|
930
|
+
/// returns: 当前值加一,缺失时按 0 起算;世代号用于把捕获与事件归因到本次进程
|
|
931
|
+
/// ---
|
|
856
932
|
/// Read + increment state.agents[agent_id].spawn_epoch. The epoch is a
|
|
857
933
|
/// monotonic counter persisted with the agent row that uniquely tags each
|
|
858
934
|
/// fresh-start / reset / restart cycle. Subsequent capture / event /
|
|
@@ -869,6 +945,12 @@ pub(super) fn next_spawn_epoch(state: &serde_json::Value, agent_id: &AgentId) ->
|
|
|
869
945
|
current.saturating_add(1)
|
|
870
946
|
}
|
|
871
947
|
|
|
948
|
+
/// ---
|
|
949
|
+
/// purpose: 停一席的对外入口
|
|
950
|
+
/// returns: 停止报告
|
|
951
|
+
/// errors: 选不到 team 返回 TeamSelect,席位不存在返回 RequirementUnmet
|
|
952
|
+
/// contract_id: lifecycle.stop_agent.entry
|
|
953
|
+
/// ---
|
|
872
954
|
/// `stop_agent(workspace, agent_id, team)`(`lifecycle/operations.py:62`)。
|
|
873
955
|
/// owner-gate → kill window → **同时关显示** → 写 state。
|
|
874
956
|
pub fn stop_agent(
|
|
@@ -895,6 +977,12 @@ pub fn stop_agent(
|
|
|
895
977
|
)
|
|
896
978
|
}
|
|
897
979
|
|
|
980
|
+
/// ---
|
|
981
|
+
/// purpose: 带注入 transport 的停席入口
|
|
982
|
+
/// returns: 停止报告
|
|
983
|
+
/// errors: 同 stop_agent
|
|
984
|
+
/// contract_id: lifecycle.stop_agent.entry
|
|
985
|
+
/// ---
|
|
898
986
|
pub(crate) fn stop_agent_with_transport(
|
|
899
987
|
workspace: &Path,
|
|
900
988
|
agent_id: &AgentId,
|
|
@@ -919,6 +1007,11 @@ pub(crate) fn stop_agent_with_transport(
|
|
|
919
1007
|
)
|
|
920
1008
|
}
|
|
921
1009
|
|
|
1010
|
+
/// ---
|
|
1011
|
+
/// purpose: 停席的实体实现,解析席位、收敛杀 pane 与窗口、取排空证据后落 state
|
|
1012
|
+
/// returns: 停止报告;注意报告里的显示已关闭字段恒为真,不反映实际关显示结果
|
|
1013
|
+
/// errors: 席位解析不到实体返回 RequirementUnmet,写 state 失败返回 StatePersist
|
|
1014
|
+
/// ---
|
|
922
1015
|
pub(super) fn stop_agent_at_paths(
|
|
923
1016
|
workspace: &Path,
|
|
924
1017
|
spec_workspace: &Path,
|
|
@@ -1076,6 +1169,13 @@ pub(super) fn stop_agent_at_paths(
|
|
|
1076
1169
|
})
|
|
1077
1170
|
}
|
|
1078
1171
|
|
|
1172
|
+
/// ---
|
|
1173
|
+
/// purpose: 取团队作用域的投影 state,把选择拒绝转成带原因的错误
|
|
1174
|
+
/// params:
|
|
1175
|
+
/// team: 目标团队,None 时按唯一性选
|
|
1176
|
+
/// returns: 投影后的 state
|
|
1177
|
+
/// errors: 目标歧义或选不出时返回 TeamSelect 并在消息里带原因与细节,读取失败返回 StatePersist
|
|
1178
|
+
/// ---
|
|
1079
1179
|
/// golden `resolve_team_scoped_state` (state.py:243): returns the team-scoped projected state, or
|
|
1080
1180
|
/// surfaces the refusal dict (`team_target_ambiguous` / `team_target_unresolved`) as a typed error
|
|
1081
1181
|
/// BEFORE the owner gate / unknown-worker raise (operations.py:64-66). The lifecycle return types are
|
|
@@ -1251,6 +1351,14 @@ fn mark_agent_started(
|
|
|
1251
1351
|
Ok(())
|
|
1252
1352
|
}
|
|
1253
1353
|
|
|
1354
|
+
/// ---
|
|
1355
|
+
/// purpose: 重置一席的对外入口
|
|
1356
|
+
/// params:
|
|
1357
|
+
/// discard_session: 必须为真,否则直接返回拒绝
|
|
1358
|
+
/// returns: 重置结果,未显式丢弃会话时返回 Refused
|
|
1359
|
+
/// errors: 选不到 team 返回 TeamSelect,其余透传实体实现
|
|
1360
|
+
/// contract_id: lifecycle.reset_agent.entry
|
|
1361
|
+
/// ---
|
|
1254
1362
|
/// `reset_agent(workspace, agent_id, discard_session, open_display, team)`
|
|
1255
1363
|
/// (`lifecycle/operations.py:102`)。discard + 重起;**未传 discard_session → 拒绝**。
|
|
1256
1364
|
pub fn reset_agent(
|
|
@@ -1286,6 +1394,14 @@ pub fn reset_agent(
|
|
|
1286
1394
|
)
|
|
1287
1395
|
}
|
|
1288
1396
|
|
|
1397
|
+
/// ---
|
|
1398
|
+
/// purpose: 带注入 transport 的重置入口
|
|
1399
|
+
/// params:
|
|
1400
|
+
/// discard_session: 必须为真,否则直接返回拒绝
|
|
1401
|
+
/// returns: 重置结果
|
|
1402
|
+
/// errors: 同 reset_agent
|
|
1403
|
+
/// contract_id: lifecycle.reset_agent.entry
|
|
1404
|
+
/// ---
|
|
1289
1405
|
pub(crate) fn reset_agent_with_transport(
|
|
1290
1406
|
workspace: &Path,
|
|
1291
1407
|
agent_id: &AgentId,
|
|
@@ -1783,6 +1899,14 @@ fn write_stop_complete_event(
|
|
|
1783
1899
|
Ok(())
|
|
1784
1900
|
}
|
|
1785
1901
|
|
|
1902
|
+
/// ---
|
|
1903
|
+
/// purpose: 写一条停席排空证据事件
|
|
1904
|
+
/// params:
|
|
1905
|
+
/// target: 被停的目标描述
|
|
1906
|
+
/// drain: 排空证据载荷,其字段被并进事件顶层
|
|
1907
|
+
/// returns: 成功返回空值
|
|
1908
|
+
/// errors: 事件写入失败时返回 StatePersist
|
|
1909
|
+
/// ---
|
|
1786
1910
|
/// 0.4.6 Stage 1: drain evidence for the old pane+pid after stop. Fires
|
|
1787
1911
|
/// during reset/stop so operators can see whether the prior worker really
|
|
1788
1912
|
/// went away or stuck around inside the resp budget.
|