@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 +74 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1614,6 +1614,13 @@ declare class StreamManager {
|
|
|
1614
1614
|
* its awaits. This can.
|
|
1615
1615
|
*/
|
|
1616
1616
|
private turnCounter;
|
|
1617
|
+
/**
|
|
1618
|
+
* True while a `resync` is between its probe and its restore. Visibility and
|
|
1619
|
+
* focus listeners can both fire for one return, and two overlapping resyncs
|
|
1620
|
+
* would each detach the other's stream mid-flight — the second call must
|
|
1621
|
+
* find the flag set and leave the first to converge.
|
|
1622
|
+
*/
|
|
1623
|
+
private _resyncing;
|
|
1617
1624
|
constructor(session: ChatSession);
|
|
1618
1625
|
get state(): StreamState;
|
|
1619
1626
|
get activeConversationId(): string | null;
|
|
@@ -1645,6 +1652,38 @@ declare class StreamManager {
|
|
|
1645
1652
|
switchTo(conversationId: string, opts?: {
|
|
1646
1653
|
skipHistoryReplay?: boolean;
|
|
1647
1654
|
}): Promise<void>;
|
|
1655
|
+
/**
|
|
1656
|
+
* Re-attach to whatever the server says is live for the ACTIVE conversation.
|
|
1657
|
+
*
|
|
1658
|
+
* The reconnect machinery inside ``consumeEventStream`` only runs while a
|
|
1659
|
+
* stream is being consumed — and a page suspended in the background (locked
|
|
1660
|
+
* phone, app switch, hidden tab) can outlive it: timers are throttled or
|
|
1661
|
+
* suspended, so the stall watchdog may never fire while hidden; the
|
|
1662
|
+
* reconnect budget (``SSE_MAX_RECONNECTS``) can burn out in fail-fast
|
|
1663
|
+
* attempts; and a 401 from a rotated access token ends the loop outright as
|
|
1664
|
+
* non-retryable. What is left is a manager that believes a turn is streaming
|
|
1665
|
+
* (or has given up on one that is still running) with nothing attached — and
|
|
1666
|
+
* no navigation will ever fix it, because ``switchTo`` early-returns on the
|
|
1667
|
+
* conversation it is already on.
|
|
1668
|
+
*
|
|
1669
|
+
* So the consumer calls this when the user COMES BACK
|
|
1670
|
+
* (``visibilitychange → visible``, window ``focus``). One ``getActiveJob``
|
|
1671
|
+
* probe, then:
|
|
1672
|
+
*
|
|
1673
|
+
* - attached to exactly the job the server calls live → healthy. The stall
|
|
1674
|
+
* watchdog owns zombie recovery from here, now that timers run again.
|
|
1675
|
+
* No-op.
|
|
1676
|
+
* - anything else — attached to a job the server no longer calls live,
|
|
1677
|
+
* attached to nothing while a job runs, or idle with a live job another
|
|
1678
|
+
* tab/device started — → detach and re-run ``restore``, the same path a
|
|
1679
|
+
* conversation reopen takes, with all of its supersession and takeover
|
|
1680
|
+
* guards inherited.
|
|
1681
|
+
*
|
|
1682
|
+
* Skipped while a restore is already in flight (it is converging on server
|
|
1683
|
+
* truth by itself) and while another resync holds the flag — see
|
|
1684
|
+
* ``_resyncing``.
|
|
1685
|
+
*/
|
|
1686
|
+
resync(): Promise<void>;
|
|
1648
1687
|
/**
|
|
1649
1688
|
* Create a conversation and make it active.
|
|
1650
1689
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1614,6 +1614,13 @@ declare class StreamManager {
|
|
|
1614
1614
|
* its awaits. This can.
|
|
1615
1615
|
*/
|
|
1616
1616
|
private turnCounter;
|
|
1617
|
+
/**
|
|
1618
|
+
* True while a `resync` is between its probe and its restore. Visibility and
|
|
1619
|
+
* focus listeners can both fire for one return, and two overlapping resyncs
|
|
1620
|
+
* would each detach the other's stream mid-flight — the second call must
|
|
1621
|
+
* find the flag set and leave the first to converge.
|
|
1622
|
+
*/
|
|
1623
|
+
private _resyncing;
|
|
1617
1624
|
constructor(session: ChatSession);
|
|
1618
1625
|
get state(): StreamState;
|
|
1619
1626
|
get activeConversationId(): string | null;
|
|
@@ -1645,6 +1652,38 @@ declare class StreamManager {
|
|
|
1645
1652
|
switchTo(conversationId: string, opts?: {
|
|
1646
1653
|
skipHistoryReplay?: boolean;
|
|
1647
1654
|
}): Promise<void>;
|
|
1655
|
+
/**
|
|
1656
|
+
* Re-attach to whatever the server says is live for the ACTIVE conversation.
|
|
1657
|
+
*
|
|
1658
|
+
* The reconnect machinery inside ``consumeEventStream`` only runs while a
|
|
1659
|
+
* stream is being consumed — and a page suspended in the background (locked
|
|
1660
|
+
* phone, app switch, hidden tab) can outlive it: timers are throttled or
|
|
1661
|
+
* suspended, so the stall watchdog may never fire while hidden; the
|
|
1662
|
+
* reconnect budget (``SSE_MAX_RECONNECTS``) can burn out in fail-fast
|
|
1663
|
+
* attempts; and a 401 from a rotated access token ends the loop outright as
|
|
1664
|
+
* non-retryable. What is left is a manager that believes a turn is streaming
|
|
1665
|
+
* (or has given up on one that is still running) with nothing attached — and
|
|
1666
|
+
* no navigation will ever fix it, because ``switchTo`` early-returns on the
|
|
1667
|
+
* conversation it is already on.
|
|
1668
|
+
*
|
|
1669
|
+
* So the consumer calls this when the user COMES BACK
|
|
1670
|
+
* (``visibilitychange → visible``, window ``focus``). One ``getActiveJob``
|
|
1671
|
+
* probe, then:
|
|
1672
|
+
*
|
|
1673
|
+
* - attached to exactly the job the server calls live → healthy. The stall
|
|
1674
|
+
* watchdog owns zombie recovery from here, now that timers run again.
|
|
1675
|
+
* No-op.
|
|
1676
|
+
* - anything else — attached to a job the server no longer calls live,
|
|
1677
|
+
* attached to nothing while a job runs, or idle with a live job another
|
|
1678
|
+
* tab/device started — → detach and re-run ``restore``, the same path a
|
|
1679
|
+
* conversation reopen takes, with all of its supersession and takeover
|
|
1680
|
+
* guards inherited.
|
|
1681
|
+
*
|
|
1682
|
+
* Skipped while a restore is already in flight (it is converging on server
|
|
1683
|
+
* truth by itself) and while another resync holds the flag — see
|
|
1684
|
+
* ``_resyncing``.
|
|
1685
|
+
*/
|
|
1686
|
+
resync(): Promise<void>;
|
|
1648
1687
|
/**
|
|
1649
1688
|
* Create a conversation and make it active.
|
|
1650
1689
|
*
|
package/dist/index.js
CHANGED
|
@@ -2324,6 +2324,13 @@ var StreamManager = class {
|
|
|
2324
2324
|
* its awaits. This can.
|
|
2325
2325
|
*/
|
|
2326
2326
|
this.turnCounter = 0;
|
|
2327
|
+
/**
|
|
2328
|
+
* True while a `resync` is between its probe and its restore. Visibility and
|
|
2329
|
+
* focus listeners can both fire for one return, and two overlapping resyncs
|
|
2330
|
+
* would each detach the other's stream mid-flight — the second call must
|
|
2331
|
+
* find the flag set and leave the first to converge.
|
|
2332
|
+
*/
|
|
2333
|
+
this._resyncing = false;
|
|
2327
2334
|
this.session = session;
|
|
2328
2335
|
this.attach();
|
|
2329
2336
|
}
|
|
@@ -2508,6 +2515,73 @@ var StreamManager = class {
|
|
|
2508
2515
|
}
|
|
2509
2516
|
}
|
|
2510
2517
|
}
|
|
2518
|
+
// ── Resync after the page sat in the background ───────────────
|
|
2519
|
+
/**
|
|
2520
|
+
* Re-attach to whatever the server says is live for the ACTIVE conversation.
|
|
2521
|
+
*
|
|
2522
|
+
* The reconnect machinery inside ``consumeEventStream`` only runs while a
|
|
2523
|
+
* stream is being consumed — and a page suspended in the background (locked
|
|
2524
|
+
* phone, app switch, hidden tab) can outlive it: timers are throttled or
|
|
2525
|
+
* suspended, so the stall watchdog may never fire while hidden; the
|
|
2526
|
+
* reconnect budget (``SSE_MAX_RECONNECTS``) can burn out in fail-fast
|
|
2527
|
+
* attempts; and a 401 from a rotated access token ends the loop outright as
|
|
2528
|
+
* non-retryable. What is left is a manager that believes a turn is streaming
|
|
2529
|
+
* (or has given up on one that is still running) with nothing attached — and
|
|
2530
|
+
* no navigation will ever fix it, because ``switchTo`` early-returns on the
|
|
2531
|
+
* conversation it is already on.
|
|
2532
|
+
*
|
|
2533
|
+
* So the consumer calls this when the user COMES BACK
|
|
2534
|
+
* (``visibilitychange → visible``, window ``focus``). One ``getActiveJob``
|
|
2535
|
+
* probe, then:
|
|
2536
|
+
*
|
|
2537
|
+
* - attached to exactly the job the server calls live → healthy. The stall
|
|
2538
|
+
* watchdog owns zombie recovery from here, now that timers run again.
|
|
2539
|
+
* No-op.
|
|
2540
|
+
* - anything else — attached to a job the server no longer calls live,
|
|
2541
|
+
* attached to nothing while a job runs, or idle with a live job another
|
|
2542
|
+
* tab/device started — → detach and re-run ``restore``, the same path a
|
|
2543
|
+
* conversation reopen takes, with all of its supersession and takeover
|
|
2544
|
+
* guards inherited.
|
|
2545
|
+
*
|
|
2546
|
+
* Skipped while a restore is already in flight (it is converging on server
|
|
2547
|
+
* truth by itself) and while another resync holds the flag — see
|
|
2548
|
+
* ``_resyncing``.
|
|
2549
|
+
*/
|
|
2550
|
+
async resync() {
|
|
2551
|
+
const conversationId = this._activeConversationId;
|
|
2552
|
+
if (!conversationId) return;
|
|
2553
|
+
if (this._state === "restoring" || this._resyncing) return;
|
|
2554
|
+
this._resyncing = true;
|
|
2555
|
+
const gen = this.generation;
|
|
2556
|
+
const turn = this.turnCounter;
|
|
2557
|
+
try {
|
|
2558
|
+
let activeJobId = null;
|
|
2559
|
+
try {
|
|
2560
|
+
activeJobId = (await this.session.client.getActiveJob(conversationId)).jobId;
|
|
2561
|
+
} catch {
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
if (gen !== this.generation || this.turnStarted(turn)) return;
|
|
2565
|
+
const attached = this._state === "streaming" || this.session.isStreaming;
|
|
2566
|
+
if (activeJobId === null && !attached) return;
|
|
2567
|
+
if (activeJobId !== null && this.session.isStreaming && this.session.currentJobId === activeJobId) {
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
this.session.detach();
|
|
2571
|
+
this.session.currentJobId = null;
|
|
2572
|
+
this._state = "idle";
|
|
2573
|
+
try {
|
|
2574
|
+
await this.restore(conversationId, gen);
|
|
2575
|
+
} catch {
|
|
2576
|
+
}
|
|
2577
|
+
} finally {
|
|
2578
|
+
this._resyncing = false;
|
|
2579
|
+
const restoring = "restoring";
|
|
2580
|
+
if (gen === this.generation && this._state === restoring) {
|
|
2581
|
+
this.settleIdle();
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2511
2585
|
// ── Create / rename / delete conversation ─────────────────────
|
|
2512
2586
|
/**
|
|
2513
2587
|
* Create a conversation and make it active.
|