@team-agent/installer 0.5.66 → 0.5.69
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 +156 -0
- package/crates/team-agent/src/cli/emit.rs +6 -5
- package/crates/team-agent/src/cli/grok_slot.rs +299 -0
- package/crates/team-agent/src/cli/leader.rs +22 -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 +3 -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 +121 -5
- 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 +31 -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 +3 -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 +973 -75
- 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 +398 -26
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +80 -0
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +60 -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 +548 -0
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp_iso.rs +120 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +208 -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 +482 -1
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
- package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +830 -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 +87 -3
- 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 +127 -25
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +140 -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 +107 -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 +99 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +52 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +214 -160
- package/crates/team-agent/src/lifecycle/restart/common.rs +513 -37
- 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 +233 -6
- 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/claude_profile_launch_red.rs +24 -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 +464 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +247 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
- package/crates/team-agent/src/lifecycle/tests/fixtures/pi_list_models_g0.stdout.txt +222 -0
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +787 -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 +485 -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 +1 -1
- 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/pi_catalog_red.rs +97 -0
- package/crates/team-agent/src/lifecycle/tests/pi_command_red.rs +128 -0
- package/crates/team-agent/src/lifecycle/tests/pi_compiler_red.rs +207 -0
- package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +838 -0
- package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +320 -0
- package/crates/team-agent/src/lifecycle/tests/pi_lifecycle_doctor_red.rs +98 -0
- package/crates/team-agent/src/lifecycle/tests/pi_session_delivery_red.rs +216 -0
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -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 +30 -13
- package/crates/team-agent/src/lifecycle/types.rs +61 -0
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +143 -12
- 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 +430 -41
- 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 +17 -6
- 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 +259 -6
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +89 -0
- package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +3 -0
- package/crates/team-agent/src/provider/adapters/pi.rs +152 -0
- package/crates/team-agent/src/provider/bypass_flags.rs +47 -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 +78 -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 +205 -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/pi.rs +172 -0
- package/crates/team-agent/src/provider/session_scan.rs +12 -0
- package/crates/team-agent/src/provider/submit_now.rs +94 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +384 -0
- package/crates/team-agent/src/provider/types.rs +4 -2
- package/crates/team-agent/src/provider/wire.rs +29 -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 +1116 -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 +32 -239
- package/skills/team-agent/command-coverage.json +31 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -488
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 把 restart resume 门的「为什么拒绝」从两个不透明字符串升级成闭合枚举,并携带操作者可用的恢复线索
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: ResumeRefusalReason
|
|
6
|
+
//! what: resume 拒绝原因闭合枚举(7 变体),新原因必须改代码才能加
|
|
7
|
+
//! - name: wire
|
|
8
|
+
//! what: 枚举 → 历史 UnresumableWorker.reason 稳定串,供 JSON/日志沿用旧形状
|
|
9
|
+
//! - name: from_legacy
|
|
10
|
+
//! what: 历史字符串 → 枚举的逆映射,未识别串一律落进 Other{legacy_reason}
|
|
11
|
+
//! - name: RecoveryHint
|
|
12
|
+
//! what: 缺 backing 时给操作者的 provider/name/cwd 三元线索
|
|
13
|
+
//! - name: picker_hint
|
|
14
|
+
//! what: 把 RecoveryHint 渲染成一行人读文本
|
|
15
|
+
//! requires:
|
|
16
|
+
//! - name: std::path::PathBuf
|
|
17
|
+
//! what: checked_paths 与 spawn_cwd 的载体
|
|
18
|
+
//! boundary:
|
|
19
|
+
//! - 只做「原因的分类与措辞」:不探测 backing 是否存在,不读磁盘,不发事件
|
|
20
|
+
//! - RecoveryHint 只呈现给人,绝不被自动 resume 消费(自动恢复需 Layer 3 多键过滤+backing 复验)
|
|
21
|
+
//! - 判定 resume 是否可行的调用点在 lifecycle/restart,不在本文件
|
|
22
|
+
//! maturity: wired
|
|
23
|
+
//! ---
|
|
1
24
|
//!
|
|
2
25
|
//! unit-5 (Stage 2) — closed `ResumeRefusalReason` enum and recovery hints.
|
|
3
26
|
//!
|
|
@@ -47,6 +70,16 @@ impl RecoveryHint {
|
|
|
47
70
|
/// Build a human-readable picker hint (one line). Layer 2 surfaces
|
|
48
71
|
/// this in the CLI refusal message and in the
|
|
49
72
|
/// `session.recovery.candidate_hint` event payload.
|
|
73
|
+
/// ---
|
|
74
|
+
/// purpose: 把 provider/name/cwd 三元线索拼成一行人读文本,供 CLI 拒绝信息与事件载荷使用
|
|
75
|
+
/// returns: 四种措辞之一,按 name/cwd 各自是否存在退化;两者都缺时退到 "<provider> session"
|
|
76
|
+
/// contract:
|
|
77
|
+
/// provides:
|
|
78
|
+
/// - name: picker_hint
|
|
79
|
+
/// what: 纯格式化,不查磁盘、不校验 cwd 是否还在
|
|
80
|
+
/// boundary:
|
|
81
|
+
/// - 输出只给人看,不做机器解析的契约,调用方不得据此自动 resume
|
|
82
|
+
/// ---
|
|
50
83
|
pub fn picker_hint(&self) -> String {
|
|
51
84
|
match (&self.provider_session_name_hint, &self.spawn_cwd) {
|
|
52
85
|
(Some(name), Some(cwd)) => format!(
|
|
@@ -118,6 +151,17 @@ impl ResumeRefusalReason {
|
|
|
118
151
|
/// `UnresumableWorker.reason` values. Use this when emitting JSON or
|
|
119
152
|
/// log fields so downstream consumers see the same strings they
|
|
120
153
|
/// always have.
|
|
154
|
+
/// ---
|
|
155
|
+
/// purpose: 把结构化拒绝原因压回历史 UnresumableWorker.reason 稳定串
|
|
156
|
+
/// returns: 六个 canonical 串之一;Other 一律折回 "session_unresumable"——未 taxonomize 的新失败类型对外与历史大杂烩不可区分
|
|
157
|
+
/// contract:
|
|
158
|
+
/// provides:
|
|
159
|
+
/// - name: wire
|
|
160
|
+
/// what: 枚举 → 稳定串的全函数映射,不丢字段但丢细节
|
|
161
|
+
/// boundary:
|
|
162
|
+
/// - 不携带 checked_paths / recovery_hint / drift 的 expected-actual 等负载,只给分类名
|
|
163
|
+
/// - 与 from_legacy 只在五个 canonical 串上互逆;SessionDrift 有 wire 串但 from_legacy 无对应臂,"session_drift" 会落进 Other,该变体不可往返
|
|
164
|
+
/// ---
|
|
121
165
|
pub fn wire(&self) -> &'static str {
|
|
122
166
|
match self {
|
|
123
167
|
ResumeRefusalReason::NoSessionId => "no_persisted_session_id",
|
|
@@ -136,6 +180,19 @@ impl ResumeRefusalReason {
|
|
|
136
180
|
|
|
137
181
|
/// Lift a legacy free-form `reason` string into the structured enum.
|
|
138
182
|
/// Round-trip-safe with `wire()` for the canonical names.
|
|
183
|
+
/// ---
|
|
184
|
+
/// purpose: 把历史自由串抬升成结构化枚举,保证旧持久化数据不丢分类
|
|
185
|
+
/// params:
|
|
186
|
+
/// reason: 历史 UnresumableWorker.reason 串;不在识别表内的任意值都合法
|
|
187
|
+
/// returns: 匹配到的变体(负载字段一律填空,因为串里没有这些事实);未匹配则 Other{legacy_reason=原串}
|
|
188
|
+
/// contract:
|
|
189
|
+
/// provides:
|
|
190
|
+
/// - name: from_legacy
|
|
191
|
+
/// what: 全函数、不失败、不 panic 的逆映射
|
|
192
|
+
/// boundary:
|
|
193
|
+
/// - 不还原 checked_paths / recovery_hint / provider 名等负载——它们在串里本就不存在
|
|
194
|
+
/// - 识别表缺 "session_drift" 臂,该串会落到 Other 而非 SessionDrift
|
|
195
|
+
/// ---
|
|
139
196
|
pub fn from_legacy(reason: &str) -> Self {
|
|
140
197
|
match reason {
|
|
141
198
|
"no_persisted_session_id" => ResumeRefusalReason::NoSessionId,
|
|
@@ -1,4 +1,33 @@
|
|
|
1
|
-
//!
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: claude 家族的会话归属过滤——按 expected id 直达 ~/.claude/projects/<编码 cwd>/<sid>.jsonl 读头验身份,并为通用扫描提供 leader-transcript 与 cwd 字段两道排除判据
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: projects_dir_for_cwd
|
|
6
|
+
//! what: 由 HOME + spawn_cwd 推出 claude 的 projects 目录(非字母数字字符一律替成 '-')
|
|
7
|
+
//! - name: encode_projects_dir
|
|
8
|
+
//! what: claude 的目录名编码规则本身
|
|
9
|
+
//! - name: scan_expected_session
|
|
10
|
+
//! what: 有 pending id 时的直达捕获:读头 64KB,验 sessionId 一致 + 有 user/assistant 记录 + 无 leader marker + 有 cwd 字段,四条全过才出候选
|
|
11
|
+
//! - name: rollout_path_has_leader_marker
|
|
12
|
+
//! what: 判某条 transcript 是不是 leader 的(customTitle/agentName == "claude leader")
|
|
13
|
+
//! - name: records_have_leader_marker
|
|
14
|
+
//! what: 上一条的记录级判据
|
|
15
|
+
//! - name: has_cwd_field
|
|
16
|
+
//! what: 记录里有没有 cwd 字段——claude transcript 的最低可信度门槛
|
|
17
|
+
//! - name: apply_expected_session_filter
|
|
18
|
+
//! what: 通用扫描结果的收窄:expected 命中则独取,未命中则只留身份/路径阳性的
|
|
19
|
+
//! requires:
|
|
20
|
+
//! - name: super::common
|
|
21
|
+
//! what: 读头、解析记录、embedded 身份、时间窗过滤都复用 common
|
|
22
|
+
//! - name: crate::provider::helpers::find_session_id
|
|
23
|
+
//! what: 从记录里取 sessionId 的统一入口
|
|
24
|
+
//! boundary:
|
|
25
|
+
//! - 只服务 Provider::Claude / ClaudeCode;rollout_path_has_leader_marker 对其它 provider 恒 false
|
|
26
|
+
//! - 不写盘、不改 state、不发事件
|
|
27
|
+
//! - 直达路径不做同 cwd「最新文件」回落——有 pending id 就只认那一个文件名
|
|
28
|
+
//! - leader transcript 一律排除,防止 worker 席位绑上 leader 的会话
|
|
29
|
+
//! maturity: wired
|
|
30
|
+
//! ---
|
|
2
31
|
use std::path::{Path, PathBuf};
|
|
3
32
|
|
|
4
33
|
use crate::provider::helpers::find_session_id;
|
|
@@ -7,6 +36,20 @@ use crate::provider::Provider;
|
|
|
7
36
|
|
|
8
37
|
use super::{CaptureSessionContext, CapturedSessionCandidate};
|
|
9
38
|
|
|
39
|
+
/// ---
|
|
40
|
+
/// purpose: 由 HOME 与席位 cwd 推出 claude 存放该工作目录 transcript 的 projects 子目录
|
|
41
|
+
/// params:
|
|
42
|
+
/// home: HOME 根;调用方决定是真实 HOME 还是隔离根
|
|
43
|
+
/// spawn_cwd: 席位工作目录;先 canonicalize,失败则原样使用
|
|
44
|
+
/// returns: <home>/.claude/projects/<编码后的 cwd>;编码结果为空串时 None
|
|
45
|
+
/// contract:
|
|
46
|
+
/// provides:
|
|
47
|
+
/// - name: projects_dir_for_cwd
|
|
48
|
+
/// what: 只拼路径,不创建目录、不判存在性
|
|
49
|
+
/// boundary:
|
|
50
|
+
/// - 不枚举目录内容、不读任何文件
|
|
51
|
+
/// - canonicalize 失败不报错,退回原路径——编码结果因此可能与 claude 实际用的目录不同
|
|
52
|
+
/// ---
|
|
10
53
|
pub(crate) fn projects_dir_for_cwd(home: &Path, spawn_cwd: &Path) -> Option<PathBuf> {
|
|
11
54
|
let canonical = std::fs::canonicalize(spawn_cwd).unwrap_or_else(|_| spawn_cwd.to_path_buf());
|
|
12
55
|
let encoded = encode_projects_dir(&canonical.to_string_lossy());
|
|
@@ -16,6 +59,19 @@ pub(crate) fn projects_dir_for_cwd(home: &Path, spawn_cwd: &Path) -> Option<Path
|
|
|
16
59
|
Some(home.join(".claude").join("projects").join(encoded))
|
|
17
60
|
}
|
|
18
61
|
|
|
62
|
+
/// ---
|
|
63
|
+
/// purpose: 复刻 claude 的 projects 目录名编码:非 ASCII 字母数字的字符一律替成单个 '-'
|
|
64
|
+
/// params:
|
|
65
|
+
/// path: 待编码的路径文本
|
|
66
|
+
/// returns: 等长的编码串;输入为空则空串
|
|
67
|
+
/// contract:
|
|
68
|
+
/// provides:
|
|
69
|
+
/// - name: encode_projects_dir
|
|
70
|
+
/// what: 纯字符映射,逐字符一对一,不折叠连续分隔符
|
|
71
|
+
/// boundary:
|
|
72
|
+
/// - 有损且不可逆:不同路径可以编出同一个目录名
|
|
73
|
+
/// - 不做长度截断、不做大小写归一
|
|
74
|
+
/// ---
|
|
19
75
|
pub(super) fn encode_projects_dir(path: &str) -> String {
|
|
20
76
|
let mut out = String::with_capacity(path.len());
|
|
21
77
|
for c in path.chars() {
|
|
@@ -28,6 +84,21 @@ pub(super) fn encode_projects_dir(path: &str) -> String {
|
|
|
28
84
|
out
|
|
29
85
|
}
|
|
30
86
|
|
|
87
|
+
/// ---
|
|
88
|
+
/// purpose: 有 pending id 时直达那一个 transcript 文件,读头验明身份后给出唯一候选
|
|
89
|
+
/// params:
|
|
90
|
+
/// context: 需要 expected_session_id;projects 根优先取 provider_projects_root,否则退到 HOME/.claude/projects;spawn_cwd 决定编码后的子目录
|
|
91
|
+
/// returns: 四道校验全过则一条 FsWatch/High 候选(带 embedded 身份与是否与本席位一致);任一条不过则空向量
|
|
92
|
+
/// contract:
|
|
93
|
+
/// provides:
|
|
94
|
+
/// - name: scan_expected_session
|
|
95
|
+
/// what: 只读该文件头 64KB;不遍历目录、不比较 mtime
|
|
96
|
+
/// boundary:
|
|
97
|
+
/// - 无 expected_session_id / 无法确定 projects 根 / 编码为空 / 文件读不出来 → 空向量
|
|
98
|
+
/// - 四道校验:sessionId 与 expected 相等、存在 user 或 assistant 记录、不含 leader marker、至少一条记录有 cwd 字段
|
|
99
|
+
/// - embedded 身份与本席位不符时仍返回候选(positive_agent_id_match=false),是否拒绝交给上游分配器判定
|
|
100
|
+
/// - agent_path_match 恒 false:直达路径下文件名就是 uuid,不含席位名
|
|
101
|
+
/// ---
|
|
31
102
|
pub(super) fn scan_expected_session(
|
|
32
103
|
context: &CaptureSessionContext,
|
|
33
104
|
) -> Vec<CapturedSessionCandidate> {
|
|
@@ -87,6 +158,20 @@ pub(super) fn scan_expected_session(
|
|
|
87
158
|
}]
|
|
88
159
|
}
|
|
89
160
|
|
|
161
|
+
/// ---
|
|
162
|
+
/// purpose: 判断一条 transcript 是不是 leader 的会话,供捕获与 event-log 修复两条通道共用排除
|
|
163
|
+
/// params:
|
|
164
|
+
/// provider: 非 Claude/ClaudeCode 一律直接判否
|
|
165
|
+
/// rollout_path: 待判定的 transcript 路径
|
|
166
|
+
/// returns: 读得到头且头部记录里出现 leader marker 才为 true
|
|
167
|
+
/// contract:
|
|
168
|
+
/// provides:
|
|
169
|
+
/// - name: rollout_path_has_leader_marker
|
|
170
|
+
/// what: 只读头 64KB
|
|
171
|
+
/// boundary:
|
|
172
|
+
/// - 文件打不开、解析不出记录一律返回 false —— 判据是 fail-open 的:读不到不等于不是 leader
|
|
173
|
+
/// - marker 只在头窗口内查;超出 64KB 之后才出现的 marker 看不见
|
|
174
|
+
/// ---
|
|
90
175
|
pub(crate) fn rollout_path_has_leader_marker(provider: Provider, rollout_path: &Path) -> bool {
|
|
91
176
|
if !matches!(provider, Provider::Claude | Provider::ClaudeCode) {
|
|
92
177
|
return false;
|
|
@@ -99,6 +184,18 @@ pub(crate) fn rollout_path_has_leader_marker(provider: Provider, rollout_path: &
|
|
|
99
184
|
records_have_leader_marker(&records)
|
|
100
185
|
}
|
|
101
186
|
|
|
187
|
+
/// ---
|
|
188
|
+
/// purpose: 在已解析的记录里找 leader 身份 marker
|
|
189
|
+
/// params:
|
|
190
|
+
/// records: 已解析的 transcript 记录切片
|
|
191
|
+
/// returns: 任一记录的 customTitle 或 agentName 小写后等于 "claude leader" 即 true
|
|
192
|
+
/// contract:
|
|
193
|
+
/// provides:
|
|
194
|
+
/// - name: records_have_leader_marker
|
|
195
|
+
/// what: 纯内存判定,无 I/O
|
|
196
|
+
/// boundary:
|
|
197
|
+
/// - 判据是精确串相等(仅大小写不敏感),不做包含匹配、不认其它别名
|
|
198
|
+
/// ---
|
|
102
199
|
pub(super) fn records_have_leader_marker(records: &[serde_json::Value]) -> bool {
|
|
103
200
|
records.iter().any(|record| {
|
|
104
201
|
let custom_title = record
|
|
@@ -114,10 +211,37 @@ pub(super) fn records_have_leader_marker(records: &[serde_json::Value]) -> bool
|
|
|
114
211
|
})
|
|
115
212
|
}
|
|
116
213
|
|
|
214
|
+
/// ---
|
|
215
|
+
/// purpose: 判断一条 claude 记录是否带 cwd 字段——用作 transcript 是否够格当候选的最低门槛
|
|
216
|
+
/// params:
|
|
217
|
+
/// record: 单条已解析记录
|
|
218
|
+
/// returns: common::record_cwd 能取到值即 true
|
|
219
|
+
/// contract:
|
|
220
|
+
/// provides:
|
|
221
|
+
/// - name: has_cwd_field
|
|
222
|
+
/// what: 只判字段有无,不比较 cwd 是否等于席位 cwd
|
|
223
|
+
/// boundary:
|
|
224
|
+
/// - 不做路径等价判定;是否同 cwd 由调用方另行判断
|
|
225
|
+
/// ---
|
|
117
226
|
pub(super) fn has_cwd_field(record: &serde_json::Value) -> bool {
|
|
118
227
|
super::common::record_cwd(record).is_some()
|
|
119
228
|
}
|
|
120
229
|
|
|
230
|
+
/// ---
|
|
231
|
+
/// purpose: 用 pending id 收窄通用扫描的结果,把「可能是它」压成「就是它」或「至少身份阳性」
|
|
232
|
+
/// params:
|
|
233
|
+
/// context: 有 expected_session_id 才做收窄;否则退到时间窗过滤
|
|
234
|
+
/// out: 待收窄的候选列表,按值传入
|
|
235
|
+
/// returns: expected 命中则只留那一条;未命中则只留 positive_agent_id_match 或 agent_path_match 为真的;无 expected 则原表经唯一时间窗过滤后返回
|
|
236
|
+
/// errors: 当前实现不产生 Err;返回 Result 是为与其它 provider 过滤器同形
|
|
237
|
+
/// contract:
|
|
238
|
+
/// provides:
|
|
239
|
+
/// - name: apply_expected_session_filter
|
|
240
|
+
/// what: 纯过滤,不读盘(时间窗分支会取候选文件 mtime)
|
|
241
|
+
/// boundary:
|
|
242
|
+
/// - 未命中 expected 时不返回空而是返回身份阳性子集——弱于「必须命中」,允许分配器再判
|
|
243
|
+
/// - 不排序;expected 优先排序由 common::sort_expected_first_if_needed 另做
|
|
244
|
+
/// ---
|
|
121
245
|
pub(super) fn apply_expected_session_filter(
|
|
122
246
|
context: &CaptureSessionContext,
|
|
123
247
|
mut out: Vec<CapturedSessionCandidate>,
|
|
@@ -1,10 +1,61 @@
|
|
|
1
|
-
//!
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: codex rollout 的时间与归属过滤——用「创建时间」而非 mtime 判新旧,用 rollout 头里的 cwd 判是不是本席位的
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: parse_spawned_at
|
|
6
|
+
//! what: RFC3339 文本 → SystemTime,解析不了给 None(调用方据此 fail-closed 清空候选)
|
|
7
|
+
//! - name: truncate_to_uuid_precision
|
|
8
|
+
//! what: 把时间截到毫秒,与 uuid-v7 里能表达的精度对齐,避免亚毫秒误差把自己的会话判成"太旧"
|
|
9
|
+
//! - name: rollout_created_at
|
|
10
|
+
//! what: 三级取创建时间:uuid-v7 时间戳 → 头记录 created_at → 文件名里的时间戳
|
|
11
|
+
//! - name: apply_expected_session_filter
|
|
12
|
+
//! what: 有 pending id 时只留 session id 精确相等的候选
|
|
13
|
+
//! - name: retain_spawn_cwd
|
|
14
|
+
//! what: 只留 rollout 头记录里 cwd 与席位 spawn_cwd 等价的候选
|
|
15
|
+
//! requires:
|
|
16
|
+
//! - name: super::common
|
|
17
|
+
//! what: 读头、解析记录、record_cwd、paths_equivalent 均复用 common
|
|
18
|
+
//! - name: chrono
|
|
19
|
+
//! what: RFC3339 解析
|
|
20
|
+
//! boundary:
|
|
21
|
+
//! - 只服务 Provider::Codex
|
|
22
|
+
//! - 一律只读:不写盘、不改 plan、不改 state
|
|
23
|
+
//! - 创建时间刻意不取文件 mtime——邻席活动会抬升 mtime,把别人的会话冒充成新的
|
|
24
|
+
//! - 头窗口之外的记录读不到;三级取值全失败时 rollout_created_at 给 None,调用方按"不通过"处理
|
|
25
|
+
//! maturity: wired
|
|
26
|
+
//! ---
|
|
27
|
+
/// ---
|
|
28
|
+
/// purpose: 把持久化在 state 上的 spawned_at 文本解析成时间点
|
|
29
|
+
/// params:
|
|
30
|
+
/// raw: RFC3339 时间串
|
|
31
|
+
/// returns: 解析成功给 UTC SystemTime;失败给 None
|
|
32
|
+
/// contract:
|
|
33
|
+
/// provides:
|
|
34
|
+
/// - name: parse_spawned_at
|
|
35
|
+
/// what: 纯解析,无 I/O、不读系统时钟
|
|
36
|
+
/// boundary:
|
|
37
|
+
/// - 只认 RFC3339;不接受 epoch 秒、不做宽松格式回落
|
|
38
|
+
/// - 返回 None 的处置由调用方定:common::apply_spawned_at_filter 选择清空候选(fail-closed)
|
|
39
|
+
/// ---
|
|
2
40
|
pub(super) fn parse_spawned_at(raw: &str) -> Option<std::time::SystemTime> {
|
|
3
41
|
chrono::DateTime::parse_from_rfc3339(raw)
|
|
4
42
|
.ok()
|
|
5
43
|
.map(|dt| std::time::SystemTime::from(dt.with_timezone(&chrono::Utc)))
|
|
6
44
|
}
|
|
7
45
|
|
|
46
|
+
/// ---
|
|
47
|
+
/// purpose: 把时间点截到毫秒,与 uuid-v7 能表达的精度对齐
|
|
48
|
+
/// params:
|
|
49
|
+
/// timestamp: 待截断的时间点
|
|
50
|
+
/// returns: 同一毫秒的起点;早于 UNIX 纪元或毫秒数溢出 u64 时 None
|
|
51
|
+
/// contract:
|
|
52
|
+
/// provides:
|
|
53
|
+
/// - name: truncate_to_uuid_precision
|
|
54
|
+
/// what: 纯算术,无 I/O
|
|
55
|
+
/// boundary:
|
|
56
|
+
/// - 只向下截断,绝不向上取整——否则会把自己刚开的会话判成"早于 spawn"
|
|
57
|
+
/// - 只在「Codex 且有 expected id」的比较里使用,不是通用时间归一
|
|
58
|
+
/// ---
|
|
8
59
|
pub(super) fn truncate_to_uuid_precision(
|
|
9
60
|
timestamp: std::time::SystemTime,
|
|
10
61
|
) -> Option<std::time::SystemTime> {
|
|
@@ -16,12 +67,40 @@ pub(super) fn truncate_to_uuid_precision(
|
|
|
16
67
|
))
|
|
17
68
|
}
|
|
18
69
|
|
|
70
|
+
/// ---
|
|
71
|
+
/// purpose: 取 codex rollout 的创建时间,作为「这条存档是不是本次 spawn 之后产生的」的判据
|
|
72
|
+
/// params:
|
|
73
|
+
/// path: rollout 文件路径
|
|
74
|
+
/// returns: 三级依次尝试:文件名尾部 uuid-v7 的时间戳 → 头记录里的 created_at → 文件名中的时间戳;全失败给 None
|
|
75
|
+
/// contract:
|
|
76
|
+
/// provides:
|
|
77
|
+
/// - name: rollout_created_at
|
|
78
|
+
/// what: 只读文件名与头 64KB
|
|
79
|
+
/// boundary:
|
|
80
|
+
/// - 刻意不取文件 mtime:mtime 会被后续追加与邻席活动抬升,不是创建时间
|
|
81
|
+
/// - uuid 分支要求版本位为 '7';非 v7 的 uuid 直接落到下一级
|
|
82
|
+
/// - 返回 None 时调用方按"不满足时间窗"处理,不做放行
|
|
83
|
+
/// ---
|
|
19
84
|
pub(super) fn rollout_created_at(path: &std::path::Path) -> Option<std::time::SystemTime> {
|
|
20
85
|
created_at_from_rollout_uuid(path)
|
|
21
86
|
.or_else(|| created_at_from_rollout_head(path))
|
|
22
87
|
.or_else(|| created_at_from_rollout_filename(path))
|
|
23
88
|
}
|
|
24
89
|
|
|
90
|
+
/// ---
|
|
91
|
+
/// purpose: 有 pending id 时把候选收窄到 session id 精确相等的那些
|
|
92
|
+
/// params:
|
|
93
|
+
/// context: 无 expected_session_id 时原样返回,不做任何过滤
|
|
94
|
+
/// out: 候选列表,按值传入并就地 retain
|
|
95
|
+
/// returns: 过滤后的候选;无一条命中则空向量
|
|
96
|
+
/// contract:
|
|
97
|
+
/// provides:
|
|
98
|
+
/// - name: apply_expected_session_filter
|
|
99
|
+
/// what: 纯内存过滤,无 I/O
|
|
100
|
+
/// boundary:
|
|
101
|
+
/// - 判据是 session id 全等;session_id 为 None 的候选一律剔除
|
|
102
|
+
/// - 与 claude 同名函数语义不同:此处未命中即空,不退到"身份阳性子集"
|
|
103
|
+
/// ---
|
|
25
104
|
pub(super) fn apply_expected_session_filter(
|
|
26
105
|
context: &super::CaptureSessionContext,
|
|
27
106
|
mut out: Vec<super::CapturedSessionCandidate>,
|
|
@@ -39,6 +118,20 @@ pub(super) fn apply_expected_session_filter(
|
|
|
39
118
|
out
|
|
40
119
|
}
|
|
41
120
|
|
|
121
|
+
/// ---
|
|
122
|
+
/// purpose: 只保留 rollout 头里记着的 cwd 与本席位 spawn_cwd 等价的候选,挡掉其它工作目录的会话
|
|
123
|
+
/// params:
|
|
124
|
+
/// context: 提供 spawn_cwd 基准
|
|
125
|
+
/// out: 就地过滤的候选列表
|
|
126
|
+
/// contract:
|
|
127
|
+
/// provides:
|
|
128
|
+
/// - name: retain_spawn_cwd
|
|
129
|
+
/// what: 逐个候选读头 64KB 后比对 cwd
|
|
130
|
+
/// boundary:
|
|
131
|
+
/// - 无 rollout_path、读不出头、头里一条 cwd 都没有 → 剔除(fail-closed)
|
|
132
|
+
/// - 等价判定走 common::paths_equivalent,它把「记录 cwd 的父目录等于 spawn_cwd」也算等价
|
|
133
|
+
/// - 只看头窗口内的记录;窗口之外的 cwd 记录看不见
|
|
134
|
+
/// ---
|
|
42
135
|
pub(super) fn retain_spawn_cwd(
|
|
43
136
|
context: &super::CaptureSessionContext,
|
|
44
137
|
out: &mut Vec<super::CapturedSessionCandidate>,
|