@kortix/sdk 0.13.4 → 0.13.5

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.
Files changed (204) hide show
  1. package/dist/browser/cache/idb-sync-cache.d.ts +19 -1
  2. package/dist/browser/cache/idb-sync-cache.d.ts.map +1 -1
  3. package/dist/browser/cache/idb-sync-cache.js +39 -4
  4. package/dist/browser/cache/idb-sync-cache.js.map +1 -1
  5. package/dist/browser/session-sync/session-sync-registry.d.ts +20 -0
  6. package/dist/browser/session-sync/session-sync-registry.d.ts.map +1 -1
  7. package/dist/browser/session-sync/session-sync-registry.js +69 -6
  8. package/dist/browser/session-sync/session-sync-registry.js.map +1 -1
  9. package/dist/browser/session-sync/visibility.d.ts +19 -0
  10. package/dist/browser/session-sync/visibility.d.ts.map +1 -0
  11. package/dist/browser/session-sync/visibility.js +16 -0
  12. package/dist/browser/session-sync/visibility.js.map +1 -0
  13. package/dist/browser/stores/sandbox-connection-store.js +1 -1
  14. package/dist/browser/stores/sandbox-connection-store.js.map +1 -1
  15. package/dist/browser/stores/sync-store.d.ts +27 -1
  16. package/dist/browser/stores/sync-store.d.ts.map +1 -1
  17. package/dist/browser/stores/sync-store.js +198 -41
  18. package/dist/browser/stores/sync-store.js.map +1 -1
  19. package/dist/core/client/kortix.d.ts +0 -11
  20. package/dist/core/client/kortix.d.ts.map +1 -1
  21. package/dist/core/client/kortix.js +14 -8
  22. package/dist/core/client/kortix.js.map +1 -1
  23. package/dist/core/rest/projects-client/channels.d.ts +0 -3
  24. package/dist/core/rest/projects-client/channels.d.ts.map +1 -1
  25. package/dist/core/rest/projects-client/channels.js +0 -6
  26. package/dist/core/rest/projects-client/channels.js.map +1 -1
  27. package/dist/core/rest/projects-client/index.d.ts +0 -2
  28. package/dist/core/rest/projects-client/index.d.ts.map +1 -1
  29. package/dist/core/rest/projects-client/index.js +0 -2
  30. package/dist/core/rest/projects-client/index.js.map +1 -1
  31. package/dist/core/rest/projects-client/projects.d.ts +1 -1
  32. package/dist/core/rest/projects-client/projects.d.ts.map +1 -1
  33. package/dist/core/rest/projects-client/projects.js +5 -3
  34. package/dist/core/rest/projects-client/projects.js.map +1 -1
  35. package/dist/core/rest/projects-client/sessions.d.ts +8 -33
  36. package/dist/core/rest/projects-client/sessions.d.ts.map +1 -1
  37. package/dist/core/rest/projects-client/sessions.js +1 -18
  38. package/dist/core/rest/projects-client/sessions.js.map +1 -1
  39. package/dist/core/runtime/attachment-part.d.ts +26 -0
  40. package/dist/core/runtime/attachment-part.d.ts.map +1 -0
  41. package/dist/core/runtime/attachment-part.js +41 -0
  42. package/dist/core/runtime/attachment-part.js.map +1 -0
  43. package/dist/core/runtime/client.d.ts +4 -2
  44. package/dist/core/runtime/client.d.ts.map +1 -1
  45. package/dist/core/runtime/client.js +3 -2
  46. package/dist/core/runtime/client.js.map +1 -1
  47. package/dist/core/runtime/opencode-v2-event-aliases.d.ts +184 -0
  48. package/dist/core/runtime/opencode-v2-event-aliases.d.ts.map +1 -0
  49. package/dist/core/runtime/opencode-v2-event-aliases.js +2 -0
  50. package/dist/core/runtime/opencode-v2-event-aliases.js.map +1 -0
  51. package/dist/core/runtime/pty.d.ts +11 -1
  52. package/dist/core/runtime/pty.d.ts.map +1 -1
  53. package/dist/core/runtime/pty.js +19 -6
  54. package/dist/core/runtime/pty.js.map +1 -1
  55. package/dist/core/session/connection.d.ts +64 -0
  56. package/dist/core/session/connection.d.ts.map +1 -0
  57. package/dist/core/session/connection.js +36 -0
  58. package/dist/core/session/connection.js.map +1 -0
  59. package/dist/core/session/index.d.ts +1 -0
  60. package/dist/core/session/index.d.ts.map +1 -1
  61. package/dist/core/session/index.js +1 -0
  62. package/dist/core/session/index.js.map +1 -1
  63. package/dist/core/session/preview-auth.d.ts +25 -0
  64. package/dist/core/session/preview-auth.d.ts.map +1 -1
  65. package/dist/core/session/preview-auth.js +31 -1
  66. package/dist/core/session/preview-auth.js.map +1 -1
  67. package/dist/core/session/preview-config.d.ts +26 -0
  68. package/dist/core/session/preview-config.d.ts.map +1 -0
  69. package/dist/core/session/preview-config.js +95 -0
  70. package/dist/core/session/preview-config.js.map +1 -0
  71. package/dist/core/session/preview-options.d.ts +25 -0
  72. package/dist/core/session/preview-options.d.ts.map +1 -0
  73. package/dist/core/session/preview-options.js +45 -0
  74. package/dist/core/session/preview-options.js.map +1 -0
  75. package/dist/core/session/preview.d.ts +8 -3
  76. package/dist/core/session/preview.d.ts.map +1 -1
  77. package/dist/core/session/preview.js +11 -4
  78. package/dist/core/session/preview.js.map +1 -1
  79. package/dist/core/session/rewind.d.ts +67 -24
  80. package/dist/core/session/rewind.d.ts.map +1 -1
  81. package/dist/core/session/rewind.js +74 -26
  82. package/dist/core/session/rewind.js.map +1 -1
  83. package/dist/core/session/server-store/active.d.ts +10 -12
  84. package/dist/core/session/server-store/active.d.ts.map +1 -1
  85. package/dist/core/session/server-store/active.js +11 -9
  86. package/dist/core/session/server-store/active.js.map +1 -1
  87. package/dist/core/session/server-store/url-helpers.d.ts +11 -2
  88. package/dist/core/session/server-store/url-helpers.d.ts.map +1 -1
  89. package/dist/core/session/server-store/url-helpers.js +11 -2
  90. package/dist/core/session/server-store/url-helpers.js.map +1 -1
  91. package/dist/core/session/url.d.ts +25 -2
  92. package/dist/core/session/url.d.ts.map +1 -1
  93. package/dist/core/session/url.js +87 -11
  94. package/dist/core/session/url.js.map +1 -1
  95. package/dist/core/session/working.d.ts +44 -0
  96. package/dist/core/session/working.d.ts.map +1 -1
  97. package/dist/core/session/working.js +146 -4
  98. package/dist/core/session/working.js.map +1 -1
  99. package/dist/core/session-sync/durable-log.d.ts +96 -0
  100. package/dist/core/session-sync/durable-log.d.ts.map +1 -0
  101. package/dist/core/session-sync/durable-log.js +94 -0
  102. package/dist/core/session-sync/durable-log.js.map +1 -0
  103. package/dist/core/session-sync/fragment.d.ts +27 -0
  104. package/dist/core/session-sync/fragment.d.ts.map +1 -0
  105. package/dist/core/session-sync/fragment.js +24 -0
  106. package/dist/core/session-sync/fragment.js.map +1 -0
  107. package/dist/core/session-sync/session-sync-controller.d.ts +83 -9
  108. package/dist/core/session-sync/session-sync-controller.d.ts.map +1 -1
  109. package/dist/core/session-sync/session-sync-controller.js +183 -17
  110. package/dist/core/session-sync/session-sync-controller.js.map +1 -1
  111. package/dist/core/turns/grouping.d.ts.map +1 -1
  112. package/dist/core/turns/grouping.js +60 -23
  113. package/dist/core/turns/grouping.js.map +1 -1
  114. package/dist/index.d.ts +1 -0
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +1 -0
  117. package/dist/index.js.map +1 -1
  118. package/dist/kortix.esm.min.js +24 -24
  119. package/dist/kortix.global.js +24 -24
  120. package/dist/platform/auth-core.d.ts +13 -1
  121. package/dist/platform/auth-core.d.ts.map +1 -1
  122. package/dist/platform/auth-core.js +13 -1
  123. package/dist/platform/auth-core.js.map +1 -1
  124. package/dist/react/opencode.d.ts +1 -0
  125. package/dist/react/opencode.d.ts.map +1 -1
  126. package/dist/react/opencode.js +3 -0
  127. package/dist/react/opencode.js.map +1 -1
  128. package/dist/react/use-admin-accounts.d.ts +35 -0
  129. package/dist/react/use-admin-accounts.d.ts.map +1 -1
  130. package/dist/react/use-admin-accounts.js +18 -0
  131. package/dist/react/use-admin-accounts.js.map +1 -1
  132. package/dist/react/use-opencode-events/handle-event.d.ts.map +1 -1
  133. package/dist/react/use-opencode-events/handle-event.js +26 -0
  134. package/dist/react/use-opencode-events/handle-event.js.map +1 -1
  135. package/dist/react/use-opencode-events/index.d.ts.map +1 -1
  136. package/dist/react/use-opencode-events/index.js +59 -6
  137. package/dist/react/use-opencode-events/index.js.map +1 -1
  138. package/dist/react/use-opencode-events/rehydrate-targets.d.ts +18 -0
  139. package/dist/react/use-opencode-events/rehydrate-targets.d.ts.map +1 -0
  140. package/dist/react/use-opencode-events/rehydrate-targets.js +20 -0
  141. package/dist/react/use-opencode-events/rehydrate-targets.js.map +1 -0
  142. package/dist/react/use-opencode-events/stream-revival.d.ts +37 -0
  143. package/dist/react/use-opencode-events/stream-revival.d.ts.map +1 -0
  144. package/dist/react/use-opencode-events/stream-revival.js +53 -0
  145. package/dist/react/use-opencode-events/stream-revival.js.map +1 -0
  146. package/dist/react/use-opencode-pty.d.ts +7 -1
  147. package/dist/react/use-opencode-pty.d.ts.map +1 -1
  148. package/dist/react/use-opencode-pty.js +6 -2
  149. package/dist/react/use-opencode-pty.js.map +1 -1
  150. package/dist/react/use-opencode-sessions/agents.d.ts.map +1 -1
  151. package/dist/react/use-opencode-sessions/agents.js +13 -2
  152. package/dist/react/use-opencode-sessions/agents.js.map +1 -1
  153. package/dist/react/use-opencode-sessions/commands.d.ts +12 -0
  154. package/dist/react/use-opencode-sessions/commands.d.ts.map +1 -1
  155. package/dist/react/use-opencode-sessions/commands.js +15 -3
  156. package/dist/react/use-opencode-sessions/commands.js.map +1 -1
  157. package/dist/react/use-opencode-sessions/index.d.ts +1 -0
  158. package/dist/react/use-opencode-sessions/index.d.ts.map +1 -1
  159. package/dist/react/use-opencode-sessions/index.js +1 -0
  160. package/dist/react/use-opencode-sessions/index.js.map +1 -1
  161. package/dist/react/use-opencode-sessions/keys.d.ts +8 -0
  162. package/dist/react/use-opencode-sessions/keys.d.ts.map +1 -1
  163. package/dist/react/use-opencode-sessions/keys.js +8 -0
  164. package/dist/react/use-opencode-sessions/keys.js.map +1 -1
  165. package/dist/react/use-opencode-sessions/providers.d.ts.map +1 -1
  166. package/dist/react/use-opencode-sessions/providers.js +12 -2
  167. package/dist/react/use-opencode-sessions/providers.js.map +1 -1
  168. package/dist/react/use-opencode-sessions/shared.d.ts +23 -0
  169. package/dist/react/use-opencode-sessions/shared.d.ts.map +1 -1
  170. package/dist/react/use-opencode-sessions/shared.js +31 -0
  171. package/dist/react/use-opencode-sessions/shared.js.map +1 -1
  172. package/dist/react/use-opencode-sessions/vcs.d.ts +28 -0
  173. package/dist/react/use-opencode-sessions/vcs.d.ts.map +1 -0
  174. package/dist/react/use-opencode-sessions/vcs.js +42 -0
  175. package/dist/react/use-opencode-sessions/vcs.js.map +1 -0
  176. package/dist/react/use-session-prompts.d.ts +37 -2
  177. package/dist/react/use-session-prompts.d.ts.map +1 -1
  178. package/dist/react/use-session-prompts.js +62 -15
  179. package/dist/react/use-session-prompts.js.map +1 -1
  180. package/dist/react/use-session-send.d.ts +17 -0
  181. package/dist/react/use-session-send.d.ts.map +1 -1
  182. package/dist/react/use-session-send.js +40 -4
  183. package/dist/react/use-session-send.js.map +1 -1
  184. package/dist/react/use-session-sync.d.ts +13 -1
  185. package/dist/react/use-session-sync.d.ts.map +1 -1
  186. package/dist/react/use-session-sync.js +81 -55
  187. package/dist/react/use-session-sync.js.map +1 -1
  188. package/dist/react/use-session-working.d.ts +17 -0
  189. package/dist/react/use-session-working.d.ts.map +1 -1
  190. package/dist/react/use-session-working.js +33 -3
  191. package/dist/react/use-session-working.js.map +1 -1
  192. package/package.json +3 -3
  193. package/dist/browser/session-sync/session-transcript-cache.d.ts +0 -79
  194. package/dist/browser/session-sync/session-transcript-cache.d.ts.map +0 -1
  195. package/dist/browser/session-sync/session-transcript-cache.js +0 -91
  196. package/dist/browser/session-sync/session-transcript-cache.js.map +0 -1
  197. package/dist/core/rest/projects-client/public-voice-join.d.ts +0 -59
  198. package/dist/core/rest/projects-client/public-voice-join.d.ts.map +0 -1
  199. package/dist/core/rest/projects-client/public-voice-join.js +0 -64
  200. package/dist/core/rest/projects-client/public-voice-join.js.map +0 -1
  201. package/dist/core/rest/projects-client/transcription.d.ts +0 -5
  202. package/dist/core/rest/projects-client/transcription.d.ts.map +0 -1
  203. package/dist/core/rest/projects-client/transcription.js +0 -14
  204. package/dist/core/rest/projects-client/transcription.js.map +0 -1
