@ggui-ai/iframe-runtime 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -231,6 +231,14 @@ export function buildBridgePolling(opts) {
231
231
  // holds the card is presumed quiet; drop to sparse un-held
232
232
  // pulls every `idleIntervalMs` so a dormant card doesn't pin
233
233
  // the host's relay open forever.
234
+ // - A FAILED pull (the relay's own `tools/call` timeout, `-32001`,
235
+ // a transport error) is not a live session either (ggui#1029):
236
+ // it counts toward demotion exactly like an empty hold, and
237
+ // repeated failures back off geometrically from `intervalMs`
238
+ // up to `idleIntervalMs` — a relay whose call timeout is shorter
239
+ // than `holdSeconds` used to answer every held pull with an
240
+ // error, which never advanced the counter, so a dormant card
241
+ // re-fired a full hold every few seconds forever.
234
242
  // Any delivered event promotes straight back to subscription mode.
235
243
  let consecutiveEmpties = 0;
236
244
  const hot = () => consecutiveEmpties < demoteAfterEmpties;
@@ -246,6 +254,10 @@ export function buildBridgePolling(opts) {
246
254
  return unwrapCallToolResult(result);
247
255
  },
248
256
  parseSnapshot: createEventsSnapshotParser(cursor),
257
+ nextDelayOnFailureMs: (consecutiveFailures) => {
258
+ consecutiveEmpties += 1;
259
+ return Math.min(idleIntervalMs, intervalMs * 2 ** Math.max(0, consecutiveFailures - 1));
260
+ },
249
261
  nextDelayMs: (body) => {
250
262
  if (isEventsResponse(body)) {
251
263
  if (body.events.length > 0) {