@@ -1084,6 +1084,167 @@ describe('BrokerManager local + cloud coexistence', () => {
10841084 await manager . shutdown ( )
10851085 } )
10861086
1087+ it ( 'waits for injection using the addressed agent when broker send result omits targets' , async ( ) => {
1088+ const manager = new BrokerManager ( )
1089+ const local = await startLocal ( manager , [ 'claude-1' ] )
1090+ local . sendMessage . mockResolvedValueOnce ( { event_id : 'evt-injected' } )
1091+ local . onEvent . mockImplementationOnce ( ( listener ) => {
1092+ setImmediate ( ( ) => {
1093+ listener ( {
1094+ kind : 'delivery_injected' ,
1095+ event_id : 'evt-injected' ,
1096+ name : 'claude-1'
1097+ } )
1098+ } )
1099+ return ( ) => undefined
1100+ } )
1101+
1102+ await expect ( manager . sendMessageAndWaitForInjected ( PROJECT_ID , {
1103+ to : 'claude-1' ,
1104+ text : '<integration-event>ping</integration-event>'
1105+ } ) ) . resolves . toEqual ( {
1106+ eventId : 'evt-injected' ,
1107+ targets : [ 'claude-1' ]
1108+ } )
1109+
1110+ await manager . shutdown ( )
1111+ } )
1112+
1113+ it ( 'does not treat delivery ack or verification as an injection confirmation' , async ( ) => {
1114+ const manager = new BrokerManager ( )
1115+ const local = await startLocal ( manager , [ 'claude-1' ] )
1116+ local . sendMessage . mockResolvedValueOnce ( { event_id : 'evt-not-injected' } )
1117+ local . onEvent . mockImplementationOnce ( ( listener ) => {
1118+ setImmediate ( ( ) => {
1119+ listener ( {
1120+ kind : 'delivery_ack' ,
1121+ event_id : 'evt-not-injected' ,
1122+ name : 'claude-1'
1123+ } )
1124+ listener ( {
1125+ kind : 'delivery_verified' ,
1126+ event_id : 'evt-not-injected' ,
1127+ name : 'claude-1'
1128+ } )
1129+ } )
1130+ return ( ) => undefined
1131+ } )
1132+
1133+ await expect ( manager . sendMessageAndWaitForInjected ( PROJECT_ID , {
1134+ to : 'claude-1' ,
1135+ text : '<integration-event>ping</integration-event>'
1136+ } , { timeoutMs : 10 } ) ) . rejects . toThrow (
1137+ 'Timed out waiting for delivery injection for evt-not-injected (claude-1)'
1138+ )
1139+
1140+ await manager . shutdown ( )
1141+ } )
1142+
1143+ it . each ( [
1144+ [ 'delivery_failed' , 'PTY write failed' ] ,
1145+ [ 'message_delivery_failed' , 'broker send failed' ]
1146+ ] as const ) ( 'rejects injection wait on %s' , async ( kind , reason ) => {
1147+ const manager = new BrokerManager ( )
1148+ const local = await startLocal ( manager , [ 'claude-1' ] )
1149+ local . sendMessage . mockResolvedValueOnce ( { event_id : `evt-${ kind } ` } )
1150+ local . onEvent . mockImplementationOnce ( ( listener ) => {
1151+ setImmediate ( ( ) => {
1152+ listener ( {
1153+ kind,
1154+ event_id : `evt-${ kind } ` ,
1155+ name : 'claude-1' ,
1156+ reason
1157+ } )
1158+ } )
1159+ return ( ) => undefined
1160+ } )
1161+
1162+ await expect ( manager . sendMessageAndWaitForInjected ( PROJECT_ID , {
1163+ to : 'claude-1' ,
1164+ text : '<integration-event>ping</integration-event>'
1165+ } ) ) . rejects . toThrow ( reason )
1166+
1167+ await manager . shutdown ( )
1168+ } )
1169+
1170+ it ( 'waits for every reported target before confirming injection' , async ( ) => {
1171+ const manager = new BrokerManager ( )
1172+ const local = await startLocal ( manager , [ 'claude-1' , 'codex-1' ] )
1173+ local . sendMessage . mockResolvedValueOnce ( {
1174+ event_id : 'evt-multi-injected' ,
1175+ targets : [ 'claude-1' , 'codex-1' ]
1176+ } )
1177+ local . onEvent . mockImplementationOnce ( ( listener ) => {
1178+ setImmediate ( ( ) => {
1179+ listener ( {
1180+ kind : 'delivery_injected' ,
1181+ event_id : 'evt-multi-injected' ,
1182+ name : 'claude-1'
1183+ } )
1184+ listener ( {
1185+ kind : 'delivery_injected' ,
1186+ event_id : 'evt-multi-injected' ,
1187+ name : 'codex-1'
1188+ } )
1189+ } )
1190+ return ( ) => undefined
1191+ } )
1192+
1193+ await expect ( manager . sendMessageAndWaitForInjected ( PROJECT_ID , {
1194+ to : 'claude-1' ,
1195+ text : '<integration-event>ping</integration-event>'
1196+ } ) ) . resolves . toEqual ( {
1197+ eventId : 'evt-multi-injected' ,
1198+ targets : [ 'claude-1' , 'codex-1' ]
1199+ } )
1200+
1201+ await manager . shutdown ( )
1202+ } )
1203+
1204+ it ( 'returns without waiting for injection when a channel send has no concrete targets' , async ( ) => {
1205+ const manager = new BrokerManager ( )
1206+ const local = await startLocal ( manager , [ 'claude-1' ] )
1207+ local . sendMessage . mockResolvedValueOnce ( { event_id : 'evt-channel' , targets : [ ] } )
1208+
1209+ await expect ( manager . sendMessageAndWaitForInjected ( PROJECT_ID , {
1210+ to : '#general' ,
1211+ text : '<integration-event>ping</integration-event>'
1212+ } , { timeoutMs : 1 } ) ) . resolves . toEqual ( {
1213+ eventId : 'evt-channel' ,
1214+ targets : [ ]
1215+ } )
1216+
1217+ await manager . shutdown ( )
1218+ } )
1219+
1220+ it ( 'replays injection events observed before sendMessage resolves' , async ( ) => {
1221+ const manager = new BrokerManager ( )
1222+ const local = await startLocal ( manager , [ 'claude-1' ] )
1223+ let eventListener : ( ( event : unknown ) => void ) | undefined
1224+ local . onEvent . mockImplementationOnce ( ( listener ) => {
1225+ eventListener = listener
1226+ return ( ) => undefined
1227+ } )
1228+ local . sendMessage . mockImplementationOnce ( async ( ) => {
1229+ eventListener ?.( {
1230+ kind : 'delivery_injected' ,
1231+ event_id : 'evt-early-injected' ,
1232+ name : 'claude-1'
1233+ } )
1234+ return { event_id : 'evt-early-injected' }
1235+ } )
1236+
1237+ await expect ( manager . sendMessageAndWaitForInjected ( PROJECT_ID , {
1238+ to : 'claude-1' ,
1239+ text : '<integration-event>ping</integration-event>'
1240+ } ) ) . resolves . toEqual ( {
1241+ eventId : 'evt-early-injected' ,
1242+ targets : [ 'claude-1' ]
1243+ } )
1244+
1245+ await manager . shutdown ( )
1246+ } )
1247+
10871248 it ( 'keeps repeated no-identity PTY chunks after intervening output' , async ( ) => {
10881249 const manager = new BrokerManager ( )
10891250 const win = createMockWindow ( )
0 commit comments