@@ -41,6 +41,8 @@ import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRe
4141import * as AcpRegistryRuntimeCoordinator from "@t3tools/provider-acp-registry/server/AcpRegistryRuntimeCoordinator" ;
4242import { ACP_SESSION_MODE_OPTION_ID } from "@t3tools/provider-acp/server/sessionConfig" ;
4343import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime" ;
44+ import { t3AcpPromptWithInstructions } from "@t3tools/provider-core/server/orchestrationInstructions" ;
45+ import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions" ;
4446import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator" ;
4547import {
4648 decodeAcpReplayTranscript ,
@@ -1043,6 +1045,98 @@ describe("AcpRegistryAdapterV2", () => {
10431045 } ) . pipe ( Effect . provide ( layerTest ) , Effect . scoped ) ,
10441046 ) ;
10451047
1048+ it . effect . each ( [ "before" , "after" ] as const ) (
1049+ "reconciles a full prompt echo %s acknowledgment" ,
1050+ ( ordering ) =>
1051+ Effect . gen ( function * ( ) {
1052+ const runtimeInstructions = {
1053+ type : "text" ,
1054+ text : buildRuntimeInstructions ( { harness : "acpRegistry" , model : "default" } ) ,
1055+ } ;
1056+ const promptBlocks = ( ordinal : number ) => [
1057+ {
1058+ type : "text" ,
1059+ text :
1060+ ordinal === 1
1061+ ? t3AcpPromptWithInstructions ( {
1062+ prompt : "test prompt" ,
1063+ state : { interactionMode : "default" , hasT3Mcp : false , hasDeviceTools : false } ,
1064+ } )
1065+ : "test prompt" ,
1066+ } ,
1067+ runtimeInstructions ,
1068+ ] ;
1069+ const echo = ( messageId : string , content : ReadonlyArray < unknown > ) =>
1070+ claudeFrames . update ( CLAUDE_ROOT_SESSION , {
1071+ sessionUpdate : "user_message" ,
1072+ messageId,
1073+ content,
1074+ } ) ;
1075+ const promptText = ( ordinal : number ) =>
1076+ promptBlocks ( ordinal )
1077+ . map ( ( block ) => block . text )
1078+ . join ( "\n" ) ;
1079+ const frames = ( ordinal : number ) => {
1080+ const [ request , , acknowledgment ] = claudeFrames . prompt ( ordinal ) ;
1081+ const matching = echo ( `echo-${ ordinal } ` , promptBlocks ( ordinal ) ) ;
1082+ return [
1083+ request ,
1084+ echo ( "different-blocks" , [
1085+ {
1086+ type : "text" ,
1087+ text : promptText ( ordinal ) ,
1088+ } ,
1089+ ] ) ,
1090+ ...( ordering === "before" ? [ matching , acknowledgment ] : [ acknowledgment , matching ] ) ,
1091+ claudeFrames . update ( CLAUDE_ROOT_SESSION , {
1092+ sessionUpdate : "user_message_chunk" ,
1093+ messageId : `echo-${ ordinal } ` ,
1094+ content : { type : "text" , text : "updated echo" } ,
1095+ } ) ,
1096+ echo ( `echo-${ ordinal } ` , [ { type : "text" , text : "updated echo" } ] ) ,
1097+ echo ( "second-id" , promptBlocks ( ordinal ) ) ,
1098+ echo ( "unmatched" , [ { type : "text" , text : "Continue the review." } ] ) ,
1099+ claudeFrames . idle ( "end_turn" ) ,
1100+ ] ;
1101+ } ;
1102+ const session = yield * openClaudeReplaySession (
1103+ [
1104+ ...claudeFrames . open ,
1105+ ...frames ( 1 ) ,
1106+ ...frames ( 2 ) ,
1107+ claudeFrames . closeRequest ,
1108+ claudeFrames . closed ,
1109+ ] ,
1110+ undefined ,
1111+ false ,
1112+ ) ;
1113+ for ( const ordinal of [ 1 , 2 ] ) {
1114+ yield * session . runtime . startTurn ( session . turnInput ( ordinal ) ) ;
1115+ const events = yield * session . until ( ( event ) => event . type === "turn.terminal" ) ;
1116+ const messages = events . flatMap ( ( event ) =>
1117+ event . type === "message.updated" && event . message . role === "user"
1118+ ? [ event . message ]
1119+ : [ ] ,
1120+ ) ;
1121+ const items = events . flatMap ( ( event ) =>
1122+ event . type === "turn_item.updated" && event . turnItem . type === "user_message"
1123+ ? [ event . turnItem ]
1124+ : [ ] ,
1125+ ) ;
1126+ const expected = new Set ( [ promptText ( ordinal ) , "Continue the review." ] ) ;
1127+ assert . deepEqual ( new Set ( messages . map ( ( message ) => message . text ) ) , expected ) ;
1128+ assert . equal ( new Set ( messages . map ( ( message ) => message . id ) ) . size , 3 ) ;
1129+ assert . equal ( new Set ( items . map ( ( item ) => item . id ) ) . size , 3 ) ;
1130+ for ( const id of [ "different-blocks" , "second-id" , "unmatched" ] ) {
1131+ assert . isTrue (
1132+ items . some ( ( item ) => item . nativeItemRef ?. nativeId ?. endsWith ( `:user:message:${ id } ` ) ) ,
1133+ `${ id } remains visible` ,
1134+ ) ;
1135+ }
1136+ }
1137+ } ) . pipe ( Effect . provide ( layerTest ) , Effect . scoped ) ,
1138+ ) ;
1139+
10461140 it . effect ( "settles Claude's accepted foreground reply while retaining child output" , ( ) =>
10471141 Effect . gen ( function * ( ) {
10481142 const gate = makeProviderReplayGate ( [ "accepted" , "child-output" , "wake" ] ) ;
0 commit comments