fix(custom-cli): progression et messages d'outils restaurés au second tour — le projecteur ne retient plus les events de progress après le premier message, seuls Text/Error restent bufferisés — #252 (QA verte)
This commit is contained in:
@ -390,8 +390,11 @@ impl LiveTurnProjector {
|
||||
}
|
||||
|
||||
if self.hold_until_new_message && self.seen_message_id {
|
||||
self.pending.push(record);
|
||||
return Vec::new();
|
||||
if matches!(record.event, ParsedEvent::Text(_) | ParsedEvent::Error(_)) {
|
||||
self.pending.push(record);
|
||||
return Vec::new();
|
||||
}
|
||||
return self.emit_record(record);
|
||||
}
|
||||
|
||||
self.emit_record(record)
|
||||
@ -1226,6 +1229,84 @@ printf '%s\n' '{"type":"step_finish","sessionID":"seeded-engine","part":{"messag
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn send_with_tap_streams_progress_for_seeded_session_with_single_current_message_id() {
|
||||
let tmp = TempDir::new("tap-seeded-single-message");
|
||||
let script = tmp.path().join("opencode-fixture.sh");
|
||||
write_executable_script(
|
||||
&script,
|
||||
r#"#!/bin/sh
|
||||
printf '%s\n' '{"type":"step_start","sessionID":"seeded-engine","part":{"messageID":"current","type":"step-start"}}'
|
||||
sleep 1
|
||||
printf '%s\n' '{"type":"tool_use","sessionID":"seeded-engine","part":{"messageID":"current","type":"tool","tool":"idea_ask_agent"}}'
|
||||
printf '%s\n' '{"type":"text","sessionID":"seeded-engine","part":{"messageID":"current","type":"text","text":"réponse courante"}}'
|
||||
printf '%s\n' '{"type":"step_finish","sessionID":"seeded-engine","part":{"messageID":"current","type":"step-finish"}}'
|
||||
"#,
|
||||
);
|
||||
let session = Arc::new(
|
||||
OpenCodeSession::new(
|
||||
SessionId::new_random(),
|
||||
script.to_string_lossy(),
|
||||
Vec::new(),
|
||||
tmp.path().to_string_lossy(),
|
||||
Some("seeded-engine".to_owned()),
|
||||
Vec::new(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let worker = {
|
||||
let session = Arc::clone(&session);
|
||||
tokio::spawn(async move {
|
||||
session
|
||||
.send_with_tap("prompt", tx)
|
||||
.await
|
||||
.map(|stream| stream.collect::<Vec<_>>())
|
||||
})
|
||||
};
|
||||
|
||||
let (first_live, rx) = tokio::task::spawn_blocking(move || {
|
||||
let first_live = rx
|
||||
.recv_timeout(Duration::from_millis(500))
|
||||
.expect("la session existante doit streamer le progress du tour courant");
|
||||
(first_live, rx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
matches!(
|
||||
first_live,
|
||||
ReplyEvent::Progress { ref progress }
|
||||
if progress.native_event.as_deref() == Some("step_start")
|
||||
),
|
||||
"le premier événement live doit être le step_start du message courant: {first_live:?}"
|
||||
);
|
||||
|
||||
let returned = worker.await.unwrap().unwrap();
|
||||
let live: Vec<_> = std::iter::once(first_live).chain(rx.into_iter()).collect();
|
||||
|
||||
assert!(
|
||||
live.iter().any(|event| matches!(
|
||||
event,
|
||||
ReplyEvent::Progress { progress }
|
||||
if progress.source == ReplyProgressSource::IdeaLocal
|
||||
&& progress.kind == ReplyProgressKind::Mcp
|
||||
&& progress.stage == ReplyProgressStage::Completed
|
||||
&& progress.tool_name.as_deref() == Some("idea_ask_agent")
|
||||
)),
|
||||
"le tool_use du seul message courant doit atteindre le tap live: {live:?}"
|
||||
);
|
||||
assert_eq!(
|
||||
returned,
|
||||
vec![ReplyEvent::Final {
|
||||
content: "réponse courante".to_owned()
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_session_id_accepts_opencode_jsonl_session_id_variants() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user