@agentshouse/mdruntime 0.1.0-ccbb89b0ca151 → 0.1.0-ccc0e3407f107

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 (138) hide show
  1. package/README.md +1 -1
  2. package/dist/adapter.d.ts +74 -57
  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 +11 -18
  8. package/dist/bootstrap.d.ts.map +1 -1
  9. package/dist/bootstrap.js +85 -108
  10. package/dist/chain.d.ts +2 -1
  11. package/dist/chain.d.ts.map +1 -1
  12. package/dist/client.d.ts +9 -5
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +2 -1
  15. package/dist/collection.d.ts +7 -4
  16. package/dist/collection.d.ts.map +1 -1
  17. package/dist/collection.js +23 -17
  18. package/dist/compile.d.ts +1 -6
  19. package/dist/compile.d.ts.map +1 -1
  20. package/dist/compile.js +91 -270
  21. package/dist/connection.d.ts +10 -9
  22. package/dist/connection.d.ts.map +1 -1
  23. package/dist/connection.js +1009 -1063
  24. package/dist/context.d.ts +2 -0
  25. package/dist/context.d.ts.map +1 -0
  26. package/dist/context.js +7 -0
  27. package/dist/conversation.d.ts +16 -13
  28. package/dist/conversation.d.ts.map +1 -1
  29. package/dist/conversation.js +229 -155
  30. package/dist/durability.d.ts +6 -1
  31. package/dist/durability.d.ts.map +1 -1
  32. package/dist/entry.d.ts +3 -2
  33. package/dist/entry.d.ts.map +1 -1
  34. package/dist/entry.js +17 -8
  35. package/dist/host.d.ts +3 -7
  36. package/dist/host.d.ts.map +1 -1
  37. package/dist/host.js +22 -27
  38. package/dist/identity.d.ts +0 -1
  39. package/dist/identity.d.ts.map +1 -1
  40. package/dist/identity.js +0 -3
  41. package/dist/index.d.ts +11 -7
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +4 -1
  44. package/dist/job.d.ts +14 -0
  45. package/dist/job.d.ts.map +1 -0
  46. package/dist/job.js +28 -0
  47. package/dist/live.d.ts +2 -1
  48. package/dist/live.d.ts.map +1 -1
  49. package/dist/live.js +10 -7
  50. package/dist/media.d.ts +1 -1
  51. package/dist/media.d.ts.map +1 -1
  52. package/dist/media.js +27 -4
  53. package/dist/memory.d.ts +11 -5
  54. package/dist/memory.d.ts.map +1 -1
  55. package/dist/memory.js +167 -84
  56. package/dist/messages.d.ts +2 -2
  57. package/dist/messages.d.ts.map +1 -1
  58. package/dist/messages.js +15 -16
  59. package/dist/model.d.ts +8 -8
  60. package/dist/model.d.ts.map +1 -1
  61. package/dist/model.js +6 -12
  62. package/dist/observation.d.ts +3 -2
  63. package/dist/observation.d.ts.map +1 -1
  64. package/dist/observation.js +8 -3
  65. package/dist/outcome.d.ts +0 -3
  66. package/dist/outcome.d.ts.map +1 -1
  67. package/dist/outcome.js +3 -1
  68. package/dist/paths.d.ts +1 -0
  69. package/dist/paths.d.ts.map +1 -1
  70. package/dist/paths.js +4 -0
  71. package/dist/query.d.ts +4 -209
  72. package/dist/query.d.ts.map +1 -1
  73. package/dist/query.js +3 -505
  74. package/dist/reach.d.ts +9 -8
  75. package/dist/reach.d.ts.map +1 -1
  76. package/dist/reach.js +37 -48
  77. package/dist/react.d.ts +3 -1
  78. package/dist/react.d.ts.map +1 -1
  79. package/dist/readiness.d.ts +3 -17
  80. package/dist/readiness.d.ts.map +1 -1
  81. package/dist/readiness.js +4 -10
  82. package/dist/recovery.d.ts +1 -0
  83. package/dist/recovery.d.ts.map +1 -1
  84. package/dist/recovery.js +26 -0
  85. package/dist/refresh.d.ts +7 -5
  86. package/dist/refresh.d.ts.map +1 -1
  87. package/dist/refresh.js +29 -5
  88. package/dist/replica.d.ts +10 -8
  89. package/dist/replica.d.ts.map +1 -1
  90. package/dist/replica.js +29 -14
  91. package/dist/room-draft.d.ts +21 -5
  92. package/dist/room-draft.d.ts.map +1 -1
  93. package/dist/room-draft.js +98 -51
  94. package/dist/rows.d.ts +14 -21
  95. package/dist/rows.d.ts.map +1 -1
  96. package/dist/rows.js +48 -184
  97. package/dist/save.d.ts +2 -0
  98. package/dist/save.d.ts.map +1 -1
  99. package/dist/save.js +20 -12
  100. package/dist/search.d.ts +12 -63
  101. package/dist/search.d.ts.map +1 -1
  102. package/dist/search.js +9 -52
  103. package/dist/shared.d.ts +0 -1
  104. package/dist/shared.d.ts.map +1 -1
  105. package/dist/shared.js +0 -10
  106. package/dist/status.d.ts +4 -3
  107. package/dist/status.d.ts.map +1 -1
  108. package/dist/status.js +3 -1
  109. package/dist/store.d.ts +21 -34
  110. package/dist/store.d.ts.map +1 -1
  111. package/dist/store.js +34 -77
  112. package/dist/stored.d.ts +11 -34
  113. package/dist/stored.d.ts.map +1 -1
  114. package/dist/stored.js +158 -241
  115. package/dist/tables.d.ts +23 -48
  116. package/dist/tables.d.ts.map +1 -1
  117. package/dist/tables.js +29 -66
  118. package/dist/transfer.d.ts +0 -5
  119. package/dist/transfer.d.ts.map +1 -1
  120. package/dist/validation.d.ts +3 -4
  121. package/dist/validation.d.ts.map +1 -1
  122. package/dist/validation.js +42 -100
  123. package/dist/windows.d.ts +1 -12
  124. package/dist/windows.d.ts.map +1 -1
  125. package/dist/windows.js +2 -75
  126. package/dist/wire.d.ts +9 -4
  127. package/dist/wire.d.ts.map +1 -1
  128. package/dist/wire.js +3 -1
  129. package/package.json +2 -2
  130. package/dist/fulltext.d.ts +0 -27
  131. package/dist/fulltext.d.ts.map +0 -1
  132. package/dist/fulltext.js +0 -368
  133. package/dist/reads.d.ts +0 -11
  134. package/dist/reads.d.ts.map +0 -1
  135. package/dist/reads.js +0 -100
  136. package/dist/words.d.ts +0 -19
  137. package/dist/words.d.ts.map +0 -1
  138. package/dist/words.js +0 -150
