@astralform/js 7.5.1 → 8.3.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.ts CHANGED
@@ -73,6 +73,15 @@ interface MemoryUpdatePayload {
73
73
  key?: string | null;
74
74
  namespace?: string | null;
75
75
  }
76
+ interface MemoryProviderErrorPayload {
77
+ /** The external memory provider that failed (registry slug, e.g. "mem0"). */
78
+ provider: string;
79
+ /** Which provider operation failed — "save" | "update" | "delete" | "get"
80
+ * | "recall" | "ingest" | "list_visible" | … Typed as string for forward compat. */
81
+ op: string;
82
+ /** One-line "<ExceptionType>: <message>" detail, length-capped by the backend. */
83
+ error: string;
84
+ }
76
85
  interface DesktopStreamPayload {
77
86
  url: string;
78
87
  sandboxId?: string | null;
@@ -117,6 +126,38 @@ interface ToolHarnessWarningPayload {
117
126
  message?: string | null;
118
127
  details?: Record<string, unknown> | null;
119
128
  }
129
+ interface ToolProgressPayload {
130
+ callId: string;
131
+ /** Known values: "stdout" | "stderr" | "progress" | "command". Typed as string
132
+ * for forward compat; the backend defaults to "progress". */
133
+ stream: string;
134
+ /** Live progress text to append; the backend newline-terminates chunks. */
135
+ chunk: string;
136
+ /** Emitting tool, e.g. "web_search" | "deep_research" | "generate_video".
137
+ * Wire key is `tool`; camelCase here, like the rest of this catalog. */
138
+ toolName?: string | null;
139
+ /** Structured metadata riding alongside `chunk` for a richer UI — a search
140
+ * result ({title,url,snippet}) or a research phase record. */
141
+ item?: Record<string, unknown> | null;
142
+ /** Position within `total`, when the producer emits one item per step. */
143
+ index?: number | null;
144
+ total?: number | null;
145
+ /** Producers splat arbitrary extra keys (`generate_video` sends `status`,
146
+ * `preset`), so this payload is open-ended by design. */
147
+ [key: string]: unknown;
148
+ }
149
+ interface NestedLlmUsagePayload {
150
+ /** Which tool made the nested calls, e.g. "deep_research". */
151
+ source: string;
152
+ /** The parent tool call these nested calls belong to. */
153
+ callId: string;
154
+ inputTokens: number;
155
+ outputTokens: number;
156
+ cachedTokens: number;
157
+ cacheCreationTokens: number;
158
+ /** Number of nested LLM calls this event aggregates. */
159
+ llmCalls: number;
160
+ }
120
161
  interface UserUnavailablePayload {
121
162
  consecutiveTimeouts: number;
122
163
  toolName?: string | null;
@@ -206,6 +247,7 @@ declare const ChatEventType: {
206
247
  readonly ContextWarning: "context_warning";
207
248
  readonly MemoryRecall: "memory_recall";
208
249
  readonly MemoryUpdate: "memory_update";
250
+ readonly MemoryProviderError: "memory_provider_error";
209
251
  readonly DesktopStream: "desktop_stream";
210
252
  readonly AttachmentStaged: "attachment_staged";
211
253
  readonly WorkspaceReady: "workspace_ready";
@@ -214,6 +256,8 @@ declare const ChatEventType: {
214
256
  readonly ToolApprovalGranted: "tool_approval_granted";
215
257
  readonly ToolPermissionDenied: "tool_permission_denied";
216
258
  readonly ToolHarnessWarning: "tool_harness_warning";
259
+ readonly ToolProgress: "tool_progress";
260
+ readonly NestedLlmUsage: "nested_llm_usage";
217
261
  readonly UserUnavailable: "user_unavailable";
218
262
  readonly PromptSuggestion: "prompt_suggestion";
219
263
  readonly StateChanged: "state_changed";
@@ -493,6 +537,15 @@ type ChatEvent = {
493
537
  memoryId?: string | null;
494
538
  key?: string | null;
495
539
  namespace?: string | null;
540
+ } | {
541
+ type: "memory_provider_error";
542
+ /** The external memory provider that failed (registry slug, e.g. "mem0"). */
543
+ provider: string;
544
+ /** Which provider operation failed — "save" | "update" | "delete" | "get"
545
+ * | "recall" | "ingest" | "list_visible" | … */
546
+ op: string;
547
+ /** One-line "<ExceptionType>: <message>" detail, length-capped by the backend. */
548
+ error: string;
496
549
  } | {
497
550
  type: "desktop_stream";
498
551
  url: string;
@@ -537,6 +590,43 @@ type ChatEvent = {
537
590
  callId: string;
538
591
  message?: string | null;
539
592
  details?: Record<string, unknown> | null;
593
+ } | {
594
+ type: "tool_progress";
595
+ callId: string;
596
+ /** Known values: "stdout" | "stderr" | "progress" | "command". Typed as
597
+ * string for forward compat; the backend defaults to "progress". */
598
+ stream: string;
599
+ /** Live progress text to append; the backend newline-terminates chunks. */
600
+ chunk: string;
601
+ /** Emitting tool, e.g. "web_search" | "deep_research" | "generate_video".
602
+ * Every producer sends it; null only if a future one does not. The wire
603
+ * key is `tool`, but this layer chooses consumer-facing names rather than
604
+ * inheriting them, and every sibling tool event calls it `toolName`. */
605
+ toolName?: string | null;
606
+ /** Structured metadata riding alongside `chunk` for a richer UI — a search
607
+ * result ({title,url,snippet}) or a research phase record. Null when the
608
+ * producer sends none. */
609
+ item?: Record<string, unknown> | null;
610
+ /** Position within `total`, when the producer emits one item per step. */
611
+ index?: number | null;
612
+ total?: number | null;
613
+ /** The raw payload, verbatim. Producers send keys beyond the named ones —
614
+ * `generate_video` splats arbitrary `**extra` (`status`, `preset`) — so
615
+ * the named fields are a convenience, not the whole event. Read here for
616
+ * anything they do not cover. */
617
+ data: Record<string, unknown>;
618
+ } | {
619
+ type: "nested_llm_usage";
620
+ /** Which tool made the nested calls, e.g. "deep_research". */
621
+ source: string;
622
+ /** The parent tool call these nested calls belong to. */
623
+ callId: string;
624
+ inputTokens: number;
625
+ outputTokens: number;
626
+ cachedTokens: number;
627
+ cacheCreationTokens: number;
628
+ /** Number of nested LLM calls this event aggregates. */
629
+ llmCalls: number;
540
630
  } | {
541
631
  type: "user_unavailable";
542
632
  consecutiveTimeouts: number;
@@ -625,8 +715,9 @@ interface AgentInfo {
625
715
  * Derived by the server from the connector, so it cannot go stale the way the
626
716
  * retired `mode` toggle could. It gates a SURFACE, not an ability: naming a
627
717
  * repository is optional on every task, and a task that names none is an
628
- * ordinary chat. Absent on Astralform older than 0.69.50 fall back to `mode`
629
- * there.
718
+ * ordinary chat. Absent on Astralform older than 0.69.50, where it should be
719
+ * read as false: those backends had an agent-level mode instead, and this
720
+ * SDK no longer carries the field that expressed it.
630
721
  *
631
722
  * It is a property of the WORKSPACE, not of a persona: `GET /v1/agents` selects
632
723
  * the workspace row itself and returns exactly one entry, so read
@@ -634,17 +725,6 @@ interface AgentInfo {
634
725
  * carry it, so a client learns this after opening an agent.
635
726
  */
636
727
  codeProjectsEnabled?: boolean;
637
- /**
638
- * @deprecated Removed in the next Astralform release. There is no agent mode —
639
- * a repository belongs to the TASK, so one agent answers general questions and
640
- * works in repositories from the same list. Read {@link codeProjectsEnabled}.
641
- *
642
- * Still reported for one release, as the STORED value of the retired column, so
643
- * clients built before the change keep behaving exactly as they did. Do not
644
- * treat it as an alias for `codeProjectsEnabled`: an agent that never had the
645
- * toggle set still reports `"chat"` while its tasks can bind perfectly well.
646
- */
647
- mode?: "chat" | "code";
648
728
  }
649
729
  interface TeamSummary {
650
730
  id: string;
@@ -1014,6 +1094,48 @@ interface ChatStreamEvent {
1014
1094
  event: string;
1015
1095
  data: string;
1016
1096
  }
1097
+ /**
1098
+ * A tool output the server declined to inline, with a handle to fetch it.
1099
+ *
1100
+ * Restore can ask for these with `toolOutputs: "stub"`. Only the `output` of a
1101
+ * `tool_use` final is ever replaced — name, arguments, status, duration and the
1102
+ * hoisted image previews all survive — so a stubbed tool call renders like a
1103
+ * resolved one until the reader opens it.
1104
+ */
1105
+ interface ToolOutputStub {
1106
+ __stub: "tool_output";
1107
+ call_id: string;
1108
+ /**
1109
+ * Size of the output this replaces, for a "load 240 KB" affordance.
1110
+ *
1111
+ * Optional because nothing in this SDK reads it, and the guard therefore
1112
+ * does not require it: rejecting an otherwise-valid stub over a display
1113
+ * field would drop it to "not a stub" and render the handle where the
1114
+ * output belongs — a worse outcome than a missing size label.
1115
+ */
1116
+ size_bytes?: number;
1117
+ /**
1118
+ * The job this call belongs to. **Hand it back to `getToolOutput`.**
1119
+ *
1120
+ * `/events?job_id=X` deliberately returns jobs that regeneration has
1121
+ * replaced — reading a superseded version is the whole purpose of that
1122
+ * parameter — while the fetch route excludes them unless scoped to a job. A
1123
+ * fetch that drops this therefore 404s on exactly the pills a
1124
+ * version-switched read is displaying. Absent only when the event carried no
1125
+ * job id, in which case the unscoped fetch is the correct one.
1126
+ */
1127
+ job_id?: string;
1128
+ }
1129
+ /**
1130
+ * Is this tool output a stub to fetch rather than the output itself?
1131
+ *
1132
+ * One obvious way to ask. Left to sniff `__stub` themselves, consumers get it
1133
+ * wrong in the direction that renders the stub object into the transcript
1134
+ * where the result belongs.
1135
+ */
1136
+ declare function isToolOutputStub(value: unknown): value is ToolOutputStub;
1137
+ /** How a restore asks for tool outputs. */
1138
+ type ToolOutputMode = "inline" | "stub";
1017
1139
  interface ConversationEvent {
1018
1140
  seq: number;
1019
1141
  event: string;
@@ -1158,6 +1280,32 @@ interface ConversationAsset {
1158
1280
  createdAt: string;
1159
1281
  }
1160
1282
 
1283
+ /**
1284
+ * One turn as the conversation's job list describes it.
1285
+ *
1286
+ * Deliberately WIDER than `restore-plan.ts`'s `RestoreJob`, which is the
1287
+ * narrow structural input `planRestore` needs. This is the wire shape, and
1288
+ * typing the page with the narrow one would drop `status` and `metrics` on
1289
+ * the floor — silently, since a narrower type is assignable.
1290
+ */
1291
+ interface ConversationJob {
1292
+ job_id: string;
1293
+ status: string;
1294
+ message_id?: string | null;
1295
+ metrics?: Record<string, unknown>;
1296
+ }
1297
+ /** One page of turns, plus where the next older page starts. */
1298
+ interface JobsPage {
1299
+ jobs: ConversationJob[];
1300
+ hasMore: boolean;
1301
+ nextBefore: string | null;
1302
+ }
1303
+ /** One page of messages, plus where the next older page starts. */
1304
+ interface MessagesPage {
1305
+ messages: Message[];
1306
+ hasMore: boolean;
1307
+ nextBeforeSeq: number | null;
1308
+ }
1161
1309
  declare class AstralformClient {
1162
1310
  private readonly baseURL;
1163
1311
  private readonly fetchFn;
@@ -1225,6 +1373,19 @@ declare class AstralformClient {
1225
1373
  private request;
1226
1374
  /** Fetch + status handling. Always called inside `withDeadline`. */
1227
1375
  private send;
1376
+ /**
1377
+ * A GET whose response HEADERS the caller needs, not only its body.
1378
+ *
1379
+ * Paging metadata rides on headers (`X-Has-More`, `X-Next-Before`) because
1380
+ * the bodies are bare lists that installed clients already parse as such.
1381
+ * Reading them needs the `Response`, which `get<T>` discards.
1382
+ *
1383
+ * Note the shape: the body is parsed INSIDE the raced callback, exactly as
1384
+ * `get`/`post`/`patch` do. See the comment above them — doing the parse
1385
+ * outside the deadline is the original hang, and this method is not an
1386
+ * exception to it.
1387
+ */
1388
+ private getWithHeaders;
1228
1389
  get<T>(path: string): Promise<T>;
1229
1390
  post<T>(path: string, body: unknown): Promise<T>;
1230
1391
  patch<T>(path: string, body: unknown): Promise<T>;
@@ -1247,6 +1408,38 @@ declare class AstralformClient {
1247
1408
  getConversations(limit?: number, offset?: number, options?: {
1248
1409
  repository?: string;
1249
1410
  }): Promise<Conversation[]>;
1411
+ /**
1412
+ * One page of a conversation's turns, newest-first window returned oldest-first.
1413
+ *
1414
+ * `hasMore` asks "are there OLDER turns beyond this page" — the only
1415
+ * direction a restore pages in, since it starts at the tail.
1416
+ *
1417
+ * **Degrades on an old backend.** A server without the cursor ignores the
1418
+ * unknown `limit` query param and returns the whole list, and its response
1419
+ * carries neither header — which reads here as one page with nothing older,
1420
+ * i.e. exactly today's behaviour. That is why absent headers must mean
1421
+ * `hasMore: false` rather than an error: the fallback has to be "everything
1422
+ * arrived", not "paging is broken".
1423
+ */
1424
+ getConversationJobsPage(conversationId: string, options?: {
1425
+ limit?: number;
1426
+ before?: string;
1427
+ }): Promise<JobsPage>;
1428
+ /**
1429
+ * One page of a conversation's messages, oldest-first within the page.
1430
+ *
1431
+ * Deliberately NOT folded into `getMessages`: that method is public surface
1432
+ * whose `Message[]` return type callers depend on, and the server keeps its
1433
+ * unbounded branch for exactly the same reason.
1434
+ *
1435
+ * Degrades like `getConversationJobsPage` — an old server ignores `limit`
1436
+ * and returns the whole branch with no headers, which reads as a single
1437
+ * complete page.
1438
+ */
1439
+ getMessagesPage(conversationId: string, options: {
1440
+ limit: number;
1441
+ beforeSeq?: number;
1442
+ }): Promise<MessagesPage>;
1250
1443
  getMessages(conversationId: string): Promise<Message[]>;
1251
1444
  /**
1252
1445
  * Replace the title the server generated from the conversation's first turn.
@@ -1274,7 +1467,29 @@ declare class AstralformClient {
1274
1467
  */
1275
1468
  getModels(): Promise<ModelOption[]>;
1276
1469
  getSkills(): Promise<SkillInfo[]>;
1277
- getConversationEvents(conversationId: string, jobId?: string): Promise<ConversationEvent[]>;
1470
+ getConversationEvents(conversationId: string, jobId?: string, options?: {
1471
+ toolOutputs?: ToolOutputMode;
1472
+ }): Promise<ConversationEvent[]>;
1473
+ /**
1474
+ * The full output behind a {@link ToolOutputStub}.
1475
+ *
1476
+ * The safe form: the stub carries its own `job_id`, so the scoping that a
1477
+ * version-switched read depends on cannot be dropped. See the id overload
1478
+ * below for what that scoping is and why omitting it 404s.
1479
+ */
1480
+ getToolOutput(conversationId: string, stub: ToolOutputStub): Promise<unknown>;
1481
+ /**
1482
+ * By ids. **Pass `jobId`** — this is the form that can get it wrong.
1483
+ *
1484
+ * `/events?job_id=X` deliberately returns jobs that regeneration has
1485
+ * replaced, since reading a superseded version is the whole purpose of that
1486
+ * parameter, while this route excludes them unless scoped to a job. Omit it
1487
+ * and the fetch 404s on exactly the pills a version-switched read is
1488
+ * displaying. Restore fetches events per job, so that is the normal path.
1489
+ *
1490
+ * Prefer the overload above, which takes the stub and cannot be got wrong.
1491
+ */
1492
+ getToolOutput(conversationId: string, callId: string, jobId?: string): Promise<unknown>;
1278
1493
  submitToolResult(request: ToolResultRequest): Promise<void>;
1279
1494
  submitToolApproval(request: ToolApprovalRequest): Promise<void>;
1280
1495
  /**
@@ -1482,6 +1697,19 @@ declare class ChatSession {
1482
1697
  * cheaper than adding a count query to every list call.
1483
1698
  */
1484
1699
  hasMoreConversations: boolean;
1700
+ /** True when the loaded window has OLDER turns behind it — the transcript's
1701
+ * analogue of `hasMoreConversations`, and what a scroll-to-top sentinel
1702
+ * gates on. False until a windowed load says otherwise, so a consumer that
1703
+ * never asks for a window never offers to page. */
1704
+ hasMoreTurns: boolean;
1705
+ /** Cursor for the next older MESSAGE page (`before_seq`), or null. */
1706
+ oldestMessageSeq: number | null;
1707
+ /** Cursor for the next older TURN page (`before`), or null. Set by the
1708
+ * restore that loaded the newest page; consumed by `loadEarlierTurns`. */
1709
+ oldestTurnCursor: string | null;
1710
+ /** Guards against a sentinel that re-fires while a page is still in flight
1711
+ * and stacks duplicate turns — the same guard `loadMoreConversations` uses. */
1712
+ isLoadingEarlierTurns: boolean;
1485
1713
  /** True while ``loadMoreConversations`` is in flight. */
1486
1714
  isLoadingConversations: boolean;
1487
1715
  messages: Message[];
@@ -1645,7 +1873,9 @@ declare class ChatSession {
1645
1873
  * Load conversation context (messages) without replaying events.
1646
1874
  * Used before reconnectToJob — SSE replay handles event replay.
1647
1875
  */
1648
- loadConversation(id: string): Promise<void>;
1876
+ loadConversation(id: string, options?: {
1877
+ limit?: number;
1878
+ }): Promise<void>;
1649
1879
  /**
1650
1880
  * Reconnect to a running job's SSE stream (e.g. after page reload).
1651
1881
  * Replays all events from the beginning and continues live.
@@ -1734,6 +1964,19 @@ declare class ChatSession {
1734
1964
  * tracking ids client-side cannot discover a row that moved into a region
1735
1965
  * already scanned.
1736
1966
  */
1967
+ /**
1968
+ * Put an older page of messages in FRONT of the loaded window.
1969
+ *
1970
+ * Pending optimistic sends stay at the tail. They are the newest thing in
1971
+ * the session by construction — a message this browser has posted and the
1972
+ * server has not confirmed — so sorting them in with a page of history
1973
+ * would move an unsent bubble into the middle of the transcript.
1974
+ *
1975
+ * Ids already present are dropped rather than duplicated: pages are cut on a
1976
+ * row sequence, but a turn landing mid-walk can still put one message in two
1977
+ * pages, and a doubled prompt is more visible than a missing one.
1978
+ */
1979
+ prependMessages(older: Message[]): void;
1737
1980
  loadMoreConversations(): Promise<Conversation[]>;
1738
1981
  /**
1739
1982
  * Rename a conversation, server first.
@@ -1854,6 +2097,42 @@ type StreamManagerEvent = {
1854
2097
  type: "stateChange";
1855
2098
  state: StreamState;
1856
2099
  conversationId: string | null;
2100
+ } | {
2101
+ /**
2102
+ * An older page of turns is about to be replayed.
2103
+ *
2104
+ * Everything between this and `historyPageEnd` belongs BEFORE what the
2105
+ * consumer already holds. The SDK says which turns and in what order;
2106
+ * where they go is the consumer's business, and deliberately so — block
2107
+ * placement is a rendering concern, and the one consumer that indexes
2108
+ * blocks by wire path cannot merge an older page into that index anyway,
2109
+ * because paths are allocated per job and collide across turns.
2110
+ */
2111
+ type: "historyPageStart";
2112
+ conversationId: string;
2113
+ position: "prepend";
2114
+ } | {
2115
+ type: "historyPageEnd";
2116
+ conversationId: string;
2117
+ position: "prepend";
2118
+ /** Whether anything older still remains after this page. */
2119
+ hasMore: boolean;
2120
+ /**
2121
+ * Did the whole page replay?
2122
+ *
2123
+ * `false` when a live turn took the view over mid-walk, or a turn
2124
+ * started. The conversation has NOT moved in either case, so the turns
2125
+ * already emitted are sitting in the live transcript and the id on this
2126
+ * event does not tell a consumer to drop them — while the cursor has
2127
+ * deliberately not advanced, so the very next page request replays the
2128
+ * same turns from the start.
2129
+ *
2130
+ * **A consumer must discard everything it buffered since
2131
+ * `historyPageStart` when this is `false`.** That is cheap by
2132
+ * construction: a page is replayed into an isolated view and merged only
2133
+ * at this event, precisely because block paths collide across turns.
2134
+ */
2135
+ complete: boolean;
1857
2136
  } | {
1858
2137
  type: "conversationChanged";
1859
2138
  conversationId: string | null;
@@ -1886,6 +2165,19 @@ type StreamManagerEvent = {
1886
2165
  type EventHandler = (event: StreamManagerEvent) => void;
1887
2166
  declare class StreamManager {
1888
2167
  private session;
2168
+ /** The oldest prompt drawn so far — where a prepended page's span ends. */
2169
+ private oldestDrawnMessageId;
2170
+ /**
2171
+ * Whether restore asks for tool outputs inline or as fetch handles.
2172
+ *
2173
+ * ONE setting, applied to both event waves — the newest page and every
2174
+ * `loadEarlierTurns` page. A consumer that got stubs only on scroll-up would
2175
+ * be worse off than one that got them nowhere: the pill would resolve itself
2176
+ * in the visible tail and need a fetch above the fold, for no stated reason.
2177
+ *
2178
+ * Defaults to inline, so this is inert until a consumer opts in.
2179
+ */
2180
+ private toolOutputs;
1889
2181
  private _state;
1890
2182
  private _activeConversationId;
1891
2183
  private _backgroundJobs;
@@ -1916,6 +2208,15 @@ declare class StreamManager {
1916
2208
  get activeConversationId(): string | null;
1917
2209
  get backgroundJobs(): ReadonlyMap<string, string>;
1918
2210
  on(handler: EventHandler): () => void;
2211
+ /**
2212
+ * Ask restore for stubbed tool outputs, resolved on demand.
2213
+ *
2214
+ * Only worth turning on by a consumer that can actually resolve a stub —
2215
+ * see `isToolOutputStub` and `client.getToolOutput`. One that cannot does
2216
+ * not render an empty result: it renders the stub OBJECT where the output
2217
+ * belongs, because that is what arrives in `final.output`.
2218
+ */
2219
+ setToolOutputMode(mode: ToolOutputMode): void;
1919
2220
  private emit;
1920
2221
  private setState;
1921
2222
  private attach;
@@ -2051,6 +2352,23 @@ declare class StreamManager {
2051
2352
  * has returned to where it started.
2052
2353
  */
2053
2354
  private turnStarted;
2355
+ /**
2356
+ * Re-issue the message load UNWINDOWED after a restore stopped before it
2357
+ * could write a turn cursor.
2358
+ *
2359
+ * The window is decided synchronously at restore entry, but "this restore
2360
+ * will page" is only settled once `replayHistory` clears its first abort
2361
+ * check. A send landing in between — nothing gates one during a restore —
2362
+ * stops the replay with the window already installed and the cursor never
2363
+ * written: a transcript truncated to one page that no pager can extend,
2364
+ * which is worse than the unbounded load this path did before windowing.
2365
+ * Reloading whole is that behaviour restored. Fire-and-forget: the load
2366
+ * token still makes a later switch win, and the pending-send reconciliation
2367
+ * inside `loadConversation` is what keeps the interrupting send's prompt.
2368
+ * Only called when the conversation has NOT moved — a superseding switch
2369
+ * announces and loads its own.
2370
+ */
2371
+ private reloadUnwindowed;
2054
2372
  /**
2055
2373
  * Replay a conversation's persisted history into the consumer's block view.
2056
2374
  *
@@ -2071,6 +2389,23 @@ declare class StreamManager {
2071
2389
  * what keeps a failed job list non-blocking exactly as it was when the fetch
2072
2390
  * lived here.
2073
2391
  */
2392
+ /**
2393
+ * Fetch and replay the next OLDER page of turns.
2394
+ *
2395
+ * The scroll-up half of tail-first restore: `restore` renders the newest
2396
+ * page and clears `restoring`, and this brings back what precedes it, on
2397
+ * demand. Full fidelity — the same per-job events wave the newest page uses,
2398
+ * just later — so a turn paged in here is byte-identical to the same turn
2399
+ * rendered live. That is the whole reason this defers the fetch rather than
2400
+ * rebuilding older turns from the message list, which persists no thinking
2401
+ * blocks and no custom events.
2402
+ *
2403
+ * Resolves to the number of turns emitted; 0 when there is nothing older,
2404
+ * a page is already in flight, or the view was taken over mid-fetch.
2405
+ */
2406
+ /** User prompts from a slice of the message window, in plan input shape. */
2407
+ private static userMessagesOf;
2408
+ loadEarlierTurns(conversationId: string): Promise<number>;
2074
2409
  private replayHistory;
2075
2410
  private setActiveConversation;
2076
2411
  }
@@ -2159,4 +2494,4 @@ declare function isEmbeddedResource(value: unknown): value is {
2159
2494
  */
2160
2495
  declare function parseEmbeddedResource(value: unknown): EmbeddedResource | null;
2161
2496
 
2162
- 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 AvailableRepositories, type AvailableRepository, type BlockDeltaPayload, CONVERSATION_PAGE_SIZE, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, type CodeProject, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EffortRung, 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 NoteUpdatePayload, type PlanUpdatePayload, 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 ThinkingDescriptor, type ThinkingRungOption, 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, VOICE_POLISH_MODES, type VoiceConfig, type VoiceLLMMode, type VoicePolishEvent, type VoicePolishMode, type VoicePolishRequest, type VoiceTranscribeOptions, type VoiceTranscript, 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, isVoiceLLMMode, isVoicePolishMode, mapSseToChat, parseEmbeddedResource, parseVoicePolishFrame, replayEvents, streamJobSSE, translateDelta };
2497
+ 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 AvailableRepositories, type AvailableRepository, type BlockDeltaPayload, CONVERSATION_PAGE_SIZE, type ChatEvent, ChatEventType, type ChatEventTypeValue, ChatSession, type ChatStorage, type ChatStreamEvent, type ChatStreamRequest, type CodeProject, ConnectionError, type ContextUpdatePayload, type ContextWarningPayload, type Conversation, type ConversationAsset, type ConversationEvent, type DesktopStreamPayload, type EffortRung, type EmbeddedResource, type FeedbackRequest, type FeedbackResponse, InMemoryStorage, type JobCreateResponse, type JobStatus, type JobSummary, LLMNotConfiguredError, type MemoryProviderErrorPayload, type MemoryRecallPayload, type MemoryRecord, type MemoryUpdatePayload, type Message, type ModelChoiceOptions, type ModelOption, type MyToolGrantsPage, type NestedLlmUsagePayload, type NoteUpdatePayload, type PlanUpdatePayload, 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 ThinkingDescriptor, type ThinkingRungOption, 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 ToolOutputMode, type ToolOutputStub, type ToolPermissionDeniedPayload, type ToolProgressPayload, ToolRegistry, type ToolResult, type ToolResultRequest, type TurnUsage, type UIComponentsConfig, type UserUnavailablePayload, VOICE_POLISH_MODES, type VoiceConfig, type VoiceLLMMode, type VoicePolishEvent, type VoicePolishMode, type VoicePolishRequest, type VoiceTranscribeOptions, type VoiceTranscript, 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, isToolOutputStub, isVoiceLLMMode, isVoicePolishMode, mapSseToChat, parseEmbeddedResource, parseVoicePolishFrame, replayEvents, streamJobSSE, translateDelta };