@agentshouse/mdruntime 0.1.0-cd9e28a590f84 → 0.1.0-cdded6c080913

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 (260) hide show
  1. package/README.md +1 -1
  2. package/dist/acceptance.d.ts +6 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +389 -0
  5. package/dist/actions.d.ts +20 -0
  6. package/dist/actions.d.ts.map +1 -0
  7. package/dist/actions.js +250 -0
  8. package/dist/adapter.d.ts +23 -25
  9. package/dist/adapter.d.ts.map +1 -1
  10. package/dist/attached-model.d.ts +3 -10
  11. package/dist/attached-model.d.ts.map +1 -1
  12. package/dist/attached-model.js +21 -56
  13. package/dist/bootstrap.d.ts +8 -4
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +209 -78
  16. package/dist/cell.d.ts +6 -0
  17. package/dist/cell.d.ts.map +1 -0
  18. package/dist/cell.js +9 -0
  19. package/dist/chain.d.ts +2 -1
  20. package/dist/chain.d.ts.map +1 -1
  21. package/dist/change.d.ts +1 -0
  22. package/dist/change.d.ts.map +1 -1
  23. package/dist/chat-types.d.ts +5 -0
  24. package/dist/chat-types.d.ts.map +1 -0
  25. package/dist/chat-types.js +13 -0
  26. package/dist/client.d.ts +17 -2
  27. package/dist/client.d.ts.map +1 -1
  28. package/dist/client.js +3 -0
  29. package/dist/collection.d.ts +2 -1
  30. package/dist/collection.d.ts.map +1 -1
  31. package/dist/compile.d.ts +7 -7
  32. package/dist/compile.d.ts.map +1 -1
  33. package/dist/compile.js +229 -301
  34. package/dist/connection.d.ts +5 -141
  35. package/dist/connection.d.ts.map +1 -1
  36. package/dist/connection.js +98 -5279
  37. package/dist/context.d.ts +2 -0
  38. package/dist/context.d.ts.map +1 -0
  39. package/dist/context.js +7 -0
  40. package/dist/conversation-row.d.ts +33 -0
  41. package/dist/conversation-row.d.ts.map +1 -0
  42. package/dist/conversation-row.js +53 -0
  43. package/dist/conversation.d.ts +16 -6
  44. package/dist/conversation.d.ts.map +1 -1
  45. package/dist/conversation.js +366 -216
  46. package/dist/discovery.d.ts +10 -0
  47. package/dist/discovery.d.ts.map +1 -0
  48. package/dist/discovery.js +175 -0
  49. package/dist/durability.d.ts +15 -0
  50. package/dist/durability.d.ts.map +1 -1
  51. package/dist/entry.d.ts +2 -2
  52. package/dist/entry.d.ts.map +1 -1
  53. package/dist/entry.js +17 -8
  54. package/dist/figures.d.ts +48 -0
  55. package/dist/figures.d.ts.map +1 -0
  56. package/dist/figures.js +197 -0
  57. package/dist/holdings.d.ts +29 -0
  58. package/dist/holdings.d.ts.map +1 -0
  59. package/dist/holdings.js +95 -0
  60. package/dist/host.d.ts +2 -2
  61. package/dist/host.d.ts.map +1 -1
  62. package/dist/host.js +22 -15
  63. package/dist/identity.d.ts +0 -1
  64. package/dist/identity.d.ts.map +1 -1
  65. package/dist/identity.js +0 -3
  66. package/dist/index.d.ts +19 -4
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +4 -0
  69. package/dist/install.d.ts +56 -0
  70. package/dist/install.d.ts.map +1 -0
  71. package/dist/install.js +560 -0
  72. package/dist/job.d.ts +33 -0
  73. package/dist/job.d.ts.map +1 -0
  74. package/dist/job.js +54 -0
  75. package/dist/lifecycle.d.ts +7 -0
  76. package/dist/lifecycle.d.ts.map +1 -0
  77. package/dist/lifecycle.js +267 -0
  78. package/dist/listing.d.ts +53 -0
  79. package/dist/listing.d.ts.map +1 -0
  80. package/dist/listing.js +175 -0
  81. package/dist/live.d.ts +5 -4
  82. package/dist/live.d.ts.map +1 -1
  83. package/dist/live.js +62 -20
  84. package/dist/locks.d.ts +9 -0
  85. package/dist/locks.d.ts.map +1 -0
  86. package/dist/locks.js +61 -0
  87. package/dist/log.d.ts +1 -0
  88. package/dist/log.d.ts.map +1 -1
  89. package/dist/lost.d.ts +5 -0
  90. package/dist/lost.d.ts.map +1 -0
  91. package/dist/lost.js +61 -0
  92. package/dist/memory.d.ts +4 -1
  93. package/dist/memory.d.ts.map +1 -1
  94. package/dist/memory.js +43 -4
  95. package/dist/messages.d.ts +25 -1
  96. package/dist/messages.d.ts.map +1 -1
  97. package/dist/messages.js +84 -5
  98. package/dist/model.d.ts +2 -9
  99. package/dist/model.d.ts.map +1 -1
  100. package/dist/model.js +0 -9
  101. package/dist/models.d.ts +14 -0
  102. package/dist/models.d.ts.map +1 -0
  103. package/dist/models.js +193 -0
  104. package/dist/observation.d.ts +2 -2
  105. package/dist/observation.d.ts.map +1 -1
  106. package/dist/observation.js +7 -2
  107. package/dist/opening.d.ts +4 -0
  108. package/dist/opening.d.ts.map +1 -0
  109. package/dist/opening.js +56 -0
  110. package/dist/outcome.d.ts +4 -1
  111. package/dist/outcome.d.ts.map +1 -1
  112. package/dist/outcome.js +3 -0
  113. package/dist/overlay.d.ts +1 -0
  114. package/dist/overlay.d.ts.map +1 -1
  115. package/dist/overlay.js +11 -0
  116. package/dist/parts.d.ts +59 -0
  117. package/dist/parts.d.ts.map +1 -0
  118. package/dist/parts.js +118 -0
  119. package/dist/preparation.d.ts +20 -0
  120. package/dist/preparation.d.ts.map +1 -0
  121. package/dist/preparation.js +226 -0
  122. package/dist/queries.d.ts +16 -0
  123. package/dist/queries.d.ts.map +1 -0
  124. package/dist/queries.js +272 -0
  125. package/dist/query.d.ts +13 -212
  126. package/dist/query.d.ts.map +1 -1
  127. package/dist/query.js +3 -524
  128. package/dist/questions.d.ts +34 -0
  129. package/dist/questions.d.ts.map +1 -0
  130. package/dist/questions.js +122 -0
  131. package/dist/reach.d.ts +12 -10
  132. package/dist/reach.d.ts.map +1 -1
  133. package/dist/reach.js +165 -109
  134. package/dist/react.d.ts +9 -2
  135. package/dist/react.d.ts.map +1 -1
  136. package/dist/read.d.ts +7 -0
  137. package/dist/read.d.ts.map +1 -0
  138. package/dist/read.js +17 -0
  139. package/dist/reads.d.ts +36 -0
  140. package/dist/reads.d.ts.map +1 -0
  141. package/dist/reads.js +150 -0
  142. package/dist/recovery.d.ts.map +1 -1
  143. package/dist/recovery.js +9 -1
  144. package/dist/refresh.d.ts +1 -1
  145. package/dist/refresh.d.ts.map +1 -1
  146. package/dist/refresh.js +8 -20
  147. package/dist/refusal.d.ts +5 -0
  148. package/dist/refusal.d.ts.map +1 -0
  149. package/dist/refusal.js +56 -0
  150. package/dist/replica.d.ts +8 -8
  151. package/dist/replica.d.ts.map +1 -1
  152. package/dist/replica.js +50 -18
  153. package/dist/room-draft.d.ts +2 -2
  154. package/dist/room-draft.d.ts.map +1 -1
  155. package/dist/room-drafts.d.ts +24 -0
  156. package/dist/room-drafts.d.ts.map +1 -0
  157. package/dist/room-drafts.js +216 -0
  158. package/dist/rows.d.ts +14 -22
  159. package/dist/rows.d.ts.map +1 -1
  160. package/dist/rows.js +48 -189
  161. package/dist/runtime.d.ts +42 -0
  162. package/dist/runtime.d.ts.map +1 -0
  163. package/dist/runtime.js +1 -0
  164. package/dist/save.d.ts.map +1 -1
  165. package/dist/save.js +11 -8
  166. package/dist/saves.d.ts +70 -0
  167. package/dist/saves.d.ts.map +1 -0
  168. package/dist/saves.js +1004 -0
  169. package/dist/search.d.ts.map +1 -1
  170. package/dist/search.js +1 -1
  171. package/dist/seed.d.ts +11 -2
  172. package/dist/seed.d.ts.map +1 -1
  173. package/dist/seed.js +71 -0
  174. package/dist/shown.d.ts +31 -0
  175. package/dist/shown.d.ts.map +1 -0
  176. package/dist/shown.js +294 -0
  177. package/dist/socket/admission.d.ts +7 -0
  178. package/dist/socket/admission.d.ts.map +1 -0
  179. package/dist/socket/admission.js +39 -0
  180. package/dist/socket/answers.d.ts +6 -0
  181. package/dist/socket/answers.d.ts.map +1 -0
  182. package/dist/socket/answers.js +20 -0
  183. package/dist/socket/lanes.d.ts +20 -0
  184. package/dist/socket/lanes.d.ts.map +1 -0
  185. package/dist/socket/lanes.js +77 -0
  186. package/dist/socket/reads.d.ts +10 -0
  187. package/dist/socket/reads.d.ts.map +1 -0
  188. package/dist/socket/reads.js +31 -0
  189. package/dist/socket/rooms.d.ts +43 -0
  190. package/dist/socket/rooms.d.ts.map +1 -0
  191. package/dist/socket/rooms.js +90 -0
  192. package/dist/socket/session.d.ts +39 -0
  193. package/dist/socket/session.d.ts.map +1 -0
  194. package/dist/socket/session.js +23 -0
  195. package/dist/socket.d.ts +70 -0
  196. package/dist/socket.d.ts.map +1 -0
  197. package/dist/socket.js +175 -0
  198. package/dist/source.d.ts +61 -0
  199. package/dist/source.d.ts.map +1 -0
  200. package/dist/source.js +81 -0
  201. package/dist/status.d.ts +0 -2
  202. package/dist/status.d.ts.map +1 -1
  203. package/dist/status.js +0 -6
  204. package/dist/store/fences.d.ts +16 -0
  205. package/dist/store/fences.d.ts.map +1 -0
  206. package/dist/store/fences.js +26 -0
  207. package/dist/store/installs.d.ts +30 -0
  208. package/dist/store/installs.d.ts.map +1 -0
  209. package/dist/store/installs.js +34 -0
  210. package/dist/store/readiness.d.ts +10 -0
  211. package/dist/store/readiness.d.ts.map +1 -0
  212. package/dist/store/readiness.js +52 -0
  213. package/dist/store/records.d.ts +212 -0
  214. package/dist/store/records.d.ts.map +1 -0
  215. package/dist/store/records.js +408 -0
  216. package/dist/store/room-held.d.ts +8 -0
  217. package/dist/store/room-held.d.ts.map +1 -0
  218. package/dist/store/room-held.js +17 -0
  219. package/dist/store/space.d.ts +11 -0
  220. package/dist/store/space.d.ts.map +1 -0
  221. package/dist/store/space.js +43 -0
  222. package/dist/store/watchers.d.ts +17 -0
  223. package/dist/store/watchers.d.ts.map +1 -0
  224. package/dist/store/watchers.js +57 -0
  225. package/dist/store/writes.d.ts +24 -0
  226. package/dist/store/writes.d.ts.map +1 -0
  227. package/dist/store/writes.js +125 -0
  228. package/dist/store.d.ts +107 -153
  229. package/dist/store.d.ts.map +1 -1
  230. package/dist/store.js +143 -396
  231. package/dist/stored.d.ts +58 -64
  232. package/dist/stored.d.ts.map +1 -1
  233. package/dist/stored.js +244 -156
  234. package/dist/surface.d.ts +88 -0
  235. package/dist/surface.d.ts.map +1 -0
  236. package/dist/surface.js +222 -0
  237. package/dist/sync.d.ts +20 -0
  238. package/dist/sync.d.ts.map +1 -0
  239. package/dist/sync.js +531 -0
  240. package/dist/tables.d.ts +110 -48
  241. package/dist/tables.d.ts.map +1 -1
  242. package/dist/tables.js +209 -54
  243. package/dist/tallies.d.ts +18 -0
  244. package/dist/tallies.d.ts.map +1 -0
  245. package/dist/tallies.js +129 -0
  246. package/dist/validation.d.ts.map +1 -1
  247. package/dist/validation.js +7 -4
  248. package/dist/windows.d.ts +1 -12
  249. package/dist/windows.d.ts.map +1 -1
  250. package/dist/windows.js +2 -75
  251. package/dist/wire.d.ts +16 -1
  252. package/dist/wire.d.ts.map +1 -1
  253. package/dist/wire.js +6 -0
  254. package/package.json +2 -2
  255. package/dist/fence.d.ts +0 -6
  256. package/dist/fence.d.ts.map +0 -1
  257. package/dist/fence.js +0 -10
  258. package/dist/phrase.d.ts +0 -3
  259. package/dist/phrase.d.ts.map +0 -1
  260. package/dist/phrase.js +0 -8
