@astralform/js 9.5.0 → 9.7.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 +55 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +55 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2334,6 +2334,31 @@ declare class ChatSession {
|
|
|
2334
2334
|
* pages, and a doubled prompt is more visible than a missing one.
|
|
2335
2335
|
*/
|
|
2336
2336
|
prependMessages(older: Message[]): void;
|
|
2337
|
+
/**
|
|
2338
|
+
* Re-fetch the NEWEST page of the conversation list and merge it in.
|
|
2339
|
+
*
|
|
2340
|
+
* The list the sidebar renders is seeded once at `connect()` and then only
|
|
2341
|
+
* changed by this client's own actions (create, delete, rename) — so a row's
|
|
2342
|
+
* `activeJobId` is as fresh as that seed, and a job started in ANOTHER tab or
|
|
2343
|
+
* on another device never appears, while one that ended elsewhere stays lit
|
|
2344
|
+
* forever. A caller that refreshes on a trigger (the tab becoming visible, or
|
|
2345
|
+
* a bounded interval while any job is live) keeps the rows honest without a
|
|
2346
|
+
* socket per conversation.
|
|
2347
|
+
*
|
|
2348
|
+
* Merge rules, and why each is what it is:
|
|
2349
|
+
*
|
|
2350
|
+
* - the server's ORDER wins for the rows it returns: an updated conversation
|
|
2351
|
+
* moves to its new place, which is the whole point of the call;
|
|
2352
|
+
* - rows paged in EARLIER (`loadMoreConversations`) are kept — this reads page
|
|
2353
|
+
* 1 only, and dropping the tail would make a refresh silently truncate a
|
|
2354
|
+
* list the reader has already scrolled;
|
|
2355
|
+
* - a row the caller MINTED locally and the server does not know yet is kept
|
|
2356
|
+
* too (it has no server row to be refreshed by);
|
|
2357
|
+
* - `conversationsGeneration` is bumped, for the same reason `connect()`
|
|
2358
|
+
* bumps it: a page request already in flight was cut against the old head
|
|
2359
|
+
* and its offset no longer means what it did.
|
|
2360
|
+
*/
|
|
2361
|
+
refreshConversations(): Promise<Conversation[]>;
|
|
2337
2362
|
loadMoreConversations(): Promise<Conversation[]>;
|
|
2338
2363
|
/**
|
|
2339
2364
|
* Rename a conversation, server first.
|
|
@@ -2688,6 +2713,18 @@ declare class StreamManager {
|
|
|
2688
2713
|
have?: ReadonlyArray<{
|
|
2689
2714
|
jobId: string;
|
|
2690
2715
|
lastSeq: number;
|
|
2716
|
+
/**
|
|
2717
|
+
* The prompt this job's turn drew, when the caller knows it.
|
|
2718
|
+
*
|
|
2719
|
+
* The caller's cache holds the PROMPT too (it renders the turn), and a
|
|
2720
|
+
* prompt is a message-table row rather than a stream event — so the
|
|
2721
|
+
* plan's empty-walk rescue has to be told which prompts are already on
|
|
2722
|
+
* screen. Marks are matched against the page of jobs this restore walks,
|
|
2723
|
+
* so a mark for an OLDER turn is not otherwise visible to that rescue,
|
|
2724
|
+
* and a cache deeper than one turn page would re-emit those prompts as
|
|
2725
|
+
* steer bubbles.
|
|
2726
|
+
*/
|
|
2727
|
+
messageId?: string | null;
|
|
2691
2728
|
}>;
|
|
2692
2729
|
}): Promise<void>;
|
|
2693
2730
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -2334,6 +2334,31 @@ declare class ChatSession {
|
|
|
2334
2334
|
* pages, and a doubled prompt is more visible than a missing one.
|
|
2335
2335
|
*/
|
|
2336
2336
|
prependMessages(older: Message[]): void;
|
|
2337
|
+
/**
|
|
2338
|
+
* Re-fetch the NEWEST page of the conversation list and merge it in.
|
|
2339
|
+
*
|
|
2340
|
+
* The list the sidebar renders is seeded once at `connect()` and then only
|
|
2341
|
+
* changed by this client's own actions (create, delete, rename) — so a row's
|
|
2342
|
+
* `activeJobId` is as fresh as that seed, and a job started in ANOTHER tab or
|
|
2343
|
+
* on another device never appears, while one that ended elsewhere stays lit
|
|
2344
|
+
* forever. A caller that refreshes on a trigger (the tab becoming visible, or
|
|
2345
|
+
* a bounded interval while any job is live) keeps the rows honest without a
|
|
2346
|
+
* socket per conversation.
|
|
2347
|
+
*
|
|
2348
|
+
* Merge rules, and why each is what it is:
|
|
2349
|
+
*
|
|
2350
|
+
* - the server's ORDER wins for the rows it returns: an updated conversation
|
|
2351
|
+
* moves to its new place, which is the whole point of the call;
|
|
2352
|
+
* - rows paged in EARLIER (`loadMoreConversations`) are kept — this reads page
|
|
2353
|
+
* 1 only, and dropping the tail would make a refresh silently truncate a
|
|
2354
|
+
* list the reader has already scrolled;
|
|
2355
|
+
* - a row the caller MINTED locally and the server does not know yet is kept
|
|
2356
|
+
* too (it has no server row to be refreshed by);
|
|
2357
|
+
* - `conversationsGeneration` is bumped, for the same reason `connect()`
|
|
2358
|
+
* bumps it: a page request already in flight was cut against the old head
|
|
2359
|
+
* and its offset no longer means what it did.
|
|
2360
|
+
*/
|
|
2361
|
+
refreshConversations(): Promise<Conversation[]>;
|
|
2337
2362
|
loadMoreConversations(): Promise<Conversation[]>;
|
|
2338
2363
|
/**
|
|
2339
2364
|
* Rename a conversation, server first.
|
|
@@ -2688,6 +2713,18 @@ declare class StreamManager {
|
|
|
2688
2713
|
have?: ReadonlyArray<{
|
|
2689
2714
|
jobId: string;
|
|
2690
2715
|
lastSeq: number;
|
|
2716
|
+
/**
|
|
2717
|
+
* The prompt this job's turn drew, when the caller knows it.
|
|
2718
|
+
*
|
|
2719
|
+
* The caller's cache holds the PROMPT too (it renders the turn), and a
|
|
2720
|
+
* prompt is a message-table row rather than a stream event — so the
|
|
2721
|
+
* plan's empty-walk rescue has to be told which prompts are already on
|
|
2722
|
+
* screen. Marks are matched against the page of jobs this restore walks,
|
|
2723
|
+
* so a mark for an OLDER turn is not otherwise visible to that rescue,
|
|
2724
|
+
* and a cache deeper than one turn page would re-emit those prompts as
|
|
2725
|
+
* steer bubbles.
|
|
2726
|
+
*/
|
|
2727
|
+
messageId?: string | null;
|
|
2691
2728
|
}>;
|
|
2692
2729
|
}): Promise<void>;
|
|
2693
2730
|
/**
|
package/dist/index.js
CHANGED
|
@@ -2772,6 +2772,48 @@ var ChatSession = class {
|
|
|
2772
2772
|
if (!fresh.length) return;
|
|
2773
2773
|
this.setMessages([...fresh, ...this.messages]);
|
|
2774
2774
|
}
|
|
2775
|
+
/**
|
|
2776
|
+
* Re-fetch the NEWEST page of the conversation list and merge it in.
|
|
2777
|
+
*
|
|
2778
|
+
* The list the sidebar renders is seeded once at `connect()` and then only
|
|
2779
|
+
* changed by this client's own actions (create, delete, rename) — so a row's
|
|
2780
|
+
* `activeJobId` is as fresh as that seed, and a job started in ANOTHER tab or
|
|
2781
|
+
* on another device never appears, while one that ended elsewhere stays lit
|
|
2782
|
+
* forever. A caller that refreshes on a trigger (the tab becoming visible, or
|
|
2783
|
+
* a bounded interval while any job is live) keeps the rows honest without a
|
|
2784
|
+
* socket per conversation.
|
|
2785
|
+
*
|
|
2786
|
+
* Merge rules, and why each is what it is:
|
|
2787
|
+
*
|
|
2788
|
+
* - the server's ORDER wins for the rows it returns: an updated conversation
|
|
2789
|
+
* moves to its new place, which is the whole point of the call;
|
|
2790
|
+
* - rows paged in EARLIER (`loadMoreConversations`) are kept — this reads page
|
|
2791
|
+
* 1 only, and dropping the tail would make a refresh silently truncate a
|
|
2792
|
+
* list the reader has already scrolled;
|
|
2793
|
+
* - a row the caller MINTED locally and the server does not know yet is kept
|
|
2794
|
+
* too (it has no server row to be refreshed by);
|
|
2795
|
+
* - `conversationsGeneration` is bumped, for the same reason `connect()`
|
|
2796
|
+
* bumps it: a page request already in flight was cut against the old head
|
|
2797
|
+
* and its offset no longer means what it did.
|
|
2798
|
+
*/
|
|
2799
|
+
async refreshConversations() {
|
|
2800
|
+
const page = await this.client.getConversations(CONVERSATION_PAGE_SIZE, 0);
|
|
2801
|
+
const pageIds = new Set(page.map((c) => c.id));
|
|
2802
|
+
const known = new Set(this.conversations.map((c) => c.id));
|
|
2803
|
+
this.conversationsGeneration++;
|
|
2804
|
+
this.conversations = [
|
|
2805
|
+
...page,
|
|
2806
|
+
// Everything the page did not mention: older pages, and rows that exist
|
|
2807
|
+
// only on this client (a minted conversation the server has not been told
|
|
2808
|
+
// about yet).
|
|
2809
|
+
...this.conversations.filter((c) => !pageIds.has(c.id))
|
|
2810
|
+
];
|
|
2811
|
+
for (const c of page) {
|
|
2812
|
+
this.serverConversationIds.add(c.id);
|
|
2813
|
+
known.delete(c.id);
|
|
2814
|
+
}
|
|
2815
|
+
return page;
|
|
2816
|
+
}
|
|
2775
2817
|
async loadMoreConversations() {
|
|
2776
2818
|
if (this.isLoadingConversations || !this.hasMoreConversations) return [];
|
|
2777
2819
|
this.isLoadingConversations = true;
|
|
@@ -3367,6 +3409,7 @@ var StreamManager = class _StreamManager {
|
|
|
3367
3409
|
const haveByJobId = new Map(
|
|
3368
3410
|
(have ?? []).map((h) => [h.jobId, h.lastSeq])
|
|
3369
3411
|
);
|
|
3412
|
+
const heldFromMarks = (have ?? []).map((h) => h.messageId).filter((id) => typeof id === "string" && id.length > 0);
|
|
3370
3413
|
const superseded = () => gen !== this.generation;
|
|
3371
3414
|
if (superseded()) return;
|
|
3372
3415
|
const turn = this.turnCounter;
|
|
@@ -3394,7 +3437,8 @@ var StreamManager = class _StreamManager {
|
|
|
3394
3437
|
activeJobId,
|
|
3395
3438
|
turn,
|
|
3396
3439
|
jobsRequest,
|
|
3397
|
-
haveByJobId
|
|
3440
|
+
haveByJobId,
|
|
3441
|
+
heldFromMarks
|
|
3398
3442
|
))
|
|
3399
3443
|
return;
|
|
3400
3444
|
if (activeJobId) {
|
|
@@ -3591,7 +3635,7 @@ var StreamManager = class _StreamManager {
|
|
|
3591
3635
|
session.isLoadingEarlierTurns = false;
|
|
3592
3636
|
}
|
|
3593
3637
|
}
|
|
3594
|
-
async replayHistory(conversationId, gen, activeJobId, turn, jobsRequest, haveByJobId) {
|
|
3638
|
+
async replayHistory(conversationId, gen, activeJobId, turn, jobsRequest, haveByJobId, heldFromMarks) {
|
|
3595
3639
|
const stopReplay = () => gen !== this.generation || this.viewTakenOverByLiveTurn() || this.turnStarted(turn);
|
|
3596
3640
|
let complete = false;
|
|
3597
3641
|
try {
|
|
@@ -3638,7 +3682,10 @@ var StreamManager = class _StreamManager {
|
|
|
3638
3682
|
// primary cache scenario, a SETTLED conversation whose turns are all
|
|
3639
3683
|
// held — and the empty-walk rescue would otherwise re-emit every one of
|
|
3640
3684
|
// them as a steer bubble.
|
|
3641
|
-
heldMessageIds:
|
|
3685
|
+
heldMessageIds: [
|
|
3686
|
+
...jobs.filter((j) => haveByJobId.has(j.job_id)).map((j) => j.message_id),
|
|
3687
|
+
...heldFromMarks
|
|
3688
|
+
],
|
|
3642
3689
|
userMessages: userMessages.map((m) => ({
|
|
3643
3690
|
id: m.id,
|
|
3644
3691
|
content: m.content
|
|
@@ -3672,8 +3719,11 @@ var StreamManager = class _StreamManager {
|
|
|
3672
3719
|
step.messageId
|
|
3673
3720
|
);
|
|
3674
3721
|
}
|
|
3675
|
-
|
|
3676
|
-
|
|
3722
|
+
const drewNothing = plan.length === 0;
|
|
3723
|
+
const holdsTurns = haveByJobId.size > 0;
|
|
3724
|
+
const noDrawableTurns = drewNothing && holdsTurns;
|
|
3725
|
+
this.session.hasMoreTurns = noDrawableTurns ? false : page.hasMore;
|
|
3726
|
+
this.session.oldestTurnCursor = noDrawableTurns ? null : page.nextBefore;
|
|
3677
3727
|
this.oldestDrawnMessageId = plan[0]?.messageId ?? null;
|
|
3678
3728
|
complete = true;
|
|
3679
3729
|
if (stopReplay()) return false;
|