feat(protocol): negotiate Trace v3 with v2 compatibility

Define Trace v3 wire types and generated schemas while keeping protocol
1.1 peers compatible with 1.0. Default record start still produces Trace
v2, and only an explicit trace_version=3 request enables the new shape.
Refs #90

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
“haonan”
2026-08-23 21:26:39 +08:00
committed by GitHub
co-authored by Cursor
parent d05279f991
commit f98198949a
32 changed files with 2573 additions and 671 deletions
+5 -5
View File
@@ -486,7 +486,7 @@ mod m2_tests {
fn fake_status(browsers: Vec<BrowserStatusEntry>, skew: Vec<VersionSkewEntry>) -> StatusResult {
StatusResult {
daemon_version: env!("CARGO_PKG_VERSION").into(),
protocol_version: "1.0".into(),
protocol_version: "1.1".into(),
pid: 1,
uptime_secs: 0,
ws_port: 0,
@@ -571,7 +571,7 @@ mod m2_tests {
session_count: 0,
connected_at_ms: 1,
version_skew: false,
extension_protocol_version: "1.0".into(),
extension_protocol_version: "1.1".into(),
}],
Vec::new(),
);
@@ -592,7 +592,7 @@ mod m2_tests {
session_count: 0,
connected_at_ms: 1,
version_skew: true,
extension_protocol_version: "1.1".into(),
extension_protocol_version: "1.2".into(),
}],
vec![VersionSkewEntry {
instance_id: "alpha".into(),
@@ -600,8 +600,8 @@ mod m2_tests {
label: "Personal".into(),
server_version: env!("CARGO_PKG_VERSION").into(),
client_version: "0.0.9".into(),
server_protocol_version: "1.0".into(),
client_protocol_version: "1.1".into(),
server_protocol_version: "1.1".into(),
client_protocol_version: "1.2".into(),
}],
);
let check = check_browsers_protocol_compatible(Some(&status));
+11 -4
View File
@@ -8,7 +8,7 @@ use anyhow::Context;
use bsk_protocol::Method;
use bsk_protocol::tools::{
RecordAwaitParams, RecordAwaitResult, RecordStartParams, RecordStartResult, RecordStopParams,
RecordStopResult, Trace,
RecordStopResult, RecordedTrace,
};
use clap::{Args, Subcommand};
@@ -98,6 +98,9 @@ fn dispatch_start(args: RecordStartArgs, format: Format) -> Result<(), CliError>
tab_id: args.tab_id,
url: args.url,
purpose: args.purpose.clone(),
max_page_tokens: None,
redact_values: None,
trace_version: None,
};
let start_result = business_rpc::call::<RecordStartParams, RecordStartResult>(
info.sock_path.clone(),
@@ -190,7 +193,7 @@ fn record_await_ipc_timeout(timeout_ms: u32) -> Duration {
.unwrap_or(Duration::from_secs(u64::from(timeout_ms / 1_000) + 15))
}
fn write_trace_file(output: &PathBuf, trace: &Trace) -> Result<(), CliError> {
fn write_trace_file(output: &PathBuf, trace: &RecordedTrace) -> Result<(), CliError> {
let json = serde_json::to_string_pretty(trace)
.context("serialize trace JSON")
.map_err(CliError::Local)?;
@@ -207,7 +210,7 @@ fn write_trace_file(output: &PathBuf, trace: &Trace) -> Result<(), CliError> {
Ok(())
}
fn render_finish(trace: &Trace, output: &PathBuf, format: Format) -> Result<(), CliError> {
fn render_finish(trace: &RecordedTrace, output: &PathBuf, format: Format) -> Result<(), CliError> {
match format {
Format::Json => {
println!(
@@ -221,7 +224,11 @@ fn render_finish(trace: &Trace, output: &PathBuf, format: Format) -> Result<(),
);
}
Format::Human => {
println!("saved {} steps to {}", trace.steps.len(), output.display());
let step_count = match trace {
RecordedTrace::V2(trace) => trace.steps.len(),
RecordedTrace::V3(trace) => trace.steps.len(),
};
println!("saved {step_count} steps to {}", output.display());
}
}
Ok(())