@astralform/js 4.2.0 → 4.4.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 +196 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -23
- package/dist/index.d.ts +87 -23
- package/dist/index.js +195 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -420,6 +420,12 @@ type ChatEvent = {
|
|
|
420
420
|
type: "user_message";
|
|
421
421
|
content: string;
|
|
422
422
|
createdAt?: number;
|
|
423
|
+
id?: string;
|
|
424
|
+
/** This message was steered into a run already in flight — it started
|
|
425
|
+
* no turn of its own. Consumers that badge a steer (a "waiting to be
|
|
426
|
+
* read" notice) key off this; without it a replayed steer is
|
|
427
|
+
* indistinguishable from an ordinary prompt. */
|
|
428
|
+
steer?: boolean;
|
|
423
429
|
} | {
|
|
424
430
|
type: "title_generated";
|
|
425
431
|
title: string;
|
|
@@ -796,7 +802,7 @@ declare class AstralformClient {
|
|
|
796
802
|
constructor(config: AstralformConfig);
|
|
797
803
|
/**
|
|
798
804
|
* Replace the current OIDC access token without reconstructing the client.
|
|
799
|
-
* Use after refreshing via the host's token manager
|
|
805
|
+
* Use after refreshing via the host app's own token manager.
|
|
800
806
|
* Throws if the client was created in API-key mode.
|
|
801
807
|
*/
|
|
802
808
|
updateAccessToken(accessToken: string): void;
|
|
@@ -980,6 +986,13 @@ declare class ToolRegistry {
|
|
|
980
986
|
}
|
|
981
987
|
|
|
982
988
|
type ChatEventHandler = (event: ChatEvent) => void;
|
|
989
|
+
/**
|
|
990
|
+
* Conversations fetched per page, by ``connect`` and ``loadMoreConversations``
|
|
991
|
+
* alike. The two must use the same size: the offset is derived from how many
|
|
992
|
+
* rows the server has returned so far, so a first page of a different size
|
|
993
|
+
* would leave the second page's offset pointing at the wrong row.
|
|
994
|
+
*/
|
|
995
|
+
declare const CONVERSATION_PAGE_SIZE = 50;
|
|
983
996
|
/**
|
|
984
997
|
* ChatSession — translates the backend wire protocol into typed ChatEvents
|
|
985
998
|
* for consumers. Owns HTTP + SSE plumbing, conversation state, and the
|
|
@@ -1000,6 +1013,17 @@ declare class ChatSession {
|
|
|
1000
1013
|
readonly protocols: ProtocolRegistry<ProtocolAdapter>;
|
|
1001
1014
|
conversationId: string | null;
|
|
1002
1015
|
conversations: Conversation[];
|
|
1016
|
+
/**
|
|
1017
|
+
* Whether another page of conversations may exist on the server.
|
|
1018
|
+
*
|
|
1019
|
+
* Inferred from the last page being full, since the list endpoint returns a
|
|
1020
|
+
* bare array with no total. A total that happens to be an exact multiple of
|
|
1021
|
+
* the page size therefore costs one extra empty request before this flips —
|
|
1022
|
+
* cheaper than adding a count query to every list call.
|
|
1023
|
+
*/
|
|
1024
|
+
hasMoreConversations: boolean;
|
|
1025
|
+
/** True while ``loadMoreConversations`` is in flight. */
|
|
1026
|
+
isLoadingConversations: boolean;
|
|
1003
1027
|
messages: Message[];
|
|
1004
1028
|
isStreaming: boolean;
|
|
1005
1029
|
agentStatus: AgentStatus | null;
|
|
@@ -1007,6 +1031,32 @@ declare class ChatSession {
|
|
|
1007
1031
|
skills: SkillInfo[];
|
|
1008
1032
|
enabledClientTools: Set<string>;
|
|
1009
1033
|
modelDisplayName: string | null;
|
|
1034
|
+
/**
|
|
1035
|
+
* Ids of conversations the SERVER has handed us, which is the paging offset.
|
|
1036
|
+
*
|
|
1037
|
+
* Deliberately not ``conversations.length``. That array also holds
|
|
1038
|
+
* conversations created locally and unshifted on top (``createNewConversation``,
|
|
1039
|
+
* and the auto-created conversation in ``consumeJobStream``), so using its
|
|
1040
|
+
* length as the offset would over-count and silently skip a row of real
|
|
1041
|
+
* history on the next page. Tracking ids rather than a counter also makes
|
|
1042
|
+
* deletion self-correcting: removing a server-sourced conversation shifts
|
|
1043
|
+
* every later page up by one, and dropping its id from this set is exactly
|
|
1044
|
+
* that shift — while deleting a purely local one correctly changes nothing.
|
|
1045
|
+
*/
|
|
1046
|
+
private serverConversationIds;
|
|
1047
|
+
/**
|
|
1048
|
+
* Bumped every time ``connect()`` re-seeds the conversation list.
|
|
1049
|
+
*
|
|
1050
|
+
* A ``loadMoreConversations`` request issued before a re-seed describes the
|
|
1051
|
+
* OLD paging state, so applying its response afterwards both appends the
|
|
1052
|
+
* wrong rows and corrupts the offset. Concretely: with 100 rows held, an
|
|
1053
|
+
* offset-100 response landing after a reconnect has reset to rows 0-49 would
|
|
1054
|
+
* append rows 100-149 — a 50-row hole — and leave the id set at 100, so every
|
|
1055
|
+
* later page re-requests offset 100 and never advances again. The generation
|
|
1056
|
+
* is captured before the await and rechecked after, so a superseded response
|
|
1057
|
+
* is discarded instead.
|
|
1058
|
+
*/
|
|
1059
|
+
private conversationsGeneration;
|
|
1010
1060
|
private accumulatedText;
|
|
1011
1061
|
private currentTextPath;
|
|
1012
1062
|
private handlers;
|
|
@@ -1095,7 +1145,7 @@ declare class ChatSession {
|
|
|
1095
1145
|
* ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
|
|
1096
1146
|
* so leading with the prompt keeps the turn in order.
|
|
1097
1147
|
*/
|
|
1098
|
-
replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string): void;
|
|
1148
|
+
replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string, userMessageId?: string, isSteer?: boolean): void;
|
|
1099
1149
|
/**
|
|
1100
1150
|
* Load a conversation's messages and replay its persisted history.
|
|
1101
1151
|
*
|
|
@@ -1108,6 +1158,40 @@ declare class ChatSession {
|
|
|
1108
1158
|
* job's events.
|
|
1109
1159
|
*/
|
|
1110
1160
|
switchConversation(id: string, jobId?: string): Promise<void>;
|
|
1161
|
+
/**
|
|
1162
|
+
* Append the next page of conversation history to ``conversations``.
|
|
1163
|
+
*
|
|
1164
|
+
* The list is ordered ``updated_at DESC`` and paged by offset, so a
|
|
1165
|
+
* conversation bumped to the top mid-scroll can surface again in a later
|
|
1166
|
+
* page; ids already held are dropped rather than duplicated. Returns only
|
|
1167
|
+
* the conversations actually appended, which may be empty even on a full
|
|
1168
|
+
* page. Rejects on network failure with ``hasMoreConversations`` still true,
|
|
1169
|
+
* so the caller can retry.
|
|
1170
|
+
*
|
|
1171
|
+
* KNOWN LIMITATION — offset paging is only stable while the prefix already
|
|
1172
|
+
* consumed stays put. The offset tracking here corrects for perturbations
|
|
1173
|
+
* THIS session causes (local unshifts, ``deleteConversation``), but not for
|
|
1174
|
+
* ones it never sees:
|
|
1175
|
+
*
|
|
1176
|
+
* - a conversation this session hasn't loaded yet is bumped to the top (a
|
|
1177
|
+
* headless routine or another device posting to it), pushing the whole
|
|
1178
|
+
* list down — it lands inside the consumed prefix, which no later offset
|
|
1179
|
+
* revisits;
|
|
1180
|
+
* - a conversation is deleted from another tab/device, shrinking the list so
|
|
1181
|
+
* the next offset lands one row too far in.
|
|
1182
|
+
*
|
|
1183
|
+
* Each perturbation costs at most one conversation off the sidebar, and only
|
|
1184
|
+
* until the next ``connect()`` — that re-seeds page 1 and resets the paging
|
|
1185
|
+
* state, so a reload or reconnect always recovers it. Nothing is lost
|
|
1186
|
+
* server-side. Both cases are pinned by tests in
|
|
1187
|
+
* ``tests/conversation-paging.test.ts``.
|
|
1188
|
+
*
|
|
1189
|
+
* Closing the gap properly needs a stable server cursor (keyset paging on
|
|
1190
|
+
* ``(updated_at, id)``) rather than a raw offset, which is a backend change —
|
|
1191
|
+
* tracking ids client-side cannot discover a row that moved into a region
|
|
1192
|
+
* already scanned.
|
|
1193
|
+
*/
|
|
1194
|
+
loadMoreConversations(): Promise<Conversation[]>;
|
|
1111
1195
|
deleteConversation(id: string): Promise<void>;
|
|
1112
1196
|
toggleClientTool(name: string): boolean;
|
|
1113
1197
|
}
|
|
@@ -1158,26 +1242,6 @@ declare function generateId(): string;
|
|
|
1158
1242
|
*/
|
|
1159
1243
|
declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<ChatStreamEvent>;
|
|
1160
1244
|
|
|
1161
|
-
/**
|
|
1162
|
-
* StreamManager — high-level conversation lifecycle coordinator.
|
|
1163
|
-
*
|
|
1164
|
-
* Sits on top of ChatSession and manages the state machine for
|
|
1165
|
-
* multi-conversation SSE streaming. Framework-agnostic: emits
|
|
1166
|
-
* typed events to registered handlers. Block construction is NOT
|
|
1167
|
-
* the SDK's concern — consumers build their own block tree from
|
|
1168
|
-
* the forwarded ``ChatEvent`` instances.
|
|
1169
|
-
*
|
|
1170
|
-
* import { ChatSession, StreamManager } from "@astralform/js";
|
|
1171
|
-
* const session = new ChatSession({ ... });
|
|
1172
|
-
* const manager = new StreamManager(session);
|
|
1173
|
-
* manager.on((event) => {
|
|
1174
|
-
* if (event.type === "event") {
|
|
1175
|
-
* // event.event is a typed ChatEvent — dispatch to your reducer
|
|
1176
|
-
* }
|
|
1177
|
-
* });
|
|
1178
|
-
* await manager.send("Hello");
|
|
1179
|
-
*/
|
|
1180
|
-
|
|
1181
1245
|
type StreamState = "idle" | "streaming" | "restoring" | "detached";
|
|
1182
1246
|
interface SendOptions extends ModelChoiceOptions {
|
|
1183
1247
|
agentName?: string;
|
|
@@ -1340,4 +1404,4 @@ declare function isEmbeddedResource(value: unknown): value is {
|
|
|
1340
1404
|
*/
|
|
1341
1405
|
declare function parseEmbeddedResource(value: unknown): EmbeddedResource | null;
|
|
1342
1406
|
|
|
1343
|
-
export { type ActiveJob, type AgentIdentity, type AgentInfo, type AgentStatus, type AssetCreatedPayload, type AstralformApiKeyConfig, AstralformClient, type AstralformConfig, AstralformError, type AstralformUserTokenConfig, type AttachmentStagedPayload, AuthenticationError, type BlockDeltaPayload, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type ModelChoiceOptions, type ModelOption, type MyToolGrantsPage, type PromptSuggestionPayload, type ProtocolAdapter, ProtocolRegistry, RateLimitError, type RateLimitErrorDetails, type RawSseEvent, type ReasoningEffort, type SendOptions, ServerError, type SkillInfo, StreamAbortedError, type StreamJobSSEOptions, StreamManager, type StreamManagerEvent, type StreamState, type SubagentStartPayload, type SubagentStopPayload, type TaskStatus, type TeamAgentSummary, type TeamSummary, type TitleGeneratedPayload, type TodoItem, type TodoUpdatePayload, type ToolApprovalDecision, type ToolApprovalGrantedPayload, type ToolApprovalRequest, type ToolApprovalRequestedPayload, type ToolApprovalScope, type ToolCallRequest, type ToolDefinition, type ToolGrant, type ToolHandler, type ToolHarnessWarningPayload, type ToolPermissionDeniedPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, type WireBlockDelta, type WireBlockDeltaPayload, type WireBlockKind, type WireBlockStart, type WireBlockStatus, type WireBlockStop, type WireCustomEvent, type WireErrorEvent, type WireEvent, type WireInputArgDelta, type WireInputDelta, type WireKeepalive, type WireMessageStart, type WireMessageStop, type WireOutputDelta, type WireRetryEvent, type WireSignatureDelta, type WireStallWarning, type WireStatusDelta, type WireStopReason, type WireTextDelta, type WireThinkingDelta, type WorkspaceReadyPayload, generateId, isEmbeddedResource, mapSseToChat, parseEmbeddedResource, replayEvents, streamJobSSE, translateDelta };
|
|
1407
|
+
export { type ActiveJob, type AgentIdentity, type AgentInfo, type AgentStatus, type AssetCreatedPayload, type AstralformApiKeyConfig, AstralformClient, type AstralformConfig, AstralformError, type AstralformUserTokenConfig, type AttachmentStagedPayload, AuthenticationError, type BlockDeltaPayload, CONVERSATION_PAGE_SIZE, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type ModelChoiceOptions, type ModelOption, type MyToolGrantsPage, type PromptSuggestionPayload, type ProtocolAdapter, ProtocolRegistry, RateLimitError, type RateLimitErrorDetails, type RawSseEvent, type ReasoningEffort, type SendOptions, ServerError, type SkillInfo, StreamAbortedError, type StreamJobSSEOptions, StreamManager, type StreamManagerEvent, type StreamState, type SubagentStartPayload, type SubagentStopPayload, type TaskStatus, type TeamAgentSummary, type TeamSummary, type TitleGeneratedPayload, type TodoItem, type TodoUpdatePayload, type ToolApprovalDecision, type ToolApprovalGrantedPayload, type ToolApprovalRequest, type ToolApprovalRequestedPayload, type ToolApprovalScope, type ToolCallRequest, type ToolDefinition, type ToolGrant, type ToolHandler, type ToolHarnessWarningPayload, type ToolPermissionDeniedPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, type WireBlockDelta, type WireBlockDeltaPayload, type WireBlockKind, type WireBlockStart, type WireBlockStatus, type WireBlockStop, type WireCustomEvent, type WireErrorEvent, type WireEvent, type WireInputArgDelta, type WireInputDelta, type WireKeepalive, type WireMessageStart, type WireMessageStop, type WireOutputDelta, type WireRetryEvent, type WireSignatureDelta, type WireStallWarning, type WireStatusDelta, type WireStopReason, type WireTextDelta, type WireThinkingDelta, type WorkspaceReadyPayload, generateId, isEmbeddedResource, mapSseToChat, parseEmbeddedResource, replayEvents, streamJobSSE, translateDelta };
|
package/dist/index.d.ts
CHANGED
|
@@ -420,6 +420,12 @@ type ChatEvent = {
|
|
|
420
420
|
type: "user_message";
|
|
421
421
|
content: string;
|
|
422
422
|
createdAt?: number;
|
|
423
|
+
id?: string;
|
|
424
|
+
/** This message was steered into a run already in flight — it started
|
|
425
|
+
* no turn of its own. Consumers that badge a steer (a "waiting to be
|
|
426
|
+
* read" notice) key off this; without it a replayed steer is
|
|
427
|
+
* indistinguishable from an ordinary prompt. */
|
|
428
|
+
steer?: boolean;
|
|
423
429
|
} | {
|
|
424
430
|
type: "title_generated";
|
|
425
431
|
title: string;
|
|
@@ -796,7 +802,7 @@ declare class AstralformClient {
|
|
|
796
802
|
constructor(config: AstralformConfig);
|
|
797
803
|
/**
|
|
798
804
|
* Replace the current OIDC access token without reconstructing the client.
|
|
799
|
-
* Use after refreshing via the host's token manager
|
|
805
|
+
* Use after refreshing via the host app's own token manager.
|
|
800
806
|
* Throws if the client was created in API-key mode.
|
|
801
807
|
*/
|
|
802
808
|
updateAccessToken(accessToken: string): void;
|
|
@@ -980,6 +986,13 @@ declare class ToolRegistry {
|
|
|
980
986
|
}
|
|
981
987
|
|
|
982
988
|
type ChatEventHandler = (event: ChatEvent) => void;
|
|
989
|
+
/**
|
|
990
|
+
* Conversations fetched per page, by ``connect`` and ``loadMoreConversations``
|
|
991
|
+
* alike. The two must use the same size: the offset is derived from how many
|
|
992
|
+
* rows the server has returned so far, so a first page of a different size
|
|
993
|
+
* would leave the second page's offset pointing at the wrong row.
|
|
994
|
+
*/
|
|
995
|
+
declare const CONVERSATION_PAGE_SIZE = 50;
|
|
983
996
|
/**
|
|
984
997
|
* ChatSession — translates the backend wire protocol into typed ChatEvents
|
|
985
998
|
* for consumers. Owns HTTP + SSE plumbing, conversation state, and the
|
|
@@ -1000,6 +1013,17 @@ declare class ChatSession {
|
|
|
1000
1013
|
readonly protocols: ProtocolRegistry<ProtocolAdapter>;
|
|
1001
1014
|
conversationId: string | null;
|
|
1002
1015
|
conversations: Conversation[];
|
|
1016
|
+
/**
|
|
1017
|
+
* Whether another page of conversations may exist on the server.
|
|
1018
|
+
*
|
|
1019
|
+
* Inferred from the last page being full, since the list endpoint returns a
|
|
1020
|
+
* bare array with no total. A total that happens to be an exact multiple of
|
|
1021
|
+
* the page size therefore costs one extra empty request before this flips —
|
|
1022
|
+
* cheaper than adding a count query to every list call.
|
|
1023
|
+
*/
|
|
1024
|
+
hasMoreConversations: boolean;
|
|
1025
|
+
/** True while ``loadMoreConversations`` is in flight. */
|
|
1026
|
+
isLoadingConversations: boolean;
|
|
1003
1027
|
messages: Message[];
|
|
1004
1028
|
isStreaming: boolean;
|
|
1005
1029
|
agentStatus: AgentStatus | null;
|
|
@@ -1007,6 +1031,32 @@ declare class ChatSession {
|
|
|
1007
1031
|
skills: SkillInfo[];
|
|
1008
1032
|
enabledClientTools: Set<string>;
|
|
1009
1033
|
modelDisplayName: string | null;
|
|
1034
|
+
/**
|
|
1035
|
+
* Ids of conversations the SERVER has handed us, which is the paging offset.
|
|
1036
|
+
*
|
|
1037
|
+
* Deliberately not ``conversations.length``. That array also holds
|
|
1038
|
+
* conversations created locally and unshifted on top (``createNewConversation``,
|
|
1039
|
+
* and the auto-created conversation in ``consumeJobStream``), so using its
|
|
1040
|
+
* length as the offset would over-count and silently skip a row of real
|
|
1041
|
+
* history on the next page. Tracking ids rather than a counter also makes
|
|
1042
|
+
* deletion self-correcting: removing a server-sourced conversation shifts
|
|
1043
|
+
* every later page up by one, and dropping its id from this set is exactly
|
|
1044
|
+
* that shift — while deleting a purely local one correctly changes nothing.
|
|
1045
|
+
*/
|
|
1046
|
+
private serverConversationIds;
|
|
1047
|
+
/**
|
|
1048
|
+
* Bumped every time ``connect()`` re-seeds the conversation list.
|
|
1049
|
+
*
|
|
1050
|
+
* A ``loadMoreConversations`` request issued before a re-seed describes the
|
|
1051
|
+
* OLD paging state, so applying its response afterwards both appends the
|
|
1052
|
+
* wrong rows and corrupts the offset. Concretely: with 100 rows held, an
|
|
1053
|
+
* offset-100 response landing after a reconnect has reset to rows 0-49 would
|
|
1054
|
+
* append rows 100-149 — a 50-row hole — and leave the id set at 100, so every
|
|
1055
|
+
* later page re-requests offset 100 and never advances again. The generation
|
|
1056
|
+
* is captured before the await and rechecked after, so a superseded response
|
|
1057
|
+
* is discarded instead.
|
|
1058
|
+
*/
|
|
1059
|
+
private conversationsGeneration;
|
|
1010
1060
|
private accumulatedText;
|
|
1011
1061
|
private currentTextPath;
|
|
1012
1062
|
private handlers;
|
|
@@ -1095,7 +1145,7 @@ declare class ChatSession {
|
|
|
1095
1145
|
* ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
|
|
1096
1146
|
* so leading with the prompt keeps the turn in order.
|
|
1097
1147
|
*/
|
|
1098
|
-
replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string): void;
|
|
1148
|
+
replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string, userMessageId?: string, isSteer?: boolean): void;
|
|
1099
1149
|
/**
|
|
1100
1150
|
* Load a conversation's messages and replay its persisted history.
|
|
1101
1151
|
*
|
|
@@ -1108,6 +1158,40 @@ declare class ChatSession {
|
|
|
1108
1158
|
* job's events.
|
|
1109
1159
|
*/
|
|
1110
1160
|
switchConversation(id: string, jobId?: string): Promise<void>;
|
|
1161
|
+
/**
|
|
1162
|
+
* Append the next page of conversation history to ``conversations``.
|
|
1163
|
+
*
|
|
1164
|
+
* The list is ordered ``updated_at DESC`` and paged by offset, so a
|
|
1165
|
+
* conversation bumped to the top mid-scroll can surface again in a later
|
|
1166
|
+
* page; ids already held are dropped rather than duplicated. Returns only
|
|
1167
|
+
* the conversations actually appended, which may be empty even on a full
|
|
1168
|
+
* page. Rejects on network failure with ``hasMoreConversations`` still true,
|
|
1169
|
+
* so the caller can retry.
|
|
1170
|
+
*
|
|
1171
|
+
* KNOWN LIMITATION — offset paging is only stable while the prefix already
|
|
1172
|
+
* consumed stays put. The offset tracking here corrects for perturbations
|
|
1173
|
+
* THIS session causes (local unshifts, ``deleteConversation``), but not for
|
|
1174
|
+
* ones it never sees:
|
|
1175
|
+
*
|
|
1176
|
+
* - a conversation this session hasn't loaded yet is bumped to the top (a
|
|
1177
|
+
* headless routine or another device posting to it), pushing the whole
|
|
1178
|
+
* list down — it lands inside the consumed prefix, which no later offset
|
|
1179
|
+
* revisits;
|
|
1180
|
+
* - a conversation is deleted from another tab/device, shrinking the list so
|
|
1181
|
+
* the next offset lands one row too far in.
|
|
1182
|
+
*
|
|
1183
|
+
* Each perturbation costs at most one conversation off the sidebar, and only
|
|
1184
|
+
* until the next ``connect()`` — that re-seeds page 1 and resets the paging
|
|
1185
|
+
* state, so a reload or reconnect always recovers it. Nothing is lost
|
|
1186
|
+
* server-side. Both cases are pinned by tests in
|
|
1187
|
+
* ``tests/conversation-paging.test.ts``.
|
|
1188
|
+
*
|
|
1189
|
+
* Closing the gap properly needs a stable server cursor (keyset paging on
|
|
1190
|
+
* ``(updated_at, id)``) rather than a raw offset, which is a backend change —
|
|
1191
|
+
* tracking ids client-side cannot discover a row that moved into a region
|
|
1192
|
+
* already scanned.
|
|
1193
|
+
*/
|
|
1194
|
+
loadMoreConversations(): Promise<Conversation[]>;
|
|
1111
1195
|
deleteConversation(id: string): Promise<void>;
|
|
1112
1196
|
toggleClientTool(name: string): boolean;
|
|
1113
1197
|
}
|
|
@@ -1158,26 +1242,6 @@ declare function generateId(): string;
|
|
|
1158
1242
|
*/
|
|
1159
1243
|
declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<ChatStreamEvent>;
|
|
1160
1244
|
|
|
1161
|
-
/**
|
|
1162
|
-
* StreamManager — high-level conversation lifecycle coordinator.
|
|
1163
|
-
*
|
|
1164
|
-
* Sits on top of ChatSession and manages the state machine for
|
|
1165
|
-
* multi-conversation SSE streaming. Framework-agnostic: emits
|
|
1166
|
-
* typed events to registered handlers. Block construction is NOT
|
|
1167
|
-
* the SDK's concern — consumers build their own block tree from
|
|
1168
|
-
* the forwarded ``ChatEvent`` instances.
|
|
1169
|
-
*
|
|
1170
|
-
* import { ChatSession, StreamManager } from "@astralform/js";
|
|
1171
|
-
* const session = new ChatSession({ ... });
|
|
1172
|
-
* const manager = new StreamManager(session);
|
|
1173
|
-
* manager.on((event) => {
|
|
1174
|
-
* if (event.type === "event") {
|
|
1175
|
-
* // event.event is a typed ChatEvent — dispatch to your reducer
|
|
1176
|
-
* }
|
|
1177
|
-
* });
|
|
1178
|
-
* await manager.send("Hello");
|
|
1179
|
-
*/
|
|
1180
|
-
|
|
1181
1245
|
type StreamState = "idle" | "streaming" | "restoring" | "detached";
|
|
1182
1246
|
interface SendOptions extends ModelChoiceOptions {
|
|
1183
1247
|
agentName?: string;
|
|
@@ -1340,4 +1404,4 @@ declare function isEmbeddedResource(value: unknown): value is {
|
|
|
1340
1404
|
*/
|
|
1341
1405
|
declare function parseEmbeddedResource(value: unknown): EmbeddedResource | null;
|
|
1342
1406
|
|
|
1343
|
-
export { type ActiveJob, type AgentIdentity, type AgentInfo, type AgentStatus, type AssetCreatedPayload, type AstralformApiKeyConfig, AstralformClient, type AstralformConfig, AstralformError, type AstralformUserTokenConfig, type AttachmentStagedPayload, AuthenticationError, type BlockDeltaPayload, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type ModelChoiceOptions, type ModelOption, type MyToolGrantsPage, type PromptSuggestionPayload, type ProtocolAdapter, ProtocolRegistry, RateLimitError, type RateLimitErrorDetails, type RawSseEvent, type ReasoningEffort, type SendOptions, ServerError, type SkillInfo, StreamAbortedError, type StreamJobSSEOptions, StreamManager, type StreamManagerEvent, type StreamState, type SubagentStartPayload, type SubagentStopPayload, type TaskStatus, type TeamAgentSummary, type TeamSummary, type TitleGeneratedPayload, type TodoItem, type TodoUpdatePayload, type ToolApprovalDecision, type ToolApprovalGrantedPayload, type ToolApprovalRequest, type ToolApprovalRequestedPayload, type ToolApprovalScope, type ToolCallRequest, type ToolDefinition, type ToolGrant, type ToolHandler, type ToolHarnessWarningPayload, type ToolPermissionDeniedPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, type WireBlockDelta, type WireBlockDeltaPayload, type WireBlockKind, type WireBlockStart, type WireBlockStatus, type WireBlockStop, type WireCustomEvent, type WireErrorEvent, type WireEvent, type WireInputArgDelta, type WireInputDelta, type WireKeepalive, type WireMessageStart, type WireMessageStop, type WireOutputDelta, type WireRetryEvent, type WireSignatureDelta, type WireStallWarning, type WireStatusDelta, type WireStopReason, type WireTextDelta, type WireThinkingDelta, type WorkspaceReadyPayload, generateId, isEmbeddedResource, mapSseToChat, parseEmbeddedResource, replayEvents, streamJobSSE, translateDelta };
|
|
1407
|
+
export { type ActiveJob, type AgentIdentity, type AgentInfo, type AgentStatus, type AssetCreatedPayload, type AstralformApiKeyConfig, AstralformClient, type AstralformConfig, AstralformError, type AstralformUserTokenConfig, type AttachmentStagedPayload, AuthenticationError, type BlockDeltaPayload, CONVERSATION_PAGE_SIZE, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type ModelChoiceOptions, type ModelOption, type MyToolGrantsPage, type PromptSuggestionPayload, type ProtocolAdapter, ProtocolRegistry, RateLimitError, type RateLimitErrorDetails, type RawSseEvent, type ReasoningEffort, type SendOptions, ServerError, type SkillInfo, StreamAbortedError, type StreamJobSSEOptions, StreamManager, type StreamManagerEvent, type StreamState, type SubagentStartPayload, type SubagentStopPayload, type TaskStatus, type TeamAgentSummary, type TeamSummary, type TitleGeneratedPayload, type TodoItem, type TodoUpdatePayload, type ToolApprovalDecision, type ToolApprovalGrantedPayload, type ToolApprovalRequest, type ToolApprovalRequestedPayload, type ToolApprovalScope, type ToolCallRequest, type ToolDefinition, type ToolGrant, type ToolHandler, type ToolHarnessWarningPayload, type ToolPermissionDeniedPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, type WireBlockDelta, type WireBlockDeltaPayload, type WireBlockKind, type WireBlockStart, type WireBlockStatus, type WireBlockStop, type WireCustomEvent, type WireErrorEvent, type WireEvent, type WireInputArgDelta, type WireInputDelta, type WireKeepalive, type WireMessageStart, type WireMessageStop, type WireOutputDelta, type WireRetryEvent, type WireSignatureDelta, type WireStallWarning, type WireStatusDelta, type WireStopReason, type WireTextDelta, type WireThinkingDelta, type WorkspaceReadyPayload, generateId, isEmbeddedResource, mapSseToChat, parseEmbeddedResource, replayEvents, streamJobSSE, translateDelta };
|
package/dist/index.js
CHANGED
|
@@ -307,7 +307,7 @@ var AstralformClient = class {
|
|
|
307
307
|
}
|
|
308
308
|
/**
|
|
309
309
|
* Replace the current OIDC access token without reconstructing the client.
|
|
310
|
-
* Use after refreshing via the host's token manager
|
|
310
|
+
* Use after refreshing via the host app's own token manager.
|
|
311
311
|
* Throws if the client was created in API-key mode.
|
|
312
312
|
*/
|
|
313
313
|
updateAccessToken(accessToken) {
|
|
@@ -1232,6 +1232,7 @@ function translateWireEvent(wire) {
|
|
|
1232
1232
|
// src/session.ts
|
|
1233
1233
|
var SSE_MAX_RECONNECTS = 6;
|
|
1234
1234
|
var TOOL_RESULT_MAX_RETRIES = 3;
|
|
1235
|
+
var CONVERSATION_PAGE_SIZE = 50;
|
|
1235
1236
|
function sseReconnectDelayMs(attempt) {
|
|
1236
1237
|
return Math.min(500 * 2 ** (attempt - 1), 5e3);
|
|
1237
1238
|
}
|
|
@@ -1255,6 +1256,17 @@ var ChatSession = class {
|
|
|
1255
1256
|
// State
|
|
1256
1257
|
this.conversationId = null;
|
|
1257
1258
|
this.conversations = [];
|
|
1259
|
+
/**
|
|
1260
|
+
* Whether another page of conversations may exist on the server.
|
|
1261
|
+
*
|
|
1262
|
+
* Inferred from the last page being full, since the list endpoint returns a
|
|
1263
|
+
* bare array with no total. A total that happens to be an exact multiple of
|
|
1264
|
+
* the page size therefore costs one extra empty request before this flips —
|
|
1265
|
+
* cheaper than adding a count query to every list call.
|
|
1266
|
+
*/
|
|
1267
|
+
this.hasMoreConversations = false;
|
|
1268
|
+
/** True while ``loadMoreConversations`` is in flight. */
|
|
1269
|
+
this.isLoadingConversations = false;
|
|
1258
1270
|
this.messages = [];
|
|
1259
1271
|
this.isStreaming = false;
|
|
1260
1272
|
this.agentStatus = null;
|
|
@@ -1262,6 +1274,32 @@ var ChatSession = class {
|
|
|
1262
1274
|
this.skills = [];
|
|
1263
1275
|
this.enabledClientTools = /* @__PURE__ */ new Set();
|
|
1264
1276
|
this.modelDisplayName = null;
|
|
1277
|
+
/**
|
|
1278
|
+
* Ids of conversations the SERVER has handed us, which is the paging offset.
|
|
1279
|
+
*
|
|
1280
|
+
* Deliberately not ``conversations.length``. That array also holds
|
|
1281
|
+
* conversations created locally and unshifted on top (``createNewConversation``,
|
|
1282
|
+
* and the auto-created conversation in ``consumeJobStream``), so using its
|
|
1283
|
+
* length as the offset would over-count and silently skip a row of real
|
|
1284
|
+
* history on the next page. Tracking ids rather than a counter also makes
|
|
1285
|
+
* deletion self-correcting: removing a server-sourced conversation shifts
|
|
1286
|
+
* every later page up by one, and dropping its id from this set is exactly
|
|
1287
|
+
* that shift — while deleting a purely local one correctly changes nothing.
|
|
1288
|
+
*/
|
|
1289
|
+
this.serverConversationIds = /* @__PURE__ */ new Set();
|
|
1290
|
+
/**
|
|
1291
|
+
* Bumped every time ``connect()`` re-seeds the conversation list.
|
|
1292
|
+
*
|
|
1293
|
+
* A ``loadMoreConversations`` request issued before a re-seed describes the
|
|
1294
|
+
* OLD paging state, so applying its response afterwards both appends the
|
|
1295
|
+
* wrong rows and corrupts the offset. Concretely: with 100 rows held, an
|
|
1296
|
+
* offset-100 response landing after a reconnect has reset to rows 0-49 would
|
|
1297
|
+
* append rows 100-149 — a 50-row hole — and leave the id set at 100, so every
|
|
1298
|
+
* later page re-requests offset 100 and never advances again. The generation
|
|
1299
|
+
* is captured before the await and rechecked after, so a superseded response
|
|
1300
|
+
* is discarded instead.
|
|
1301
|
+
*/
|
|
1302
|
+
this.conversationsGeneration = 0;
|
|
1265
1303
|
// Minimal in-session accumulation for the assistant message record.
|
|
1266
1304
|
// Only top-level ``text`` blocks contribute; subagent / tool output
|
|
1267
1305
|
// is tracked by the consumer's own block store.
|
|
@@ -1301,7 +1339,7 @@ var ChatSession = class {
|
|
|
1301
1339
|
async connect() {
|
|
1302
1340
|
const [status, conversations, agents, skills] = await Promise.allSettled([
|
|
1303
1341
|
this.client.getAgentStatus(),
|
|
1304
|
-
this.client.getConversations(),
|
|
1342
|
+
this.client.getConversations(CONVERSATION_PAGE_SIZE),
|
|
1305
1343
|
this.client.getAgents().catch(() => []),
|
|
1306
1344
|
this.client.getSkills().catch(() => [])
|
|
1307
1345
|
]);
|
|
@@ -1309,7 +1347,12 @@ var ChatSession = class {
|
|
|
1309
1347
|
this.agentStatus = status.value;
|
|
1310
1348
|
}
|
|
1311
1349
|
if (conversations.status === "fulfilled") {
|
|
1350
|
+
this.conversationsGeneration++;
|
|
1312
1351
|
this.conversations = conversations.value;
|
|
1352
|
+
this.serverConversationIds = new Set(
|
|
1353
|
+
conversations.value.map((c) => c.id)
|
|
1354
|
+
);
|
|
1355
|
+
this.hasMoreConversations = conversations.value.length === CONVERSATION_PAGE_SIZE;
|
|
1313
1356
|
}
|
|
1314
1357
|
if (agents.status === "fulfilled") {
|
|
1315
1358
|
this.agents = agents.value;
|
|
@@ -1730,11 +1773,16 @@ var ChatSession = class {
|
|
|
1730
1773
|
* ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
|
|
1731
1774
|
* so leading with the prompt keeps the turn in order.
|
|
1732
1775
|
*/
|
|
1733
|
-
replayTurn(id, events, userMessageContent) {
|
|
1776
|
+
replayTurn(id, events, userMessageContent, userMessageId, isSteer = false) {
|
|
1734
1777
|
this.conversationId = id;
|
|
1735
1778
|
this.resetStreamingState();
|
|
1736
1779
|
if (userMessageContent) {
|
|
1737
|
-
this.emit({
|
|
1780
|
+
this.emit({
|
|
1781
|
+
type: "user_message",
|
|
1782
|
+
content: userMessageContent,
|
|
1783
|
+
...userMessageId ? { id: userMessageId } : {},
|
|
1784
|
+
...isSteer ? { steer: true } : {}
|
|
1785
|
+
});
|
|
1738
1786
|
}
|
|
1739
1787
|
for (const ev of events) {
|
|
1740
1788
|
const type = ev.data.type || ev.event;
|
|
@@ -1768,12 +1816,69 @@ var ChatSession = class {
|
|
|
1768
1816
|
eventsResult.status === "fulfilled" ? eventsResult.value : []
|
|
1769
1817
|
);
|
|
1770
1818
|
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Append the next page of conversation history to ``conversations``.
|
|
1821
|
+
*
|
|
1822
|
+
* The list is ordered ``updated_at DESC`` and paged by offset, so a
|
|
1823
|
+
* conversation bumped to the top mid-scroll can surface again in a later
|
|
1824
|
+
* page; ids already held are dropped rather than duplicated. Returns only
|
|
1825
|
+
* the conversations actually appended, which may be empty even on a full
|
|
1826
|
+
* page. Rejects on network failure with ``hasMoreConversations`` still true,
|
|
1827
|
+
* so the caller can retry.
|
|
1828
|
+
*
|
|
1829
|
+
* KNOWN LIMITATION — offset paging is only stable while the prefix already
|
|
1830
|
+
* consumed stays put. The offset tracking here corrects for perturbations
|
|
1831
|
+
* THIS session causes (local unshifts, ``deleteConversation``), but not for
|
|
1832
|
+
* ones it never sees:
|
|
1833
|
+
*
|
|
1834
|
+
* - a conversation this session hasn't loaded yet is bumped to the top (a
|
|
1835
|
+
* headless routine or another device posting to it), pushing the whole
|
|
1836
|
+
* list down — it lands inside the consumed prefix, which no later offset
|
|
1837
|
+
* revisits;
|
|
1838
|
+
* - a conversation is deleted from another tab/device, shrinking the list so
|
|
1839
|
+
* the next offset lands one row too far in.
|
|
1840
|
+
*
|
|
1841
|
+
* Each perturbation costs at most one conversation off the sidebar, and only
|
|
1842
|
+
* until the next ``connect()`` — that re-seeds page 1 and resets the paging
|
|
1843
|
+
* state, so a reload or reconnect always recovers it. Nothing is lost
|
|
1844
|
+
* server-side. Both cases are pinned by tests in
|
|
1845
|
+
* ``tests/conversation-paging.test.ts``.
|
|
1846
|
+
*
|
|
1847
|
+
* Closing the gap properly needs a stable server cursor (keyset paging on
|
|
1848
|
+
* ``(updated_at, id)``) rather than a raw offset, which is a backend change —
|
|
1849
|
+
* tracking ids client-side cannot discover a row that moved into a region
|
|
1850
|
+
* already scanned.
|
|
1851
|
+
*/
|
|
1852
|
+
async loadMoreConversations() {
|
|
1853
|
+
if (this.isLoadingConversations || !this.hasMoreConversations) return [];
|
|
1854
|
+
this.isLoadingConversations = true;
|
|
1855
|
+
const generation = this.conversationsGeneration;
|
|
1856
|
+
try {
|
|
1857
|
+
const page = await this.client.getConversations(
|
|
1858
|
+
CONVERSATION_PAGE_SIZE,
|
|
1859
|
+
this.serverConversationIds.size
|
|
1860
|
+
);
|
|
1861
|
+
if (generation !== this.conversationsGeneration) return [];
|
|
1862
|
+
this.hasMoreConversations = page.length === CONVERSATION_PAGE_SIZE;
|
|
1863
|
+
const fresh = page.filter((c) => !this.serverConversationIds.has(c.id));
|
|
1864
|
+
for (const c of page) this.serverConversationIds.add(c.id);
|
|
1865
|
+
const known = new Set(this.conversations.map((c) => c.id));
|
|
1866
|
+
const appended = fresh.filter((c) => !known.has(c.id));
|
|
1867
|
+
this.conversations.push(...appended);
|
|
1868
|
+
return appended;
|
|
1869
|
+
} finally {
|
|
1870
|
+
this.isLoadingConversations = false;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1771
1873
|
async deleteConversation(id) {
|
|
1772
1874
|
try {
|
|
1773
1875
|
await this.client.deleteConversation(id);
|
|
1774
1876
|
} catch {
|
|
1775
1877
|
}
|
|
1776
1878
|
await this.storage.deleteConversation(id);
|
|
1879
|
+
if (this.serverConversationIds.delete(id)) {
|
|
1880
|
+
this.conversationsGeneration++;
|
|
1881
|
+
}
|
|
1777
1882
|
this.conversations = this.conversations.filter((c) => c.id !== id);
|
|
1778
1883
|
if (this.conversationId === id) {
|
|
1779
1884
|
this.conversationId = null;
|
|
@@ -1790,6 +1895,64 @@ var ChatSession = class {
|
|
|
1790
1895
|
}
|
|
1791
1896
|
};
|
|
1792
1897
|
|
|
1898
|
+
// src/restore-plan.ts
|
|
1899
|
+
function planRestore(args) {
|
|
1900
|
+
const { completedJobs, userMessages } = args;
|
|
1901
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1902
|
+
userMessages.forEach((m, i) => {
|
|
1903
|
+
if (m.id) byId.set(m.id, i);
|
|
1904
|
+
});
|
|
1905
|
+
const linkOf = (j) => j.message_id ? byId.get(j.message_id) : void 0;
|
|
1906
|
+
const positional = (jobs, msgs) => jobs.map((job, i) => ({
|
|
1907
|
+
kind: "turn",
|
|
1908
|
+
jobId: job.job_id,
|
|
1909
|
+
content: msgs[i]?.content,
|
|
1910
|
+
messageId: msgs[i]?.id
|
|
1911
|
+
}));
|
|
1912
|
+
const firstLinked = completedJobs.findIndex((j) => linkOf(j) !== void 0);
|
|
1913
|
+
if (firstLinked === -1) {
|
|
1914
|
+
return positional(completedJobs, userMessages);
|
|
1915
|
+
}
|
|
1916
|
+
const cutover = linkOf(completedJobs[firstLinked]);
|
|
1917
|
+
const steps = positional(
|
|
1918
|
+
completedJobs.slice(0, firstLinked),
|
|
1919
|
+
userMessages.slice(0, cutover)
|
|
1920
|
+
);
|
|
1921
|
+
let cursor = cutover;
|
|
1922
|
+
const isSteer = (m) => !!m?.id && !completedJobs.some((j) => j.message_id === m.id);
|
|
1923
|
+
const drainTo = (stopAt) => {
|
|
1924
|
+
while (cursor < stopAt) {
|
|
1925
|
+
const m = userMessages[cursor++];
|
|
1926
|
+
if (isSteer(m)) {
|
|
1927
|
+
steps.push({ kind: "steer", content: m.content, messageId: m.id });
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
cursor = stopAt + 1;
|
|
1931
|
+
};
|
|
1932
|
+
for (const job of completedJobs.slice(firstLinked)) {
|
|
1933
|
+
const at = linkOf(job);
|
|
1934
|
+
if (at !== void 0) {
|
|
1935
|
+
drainTo(at);
|
|
1936
|
+
const prompt = userMessages[at];
|
|
1937
|
+
steps.push({
|
|
1938
|
+
kind: "turn",
|
|
1939
|
+
jobId: job.job_id,
|
|
1940
|
+
content: prompt.content,
|
|
1941
|
+
messageId: prompt.id
|
|
1942
|
+
});
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
steps.push({ kind: "turn", jobId: job.job_id });
|
|
1946
|
+
}
|
|
1947
|
+
for (let i = cursor; i < userMessages.length; i++) {
|
|
1948
|
+
const m = userMessages[i];
|
|
1949
|
+
if (isSteer(m)) {
|
|
1950
|
+
steps.push({ kind: "steer", content: m.content, messageId: m.id });
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
return steps;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1793
1956
|
// src/types.ts
|
|
1794
1957
|
var ChatEventType = {
|
|
1795
1958
|
// Connection lifecycle (SDK-local, not wire)
|
|
@@ -2058,16 +2221,40 @@ var StreamManager = class {
|
|
|
2058
2221
|
const userMessages = this.session.messages.filter(
|
|
2059
2222
|
(m) => m.role === "user"
|
|
2060
2223
|
);
|
|
2224
|
+
const plan = planRestore({
|
|
2225
|
+
completedJobs: completedJobs.map((j) => ({
|
|
2226
|
+
job_id: j.job_id,
|
|
2227
|
+
message_id: j.message_id
|
|
2228
|
+
})),
|
|
2229
|
+
userMessages: userMessages.map((m) => ({
|
|
2230
|
+
id: m.id,
|
|
2231
|
+
content: m.content
|
|
2232
|
+
}))
|
|
2233
|
+
});
|
|
2061
2234
|
const eventLists = await Promise.all(
|
|
2062
2235
|
completedJobs.map(
|
|
2063
2236
|
(job) => this.session.client.getConversationEvents(conversationId, job.job_id).catch(() => [])
|
|
2064
2237
|
)
|
|
2065
2238
|
);
|
|
2066
|
-
|
|
2239
|
+
const eventsByJobId = new Map(
|
|
2240
|
+
completedJobs.map((job, i) => [job.job_id, eventLists[i] ?? []])
|
|
2241
|
+
);
|
|
2242
|
+
for (const step of plan) {
|
|
2243
|
+
if (step.kind === "steer") {
|
|
2244
|
+
this.session.replayTurn(
|
|
2245
|
+
conversationId,
|
|
2246
|
+
[],
|
|
2247
|
+
step.content,
|
|
2248
|
+
step.messageId,
|
|
2249
|
+
true
|
|
2250
|
+
);
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2067
2253
|
this.session.replayTurn(
|
|
2068
2254
|
conversationId,
|
|
2069
|
-
|
|
2070
|
-
|
|
2255
|
+
eventsByJobId.get(step.jobId) ?? [],
|
|
2256
|
+
step.content,
|
|
2257
|
+
step.messageId
|
|
2071
2258
|
);
|
|
2072
2259
|
}
|
|
2073
2260
|
if (completedJobs.length > 0) {
|
|
@@ -2158,6 +2345,7 @@ export {
|
|
|
2158
2345
|
AstralformClient,
|
|
2159
2346
|
AstralformError,
|
|
2160
2347
|
AuthenticationError,
|
|
2348
|
+
CONVERSATION_PAGE_SIZE,
|
|
2161
2349
|
ChatEventType,
|
|
2162
2350
|
ChatSession,
|
|
2163
2351
|
ConnectionError,
|