@astralform/js 7.1.1 → 7.2.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.
package/dist/index.cjs CHANGED
@@ -2371,6 +2371,13 @@ var StreamManager = class {
2371
2371
  * its awaits. This can.
2372
2372
  */
2373
2373
  this.turnCounter = 0;
2374
+ /**
2375
+ * True while a `resync` is between its probe and its restore. Visibility and
2376
+ * focus listeners can both fire for one return, and two overlapping resyncs
2377
+ * would each detach the other's stream mid-flight — the second call must
2378
+ * find the flag set and leave the first to converge.
2379
+ */
2380
+ this._resyncing = false;
2374
2381
  this.session = session;
2375
2382
  this.attach();
2376
2383
  }
@@ -2555,6 +2562,73 @@ var StreamManager = class {
2555
2562
  }
2556
2563
  }
2557
2564
  }
2565
+ // ── Resync after the page sat in the background ───────────────
2566
+ /**
2567
+ * Re-attach to whatever the server says is live for the ACTIVE conversation.
2568
+ *
2569
+ * The reconnect machinery inside ``consumeEventStream`` only runs while a
2570
+ * stream is being consumed — and a page suspended in the background (locked
2571
+ * phone, app switch, hidden tab) can outlive it: timers are throttled or
2572
+ * suspended, so the stall watchdog may never fire while hidden; the
2573
+ * reconnect budget (``SSE_MAX_RECONNECTS``) can burn out in fail-fast
2574
+ * attempts; and a 401 from a rotated access token ends the loop outright as
2575
+ * non-retryable. What is left is a manager that believes a turn is streaming
2576
+ * (or has given up on one that is still running) with nothing attached — and
2577
+ * no navigation will ever fix it, because ``switchTo`` early-returns on the
2578
+ * conversation it is already on.
2579
+ *
2580
+ * So the consumer calls this when the user COMES BACK
2581
+ * (``visibilitychange → visible``, window ``focus``). One ``getActiveJob``
2582
+ * probe, then:
2583
+ *
2584
+ * - attached to exactly the job the server calls live → healthy. The stall
2585
+ * watchdog owns zombie recovery from here, now that timers run again.
2586
+ * No-op.
2587
+ * - anything else — attached to a job the server no longer calls live,
2588
+ * attached to nothing while a job runs, or idle with a live job another
2589
+ * tab/device started — → detach and re-run ``restore``, the same path a
2590
+ * conversation reopen takes, with all of its supersession and takeover
2591
+ * guards inherited.
2592
+ *
2593
+ * Skipped while a restore is already in flight (it is converging on server
2594
+ * truth by itself) and while another resync holds the flag — see
2595
+ * ``_resyncing``.
2596
+ */
2597
+ async resync() {
2598
+ const conversationId = this._activeConversationId;
2599
+ if (!conversationId) return;
2600
+ if (this._state === "restoring" || this._resyncing) return;
2601
+ this._resyncing = true;
2602
+ const gen = this.generation;
2603
+ const turn = this.turnCounter;
2604
+ try {
2605
+ let activeJobId = null;
2606
+ try {
2607
+ activeJobId = (await this.session.client.getActiveJob(conversationId)).jobId;
2608
+ } catch {
2609
+ return;
2610
+ }
2611
+ if (gen !== this.generation || this.turnStarted(turn)) return;
2612
+ const attached = this._state === "streaming" || this.session.isStreaming;
2613
+ if (activeJobId === null && !attached) return;
2614
+ if (activeJobId !== null && this.session.isStreaming && this.session.currentJobId === activeJobId) {
2615
+ return;
2616
+ }
2617
+ this.session.detach();
2618
+ this.session.currentJobId = null;
2619
+ this._state = "idle";
2620
+ try {
2621
+ await this.restore(conversationId, gen);
2622
+ } catch {
2623
+ }
2624
+ } finally {
2625
+ this._resyncing = false;
2626
+ const restoring = "restoring";
2627
+ if (gen === this.generation && this._state === restoring) {
2628
+ this.settleIdle();
2629
+ }
2630
+ }
2631
+ }
2558
2632
  // ── Create / rename / delete conversation ─────────────────────
2559
2633
  /**
2560
2634
  * Create a conversation and make it active.