@@ -1,15 +1,50 @@
1
1
  import type { Message, Part, SessionStatus } from '@opencode-ai/sdk/v2/client';
2
+ export declare const SESSION_SYNC_PAGE_SIZE = 100;
2
3
  /**
3
- * Messages per bounded read — the newest-first window a session opens with,
4
- * and the step size when the transcript pulls older history.
4
+ * The FIRST page — the one the user waits on.
5
5
  *
6
- * OpenCode carries tool output as *parts* of the assistant message, so a page
7
- * is roughly half as many turns as it is messages: 50 covers ~25 turns, which
8
- * is a whole ordinary session. That is the point most sessions now open
9
- * fully loaded and never pull at all, while a long one still opens bounded
10
- * instead of dragging its entire history over the sandbox proxy.
6
+ * Time to first paint is bytes, not messages. Measured on a heavy session
7
+ * (essentia, 2026-08-24, a run with hundreds of image reads whose parts carried
8
+ * base64 BEFORE the attachment bytes were stripped from the list):
9
+ *
10
+ * message?limit=50 -> 8,228 kB 30.39 s
11
+ * message?limit=50 -> 24,460 kB 48.76 s
12
+ * message?limit=50 -> 20,284 kB 35.74 s
13
+ * message?limit=50 -> 25,125 kB 29.23 s
14
+ *
15
+ * That was 165-500 kB PER MESSAGE, and it drove the first page down to 20.
16
+ * With the bytes gone (`stripInlineAttachmentBytes`, same session, same day)
17
+ * a message is ~2-7 kB: `limit=20` measured 132 kB, an older page of 50
18
+ * measured 345-420 kB. At that weight a small first page is a false economy —
19
+ * on a normal viewport 20 messages do not fill the screen, so the top sentinel
20
+ * pulled two more pages before the reader touched anything, and three round
21
+ * trips (each paying a CORS preflight, one measured at 3.34 s) replaced one.
22
+ *
23
+ * Fifty fills the view. Older pages go to a hundred: by then the reader is
24
+ * scrolling deliberately, a spinner is honest, and fewer round trips wins.
25
+ *
26
+ * NOTE this is not what fixed the blank transcript. That was structural:
27
+ * `hydrate` ran only after a multi-page backward walk, so NOTHING rendered at
28
+ * any page size — a smaller limit would have meant MORE sequential round trips
29
+ * before that single paint, and a longer blank. The walk is gone (see
30
+ * `loadTail`); this only makes the first paint lighter.
31
+ */
32
+ export declare const SESSION_SYNC_TAIL_PAGE_SIZE = 50;
33
+ /**
34
+ * How far back the tail read will walk to complete a turn before it stops and
35
+ * leaves the rest to "load older".
36
+ *
37
+ * The walk exists so an assistant message is not rendered above its own prompt.
38
+ * It had no ceiling, and a session whose last turn is thousands of messages —
39
+ * an agent run with hundreds of tool calls — therefore paged the WHOLE session
40
+ * 50 at a time, serially, through the sandbox proxy, before painting anything.
41
+ * The read returns 200 the entire time, which is why this looked like a bug in
42
+ * rendering rather than in loading.
43
+ *
44
+ * 10 pages = 500 messages, far past any honest turn, and the cursor survives so
45
+ * nothing becomes unreachable.
11
46
  */
