@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
|
@@ -93,6 +93,27 @@ fn turn_verification_serde_snake_case_byte_locked() {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
#[test]
|
|
97
|
+
fn turn_verification_wire_never_defaults_started() {
|
|
98
|
+
use crate::transport::turn_verification_wire;
|
|
99
|
+
assert_eq!(
|
|
100
|
+
turn_verification_wire(TurnVerification::NotYetObserved),
|
|
101
|
+
"not_yet_observed"
|
|
102
|
+
);
|
|
103
|
+
assert_eq!(
|
|
104
|
+
turn_verification_wire(TurnVerification::LeaderNewTurnBoundaryMissing),
|
|
105
|
+
"leader_new_turn_boundary_missing"
|
|
106
|
+
);
|
|
107
|
+
assert_eq!(
|
|
108
|
+
turn_verification_wire(TurnVerification::LeaderNewTurnBoundaryVerified),
|
|
109
|
+
"leader_new_turn_boundary_verified"
|
|
110
|
+
);
|
|
111
|
+
assert_ne!(
|
|
112
|
+
turn_verification_wire(TurnVerification::NotYetObserved),
|
|
113
|
+
"leader_new_turn_boundary_verified"
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
96
117
|
#[test]
|
|
97
118
|
fn submit_verification_wire_strings_byte_locked() {
|
|
98
119
|
// 09-transport.md 表 §39 + tmux_io.py:64/215-221 + tmux_prompt.py:304/313。
|
|
@@ -349,11 +370,12 @@ fn inject_empty_payload_reports_empty_text_send_keys_and_turn_not_required() {
|
|
|
349
370
|
fn empty_inject_is_single_direct_send_keys_never_buffer() {
|
|
350
371
|
// 命令构造 golden(STEP-9):空文本禁走 buffer,只发一条 send-keys 提交键
|
|
351
372
|
// (tmux_io.py:42 —— tmux 拒空 buffer 会卡 trust prompt)。
|
|
352
|
-
// golden empty_inject_calls 仅 1 条:send-keys -t %7
|
|
373
|
+
// golden empty_inject_calls 仅 1 条:send-keys -t %7 C-m(锁定阳性 CR)。
|
|
353
374
|
assert_eq!(
|
|
354
375
|
tmux_empty_inject_argv(&PaneId::new("%7"), Key::Enter),
|
|
355
|
-
vec!["tmux", "send-keys", "-t", "%7", "
|
|
376
|
+
vec!["tmux", "send-keys", "-t", "%7", "C-m"]
|
|
356
377
|
);
|
|
378
|
+
assert_eq!(crate::transport::tmux_submit_key_name(Key::Enter), "C-m");
|
|
357
379
|
}
|
|
358
380
|
|
|
359
381
|
#[test]
|
|
@@ -519,6 +541,10 @@ fn cancel_mode_is_noop_on_non_tmux_backends() {
|
|
|
519
541
|
tmux_cancel_mode_argv(&PaneId::new("%7"), PaneMode::Unknown),
|
|
520
542
|
vec!["tmux", "send-keys", "-t", "%7", "-X", "cancel"]
|
|
521
543
|
);
|
|
544
|
+
assert_eq!(
|
|
545
|
+
tmux_close_bracketed_paste_argv(&PaneId::new("%7")),
|
|
546
|
+
vec!["tmux", "send-keys", "-t", "%7", "-l", "\u{1b}[201~"]
|
|
547
|
+
);
|
|
522
548
|
}
|
|
523
549
|
|
|
524
550
|
// ════════════════════════════════════════════════════════════════════════
|
|
@@ -1,3 +1,43 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 终端控制面的 Transport trait、typed 载荷,以及 tmux argv 的纯构造函数
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: Transport
|
|
6
|
+
//! what: 后端无关的 spawn / inject / capture / query / liveness / lifecycle 接口;daemon 可达方法一律返回 Result<_, TransportError>
|
|
7
|
+
//! - name: SetEnvOutcome
|
|
8
|
+
//! what: set-env 的能力性拒绝走 typed 变体,不走 Err、不 panic
|
|
9
|
+
//! - name: AttachOutcome
|
|
10
|
+
//! what: attach 的能力性拒绝走 typed 变体,不走 Err、不 panic
|
|
11
|
+
//! - name: tmux_spawn_argv
|
|
12
|
+
//! what: spawn argv 的唯一构造点(first→new-session / 否则 new-window),纯函数可 golden 锁
|
|
13
|
+
//! - name: tmux_inject_text_argv
|
|
14
|
+
//! what: 文本注入三步 argv(装 buffer→paste→删 buffer)的唯一构造点
|
|
15
|
+
//! - name: tmux_send_submit_argv
|
|
16
|
+
//! what: 提交键 argv 的唯一构造点,Enter 锁定为 C-m
|
|
17
|
+
//! - name: tmux_capture_argv
|
|
18
|
+
//! what: capture-pane argv 的唯一构造点
|
|
19
|
+
//! - name: tmux_query_argv
|
|
20
|
+
//! what: display-message 单字段查询 argv 的唯一构造点
|
|
21
|
+
//! - name: tmux_cancel_mode_argv
|
|
22
|
+
//! what: 按 pane mode 分派退出键的 argv 构造点
|
|
23
|
+
//! - name: InjectReport
|
|
24
|
+
//! what: 阶段化注入报告(stage_reached / inject_verification / submit_verification / turn_verification / attempts / submit_diagnostics)
|
|
25
|
+
//! - name: submit_verification_wire
|
|
26
|
+
//! what: SubmitVerification 落 JSON 的唯一拼写来源
|
|
27
|
+
//! - name: turn_verification_wire
|
|
28
|
+
//! what: TurnVerification 落 JSON 的唯一拼写来源
|
|
29
|
+
//! depends:
|
|
30
|
+
//! - crate::model::enums::PaneLiveness
|
|
31
|
+
//! - serde
|
|
32
|
+
//! - thiserror
|
|
33
|
+
//! boundary:
|
|
34
|
+
//! - 不做投递语义(发给谁 / 为何发 / 可见性启发式)——那在 transport 之上的 delivery 层
|
|
35
|
+
//! - 本文件不执行 I/O;真正跑 argv 的是 tmux_backend
|
|
36
|
+
//! - turn_verification 不是投递闸门(Gap42),not_yet_observed 也算成功
|
|
37
|
+
//! - 不透传后端字面键名:抽象 Key 由各后端自行翻译
|
|
38
|
+
//! - submit_diagnostics 不上 typed wire,只在进程内传递
|
|
39
|
+
//! maturity: wired
|
|
40
|
+
//! ---
|
|
1
41
|
//!
|
|
2
42
|
//! step 9 · transport — 控制面 trait + typed payload(ROUND-0 SKELETON).
|
|
3
43
|
//!
|
|
@@ -46,9 +86,21 @@ pub use crate::model::enums::PaneLiveness;
|
|
|
46
86
|
pub struct PaneId(pub String);
|
|
47
87
|
|
|
48
88
|
impl PaneId {
|
|
89
|
+
/// ---
|
|
90
|
+
/// purpose: 把已知的 pane 标识字符串包成 PaneId newtype
|
|
91
|
+
/// params:
|
|
92
|
+
/// s: 后端给出的 pane 标识原文;不做校验、不归一化
|
|
93
|
+
/// returns: 与入参字节相同的 PaneId(serde transparent)
|
|
94
|
+
/// boundary: 不校验格式、不区分 tmux/wezterm/conpty 方言;混入 window/session 名由类型系统在调用点挡
|
|
95
|
+
/// ---
|
|
49
96
|
pub fn new(s: impl Into<String>) -> Self {
|
|
50
97
|
Self(s.into())
|
|
51
98
|
}
|
|
99
|
+
/// ---
|
|
100
|
+
/// purpose: 借出 pane 标识的原始字符串,供 argv 拼装
|
|
101
|
+
/// returns: 内部字符串的借用,与构造时字节一致
|
|
102
|
+
/// boundary: 不做转义、不加引号;shell 引用由调用方负责
|
|
103
|
+
/// ---
|
|
52
104
|
pub fn as_str(&self) -> &str {
|
|
53
105
|
&self.0
|
|
54
106
|
}
|
|
@@ -72,9 +124,21 @@ impl From<&str> for PaneId {
|
|
|
72
124
|
pub struct SessionName(pub String);
|
|
73
125
|
|
|
74
126
|
impl SessionName {
|
|
127
|
+
/// ---
|
|
128
|
+
/// purpose: 把 tmux session 名包成 SessionName newtype
|
|
129
|
+
/// params:
|
|
130
|
+
/// s: session 名原文;不做校验、不归一化
|
|
131
|
+
/// returns: 与入参字节相同的 SessionName(serde transparent)
|
|
132
|
+
/// boundary: 不校验 session 是否存在(那是 Transport::has_session)
|
|
133
|
+
/// ---
|
|
75
134
|
pub fn new(s: impl Into<String>) -> Self {
|
|
76
135
|
Self(s.into())
|
|
77
136
|
}
|
|
137
|
+
/// ---
|
|
138
|
+
/// purpose: 借出 session 名原文,供 argv 拼装
|
|
139
|
+
/// returns: 内部字符串的借用,与构造时字节一致
|
|
140
|
+
/// boundary: 不做转义、不加引号
|
|
141
|
+
/// ---
|
|
78
142
|
pub fn as_str(&self) -> &str {
|
|
79
143
|
&self.0
|
|
80
144
|
}
|
|
@@ -98,9 +162,21 @@ impl From<&str> for SessionName {
|
|
|
98
162
|
pub struct WindowName(pub String);
|
|
99
163
|
|
|
100
164
|
impl WindowName {
|
|
165
|
+
/// ---
|
|
166
|
+
/// purpose: 把 tmux window 名包成 WindowName newtype
|
|
167
|
+
/// params:
|
|
168
|
+
/// s: window 名原文;不做校验、不归一化
|
|
169
|
+
/// returns: 与入参字节相同的 WindowName(serde transparent)
|
|
170
|
+
/// boundary: 不校验 window 是否存在(那是 Transport::list_windows)
|
|
171
|
+
/// ---
|
|
101
172
|
pub fn new(s: impl Into<String>) -> Self {
|
|
102
173
|
Self(s.into())
|
|
103
174
|
}
|
|
175
|
+
/// ---
|
|
176
|
+
/// purpose: 借出 window 名原文,供 argv 拼装
|
|
177
|
+
/// returns: 内部字符串的借用,与构造时字节一致
|
|
178
|
+
/// boundary: 不做转义、不加引号
|
|
179
|
+
/// ---
|
|
104
180
|
pub fn as_str(&self) -> &str {
|
|
105
181
|
&self.0
|
|
106
182
|
}
|
|
@@ -147,6 +223,11 @@ pub enum InjectPayload {
|
|
|
147
223
|
}
|
|
148
224
|
|
|
149
225
|
impl InjectPayload {
|
|
226
|
+
/// ---
|
|
227
|
+
/// purpose: 取出载荷正文,把「有正文」与「空载荷」在类型上分开
|
|
228
|
+
/// returns: Text / TextSkipConsumptionPoll 返回 Some(正文);Empty 返回 None
|
|
229
|
+
/// boundary: 不区分两种带正文的变体(要区分用 skip_consumption_poll);不裁剪、不脱敏
|
|
230
|
+
/// ---
|
|
150
231
|
pub fn text(&self) -> Option<&str> {
|
|
151
232
|
match self {
|
|
152
233
|
Self::Text(text) | Self::TextSkipConsumptionPoll(text) => Some(text),
|
|
@@ -154,6 +235,11 @@ impl InjectPayload {
|
|
|
154
235
|
}
|
|
155
236
|
}
|
|
156
237
|
|
|
238
|
+
/// ---
|
|
239
|
+
/// purpose: 标出「收件 pane 不消费 provider 回合」的载荷,后端据此跳过 post-Enter 消费轮询
|
|
240
|
+
/// returns: 仅 TextSkipConsumptionPoll 为 true
|
|
241
|
+
/// boundary: 只表达「不轮询消费」这一件事,不表达投递成功;调用方不得据此直接判 delivered
|
|
242
|
+
/// ---
|
|
157
243
|
pub fn skip_consumption_poll(&self) -> bool {
|
|
158
244
|
matches!(self, Self::TextSkipConsumptionPoll(_))
|
|
159
245
|
}
|
|
@@ -832,6 +918,14 @@ pub enum PaneMode {
|
|
|
832
918
|
/// 把抽象 `Key` 翻译成 tmux send-keys 字面键名(各后端翻译,不透传字面量;§gap-5)。
|
|
833
919
|
/// tmux:Enter/Up/Down/Left/Right/数字字符/C-c;CancelMode 不是单键(走 cancel_mode_argv)。
|
|
834
920
|
/// 真相源:codex.py:266 `send-keys -t %7 Down Enter`、tmux send-keys 键名约定。
|
|
921
|
+
/// 菜单/方向键序列继续用这里的 `Enter`。投递提交键走 [`tmux_submit_key_name`]。
|
|
922
|
+
/// ---
|
|
923
|
+
/// purpose: 抽象 Key 到 tmux send-keys 字面键名的翻译表
|
|
924
|
+
/// params:
|
|
925
|
+
/// key: 抽象按键;CancelMode 与非数字 Char 无单键拼写
|
|
926
|
+
/// returns: tmux 键名;CancelMode 与未覆盖的 Char 返回空串,由调用方过滤掉
|
|
927
|
+
/// boundary: 不构 argv、不发键;CancelMode 的真正出口是 tmux_cancel_mode_argv;提交键拼写不在这里(见 tmux_submit_key_name)
|
|
928
|
+
/// ---
|
|
835
929
|
pub fn tmux_key_name(key: Key) -> &'static str {
|
|
836
930
|
match key {
|
|
837
931
|
Key::Enter => "Enter",
|
|
@@ -860,6 +954,14 @@ pub fn tmux_key_name(key: Key) -> &'static str {
|
|
|
860
954
|
|
|
861
955
|
/// `send-keys -t <target> <k1> <k2> ...`(键名经 `tmux_key_name` 翻译)。
|
|
862
956
|
/// golden:`[Down, Enter]` → `["tmux","send-keys","-t","%7","Down","Enter"]`(codex.py:266)。
|
|
957
|
+
/// ---
|
|
958
|
+
/// purpose: 构造按键序列的 send-keys argv(菜单/方向键路径)
|
|
959
|
+
/// params:
|
|
960
|
+
/// pane: 目标 pane 标识,直接进 -t
|
|
961
|
+
/// keys: 按顺序翻译成键名;翻译为空串的键被丢弃,不占位
|
|
962
|
+
/// returns: 完整 argv;keys 全为空串时退化成只有前缀的 send-keys(不会报错)
|
|
963
|
+
/// boundary: 纯构造,不执行;不加 socket 参数(-L/-S 由 tmux_backend 的 run chokepoint 补);此处的 Enter 是菜单键名,不是提交拼写
|
|
964
|
+
/// ---
|
|
863
965
|
pub fn tmux_send_keys_argv(pane: &PaneId, keys: &[Key]) -> Vec<String> {
|
|
864
966
|
let mut argv = vec![
|
|
865
967
|
"tmux".to_string(),
|
|
@@ -877,8 +979,74 @@ pub fn tmux_send_keys_argv(pane: &PaneId, keys: &[Key]) -> Vec<String> {
|
|
|
877
979
|
argv
|
|
878
980
|
}
|
|
879
981
|
|
|
982
|
+
/// 投递提交键的 tmux 拼写。
|
|
983
|
+
///
|
|
984
|
+
/// ledger.p0enter 锁定对:`C-j`/LF 5/5 不成回合,`C-m`/CR 5/5 成回合。
|
|
985
|
+
/// `send-keys Enter` 在 raw PTY 上也是 `0d`,但提交路径只发锁定阳性名
|
|
986
|
+
/// `C-m`,避免落到 LF 族。菜单 `send_keys([Down, Enter])` 不走这里。
|
|
987
|
+
/// ---
|
|
988
|
+
/// purpose: 投递提交键的 tmux 拼写:Enter 锁定为 C-m(CR),其余键回落通用翻译
|
|
989
|
+
/// params:
|
|
990
|
+
/// key: 提交键;只有 Enter 有专门拼写
|
|
991
|
+
/// returns: 提交用键名;非 Enter 与 tmux_key_name 同值(含空串情形)
|
|
992
|
+
/// boundary: 只管拼写,不管何时提交、提交是否成功;不发送、不验证
|
|
993
|
+
/// ---
|
|
994
|
+
pub fn tmux_submit_key_name(key: Key) -> &'static str {
|
|
995
|
+
match key {
|
|
996
|
+
Key::Enter => "C-m",
|
|
997
|
+
other => tmux_key_name(other),
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/// 投递提交:`send-keys -t <pane> <submit-spelling>`。
|
|
1002
|
+
/// ---
|
|
1003
|
+
/// purpose: 构造投递提交键的 send-keys argv
|
|
1004
|
+
/// params:
|
|
1005
|
+
/// pane: 目标 pane 标识
|
|
1006
|
+
/// submit: 提交键;经 tmux_submit_key_name 取拼写
|
|
1007
|
+
/// returns: 单条 argv,末位为提交键拼写
|
|
1008
|
+
/// boundary: 纯构造,不执行;tmux 对该 argv 返回 rc=0 不等于回合已提交(copy-mode 会吞键),真伪判定在 tmux_backend 的消费验证
|
|
1009
|
+
/// ---
|
|
1010
|
+
pub fn tmux_send_submit_argv(pane: &PaneId, submit: Key) -> Vec<String> {
|
|
1011
|
+
vec![
|
|
1012
|
+
"tmux".to_string(),
|
|
1013
|
+
"send-keys".to_string(),
|
|
1014
|
+
"-t".to_string(),
|
|
1015
|
+
pane.as_str().to_string(),
|
|
1016
|
+
tmux_submit_key_name(submit).to_string(),
|
|
1017
|
+
]
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/// 闭合未完成的 bracketed paste:字面 CSI `ESC [ 201 ~`。
|
|
1021
|
+
/// 不是 `Escape` 键(E55:重试只重按回车,绝不送 Escape/C-c)。
|
|
1022
|
+
/// ---
|
|
1023
|
+
/// purpose: 构造「字面写入 CSI 201~」的 argv,闭合 composer 里未收尾的 bracketed paste
|
|
1024
|
+
/// params:
|
|
1025
|
+
/// pane: 目标 pane 标识
|
|
1026
|
+
/// returns: 带 -l 的 send-keys argv,写入的是字面转义序列而非键名
|
|
1027
|
+
/// boundary: 绝不用 Escape / Ctrl-C 代替(E55 红线);纯构造,不执行;不判断 pane 当前是否真处于 paste 中
|
|
1028
|
+
/// ---
|
|
1029
|
+
pub fn tmux_close_bracketed_paste_argv(pane: &PaneId) -> Vec<String> {
|
|
1030
|
+
vec![
|
|
1031
|
+
"tmux".to_string(),
|
|
1032
|
+
"send-keys".to_string(),
|
|
1033
|
+
"-t".to_string(),
|
|
1034
|
+
pane.as_str().to_string(),
|
|
1035
|
+
"-l".to_string(),
|
|
1036
|
+
"\u{1b}[201~".to_string(),
|
|
1037
|
+
]
|
|
1038
|
+
}
|
|
1039
|
+
|
|
880
1040
|
/// CancelMode 在 tmux 上按 pane mode 分派退出键(tmux_io.py:419-426)。
|
|
881
1041
|
/// golden:Copy→`-X cancel`,Tree/View→`q`,Client→`d`,Unknown→`-X cancel`(+warn)。
|
|
1042
|
+
/// ---
|
|
1043
|
+
/// purpose: 按 pane 当前 mode 分派退出键,构造离开特殊 mode 的 argv
|
|
1044
|
+
/// params:
|
|
1045
|
+
/// pane: 目标 pane 标识
|
|
1046
|
+
/// mode: 已探到的 pane mode;Unknown 保守走 -X cancel
|
|
1047
|
+
/// returns: 单条 send-keys argv,尾部按 mode 取 -X cancel / q / d
|
|
1048
|
+
/// boundary: 纯构造,不执行、不探测 mode(探测在 tmux_backend);不保证 mode 真的退出了,调用方需复查
|
|
1049
|
+
/// ---
|
|
882
1050
|
pub fn tmux_cancel_mode_argv(pane: &PaneId, mode: PaneMode) -> Vec<String> {
|
|
883
1051
|
let mut argv = vec![
|
|
884
1052
|
"tmux".to_string(),
|
|
@@ -900,6 +1068,14 @@ pub fn tmux_cancel_mode_argv(pane: &PaneId, mode: PaneMode) -> Vec<String> {
|
|
|
900
1068
|
/// CaptureRange → `capture-pane -p -S <spec> -t <target>`。
|
|
901
1069
|
/// golden:`Tail(40)` → `-S -40`(tmux_prompt.py:149/tmux_io.py:410);
|
|
902
1070
|
/// `Full` → `-S -`(runtime.py:519)。
|
|
1071
|
+
/// ---
|
|
1072
|
+
/// purpose: 把 CaptureRange 翻成 capture-pane 的起止参数
|
|
1073
|
+
/// params:
|
|
1074
|
+
/// pane: 目标 pane 标识
|
|
1075
|
+
/// range: Tail(n) 取倒数 n 行 -S -n;Head(n) 取 -S 0 并追加 -E n-1;Full 取 -S -
|
|
1076
|
+
/// returns: 完整 capture-pane argv;Head(0) 时 -E 饱和到 0(不会下溢)
|
|
1077
|
+
/// boundary: 纯构造,不执行、不解析输出;抓回来的文本要经 normalize_capture 才交给识别层
|
|
1078
|
+
/// ---
|
|
903
1079
|
pub fn tmux_capture_argv(pane: &PaneId, range: CaptureRange) -> Vec<String> {
|
|
904
1080
|
let spec = match range {
|
|
905
1081
|
CaptureRange::Tail(lines) => format!("-{lines}"),
|
|
@@ -924,6 +1100,14 @@ pub fn tmux_capture_argv(pane: &PaneId, range: CaptureRange) -> Vec<String> {
|
|
|
924
1100
|
/// PaneField → `display-message -p -t <target> [-F] <fmt>`。
|
|
925
1101
|
/// golden:PaneWidth → `-F '#{pane_width}'`(delivery.py:34);
|
|
926
1102
|
/// PaneMode → `'#{pane_mode}'`(tmux_io.py:403);PaneId → `'#{pane_id}'`(state.py:346)。
|
|
1103
|
+
/// ---
|
|
1104
|
+
/// purpose: 把 PaneField 翻成 display-message 的单字段查询 argv
|
|
1105
|
+
/// params:
|
|
1106
|
+
/// pane: 目标 pane 标识
|
|
1107
|
+
/// field: 待查字段;PaneMode 走不带 -F 的形式,其余字段加 -F
|
|
1108
|
+
/// returns: 完整 display-message argv,末位是 tmux 格式串
|
|
1109
|
+
/// boundary: 纯构造,不执行、不解析;查询失败与「字段不适用」的区分在 Transport::query 的 Option 返回值上
|
|
1110
|
+
/// ---
|
|
927
1111
|
pub fn tmux_query_argv(pane: &PaneId, field: PaneField) -> Vec<String> {
|
|
928
1112
|
let fmt = match field {
|
|
929
1113
|
PaneField::PaneId => "#{pane_id}",
|
|
@@ -953,6 +1137,16 @@ pub fn tmux_query_argv(pane: &PaneId, field: PaneField) -> Vec<String> {
|
|
|
953
1137
|
/// first → `new-session -d -P -F #{pane_id} -s <s> -n <w> sh -lc <cmd>`
|
|
954
1138
|
/// into → `new-window -d -P -F #{pane_id} -t <s> -n <w> sh -lc <cmd>`
|
|
955
1139
|
/// `argv` 被组装成单条 `sh -lc` 命令字符串(provider 启动行)。
|
|
1140
|
+
/// ---
|
|
1141
|
+
/// purpose: 构造 spawn argv:首个席位用 new-session,后续席位用 new-window
|
|
1142
|
+
/// params:
|
|
1143
|
+
/// session: 目标 session 名
|
|
1144
|
+
/// window: 新窗口名
|
|
1145
|
+
/// command: 已拼好的单条 shell 命令行,原样进 sh -lc(本函数不做引用/转义)
|
|
1146
|
+
/// first: true 走 new-session,false 走 new-window
|
|
1147
|
+
/// returns: 完整 argv;两支都带 -d(后台创建,不抢客户端焦点)与 -P -F 使 tmux 回打 pane_id
|
|
1148
|
+
/// boundary: 纯构造,不执行、不校验 session 是否已存在;command 的 shell 引用由调用方(tmux_backend 的 shell wrapper)负责
|
|
1149
|
+
/// ---
|
|
956
1150
|
pub fn tmux_spawn_argv(
|
|
957
1151
|
session: &SessionName,
|
|
958
1152
|
window: &WindowName,
|
|
@@ -1007,6 +1201,16 @@ pub fn tmux_spawn_argv(
|
|
|
1007
1201
|
/// set-buffer → `set-buffer -b <buf> <text>`
|
|
1008
1202
|
/// paste-buffer→ `paste-buffer -t <target> -b <buf> -p`(-p = bracketed)
|
|
1009
1203
|
/// delete-buffer→`delete-buffer -b <buf>`
|
|
1204
|
+
/// ---
|
|
1205
|
+
/// purpose: 构造文本注入的三步 argv 序列:装 buffer → 粘到 pane → 删 buffer
|
|
1206
|
+
/// params:
|
|
1207
|
+
/// pane: 目标 pane 标识
|
|
1208
|
+
/// buffer_name: 本次注入独占的 tmux buffer 名,三步共用同一个名字
|
|
1209
|
+
/// text: 待注入正文;长度 >= 16 KiB 改走 load-buffer - (由 stdin 喂),否则 set-buffer 直传
|
|
1210
|
+
/// bracketed: true 时 paste-buffer 加 -p(bracketed paste)
|
|
1211
|
+
/// returns: 有序的三条 argv:[load 或 set, paste, delete];调用方必须按序执行
|
|
1212
|
+
/// boundary: 纯构造,不执行、不发提交键;空文本禁走这条路径(见 tmux_empty_inject_argv);不保证 buffer 名唯一,去重由调用方负责
|
|
1213
|
+
/// ---
|
|
1010
1214
|
pub fn tmux_inject_text_argv(
|
|
1011
1215
|
pane: &PaneId,
|
|
1012
1216
|
buffer_name: &str,
|
|
@@ -1056,13 +1260,29 @@ pub fn tmux_inject_text_argv(
|
|
|
1056
1260
|
|
|
1057
1261
|
/// 空文本 inject:纯 `send-keys -t <target> <submit_key>`,**禁** buffer 路径
|
|
1058
1262
|
/// (tmux 拒空 buffer 会卡 trust prompt;tmux_io.py:42)。
|
|
1263
|
+
/// `Key::Enter` 在此译成锁定阳性 `C-m`,不是菜单用的 `Enter`。
|
|
1264
|
+
/// ---
|
|
1265
|
+
/// purpose: 空载荷注入(唤醒/ping)的 argv:只发提交键,不碰 buffer
|
|
1266
|
+
/// params:
|
|
1267
|
+
/// pane: 目标 pane 标识
|
|
1268
|
+
/// submit: 提交键;转发给 tmux_send_submit_argv 取拼写
|
|
1269
|
+
/// returns: 与 tmux_send_submit_argv 完全相同的单条 argv
|
|
1270
|
+
/// boundary: 禁走 buffer 路径(tmux 拒空 buffer 会卡 trust prompt);纯构造,不执行、不做任何提交验证
|
|
1271
|
+
/// ---
|
|
1059
1272
|
pub fn tmux_empty_inject_argv(pane: &PaneId, submit: Key) -> Vec<String> {
|
|
1060
|
-
|
|
1273
|
+
tmux_send_submit_argv(pane, submit)
|
|
1061
1274
|
}
|
|
1062
1275
|
|
|
1063
1276
|
/// capture 出口规范化(§4b,design line 399-400):逐行 rstrip 行尾空白 +
|
|
1064
1277
|
/// `\xa0`(NBSP)→`\x20`,保留 box-drawing。recognizer 不为后端开分支。
|
|
1065
1278
|
/// golden:`"line one \nbusy\xa0marker \n \n"` → `"line one\nbusy marker\n\n"`。
|
|
1279
|
+
/// ---
|
|
1280
|
+
/// purpose: capture 出口的统一规范化,让识别层不必为后端开分支
|
|
1281
|
+
/// params:
|
|
1282
|
+
/// raw: 后端抓回的原始屏幕文本
|
|
1283
|
+
/// returns: 逐行去掉行尾空白、NBSP 归一为普通空格的文本;换行位置与行数不变
|
|
1284
|
+
/// boundary: 只做这两件事——不剥 ANSI 转义、不裁剪行数、不脱敏、不改 box-drawing 字符
|
|
1285
|
+
/// ---
|
|
1066
1286
|
pub fn normalize_capture(raw: &str) -> String {
|
|
1067
1287
|
raw.replace('\u{a0}', " ")
|
|
1068
1288
|
.split_inclusive('\n')
|
|
@@ -1083,6 +1303,16 @@ pub fn normalize_capture(raw: &str) -> String {
|
|
|
1083
1303
|
/// 真相源:`enter_sent_without_placeholder_check` /
|
|
1084
1304
|
/// `pasted_content_prompt_absent_after_submit` / `send_keys_failed` /
|
|
1085
1305
|
/// `Enter_sent_after_visible_token`(submit_key 字面)。
|
|
1306
|
+
/// ---
|
|
1307
|
+
/// purpose: SubmitVerification 判定值落 JSON / 事件的唯一拼写来源
|
|
1308
|
+
/// params:
|
|
1309
|
+
/// v: 后端给出的提交验证判定
|
|
1310
|
+
/// returns: 稳定 wire 字符串;KeySentAfterVisibleToken 由 tmux_key_name 拼成 <键名>_sent_after_visible_token
|
|
1311
|
+
/// boundary:
|
|
1312
|
+
/// - 只翻译不判定:不把 SubmitConsumptionUnverified 折成成功值,也不合并任何两个变体
|
|
1313
|
+
/// - 名与语义已知脱节:enter_sent_without_placeholder_check 实际含义是「消费已确认或跳过消费轮询」,读日志时不可按字面理解成未验证
|
|
1314
|
+
/// - 是否算 delivered 不在这里裁定,由 delivery 层的判定表决定
|
|
1315
|
+
/// ---
|
|
1086
1316
|
pub fn submit_verification_wire(v: SubmitVerification) -> String {
|
|
1087
1317
|
match v {
|
|
1088
1318
|
SubmitVerification::EnterSentWithoutPlaceholderCheck => {
|
|
@@ -1104,6 +1334,23 @@ pub fn submit_verification_wire(v: SubmitVerification) -> String {
|
|
|
1104
1334
|
}
|
|
1105
1335
|
}
|
|
1106
1336
|
|
|
1337
|
+
/// ---
|
|
1338
|
+
/// purpose: TurnVerification 的 send JSON / persist 用词
|
|
1339
|
+
/// params:
|
|
1340
|
+
/// v: 回合观测判定
|
|
1341
|
+
/// returns: 与 serde snake_case 一致的固定拼写
|
|
1342
|
+
/// contract: 与 serde snake_case 一致;未知必须是 not_yet_observed,不得写成 verified
|
|
1343
|
+
/// boundary: 不是投递闸门(Gap42);只翻译不判定
|
|
1344
|
+
/// ---
|
|
1345
|
+
pub fn turn_verification_wire(v: TurnVerification) -> &'static str {
|
|
1346
|
+
match v {
|
|
1347
|
+
TurnVerification::LeaderNewTurnBoundaryVerified => "leader_new_turn_boundary_verified",
|
|
1348
|
+
TurnVerification::LeaderNewTurnBoundaryMissing => "leader_new_turn_boundary_missing",
|
|
1349
|
+
TurnVerification::NotYetObserved => "not_yet_observed",
|
|
1350
|
+
TurnVerification::NotRequired => "not_required",
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1107
1354
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1108
1355
|
// RED contracts — step 9 transport(ROUND-0).
|
|
1109
1356
|
//
|