@@ -1,10 +1,14 @@
1
+ import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
1
2
  import { conversationChangeSet } from './change-set.js';
3
+ import { heldFacts } from './conversation-row.js';
2
4
  import { runtimeError } from './failure.js';
3
5
  import { refusedForSpace } from './durability.js';
4
6
  import { changeBatchIdentity, chatDraftRevision } from './identity.js';
5
7
  import { OFFLINE } from './outcome.js';
8
+ import { jobsOf, runningLine } from './parts.js';
9
+ import { openQuestion, questionAsked } from './questions.js';
6
10
  import { untilAnswered } from './recovery.js';
7
- import { capped, cursorOf, decodedCursor, FROM_START, messageOrder, precedes } from './windows.js';
11
+ import { capped, FROM_START, messageOrder, precedes } from './windows.js';
8
12
  const QUIET = {
9
13
  turn: null,
10
14
  questions: [],
@@ -33,6 +37,7 @@ export function unheldView(conversation) {
33
37
  turn: null,
34
38
  questions: [],
35
39
  process: null,
40
+ processEnded: null,
36
41
  commands: [],
37
42
  options: [],
38
43
  starting: null,
@@ -40,21 +45,22 @@ export function unheldView(conversation) {
40
45
  link: null,
41
46
  deliveryUnknown: [],
42
47
  chatDraft: null,
48
+ jobs: [],
43
49
  };
44
50
  }
45
51
  export const SESSION_STARTED = 'Session started';
46
52
  const NEW_CHAT = 'new:';
47
53
  export const HISTORY_CONVERSATIONS = 64;
54
+ // A row is read at a glance and each telling clones the list to every observing tab, while a running line changes at each Kit flush.
55
+ const ROW_INTERVAL_MS = 1_000;
48
56
  export function newChat(agent) {
49
57
  return `${NEW_CHAT}${agent}`;
50
58
  }
51
59
  function creationOf(input) {
52
60
  if (typeof input !== 'object' || input === null)
53
61
  return null;
54
- const { agent_id: agent, operation_id: operation, replaces, } = input;
55
- return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
56
- ? { agent, operation }
57
- : null;
62
+ const { agent_id: agent, operation_id: operation } = input;
63
+ return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
58
64
  }
59
65
  function startedTurn(turn, operation) {
60
66
  if (operation.op === 'begin')
@@ -221,7 +227,6 @@ function phraseOf(text) {
221
227
  }
222
228
  return text;
223
229
  }
224
- const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
225
230
  const DURABLE_WRITE = {
226
231
  kind: 'rejected',
227
232
  code: 'durable-write',
@@ -255,10 +260,6 @@ function returned(draft, submission, others) {
255
260
  }, others);
256
261
  }
