From 847e92c7ea627bd027e9994db7b81eff1e8cdd80 Mon Sep 17 00:00:00 2001 From: leookun Date: Wed, 26 Aug 2026 01:46:30 +0800 Subject: [PATCH] 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. --- .../migrations/0003_run_cursor_request_id.sql | 6 + server/src/cursor/actor.rs | 52 +++++--- server/src/cursor/handlers.rs | 10 +- server/src/cursor/request/prepare.rs | 114 +++++++++++++++--- server/src/cursor/sessions.rs | 6 +- server/src/model/run.rs | 1 + server/src/store/runs.rs | 21 +++- server/tests/background_completion.rs | 7 +- server/tests/checkpoint_recovery.rs | 10 +- server/tests/client_contract.rs | 1 + server/tests/interrupt.rs | 1 + server/tests/provider_console.rs | 1 + server/tests/revision_branch.rs | 30 +++++ server/tests/runtime_modes.rs | 2 +- server/tests/runtime_tag_once.rs | 1 + server/tests/schema_upgrade.rs | 43 +++++++ server/tests/selected_images.rs | 4 +- server/tests/text_turn.rs | 15 ++- server/tests/tool_loop.rs | 2 +- server/tests/tool_order.rs | 1 + 20 files changed, 271 insertions(+), 57 deletions(-) create mode 100644 server/migrations/0003_run_cursor_request_id.sql create mode 100644 server/tests/schema_upgrade.rs diff --git a/server/migrations/0003_run_cursor_request_id.sql b/server/migrations/0003_run_cursor_request_id.sql new file mode 100644 index 0000000..9a12dcc --- /dev/null +++ b/server/migrations/0003_run_cursor_request_id.sql @@ -0,0 +1,6 @@ +-- Cursor may reuse one transport request id for multiple queued executions. +-- Keep that id as an association key while each local Run keeps its own identity. +ALTER TABLE runs ADD COLUMN cursor_request_id TEXT; + +CREATE INDEX idx_runs_cursor_request_active +ON runs(cursor_request_id, status, created_at_ms DESC); diff --git a/server/src/cursor/actor.rs b/server/src/cursor/actor.rs index 590c669..6ca1cdd 100644 --- a/server/src/cursor/actor.rs +++ b/server/src/cursor/actor.rs @@ -89,24 +89,42 @@ impl CursorActor { .map(|parent| parent.tool_call_id.clone()), request.conversation_state.clone(), ); - let parent = handle.parent().map(|parent| { - ( - crate::model::RunId::new(&parent.run_id), - parent.tool_call_id.clone(), + let prepared = async { + let parent = match handle.parent() { + Some(parent) => { + let parent_run_id = dependencies + .store + .active_run_for_cursor_request( + &parent.request_id, + ) + .await? + .ok_or_else(|| { + crate::Error::Protocol(format!( + "Cursor parent request {} has no active local Run", + parent.request_id + )) + })?; + Some(( + parent_run_id, + parent.tool_call_id.clone(), + )) + } + None => None, + }; + request::prepare( + handle.request_id(), + &request, + parent, + request::PrepareDependencies { + compiler: &dependencies.compiler, + store: &dependencies.store, + checkpoint: &checkpoint, + blob_sync: &blob_sync, + context_sync: &context_sync, + }, ) - }); - let prepared = request::prepare( - handle.request_id(), - &request, - parent, - request::PrepareDependencies { - compiler: &dependencies.compiler, - store: &dependencies.store, - checkpoint: &checkpoint, - blob_sync: &blob_sync, - context_sync: &context_sync, - }, - ) + .await + } .await; let (prepared, context) = match prepared { Ok(prepared) => prepared, diff --git a/server/src/cursor/handlers.rs b/server/src/cursor/handlers.rs index 4b292eb..5fe9d5e 100644 --- a/server/src/cursor/handlers.rs +++ b/server/src/cursor/handlers.rs @@ -203,12 +203,12 @@ async fn buffered(request: Request) -> Result<(axum::http::request::Parts, } fn parent_headers(headers: &HeaderMap) -> Result> { - let run_id = header_text(headers, "x-parent-request-id")?; + let request_id = header_text(headers, "x-parent-request-id")?; let tool_call_id = header_text(headers, "x-parent-agent-tool-call-id")?; - match (run_id, tool_call_id) { + match (request_id, tool_call_id) { (None, None) => Ok(None), - (Some(run_id), Some(tool_call_id)) => Ok(Some(CursorParent { - run_id: run_id.into(), + (Some(request_id), Some(tool_call_id)) => Ok(Some(CursorParent { + request_id: request_id.into(), tool_call_id: tool_call_id.into(), })), _ => Err(crate::Error::Protocol( @@ -270,7 +270,7 @@ mod tests { assert_eq!( parent_headers(&headers).unwrap(), Some(CursorParent { - run_id: "parent-run".into(), + request_id: "parent-run".into(), tool_call_id: "parent-call".into(), }) ); diff --git a/server/src/cursor/request/prepare.rs b/server/src/cursor/request/prepare.rs index 1f88129..c2d18bf 100644 --- a/server/src/cursor/request/prepare.rs +++ b/server/src/cursor/request/prepare.rs @@ -1,5 +1,7 @@ use std::collections::BTreeMap; +use uuid::Uuid; + use crate::{ cursor::prompting::{Mode, PromptCompiler}, cursor::{ @@ -71,9 +73,7 @@ pub(crate) async fn prepare( .clone() .unwrap_or_else(|| request_id.into()), ); - // RunSSE/Bidi request_id identifies this concrete execution attempt. Cursor may - // reuse AgentRunRequest.run_id when a queued or subagent-driven attempt resumes. - let run_id = RunId::new(request_id); + let run_id = execution_run_id(request_id); let mut base_messages = if request.conversation_state.is_some() { Some( checkpoint @@ -128,7 +128,7 @@ pub(crate) async fn prepare( starts_turn, compacting, background_completion, - } = action(request_id, request)?; + } = action(request)?; let checkpoint_mode = if request.subagent_type_name.is_some() { Mode::Subagent } else { @@ -286,6 +286,7 @@ pub(crate) async fn prepare( Ok(( PreparedRun { run_id, + cursor_request_id: Some(request_id.into()), conversation_id, kind, model, @@ -348,12 +349,17 @@ fn validate_prompt_root(messages: &[CanonicalMessage]) -> Result<()> { Ok(()) } -fn action(request_id: &str, request: &pb::AgentRunRequest) -> Result { - let mode = request +fn execution_run_id(request_id: &str) -> RunId { + let execution_id = Uuid::new_v4().simple().to_string(); + RunId::new(format!("{request_id}:{}", &execution_id[..8])) +} + +fn action(request: &pb::AgentRunRequest) -> Result { + let conversation_mode = request .conversation_state .as_ref() - .and_then(|state| state.mode) - .unwrap_or(pb::AgentMode::Agent as i32); + .and_then(|state| state.mode); + let mode = conversation_mode.unwrap_or(pb::AgentMode::Agent as i32); let Some(action) = request .action .as_ref() @@ -375,6 +381,11 @@ fn action(request_id: &str, request: &pb::AgentRunRequest) -> Result Result Result Result { let projection = background::project(action, mode)?; + let event_id = projection.turn_user.message_id.clone(); Ok(ActionProjection { mode, action_context: projection.context, - event_id: Some(format!("run-request:{request_id}")), + event_id: Some(event_id), input_id: None, turn_user: Some(projection.turn_user), starts_turn: true, @@ -598,6 +611,18 @@ mod tests { )); } + #[test] + fn execution_run_id_keeps_the_request_id_and_adds_eight_uuid_hex_digits() { + let run_id = execution_run_id("01bba7c5-9c00-4922-b1df-1f58146b5d90"); + let suffix = run_id + .as_str() + .strip_prefix("01bba7c5-9c00-4922-b1df-1f58146b5d90:") + .unwrap(); + + assert_eq!(suffix.len(), 8); + assert!(suffix.bytes().all(|byte| byte.is_ascii_hexdigit())); + } + #[test] fn current_user_message_consumes_the_mode_instead_of_history_mode() { let request = pb::AgentRunRequest { @@ -621,7 +646,7 @@ mod tests { }), ..Default::default() }; - let projection = action("request", &request).unwrap(); + let projection = action(&request).unwrap(); assert_eq!(projection.mode, pb::AgentMode::Ask as i32); assert_eq!( projection.input_id.as_deref(), @@ -630,6 +655,63 @@ mod tests { assert_eq!(mode_from_proto(projection.mode).unwrap(), Mode::Ask); } + #[test] + fn queued_user_message_without_mode_inherits_conversation_mode() { + let request = pb::AgentRunRequest { + conversation_state: Some(pb::ConversationStateStructure { + mode: Some(pb::AgentMode::Agent as i32), + ..Default::default() + }), + action: Some(pb::ConversationAction { + action: Some(pb::conversation_action::Action::UserMessageAction( + pb::UserMessageAction { + user_message: Some(pb::UserMessage { + text: "queued follow-up".into(), + message_id: "queued-user-message".into(), + ..Default::default() + }), + ..Default::default() + }, + )), + ..Default::default() + }), + ..Default::default() + }; + + let projection = action(&request).unwrap(); + + assert_eq!(projection.mode, pb::AgentMode::Agent as i32); + assert_eq!(mode_from_proto(projection.mode).unwrap(), Mode::Agent); + } + + #[test] + fn queued_messages_reusing_a_request_id_keep_distinct_runtime_identities() { + let request = |message_id: &str| pb::AgentRunRequest { + action: Some(pb::ConversationAction { + action: Some(pb::conversation_action::Action::UserMessageAction( + pb::UserMessageAction { + user_message: Some(pb::UserMessage { + text: "queued follow-up".into(), + message_id: message_id.into(), + mode: pb::AgentMode::Agent as i32, + ..Default::default() + }), + ..Default::default() + }, + )), + ..Default::default() + }), + ..Default::default() + }; + + let first = action(&request("message-one")).unwrap(); + let second = action(&request("message-two")).unwrap(); + + assert_eq!(first.event_id.as_deref(), Some("cursor:user:message-one")); + assert_eq!(second.event_id.as_deref(), Some("cursor:user:message-two")); + assert_ne!(first.event_id, second.event_id); + } + #[test] fn execute_plan_appends_the_approved_plan_as_a_stable_runtime_event() { let execute = pb::ExecutePlanAction { @@ -648,8 +730,8 @@ mod tests { ..Default::default() }; - let first = action("request-one", &request).unwrap(); - let second = action("request-two", &request).unwrap(); + let first = action(&request).unwrap(); + let second = action(&request).unwrap(); assert_eq!(first.mode, pb::AgentMode::Agent as i32); assert!(first.starts_turn); assert_eq!(first.event_id, second.event_id); diff --git a/server/src/cursor/sessions.rs b/server/src/cursor/sessions.rs index 5ae6ad7..1b4b8c1 100644 --- a/server/src/cursor/sessions.rs +++ b/server/src/cursor/sessions.rs @@ -35,7 +35,7 @@ pub struct CursorSessionHandle { #[derive(Clone, Debug, PartialEq, Eq)] pub struct CursorParent { - pub run_id: String, + pub request_id: String, pub tool_call_id: String, } @@ -69,9 +69,9 @@ impl CursorSessionHandle { self.cancellation.clone() } pub fn set_parent(&self, parent: CursorParent) -> Result<()> { - if parent.run_id.is_empty() || parent.tool_call_id.is_empty() { + if parent.request_id.is_empty() || parent.tool_call_id.is_empty() { return Err(crate::Error::Protocol( - "Cursor parent run and tool call ids are required".into(), + "Cursor parent request and tool call ids are required".into(), )); } if self.parent.get().is_some_and(|current| current != &parent) { diff --git a/server/src/model/run.rs b/server/src/model/run.rs index 321a18a..9896ee8 100644 --- a/server/src/model/run.rs +++ b/server/src/model/run.rs @@ -48,6 +48,7 @@ pub struct RecoveredToolRound { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct PreparedRun { pub run_id: RunId, + pub cursor_request_id: Option, pub conversation_id: ConversationId, pub kind: RunKind, pub model: ModelSpec, diff --git a/server/src/store/runs.rs b/server/src/store/runs.rs index c88ca98..9c8a70e 100644 --- a/server/src/store/runs.rs +++ b/server/src/store/runs.rs @@ -88,12 +88,13 @@ impl Store { run_kind_columns(&prepared.kind); sqlx::query( "INSERT INTO runs - (run_id, conversation_id, base_revision_id, head_revision_id, + (run_id, cursor_request_id, conversation_id, base_revision_id, head_revision_id, parent_run_id, parent_tool_call_id, run_kind, subagent_kind, status, created_at_ms, updated_at_ms) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?)", + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?)", ) .bind(prepared.run_id.as_str()) + .bind(prepared.cursor_request_id.as_deref()) .bind(prepared.conversation_id.as_str()) .bind(prepared.base_revision_id.0) .bind(prepared.base_revision_id.0) @@ -128,6 +129,22 @@ impl Store { }) } + pub async fn active_run_for_cursor_request( + &self, + cursor_request_id: &str, + ) -> Result> { + let run_id: Option = sqlx::query_scalar( + "SELECT run_id FROM runs + WHERE cursor_request_id = ? AND status = 'running' + ORDER BY created_at_ms DESC + LIMIT 1", + ) + .bind(cursor_request_id) + .fetch_optional(&self.pool) + .await?; + Ok(run_id.map(RunId)) + } + pub async fn begin_provider_call(&self, run_id: &RunId) -> Result { let index: Option = sqlx::query_scalar( "UPDATE runs SET provider_call_index = provider_call_index + 1, updated_at_ms = ? diff --git a/server/tests/background_completion.rs b/server/tests/background_completion.rs index e8029e1..07adc15 100644 --- a/server/tests/background_completion.rs +++ b/server/tests/background_completion.rs @@ -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" ] ); } diff --git a/server/tests/checkpoint_recovery.rs b/server/tests/checkpoint_recovery.rs index 7077658..bfb7fb3 100644 --- a/server/tests/checkpoint_recovery.rs +++ b/server/tests/checkpoint_recovery.rs @@ -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(); diff --git a/server/tests/client_contract.rs b/server/tests/client_contract.rs index f0ac677..3d8866e 100644 --- a/server/tests/client_contract.rs +++ b/server/tests/client_contract.rs @@ -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"), diff --git a/server/tests/interrupt.rs b/server/tests/interrupt.rs index a1d8755..034b2fc 100644 --- a/server/tests/interrupt.rs +++ b/server/tests/interrupt.rs @@ -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"), diff --git a/server/tests/provider_console.rs b/server/tests/provider_console.rs index 461ace9..3fa2840 100644 --- a/server/tests/provider_console.rs +++ b/server/tests/provider_console.rs @@ -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()), diff --git a/server/tests/revision_branch.rs b/server/tests/revision_branch.rs index 9bba67c..930cdf7 100644 --- a/server/tests/revision_branch.rs +++ b/server/tests/revision_branch.rs @@ -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; diff --git a/server/tests/runtime_modes.rs b/server/tests/runtime_modes.rs index 6f61545..3ec6b01 100644 --- a/server/tests/runtime_modes.rs +++ b/server/tests/runtime_modes.rs @@ -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("")); diff --git a/server/tests/runtime_tag_once.rs b/server/tests/runtime_tag_once.rs index de54a51..c218a91 100644 --- a/server/tests/runtime_tag_once.rs +++ b/server/tests/runtime_tag_once.rs @@ -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"), diff --git a/server/tests/schema_upgrade.rs b/server/tests/schema_upgrade.rs new file mode 100644 index 0000000..5ac4eaf --- /dev/null +++ b/server/tests/schema_upgrade.rs @@ -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::("name") == "cursor_request_id")); +} diff --git a/server/tests/selected_images.rs b/server/tests/selected_images.rs index e357743..358331a 100644 --- a/server/tests/selected_images.rs +++ b/server/tests/selected_images.rs @@ -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; } diff --git a/server/tests/text_turn.rs b/server/tests/text_turn.rs index 0ee3553..78d5c98 100644 --- a/server/tests/text_turn.rs +++ b/server/tests/text_turn.rs @@ -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 { diff --git a/server/tests/tool_loop.rs b/server/tests/tool_loop.rs index da5c19a..38a073c 100644 --- a/server/tests/tool_loop.rs +++ b/server/tests/tool_loop.rs @@ -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 diff --git a/server/tests/tool_order.rs b/server/tests/tool_order.rs index 844c373..98cc298 100644 --- a/server/tests/tool_order.rs +++ b/server/tests/tool_order.rs @@ -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"),