@@ -193,6 +193,160 @@ describe('legacyCompatibilityActions', () => {
193193 } ,
194194 ) ;
195195
196+ it (
197+ 'trusts server-reported avatar ready phases from stream/start without a second hard failure gate' ,
198+ { timeout : 10000 } ,
199+ async ( ) => {
200+ setEnv ( 'STREAM555_BASE_URL' , 'https://stream.example' ) ;
201+ setEnv ( 'STREAM555_AGENT_TOKEN' , 'static-token' ) ;
202+
203+ const stopCalls : string [ ] = [ ] ;
204+ const service = {
205+ getCurrentSessionId : ( ) => 'session-1' ,
206+ getBoundSessionId : ( ) => 'session-1' ,
207+ getConfig : ( ) => ( {
208+ baseUrl : 'https://stream.example' ,
209+ agentToken : 'static-token' ,
210+ defaultSessionId : 'session-1' ,
211+ } ) ,
212+ stopStream : async ( sessionId ?: string ) => {
213+ stopCalls . push ( sessionId ?? '' ) ;
214+ return { stopped : true , wasActive : true } ;
215+ } ,
216+ } as unknown as StreamControlService ;
217+ const runtime = {
218+ getService : ( name : string ) => ( name === 'stream555' ? service : undefined ) ,
219+ } as IAgentRuntime ;
220+
221+ const requestedUrls : string [ ] = [ ] ;
222+ globalThis . fetch = ( async ( input : unknown ) => {
223+ requestedUrls . push ( String ( input ) ) ;
224+ return buildJsonResponse ( 201 , {
225+ status : 'started' ,
226+ phase : 'outputs_pending' ,
227+ sessionId : 'session-1' ,
228+ active : true ,
229+ cfSessionId : 'cf-session-1' ,
230+ publisher : 'capture_service_rtmps' ,
231+ statusReason : 'ingest connected, waiting for outputs: twitch(pending), kick(pending)' ,
232+ requiredOutputsReady : false ,
233+ cloudflare : { isConnected : false , state : 'unknown' } ,
234+ platforms : {
235+ twitch : { enabled : true , status : 'idle' } ,
236+ kick : { enabled : true , status : 'idle' } ,
237+ } ,
238+ } ) ;
239+ } ) as typeof fetch ;
240+
241+ const callbackPayloads : Array < Record < string , unknown > > = [ ] ;
242+ const action = findAction ( 'STREAM555_GO_LIVE' ) ;
243+ const ok = await action . handler (
244+ runtime ,
245+ { } ,
246+ undefined ,
247+ {
248+ sessionId : 'session-1' ,
249+ inputType : 'avatar' ,
250+ layoutMode : 'camera-full' ,
251+ } ,
252+ ( payload ) => {
253+ callbackPayloads . push ( payload as Record < string , unknown > ) ;
254+ } ,
255+ ) ;
256+
257+ assert . equal ( ok , true ) ;
258+ assert . equal ( requestedUrls . length , 1 ) ;
259+ assert . match ( requestedUrls [ 0 ] ?? '' , / \/ s t r e a m \/ s t a r t $ / ) ;
260+ assert . equal ( stopCalls . length , 0 ) ;
261+
262+ const lastPayload = callbackPayloads . at ( - 1 ) as {
263+ content ?: { success ?: boolean ; data ?: Record < string , unknown > } ;
264+ } ;
265+ assert . equal ( lastPayload . content ?. success , true ) ;
266+ assert . equal ( lastPayload . content ?. data ?. phase , 'outputs_pending' ) ;
267+ assert . equal ( lastPayload . content ?. data ?. cfSessionId , 'cf-session-1' ) ;
268+ assert . equal ( lastPayload . content ?. data ?. requiredOutputsReady , false ) ;
269+ assert . equal ( lastPayload . content ?. data ?. statusReason , 'ingest connected, waiting for outputs: twitch(pending), kick(pending)' ) ;
270+ } ,
271+ ) ;
272+
273+ it (
274+ 'accepts ready avatar phases observed during status polling even if Cloudflare lagged the boolean flag' ,
275+ { timeout : 10000 } ,
276+ async ( ) => {
277+ setEnv ( 'STREAM555_BASE_URL' , 'https://stream.example' ) ;
278+ setEnv ( 'STREAM555_AGENT_TOKEN' , 'static-token' ) ;
279+
280+ const stopCalls : string [ ] = [ ] ;
281+ const service = {
282+ getCurrentSessionId : ( ) => 'session-1' ,
283+ getBoundSessionId : ( ) => 'session-1' ,
284+ getConfig : ( ) => ( {
285+ baseUrl : 'https://stream.example' ,
286+ agentToken : 'static-token' ,
287+ defaultSessionId : 'session-1' ,
288+ } ) ,
289+ stopStream : async ( sessionId ?: string ) => {
290+ stopCalls . push ( sessionId ?? '' ) ;
291+ return { stopped : true , wasActive : true } ;
292+ } ,
293+ } as unknown as StreamControlService ;
294+ const runtime = {
295+ getService : ( name : string ) => ( name === 'stream555' ? service : undefined ) ,
296+ } as IAgentRuntime ;
297+
298+ const requestedUrls : string [ ] = [ ] ;
299+ const responses = [
300+ buildJsonResponse ( 201 , { status : 'started' , cfSessionId : 'cf-session-1' } ) ,
301+ buildJsonResponse ( 200 , {
302+ sessionId : 'session-1' ,
303+ active : true ,
304+ phase : 'outputs_pending' ,
305+ cfSessionId : 'cf-session-1' ,
306+ publisher : 'capture_service_rtmps' ,
307+ statusReason : 'ingest connected, waiting for outputs: twitch(pending)' ,
308+ requiredOutputsReady : false ,
309+ cloudflare : { isConnected : false , state : 'unknown' } ,
310+ platforms : { twitch : { enabled : true , status : 'idle' } } ,
311+ } ) ,
312+ ] ;
313+ globalThis . fetch = ( async ( input : unknown ) => {
314+ requestedUrls . push ( String ( input ) ) ;
315+ const next = responses . shift ( ) ;
316+ assert . ok ( next , 'unexpected fetch' ) ;
317+ return next ;
318+ } ) as typeof fetch ;
319+
320+ const callbackPayloads : Array < Record < string , unknown > > = [ ] ;
321+ const action = findAction ( 'STREAM555_GO_LIVE' ) ;
322+ const ok = await action . handler (
323+ runtime ,
324+ { } ,
325+ undefined ,
326+ {
327+ sessionId : 'session-1' ,
328+ inputType : 'avatar' ,
329+ layoutMode : 'camera-full' ,
330+ } ,
331+ ( payload ) => {
332+ callbackPayloads . push ( payload as Record < string , unknown > ) ;
333+ } ,
334+ ) ;
335+
336+ assert . equal ( ok , true ) ;
337+ assert . equal ( stopCalls . length , 0 ) ;
338+ assert . match ( requestedUrls [ 0 ] ?? '' , / \/ s t r e a m \/ s t a r t $ / ) ;
339+ assert . match ( requestedUrls [ 1 ] ?? '' , / \/ s t r e a m \/ s t a t u s $ / ) ;
340+
341+ const lastPayload = callbackPayloads . at ( - 1 ) as {
342+ content ?: { success ?: boolean ; data ?: Record < string , unknown > } ;
343+ } ;
344+ assert . equal ( lastPayload . content ?. success , true ) ;
345+ assert . equal ( lastPayload . content ?. data ?. phase , 'outputs_pending' ) ;
346+ assert . equal ( lastPayload . content ?. data ?. requiredOutputsReady , false ) ;
347+ } ,
348+ ) ;
349+
196350 it (
197351 'prefers fresh API-key exchange over a stale configured bearer for go-live' ,
198352 { timeout : 10000 } ,
0 commit comments