257
262
  export function createConversationStore(deps) {
258
- const stored = new Set();
259
- const reads = new Map();
260
- const partials = new Set();
261
- const drafts = new Map();
262
263
  const sending = new Map();
263
264
  const behind = new Set();
264
265
  const catching = new Map();
@@ -266,15 +267,16 @@ export function createConversationStore(deps) {
266
267
  const lives = new Map();
267
268
  const unsent = new Map();
268
269
  const views = new Map();
269
- const listing = new Map();
270
+ const heardNewest = new Map();
270
271
  let relisting = null;
271
- let listCursor = null;
272
272
  const creations = new Set();
273
273
  const listWatchers = new Set();
274
274
  const unlisted = new Set();
275
275
  let listKnown = false;
276
- let listHeld = false;
277
- let listKept = false;
276
+ let listOwed = false;
277
+ let listTelling = false;
278
+ let rowTimer = null;
279
+ const closing = new AbortController();
278
280
  const agentWatchers = new Set();
279
281
  const unnamed = new Set();
280
282
  let agentsShown = null;
@@ -285,8 +287,7 @@ export function createConversationStore(deps) {
285
287
  function liveOf(conversation) {
286
288
  return lives.get(conversation) ?? QUIET;
287
289
  }
288
- function viewOf(conversation, held, frame) {
289
- const draft = drafts.get(conversation);
290
+ function viewOf(conversation, held, frame, draft, ended, marked) {
290
291
  const facts = liveOf(conversation);
291
292
  return {
292
293
  conversation,
@@ -300,15 +301,17 @@ export function createConversationStore(deps) {
300
301
  turn: structuredClone(facts.turn),
301
302
  questions: structuredClone(facts.questions),
302
303
  process: facts.process === null ? null : { ...facts.process },
304
+ processEnded: ended,
303
305
  commands: structuredClone(facts.commands),
304
306
  options: structuredClone(facts.options),
305
307
  starting: facts.starting,
306
308
  subagents: structuredClone(facts.subagents),
307
309
  link: facts.link === null ? null : { ...facts.link },
308
310
  deliveryUnknown: [...facts.deliveryUnknown],
309
- chatDraft: draft === undefined || unwritten(draft)
310
- ? null
311
- : { text: draft.text, revision: draft.revision },
311
+ chatDraft: draft === null || unwritten(draft) ? null : { text: draft.text, revision: draft.revision },
312
+ jobs: facts.process?.running === false
313
+ ? []
314
+ : jobsOf(marked.filter(({ at }) => ended === null || at > ended), facts.turn),
312
315
  };
313
316
  }
314
317
  function emit(conversation) {
@@ -320,7 +323,7 @@ export function createConversationStore(deps) {
320
323
  const ready = [...view.watchers].filter((watcher) => watcher.ready);
321
324
  if (ready.length === 0)
322
325
  continue;
323
- const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
326
+ const value = viewOf(conversation, view.window, sharedFrame(view, view.window), view.draft, view.ended, view.marked);
324
327
  for (const watcher of ready)
325
328
  watcher.notify(value);
326
329
  }
@@ -330,11 +333,13 @@ export function createConversationStore(deps) {
330
333
  }
331
334
  async function slide(local, conversation, arriving) {
332
335
  let count;
336
+ let marked;
333
337
  for (const view of views.values()) {
334
338
  if (view.conversation !== conversation || view.window === null)
335
339
  continue;
336
340
  if (arriving === null) {
337
341
  view.window = [];
342
+ view.marked = [];
338
343
  view.more = false;
339
344
  if (view.total !== null)
340
345
  view.total = 0;
@@ -344,6 +349,10 @@ export function createConversationStore(deps) {
344
349
  const next = windowOf(view.window, joining, view.asked.limit);
345
350
  view.window = next.messages;
346
351
  view.more ||= next.dropped;
352
+ if (arriving.length > 0) {
353
+ marked ??= await local.jobMarkers(conversation);
354
+ view.marked = marked;
355
+ }
347
356
  if (view.total !== null && arriving.length > 0) {
348
357
  count ??= await local.messageCount(conversation);
349
358
  view.total = count;
@@ -356,79 +365,179 @@ export function createConversationStore(deps) {
356
365
  return 'asking';
357
366
  return facts.turn === null ? 'idle' : 'working';
358
367
  }
359
- function entries() {
360
- return [...listing.values()].map((entry) => ({
368
+ function rowOf(conversation) {
369
+ const turn = lives.get(conversation)?.turn ?? null;
370
+ return {
371
+ state: stateOf(conversation),
372
+ started: turn?.started ?? null,
373
+ line: turn === null ? null : runningLine(turn.parts),
374
+ question: openQuestion(liveOf(conversation).questions),
375
+ asked: questionAsked(liveOf(conversation).questions),
376
+ };
377
+ }
378
+ async function entries() {
379
+ const list = await heldList();
380
+ if (list === null)
381
+ return null;
382
+ const held = list.length === 0
383
+ ? []
384
+ : await deps.durably((local) => local.conversationRows(list.map(({ conversation }) => conversation)));
385
+ const rows = new Map(held.map((one) => [one.conversation, one]));
386
+ return list.map((entry) => ({
361
387
  conversation: entry.conversation,
362
- agent: { identity: entry.agent, name: entry.agentName },
388
+ agent: { identity: entry.agent, name: entry.agentName, retired: entry.agentRetired },
363
389
  name: entry.name,
364
390
  created: entry.created,
365
391
  finished: entry.finished,
366
392
  outcome: entry.outcome,
367
393
  waiting: entry.waiting,
368
- state: stateOf(entry.conversation),
369
- started: lives.get(entry.conversation)?.turn?.started ?? null,
394
+ ...rowOf(entry.conversation),
395
+ ...heldFacts(rows.get(entry.conversation), entry),
370
396
  }));
371
397
  }
372
- function keepList() {
373
- if (!listKnown || listKept)
374
- return;
375
- listKept = true;
376
- queueMicrotask(() => {
377
- listKept = false;
378
- if (deps.closed())
398
+ async function heldList() {
399
+ const stored = (await (await deps.local()).facts()).conversationList;
400
+ if (stored !== null)
401
+ return stored;
402
+ return !listKnown && deps.stream() !== undefined && deps.online() ? null : [];
403
+ }
404
+ async function endedIn(conversation) {
405
+ const list = await heldList();
406
+ return list?.find((entry) => entry.conversation === conversation)?.processEnded ?? null;
407
+ }
408
+ function listChanged(change) {
409
+ void deps
410
+ .durably(async (local) => {
411
+ const { conversationList, conversationCursor } = await local.facts();
412
+ const kept = change.replacing ? [] : (conversationList ?? []);
413
+ const held = new Map(kept.map((entry) => [entry.conversation, entry]));
414
+ for (const entry of change.set) {
415
+ held.set(entry.conversation, { ...entry });
416
+ creations.delete(entry.conversation);
417
+ }
418
+ if (change.gone !== null)
419
+ held.delete(change.gone);
420
+ const list = [...held.values()];
421
+ if (JSON.stringify(list) !== JSON.stringify(conversationList)) {
422
+ await local.fact('conversationList', list);
423
+ }
424
+ if (change.cursor !== undefined) {
425
+ if (change.cursor !== conversationCursor) {
426
+ await local.fact('conversationCursor', change.cursor);
427
+ }
428
+ deps.stream()?.hold(change.cursor);
429
+ }
430
+ return list;
431
+ })
432
+ .catch(async () => (await (await deps.local()).facts()).conversationList ?? [])
433
+ .then((list) => {
434
+ if (deps.closed() || change.paged === true)
379
435
  return;
380
- const entries = [...listing.values()];
381
- const cursor = listCursor;
382
- void deps
383
- .durably(async (local) => {
384
- await local.fact('conversationList', entries);
385
- await local.fact('conversationCursor', cursor);
386
- })
387
- .catch(() => { });
436
+ endedAnew(list);
437
+ if (change.cursor !== undefined) {
438
+ listKnown = true;
439
+ settleBehind(list);
440
+ openingEnded();
441
+ deps.settled();
442
+ }
443
+ emitList();
444
+ if (!listKnown)
445
+ return;
446
+ retiredDrafts(list);
447
+ if (change.cursor === undefined &&
448
+ (list.length !== replicated.size ||
449
+ list.some(({ conversation }) => !replicated.has(conversation)))) {
450
+ deps.again();
451
+ }
452
+ }, () => { });
453
+ }
454
+ function endedAnew(list) {
455
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
456
+ for (const view of views.values()) {
457
+ const ended = entries.get(view.conversation)?.processEnded ?? null;
458
+ if (view.window === null || ended === view.ended)
459
+ continue;
460
+ view.ended = ended;
461
+ emit(view.conversation);
462
+ }
463
+ }
464
+ let listShown = Promise.resolve();
465
+ function shownList(work) {
466
+ const reading = heldList();
467
+ listShown = listShown.then(async () => {
468
+ const list = await reading.catch(() => null);
469
+ if (list === null || deps.closed())
470
+ return;
471
+ work(list);
388
472
  });
389
473
  }
390
- function listed(entry) {
391
- listing.set(entry.conversation, { ...entry });
392
- creations.delete(entry.conversation);
474
+ async function told() {
475
+ try {
476
+ while (listOwed && !deps.closed()) {
477
+ listOwed = false;
478
+ const value = await untilAnswered(entries, closing.signal);
479
+ if (deps.closed())
480
+ return;
481
+ if (value === null)
482
+ continue;
483
+ for (const watcher of listWatchers)
484
+ watcher(value);
485
+ for (const watcher of [...unlisted]) {
486
+ unlisted.delete(watcher);
487
+ listWatchers.add(watcher);
488
+ watcher(value);
489
+ }
490
+ }
491
+ }
492
+ finally {
493
+ listTelling = false;
494
+ }
495
+ }
496
+ function tellList() {
497
+ if (deps.closed() || (listWatchers.size === 0 && unlisted.size === 0))
498
+ return;
499
+ listOwed = true;
500
+ if (listTelling)
501
+ return;
502
+ listTelling = true;
503
+ void told().catch(() => { });
504
+ }
505
+ function tellSoon() {
506
+ if (rowTimer !== null)
507
+ return;
508
+ rowTimer = setTimeout(() => {
509
+ rowTimer = null;
510
+ tellList();
511
+ }, ROW_INTERVAL_MS);
393
512
  }
394
513
  function emitList() {
395
514
  if (deps.closed())
396
515
  return;
397
- if (listWatchers.size > 0) {
398
- const value = entries();
399
- for (const watcher of listWatchers)
400
- watcher(value);
401
- }
516
+ tellList();
402
517
  emitAgents();
403
518
  learned();
404
519
  }
405
520
  function learned() {
406
521
  if (deps.closed())
407
522
  return;
408
- if (!listKnown && !listHeld && deps.stream() !== undefined && deps.online())
523
+ if (unlisted.size > 0)
524
+ tellList();
525
+ if (unnamed.size === 0)
409
526
  return;
410
- if (unlisted.size > 0) {
411
- const value = entries();
412
- for (const watcher of [...unlisted]) {
413
- unlisted.delete(watcher);
414
- listWatchers.add(watcher);
415
- watcher(value);
416
- }
417
- }
418
- if (unnamed.size > 0) {
419
- const value = agents();
527
+ shownList((list) => {
528
+ const value = agents(list);
420
529
  for (const watcher of [...unnamed]) {
421
530
  unnamed.delete(watcher);
422
531
  agentWatchers.add(watcher);
423
532
  watcher(value);
424
533
  }
425
- }
534
+ });
426
535
  }
427
- function agents() {
536
+ function agents(list) {
428
537
  const named = new Map();
429
538
  for (const agent of listedAgents ?? [])
430
539
  named.set(agent.agent, agent);
431
- for (const entry of listing.values()) {
540
+ for (const entry of list) {
432
541
  named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
433
542
  }
434
543
  return [...named]
@@ -458,31 +567,35 @@ export function createConversationStore(deps) {
458
567
  function emitAgents() {
459
568
  if (deps.closed() || agentWatchers.size === 0)
460
569
  return;
461
- const value = agents();
462
- const shown = JSON.stringify(value);
463
- if (shown === agentsShown)
464
- return;
465
- agentsShown = shown;
466
- for (const watcher of agentWatchers)
467
- watcher(value);
570
+ shownList((list) => {
571
+ const value = agents(list);
572
+ const shown = JSON.stringify(value);
573
+ if (shown === agentsShown)
574
+ return;
575
+ agentsShown = shown;
576
+ for (const watcher of agentWatchers)
577
+ watcher(value);
578
+ });
468
579
  }
469
580
  function setLive(conversation, next) {
470
- const before = stateOf(conversation);
471
- const began = lives.get(conversation)?.turn?.started;
581
+ const before = rowOf(conversation);
472
582
  if (next === null)
473
583
  lives.delete(conversation);
474
584
  else
475
585
  lives.set(conversation, next);
476
- if (!listing.has(conversation))
477
- return;
478
- if (stateOf(conversation) !== before || next?.turn?.started !== began)
586
+ const after = rowOf(conversation);
587
+ if (after.state !== before.state || after.started !== before.started)
479
588
  emitList();
589
+ else if (after.line !== before.line ||
590
+ after.question !== before.question ||
591
+ after.asked !== before.asked)
592
+ tellSoon();
480
593
  }
481
- function retiredChat(conversation) {
594
+ function retiredChat(conversation, list) {
482
595
  if (!conversation.startsWith(NEW_CHAT))
483
596
  return false;
484
597
  const agent = conversation.slice(NEW_CHAT.length);
485
- return [...listing.values()].some((entry) => entry.agent === agent && entry.agentRetired);
598
+ return list.some((entry) => entry.agent === agent && entry.agentRetired);
486
599
  }
487
600
  function live(conversation, change) {
488
601
  const owed = unsent.get(conversation);
@@ -540,17 +653,23 @@ export function createConversationStore(deps) {
540
653
  }
541
654
  async function remove(leaving) {
542
655
  await deps.durably(async (local) => {
656
+ const kept = new Set([
657
+ ...(await local.conversations(leaving)).map(({ conversation }) => conversation),
658
+ ...(await local.chatDrafts())
659
+ .filter(({ conversation }) => leaving.includes(conversation))
660
+ .map(({ conversation }) => conversation),
661
+ ]);
543
662
  await local.dropConversations(leaving);
544
663
  const changed = [];
545
664
  for (const conversation of leaving) {
546
- if (stored.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
665
+ if (kept.has(conversation) || lives.has(conversation))
547
666
  changed.push(conversation);
667
+ for (const view of views.values()) {
668
+ if (view.conversation === conversation)
669
+ view.draft = null;
548
670
  }
549
- stored.delete(conversation);
550
- reads.delete(conversation);
551
- partials.delete(conversation);
552
- drafts.delete(conversation);
553
671
  behind.delete(conversation);
672
+ heardNewest.delete(conversation);
554
673
  unsent.delete(conversation);
555
674
  setLive(conversation, null);
556
675
  await slide(local, conversation, null);
@@ -559,19 +678,68 @@ export function createConversationStore(deps) {
559
678
  deps.publish(conversationChangeSet(changed));
560
679
  });
561
680
  }
562
- function missed(conversation) {
563
- const read = reads.get(conversation);
564
- const newest = listing.get(conversation)?.newest;
681
+ function newestOfListed(entry) {
682
+ if (entry === undefined)
683
+ return undefined;
684
+ const heard = heardNewest.get(entry.conversation);
685
+ if (heard === undefined)
686
+ return entry.newest;
687
+ return entry.newest === null || heard > entry.newest ? heard : entry.newest;
688
+ }
689
+ function missed(conversation, held, entry) {
690
+ const read = held?.read;
691
+ const newest = newestOfListed(entry);
565
692
  return (read === undefined ||
566
693
  newest === undefined ||
567
- partials.has(conversation) ||
694
+ held?.partial === true ||
568
695
  (newest !== null && (read === null || newest > read)));
569
696
  }
570
- function noted(conversation, message) {
571
- const entry = listing.get(conversation);
572
- if (entry === undefined || (entry.newest !== null && entry.newest >= message.position))
697
+ async function readsOf(local, conversations) {
698
+ return new Map((await local.conversations(conversations)).map((held) => [held.conversation, held]));
699
+ }
700
+ function retiredDrafts(list) {
701
+ if (!list.some((entry) => entry.agentRetired))
573
702
  return;
574
- listing.set(conversation, { ...entry, newest: message.position });
703
+ void deps
704
+ .local()
705
+ .then((local) => local.chatDrafts())
706
+ .then((held) => {
707
+ if (!deps.closed() && held.some(({ conversation }) => retiredChat(conversation, list))) {
708
+ deps.again();
709
+ }
710
+ }, () => { });
711
+ }
712
+ function settleBehind(list) {
713
+ void deps
714
+ .durably(async (local) => {
715
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
716
+ const named = [...replicated].filter((conversation) => entries.has(conversation));
717
+ const held = await readsOf(local, named);
718
+ let owed = false;
719
+ for (const conversation of named) {
720
+ if (!missed(conversation, held.get(conversation), entries.get(conversation))) {
721
+ behind.delete(conversation);
722
+ }
723
+ else if (!behind.has(conversation)) {
724
+ gap(conversation);
725
+ owed = true;
726
+ }
727
+ }
728
+ return owed;
729
+ })
730
+ .then((owed) => {
731
+ if (deps.closed())
732
+ return;
733
+ deps.settled();
734
+ if (owed)
735
+ deps.again();
736
+ }, () => { });
737
+ }
738
+ function noted(conversation, message) {
739
+ const heard = heardNewest.get(conversation);
740
+ if (heard === undefined || message.position > heard) {
741
+ heardNewest.set(conversation, message.position);
742
+ }
575
743
  }
576
744
  function gap(conversation) {
577
745
  gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
@@ -581,7 +749,7 @@ export function createConversationStore(deps) {
581
749
  return deps.durably(async (local) => {
582
750
  if (!replicated.has(conversation))
583
751
  return;
584
- const read = reads.get(conversation);
752
+ const read = (await readsOf(local, [conversation])).get(conversation)?.read;
585
753
  const advances = read !== undefined &&
586
754
  !behind.has(conversation) &&
587
755
  (read === null || message.position > read);
@@ -591,19 +759,18 @@ export function createConversationStore(deps) {
591
759
  messages: [message],
592
760
  ...(advances ? { read: { at: message.position, partial: false } } : {}),
593
761
  });
594
- stored.add(conversation);
595
- if (advances)
596
- reads.set(conversation, message.position);
597
762
  sealTurns(conversation, new Set([message.turn]));
598
763
  await slide(local, conversation, [message]);
599
764
  deps.publish(conversationChangeSet([conversation]));
765
+ tellList();
600
766
  });
601
767
  }
602
768
  async function readHistories(conversations, ended) {
603
769
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
770
+ const held = await readsOf(await deps.local(), conversations);
604
771
  let waiting = conversations.map((conversation) => ({
605
772
  conversation,
606
- after: reads.get(conversation) ?? undefined,
773
+ after: held.get(conversation)?.read ?? undefined,
607
774
  }));
608
775
  while (waiting.length > 0) {
609
776
  const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
@@ -659,15 +826,10 @@ export function createConversationStore(deps) {
659
826
  return;
660
827
  const whole = next === undefined;
661
828
  const cursor = next ?? newestOf(arriving, from);
662
- const partial = !whole && (partials.has(conversation) || !reads.has(conversation));
829
+ const held = (await readsOf(local, [conversation])).get(conversation);
830
+ const partial = !whole && (held?.partial === true || held?.read === undefined);
663
831
  const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
664
832
  await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
665
- stored.add(conversation);
666
- reads.set(conversation, cursor);
667
- if (partial)
668
- partials.add(conversation);
669
- else
670
- partials.delete(conversation);
671
833
  sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
672
834
  if (whole && (gaps.get(conversation) ?? 0) === generation)
673
835
  behind.delete(conversation);
@@ -675,6 +837,8 @@ export function createConversationStore(deps) {
675
837
  if (arriving.length > 0 || sealing || whole) {
676
838
  deps.publish(conversationChangeSet([conversation]));
677
839
  }
840
+ if (arriving.length > 0)
841
+ tellSoon();
678
842
  });
679
843
  }
680
844
  function heard(event) {
@@ -741,17 +905,24 @@ export function createConversationStore(deps) {
741
905
  emit(conversation);
742
906
  }
743
907
  async function kept(local, conversation, following) {
744
- if (following === null || empty(following)) {
908
+ const shown = following === null || empty(following) ? null : following;
909
+ if (shown === null)
745
910
  await local.dropChatDraft(conversation);
746
- drafts.delete(conversation);
747
- }
748
- else {
749
- await local.keepChatDrafts([following]);
750
- drafts.set(conversation, following);
911
+ else
912
+ await local.keepChatDrafts([shown]);
913
+ shownDraft(conversation, shown);
914
+ }
915
+ function shownDraft(conversation, draft) {
916
+ for (const view of views.values()) {
917
+ if (view.conversation === conversation)
918
+ view.draft = draft;
751
919
  }
752
920
  }
921
+ async function draftAt(local, conversation) {
922
+ return (await local.chatDraft(conversation)) ?? undefined;
923
+ }
753
924
  async function sealed(local, conversation, turns) {
754
- const held = drafts.get(conversation);
925
+ const held = await draftAt(local, conversation);
755
926
  const submitted = held?.submitted ?? [];
756
927
  const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
757
928
  if (held === undefined || left.length === submitted.length)
@@ -762,8 +933,8 @@ export function createConversationStore(deps) {
762
933
  function moved(agent, operation, to) {
763
934
  const from = newChat(agent);
764
935
  return deps.durably(async (local) => {
765
- const held = drafts.get(from);
766
- const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
936
+ const held = await draftAt(local, from);
937
+ const earlier = held?.moves ?? [];
767
938
  if (earlier.includes(operation))
768
939
  return;
769
940
  const blank = {
@@ -772,18 +943,20 @@ export function createConversationStore(deps) {
772
943
  revision: chatDraftRevision(),
773
944
  };
774
945
  const { moves: _answered, ...unsent } = held ?? blank;
775
- const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
946
+ const taken = (await draftAt(local, to)) !== undefined || empty(unsent)
947
+ ? []
948
+ : [{ ...unsent, conversation: to }];
776
949
  const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
777
950
  await local.keepChatDrafts([left, ...taken]);
778
- drafts.set(from, left);
951
+ shownDraft(from, left);
779
952
  for (const one of taken)
780
- drafts.set(to, one);
953
+ shownDraft(to, one);
781
954
  deps.publish(conversationChangeSet([from, to]));
782
955
  });
783
956
  }
784
957
  function withDraft(conversation, next) {
785
958
  return deps.durably(async (local) => {
786
- const following = await next(drafts.get(conversation), local);
959
+ const following = await next(await draftAt(local, conversation), local);
787
960
  if (following === undefined)
788
961
  return;
789
962
  await kept(local, conversation, following);
@@ -792,10 +965,8 @@ export function createConversationStore(deps) {
792
965
  }
793
966
  async function submitted(conversation, operation, draft) {
794
967
  let submission;
795
- let earlier = false;
796
968
  await withDraft(conversation, (current) => {
797
969
  submission = current?.submitted?.find((one) => one.operation === operation);
798
- earlier = submission !== undefined;
799
970
  if (submission !== undefined)
800
971
  return undefined;
801
972
  const said = draft ?? current;
@@ -808,15 +979,7 @@ export function createConversationStore(deps) {
808
979
  submitted: [...(current?.submitted ?? []), submission],
809
980
  };
810
981
  });
811
- return { submission: submission, earlier };
812
- }
813
- function forgotten(conversation, submission) {
814
- return withDraft(conversation, (current) => {
815
- const held = current?.submitted ?? [];
816
- if (current === undefined || !held.includes(submission))
817
- return undefined;
818
- return withSubmitted(current, held.filter((one) => one !== submission));
819
- });
982
+ return submission;
820
983
  }
821
984
  async function dispatched(conversation, submission) {
822
985
  const dispatch = { answered: false };
@@ -875,7 +1038,8 @@ export function createConversationStore(deps) {
875
1038
  return deps.durably(async (local) => {
876
1039
  const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
877
1040
  const total = asked.total ? await local.messageCount(conversation, phrase) : null;
878
- return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
1041
+ const marked = await local.jobMarkers(conversation);
1042
+ return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total), (await local.chatDraft(conversation)) ?? null, await endedIn(conversation), marked));
879
1043
  });
880
1044
  },
881
1045
  async create(input) {
@@ -883,8 +1047,10 @@ export function createConversationStore(deps) {
883
1047
  const conversation = answered(result, 'conversation_id');
884
1048
  if (conversation === null)
885
1049
  return result;
886
- if (!listing.has(conversation))
1050
+ const listed = (await heldList()) ?? [];
1051
+ if (!listed.some((entry) => entry.conversation === conversation)) {
887
1052
  creations.add(conversation);
1053
+ }
888
1054
  const creation = creationOf(input);
889
1055
  if (creation !== null)
890
1056
  await moved(creation.agent, creation.operation, conversation);
@@ -901,11 +1067,7 @@ export function createConversationStore(deps) {
901
1067
  });
902
1068
  if (held === null)
903
1069
  return DURABLE_WRITE;
904
- const { submission, earlier } = held;
905
- if (!earlier || !deps.expired(operation))
906
- return dispatched(conversation, submission);
907
- await forgotten(conversation, submission);
908
- return EXPIRED;
1070
+ return dispatched(conversation, held);
909
1071
  }
910
1072
  if (draft !== undefined) {
911
1073
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
@@ -956,43 +1118,40 @@ export function createConversationStore(deps) {
956
1118
  return {
957
1119
  surface,
958
1120
  chatDraft,
959
- hydrate(held, kept, list, cursor) {
960
- if (list !== null && !listKnown) {
961
- listing.clear();
962
- for (const entry of list)
963
- listing.set(entry.conversation, { ...entry });
964
- listHeld = true;
965
- listCursor = cursor;
966
- deps.stream()?.hold(cursor);
967
- }
968
- stored.clear();
969
- reads.clear();
970
- partials.clear();
971
- for (const entry of held) {
972
- stored.add(entry.conversation);
973
- if (entry.read !== undefined)
974
- reads.set(entry.conversation, entry.read);
975
- if (entry.partial)
976
- partials.add(entry.conversation);
977
- }
978
- drafts.clear();
979
- for (const record of kept)
980
- drafts.set(record.conversation, record);
981
- if (listHeld)
982
- learned();
1121
+ async opened(local) {
1122
+ const { conversationList, conversationCursor } = await local.facts();
1123
+ if (conversationList === null || listKnown)
1124
+ return;
1125
+ deps.stream()?.hold(conversationCursor);
1126
+ learned();
983
1127
  },
984
1128
  async reconcile() {
985
1129
  if (!listKnown)
986
1130
  return;
987
- const named = new Set(listing.keys());
1131
+ const local = await deps.local();
1132
+ const [held, kept, { conversationList }] = await Promise.all([
1133
+ local.conversations(),
1134
+ local.chatDrafts(),
1135
+ local.facts(),
1136
+ ]);
1137
+ const list = conversationList ?? [];
1138
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
1139
+ const named = new Set(entries.keys());
1140
+ const reads = new Map(held.map((one) => [one.conversation, one]));
988
1141
  const leaving = [
989
- ...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
1142
+ ...new Set([
1143
+ ...replicated,
1144
+ ...reads.keys(),
1145
+ ...kept.map(({ conversation }) => conversation),
1146
+ ]),
990
1147
  ].filter((conversation) => !named.has(conversation) &&
991
1148
  !creations.has(conversation) &&
992
- (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
1149
+ (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation, list)));
993
1150
  for (const conversation of named) {
994
- if (!replicated.has(conversation) && missed(conversation))
1151
+ if (!replicated.has(conversation) &&
1152
+ missed(conversation, reads.get(conversation), entries.get(conversation))) {
995
1153
  behind.add(conversation);
1154
+ }
996
1155
  }
997
1156
  replicated = named;
998
1157
  handReplicated();
@@ -1023,16 +1182,16 @@ export function createConversationStore(deps) {
1023
1182
  progress.complete = false;
1024
1183
  },
1025
1184
  async resend(progress) {
1026
- for (const [conversation, held] of [...drafts]) {
1027
- for (const submission of held.submitted ?? []) {
1028
- if (submission.turn !== null || sending.has(submission.operation))
1029
- continue;
1185
+ const local = await deps.local();
1186
+ const tried = new Set();
1187
+ for (const { conversation } of await local.chatDrafts()) {
1188
+ for (;;) {
1189
+ const submission = (await local.chatDraft(conversation))?.submitted?.find((one) => one.turn === null && !sending.has(one.operation) && !tried.has(one.operation));
1190
+ if (submission === undefined)
1191
+ break;
1030
1192
  if (deps.closed())
1031
1193
  return;
1032
- if (deps.expired(submission.operation)) {
1033
- await forgotten(conversation, submission);
1034
- continue;
1035
- }
1194
+ tried.add(submission.operation);
1036
1195
  const result = await dispatched(conversation, submission);
1037
1196
  if (result.kind === 'revoked-authorization')
1038
1197
  return;
@@ -1070,6 +1229,9 @@ export function createConversationStore(deps) {
1070
1229
  asked,
1071
1230
  watchers: new Set(),
1072
1231
  window: null,
1232
+ draft: null,
1233
+ ended: null,
1234
+ marked: [],
1073
1235
  more: false,
1074
1236
  total: null,
1075
1237
  };
@@ -1081,7 +1243,10 @@ export function createConversationStore(deps) {
1081
1243
  if (view.window !== null)
1082
1244
  return;
1083
1245
  const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
1246
+ view.draft = (await local.chatDraft(conversation)) ?? null;
1247
+ view.ended = await endedIn(conversation);
1084
1248
  view.window = messages;
1249
+ view.marked = await local.jobMarkers(conversation);
1085
1250
  view.more = more;
1086
1251
  view.total = asked.total ? await local.messageCount(conversation) : null;
1087
1252
  }), reading.signal)
@@ -1090,7 +1255,7 @@ export function createConversationStore(deps) {
1090
1255
  return;
1091
1256
  watcher.ready = true;
1092
1257
  const window = view.window ?? [];
1093
- watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
1258
+ watcher.notify(viewOf(conversation, window, sharedFrame(view, window), view.draft, view.ended, view.marked));
1094
1259
  })
1095
1260
  .catch(() => { });
1096
1261
  return () => {
@@ -1125,58 +1290,43 @@ export function createConversationStore(deps) {
1125
1290
  },
1126
1291
  receive(event) {
1127
1292
  if (deps.closed())
1128
- return false;
1129
- if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
1130
- if (event.kind === 'list') {
1131
- if (event.whole)
1132
- relisting ??= new Map();
1133
- for (const entry of event.entries) {
1134
- if (relisting === null)
1135
- listed(entry);
1136
- else
1137
- relisting.set(entry.conversation, { ...entry });
1138
- }
1139
- if (event.more)
1140
- return false;
1141
- if (relisting !== null) {
1142
- listing.clear();
1143
- for (const entry of relisting.values())
1144
- listed(entry);
1145
- relisting = null;
1146
- }
1147
- listCursor = event.cursor;
1148
- deps.stream()?.hold(event.cursor);
1149
- listKnown = true;
1150
- for (const conversation of replicated) {
1151
- if (!listing.has(conversation))
1152
- continue;
1153
- if (!missed(conversation))
1154
- behind.delete(conversation);
1155
- else if (!behind.has(conversation))
1156
- gap(conversation);
1157
- }
1158
- openingEnded();
1159
- }
1160
- else if (event.kind === 'listed') {
1161
- listed(event.entry);
1162
- relisting?.set(event.entry.conversation, { ...event.entry });
1163
- }
1164
- else {
1165
- listing.delete(event.conversation);
1166
- relisting?.delete(event.conversation);
1293
+ return;
1294
+ if (event.kind === 'list') {
1295
+ if (event.whole)
1296
+ relisting ??= new Map();
1297
+ if (relisting !== null) {
1298
+ for (const entry of event.entries)
1299
+ relisting.set(entry.conversation, { ...entry });
1167
1300
  }
1168
- keepList();
1169
- emitList();
1170
- return ((event.kind !== 'list' &&
1171
- listKnown &&
1172
- (listing.size !== replicated.size ||
1173
- [...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
1174
- (listKnown && [...drafts.keys()].some(retiredChat)));
1301
+ if (event.more && relisting !== null)
1302
+ return;
1303
+ const whole = relisting;
1304
+ relisting = null;
1305
+ listChanged({
1306
+ replacing: whole !== null,
1307
+ set: whole === null ? event.entries : [...whole.values()],
1308
+ gone: null,
1309
+ ...(event.more ? { paged: true } : { cursor: event.cursor }),
1310
+ });
1311
+ return;
1312
+ }
1313
+ if (event.kind === 'listed') {
1314
+ relisting?.set(event.entry.conversation, { ...event.entry });
1315
+ listChanged({ replacing: false, set: [event.entry], gone: null });
1316
+ return;
1317
+ }
1318
+ if (event.kind === 'unlisted') {
1319
+ relisting?.delete(event.conversation);
1320
+ listChanged({ replacing: false, set: [], gone: event.conversation });
1321
+ return;
1175
1322
  }
1176
1323
  heard(event);
1177
- return false;
1178
1324
  },
1179
1325
  close() {
1326
+ closing.abort();
1327
+ if (rowTimer !== null)
1328
+ clearTimeout(rowTimer);
1329
+ rowTimer = null;
1180
1330
  views.clear();
1181
1331
  lives.clear();
1182
1332
  unsent.clear();