@@ -1,9 +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
5
  import { changeBatchIdentity, chatDraftRevision } from './identity.js';
5
6
  import { OFFLINE } from './outcome.js';
6
- 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';
7
9
  const QUIET = {
8
10
  turn: null,
9
11
  questions: [],
@@ -11,6 +13,7 @@ const QUIET = {
11
13
  commands: [],
12
14
  options: [],
13
15
  starting: null,
16
+ subagents: [],
14
17
  link: null,
15
18
  deliveryUnknown: [],
16
19
  };
@@ -20,6 +23,7 @@ const FACTS = [
20
23
  'commands',
21
24
  'options',
22
25
  'starting',
26
+ 'subagents',
23
27
  'link',
24
28
  'deliveryUnknown',
25
29
  ];
@@ -30,9 +34,11 @@ export function unheldView(conversation) {
30
34
  turn: null,
31
35
  questions: [],
32
36
  process: null,
37
+ processEnded: null,
33
38
  commands: [],
34
39
  options: [],
35
40
  starting: null,
41
+ subagents: [],
36
42
  link: null,
37
43
  deliveryUnknown: [],
38
44
  chatDraft: null,
@@ -47,10 +53,8 @@ export function newChat(agent) {
47
53
  function creationOf(input) {
48
54
  if (typeof input !== 'object' || input === null)
49
55
  return null;
50
- const { agent_id: agent, operation_id: operation, replaces, } = input;
51
- return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
52
- ? { agent, operation }
53
- : null;
56
+ const { agent_id: agent, operation_id: operation } = input;
57
+ return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
54
58
  }
55
59
  function startedTurn(turn, operation) {
56
60
  if (operation.op === 'begin')
@@ -209,7 +213,14 @@ function askedOf(options) {
209
213
  }
210
214
  return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
211
215
  }
212
- 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
+ }
213
224
  const DURABLE_WRITE = {
214
225
  kind: 'rejected',
215
226
  code: 'durable-write',
@@ -255,13 +266,19 @@ export function createConversationStore(deps) {
255
266
  const unsent = new Map();
256
267
  const views = new Map();
257
268
  const listing = new Map();
269
+ let relisting = null;
270
+ let listCursor = null;
258
271
  const creations = new Set();
259
272
  const listWatchers = new Set();
273
+ const unlisted = new Set();
260
274
  let listKnown = false;
275
+ let listHeld = false;
276
+ let listKept = false;
261
277
  const agentWatchers = new Set();
262
- let agentNames = new Map();
263
- let retiredAgents = new Set();
264
- let agentsRead = null;
278
+ const unnamed = new Set();
279
+ let agentsShown = null;
280
+ let listedAgents = null;
281
+ let agentsReading = false;
265
282
  let replicated = new Set();
266
283
  let handedReplicated;
267
284
  function liveOf(conversation) {
@@ -282,9 +299,11 @@ export function createConversationStore(deps) {
282
299
  turn: structuredClone(facts.turn),
283
300
  questions: structuredClone(facts.questions),
284
301
  process: facts.process === null ? null : { ...facts.process },
302
+ processEnded: listing.get(conversation)?.processEnded ?? null,
285
303
  commands: structuredClone(facts.commands),
286
304
  options: structuredClone(facts.options),
287
305
  starting: facts.starting,
306
+ subagents: structuredClone(facts.subagents),
288
307
  link: facts.link === null ? null : { ...facts.link },
289
308
  deliveryUnknown: [...facts.deliveryUnknown],
290
309
  chatDraft: draft === undefined || unwritten(draft)
@@ -340,7 +359,7 @@ export function createConversationStore(deps) {
340
359
  function entries() {
341
360
  return [...listing.values()].map((entry) => ({
342
361
  conversation: entry.conversation,
343
- agent: { identity: entry.agent, name: agentNames.get(entry.agent) ?? null },
362
+ agent: { identity: entry.agent, name: entry.agentName },
344
363
  name: entry.name,
345
364
  created: entry.created,
346
365
  finished: entry.finished,
@@ -350,25 +369,103 @@ export function createConversationStore(deps) {
350
369
  started: lives.get(entry.conversation)?.turn?.started ?? null,
351
370
  }));
352
371
  }
372
+ function keepList() {
373
+ if (!listKnown || listKept)
374
+ return;
375
+ listKept = true;
376
+ queueMicrotask(() => {
377
+ listKept = false;
378
+ if (deps.closed())
379
+ 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(() => { });
388
+ });
389
+ }
390
+ function listed(entry) {
391
+ const ended = listing.get(entry.conversation)?.processEnded ?? null;
392
+ listing.set(entry.conversation, { ...entry });
393
+ creations.delete(entry.conversation);
394
+ if (entry.processEnded !== ended)
395
+ emit(entry.conversation);
396
+ }
353
397
  function emitList() {
354
- if (deps.closed() || listWatchers.size === 0)
398
+ if (deps.closed())
355
399
  return;
356
- const value = entries();
357
- for (const watcher of listWatchers)
358
- watcher(value);
400
+ if (listWatchers.size > 0) {
401
+ const value = entries();
402
+ for (const watcher of listWatchers)
403
+ watcher(value);
404
+ }
405
+ emitAgents();
406
+ learned();
407
+ }
408
+ function learned() {
409
+ if (deps.closed())
410
+ return;
411
+ if (!listKnown && !listHeld && deps.stream() !== undefined && deps.online())
412
+ return;
413
+ if (unlisted.size > 0) {
414
+ const value = entries();
415
+ for (const watcher of [...unlisted]) {
416
+ unlisted.delete(watcher);
417
+ listWatchers.add(watcher);
418
+ watcher(value);
419
+ }
420
+ }
421
+ if (unnamed.size > 0) {
422
+ const value = agents();
423
+ for (const watcher of [...unnamed]) {
424
+ unnamed.delete(watcher);
425
+ agentWatchers.add(watcher);
426
+ watcher(value);
427
+ }
428
+ }
359
429
  }
360
430
  function agents() {
361
- return [...agentNames]
362
- .filter(([identity]) => !retiredAgents.has(identity))
363
- .map(([identity, name]) => ({ identity, name }))
431
+ const named = new Map();
432
+ for (const agent of listedAgents ?? [])
433
+ named.set(agent.agent, agent);
434
+ for (const entry of listing.values()) {
435
+ named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
436
+ }
437
+ return [...named]
438
+ .filter(([, agent]) => !agent.retired)
439
+ .map(([identity, agent]) => ({ identity, name: agent.name }))
364
440
  .sort((left, right) => left.name === right.name
365
441
  ? left.identity.localeCompare(right.identity)
366
442
  : left.name.localeCompare(right.name));
367
443
  }
444
+ function readAgents() {
445
+ const stream = deps.stream();
446
+ if (stream?.agents === undefined || !deps.online() || agentsReading)
447
+ return;
448
+ agentsReading = true;
449
+ void stream
450
+ .agents()
451
+ .then((read) => {
452
+ if (read === null)
453
+ return;
454
+ listedAgents = read;
455
+ emitAgents();
456
+ }, () => { })
457
+ .finally(() => {
458
+ agentsReading = false;
459
+ });
460
+ }
368
461
  function emitAgents() {
369
462
  if (deps.closed() || agentWatchers.size === 0)
370
463
  return;
371
464
  const value = agents();
465
+ const shown = JSON.stringify(value);
466
+ if (shown === agentsShown)
467
+ return;
468
+ agentsShown = shown;
372
469
  for (const watcher of agentWatchers)
373
470
  watcher(value);
374
471
  }
@@ -384,55 +481,11 @@ export function createConversationStore(deps) {
384
481
  if (stateOf(conversation) !== before || next?.turn?.started !== began)
385
482
  emitList();
386
483
  }
387
- async function readAgents() {
388
- const declaration = deps.agents();
389
- if (declaration === undefined)
390
- return;
391
- const names = new Map();
392
- const retired = new Set();
393
- let covered = null;
394
- for (let after;;) {
395
- const read = await deps.documents(declaration, after);
396
- if ('pending' in read)
397
- return;
398
- const coverage = JSON.stringify(read.coverage);
399
- if (covered !== null && coverage !== covered)
400
- return readAgents();
401
- covered = coverage;
402
- for (const document of read.documents) {
403
- const identity = document.fields?.[declaration.identity];
404
- const name = document.fields?.[declaration.name];
405
- if (typeof identity !== 'string' || typeof name !== 'string')
406
- continue;
407
- names.set(identity, name);
408
- const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
409
- if (ended !== undefined && ended !== null)
410
- retired.add(identity);
411
- }
412
- if (read.next === undefined)
413
- break;
414
- after = read.next;
415
- }
416
- const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
417
- if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
418
- return;
419
- agentNames = names;
420
- retiredAgents = retired;
421
- if (named)
422
- emitList();
423
- emitAgents();
424
- const ended = [...drafts.keys()].filter(retiredChat);
425
- if (ended.length > 0)
426
- await remove(ended);
427
- }
428
484
  function retiredChat(conversation) {
429
- return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
430
- }
431
- function agentsReady() {
432
- agentsRead ??= readAgents().catch(() => {
433
- agentsRead = null;
434
- });
435
- return agentsRead;
485
+ if (!conversation.startsWith(NEW_CHAT))
486
+ return false;
487
+ const agent = conversation.slice(NEW_CHAT.length);
488
+ return [...listing.values()].some((entry) => entry.agent === agent && entry.agentRetired);
436
489
  }
437
490
  function live(conversation, change) {
438
491
  const owed = unsent.get(conversation);
@@ -455,6 +508,7 @@ export function createConversationStore(deps) {
455
508
  commands: facts.has('commands') ? QUIET.commands : held.commands,
456
509
  options: facts.has('options') ? QUIET.options : held.options,
457
510
  starting: facts.has('starting') ? QUIET.starting : held.starting,
511
+ subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
458
512
  link: facts.has('link') ? QUIET.link : held.link,
459
513
  deliveryUnknown: facts.has('deliveryUnknown')
460
514
  ? QUIET.deliveryUnknown
@@ -508,6 +562,20 @@ export function createConversationStore(deps) {
508
562
  deps.publish(conversationChangeSet(changed));
509
563
  });
510
564
  }
565
+ function missed(conversation) {
566
+ const read = reads.get(conversation);
567
+ const newest = listing.get(conversation)?.newest;
568
+ return (read === undefined ||
569
+ newest === undefined ||
570
+ partials.has(conversation) ||
571
+ (newest !== null && (read === null || newest > read)));
572
+ }
573
+ function noted(conversation, message) {
574
+ const entry = listing.get(conversation);
575
+ if (entry === undefined || (entry.newest !== null && entry.newest >= message.position))
576
+ return;
577
+ listing.set(conversation, { ...entry, newest: message.position });
578
+ }
511
579
  function gap(conversation) {
512
580
  gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
513
581
  behind.add(conversation);
@@ -542,7 +610,7 @@ export function createConversationStore(deps) {
542
610
  }));
543
611
  while (waiting.length > 0) {
544
612
  const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
545
- const result = await deps.call('conversation.history', {
613
+ const result = await deps.history({
546
614
  conversations: asking.map(({ conversation, after }) => ({
547
615
  conversation,
548
616
  ...(after === undefined ? {} : { after }),
@@ -645,6 +713,10 @@ export function createConversationStore(deps) {
645
713
  live(conversation, { starting: event.step });
646
714
  return;
647
715
  }
716
+ if (event.kind === 'subagents') {
717
+ live(conversation, { subagents: structuredClone(event.subagents) });
718
+ return;
719
+ }
648
720
  if (event.kind === 'link') {
649
721
  live(conversation, { link: { connected: event.connected } });
650
722
  return;
@@ -656,6 +728,7 @@ export function createConversationStore(deps) {
656
728
  const arriving = acceptedMessage(event.message);
657
729
  if (arriving === null)
658
730
  return;
731
+ noted(conversation, arriving);
659
732
  if (replicated.has(conversation)) {
660
733
  if (deps.full()) {
661
734
  gap(conversation);
@@ -693,7 +766,7 @@ export function createConversationStore(deps) {
693
766
  const from = newChat(agent);
694
767
  return deps.durably(async (local) => {
695
768
  const held = drafts.get(from);
696
- const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
769
+ const earlier = held?.moves ?? [];
697
770
  if (earlier.includes(operation))
698
771
  return;
699
772
  const blank = {
@@ -722,10 +795,8 @@ export function createConversationStore(deps) {
722
795
  }
723
796
  async function submitted(conversation, operation, draft) {
724
797
  let submission;
725
- let earlier = false;
726
798
  await withDraft(conversation, (current) => {
727
799
  submission = current?.submitted?.find((one) => one.operation === operation);
728
- earlier = submission !== undefined;
729
800
  if (submission !== undefined)
730
801
  return undefined;
731
802
  const said = draft ?? current;
@@ -738,23 +809,15 @@ export function createConversationStore(deps) {
738
809
  submitted: [...(current?.submitted ?? []), submission],
739
810
  };
740
811
  });
741
- return { submission: submission, earlier };
742
- }
743
- function forgotten(conversation, submission) {
744
- return withDraft(conversation, (current) => {
745
- const held = current?.submitted ?? [];
746
- if (current === undefined || !held.includes(submission))
747
- return undefined;
748
- return withSubmitted(current, held.filter((one) => one !== submission));
749
- });
812
+ return submission;
750
813
  }
751
814
  async function dispatched(conversation, submission) {
752
815
  const dispatch = { answered: false };
753
816
  sending.set(submission.operation, dispatch);
754
817
  try {
755
- const result = await invoke('conversation.send', conversation, {
756
- text: submission.text,
818
+ const result = await invoke('conversations/send', conversation, {
757
819
  operation_id: submission.operation,
820
+ text: submission.text,
758
821
  });
759
822
  dispatch.answered = result.kind !== 'transport-uncertainty';
760
823
  if (result.kind === 'revoked-authorization')
@@ -763,7 +826,7 @@ export function createConversationStore(deps) {
763
826
  deps.unanswered();
764
827
  return result;
765
828
  }
766
- const turn = answered(result, 'turn');
829
+ const turn = answered(result, 'message_id');
767
830
  await withDraft(conversation, async (current, local) => {
768
831
  const held = current?.submitted?.find((one) => one.operation === submission.operation);
769
832
  if (current === undefined || held === undefined || held.turn !== null)
@@ -783,25 +846,34 @@ export function createConversationStore(deps) {
783
846
  sending.delete(submission.operation);
784
847
  }
785
848
  }
786
- function invoke(name, conversation, extra = {}) {
787
- return deps.call(name, { conversation, ...extra });
849
+ function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
850
+ return deps.call(name, { conversation_id: conversation, ...extra });
788
851
  }
789
852
  const surface = {
790
- async list() {
791
- await agentsReady();
792
- return entries();
853
+ list() {
854
+ return new Promise((settle) => {
855
+ const answer = (listed) => {
856
+ unlisted.delete(answer);
857
+ listWatchers.delete(answer);
858
+ settle(listed);
859
+ };
860
+ unlisted.add(answer);
861
+ learned();
862
+ });
793
863
  },
794
864
  async read(conversation, options = {}) {
795
- const asked = askedOf(options);
865
+ const { text, ...windowed } = options;
866
+ const asked = askedOf(windowed);
867
+ const phrase = phraseOf(text);
796
868
  return deps.durably(async (local) => {
797
- const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
798
- const total = asked.total ? await local.messageCount(conversation) : null;
869
+ const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
870
+ const total = asked.total ? await local.messageCount(conversation, phrase) : null;
799
871
  return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
800
872
  });
801
873
  },
802
874
  async create(input) {
803
- const result = await deps.call('conversation.create', input);
804
- const conversation = answered(result, 'conversation');
875
+ const result = await deps.call('conversations/create', input);
876
+ const conversation = answered(result, 'conversation_id');
805
877
  if (conversation === null)
806
878
  return result;
807
879
  if (!listing.has(conversation))
@@ -822,11 +894,7 @@ export function createConversationStore(deps) {
822
894
  });
823
895
  if (held === null)
824
896
  return DURABLE_WRITE;
825
- const { submission, earlier } = held;
826
- if (!earlier || !deps.expired(operation))
827
- return dispatched(conversation, submission);
828
- await forgotten(conversation, submission);
829
- return EXPIRED;
897
+ return dispatched(conversation, held);
830
898
  }
831
899
  if (draft !== undefined) {
832
900
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
@@ -836,28 +904,19 @@ export function createConversationStore(deps) {
836
904
  return OFFLINE;
837
905
  },
838
906
  answer(conversation, answer) {
839
- return invoke('conversation.answer', conversation, { answer });
907
+ return invoke('conversations/interaction/answer', conversation, {
908
+ operation_id: changeBatchIdentity(),
909
+ ...answer,
910
+ });
840
911
  },
841
912
  interrupt(conversation) {
842
- return invoke('conversation.interrupt', conversation);
913
+ return invoke('conversations/interrupt', conversation);
843
914
  },
844
915
  kill(conversation) {
845
- return invoke('conversation.kill', conversation);
846
- },
847
- async cancel(conversation, turn) {
848
- const result = await invoke('conversation.cancel', conversation, { turn });
849
- if (result.kind !== 'action')
850
- return result;
851
- await withDraft(conversation, (current) => {
852
- const held = current?.submitted?.find((one) => one.turn === turn);
853
- if (current === undefined || held === undefined)
854
- return undefined;
855
- return returned(current, held, current.submitted.filter((one) => one !== held));
856
- });
857
- return result;
916
+ return invoke('conversations/kill', conversation);
858
917
  },
859
918
  delete(conversation) {
860
- return invoke('conversation.delete', conversation);
919
+ return invoke('conversations/delete', conversation);
861
920
  },
862
921
  };
863
922
  const chatDraft = {
@@ -886,7 +945,15 @@ export function createConversationStore(deps) {
886
945
  return {
887
946
  surface,
888
947
  chatDraft,
889
- hydrate(held, kept) {
948
+ hydrate(held, kept, list, cursor) {
949
+ if (list !== null && !listKnown) {
950
+ listing.clear();
951
+ for (const entry of list)
952
+ listing.set(entry.conversation, { ...entry });
953
+ listHeld = true;
954
+ listCursor = cursor;
955
+ deps.stream()?.hold(cursor);
956
+ }
890
957
  stored.clear();
891
958
  reads.clear();
892
959
  partials.clear();
@@ -900,6 +967,8 @@ export function createConversationStore(deps) {
900
967
  drafts.clear();
901
968
  for (const record of kept)
902
969
  drafts.set(record.conversation, record);
970
+ if (listHeld)
971
+ learned();
903
972
  },
904
973
  async reconcile() {
905
974
  if (!listKnown)
@@ -911,7 +980,7 @@ export function createConversationStore(deps) {
911
980
  !creations.has(conversation) &&
912
981
  (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
913
982
  for (const conversation of named) {
914
- if (!replicated.has(conversation))
983
+ if (!replicated.has(conversation) && missed(conversation))
915
984
  behind.add(conversation);
916
985
  }
917
986
  replicated = named;
@@ -949,10 +1018,6 @@ export function createConversationStore(deps) {
949
1018
  continue;
950
1019
  if (deps.closed())
951
1020
  return;
952
- if (deps.expired(submission.operation)) {
953
- await forgotten(conversation, submission);
954
- continue;
955
- }
956
1021
  const result = await dispatched(conversation, submission);
957
1022
  if (result.kind === 'revoked-authorization')
958
1023
  return;
@@ -973,11 +1038,8 @@ export function createConversationStore(deps) {
973
1038
  known() {
974
1039
  return listKnown;
975
1040
  },
976
- reconnected() {
977
- for (const conversation of replicated)
978
- gap(conversation);
979
- },
980
1041
  reopened() {
1042
+ relisting = null;
981
1043
  for (const [operation, dispatch] of sending)
982
1044
  if (!dispatch.answered)
983
1045
  sending.delete(operation);
@@ -999,15 +1061,15 @@ export function createConversationStore(deps) {
999
1061
  views.set(key, view);
1000
1062
  const watcher = { notify: deliver, ready: false };
1001
1063
  view.watchers.add(watcher);
1002
- void deps
1003
- .durably(async (local) => {
1064
+ const reading = new AbortController();
1065
+ void untilAnswered(() => deps.durably(async (local) => {
1004
1066
  if (view.window !== null)
1005
1067
  return;
1006
1068
  const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
1007
1069
  view.window = messages;
1008
1070
  view.more = more;
1009
1071
  view.total = asked.total ? await local.messageCount(conversation) : null;
1010
- })
1072
+ }), reading.signal)
1011
1073
  .then(() => {
1012
1074
  if (deps.closed() || !view.watchers.has(watcher))
1013
1075
  return;
@@ -1017,6 +1079,7 @@ export function createConversationStore(deps) {
1017
1079
  })
1018
1080
  .catch(() => { });
1019
1081
  return () => {
1082
+ reading.abort();
1020
1083
  if (!view.watchers.delete(watcher))
1021
1084
  return;
1022
1085
  if (view.watchers.size === 0)
@@ -1024,77 +1087,88 @@ export function createConversationStore(deps) {
1024
1087
  };
1025
1088
  },
1026
1089
  watchList(deliver) {
1027
- listWatchers.add(deliver);
1028
- void agentsReady().then(() => {
1029
- if (!deps.closed() && listWatchers.has(deliver))
1030
- deliver(entries());
1031
- });
1090
+ unlisted.add(deliver);
1091
+ learned();
1032
1092
  return () => {
1093
+ unlisted.delete(deliver);
1033
1094
  listWatchers.delete(deliver);
1034
1095
  };
1035
1096
  },
1036
1097
  watchAgents(deliver) {
1037
- agentWatchers.add(deliver);
1038
- void agentsReady().then(() => {
1039
- if (!deps.closed() && agentWatchers.has(deliver))
1040
- deliver(agents());
1041
- });
1098
+ unnamed.add(deliver);
1099
+ learned();
1100
+ readAgents();
1042
1101
  return () => {
1102
+ unnamed.delete(deliver);
1043
1103
  agentWatchers.delete(deliver);
1044
1104
  };
1045
1105
  },
1106
+ learned,
1046
1107
  changed(set) {
1047
1108
  for (const conversation of set.conversations)
1048
1109
  emit(conversation);
1049
- const declaration = deps.agents();
1050
- if (declaration !== undefined &&
1051
- set.documents.some((change) => change.locator === declaration.locator)) {
1052
- agentsRead = readAgents().catch(() => {
1053
- agentsRead = null;
1054
- });
1055
- }
1056
1110
  },
1057
1111
  receive(event) {
1058
1112
  if (deps.closed())
1059
1113
  return false;
1060
1114
  if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
1061
1115
  if (event.kind === 'list') {
1062
- listing.clear();
1116
+ if (event.whole)
1117
+ relisting ??= new Map();
1063
1118
  for (const entry of event.entries) {
1064
- listing.set(entry.conversation, { ...entry });
1065
- creations.delete(entry.conversation);
1119
+ if (relisting === null)
1120
+ listed(entry);
1121
+ else
1122
+ relisting.set(entry.conversation, { ...entry });
1123
+ }
1124
+ if (event.more)
1125
+ return false;
1126
+ if (relisting !== null) {
1127
+ listing.clear();
1128
+ for (const entry of relisting.values())
1129
+ listed(entry);
1130
+ relisting = null;
1066
1131
  }
1132
+ listCursor = event.cursor;
1133
+ deps.stream()?.hold(event.cursor);
1067
1134
  listKnown = true;
1135
+ for (const conversation of replicated) {
1136
+ if (!listing.has(conversation))
1137
+ continue;
1138
+ if (!missed(conversation))
1139
+ behind.delete(conversation);
1140
+ else if (!behind.has(conversation))
1141
+ gap(conversation);
1142
+ }
1068
1143
  openingEnded();
1069
1144
  }
1070
1145
  else if (event.kind === 'listed') {
1071
- listing.set(event.entry.conversation, { ...event.entry });
1072
- creations.delete(event.entry.conversation);
1146
+ listed(event.entry);
1147
+ relisting?.set(event.entry.conversation, { ...event.entry });
1073
1148
  }
1074
1149
  else {
1075
1150
  listing.delete(event.conversation);
1151
+ relisting?.delete(event.conversation);
1076
1152
  }
1153
+ keepList();
1077
1154
  emitList();
1078
- return (event.kind !== 'list' &&
1155
+ return ((event.kind !== 'list' &&
1079
1156
  listKnown &&
1080
1157
  (listing.size !== replicated.size ||
1081
- [...listing.keys()].some((conversation) => !replicated.has(conversation))));
1158
+ [...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
1159
+ (listKnown && [...drafts.keys()].some(retiredChat)));
1082
1160
  }
1083
1161
  heard(event);
1084
1162
  return false;
1085
1163
  },
1086
- searchable() {
1087
- return [...(listKnown ? listing.keys() : stored)].sort();
1088
- },
1089
- acquired(conversation) {
1090
- return reads.has(conversation) && !partials.has(conversation);
1091
- },
1092
1164
  close() {
1093
1165
  views.clear();
1094
1166
  lives.clear();
1095
1167
  unsent.clear();
1096
1168
  listWatchers.clear();
1169
+ unlisted.clear();
1097
1170
  agentWatchers.clear();
1171
+ unnamed.clear();
1098
1172
  },
1099
1173
  };
1100
1174
  }