@@ -268,6 +268,115 @@ describe("runtime tool fixture", () => {
268268 expect ( details ) . toContain ( "read live provider failure planned args" ) ;
269269 } ) ;
270270
271+ it ( "allows async live runtime tool fixtures to prove the happy path with the planned call" , async ( ) => {
272+ const env = await makeEnv ( ) ;
273+ await writeQaSessionTranscript ( env , "agent:qa:runtime-tool:image_generate:happy" , [
274+ {
275+ role : "assistant" ,
276+ content : [
277+ {
278+ type : "tool_use" ,
279+ id : "call-image-happy" ,
280+ name : "image_generate" ,
281+ input : { prompt : "QA lighthouse runtime parity fixture" } ,
282+ } ,
283+ ] ,
284+ } ,
285+ ] ) ;
286+ await writeQaSessionTranscript ( env , "agent:qa:runtime-tool:image_generate:failure" , [
287+ {
288+ role : "assistant" ,
289+ content : [
290+ {
291+ type : "tool_use" ,
292+ id : "call-image-failure" ,
293+ name : "image_generate" ,
294+ input : { __qaFailureMode : "denied-input" } ,
295+ } ,
296+ ] ,
297+ } ,
298+ {
299+ role : "tool" ,
300+ toolName : "image_generate" ,
301+ tool_call_id : "call-image-failure" ,
302+ isError : true ,
303+ content : "denied-input" ,
304+ } ,
305+ ] ) ;
306+
307+ const details = await runRuntimeToolFixture (
308+ env ,
309+ {
310+ toolName : "image_generate" ,
311+ toolCoverage : {
312+ bucket : "openclaw-dynamic-integration" ,
313+ expectedLayer : "openclaw-dynamic" ,
314+ } ,
315+ happyPathOutputRequired : false ,
316+ } ,
317+ {
318+ createSession : vi . fn ( async ( _env , _label , key ) => key ! ) ,
319+ readEffectiveTools : vi . fn ( async ( ) => new Set ( [ "image_generate" ] ) ) ,
320+ runAgentPrompt : vi . fn ( async ( ) => ( { } ) ) ,
321+ fetchJson : vi . fn ( ) ,
322+ ensureImageGenerationConfigured : vi . fn ( ) ,
323+ } ,
324+ ) ;
325+
326+ expect ( details ) . toContain (
327+ "image_generate live provider happy direct output not required for this async fixture" ,
328+ ) ;
329+ expect ( details ) . toContain ( "image_generate live provider failure planned args" ) ;
330+ } ) ;
331+
332+ it ( "still requires async live runtime tool fixtures to call the happy-path tool" , async ( ) => {
333+ const env = await makeEnv ( ) ;
334+ await writeQaSessionTranscript ( env , "agent:qa:runtime-tool:image_generate:happy" , [
335+ { role : "assistant" , content : "I can start image generation later." } ,
336+ ] ) ;
337+ await writeQaSessionTranscript ( env , "agent:qa:runtime-tool:image_generate:failure" , [
338+ {
339+ role : "assistant" ,
340+ content : [
341+ {
342+ type : "tool_use" ,
343+ id : "call-image-failure" ,
344+ name : "image_generate" ,
345+ input : { __qaFailureMode : "denied-input" } ,
346+ } ,
347+ ] ,
348+ } ,
349+ {
350+ role : "tool" ,
351+ toolName : "image_generate" ,
352+ tool_call_id : "call-image-failure" ,
353+ isError : true ,
354+ content : "denied-input" ,
355+ } ,
356+ ] ) ;
357+
358+ await expect (
359+ runRuntimeToolFixture (
360+ env ,
361+ {
362+ toolName : "image_generate" ,
363+ toolCoverage : {
364+ bucket : "openclaw-dynamic-integration" ,
365+ expectedLayer : "openclaw-dynamic" ,
366+ } ,
367+ happyPathOutputRequired : false ,
368+ } ,
369+ {
370+ createSession : vi . fn ( async ( _env , _label , key ) => key ! ) ,
371+ readEffectiveTools : vi . fn ( async ( ) => new Set ( [ "image_generate" ] ) ) ,
372+ runAgentPrompt : vi . fn ( async ( ) => ( { } ) ) ,
373+ fetchJson : vi . fn ( ) ,
374+ ensureImageGenerationConfigured : vi . fn ( ) ,
375+ } ,
376+ ) ,
377+ ) . rejects . toThrow ( "expected live happy-path tool call for image_generate" ) ;
378+ } ) ;
379+
271380 it ( "requires live failure fixtures to produce failure-shaped tool output" , async ( ) => {
272381 const env = await makeEnv ( ) ;
273382 await writeQaSessionTranscript ( env , "agent:qa:runtime-tool:read:happy" , [
@@ -445,6 +554,70 @@ describe("runtime tool fixture", () => {
445554 expect ( details ) . toContain ( "mock provider happy planned args (diagnostic only)" ) ;
446555 } ) ;
447556
557+ it ( "reports Codex-native async planned-only happy fixtures without dereferencing missing output" , async ( ) => {
558+ const env = await makeEnv ( {
559+ mock : { baseUrl : "http://127.0.0.1:9999" } ,
560+ gateway : {
561+ baseUrl : "http://127.0.0.1:1" ,
562+ tempRoot : "" ,
563+ workspaceDir : "" ,
564+ runtimeEnv : { OPENCLAW_QA_FORCE_RUNTIME : "codex" } ,
565+ call : vi . fn ( ) ,
566+ } ,
567+ } ) ;
568+ env . gateway . tempRoot = env . repoRoot ;
569+ env . gateway . workspaceDir = env . repoRoot ;
570+
571+ const fetchJson = vi
572+ . fn ( )
573+ . mockResolvedValueOnce ( [ ] )
574+ . mockResolvedValueOnce ( [
575+ {
576+ allInputText : "target=image_generate" ,
577+ plannedToolCallId : "call-image-happy" ,
578+ plannedToolName : "image_generate" ,
579+ plannedToolArgs : { prompt : "QA lighthouse runtime parity fixture" } ,
580+ } ,
581+ {
582+ allInputText : "failure target=image_generate" ,
583+ plannedToolCallId : "call-image-failure" ,
584+ plannedToolName : "image_generate" ,
585+ plannedToolArgs : { __qaFailureMode : "denied-input" } ,
586+ } ,
587+ {
588+ allInputText : "failure target=image_generate" ,
589+ toolOutputCallId : "call-image-failure" ,
590+ toolOutput : "Error: denied-input" ,
591+ } ,
592+ ] ) ;
593+
594+ const details = await runRuntimeToolFixture (
595+ env ,
596+ {
597+ toolName : "image_generate" ,
598+ toolCoverage : {
599+ bucket : "codex-native-workspace" ,
600+ expectedLayer : "codex-native-workspace" ,
601+ reason : "Codex owns image generation natively in this fixture." ,
602+ } ,
603+ promptSnippet : "target=image_generate" ,
604+ failurePromptSnippet : "failure target=image_generate" ,
605+ happyPathOutputRequired : false ,
606+ } ,
607+ {
608+ createSession : vi . fn ( async ( _env , _label , key ) => key ! ) ,
609+ readEffectiveTools : vi . fn ( async ( ) => new Set < string > ( ) ) ,
610+ runAgentPrompt : vi . fn ( async ( ) => ( { } ) ) ,
611+ fetchJson,
612+ ensureImageGenerationConfigured : vi . fn ( ) ,
613+ } ,
614+ ) ;
615+
616+ expect ( details ) . toContain ( "codex-native-workspace image_generate" ) ;
617+ expect ( details ) . toContain ( '"prompt":"QA lighthouse runtime parity fixture"' ) ;
618+ expect ( details ) . toContain ( '"__qaFailureMode":"denied-input"' ) ;
619+ } ) ;
620+
448621 it ( "requires mock runtime tool fixtures to produce tool output" , async ( ) => {
449622 const env = await makeEnv ( {
450623 mock : { baseUrl : "http://127.0.0.1:9999" } ,
@@ -492,6 +665,61 @@ describe("runtime tool fixture", () => {
492665 ) . rejects . toThrow ( "expected mock happy-path tool output for read" ) ;
493666 } ) ;
494667
668+ it ( "allows async mock runtime tool fixtures to prove the happy path with the planned call" , async ( ) => {
669+ const env = await makeEnv ( {
670+ mock : { baseUrl : "http://127.0.0.1:9999" } ,
671+ } ) ;
672+ const fetchJson = vi
673+ . fn ( )
674+ . mockResolvedValueOnce ( [ ] )
675+ . mockResolvedValueOnce ( [
676+ {
677+ allInputText : "target=image_generate" ,
678+ plannedToolCallId : "call-image-happy" ,
679+ plannedToolName : "image_generate" ,
680+ plannedToolArgs : { prompt : "QA lighthouse runtime parity fixture" } ,
681+ } ,
682+ {
683+ allInputText : "failure target=image_generate" ,
684+ plannedToolCallId : "call-image-failure" ,
685+ plannedToolName : "image_generate" ,
686+ plannedToolArgs : { __qaFailureMode : "denied-input" } ,
687+ } ,
688+ {
689+ allInputText : "failure target=image_generate" ,
690+ toolOutputCallId : "call-image-failure" ,
691+ toolOutput : "Error: denied-input" ,
692+ } ,
693+ ] ) ;
694+
695+ const details = await runRuntimeToolFixture (
696+ env ,
697+ {
698+ toolName : "image_generate" ,
699+ toolCoverage : {
700+ bucket : "openclaw-dynamic-integration" ,
701+ expectedLayer : "openclaw-dynamic" ,
702+ } ,
703+ promptSnippet : "target=image_generate" ,
704+ failurePromptSnippet : "failure target=image_generate" ,
705+ happyPathOutputRequired : false ,
706+ } ,
707+ {
708+ createSession : vi . fn ( async ( _env , _label , key ) => key ! ) ,
709+ readEffectiveTools : vi . fn ( async ( ) => new Set ( [ "image_generate" ] ) ) ,
710+ runAgentPrompt : vi . fn ( async ( ) => ( { } ) ) ,
711+ fetchJson,
712+ ensureImageGenerationConfigured : vi . fn ( ) ,
713+ } ,
714+ ) ;
715+
716+ expect ( details ) . toContain (
717+ "image_generate mock provider happy direct output not required for this async fixture" ,
718+ ) ;
719+ expect ( details ) . toContain ( '"prompt":"QA lighthouse runtime parity fixture"' ) ;
720+ expect ( details ) . toContain ( '"__qaFailureMode":"denied-input"' ) ;
721+ } ) ;
722+
495723 it ( "accepts mock runtime tool fixtures only after planned calls return output" , async ( ) => {
496724 const env = await makeEnv ( {
497725 mock : { baseUrl : "http://127.0.0.1:9999" } ,
0 commit comments