12
- export declare const SESSION_SYNC_PAGE_SIZE = 50;
47
+ export declare const MAX_TURN_BACKFILL_PAGES = 10;
13
48
  export type SessionSyncFreshness = 'idle' | 'loading' | 'fresh' | 'stale' | 'error';
14
49
  export interface SessionSyncMessage {
15
50
  info: Message;
@@ -43,7 +78,16 @@ export interface SessionSyncScheduler {
43
78
  setTimeout?: (handler: () => void, timeoutMs: number) => unknown;
44
79
  clearTimeout?: (handle: unknown) => void;
45
80
  }
46
- export type SessionSyncReason = 'initial' | 'poll' | 'sse-gap' | 'compaction' | 'session-error' | 'send-recovery' | 'manual';
81
+ export type SessionSyncReason = 'initial' | 'poll' | 'sse-gap' | 'compaction' | 'session-error' | 'send-recovery'
82
+ /** The turn ended. The last read of a busy session, and the one that catches
83
+ * a final answer whose closing frames the stream never delivered. */
84
+ | 'turn-end'
85
+ /** The tab became visible again. A backgrounded tab is throttled, not
86
+ * notified, so return is a moment to re-read rather than to assume. */
87
+ | 'visible'
88
+ /** The transcript was evicted while detached and the live stream refilled it
89
+ * from the middle — see `transcriptIsFragment`. */
90
+ | 'eviction' | 'manual';
47
91
  export interface SessionSyncTelemetryEvent {
48
92
  operation: 'tail' | 'older';
49
93
  reason: SessionSyncReason;
@@ -89,6 +133,21 @@ type HttpSessionSyncSourceOptions = Pick<HttpSessionSyncControllerOptions, 'base
89
133
  export declare function createHttpSessionSyncPageLoader(options: HttpSessionSyncSourceOptions): SessionSyncControllerOptions['loadPage'];
90
134
  export declare function loadHttpSessionHistory(options: HttpSessionSyncSourceOptions): Promise<SessionSyncMessage[]>;
91
135
  export declare function createHttpSessionSyncController(options: HttpSessionSyncControllerOptions): SessionSyncController;
136
+ /**
137
+ * Every message in the session, oldest first.
138
+ *
139
+ * The order is the SERVER's: `MessageV2.page()` orders by `time_created`, so
140
+ * each page arrives already ordered and is concatenated untouched. Paging
141
+ * walks BACKWARDS (page 1 is the newest tail, `before` steps into history), so
142
+ * the transcript is the pages in reverse fetch order.
143
+ *
144
+ * This used to re-sort the union by `info.id.localeCompare(...)`. That was
145
+ * wrong twice over: message ids no longer ascend with time at all (OpenCode
146
+ * 1.18.15 retired that invariant — turn exit reads
147
+ * `lastAssistant.parentID === lastUser.id`, and `MessageV2.latest()` orders by
148
+ * `time.created`), and `localeCompare` is not byte order, so it disagreed with
149
+ * every other id comparison in the product on the same data.
150
+ */
92
151
  export declare function loadCompleteSessionHistory(loadPage: SessionSyncControllerOptions['loadPage']): Promise<SessionSyncMessage[]>;
93
152
  /**
94
153
  * Owns bounded session history synchronization without depending on React,
@@ -98,6 +157,9 @@ export declare class SessionSyncController {
98
157
  private readonly options;
99
158
  private readonly scheduler;
100
159
  private readonly livenessIntervalMs;
160
+ /** Consecutive failed tail reads, for the retry backoff. Reset by success. */
161
+ private retryAttempt;
162
+ private tailRetryTimer;
101
163
  private snapshot;
102
164
  private nextCursor;
103
165
  private knownUserMessageIds;
@@ -126,6 +188,18 @@ export declare class SessionSyncController {
126
188
  setBusy(isBusy: boolean): void;
127
189
  destroy(): void;
128
190
  private loadTail;
191
+ /**
192
+ * Come back for a read that lost.
193
+ *
194
+ * Without this a session got exactly ONE chance to load, and whether it took
195
+ * it depended on a race with the sandbox's boot. Losing that race left the
196
+ * page on "Waking the agent" with no exit but the health probe — the least
197
+ * reliable signal in the system — or a manual reload.
198
+ *
199
+ * Backoff so a genuinely dead box costs little, capped so a box that comes
200
+ * back is picked up promptly.
201
+ */
202
+ private scheduleTailRetry;
129
203
  private loadPage;
130
204
  private loadCompleteOlderTurn;
131
205
  private loadCompleteTurn;
@@ -1 +1 @@
1
- {"version":3,"file":"session-sync-controller.d.ts","sourceRoot":"","sources":["../../../src/core/session-sync/session-sync-controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-sync-controller.d.ts","sourceRoot":"","sources":["../../../src/core/session-sync/session-sync-controller.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,56 @@
8
8
  * fully loaded and never pull at all, while a long one still opens bounded
9
9
  * instead of dragging its entire history over the sandbox proxy.
10
10
  */
11
- export const SESSION_SYNC_PAGE_SIZE = 50;
11
+ /** First retry delay after a failed tail read. */
12
+ const TAIL_RETRY_BASE_MS = 1_000;
13
+ /** Ceiling for the retry backoff — a box that comes back is picked up within it. */
14
+ const TAIL_RETRY_MAX_MS = 15_000;
15
+ export const SESSION_SYNC_PAGE_SIZE = 100;
16
+ /**
17
+ * The FIRST page — the one the user waits on.
18
+ *
19
+ * Time to first paint is bytes, not messages. Measured on a heavy session
20
+ * (essentia, 2026-08-24, a run with hundreds of image reads whose parts carried
21
+ * base64 — BEFORE the attachment bytes were stripped from the list):
22
+ *
23
+ * message?limit=50 -> 8,228 kB 30.39 s
24
+ * message?limit=50 -> 24,460 kB 48.76 s
25
+ * message?limit=50 -> 20,284 kB 35.74 s
26
+ * message?limit=50 -> 25,125 kB 29.23 s
27
+ *
28
+ * That was 165-500 kB PER MESSAGE, and it drove the first page down to 20.
29
+ * With the bytes gone (`stripInlineAttachmentBytes`, same session, same day)
30
+ * a message is ~2-7 kB: `limit=20` measured 132 kB, an older page of 50
31
+ * measured 345-420 kB. At that weight a small first page is a false economy —
32
+ * on a normal viewport 20 messages do not fill the screen, so the top sentinel
33
+ * pulled two more pages before the reader touched anything, and three round
34
+ * trips (each paying a CORS preflight, one measured at 3.34 s) replaced one.
35
+ *
36
+ * Fifty fills the view. Older pages go to a hundred: by then the reader is
37
+ * scrolling deliberately, a spinner is honest, and fewer round trips wins.
38
+ *
39
+ * NOTE this is not what fixed the blank transcript. That was structural:
40
+ * `hydrate` ran only after a multi-page backward walk, so NOTHING rendered at
41
+ * any page size — a smaller limit would have meant MORE sequential round trips
42
+ * before that single paint, and a longer blank. The walk is gone (see
43
+ * `loadTail`); this only makes the first paint lighter.
44
+ */
45
+ export const SESSION_SYNC_TAIL_PAGE_SIZE = 50;
46
+ /**
47
+ * How far back the tail read will walk to complete a turn before it stops and
48
+ * leaves the rest to "load older".
49
+ *
50
+ * The walk exists so an assistant message is not rendered above its own prompt.
51
+ * It had no ceiling, and a session whose last turn is thousands of messages —
52
+ * an agent run with hundreds of tool calls — therefore paged the WHOLE session
53
+ * 50 at a time, serially, through the sandbox proxy, before painting anything.
54
+ * The read returns 200 the entire time, which is why this looked like a bug in
55
+ * rendering rather than in loading.
56
+ *
57
+ * 10 pages = 500 messages, far past any honest turn, and the cursor survives so
58
+ * nothing becomes unreachable.
59
+ */
60
+ export const MAX_TURN_BACKFILL_PAGES = 10;
12
61
  export function createHttpSessionSyncPageLoader(options) {
13
62
  const fetchImpl = options.fetch ?? globalThis.fetch;
14
63
  const baseUrl = options.baseUrl.replace(/\/$/, '');
@@ -39,8 +88,23 @@ export function createHttpSessionSyncController(options) {
39
88
  loadPage: createHttpSessionSyncPageLoader(options),
40
89
  });
41
90
  }
91
+ /**
92
+ * Every message in the session, oldest first.
93
+ *
94
+ * The order is the SERVER's: `MessageV2.page()` orders by `time_created`, so
95
+ * each page arrives already ordered and is concatenated untouched. Paging
96
+ * walks BACKWARDS (page 1 is the newest tail, `before` steps into history), so
97
+ * the transcript is the pages in reverse fetch order.
98
+ *
99
+ * This used to re-sort the union by `info.id.localeCompare(...)`. That was
100
+ * wrong twice over: message ids no longer ascend with time at all (OpenCode
101
+ * 1.18.15 retired that invariant — turn exit reads
102
+ * `lastAssistant.parentID === lastUser.id`, and `MessageV2.latest()` orders by
103
+ * `time.created`), and `localeCompare` is not byte order, so it disagreed with
104
+ * every other id comparison in the product on the same data.
105
+ */
42
106
  export async function loadCompleteSessionHistory(loadPage) {
43
- const messages = new Map();
107
+ const pages = [];
44
108
  const cursors = new Set();
45
109
  let before;
46
110
  do {
@@ -48,9 +112,7 @@ export async function loadCompleteSessionHistory(loadPage) {
48
112
  limit: SESSION_SYNC_PAGE_SIZE,
49
113
  ...(before ? { before } : {}),
50
114
  });
51
- for (const message of page.messages) {
52
- messages.set(message.info.id, message);
53
- }
115
+ pages.push(page.messages);
54
116
  before = page.nextCursor;
55
117
  if (before && cursors.has(before)) {
56
118
  throw new Error(`Session history cursor repeated: ${before}`);
@@ -58,7 +120,20 @@ export async function loadCompleteSessionHistory(loadPage) {
58
120
  if (before)
59
121
  cursors.add(before);
60
122
  } while (before);
61
- return [...messages.values()].sort((a, b) => a.info.id.localeCompare(b.info.id));
123
+ // Oldest page last out of the loop, so walk the pages backwards. A message
124
+ // repeated across two overlapping pages keeps the OLDER page's copy and its
125
+ // older position — the same copy the id-keyed Map used to end up holding.
126
+ const ordered = [];
127
+ const seen = new Set();
128
+ for (let index = pages.length - 1; index >= 0; index--) {
129
+ for (const message of pages[index]) {
130
+ if (seen.has(message.info.id))
131
+ continue;
132
+ seen.add(message.info.id);
133
+ ordered.push(message);
134
+ }
135
+ }
136
+ return ordered;
62
137
  }
63
138
  const defaultScheduler = {
64
139
  now: Date.now,
@@ -75,6 +150,9 @@ export class SessionSyncController {
75
150
  options;
76
151
  scheduler;
77
152
  livenessIntervalMs;
153
+ /** Consecutive failed tail reads, for the retry backoff. Reset by success. */
154
+ retryAttempt = 0;
155
+ tailRetryTimer;
78
156
  snapshot = {
79
157
  freshness: 'idle',
80
158
  hasOlder: false,
@@ -156,7 +234,21 @@ export class SessionSyncController {
156
234
  */
157
235
  setBusy(isBusy) {
158
236
  if (!isBusy) {
237
+ // The turn is over — and that is exactly when the transcript is most
238
+ // likely to be short. A stream that dropped its last frames leaves the
239
+ // browser holding a truncated answer while the runtime holds the whole
240
+ // one, and stopping the poll here used to make that state PERMANENT:
241
+ // nothing read the tail again until the session was reopened. Reported
242
+ // from a live self-host (2026-08-24): an 8m13s turn finished in the
243
+ // runtime's own terminal while the tab still showed a spinner under a
244
+ // half-written answer.
245
+ //
246
+ // One bounded read, only for a session that was actually busy, so an
247
+ // idle session churns nothing.
248
+ const wasBusy = this.livenessTimer !== undefined;
159
249
  this.stopLivenessTimer();
250
+ if (wasBusy && !this.destroyed)
251
+ void this.reconcile('turn-end');
160
252
  return;
161
253
  }
162
254
  if (this.livenessTimer !== undefined)
@@ -165,14 +257,43 @@ export class SessionSyncController {
165
257
  this.livenessTimer = this.scheduler.setInterval(() => void this.checkLiveness(), this.livenessIntervalMs);
166
258
  }
167
259
  destroy() {
260
+ // `destroyed` FIRST: `stopLivenessTimer` is also reached through
261
+ // `setBusy(false)`, which now fires a turn-end read, and a controller being
262
+ // torn down must not start a request it can never hydrate.
168
263
  this.destroyed = true;
169
264
  this.stopLivenessTimer();
265
+ if (this.tailRetryTimer !== undefined) {
266
+ this.cancelTimer(this.tailRetryTimer);
267
+ this.tailRetryTimer = undefined;
268
+ }
170
269
  this.listeners.clear();
171
270
  }
172
271
  async loadTail(reason) {
173
272
  try {
174
- const firstPage = await this.loadPage('tail', reason);
175
- const page = await this.loadCompleteTurn(firstPage, 'tail', reason);
273
+ // ONE PAGE. Render it. This is what OpenCode's own client does, and the
274
+ // reason we now do it too is measured:
275
+ //
276
+ // message?limit=50 200 8,228 kB 30.39 s
277
+ // message?limit=50 200 24,460 kB 48.76 s
278
+ // message?limit=50&before=.. 200 20,284 kB 35.74 s
279
+ // message?limit=50&before=.. 200 25,125 kB 29.23 s
280
+ // -> 78,097 kB transferred, finish 3.8 min, NOTHING on screen
281
+ //
282
+ // (essentia, 2026-08-24, a run with hundreds of image reads: fifty
283
+ // messages weigh 8-25 MB because the parts carry the image bytes.)
284
+ //
285
+ // There used to be a backward WALK here that kept fetching pages until
286
+ // every assistant message had its parent user message in hand, so an
287
+ // assistant reply could never render above its own prompt — and `hydrate`
288
+ // ran only after the walk finished. On a long turn that walk is the whole
289
+ // session, serially, through the sandbox proxy, with an empty thread the
290
+ // entire time. A cosmetic guarantee at the top edge of the window is not
291
+ // worth minutes of blank screen.
292
+ //
293
+ // The window may therefore start on an assistant message whose prompt is
294
+ // one page up. That is exactly what OpenCode shows, and `loadOlder` —
295
+ // which the user drives — still completes the turn when they scroll.
296
+ const page = await this.loadPage('tail', reason);
176
297
  if (this.destroyed)
177
298
  return;
178
299
  this.rememberUserMessages(page.messages);
@@ -181,22 +302,57 @@ export class SessionSyncController {
181
302
  this.setCursor(page.nextCursor);
182
303
  }
183
304
  this.update({ freshness: 'fresh' });
305
+ // SUCCESS ONLY. `markLoaded` used to sit in a `finally`, so a read that
306
+ // FAILED still told the store this session was loaded — and the store's
307
+ // implementation of that plants an empty message list. A first read that
308
+ // lost to a waking box, a 503 from the proxy, or a flapping probe was
309
+ // therefore RECORDED as "this session has no messages", and the UI
310
+ // painted an empty conversation over a session that had plenty. Nothing
311
+ // came back for it either: the mount had already run, and the liveness
312
+ // poll only turns on while a session is working.
313
+ //
314
+ // A successful read of zero messages still marks loaded — that is a fact
315
+ // about the session. A failed read is not a fact about anything.
316
+ this.options.markLoaded();
317
+ this.retryAttempt = 0;
184
318
  }
185
319
  catch {
186
- if (!this.destroyed) {
187
- this.update({ freshness: 'error' });
188
- }
189
- }
190
- finally {
191
- if (!this.destroyed)
192
- this.options.markLoaded();
320
+ if (this.destroyed)
321
+ return;
322
+ this.update({ freshness: 'error' });
323
+ this.scheduleTailRetry(reason);
193
324
  }
194
325
  }
326
+ /**
327
+ * Come back for a read that lost.
328
+ *
329
+ * Without this a session got exactly ONE chance to load, and whether it took
330
+ * it depended on a race with the sandbox's boot. Losing that race left the
331
+ * page on "Waking the agent" with no exit but the health probe — the least
332
+ * reliable signal in the system — or a manual reload.
333
+ *
334
+ * Backoff so a genuinely dead box costs little, capped so a box that comes
335
+ * back is picked up promptly.
336
+ */
337
+ scheduleTailRetry(reason) {
338
+ if (this.destroyed || this.tailRetryTimer !== undefined)
339
+ return;
340
+ const delay = Math.min(TAIL_RETRY_BASE_MS * 2 ** this.retryAttempt, TAIL_RETRY_MAX_MS);
341
+ this.retryAttempt += 1;
342
+ this.tailRetryTimer = this.startTimer(() => {
343
+ this.tailRetryTimer = undefined;
344
+ if (this.destroyed)
345
+ return;
346
+ void this.reconcile(reason);
347
+ }, delay);
348
+ }
195
349
  async loadPage(operation, reason, before) {
196
350
  const startedAt = this.scheduler.now();
197
351
  try {
198
352
  const page = await this.options.loadPage({
199
- limit: SESSION_SYNC_PAGE_SIZE,
353
+ // The tail is what someone is waiting for; an older page is what they
354
+ // asked for. Different budgets — see SESSION_SYNC_TAIL_PAGE_SIZE.
355
+ limit: operation === 'tail' ? SESSION_SYNC_TAIL_PAGE_SIZE : SESSION_SYNC_PAGE_SIZE,
200
356
  ...(before ? { before } : {}),
201
357
  });
202
358
  this.options.onTelemetry?.({
@@ -223,17 +379,21 @@ export class SessionSyncController {
223
379
  const firstPage = await this.loadPage('older', 'manual', before);
224
380
  return this.loadCompleteTurn(firstPage, 'older', 'manual', before);
225
381
  }
226
- async loadCompleteTurn(firstPage, operation, reason, initialCursor) {
382
+ async loadCompleteTurn(firstPage, operation, reason, initialCursor, onPage) {
227
383
  const messages = [...firstPage.messages];
228
384
  const knownUserMessageIds = new Set(this.knownUserMessageIds);
229
385
  const seenCursors = new Set(initialCursor ? [initialCursor] : []);
230
386
  let cursor = firstPage.nextCursor;
387
+ let pagesRead = 0;
231
388
  for (const message of firstPage.messages) {
232
389
  if (message.info.role === 'user') {
233
390
  knownUserMessageIds.add(message.info.id);
234
391
  }
235
392
  }
236
393
  while (cursor &&
394
+ // BOUNDED. Without a ceiling a turn of a few thousand messages paged the
395
+ // whole session before anything rendered — see MAX_TURN_BACKFILL_PAGES.
396
+ pagesRead < MAX_TURN_BACKFILL_PAGES &&
237
397
  messages.some((message) => message.info.role === 'assistant' &&
238
398
  Boolean(message.info.parentID) &&
239
399
  !knownUserMessageIds.has(message.info.parentID))) {
@@ -242,6 +402,9 @@ export class SessionSyncController {
242
402
  }
243
403
  seenCursors.add(cursor);
244
404
  const page = await this.loadPage(operation, reason, cursor);
405
+ if (this.destroyed)
406
+ return { messages, nextCursor: cursor };
407
+ pagesRead += 1;
245
408
  messages.unshift(...page.messages);
246
409
  for (const message of page.messages) {
247
410
  if (message.info.role === 'user') {
@@ -249,6 +412,9 @@ export class SessionSyncController {
249
412
  }
250
413
  }
251
414
  cursor = page.nextCursor;
415
+ // Repaint as each page lands, so a long turn fills in front of the user
416
+ // instead of withholding everything until the walk ends.
417
+ onPage?.([...messages]);
252
418
  }
253
419
  return { messages, nextCursor: cursor };
254
420
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session-sync-controller.js","sourceRoot":"","sources":["../../../src/core/session-sync/session-sync-controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-sync-controller.js","sourceRoot":"","sources":["../../../src/core/session-sync/session-sync-controller.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"grouping.d.ts","sourceRoot":"","sources":["../../../src/core/turns/grouping.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,eAAe,EAAgB,QAAQ,EAAE,MAAM,SAAS,CAAC;AAyCvG,wBAAgB,yBAAyB,CACvC,CAAC,EAAE;IAAE,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,EACxD,CAAC,EAAE;IAAE,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GACvD,MAAM,CAaR;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,oBAAoB,EACnE,KAAK,EAAE,SAAS,CAAC,EAAE,GAClB,QAAQ,CAAC,CAAC,CAAC,EAAE,CAgEf;AAMD,0DAA0D;AAC1D,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,oBAAoB,EAC7D,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAChB,eAAe,CAAC,CAAC,CAAC,EAAE,CAQtB;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,QAAQ,EACjD,KAAK,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,GAChC,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAQpC;AAED,sCAAsC;AACtC,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC,GAAG,OAAO,CAQ9E;AAMD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,oBAAoB,EACrE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EACjB,cAAc,EAAE,OAAO,EACvB,iBAAiB,EAAE,OAAO,GACzB,eAAe,CAAC,CAAC,CAAC,EAAE,CAgBtB;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,aAAa,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACzD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAYvB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,IAGpC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAC9D,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,YAWjE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAS7F"}
1
+ {"version":3,"file":"grouping.d.ts","sourceRoot":"","sources":["../../../src/core/turns/grouping.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,eAAe,EAAgB,QAAQ,EAAE,MAAM,SAAS,CAAC;AAuEvG,wBAAgB,yBAAyB,CACvC,CAAC,EAAE;IAAE,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,EACxD,CAAC,EAAE;IAAE,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GACvD,MAAM,CAsBR;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,oBAAoB,EACnE,KAAK,EAAE,SAAS,CAAC,EAAE,GAClB,QAAQ,CAAC,CAAC,CAAC,EAAE,CAgEf;AAMD,0DAA0D;AAC1D,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,oBAAoB,EAC7D,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAChB,eAAe,CAAC,CAAC,CAAC,EAAE,CAQtB;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,QAAQ,EACjD,KAAK,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,GAChC,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAQpC;AAED,sCAAsC;AACtC,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC,GAAG,OAAO,CAQ9E;AAMD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,oBAAoB,EACrE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EACjB,cAAc,EAAE,OAAO,EACvB,iBAAiB,EAAE,OAAO,GACzB,eAAe,CAAC,CAAC,CAAC,EAAE,CAgBtB;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,aAAa,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACzD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAYvB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,IAGpC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAC9D,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,YAWjE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAS7F"}
@@ -12,36 +12,73 @@ import { isTextPart, isToolPart } from './parts.js';
12
12
  // Turn grouping
13
13
  // ============================================================================
14
14
  /**
15
- * Display order: WIRE ID first for two well-formed wire ids, `time.created`
16
- * otherwise, untimed messages (optimistic stubs) last.
15
+ * Display order, as a TOTAL order over two disjoint segments.
17
16
  *
18
- * The wire id is the message's POSITION: OpenCode's own loop resolves "is
19
- * this answered?" by id order, and every id that reaches the transcript is
20
- * placed by the control plane (the proxy's wire-id repair lifts a stale
21
- * client id; the drain re-mints a queued prompt above the live turn). So for
22
- * wire ids, id order IS conversation order.
17
+ * 1. PLACED — the message has a wire id, so the server has given it a
18
+ * position. Ordered by that id.
19
+ * 2. LOCAL an optimistic stub, or a queued inbox row the transcript has
20
+ * not echoed yet. Ordered by the instant the user sent it, untimed last.
23
21
  *
24
- * `time.created` is NOT: OpenCode stamps it at PERSISTENCE, from arrival
25
- * wall-clock. A batch of queued prompts is posted concurrently on purpose
26
- * (so one step answers them all), and their arrival order is the network's —
27
- * sorting by it displayed "B4, FIRST, B2, B3" for a burst the model itself
28
- * read, and answered, in id order. It remains the fallback for anything
29
- * without two well-formed wire ids, where it is the only clock there is.
22
+ * Every placed message precedes every local one, because a local placeholder
23
+ * exists precisely because the server has not placed it yet; the moment it is
24
+ * echoed it gains a wire id and moves into segment 1.
30
25
  *
31
- * A message with no timestamp AND no orderable id (a host's plain stub)
32
- * sorts as newest it is the latest thing the user did and two such keep
33
- * their INPUT order. Equal keys keep id order. A weak order (stable sort),
34
- * never a partial one.
26
+ * The wire id is the message's POSITION: OpenCode's own loop resolves "is this
27
+ * answered?" by id order, and every id that reaches the transcript is placed by
28
+ * the control plane (the proxy's wire-id repair lifts a stale client id; the
29
+ * drain re-mints a queued prompt above the live turn). So for wire ids, id
30
+ * order IS conversation order.
31
+ *
32
+ * `time.created` is NOT, which is why it only ever orders segment 2. OpenCode
33
+ * stamps it at PERSISTENCE, from arrival wall-clock. A batch of queued prompts
34
+ * is posted concurrently on purpose (so one step answers them all), and their
35
+ * arrival order is the network's — sorting placed messages by it displayed
36
+ * "B4, FIRST, B2, B3" for a burst the model itself read, and answered, in id
37
+ * order.
38
+ *
39
+ * WHY THE SEGMENTS. This function used to switch ordering per PAIR: wire-id
40
+ * order when both ids were well-formed wire ids, `time.created` for every pair
41
+ * involving anything else. That is not an order at all. Two placed messages A,
42
+ * B and one queued row S whose host-fabricated stamp sat below the box clock
43
+ * compared as A < B (by id), S < A (by time) and B < S (by time) — a cycle.
44
+ * `Array.prototype.sort` may emit any permutation of a cyclic comparator, and
45
+ * V8 switches algorithm with input length, so three prompts sent "who", "are",
46
+ * "you" rendered "who", "you", "are". `groupMessagesIntoTurns` walks the same
47
+ * sorted list to attach assistant messages that carry no `parentID`, so the
48
+ * replies re-parented too, and a queued row could sort ABOVE the whole
49
+ * transcript. Segmenting removes the mixed comparison, and with it the need
50
+ * for any host to fabricate a timestamp to keep a queued row in place.
51
+ *
52
+ * A message with no timestamp AND no wire id (a host's plain stub) sorts last —
53
+ * it is the latest thing the user did — and two such are a TIE, so the stable
54
+ * sort keeps the order the host handed them over in. Equal timestamps keep id
55
+ * order.
35
56
  */
36
57
  const WIRE_DISPLAY_ID = /^msg_[0-9a-f]{12}/;
58
+ /** `0` for a message the server has placed, `1` for one only this tab knows. */
59
+ function displaySegment(id) {
60
+ return WIRE_DISPLAY_ID.test(id) ? 0 : 1;
61
+ }
62
+ function compareIds(a, b) {
63
+ return a < b ? -1 : a > b ? 1 : 0;
64
+ }
37
65
  export function compareMessagesForDisplay(a, b) {
38
- const aWire = WIRE_DISPLAY_ID.test(a.info.id);
39
- const bWire = WIRE_DISPLAY_ID.test(b.info.id);
40
- if (aWire && bWire) {
41
- return a.info.id < b.info.id ? -1 : a.info.id > b.info.id ? 1 : 0;
42
- }
66
+ const segmentA = displaySegment(a.info.id);
67
+ const segmentB = displaySegment(b.info.id);
68
+ if (segmentA !== segmentB)
69
+ return segmentA - segmentB;
70
+ // Placed: the wire id is the position, and it is the only clock that agrees
71
+ // with the loop that produced the messages.
72
+ if (segmentA === 0)
73
+ return compareIds(a.info.id, b.info.id);
74
+ // Local: the send instant is the only record of what the user did, and it is
75
+ // this tab's own clock for both sides, so it is comparable. Untimed last.
43
76
  const at = typeof a.info.time?.created === 'number' ? a.info.time.created : null;
44
77
  const bt = typeof b.info.time?.created === 'number' ? b.info.time.created : null;
78
+ // Two untimed stubs are a TIE, so the stable sort keeps the order the host
79
+ // handed them over in. That is the only signal left about which came first,
80
+ // and an id tiebreak here would reorder `groupMessagesIntoTurns`' own
81
+ // sequential fallback (`u1`, `a1`, `a2` regrouped as `a1`, `a2`, `u1`).
45
82
  if (at === null && bt === null)
46
83
  return 0;
47
84
  if (at === null)
@@ -50,7 +87,7 @@ export function compareMessagesForDisplay(a, b) {
50
87
  return -1;
51
88
  if (at !== bt)
52
89
  return at < bt ? -1 : 1;
53
- return a.info.id < b.info.id ? -1 : a.info.id > b.info.id ? 1 : 0;
90
+ return compareIds(a.info.id, b.info.id);
54
91
  }
55
92
  /**
56
93
  * Group messages into turns: each turn starts with a user message followed