mod cli_test_util;
use std::time::Duration;
use objectiveai_sdk::agent::InlineAgentBaseWithFallbacksOrRemoteCommitOptional;
use objectiveai_sdk::cli::command::agents::message::{
MessageTarget, Request as MessageRequest,
RequestDangerousAdvanced as MessageDangerousAdvanced, RequestMessage,
ResponseItem as MessageResponseItem,
};
use objectiveai_sdk::cli::command::agents::spawn::{
AgentResolution, AgentSpec, Request as SpawnRequest, RequestDangerousAdvanced,
ResponseItem as SpawnResponseItem,
};
#[tokio::test]
async fn spawn_then_message_propagates_response_continuation() {
if cli_test_util::test_api_address().is_none() {
eprintln!(
"OBJECTIVEAI_TEST_PORT not set — skipping spawn_then_message_propagates_response_continuation"
);
return;
}
let base_dir = cli_test_util::test_base_dir();
let executor = cli_test_util::executor_with_base_dir(&base_dir);
let spawn_request = SpawnRequest {
path_type: objectiveai_sdk::cli::command::agents::spawn::Path::AgentsSpawn,
message: RequestMessage::Simple("first turn".to_string()),
agent: AgentResolution::Direct {
agent_spec: AgentSpec::Resolved(
serde_json::from_value::<InlineAgentBaseWithFallbacksOrRemoteCommitOptional>(
serde_json::json!({"upstream":"mock","output_mode":"instruction"}),
)
.expect("inline mock agent must deserialize"),
),
},
dangerous_advanced: Some(RequestDangerousAdvanced {
stream: Some(true),
seed: Some(42),
}),
jq: None,
};
let spawn_items: Vec<SpawnResponseItem> =
cli_test_util::collect_stream(&executor, spawn_request).await;
let spawn_chunk_aih = spawn_items
.iter()
.find_map(|item| match item {
SpawnResponseItem::Chunk(chunk) if !chunk.agent_instance_hierarchy.is_empty() => {
Some(chunk.agent_instance_hierarchy.clone())
}
_ => None,
})
.expect("agents spawn must emit a Chunk with a non-empty agent_instance_hierarchy");
let spawn_response_id = spawn_items
.iter()
.find_map(|item| match item {
SpawnResponseItem::Chunk(chunk) if !chunk.id.is_empty() => Some(chunk.id.clone()),
_ => None,
})
.expect("agents spawn must emit a Chunk with non-empty id");
let spawn_continuation =
cli_test_util::wait_for_continuation(&executor, &spawn_chunk_aih, Duration::from_secs(30))
.await;
let (parent, instance) = spawn_chunk_aih
.rsplit_once('/')
.map(|(p, i)| (Some(p.to_string()), i.to_string()))
.expect("spawn_chunk_aih must carry at least one '/'");
let message_request = MessageRequest {
path_type: objectiveai_sdk::cli::command::agents::message::Path::AgentsMessage,
target: MessageTarget::Direct {
parent_agent_instance_hierarchy: parent,
agent_instance: instance,
},
message: RequestMessage::Simple("follow up".to_string()),
enqueue: None,
dangerous_advanced: Some(MessageDangerousAdvanced {
stream: Some(true),
seed: Some(42),
}),
jq: None,
};
let items: Vec<MessageResponseItem> =
cli_test_util::collect_stream(&executor, message_request).await;
let new_response_id = items
.iter()
.find_map(|item| match item {
MessageResponseItem::Chunk(chunk) if !chunk.id.is_empty() => Some(chunk.id.clone()),
_ => None,
})
.unwrap_or(spawn_response_id);
let request_continuation =
cli_test_util::wait_for_request_continuation(&executor, &new_response_id, Duration::from_secs(30))
.await
.expect("second turn's request body must carry a continuation");
assert_eq!(
request_continuation, spawn_continuation,
"second turn's request continuation must equal first turn's agent_continuations value",
);
}