mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
feat(vom): update vom algorithm, fix overlay problem with background state machine
This commit is contained in:
@@ -283,11 +283,11 @@ async fn session_user_interrupt_event_cancels_inflight_with_user_aborted() {
|
||||
handle.shutdown().await;
|
||||
}
|
||||
|
||||
/// The pending-interrupt marker must reject a *new* mutating tool
|
||||
/// The pending-interrupt marker must reject a *new* browser-input tool
|
||||
/// call that arrives AFTER the user clicked stop, even when no
|
||||
/// tool was inflight at click time. Under v2 single-use marker
|
||||
/// semantics, the marker survives indefinitely until consumed by
|
||||
/// a mutating tool call — there is no time window. This is the
|
||||
/// an input-dispatching tool call — there is no time window. This is the
|
||||
/// core motivating scenario: agents spend most of their time
|
||||
/// between tool calls (LLM thinking), and the stop button must
|
||||
/// catch a tool dispatched arbitrarily later.
|
||||
@@ -426,12 +426,12 @@ async fn read_only_tool_passes_through_without_consuming_interrupt_marker() {
|
||||
let ws_sink = Arc::new(tokio::sync::Mutex::new(ws_sink));
|
||||
|
||||
// Fake extension: replies to session_start, console, and snapshot
|
||||
// (read-only tools must succeed transparently). Records click count
|
||||
// — must remain 0 because the daemon should reject the click
|
||||
// (passive reads must succeed transparently). Records observe count
|
||||
// — must remain 0 because the daemon should reject transient input
|
||||
// without forwarding.
|
||||
let ws_sink_for_responder = Arc::clone(&ws_sink);
|
||||
let observed_click_count = Arc::new(std::sync::atomic::AtomicUsize::new(0));
|
||||
let observed_click_count_clone = Arc::clone(&observed_click_count);
|
||||
let observed_observe_count = Arc::new(std::sync::atomic::AtomicUsize::new(0));
|
||||
let observed_observe_count_clone = Arc::clone(&observed_observe_count);
|
||||
let responder = tokio::spawn(async move {
|
||||
let mut ws_stream = ws_stream;
|
||||
while let Some(Ok(msg)) = ws_stream.next().await {
|
||||
@@ -458,8 +458,8 @@ async fn read_only_tool_passes_through_without_consuming_interrupt_marker() {
|
||||
"truncated": false
|
||||
})),
|
||||
Method::ToolSnapshot => ResponseBody::Ok(json!({"ok": true})),
|
||||
Method::ToolClick => {
|
||||
observed_click_count_clone
|
||||
Method::ToolObserve => {
|
||||
observed_observe_count_clone
|
||||
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
continue;
|
||||
}
|
||||
@@ -547,26 +547,25 @@ async fn read_only_tool_passes_through_without_consuming_interrupt_marker() {
|
||||
snapshot_outcome
|
||||
);
|
||||
|
||||
// The mutating click that follows MUST still be rejected — the
|
||||
// marker survived the snapshot.
|
||||
let click_outcome = ipc
|
||||
// The transient-input observe that follows MUST still be rejected — the
|
||||
// marker survived the passive reads.
|
||||
let observe_outcome = ipc
|
||||
.call::<_, serde_json::Value>(
|
||||
"click-after-snapshot",
|
||||
Method::ToolClick,
|
||||
"observe-after-snapshot",
|
||||
Method::ToolObserve,
|
||||
Some(json!({
|
||||
"session_id": start.session_id,
|
||||
"ref": "fake-ref-2",
|
||||
})),
|
||||
Duration::from_secs(3),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let err = click_outcome.expect_err("tool.click must be rejected");
|
||||
let err = observe_outcome.expect_err("tool.observe must be rejected");
|
||||
assert_eq!(err.code, ErrorCode::UserAborted, "got {:?}", err);
|
||||
assert_eq!(
|
||||
observed_click_count.load(std::sync::atomic::Ordering::SeqCst),
|
||||
observed_observe_count.load(std::sync::atomic::Ordering::SeqCst),
|
||||
0,
|
||||
"tool.click must NOT have been forwarded after snapshot transparency"
|
||||
"tool.observe must NOT have been forwarded after passive-read transparency"
|
||||
);
|
||||
|
||||
responder.abort();
|
||||
@@ -574,7 +573,7 @@ async fn read_only_tool_passes_through_without_consuming_interrupt_marker() {
|
||||
}
|
||||
|
||||
/// The pending-interrupt marker must survive an arbitrary delay
|
||||
/// between user click and next mutating tool call. This is the
|
||||
/// between user click and next browser-input tool call. This is the
|
||||
/// regression test for v2's core motivating bug: v1's 500ms time
|
||||
/// window dropped interrupts whenever the LLM's thinking phase
|
||||
/// took longer to respond than the window allowed.
|
||||
|
||||
@@ -11,9 +11,9 @@ use bsk::ipc_client::IpcClient;
|
||||
use bsk_protocol::system::{HandshakeParams, HandshakeResult};
|
||||
use bsk_protocol::tools::{
|
||||
ConsoleEntry, ConsoleEntryKind, ConsoleParams, ConsoleResult, GetHtmlParams, GetHtmlResult,
|
||||
NetworkEntry, NetworkEntryKind, NetworkParams, NetworkResult, ScreenshotParams,
|
||||
ScreenshotResult, SessionStartParams, SessionStartResult, SnapshotParams, SnapshotResult,
|
||||
TabInfo, TabListParams, TabListResult, TabScope,
|
||||
NetworkEntry, NetworkEntryKind, NetworkParams, NetworkResult, ObserveParams, ObserveResult,
|
||||
ScreenshotParams, ScreenshotResult, SessionStartParams, SessionStartResult, SnapshotParams,
|
||||
SnapshotResult, TabInfo, TabListParams, TabListResult, TabScope,
|
||||
};
|
||||
use bsk_protocol::{
|
||||
BrowserPeerInfo, ErrorCode, Frame, Method, RequestFrame, ResponseBody, ResponseFrame, RpcError,
|
||||
@@ -462,6 +462,46 @@ async fn snapshot_returns_text_and_ref_count() {
|
||||
handle.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn observe_returns_semantic_text_and_ref_count() {
|
||||
let (handle, sock) = spawn_daemon().await;
|
||||
let mut ws = connect_ext(handle.ws_addr()).await;
|
||||
let _ = do_handshake(&mut ws).await;
|
||||
|
||||
run_extension(ws, |req| {
|
||||
assert_eq!(req.method, Method::ToolObserve);
|
||||
let _: ObserveParams = serde_json::from_value(req.params.clone().unwrap()).unwrap();
|
||||
ResponseBody::Ok(
|
||||
serde_json::to_value(ObserveResult {
|
||||
text: "@vom 1\n @e1 button \"Products\" [hover: Shoes]\n".into(),
|
||||
ref_count: 1,
|
||||
tab_id: 13,
|
||||
truncated: false,
|
||||
dialogs: vec![],
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
let session_id = ipc_session_start(&sock).await;
|
||||
let result: ObserveResult = ipc_tool_call(
|
||||
&sock,
|
||||
Method::ToolObserve,
|
||||
ObserveParams {
|
||||
session_id,
|
||||
tab_id: None,
|
||||
max_depth: None,
|
||||
max_tokens: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("observe ok");
|
||||
assert_eq!(result.ref_count, 1);
|
||||
assert!(result.text.contains("[hover: Shoes]"));
|
||||
assert_eq!(result.tab_id, 13);
|
||||
handle.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn get_html_round_trips_with_ref_alias() {
|
||||
let (handle, sock) = spawn_daemon().await;
|
||||
|
||||
Reference in New Issue
Block a user