mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-06 13:44:21 +08:00
refactor: update cursor request handling and improve parent request management
- Changed `run_id` to `request_id` in `CursorParent` struct for clarity. - Enhanced the `prepare` function to handle parent requests asynchronously, ensuring proper error handling for active runs. - Updated database interactions to include `cursor_request_id` for better tracking of requests. - Added tests to verify the behavior of reused cursor request IDs and their mapping to distinct executions.
This commit is contained in:
@@ -75,7 +75,8 @@ async fn background_subagent_completion_starts_a_simulated_parent_turn() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(messages.iter().any(|message| {
|
||||
message.runtime_event_id.as_deref() == Some("run-request:completion-request")
|
||||
message.runtime_event_id.as_deref()
|
||||
== Some("background-completed:BACKGROUND_TASK_KIND_SUBAGENT:child-id")
|
||||
&& matches!(&message.content, MessageContent::Parts { parts } if !parts.is_empty())
|
||||
}));
|
||||
|
||||
@@ -130,8 +131,8 @@ async fn background_subagent_completion_starts_a_simulated_parent_turn() {
|
||||
assert_eq!(
|
||||
runtime_ids,
|
||||
[
|
||||
"runtime:run-request:completion-request",
|
||||
"runtime:run-request:completion-request-2"
|
||||
"runtime:background-completed:BACKGROUND_TASK_KIND_SUBAGENT:child-id",
|
||||
"runtime:background-completed:BACKGROUND_TASK_KIND_SUBAGENT:child-id-2"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -169,8 +169,16 @@ async fn eligible_pending_checkpoint_resumes_tools_before_the_next_model_call()
|
||||
1,
|
||||
"resume must execute the pending batch before calling the model"
|
||||
);
|
||||
let resumed_run_id = store
|
||||
.active_run_for_cursor_request("resumed-run")
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let resumed_round = store
|
||||
.tool_round(&ToolRoundId::new("resumed-run:round:resume"))
|
||||
.tool_round(&ToolRoundId::new(format!(
|
||||
"{}:round:resume",
|
||||
resumed_run_id.as_str()
|
||||
)))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
@@ -200,6 +200,7 @@ async fn prepared(store: &cursor_server::store::Store) -> PreparedRun {
|
||||
let root = store.ensure_conversation(&conversation_id).await.unwrap();
|
||||
PreparedRun {
|
||||
run_id: RunId::new("run"),
|
||||
cursor_request_id: None,
|
||||
conversation_id,
|
||||
kind: RunKind::Root,
|
||||
model: ModelSpec::new("model"),
|
||||
|
||||
@@ -54,6 +54,7 @@ async fn a_replaced_run_cannot_overwrite_its_cancelled_status() {
|
||||
let base_revision_id = store.ensure_conversation(&conversation_id).await.unwrap();
|
||||
let prepared = |run_id: &str| PreparedRun {
|
||||
run_id: RunId::new(run_id),
|
||||
cursor_request_id: None,
|
||||
conversation_id: conversation_id.clone(),
|
||||
kind: RunKind::Root,
|
||||
model: ModelSpec::new("model"),
|
||||
|
||||
@@ -137,6 +137,7 @@ async fn call_summary_is_always_stored_and_payloads_follow_detailed_setting() {
|
||||
store
|
||||
.claim_run(&PreparedRun {
|
||||
run_id: RunId::new("run-1"),
|
||||
cursor_request_id: None,
|
||||
conversation_id,
|
||||
kind: RunKind::Root,
|
||||
model: ModelSpec::new(call.model_hash.clone()),
|
||||
|
||||
@@ -12,6 +12,7 @@ fn prepared(
|
||||
) -> PreparedRun {
|
||||
PreparedRun {
|
||||
run_id: RunId::new(run_id),
|
||||
cursor_request_id: None,
|
||||
conversation_id: conversation_id.clone(),
|
||||
kind: RunKind::Root,
|
||||
model: ModelSpec::new("test-model"),
|
||||
@@ -89,6 +90,35 @@ async fn selecting_an_old_revision_creates_a_branch_without_old_suffixes() {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reused_cursor_request_id_maps_to_the_current_distinct_execution() {
|
||||
let (_directory, store) = fixtures::temp_store().await;
|
||||
let conversation_id = ConversationId::new("queued-conversation");
|
||||
let root = store.ensure_conversation(&conversation_id).await.unwrap();
|
||||
|
||||
let mut first = prepared("reused-request:11111111", &conversation_id, root);
|
||||
first.cursor_request_id = Some("reused-request".into());
|
||||
store.claim_run(&first).await.unwrap();
|
||||
assert_eq!(
|
||||
store
|
||||
.active_run_for_cursor_request("reused-request")
|
||||
.await
|
||||
.unwrap(),
|
||||
Some(first.run_id.clone())
|
||||
);
|
||||
|
||||
let mut second = prepared("reused-request:22222222", &conversation_id, root);
|
||||
second.cursor_request_id = Some("reused-request".into());
|
||||
store.claim_run(&second).await.unwrap();
|
||||
assert_eq!(
|
||||
store
|
||||
.active_run_for_cursor_request("reused-request")
|
||||
.await
|
||||
.unwrap(),
|
||||
Some(second.run_id)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn identical_runtime_event_is_exactly_once_and_conflicts_are_rejected() {
|
||||
let (_directory, store) = fixtures::temp_store().await;
|
||||
|
||||
@@ -117,7 +117,7 @@ async fn unchanged_request_context_is_not_repeated_and_preserves_the_provider_pr
|
||||
};
|
||||
assert_eq!(
|
||||
request.history[1].message_id,
|
||||
"runtime:run-request:ask-request"
|
||||
"runtime:cursor:user:wire-user"
|
||||
);
|
||||
assert!(!request.prompt.instructions.contains("workspace rule"));
|
||||
assert!(!request.prompt.instructions.contains("<mcp_meta_tools>"));
|
||||
|
||||
@@ -12,6 +12,7 @@ async fn runtime_event_is_appended_exactly_once() {
|
||||
let root = store.ensure_conversation(&conversation_id).await.unwrap();
|
||||
let run = PreparedRun {
|
||||
run_id: RunId::new("run"),
|
||||
cursor_request_id: None,
|
||||
conversation_id: conversation_id.clone(),
|
||||
kind: RunKind::Root,
|
||||
model: ModelSpec::new("model"),
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use cursor_server::store::Store;
|
||||
use sqlx::{migrate::Migrator, sqlite::SqliteConnectOptions, Row};
|
||||
|
||||
#[tokio::test]
|
||||
async fn version_two_database_upgrades_with_cursor_request_mapping() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let database = directory.path().join("upgrade.db");
|
||||
let pool = sqlx::SqlitePool::connect_with(
|
||||
SqliteConnectOptions::new()
|
||||
.filename(&database)
|
||||
.create_if_missing(true),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let all = sqlx::migrate!("./migrations");
|
||||
let prior = Migrator {
|
||||
migrations: Cow::Owned(
|
||||
all.iter()
|
||||
.filter(|migration| migration.version <= 2)
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
ignore_missing: false,
|
||||
locking: true,
|
||||
no_tx: false,
|
||||
};
|
||||
prior.run(&pool).await.unwrap();
|
||||
drop(pool);
|
||||
|
||||
let store = Store::connect(&format!("sqlite://{}", database.display()))
|
||||
.await
|
||||
.unwrap();
|
||||
let columns = sqlx::query("PRAGMA table_info(runs)")
|
||||
.fetch_all(store.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(columns
|
||||
.iter()
|
||||
.any(|column| column.get::<String, _>("name") == "cursor_request_id"));
|
||||
}
|
||||
@@ -89,7 +89,7 @@ async fn selected_image_bytes_flow_from_run_request_to_history_providers_and_che
|
||||
let user = requests[0]
|
||||
.history
|
||||
.iter()
|
||||
.find(|message| message.message_id == "runtime:run-request:image-run")
|
||||
.find(|message| message.message_id == "runtime:cursor:user:image-user")
|
||||
.unwrap();
|
||||
let ProjectedContent::Parts(parts) = &user.content else {
|
||||
panic!("runtime user message must retain typed parts")
|
||||
@@ -128,7 +128,7 @@ async fn selected_image_bytes_flow_from_run_request_to_history_providers_and_che
|
||||
let id = BlobId::from_bytes(raw_id).unwrap();
|
||||
let bytes = store.get_blob(&id).await.unwrap().unwrap();
|
||||
let value: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
|
||||
if value["id"] == "runtime:run-request:image-run" {
|
||||
if value["id"] == "runtime:cursor:user:image-user" {
|
||||
user_root = Some(value);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ async fn text_turn_runs_from_bidi_request_through_checkpoint_and_end_stream() {
|
||||
.unwrap();
|
||||
assert!(messages[0].message_id.starts_with("request-context:"));
|
||||
assert_eq!(messages[0].role, Role::User);
|
||||
assert_eq!(messages[1].message_id, "runtime:run-request:request");
|
||||
assert_eq!(messages[1].message_id, "runtime:cursor:user:user");
|
||||
assert_eq!(messages[1].role, Role::User);
|
||||
assert_eq!(
|
||||
messages.len(),
|
||||
@@ -300,11 +300,14 @@ async fn text_turn_runs_from_bidi_request_through_checkpoint_and_end_stream() {
|
||||
.fetch_all(store.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
stored_runs,
|
||||
vec!["request"],
|
||||
"the concrete request_id, not Cursor's reusable wire run_id, owns the execution"
|
||||
);
|
||||
assert_eq!(stored_runs.len(), 1);
|
||||
let execution_suffix = stored_runs[0]
|
||||
.strip_prefix("request:")
|
||||
.expect("the local Run keeps the Cursor request id as a readable prefix");
|
||||
assert_eq!(execution_suffix.len(), 8);
|
||||
assert!(execution_suffix
|
||||
.bytes()
|
||||
.all(|byte| byte.is_ascii_hexdigit()));
|
||||
}
|
||||
|
||||
fn client_run(conversation_id: &str, text: &str, model_id: &str) -> pb::AgentClientMessage {
|
||||
|
||||
@@ -965,7 +965,7 @@ async fn provider_tool_use_waits_for_client_result_then_calls_provider_again() {
|
||||
.await
|
||||
.unwrap();
|
||||
let provider_call_index: i64 =
|
||||
sqlx::query_scalar("SELECT provider_call_index FROM runs WHERE run_id = ?")
|
||||
sqlx::query_scalar("SELECT provider_call_index FROM runs WHERE cursor_request_id = ?")
|
||||
.bind("tool-request")
|
||||
.fetch_one(&database)
|
||||
.await
|
||||
|
||||
@@ -16,6 +16,7 @@ async fn results_commit_adjacent_pairs_in_arrival_order() {
|
||||
let root = store.ensure_conversation(&conversation_id).await.unwrap();
|
||||
let run = PreparedRun {
|
||||
run_id: RunId::new("run"),
|
||||
cursor_request_id: None,
|
||||
conversation_id: conversation_id.clone(),
|
||||
kind: RunKind::Root,
|
||||
model: ModelSpec::new("model"),
|
||||
|
||||
Reference in New Issue
Block a user