@agentshouse/mdruntime 0.1.0-ca6edc1abd618 → 0.1.0-ca92b54804c33

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 (149) hide show
  1. package/README.md +2 -2
  2. package/dist/adapter.d.ts +101 -70
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +3 -10
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +29 -47
  7. package/dist/bootstrap.d.ts +16 -14
  8. package/dist/bootstrap.d.ts.map +1 -1
  9. package/dist/bootstrap.js +272 -129
  10. package/dist/chain.d.ts +2 -1
  11. package/dist/chain.d.ts.map +1 -1
  12. package/dist/change.d.ts +1 -0
  13. package/dist/change.d.ts.map +1 -1
  14. package/dist/client.d.ts +13 -9
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +54 -6
  17. package/dist/collection.d.ts +7 -4
  18. package/dist/collection.d.ts.map +1 -1
  19. package/dist/collection.js +23 -17
  20. package/dist/compile.d.ts +1 -6
  21. package/dist/compile.d.ts.map +1 -1
  22. package/dist/compile.js +108 -243
  23. package/dist/connection.d.ts +14 -11
  24. package/dist/connection.d.ts.map +1 -1
  25. package/dist/connection.js +2264 -1933
  26. package/dist/context.d.ts +2 -0
  27. package/dist/context.d.ts.map +1 -0
  28. package/dist/context.js +7 -0
  29. package/dist/conversation.d.ts +21 -17
  30. package/dist/conversation.d.ts.map +1 -1
  31. package/dist/conversation.js +405 -253
  32. package/dist/durability.d.ts +6 -1
  33. package/dist/durability.d.ts.map +1 -1
  34. package/dist/entry.d.ts +3 -2
  35. package/dist/entry.d.ts.map +1 -1
  36. package/dist/entry.js +18 -8
  37. package/dist/file.d.ts +2 -11
  38. package/dist/file.d.ts.map +1 -1
  39. package/dist/file.js +6 -4
  40. package/dist/host.d.ts +3 -7
  41. package/dist/host.d.ts.map +1 -1
  42. package/dist/host.js +26 -29
  43. package/dist/identity.d.ts +0 -1
  44. package/dist/identity.d.ts.map +1 -1
  45. package/dist/identity.js +0 -3
  46. package/dist/index.d.ts +12 -9
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +4 -1
  49. package/dist/job.d.ts +14 -0
  50. package/dist/job.d.ts.map +1 -0
  51. package/dist/job.js +28 -0
  52. package/dist/live.d.ts +5 -4
  53. package/dist/live.d.ts.map +1 -1
  54. package/dist/live.js +62 -20
  55. package/dist/media.d.ts +8 -0
  56. package/dist/media.d.ts.map +1 -0
  57. package/dist/media.js +218 -0
  58. package/dist/memory.d.ts +12 -5
  59. package/dist/memory.d.ts.map +1 -1
  60. package/dist/memory.js +216 -85
  61. package/dist/messages.d.ts +3 -3
  62. package/dist/messages.d.ts.map +1 -1
  63. package/dist/messages.js +20 -19
  64. package/dist/model.d.ts +8 -10
  65. package/dist/model.d.ts.map +1 -1
  66. package/dist/model.js +5 -17
  67. package/dist/observation.d.ts +3 -2
  68. package/dist/observation.d.ts.map +1 -1
  69. package/dist/observation.js +8 -3
  70. package/dist/outcome.d.ts +13 -5
  71. package/dist/outcome.d.ts.map +1 -1
  72. package/dist/outcome.js +16 -2
  73. package/dist/overlay.d.ts +1 -0
  74. package/dist/overlay.d.ts.map +1 -1
  75. package/dist/overlay.js +11 -0
  76. package/dist/paths.d.ts +1 -0
  77. package/dist/paths.d.ts.map +1 -1
  78. package/dist/paths.js +4 -0
  79. package/dist/query.d.ts +8 -207
  80. package/dist/query.d.ts.map +1 -1
  81. package/dist/query.js +3 -505
  82. package/dist/reach.d.ts +18 -11
  83. package/dist/reach.d.ts.map +1 -1
  84. package/dist/reach.js +195 -126
  85. package/dist/react.d.ts +3 -1
  86. package/dist/react.d.ts.map +1 -1
  87. package/dist/readiness.d.ts +3 -17
  88. package/dist/readiness.d.ts.map +1 -1
  89. package/dist/readiness.js +4 -10
  90. package/dist/recovery.d.ts +1 -0
  91. package/dist/recovery.d.ts.map +1 -1
  92. package/dist/recovery.js +35 -1
  93. package/dist/refresh.d.ts +7 -5
  94. package/dist/refresh.d.ts.map +1 -1
  95. package/dist/refresh.js +33 -5
  96. package/dist/replica.d.ts +10 -8
  97. package/dist/replica.d.ts.map +1 -1
  98. package/dist/replica.js +52 -20
  99. package/dist/room-draft.d.ts +21 -5
  100. package/dist/room-draft.d.ts.map +1 -1
  101. package/dist/room-draft.js +98 -51
  102. package/dist/rows.d.ts +18 -16
  103. package/dist/rows.d.ts.map +1 -1
  104. package/dist/rows.js +45 -153
  105. package/dist/save.d.ts +2 -0
  106. package/dist/save.d.ts.map +1 -1
  107. package/dist/save.js +22 -13
  108. package/dist/search.d.ts +12 -63
  109. package/dist/search.d.ts.map +1 -1
  110. package/dist/search.js +9 -52
  111. package/dist/shared.d.ts +0 -1
  112. package/dist/shared.d.ts.map +1 -1
  113. package/dist/shared.js +0 -10
  114. package/dist/status.d.ts +4 -5
  115. package/dist/status.d.ts.map +1 -1
  116. package/dist/status.js +3 -7
  117. package/dist/store.d.ts +101 -61
  118. package/dist/store.d.ts.map +1 -1
  119. package/dist/store.js +153 -161
  120. package/dist/stored.d.ts +66 -55
  121. package/dist/stored.d.ts.map +1 -1
  122. package/dist/stored.js +341 -273
  123. package/dist/tables.d.ts +90 -63
  124. package/dist/tables.d.ts.map +1 -1
  125. package/dist/tables.js +174 -98
  126. package/dist/transfer.d.ts +0 -5
  127. package/dist/transfer.d.ts.map +1 -1
  128. package/dist/validation.d.ts +3 -4
  129. package/dist/validation.d.ts.map +1 -1
  130. package/dist/validation.js +42 -100
  131. package/dist/windows.d.ts +1 -12
  132. package/dist/windows.d.ts.map +1 -1
  133. package/dist/windows.js +2 -75
  134. package/dist/wire.d.ts +17 -8
  135. package/dist/wire.d.ts.map +1 -1
  136. package/dist/wire.js +5 -2
  137. package/package.json +2 -2
  138. package/dist/fence.d.ts +0 -6
  139. package/dist/fence.d.ts.map +0 -1
  140. package/dist/fence.js +0 -10
  141. package/dist/fulltext.d.ts +0 -27
  142. package/dist/fulltext.d.ts.map +0 -1
  143. package/dist/fulltext.js +0 -368
  144. package/dist/reads.d.ts +0 -11
  145. package/dist/reads.d.ts.map +0 -1
  146. package/dist/reads.js +0 -100
  147. package/dist/words.d.ts +0 -19
  148. package/dist/words.d.ts.map +0 -1
  149. package/dist/words.js +0 -150
