@team-agent/installer 0.5.65 → 0.5.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +8 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +6 -0
- package/crates/team-agent/src/cli/adapters.rs +6 -1
- package/crates/team-agent/src/cli/diagnose.rs +82 -0
- package/crates/team-agent/src/cli/emit.rs +3 -3
- package/crates/team-agent/src/cli/grok_slot.rs +299 -0
- package/crates/team-agent/src/cli/leader.rs +21 -7
- package/crates/team-agent/src/cli/leaders.rs +125 -1
- package/crates/team-agent/src/cli/mod.rs +20 -0
- package/crates/team-agent/src/cli/send/presentation.rs +7 -1
- package/crates/team-agent/src/cli/spec.rs +2 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
- package/crates/team-agent/src/cli/tests/base.rs +26 -3
- package/crates/team-agent/src/cli/tests/compile.rs +1 -1
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
- package/crates/team-agent/src/cli/tests/mod.rs +2 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
- package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
- package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
- package/crates/team-agent/src/communication_mode/mod.rs +6 -4
- package/crates/team-agent/src/compiler/tests.rs +23 -20
- package/crates/team-agent/src/compiler.rs +84 -5
- package/crates/team-agent/src/conpty/backend.rs +16 -0
- package/crates/team-agent/src/coordinator/backoff.rs +55 -0
- package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
- package/crates/team-agent/src/coordinator/health.rs +173 -0
- package/crates/team-agent/src/coordinator/mod.rs +31 -0
- package/crates/team-agent/src/coordinator/orphan.rs +31 -0
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
- package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
- package/crates/team-agent/src/coordinator/tick.rs +133 -22
- package/crates/team-agent/src/coordinator/types.rs +49 -0
- package/crates/team-agent/src/db/message_store.rs +39 -5
- package/crates/team-agent/src/layout/worker_env.rs +20 -3
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
- package/crates/team-agent/src/leader/registry.rs +65 -0
- package/crates/team-agent/src/leader/start.rs +1080 -58
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/display.rs +56 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
- package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
- package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
- package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
- package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
- package/crates/team-agent/src/lifecycle/launch.rs +209 -43
- package/crates/team-agent/src/lifecycle/lock.rs +42 -1
- package/crates/team-agent/src/lifecycle/mod.rs +11 -0
- package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
- package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
- package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
- package/crates/team-agent/src/lifecycle/restart.rs +29 -0
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
- package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
- package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
- package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
- package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
- package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
- package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
- package/crates/team-agent/src/lifecycle/tests.rs +23 -15
- package/crates/team-agent/src/lifecycle/types.rs +61 -2
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
- package/crates/team-agent/src/mcp_server/tests.rs +196 -2
- package/crates/team-agent/src/messaging/delivery.rs +405 -40
- package/crates/team-agent/src/messaging/helpers.rs +2 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
- package/crates/team-agent/src/messaging/results.rs +4 -0
- package/crates/team-agent/src/messaging/send.rs +34 -0
- package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
- package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/types.rs +4 -1
- package/crates/team-agent/src/model/enums.rs +14 -5
- package/crates/team-agent/src/model/permissions.rs +3 -0
- package/crates/team-agent/src/model/spec.rs +129 -6
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
- package/crates/team-agent/src/os_probe.rs +73 -2
- package/crates/team-agent/src/provider/adapter.rs +231 -4
- package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
- package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
- package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
- package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
- package/crates/team-agent/src/provider/classify.rs +4 -2
- package/crates/team-agent/src/provider/faults.rs +2 -1
- package/crates/team-agent/src/provider/mod.rs +13 -0
- package/crates/team-agent/src/provider/session/capture.rs +158 -20
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
- package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
- package/crates/team-agent/src/provider/session/mod.rs +18 -0
- package/crates/team-agent/src/provider/session/resume.rs +57 -0
- package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
- package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
- package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
- package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
- package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
- package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
- package/crates/team-agent/src/provider/session_scan.rs +8 -0
- package/crates/team-agent/src/provider/submit_now.rs +94 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
- package/crates/team-agent/src/provider/types.rs +4 -2
- package/crates/team-agent/src/provider/wire.rs +23 -1
- package/crates/team-agent/src/state/persist.rs +301 -8
- package/crates/team-agent/src/state/repository.rs +5 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
- package/crates/team-agent/src/tmux_backend.rs +1083 -75
- package/crates/team-agent/src/transport/test_support.rs +20 -0
- package/crates/team-agent/src/transport/tests/wire.rs +28 -2
- package/crates/team-agent/src/transport.rs +265 -1
- package/npm/install.mjs +129 -73
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +33 -238
- package/skills/team-agent/command-coverage.json +31 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
|
@@ -35,6 +35,7 @@ struct OfflineState {
|
|
|
35
35
|
calls: Vec<&'static str>,
|
|
36
36
|
spawns: Vec<SpawnRecord>,
|
|
37
37
|
pane_titles: Vec<(String, String, String, String)>,
|
|
38
|
+
window_options: Vec<(String, String)>,
|
|
38
39
|
inject_targets: Vec<Target>,
|
|
39
40
|
inject_payloads: Vec<String>,
|
|
40
41
|
tmux_endpoint: Option<String>,
|
|
@@ -70,6 +71,7 @@ impl Default for OfflineState {
|
|
|
70
71
|
calls: Vec::new(),
|
|
71
72
|
spawns: Vec::new(),
|
|
72
73
|
pane_titles: Vec::new(),
|
|
74
|
+
window_options: Vec::new(),
|
|
73
75
|
inject_targets: Vec::new(),
|
|
74
76
|
inject_payloads: Vec::new(),
|
|
75
77
|
tmux_endpoint: None,
|
|
@@ -267,6 +269,10 @@ impl OfflineTransport {
|
|
|
267
269
|
self.with_state(|state| state.pane_titles.clone())
|
|
268
270
|
}
|
|
269
271
|
|
|
272
|
+
pub fn window_options(&self) -> Vec<(String, String)> {
|
|
273
|
+
self.with_state(|state| state.window_options.clone())
|
|
274
|
+
}
|
|
275
|
+
|
|
270
276
|
pub fn inject_targets(&self) -> Vec<Target> {
|
|
271
277
|
self.with_state(|state| state.inject_targets.clone())
|
|
272
278
|
}
|
|
@@ -555,6 +561,20 @@ impl Transport for OfflineTransport {
|
|
|
555
561
|
Ok(SetEnvOutcome::Applied)
|
|
556
562
|
}
|
|
557
563
|
|
|
564
|
+
fn set_window_option(
|
|
565
|
+
&self,
|
|
566
|
+
_session: &SessionName,
|
|
567
|
+
_window: &WindowName,
|
|
568
|
+
option: &str,
|
|
569
|
+
value: &str,
|
|
570
|
+
) -> Result<(), TransportError> {
|
|
571
|
+
self.with_state(|state| {
|
|
572
|
+
state.calls.push("set_window_option");
|
|
573
|
+
state.window_options.push((option.to_string(), value.to_string()));
|
|
574
|
+
});
|
|
575
|
+
Ok(())
|
|
576
|
+
}
|
|
577
|
+
|
|
558
578
|
fn kill_session(&self, session: &SessionName) -> Result<(), TransportError> {
|
|
559
579
|
self.with_state(|state| {
|
|
560
580
|
state.calls.push("kill_session");
|
|
@@ -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
|
}
|
|
@@ -762,6 +848,23 @@ pub trait Transport: Send + Sync {
|
|
|
762
848
|
value: &str,
|
|
763
849
|
) -> Result<SetEnvOutcome, TransportError>;
|
|
764
850
|
|
|
851
|
+
/// A-55 (window rename race): set a tmux per-window option (e.g.
|
|
852
|
+
/// `allow-rename off`, `automatic-rename off`) on an existing window.
|
|
853
|
+
/// tmux=`set-window-option -t SESSION:WINDOW`; non-tmux backends refuse
|
|
854
|
+
/// via the default (typed unsupported).
|
|
855
|
+
fn set_window_option(
|
|
856
|
+
&self,
|
|
857
|
+
_session: &SessionName,
|
|
858
|
+
_window: &WindowName,
|
|
859
|
+
_option: &str,
|
|
860
|
+
_value: &str,
|
|
861
|
+
) -> Result<(), TransportError> {
|
|
862
|
+
Err(TransportError::MuxUnavailable {
|
|
863
|
+
backend: self.kind(),
|
|
864
|
+
detail: "set-window-option is tmux-only".to_string(),
|
|
865
|
+
})
|
|
866
|
+
}
|
|
867
|
+
|
|
765
868
|
// —— LIFECYCLE(SL)——
|
|
766
869
|
|
|
767
870
|
fn kill_server(&self) -> Result<(), TransportError> {
|
|
@@ -815,6 +918,14 @@ pub enum PaneMode {
|
|
|
815
918
|
/// 把抽象 `Key` 翻译成 tmux send-keys 字面键名(各后端翻译,不透传字面量;§gap-5)。
|
|
816
919
|
/// tmux:Enter/Up/Down/Left/Right/数字字符/C-c;CancelMode 不是单键(走 cancel_mode_argv)。
|
|
817
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
|
+
/// ---
|
|
818
929
|
pub fn tmux_key_name(key: Key) -> &'static str {
|
|
819
930
|
match key {
|
|
820
931
|
Key::Enter => "Enter",
|
|
@@ -843,6 +954,14 @@ pub fn tmux_key_name(key: Key) -> &'static str {
|
|
|
843
954
|
|
|
844
955
|
/// `send-keys -t <target> <k1> <k2> ...`(键名经 `tmux_key_name` 翻译)。
|
|
845
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
|
+
/// ---
|
|
846
965
|
pub fn tmux_send_keys_argv(pane: &PaneId, keys: &[Key]) -> Vec<String> {
|
|
847
966
|
let mut argv = vec![
|
|
848
967
|
"tmux".to_string(),
|
|
@@ -860,8 +979,74 @@ pub fn tmux_send_keys_argv(pane: &PaneId, keys: &[Key]) -> Vec<String> {
|
|
|
860
979
|
argv
|
|
861
980
|
}
|
|
862
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
|
+
|
|
863
1040
|
/// CancelMode 在 tmux 上按 pane mode 分派退出键(tmux_io.py:419-426)。
|
|
864
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
|
+
/// ---
|
|
865
1050
|
pub fn tmux_cancel_mode_argv(pane: &PaneId, mode: PaneMode) -> Vec<String> {
|
|
866
1051
|
let mut argv = vec![
|
|
867
1052
|
"tmux".to_string(),
|
|
@@ -883,6 +1068,14 @@ pub fn tmux_cancel_mode_argv(pane: &PaneId, mode: PaneMode) -> Vec<String> {
|
|
|
883
1068
|
/// CaptureRange → `capture-pane -p -S <spec> -t <target>`。
|
|
884
1069
|
/// golden:`Tail(40)` → `-S -40`(tmux_prompt.py:149/tmux_io.py:410);
|
|
885
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
|
+
/// ---
|
|
886
1079
|
pub fn tmux_capture_argv(pane: &PaneId, range: CaptureRange) -> Vec<String> {
|
|
887
1080
|
let spec = match range {
|
|
888
1081
|
CaptureRange::Tail(lines) => format!("-{lines}"),
|
|
@@ -907,6 +1100,14 @@ pub fn tmux_capture_argv(pane: &PaneId, range: CaptureRange) -> Vec<String> {
|
|
|
907
1100
|
/// PaneField → `display-message -p -t <target> [-F] <fmt>`。
|
|
908
1101
|
/// golden:PaneWidth → `-F '#{pane_width}'`(delivery.py:34);
|
|
909
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
|
+
/// ---
|
|
910
1111
|
pub fn tmux_query_argv(pane: &PaneId, field: PaneField) -> Vec<String> {
|
|
911
1112
|
let fmt = match field {
|
|
912
1113
|
PaneField::PaneId => "#{pane_id}",
|
|
@@ -936,6 +1137,16 @@ pub fn tmux_query_argv(pane: &PaneId, field: PaneField) -> Vec<String> {
|
|
|
936
1137
|
/// first → `new-session -d -P -F #{pane_id} -s <s> -n <w> sh -lc <cmd>`
|
|
937
1138
|
/// into → `new-window -d -P -F #{pane_id} -t <s> -n <w> sh -lc <cmd>`
|
|
938
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
|
+
/// ---
|
|
939
1150
|
pub fn tmux_spawn_argv(
|
|
940
1151
|
session: &SessionName,
|
|
941
1152
|
window: &WindowName,
|
|
@@ -990,6 +1201,16 @@ pub fn tmux_spawn_argv(
|
|
|
990
1201
|
/// set-buffer → `set-buffer -b <buf> <text>`
|
|
991
1202
|
/// paste-buffer→ `paste-buffer -t <target> -b <buf> -p`(-p = bracketed)
|
|
992
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
|
+
/// ---
|
|
993
1214
|
pub fn tmux_inject_text_argv(
|
|
994
1215
|
pane: &PaneId,
|
|
995
1216
|
buffer_name: &str,
|
|
@@ -1039,13 +1260,29 @@ pub fn tmux_inject_text_argv(
|
|
|
1039
1260
|
|
|
1040
1261
|
/// 空文本 inject:纯 `send-keys -t <target> <submit_key>`,**禁** buffer 路径
|
|
1041
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
|
+
/// ---
|
|
1042
1272
|
pub fn tmux_empty_inject_argv(pane: &PaneId, submit: Key) -> Vec<String> {
|
|
1043
|
-
|
|
1273
|
+
tmux_send_submit_argv(pane, submit)
|
|
1044
1274
|
}
|
|
1045
1275
|
|
|
1046
1276
|
/// capture 出口规范化(§4b,design line 399-400):逐行 rstrip 行尾空白 +
|
|
1047
1277
|
/// `\xa0`(NBSP)→`\x20`,保留 box-drawing。recognizer 不为后端开分支。
|
|
1048
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
|
+
/// ---
|
|
1049
1286
|
pub fn normalize_capture(raw: &str) -> String {
|
|
1050
1287
|
raw.replace('\u{a0}', " ")
|
|
1051
1288
|
.split_inclusive('\n')
|
|
@@ -1066,6 +1303,16 @@ pub fn normalize_capture(raw: &str) -> String {
|
|
|
1066
1303
|
/// 真相源:`enter_sent_without_placeholder_check` /
|
|
1067
1304
|
/// `pasted_content_prompt_absent_after_submit` / `send_keys_failed` /
|
|
1068
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
|
+
/// ---
|
|
1069
1316
|
pub fn submit_verification_wire(v: SubmitVerification) -> String {
|
|
1070
1317
|
match v {
|
|
1071
1318
|
SubmitVerification::EnterSentWithoutPlaceholderCheck => {
|
|
@@ -1087,6 +1334,23 @@ pub fn submit_verification_wire(v: SubmitVerification) -> String {
|
|
|
1087
1334
|
}
|
|
1088
1335
|
}
|
|
1089
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
|
+
|
|
1090
1354
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1091
1355
|
// RED contracts — step 9 transport(ROUND-0).
|
|
1092
1356
|
//
|