@astralform/js 4.3.0 → 4.5.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.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;
@@ -546,6 +552,12 @@ interface UIComponentsConfig {
546
552
  /** MIME type to match against embedded resources (e.g. "application/json+a2ui"). */
547
553
  mimeType: string | null;
548
554
  }
555
+ /** One capability the agent either has or does not, right now. */
556
+ interface AgentCapability {
557
+ /** Stable key, e.g. `"image"`, `"web"`, `"skills"`. */
558
+ key: string;
559
+ enabled: boolean;
560
+ }
549
561
  interface AgentStatus {
550
562
  isReady: boolean;
551
563
  llmConfigured: boolean;
@@ -553,6 +565,16 @@ interface AgentStatus {
553
565
  llmModel?: string;
554
566
  message: string;
555
567
  uiComponents: UIComponentsConfig;
568
+ /**
569
+ * What the agent can do right now, so a client can offer a capability only
570
+ * where it will actually work rather than failing on send.
571
+ *
572
+ * Empty when the backend does not report it (older servers) or when the agent
573
+ * is not ready — treat an ABSENT key as "not reported", never as disabled.
574
+ * Only capabilities with a real per-agent gate appear; always-on ones do not,
575
+ * because a constant tells a client nothing.
576
+ */
577
+ capabilities: AgentCapability[];
556
578
  }
557
579
  interface AgentInfo {
558
580
  name: string;
@@ -1139,7 +1161,7 @@ declare class ChatSession {
1139
1161
  * ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
1140
1162
  * so leading with the prompt keeps the turn in order.
1141
1163
  */
1142
- replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string): void;
1164
+ replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string, userMessageId?: string, isSteer?: boolean): void;
1143
1165
  /**
1144
1166
  * Load a conversation's messages and replay its persisted history.
1145
1167
  *
@@ -1236,26 +1258,6 @@ declare function generateId(): string;
1236
1258
  */
1237
1259
  declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<ChatStreamEvent>;
1238
1260
 
1239
- /**
1240
- * StreamManager — high-level conversation lifecycle coordinator.
1241
- *
1242
- * Sits on top of ChatSession and manages the state machine for
1243
- * multi-conversation SSE streaming. Framework-agnostic: emits
1244
- * typed events to registered handlers. Block construction is NOT
1245
- * the SDK's concern — consumers build their own block tree from
1246
- * the forwarded ``ChatEvent`` instances.
1247
- *
1248
- * import { ChatSession, StreamManager } from "@astralform/js";
1249
- * const session = new ChatSession({ ... });
1250
- * const manager = new StreamManager(session);
1251
- * manager.on((event) => {
1252
- * if (event.type === "event") {
1253
- * // event.event is a typed ChatEvent — dispatch to your reducer
1254
- * }
1255
- * });
1256
- * await manager.send("Hello");
1257
- */
1258
-
1259
1261
  type StreamState = "idle" | "streaming" | "restoring" | "detached";
1260
1262
  interface SendOptions extends ModelChoiceOptions {
1261
1263
  agentName?: string;
@@ -1418,4 +1420,4 @@ declare function isEmbeddedResource(value: unknown): value is {
1418
1420
  */
1419
1421
  declare function parseEmbeddedResource(value: unknown): EmbeddedResource | null;
1420
1422
 
1421
- 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 };
1423
+ export { type ActiveJob, type AgentCapability, 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;
@@ -546,6 +552,12 @@ interface UIComponentsConfig {
546
552
  /** MIME type to match against embedded resources (e.g. "application/json+a2ui"). */
547
553
  mimeType: string | null;
548
554
  }
555
+ /** One capability the agent either has or does not, right now. */
556
+ interface AgentCapability {
557
+ /** Stable key, e.g. `"image"`, `"web"`, `"skills"`. */
558
+ key: string;
559
+ enabled: boolean;
560
+ }
549
561
  interface AgentStatus {
550
562
  isReady: boolean;
551
563
  llmConfigured: boolean;
@@ -553,6 +565,16 @@ interface AgentStatus {
553
565
  llmModel?: string;
554
566
  message: string;
555
567
  uiComponents: UIComponentsConfig;
568
+ /**
569
+ * What the agent can do right now, so a client can offer a capability only
570
+ * where it will actually work rather than failing on send.
571
+ *
572
+ * Empty when the backend does not report it (older servers) or when the agent
573
+ * is not ready — treat an ABSENT key as "not reported", never as disabled.
574
+ * Only capabilities with a real per-agent gate appear; always-on ones do not,
575
+ * because a constant tells a client nothing.
576
+ */
577
+ capabilities: AgentCapability[];
556
578
  }
557
579
  interface AgentInfo {
558
580
  name: string;
@@ -1139,7 +1161,7 @@ declare class ChatSession {
1139
1161
  * ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
1140
1162
  * so leading with the prompt keeps the turn in order.
1141
1163
  */
1142
- replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string): void;
1164
+ replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string, userMessageId?: string, isSteer?: boolean): void;
1143
1165
  /**
1144
1166
  * Load a conversation's messages and replay its persisted history.
1145
1167
  *
@@ -1236,26 +1258,6 @@ declare function generateId(): string;
1236
1258
  */
1237
1259
  declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<ChatStreamEvent>;
1238
1260
 
1239
- /**
1240
- * StreamManager — high-level conversation lifecycle coordinator.
1241
- *
1242
- * Sits on top of ChatSession and manages the state machine for
1243
- * multi-conversation SSE streaming. Framework-agnostic: emits
1244
- * typed events to registered handlers. Block construction is NOT
1245
- * the SDK's concern — consumers build their own block tree from
1246
- * the forwarded ``ChatEvent`` instances.
1247
- *
1248
- * import { ChatSession, StreamManager } from "@astralform/js";
1249
- * const session = new ChatSession({ ... });
1250
- * const manager = new StreamManager(session);
1251
- * manager.on((event) => {
1252
- * if (event.type === "event") {
1253
- * // event.event is a typed ChatEvent — dispatch to your reducer
1254
- * }
1255
- * });
1256
- * await manager.send("Hello");
1257
- */
1258
-
1259
1261
  type StreamState = "idle" | "streaming" | "restoring" | "detached";
1260
1262
  interface SendOptions extends ModelChoiceOptions {
1261
1263
  agentName?: string;
@@ -1418,4 +1420,4 @@ declare function isEmbeddedResource(value: unknown): value is {
1418
1420
  */
1419
1421
  declare function parseEmbeddedResource(value: unknown): EmbeddedResource | null;
1420
1422
 
1421
- 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 };
1423
+ export { type ActiveJob, type AgentCapability, 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
@@ -491,6 +491,14 @@ var AstralformClient = class {
491
491
  llmProvider: raw.llm_provider,
492
492
  llmModel: raw.llm_model,
493
493
  message: raw.message,
494
+ // Defaults to [] rather than undefined so a caller can iterate without a
495
+ // guard, and so a server that predates the field behaves as "reports
496
+ // nothing" instead of throwing. Entries missing a key are dropped: a
497
+ // capability with no name cannot be matched against and would render as
498
+ // an unlabelled row.
499
+ capabilities: (raw.capabilities ?? []).flatMap(
500
+ (c) => typeof c?.key === "string" && c.key.length > 0 ? [{ key: c.key, enabled: Boolean(c.enabled) }] : []
501
+ ),
494
502
  uiComponents: {
495
503
  enabled: Boolean(ui.enabled),
496
504
  protocol: ui.protocol ?? null,
@@ -1773,11 +1781,16 @@ var ChatSession = class {
1773
1781
  * ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
1774
1782
  * so leading with the prompt keeps the turn in order.
1775
1783
  */
1776
- replayTurn(id, events, userMessageContent) {
1784
+ replayTurn(id, events, userMessageContent, userMessageId, isSteer = false) {
1777
1785
  this.conversationId = id;
1778
1786
  this.resetStreamingState();
1779
1787
  if (userMessageContent) {
1780
- this.emit({ type: "user_message", content: userMessageContent });
1788
+ this.emit({
1789
+ type: "user_message",
1790
+ content: userMessageContent,
1791
+ ...userMessageId ? { id: userMessageId } : {},
1792
+ ...isSteer ? { steer: true } : {}
1793
+ });
1781
1794
  }
1782
1795
  for (const ev of events) {
1783
1796
  const type = ev.data.type || ev.event;
@@ -1890,6 +1903,64 @@ var ChatSession = class {
1890
1903
  }
1891
1904
  };
1892
1905
 
1906
+ // src/restore-plan.ts
1907
+ function planRestore(args) {
1908
+ const { completedJobs, userMessages } = args;
1909
+ const byId = /* @__PURE__ */ new Map();
1910
+ userMessages.forEach((m, i) => {
1911
+ if (m.id) byId.set(m.id, i);
1912
+ });
1913
+ const linkOf = (j) => j.message_id ? byId.get(j.message_id) : void 0;
1914
+ const positional = (jobs, msgs) => jobs.map((job, i) => ({
1915
+ kind: "turn",
1916
+ jobId: job.job_id,
1917
+ content: msgs[i]?.content,
1918
+ messageId: msgs[i]?.id
1919
+ }));
1920
+ const firstLinked = completedJobs.findIndex((j) => linkOf(j) !== void 0);
1921
+ if (firstLinked === -1) {
1922
+ return positional(completedJobs, userMessages);
1923
+ }
1924
+ const cutover = linkOf(completedJobs[firstLinked]);
1925
+ const steps = positional(
1926
+ completedJobs.slice(0, firstLinked),
1927
+ userMessages.slice(0, cutover)
1928
+ );
1929
+ let cursor = cutover;
1930
+ const isSteer = (m) => !!m?.id && !completedJobs.some((j) => j.message_id === m.id);
1931
+ const drainTo = (stopAt) => {
1932
+ while (cursor < stopAt) {
1933
+ const m = userMessages[cursor++];
1934
+ if (isSteer(m)) {
1935
+ steps.push({ kind: "steer", content: m.content, messageId: m.id });
1936
+ }
1937
+ }
1938
+ cursor = stopAt + 1;
1939
+ };
1940
+ for (const job of completedJobs.slice(firstLinked)) {
1941
+ const at = linkOf(job);
1942
+ if (at !== void 0) {
1943
+ drainTo(at);
1944
+ const prompt = userMessages[at];
1945
+ steps.push({
1946
+ kind: "turn",
1947
+ jobId: job.job_id,
1948
+ content: prompt.content,
1949
+ messageId: prompt.id
1950
+ });
1951
+ continue;
1952
+ }
1953
+ steps.push({ kind: "turn", jobId: job.job_id });
1954
+ }
1955
+ for (let i = cursor; i < userMessages.length; i++) {
1956
+ const m = userMessages[i];
1957
+ if (isSteer(m)) {
1958
+ steps.push({ kind: "steer", content: m.content, messageId: m.id });
1959
+ }
1960
+ }
1961
+ return steps;
1962
+ }
1963
+
1893
1964
  // src/types.ts
1894
1965
  var ChatEventType = {
1895
1966
  // Connection lifecycle (SDK-local, not wire)
@@ -2158,16 +2229,40 @@ var StreamManager = class {
2158
2229
  const userMessages = this.session.messages.filter(
2159
2230
  (m) => m.role === "user"
2160
2231
  );
2232
+ const plan = planRestore({
2233
+ completedJobs: completedJobs.map((j) => ({
2234
+ job_id: j.job_id,
2235
+ message_id: j.message_id
2236
+ })),
2237
+ userMessages: userMessages.map((m) => ({
2238
+ id: m.id,
2239
+ content: m.content
2240
+ }))
2241
+ });
2161
2242
  const eventLists = await Promise.all(
2162
2243
  completedJobs.map(
2163
2244
  (job) => this.session.client.getConversationEvents(conversationId, job.job_id).catch(() => [])
2164
2245
  )
2165
2246
  );
2166
- for (let i = 0; i < completedJobs.length; i++) {
2247
+ const eventsByJobId = new Map(
2248
+ completedJobs.map((job, i) => [job.job_id, eventLists[i] ?? []])
2249
+ );
2250
+ for (const step of plan) {
2251
+ if (step.kind === "steer") {
2252
+ this.session.replayTurn(
2253
+ conversationId,
2254
+ [],
2255
+ step.content,
2256
+ step.messageId,
2257
+ true
2258
+ );
2259
+ continue;
2260
+ }
2167
2261
  this.session.replayTurn(
2168
2262
  conversationId,
2169
- eventLists[i] ?? [],
2170
- userMessages[i]?.content
2263
+ eventsByJobId.get(step.jobId) ?? [],
2264
+ step.content,
2265
+ step.messageId
2171
2266
  );
2172
2267
  }
2173
2268
  if (completedJobs.length > 0) {