@@ -1,10 +1,11 @@
1
+ import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
1
2
  import { conversationChangeSet } from './change-set.js';
2
3
  import { runtimeError } from './failure.js';
3
4
  import { refusedForSpace } from './durability.js';
4
- import { declaredFile } from './file.js';
5
5
  import { changeBatchIdentity, chatDraftRevision } from './identity.js';
6
6
  import { OFFLINE } from './outcome.js';
7
- import { capped, cursorOf, decodedCursor, FROM_START, messageOrder, precedes } from './windows.js';
7
+ import { untilAnswered } from './recovery.js';
8
+ import { capped, FROM_START, messageOrder, precedes } from './windows.js';
8
9
  const QUIET = {
9
10
  turn: null,
10
11
  questions: [],
@@ -12,6 +13,7 @@ const QUIET = {
12
13
  commands: [],
13
14
  options: [],
14
15
  starting: null,
16
+ subagents: [],
15
17
  link: null,
16
18
  deliveryUnknown: [],
17
19
  };
@@ -21,6 +23,7 @@ const FACTS = [
21
23
  'commands',
22
24
  'options',
23
25
  'starting',
26
+ 'subagents',
24
27
  'link',
25
28
  'deliveryUnknown',
26
29
  ];
@@ -31,9 +34,11 @@ export function unheldView(conversation) {
31
34
  turn: null,
32
35
  questions: [],
33
36
  process: null,
37
+ processEnded: null,
34
38
  commands: [],
35
39
  options: [],
36
40
  starting: null,
41
+ subagents: [],
37
42
  link: null,
38
43
  deliveryUnknown: [],
39
44
  chatDraft: null,
@@ -48,10 +53,8 @@ export function newChat(agent) {
48
53
  function creationOf(input) {
49
54
  if (typeof input !== 'object' || input === null)
50
55
  return null;
51
- const { agent_id: agent, operation_id: operation, replaces, } = input;
52
- return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
53
- ? { agent, operation }
54
- : null;
56
+ const { agent_id: agent, operation_id: operation } = input;
57
+ return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
55
58
  }
56
59
  function startedTurn(turn, operation) {
57
60
  if (operation.op === 'begin')
@@ -210,7 +213,14 @@ function askedOf(options) {
210
213
  }
211
214
  return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
212
215
  }
213
- const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
216
+ function phraseOf(text) {
217
+ if (text === undefined)
218
+ return null;
219
+ if (typeof text !== 'string' || text === '') {
220
+ throw runtimeError('invalid-text', 'a conversation is found by a phrase');
221
+ }
222
+ return text;
223
+ }
214
224
  const DURABLE_WRITE = {
215
225
  kind: 'rejected',
216
226
  code: 'durable-write',
@@ -244,10 +254,6 @@ function returned(draft, submission, others) {
244
254
  }, others);
245
255
  }
246
256
  export function createConversationStore(deps) {
247
- const stored = new Set();
248
- const reads = new Map();
249
- const partials = new Set();
250
- const drafts = new Map();
251
257
  const sending = new Map();
252
258
  const behind = new Set();
253
259
  const catching = new Map();
@@ -255,21 +261,23 @@ export function createConversationStore(deps) {
255
261
  const lives = new Map();
256
262
  const unsent = new Map();
257
263
  const views = new Map();
258
- const listing = new Map();
264
+ const heardNewest = new Map();
265
+ let relisting = null;
259
266
  const creations = new Set();
260
267
  const listWatchers = new Set();
268
+ const unlisted = new Set();
261
269
  let listKnown = false;
262
270
  const agentWatchers = new Set();
263
- let agentNames = new Map();
264
- let retiredAgents = new Set();
265
- let agentsRead = null;
271
+ const unnamed = new Set();
272
+ let agentsShown = null;
273
+ let listedAgents = null;
274
+ let agentsReading = false;
266
275
  let replicated = new Set();
267
276
  let handedReplicated;
268
277
  function liveOf(conversation) {
269
278
  return lives.get(conversation) ?? QUIET;
270
279
  }
271
- function viewOf(conversation, held, frame) {
272
- const draft = drafts.get(conversation);
280
+ function viewOf(conversation, held, frame, draft, ended) {
273
281
  const facts = liveOf(conversation);
274
282
  return {
275
283
  conversation,
@@ -283,14 +291,14 @@ export function createConversationStore(deps) {
283
291
  turn: structuredClone(facts.turn),
284
292
  questions: structuredClone(facts.questions),
285
293
  process: facts.process === null ? null : { ...facts.process },
294
+ processEnded: ended,
286
295
  commands: structuredClone(facts.commands),
287
296
  options: structuredClone(facts.options),
288
297
  starting: facts.starting,
298
+ subagents: structuredClone(facts.subagents),
289
299
  link: facts.link === null ? null : { ...facts.link },
290
300
  deliveryUnknown: [...facts.deliveryUnknown],
291
- chatDraft: draft === undefined || unwritten(draft)
292
- ? null
293
- : { text: draft.text, revision: draft.revision },
301
+ chatDraft: draft === null || unwritten(draft) ? null : { text: draft.text, revision: draft.revision },
294
302
  };
295
303
  }
296
304
  function emit(conversation) {
@@ -302,7 +310,7 @@ export function createConversationStore(deps) {
302
310
  const ready = [...view.watchers].filter((watcher) => watcher.ready);
303
311
  if (ready.length === 0)
304
312
  continue;
305
- const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
313
+ const value = viewOf(conversation, view.window, sharedFrame(view, view.window), view.draft, view.ended);
306
314
  for (const watcher of ready)
307
315
  watcher.notify(value);
308
316
  }
@@ -338,10 +346,10 @@ export function createConversationStore(deps) {
338
346
  return 'asking';
339
347
  return facts.turn === null ? 'idle' : 'working';
340
348
  }
341
- function entries() {
342
- return [...listing.values()].map((entry) => ({
349
+ function entries(list) {
350
+ return list.map((entry) => ({
343
351
  conversation: entry.conversation,
344
- agent: { identity: entry.agent, name: agentNames.get(entry.agent) ?? null },
352
+ agent: { identity: entry.agent, name: entry.agentName },
345
353
  name: entry.name,
346
354
  created: entry.created,
347
355
  finished: entry.finished,
@@ -351,27 +359,160 @@ export function createConversationStore(deps) {
351
359
  started: lives.get(entry.conversation)?.turn?.started ?? null,
352
360
  }));
353
361
  }
362
+ async function heldList() {
363
+ const stored = (await (await deps.local()).facts()).conversationList;
364
+ if (stored !== null)
365
+ return stored;
366
+ return !listKnown && deps.stream() !== undefined && deps.online() ? null : [];
367
+ }
368
+ async function endedIn(conversation) {
369
+ const list = await heldList();
370
+ return list?.find((entry) => entry.conversation === conversation)?.processEnded ?? null;
371
+ }
372
+ function listChanged(change) {
373
+ void deps
374
+ .durably(async (local) => {
375
+ const { conversationList, conversationCursor } = await local.facts();
376
+ const kept = change.replacing ? [] : (conversationList ?? []);
377
+ const held = new Map(kept.map((entry) => [entry.conversation, entry]));
378
+ for (const entry of change.set) {
379
+ held.set(entry.conversation, { ...entry });
380
+ creations.delete(entry.conversation);
381
+ }
382
+ if (change.gone !== null)
383
+ held.delete(change.gone);
384
+ const list = [...held.values()];
385
+ if (JSON.stringify(list) !== JSON.stringify(conversationList)) {
386
+ await local.fact('conversationList', list);
387
+ }
388
+ if (change.cursor !== undefined) {
389
+ if (change.cursor !== conversationCursor) {
390
+ await local.fact('conversationCursor', change.cursor);
391
+ }
392
+ deps.stream()?.hold(change.cursor);
393
+ }
394
+ return list;
395
+ })
396
+ .catch(async () => (await (await deps.local()).facts()).conversationList ?? [])
397
+ .then((list) => {
398
+ if (deps.closed() || change.paged === true)
399
+ return;
400
+ endedAnew(list);
401
+ if (change.cursor !== undefined) {
402
+ listKnown = true;
403
+ settleBehind(list);
404
+ openingEnded();
405
+ deps.settled();
406
+ }
407
+ emitList();
408
+ if (!listKnown)
409
+ return;
410
+ retiredDrafts(list);
411
+ if (change.cursor === undefined &&
412
+ (list.length !== replicated.size ||
413
+ list.some(({ conversation }) => !replicated.has(conversation)))) {
414
+ deps.again();
415
+ }
416
+ }, () => { });
417
+ }
418
+ function endedAnew(list) {
419
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
420
+ for (const view of views.values()) {
421
+ const ended = entries.get(view.conversation)?.processEnded ?? null;
422
+ if (view.window === null || ended === view.ended)
423
+ continue;
424
+ view.ended = ended;
425
+ emit(view.conversation);
426
+ }
427
+ }
428
+ let listShown = Promise.resolve();
429
+ function shownList(work) {
430
+ const reading = heldList();
431
+ listShown = listShown.then(async () => {
432
+ const list = await reading.catch(() => null);
433
+ if (list === null || deps.closed())
434
+ return;
435
+ work(list);
436
+ });
437
+ }
354
438
  function emitList() {
355
- if (deps.closed() || listWatchers.size === 0)
439
+ if (deps.closed())
356
440
  return;
357
- const value = entries();
358
- for (const watcher of listWatchers)
359
- watcher(value);
441
+ if (listWatchers.size > 0) {
442
+ shownList((list) => {
443
+ const value = entries(list);
444
+ for (const watcher of listWatchers)
445
+ watcher(value);
446
+ });
447
+ }
448
+ emitAgents();
449
+ learned();
450
+ }
451
+ function learned() {
452
+ if (deps.closed() || (unlisted.size === 0 && unnamed.size === 0))
453
+ return;
454
+ shownList((list) => {
455
+ if (unlisted.size > 0) {
456
+ const value = entries(list);
457
+ for (const watcher of [...unlisted]) {
458
+ unlisted.delete(watcher);
459
+ listWatchers.add(watcher);
460
+ watcher(value);
461
+ }
462
+ }
463
+ if (unnamed.size > 0) {
464
+ const value = agents(list);
465
+ for (const watcher of [...unnamed]) {
466
+ unnamed.delete(watcher);
467
+ agentWatchers.add(watcher);
468
+ watcher(value);
469
+ }
470
+ }
471
+ });
360
472
  }
361
- function agents() {
362
- return [...agentNames]
363
- .filter(([identity]) => !retiredAgents.has(identity))
364
- .map(([identity, name]) => ({ identity, name }))
473
+ function agents(list) {
474
+ const named = new Map();
475
+ for (const agent of listedAgents ?? [])
476
+ named.set(agent.agent, agent);
477
+ for (const entry of list) {
478
+ named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
479
+ }
480
+ return [...named]
481
+ .filter(([, agent]) => !agent.retired)
482
+ .map(([identity, agent]) => ({ identity, name: agent.name }))
365
483
  .sort((left, right) => left.name === right.name
366
484
  ? left.identity.localeCompare(right.identity)
367
485
  : left.name.localeCompare(right.name));
368
486
  }
487
+ function readAgents() {
488
+ const stream = deps.stream();
489
+ if (stream?.agents === undefined || !deps.online() || agentsReading)
490
+ return;
491
+ agentsReading = true;
492
+ void stream
493
+ .agents()
494
+ .then((read) => {
495
+ if (read === null)
496
+ return;
497
+ listedAgents = read;
498
+ emitAgents();
499
+ }, () => { })
500
+ .finally(() => {
501
+ agentsReading = false;
502
+ });
503
+ }
369
504
  function emitAgents() {
370
505
  if (deps.closed() || agentWatchers.size === 0)
371
506
  return;
372
- const value = agents();
373
- for (const watcher of agentWatchers)
374
- watcher(value);
507
+ shownList((list) => {
508
+ const value = agents(list);
509
+ const shown = JSON.stringify(value);
510
+ if (shown === agentsShown)
511
+ return;
512
+ agentsShown = shown;
513
+ for (const watcher of agentWatchers)
514
+ watcher(value);
515
+ });
375
516
  }
376
517
  function setLive(conversation, next) {
377
518
  const before = stateOf(conversation);
@@ -380,60 +521,14 @@ export function createConversationStore(deps) {
380
521
  lives.delete(conversation);
381
522
  else
382
523
  lives.set(conversation, next);
383
- if (!listing.has(conversation))
384
- return;
385
524
  if (stateOf(conversation) !== before || next?.turn?.started !== began)
386
525
  emitList();
387
526
  }
388
- async function readAgents() {
389
- const declaration = deps.agents();
390
- if (declaration === undefined)
391
- return;
392
- const names = new Map();
393
- const retired = new Set();
394
- let covered = null;
395
- for (let after;;) {
396
- const read = await deps.documents(declaration, after);
397
- if ('pending' in read)
398
- return;
399
- const coverage = JSON.stringify(read.coverage);
400
- if (covered !== null && coverage !== covered)
401
- return readAgents();
402
- covered = coverage;
403
- for (const document of read.documents) {
404
- const identity = document.fields?.[declaration.identity];
405
- const name = document.fields?.[declaration.name];
406
- if (typeof identity !== 'string' || typeof name !== 'string')
407
- continue;
408
- names.set(identity, name);
409
- const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
410
- if (ended !== undefined && ended !== null)
411
- retired.add(identity);
412
- }
413
- if (read.next === undefined)
414
- break;
415
- after = read.next;
416
- }
417
- const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
418
- if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
419
- return;
420
- agentNames = names;
421
- retiredAgents = retired;
422
- if (named)
423
- emitList();
424
- emitAgents();
425
- const ended = [...drafts.keys()].filter(retiredChat);
426
- if (ended.length > 0)
427
- await remove(ended);
428
- }
429
- function retiredChat(conversation) {
430
- return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
431
- }
432
- function agentsReady() {
433
- agentsRead ??= readAgents().catch(() => {
434
- agentsRead = null;
435
- });
436
- return agentsRead;
527
+ function retiredChat(conversation, list) {
528
+ if (!conversation.startsWith(NEW_CHAT))
529
+ return false;
530
+ const agent = conversation.slice(NEW_CHAT.length);
531
+ return list.some((entry) => entry.agent === agent && entry.agentRetired);
437
532
  }
438
533
  function live(conversation, change) {
439
534
  const owed = unsent.get(conversation);
@@ -456,6 +551,7 @@ export function createConversationStore(deps) {
456
551
  commands: facts.has('commands') ? QUIET.commands : held.commands,
457
552
  options: facts.has('options') ? QUIET.options : held.options,
458
553
  starting: facts.has('starting') ? QUIET.starting : held.starting,
554
+ subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
459
555
  link: facts.has('link') ? QUIET.link : held.link,
460
556
  deliveryUnknown: facts.has('deliveryUnknown')
461
557
  ? QUIET.deliveryUnknown
@@ -490,17 +586,23 @@ export function createConversationStore(deps) {
490
586
  }
491
587
  async function remove(leaving) {
492
588
  await deps.durably(async (local) => {
589
+ const kept = new Set([
590
+ ...(await local.conversations(leaving)).map(({ conversation }) => conversation),
591
+ ...(await local.chatDrafts())
592
+ .filter(({ conversation }) => leaving.includes(conversation))
593
+ .map(({ conversation }) => conversation),
594
+ ]);
493
595
  await local.dropConversations(leaving);
494
596
  const changed = [];
495
597
  for (const conversation of leaving) {
496
- if (stored.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
598
+ if (kept.has(conversation) || lives.has(conversation))
497
599
  changed.push(conversation);
600
+ for (const view of views.values()) {
601
+ if (view.conversation === conversation)
602
+ view.draft = null;
498
603
  }
499
- stored.delete(conversation);
500
- reads.delete(conversation);
501
- partials.delete(conversation);
502
- drafts.delete(conversation);
503
604
  behind.delete(conversation);
605
+ heardNewest.delete(conversation);
504
606
  unsent.delete(conversation);
505
607
  setLive(conversation, null);
506
608
  await slide(local, conversation, null);
@@ -509,6 +611,69 @@ export function createConversationStore(deps) {
509
611
  deps.publish(conversationChangeSet(changed));
510
612
  });
511
613
  }
614
+ function newestOfListed(entry) {
615
+ if (entry === undefined)
616
+ return undefined;
617
+ const heard = heardNewest.get(entry.conversation);
618
+ if (heard === undefined)
619
+ return entry.newest;
620
+ return entry.newest === null || heard > entry.newest ? heard : entry.newest;
621
+ }
622
+ function missed(conversation, held, entry) {
623
+ const read = held?.read;
624
+ const newest = newestOfListed(entry);
625
+ return (read === undefined ||
626
+ newest === undefined ||
627
+ held?.partial === true ||
628
+ (newest !== null && (read === null || newest > read)));
629
+ }
630
+ async function readsOf(local, conversations) {
631
+ return new Map((await local.conversations(conversations)).map((held) => [held.conversation, held]));
632
+ }
633
+ function retiredDrafts(list) {
634
+ if (!list.some((entry) => entry.agentRetired))
635
+ return;
636
+ void deps
637
+ .local()
638
+ .then((local) => local.chatDrafts())
639
+ .then((held) => {
640
+ if (!deps.closed() && held.some(({ conversation }) => retiredChat(conversation, list))) {
641
+ deps.again();
642
+ }
643
+ }, () => { });
644
+ }
645
+ function settleBehind(list) {
646
+ void deps
647
+ .durably(async (local) => {
648
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
649
+ const named = [...replicated].filter((conversation) => entries.has(conversation));
650
+ const held = await readsOf(local, named);
651
+ let owed = false;
652
+ for (const conversation of named) {
653
+ if (!missed(conversation, held.get(conversation), entries.get(conversation))) {
654
+ behind.delete(conversation);
655
+ }
656
+ else if (!behind.has(conversation)) {
657
+ gap(conversation);
658
+ owed = true;
659
+ }
660
+ }
661
+ return owed;
662
+ })
663
+ .then((owed) => {
664
+ if (deps.closed())
665
+ return;
666
+ deps.settled();
667
+ if (owed)
668
+ deps.again();
669
+ }, () => { });
670
+ }
671
+ function noted(conversation, message) {
672
+ const heard = heardNewest.get(conversation);
673
+ if (heard === undefined || message.position > heard) {
674
+ heardNewest.set(conversation, message.position);
675
+ }
676
+ }
512
677
  function gap(conversation) {
513
678
  gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
514
679
  behind.add(conversation);
@@ -517,7 +682,7 @@ export function createConversationStore(deps) {
517
682
  return deps.durably(async (local) => {
518
683
  if (!replicated.has(conversation))
519
684
  return;
520
- const read = reads.get(conversation);
685
+ const read = (await readsOf(local, [conversation])).get(conversation)?.read;
521
686
  const advances = read !== undefined &&
522
687
  !behind.has(conversation) &&
523
688
  (read === null || message.position > read);
@@ -527,9 +692,6 @@ export function createConversationStore(deps) {
527
692
  messages: [message],
528
693
  ...(advances ? { read: { at: message.position, partial: false } } : {}),
529
694
  });
530
- stored.add(conversation);
531
- if (advances)
532
- reads.set(conversation, message.position);
533
695
  sealTurns(conversation, new Set([message.turn]));
534
696
  await slide(local, conversation, [message]);
535
697
  deps.publish(conversationChangeSet([conversation]));
@@ -537,13 +699,14 @@ export function createConversationStore(deps) {
537
699
  }
538
700
  async function readHistories(conversations, ended) {
539
701
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
702
+ const held = await readsOf(await deps.local(), conversations);
540
703
  let waiting = conversations.map((conversation) => ({
541
704
  conversation,
542
- after: reads.get(conversation) ?? undefined,
705
+ after: held.get(conversation)?.read ?? undefined,
543
706
  }));
544
707
  while (waiting.length > 0) {
545
708
  const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
546
- const result = await deps.call('conversation.history', {
709
+ const result = await deps.history({
547
710
  conversations: asking.map(({ conversation, after }) => ({
548
711
  conversation,
549
712
  ...(after === undefined ? {} : { after }),
@@ -595,15 +758,10 @@ export function createConversationStore(deps) {
595
758
  return;
596
759
  const whole = next === undefined;
597
760
  const cursor = next ?? newestOf(arriving, from);
598
- const partial = !whole && (partials.has(conversation) || !reads.has(conversation));
761
+ const held = (await readsOf(local, [conversation])).get(conversation);
762
+ const partial = !whole && (held?.partial === true || held?.read === undefined);
599
763
  const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
600
764
  await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
601
- stored.add(conversation);
602
- reads.set(conversation, cursor);
603
- if (partial)
604
- partials.add(conversation);
605
- else
606
- partials.delete(conversation);
607
765
  sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
608
766
  if (whole && (gaps.get(conversation) ?? 0) === generation)
609
767
  behind.delete(conversation);
@@ -646,6 +804,10 @@ export function createConversationStore(deps) {
646
804
  live(conversation, { starting: event.step });
647
805
  return;
648
806
  }
807
+ if (event.kind === 'subagents') {
808
+ live(conversation, { subagents: structuredClone(event.subagents) });
809
+ return;
810
+ }
649
811
  if (event.kind === 'link') {
650
812
  live(conversation, { link: { connected: event.connected } });
651
813
  return;
@@ -657,6 +819,7 @@ export function createConversationStore(deps) {
657
819
  const arriving = acceptedMessage(event.message);
658
820
  if (arriving === null)
659
821
  return;
822
+ noted(conversation, arriving);
660
823
  if (replicated.has(conversation)) {
661
824
  if (deps.full()) {
662
825
  gap(conversation);
@@ -672,17 +835,24 @@ export function createConversationStore(deps) {
672
835
  emit(conversation);
673
836
  }
674
837
  async function kept(local, conversation, following) {
675
- if (following === null || empty(following)) {
838
+ const shown = following === null || empty(following) ? null : following;
839
+ if (shown === null)
676
840
  await local.dropChatDraft(conversation);
677
- drafts.delete(conversation);
678
- }
679
- else {
680
- await local.keepChatDrafts([following]);
681
- drafts.set(conversation, following);
841
+ else
842
+ await local.keepChatDrafts([shown]);
843
+ shownDraft(conversation, shown);
844
+ }
845
+ function shownDraft(conversation, draft) {
846
+ for (const view of views.values()) {
847
+ if (view.conversation === conversation)
848
+ view.draft = draft;
682
849
  }
683
850
  }
851
+ async function draftAt(local, conversation) {
852
+ return (await local.chatDraft(conversation)) ?? undefined;
853
+ }
684
854
  async function sealed(local, conversation, turns) {
685
- const held = drafts.get(conversation);
855
+ const held = await draftAt(local, conversation);
686
856
  const submitted = held?.submitted ?? [];
687
857
  const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
688
858
  if (held === undefined || left.length === submitted.length)
@@ -693,8 +863,8 @@ export function createConversationStore(deps) {
693
863
  function moved(agent, operation, to) {
694
864
  const from = newChat(agent);
695
865
  return deps.durably(async (local) => {
696
- const held = drafts.get(from);
697
- const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
866
+ const held = await draftAt(local, from);
867
+ const earlier = held?.moves ?? [];
698
868
  if (earlier.includes(operation))
699
869
  return;
700
870
  const blank = {
@@ -703,18 +873,20 @@ export function createConversationStore(deps) {
703
873
  revision: chatDraftRevision(),
704
874
  };
705
875
  const { moves: _answered, ...unsent } = held ?? blank;
706
- const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
876
+ const taken = (await draftAt(local, to)) !== undefined || empty(unsent)
877
+ ? []
878
+ : [{ ...unsent, conversation: to }];
707
879
  const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
708
880
  await local.keepChatDrafts([left, ...taken]);
709
- drafts.set(from, left);
881
+ shownDraft(from, left);
710
882
  for (const one of taken)
711
- drafts.set(to, one);
883
+ shownDraft(to, one);
712
884
  deps.publish(conversationChangeSet([from, to]));
713
885
  });
714
886
  }
715
887
  function withDraft(conversation, next) {
716
888
  return deps.durably(async (local) => {
717
- const following = await next(drafts.get(conversation), local);
889
+ const following = await next(await draftAt(local, conversation), local);
718
890
  if (following === undefined)
719
891
  return;
720
892
  await kept(local, conversation, following);
@@ -723,10 +895,8 @@ export function createConversationStore(deps) {
723
895
  }
724
896
  async function submitted(conversation, operation, draft) {
725
897
  let submission;
726
- let earlier = false;
727
898
  await withDraft(conversation, (current) => {
728
899
  submission = current?.submitted?.find((one) => one.operation === operation);
729
- earlier = submission !== undefined;
730
900
  if (submission !== undefined)
731
901
  return undefined;
732
902
  const said = draft ?? current;
@@ -739,23 +909,15 @@ export function createConversationStore(deps) {
739
909
  submitted: [...(current?.submitted ?? []), submission],
740
910
  };
741
911
  });
742
- return { submission: submission, earlier };
743
- }
744
- function forgotten(conversation, submission) {
745
- return withDraft(conversation, (current) => {
746
- const held = current?.submitted ?? [];
747
- if (current === undefined || !held.includes(submission))
748
- return undefined;
749
- return withSubmitted(current, held.filter((one) => one !== submission));
750
- });
912
+ return submission;
751
913
  }
752
914
  async function dispatched(conversation, submission) {
753
915
  const dispatch = { answered: false };
754
916
  sending.set(submission.operation, dispatch);
755
917
  try {
756
- const result = await invoke('conversation.send', conversation, {
757
- text: submission.text,
918
+ const result = await invoke('conversations/send', conversation, {
758
919
  operation_id: submission.operation,
920
+ text: submission.text,
759
921
  });
760
922
  dispatch.answered = result.kind !== 'transport-uncertainty';
761
923
  if (result.kind === 'revoked-authorization')
@@ -764,7 +926,7 @@ export function createConversationStore(deps) {
764
926
  deps.unanswered();
765
927
  return result;
766
928
  }
767
- const turn = answered(result, 'turn');
929
+ const turn = answered(result, 'message_id');
768
930
  await withDraft(conversation, async (current, local) => {
769
931
  const held = current?.submitted?.find((one) => one.operation === submission.operation);
770
932
  if (current === undefined || held === undefined || held.turn !== null)
@@ -784,29 +946,40 @@ export function createConversationStore(deps) {
784
946
  sending.delete(submission.operation);
785
947
  }
786
948
  }
787
- function invoke(name, conversation, extra = {}) {
788
- return deps.call(name, { conversation, ...extra });
949
+ function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
950
+ return deps.call(name, { conversation_id: conversation, ...extra });
789
951
  }
790
952
  const surface = {
791
- async list() {
792
- await agentsReady();
793
- return entries();
953
+ list() {
954
+ return new Promise((settle) => {
955
+ const answer = (listed) => {
956
+ unlisted.delete(answer);
957
+ listWatchers.delete(answer);
958
+ settle(listed);
959
+ };
960
+ unlisted.add(answer);
961
+ learned();
962
+ });
794
963
  },
795
964
  async read(conversation, options = {}) {
796
- const asked = askedOf(options);
965
+ const { text, ...windowed } = options;
966
+ const asked = askedOf(windowed);
967
+ const phrase = phraseOf(text);
797
968
  return deps.durably(async (local) => {
798
- const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
799
- const total = asked.total ? await local.messageCount(conversation) : null;
800
- return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
969
+ const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
970
+ const total = asked.total ? await local.messageCount(conversation, phrase) : null;
971
+ return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total), (await local.chatDraft(conversation)) ?? null, await endedIn(conversation)));
801
972
  });
802
973
  },
803
974
  async create(input) {
804
- const result = await deps.call('conversation.create', input);
805
- const conversation = answered(result, 'conversation');
975
+ const result = await deps.call('conversations/create', input);
976
+ const conversation = answered(result, 'conversation_id');
806
977
  if (conversation === null)
807
978
  return result;
808
- if (!listing.has(conversation))
979
+ const listed = (await heldList()) ?? [];
980
+ if (!listed.some((entry) => entry.conversation === conversation)) {
809
981
  creations.add(conversation);
982
+ }
810
983
  const creation = creationOf(input);
811
984
  if (creation !== null)
812
985
  await moved(creation.agent, creation.operation, conversation);
@@ -823,11 +996,7 @@ export function createConversationStore(deps) {
823
996
  });
824
997
  if (held === null)
825
998
  return DURABLE_WRITE;
826
- const { submission, earlier } = held;
827
- if (!earlier || !deps.expired(operation))
828
- return dispatched(conversation, submission);
829
- await forgotten(conversation, submission);
830
- return EXPIRED;
999
+ return dispatched(conversation, held);
831
1000
  }
832
1001
  if (draft !== undefined) {
833
1002
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
@@ -836,38 +1005,20 @@ export function createConversationStore(deps) {
836
1005
  }
837
1006
  return OFFLINE;
838
1007
  },
839
- async attach(room, file) {
840
- if (!deps.online())
841
- return OFFLINE;
842
- return deps.call('conversation.attach', {
843
- room,
844
- ...(await declaredFile(file)),
845
- blob: file.blob,
846
- });
847
- },
848
1008
  answer(conversation, answer) {
849
- return invoke('conversation.answer', conversation, { answer });
1009
+ return invoke('conversations/interaction/answer', conversation, {
1010
+ operation_id: changeBatchIdentity(),
1011
+ ...answer,
1012
+ });
850
1013
  },
851
1014
  interrupt(conversation) {
852
- return invoke('conversation.interrupt', conversation);
1015
+ return invoke('conversations/interrupt', conversation);
853
1016
  },
854
1017
  kill(conversation) {
855
- return invoke('conversation.kill', conversation);
856
- },
857
- async cancel(conversation, turn) {
858
- const result = await invoke('conversation.cancel', conversation, { turn });
859
- if (result.kind !== 'action')
860
- return result;
861
- await withDraft(conversation, (current) => {
862
- const held = current?.submitted?.find((one) => one.turn === turn);
863
- if (current === undefined || held === undefined)
864
- return undefined;
865
- return returned(current, held, current.submitted.filter((one) => one !== held));
866
- });
867
- return result;
1018
+ return invoke('conversations/kill', conversation);
868
1019
  },
869
1020
  delete(conversation) {
870
- return invoke('conversation.delete', conversation);
1021
+ return invoke('conversations/delete', conversation);
871
1022
  },
872
1023
  };
873
1024
  const chatDraft = {
@@ -896,33 +1047,40 @@ export function createConversationStore(deps) {
896
1047
  return {
897
1048
  surface,
898
1049
  chatDraft,
899
- hydrate(held, kept) {
900
- stored.clear();
901
- reads.clear();
902
- partials.clear();
903
- for (const entry of held) {
904
- stored.add(entry.conversation);
905
- if (entry.read !== undefined)
906
- reads.set(entry.conversation, entry.read);
907
- if (entry.partial)
908
- partials.add(entry.conversation);
909
- }
910
- drafts.clear();
911
- for (const record of kept)
912
- drafts.set(record.conversation, record);
1050
+ async opened(local) {
1051
+ const { conversationList, conversationCursor } = await local.facts();
1052
+ if (conversationList === null || listKnown)
1053
+ return;
1054
+ deps.stream()?.hold(conversationCursor);
1055
+ learned();
913
1056
  },
914
1057
  async reconcile() {
915
1058
  if (!listKnown)
916
1059
  return;
917
- const named = new Set(listing.keys());
1060
+ const local = await deps.local();
1061
+ const [held, kept, { conversationList }] = await Promise.all([
1062
+ local.conversations(),
1063
+ local.chatDrafts(),
1064
+ local.facts(),
1065
+ ]);
1066
+ const list = conversationList ?? [];
1067
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
1068
+ const named = new Set(entries.keys());
1069
+ const reads = new Map(held.map((one) => [one.conversation, one]));
918
1070
  const leaving = [
919
- ...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
1071
+ ...new Set([
1072
+ ...replicated,
1073
+ ...reads.keys(),
1074
+ ...kept.map(({ conversation }) => conversation),
1075
+ ]),
920
1076
  ].filter((conversation) => !named.has(conversation) &&
921
1077
  !creations.has(conversation) &&
922
- (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
1078
+ (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation, list)));
923
1079
  for (const conversation of named) {
924
- if (!replicated.has(conversation))
1080
+ if (!replicated.has(conversation) &&
1081
+ missed(conversation, reads.get(conversation), entries.get(conversation))) {
925
1082
  behind.add(conversation);
1083
+ }
926
1084
  }
927
1085
  replicated = named;
928
1086
  handReplicated();
@@ -953,16 +1111,16 @@ export function createConversationStore(deps) {
953
1111
  progress.complete = false;
954
1112
  },
955
1113
  async resend(progress) {
956
- for (const [conversation, held] of [...drafts]) {
957
- for (const submission of held.submitted ?? []) {
958
- if (submission.turn !== null || sending.has(submission.operation))
959
- continue;
1114
+ const local = await deps.local();
1115
+ const tried = new Set();
1116
+ for (const { conversation } of await local.chatDrafts()) {
1117
+ for (;;) {
1118
+ const submission = (await local.chatDraft(conversation))?.submitted?.find((one) => one.turn === null && !sending.has(one.operation) && !tried.has(one.operation));
1119
+ if (submission === undefined)
1120
+ break;
960
1121
  if (deps.closed())
961
1122
  return;
962
- if (deps.expired(submission.operation)) {
963
- await forgotten(conversation, submission);
964
- continue;
965
- }
1123
+ tried.add(submission.operation);
966
1124
  const result = await dispatched(conversation, submission);
967
1125
  if (result.kind === 'revoked-authorization')
968
1126
  return;
@@ -983,11 +1141,8 @@ export function createConversationStore(deps) {
983
1141
  known() {
984
1142
  return listKnown;
985
1143
  },
986
- reconnected() {
987
- for (const conversation of replicated)
988
- gap(conversation);
989
- },
990
1144
  reopened() {
1145
+ relisting = null;
991
1146
  for (const [operation, dispatch] of sending)
992
1147
  if (!dispatch.answered)
993
1148
  sending.delete(operation);
@@ -1003,30 +1158,35 @@ export function createConversationStore(deps) {
1003
1158
  asked,
1004
1159
  watchers: new Set(),
1005
1160
  window: null,
1161
+ draft: null,
1162
+ ended: null,
1006
1163
  more: false,
1007
1164
  total: null,
1008
1165
  };
1009
1166
  views.set(key, view);
1010
1167
  const watcher = { notify: deliver, ready: false };
1011
1168
  view.watchers.add(watcher);
1012
- void deps
1013
- .durably(async (local) => {
1169
+ const reading = new AbortController();
1170
+ void untilAnswered(() => deps.durably(async (local) => {
1014
1171
  if (view.window !== null)
1015
1172
  return;
1016
1173
  const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
1174
+ view.draft = (await local.chatDraft(conversation)) ?? null;
1175
+ view.ended = await endedIn(conversation);
1017
1176
  view.window = messages;
1018
1177
  view.more = more;
1019
1178
  view.total = asked.total ? await local.messageCount(conversation) : null;
1020
- })
1179
+ }), reading.signal)
1021
1180
  .then(() => {
1022
1181
  if (deps.closed() || !view.watchers.has(watcher))
1023
1182
  return;
1024
1183
  watcher.ready = true;
1025
1184
  const window = view.window ?? [];
1026
- watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
1185
+ watcher.notify(viewOf(conversation, window, sharedFrame(view, window), view.draft, view.ended));
1027
1186
  })
1028
1187
  .catch(() => { });
1029
1188
  return () => {
1189
+ reading.abort();
1030
1190
  if (!view.watchers.delete(watcher))
1031
1191
  return;
1032
1192
  if (view.watchers.size === 0)
@@ -1034,77 +1194,69 @@ export function createConversationStore(deps) {
1034
1194
  };
1035
1195
  },
1036
1196
  watchList(deliver) {
1037
- listWatchers.add(deliver);
1038
- void agentsReady().then(() => {
1039
- if (!deps.closed() && listWatchers.has(deliver))
1040
- deliver(entries());
1041
- });
1197
+ unlisted.add(deliver);
1198
+ learned();
1042
1199
  return () => {
1200
+ unlisted.delete(deliver);
1043
1201
  listWatchers.delete(deliver);
1044
1202
  };
1045
1203
  },
1046
1204
  watchAgents(deliver) {
1047
- agentWatchers.add(deliver);
1048
- void agentsReady().then(() => {
1049
- if (!deps.closed() && agentWatchers.has(deliver))
1050
- deliver(agents());
1051
- });
1205
+ unnamed.add(deliver);
1206
+ learned();
1207
+ readAgents();
1052
1208
  return () => {
1209
+ unnamed.delete(deliver);
1053
1210
  agentWatchers.delete(deliver);
1054
1211
  };
1055
1212
  },
1213
+ learned,
1056
1214
  changed(set) {
1057
1215
  for (const conversation of set.conversations)
1058
1216
  emit(conversation);
1059
- const declaration = deps.agents();
1060
- if (declaration !== undefined &&
1061
- set.documents.some((change) => change.locator === declaration.locator)) {
1062
- agentsRead = readAgents().catch(() => {
1063
- agentsRead = null;
1064
- });
1065
- }
1066
1217
  },
1067
1218
  receive(event) {
1068
1219
  if (deps.closed())
1069
- return false;
1070
- if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
1071
- if (event.kind === 'list') {
1072
- listing.clear();
1073
- for (const entry of event.entries) {
1074
- listing.set(entry.conversation, { ...entry });
1075
- creations.delete(entry.conversation);
1076
- }
1077
- listKnown = true;
1078
- openingEnded();
1079
- }
1080
- else if (event.kind === 'listed') {
1081
- listing.set(event.entry.conversation, { ...event.entry });
1082
- creations.delete(event.entry.conversation);
1083
- }
1084
- else {
1085
- listing.delete(event.conversation);
1220
+ return;
1221
+ if (event.kind === 'list') {
1222
+ if (event.whole)
1223
+ relisting ??= new Map();
1224
+ if (relisting !== null) {
1225
+ for (const entry of event.entries)
1226
+ relisting.set(entry.conversation, { ...entry });
1086
1227
  }
1087
- emitList();
1088
- return (event.kind !== 'list' &&
1089
- listKnown &&
1090
- (listing.size !== replicated.size ||
1091
- [...listing.keys()].some((conversation) => !replicated.has(conversation))));
1228
+ if (event.more && relisting !== null)
1229
+ return;
1230
+ const whole = relisting;
1231
+ relisting = null;
1232
+ listChanged({
1233
+ replacing: whole !== null,
1234
+ set: whole === null ? event.entries : [...whole.values()],
1235
+ gone: null,
1236
+ ...(event.more ? { paged: true } : { cursor: event.cursor }),
1237
+ });
1238
+ return;
1239
+ }
1240
+ if (event.kind === 'listed') {
1241
+ relisting?.set(event.entry.conversation, { ...event.entry });
1242
+ listChanged({ replacing: false, set: [event.entry], gone: null });
1243
+ return;
1244
+ }
1245
+ if (event.kind === 'unlisted') {
1246
+ relisting?.delete(event.conversation);
1247
+ listChanged({ replacing: false, set: [], gone: event.conversation });
1248
+ return;
1092
1249
  }
1093
1250
  heard(event);
1094
- return false;
1095
- },
1096
- searchable() {
1097
- return [...(listKnown ? listing.keys() : stored)].sort();
1098
- },
1099
- acquired(conversation) {
1100
- return reads.has(conversation) && !partials.has(conversation);
1101
1251
  },
1102
1252
  close() {
1103
1253
  views.clear();
1104
1254
  lives.clear();
1105
1255
  unsent.clear();
1106
1256
  listWatchers.clear();
1257
+ unlisted.clear();
1107
1258
  agentWatchers.clear();
1259
+ unnamed.clear();
1108
1260
  },
1109
1261
  };
1110
1262
  }