@agentshouse/mdruntime 0.1.0-c2e8a6f99866d → 0.1.0-c2ef9303a058d

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 (220) hide show
  1. package/README.md +11 -9
  2. package/dist/adapter.d.ts +95 -67
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +5 -11
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +36 -47
  7. package/dist/binary.d.ts +0 -5
  8. package/dist/binary.d.ts.map +1 -1
  9. package/dist/binary.js +1 -1
  10. package/dist/bootstrap.d.ts +17 -9
  11. package/dist/bootstrap.d.ts.map +1 -1
  12. package/dist/bootstrap.js +143 -94
  13. package/dist/chain.d.ts +2 -1
  14. package/dist/chain.d.ts.map +1 -1
  15. package/dist/change-set.d.ts +1 -7
  16. package/dist/change-set.d.ts.map +1 -1
  17. package/dist/change-set.js +1 -4
  18. package/dist/client.d.ts +15 -12
  19. package/dist/client.d.ts.map +1 -1
  20. package/dist/client.js +56 -8
  21. package/dist/collection.d.ts +7 -4
  22. package/dist/collection.d.ts.map +1 -1
  23. package/dist/collection.js +24 -19
  24. package/dist/compile.d.ts.map +1 -1
  25. package/dist/compile.js +117 -243
  26. package/dist/connection.d.ts +14 -12
  27. package/dist/connection.d.ts.map +1 -1
  28. package/dist/connection.js +1466 -1431
  29. package/dist/context.d.ts +2 -0
  30. package/dist/context.d.ts.map +1 -0
  31. package/dist/{chat/context.js → context.js} +1 -4
  32. package/dist/conversation.d.ts +18 -15
  33. package/dist/conversation.d.ts.map +1 -1
  34. package/dist/conversation.js +273 -185
  35. package/dist/durability.d.ts +6 -4
  36. package/dist/durability.d.ts.map +1 -1
  37. package/dist/durability.js +5 -0
  38. package/dist/entry.d.ts +3 -2
  39. package/dist/entry.d.ts.map +1 -1
  40. package/dist/entry.js +17 -8
  41. package/dist/file.d.ts +2 -11
  42. package/dist/file.d.ts.map +1 -1
  43. package/dist/file.js +6 -4
  44. package/dist/host.d.ts +3 -7
  45. package/dist/host.d.ts.map +1 -1
  46. package/dist/host.js +55 -38
  47. package/dist/identity.d.ts +0 -2
  48. package/dist/identity.d.ts.map +1 -1
  49. package/dist/identity.js +0 -6
  50. package/dist/index.d.ts +15 -12
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +6 -3
  53. package/dist/job.d.ts +14 -0
  54. package/dist/job.d.ts.map +1 -0
  55. package/dist/job.js +28 -0
  56. package/dist/live.d.ts +2 -1
  57. package/dist/live.d.ts.map +1 -1
  58. package/dist/live.js +9 -5
  59. package/dist/media.d.ts +8 -0
  60. package/dist/media.d.ts.map +1 -0
  61. package/dist/media.js +218 -0
  62. package/dist/memory.d.ts +12 -6
  63. package/dist/memory.d.ts.map +1 -1
  64. package/dist/memory.js +209 -101
  65. package/dist/messages.d.ts +28 -0
  66. package/dist/messages.d.ts.map +1 -0
  67. package/dist/messages.js +89 -0
  68. package/dist/model.d.ts +8 -8
  69. package/dist/model.d.ts.map +1 -1
  70. package/dist/model.js +7 -13
  71. package/dist/observation.d.ts +3 -2
  72. package/dist/observation.d.ts.map +1 -1
  73. package/dist/observation.js +8 -3
  74. package/dist/outcome.d.ts +13 -6
  75. package/dist/outcome.d.ts.map +1 -1
  76. package/dist/outcome.js +16 -5
  77. package/dist/overlay.js +1 -1
  78. package/dist/paths.d.ts +1 -0
  79. package/dist/paths.d.ts.map +1 -1
  80. package/dist/paths.js +4 -0
  81. package/dist/query.d.ts +10 -206
  82. package/dist/query.d.ts.map +1 -1
  83. package/dist/query.js +3 -494
  84. package/dist/reach.d.ts +14 -9
  85. package/dist/reach.d.ts.map +1 -1
  86. package/dist/reach.js +84 -72
  87. package/dist/react.d.ts +3 -1
  88. package/dist/react.d.ts.map +1 -1
  89. package/dist/readiness.d.ts +14 -17
  90. package/dist/readiness.d.ts.map +1 -1
  91. package/dist/readiness.js +2 -8
  92. package/dist/recovery.d.ts +1 -0
  93. package/dist/recovery.d.ts.map +1 -1
  94. package/dist/recovery.js +26 -0
  95. package/dist/refresh.d.ts +10 -4
  96. package/dist/refresh.d.ts.map +1 -1
  97. package/dist/refresh.js +88 -30
  98. package/dist/replica.d.ts +11 -8
  99. package/dist/replica.d.ts.map +1 -1
  100. package/dist/replica.js +30 -15
  101. package/dist/room-draft.d.ts +22 -5
  102. package/dist/room-draft.d.ts.map +1 -1
  103. package/dist/room-draft.js +131 -51
  104. package/dist/rows.d.ts +18 -16
  105. package/dist/rows.d.ts.map +1 -1
  106. package/dist/rows.js +45 -153
  107. package/dist/save.d.ts +2 -0
  108. package/dist/save.d.ts.map +1 -1
  109. package/dist/save.js +13 -7
  110. package/dist/search.d.ts +12 -95
  111. package/dist/search.d.ts.map +1 -1
  112. package/dist/search.js +10 -349
  113. package/dist/shared.d.ts +0 -1
  114. package/dist/shared.d.ts.map +1 -1
  115. package/dist/shared.js +0 -10
  116. package/dist/status.d.ts +4 -3
  117. package/dist/status.d.ts.map +1 -1
  118. package/dist/status.js +3 -1
  119. package/dist/store.d.ts +25 -62
  120. package/dist/store.d.ts.map +1 -1
  121. package/dist/store.js +43 -282
  122. package/dist/stored.d.ts +12 -26
  123. package/dist/stored.d.ts.map +1 -1
  124. package/dist/stored.js +189 -226
  125. package/dist/tables.d.ts +55 -48
  126. package/dist/tables.d.ts.map +1 -1
  127. package/dist/tables.js +107 -57
  128. package/dist/transfer.d.ts +0 -5
  129. package/dist/transfer.d.ts.map +1 -1
  130. package/dist/validation.d.ts +3 -4
  131. package/dist/validation.d.ts.map +1 -1
  132. package/dist/validation.js +39 -97
  133. package/dist/windows.d.ts +1 -12
  134. package/dist/windows.d.ts.map +1 -1
  135. package/dist/windows.js +2 -75
  136. package/dist/wire.d.ts +14 -9
  137. package/dist/wire.d.ts.map +1 -1
  138. package/dist/wire.js +5 -3
  139. package/package.json +14 -18
  140. package/AGENTS.md +0 -312
  141. package/dist/chat/acts.d.ts +0 -33
  142. package/dist/chat/acts.d.ts.map +0 -1
  143. package/dist/chat/acts.js +0 -36
  144. package/dist/chat/context.d.ts +0 -3
  145. package/dist/chat/context.d.ts.map +0 -1
  146. package/dist/chat/earlier.d.ts +0 -10
  147. package/dist/chat/earlier.d.ts.map +0 -1
  148. package/dist/chat/earlier.js +0 -54
  149. package/dist/chat/house.d.ts +0 -25
  150. package/dist/chat/house.d.ts.map +0 -1
  151. package/dist/chat/house.js +0 -38
  152. package/dist/chat/input.d.ts +0 -11
  153. package/dist/chat/input.d.ts.map +0 -1
  154. package/dist/chat/input.js +0 -92
  155. package/dist/chat/kit.d.ts +0 -61
  156. package/dist/chat/kit.d.ts.map +0 -1
  157. package/dist/chat/kit.js +0 -123
  158. package/dist/chat/lasting.d.ts +0 -6
  159. package/dist/chat/lasting.d.ts.map +0 -1
  160. package/dist/chat/lasting.js +0 -19
  161. package/dist/chat/list.d.ts +0 -28
  162. package/dist/chat/list.d.ts.map +0 -1
  163. package/dist/chat/list.js +0 -103
  164. package/dist/chat/markdown.d.ts +0 -21
  165. package/dist/chat/markdown.d.ts.map +0 -1
  166. package/dist/chat/markdown.js +0 -246
  167. package/dist/chat/message.d.ts +0 -7
  168. package/dist/chat/message.d.ts.map +0 -1
  169. package/dist/chat/message.js +0 -244
  170. package/dist/chat/messages.d.ts +0 -30
  171. package/dist/chat/messages.d.ts.map +0 -1
  172. package/dist/chat/messages.js +0 -198
  173. package/dist/chat/name.d.ts +0 -7
  174. package/dist/chat/name.d.ts.map +0 -1
  175. package/dist/chat/name.js +0 -37
  176. package/dist/chat/opened.d.ts +0 -12
  177. package/dist/chat/opened.d.ts.map +0 -1
  178. package/dist/chat/opened.js +0 -19
  179. package/dist/chat/overlay.d.ts +0 -32
  180. package/dist/chat/overlay.d.ts.map +0 -1
  181. package/dist/chat/overlay.js +0 -103
  182. package/dist/chat/parts.d.ts +0 -58
  183. package/dist/chat/parts.d.ts.map +0 -1
  184. package/dist/chat/parts.js +0 -120
  185. package/dist/chat/pieces.d.ts +0 -62
  186. package/dist/chat/pieces.d.ts.map +0 -1
  187. package/dist/chat/pieces.js +0 -1110
  188. package/dist/chat/process.d.ts +0 -20
  189. package/dist/chat/process.d.ts.map +0 -1
  190. package/dist/chat/process.js +0 -32
  191. package/dist/chat/provider.d.ts +0 -70
  192. package/dist/chat/provider.d.ts.map +0 -1
  193. package/dist/chat/provider.js +0 -635
  194. package/dist/chat/question.d.ts +0 -9
  195. package/dist/chat/question.d.ts.map +0 -1
  196. package/dist/chat/question.js +0 -69
  197. package/dist/chat/questions.d.ts +0 -31
  198. package/dist/chat/questions.d.ts.map +0 -1
  199. package/dist/chat/questions.js +0 -111
  200. package/dist/chat/read.d.ts +0 -5
  201. package/dist/chat/read.d.ts.map +0 -1
  202. package/dist/chat/read.js +0 -13
  203. package/dist/chat/ready.d.ts +0 -37
  204. package/dist/chat/ready.d.ts.map +0 -1
  205. package/dist/chat/ready.js +0 -93
  206. package/dist/chat/room-file.d.ts +0 -8
  207. package/dist/chat/room-file.d.ts.map +0 -1
  208. package/dist/chat/room-file.js +0 -72
  209. package/dist/chat/steady.d.ts +0 -3
  210. package/dist/chat/steady.d.ts.map +0 -1
  211. package/dist/chat/steady.js +0 -26
  212. package/dist/chat/style.d.ts +0 -4
  213. package/dist/chat/style.d.ts.map +0 -1
  214. package/dist/chat/style.js +0 -306
  215. package/dist/chat.d.ts +0 -15
  216. package/dist/chat.d.ts.map +0 -1
  217. package/dist/chat.js +0 -8
  218. package/dist/reads.d.ts +0 -11
  219. package/dist/reads.d.ts.map +0 -1
  220. package/dist/reads.js +0 -98
@@ -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
- import { declaredFile } from './file.js';
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,
@@ -40,16 +46,15 @@ export function unheldView(conversation) {
40
46
  }
41
47
  export const SESSION_STARTED = 'Session started';
42
48
  const NEW_CHAT = 'new:';
49
+ export const HISTORY_CONVERSATIONS = 64;
43
50
  export function newChat(agent) {
44
51
  return `${NEW_CHAT}${agent}`;
45
52
  }
46
53
  function creationOf(input) {
47
54
  if (typeof input !== 'object' || input === null)
48
55
  return null;
49
- const { agent_id: agent, operation_id: operation, replaces, } = input;
50
- return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
51
- ? { agent, operation }
52
- : null;
56
+ const { agent_id: agent, operation_id: operation } = input;
57
+ return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
53
58
  }
54
59
  function startedTurn(turn, operation) {
55
60
  if (operation.op === 'begin')
@@ -208,7 +213,20 @@ function askedOf(options) {
208
213
  }
209
214
  return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
210
215
  }
211
- 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
+ }
224
+ const DURABLE_WRITE = {
225
+ kind: 'rejected',
226
+ code: 'durable-write',
227
+ fields: {},
228
+ retryable: true,
229
+ };
212
230
  function unwritten(draft) {
213
231
  return draft.text === '';
214
232
  }
@@ -248,13 +266,19 @@ export function createConversationStore(deps) {
248
266
  const unsent = new Map();
249
267
  const views = new Map();
250
268
  const listing = new Map();
269
+ let relisting = null;
270
+ let listCursor = null;
251
271
  const creations = new Set();
252
272
  const listWatchers = new Set();
273
+ const unlisted = new Set();
253
274
  let listKnown = false;
275
+ let listHeld = false;
276
+ let listKept = false;
254
277
  const agentWatchers = new Set();
255
- let agentNames = new Map();
256
- let retiredAgents = new Set();
257
- let agentsRead = null;
278
+ const unnamed = new Set();
279
+ let agentsShown = null;
280
+ let listedAgents = null;
281
+ let agentsReading = false;
258
282
  let replicated = new Set();
259
283
  let handedReplicated;
260
284
  function liveOf(conversation) {
@@ -275,9 +299,11 @@ export function createConversationStore(deps) {
275
299
  turn: structuredClone(facts.turn),
276
300
  questions: structuredClone(facts.questions),
277
301
  process: facts.process === null ? null : { ...facts.process },
302
+ processEnded: listing.get(conversation)?.processEnded ?? null,
278
303
  commands: structuredClone(facts.commands),
279
304
  options: structuredClone(facts.options),
280
305
  starting: facts.starting,
306
+ subagents: structuredClone(facts.subagents),
281
307
  link: facts.link === null ? null : { ...facts.link },
282
308
  deliveryUnknown: [...facts.deliveryUnknown],
283
309
  chatDraft: draft === undefined || unwritten(draft)
@@ -324,9 +350,6 @@ export function createConversationStore(deps) {
324
350
  }
325
351
  }
326
352
  }
327
- function changesOf(conversation, arriving) {
328
- return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
329
- }
330
353
  function stateOf(conversation) {
331
354
  const facts = liveOf(conversation);
332
355
  if (facts.questions.length > 0)
@@ -336,7 +359,7 @@ export function createConversationStore(deps) {
336
359
  function entries() {
337
360
  return [...listing.values()].map((entry) => ({
338
361
  conversation: entry.conversation,
339
- agent: { identity: entry.agent, name: agentNames.get(entry.agent) ?? null },
362
+ agent: { identity: entry.agent, name: entry.agentName },
340
363
  name: entry.name,
341
364
  created: entry.created,
342
365
  finished: entry.finished,
@@ -346,25 +369,103 @@ export function createConversationStore(deps) {
346
369
  started: lives.get(entry.conversation)?.turn?.started ?? null,
347
370
  }));
348
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
+ }
349
397
  function emitList() {
350
- if (deps.closed() || listWatchers.size === 0)
398
+ if (deps.closed())
351
399
  return;
352
- const value = entries();
353
- for (const watcher of listWatchers)
354
- 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
+ }
355
429
  }
356
430
  function agents() {
357
- return [...agentNames]
358
- .filter(([identity]) => !retiredAgents.has(identity))
359
- .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 }))
360
440
  .sort((left, right) => left.name === right.name
361
441
  ? left.identity.localeCompare(right.identity)
362
442
  : left.name.localeCompare(right.name));
363
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
+ }
364
461
  function emitAgents() {
365
462
  if (deps.closed() || agentWatchers.size === 0)
366
463
  return;
367
464
  const value = agents();
465
+ const shown = JSON.stringify(value);
466
+ if (shown === agentsShown)
467
+ return;
468
+ agentsShown = shown;
368
469
  for (const watcher of agentWatchers)
369
470
  watcher(value);
370
471
  }
@@ -380,55 +481,11 @@ export function createConversationStore(deps) {
380
481
  if (stateOf(conversation) !== before || next?.turn?.started !== began)
381
482
  emitList();
382
483
  }
383
- async function readAgents() {
384
- const declaration = deps.agents();
385
- if (declaration === undefined)
386
- return;
387
- const names = new Map();
388
- const retired = new Set();
389
- let covered = null;
390
- for (let after;;) {
391
- const read = await deps.documents(declaration, after);
392
- if ('pending' in read)
393
- return;
394
- const coverage = JSON.stringify(read.coverage);
395
- if (covered !== null && coverage !== covered)
396
- return readAgents();
397
- covered = coverage;
398
- for (const document of read.documents) {
399
- const identity = document.fields?.[declaration.identity];
400
- const name = document.fields?.[declaration.name];
401
- if (typeof identity !== 'string' || typeof name !== 'string')
402
- continue;
403
- names.set(identity, name);
404
- const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
405
- if (ended !== undefined && ended !== null)
406
- retired.add(identity);
407
- }
408
- if (read.next === undefined)
409
- break;
410
- after = read.next;
411
- }
412
- const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
413
- if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
414
- return;
415
- agentNames = names;
416
- retiredAgents = retired;
417
- if (named)
418
- emitList();
419
- emitAgents();
420
- const ended = [...drafts.keys()].filter(retiredChat);
421
- if (ended.length > 0)
422
- await remove(ended);
423
- }
424
484
  function retiredChat(conversation) {
425
- return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
426
- }
427
- function agentsReady() {
428
- agentsRead ??= readAgents().catch(() => {
429
- agentsRead = null;
430
- });
431
- 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);
432
489
  }
433
490
  function live(conversation, change) {
434
491
  const owed = unsent.get(conversation);
@@ -451,6 +508,7 @@ export function createConversationStore(deps) {
451
508
  commands: facts.has('commands') ? QUIET.commands : held.commands,
452
509
  options: facts.has('options') ? QUIET.options : held.options,
453
510
  starting: facts.has('starting') ? QUIET.starting : held.starting,
511
+ subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
454
512
  link: facts.has('link') ? QUIET.link : held.link,
455
513
  deliveryUnknown: facts.has('deliveryUnknown')
456
514
  ? QUIET.deliveryUnknown
@@ -485,12 +543,6 @@ export function createConversationStore(deps) {
485
543
  }
486
544
  async function remove(leaving) {
487
545
  await deps.durably(async (local) => {
488
- const removed = [];
489
- for (const conversation of leaving) {
490
- for (const identity of await local.messageIdentities(conversation)) {
491
- removed.push({ conversation, identity, text: null });
492
- }
493
- }
494
546
  await local.dropConversations(leaving);
495
547
  const changed = [];
496
548
  for (const conversation of leaving) {
@@ -507,9 +559,23 @@ export function createConversationStore(deps) {
507
559
  await slide(local, conversation, null);
508
560
  }
509
561
  if (changed.length > 0)
510
- deps.publish(conversationChangeSet(removed, changed));
562
+ deps.publish(conversationChangeSet(changed));
511
563
  });
512
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
+ }
513
579
  function gap(conversation) {
514
580
  gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
515
581
  behind.add(conversation);
@@ -533,17 +599,18 @@ export function createConversationStore(deps) {
533
599
  reads.set(conversation, message.position);
534
600
  sealTurns(conversation, new Set([message.turn]));
535
601
  await slide(local, conversation, [message]);
536
- deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
602
+ deps.publish(conversationChangeSet([conversation]));
537
603
  });
538
604
  }
539
605
  async function readHistories(conversations, ended) {
540
606
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
541
- let asking = conversations.map((conversation) => ({
607
+ let waiting = conversations.map((conversation) => ({
542
608
  conversation,
543
609
  after: reads.get(conversation) ?? undefined,
544
610
  }));
545
- while (asking.length > 0) {
546
- const result = await deps.call('conversation.history', {
611
+ while (waiting.length > 0) {
612
+ const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
613
+ const result = await deps.history({
547
614
  conversations: asking.map(({ conversation, after }) => ({
548
615
  conversation,
549
616
  ...(after === undefined ? {} : { after }),
@@ -566,7 +633,7 @@ export function createConversationStore(deps) {
566
633
  if (!following.some((next) => next.conversation === conversation))
567
634
  ended(conversation);
568
635
  }
569
- asking = following;
636
+ waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
570
637
  }
571
638
  return null;
572
639
  }
@@ -609,7 +676,7 @@ export function createConversationStore(deps) {
609
676
  behind.delete(conversation);
610
677
  await slide(local, conversation, arriving);
611
678
  if (arriving.length > 0 || sealing || whole) {
612
- deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
679
+ deps.publish(conversationChangeSet([conversation]));
613
680
  }
614
681
  });
615
682
  }
@@ -646,6 +713,10 @@ export function createConversationStore(deps) {
646
713
  live(conversation, { starting: event.step });
647
714
  return;
648
715
  }
716
+ if (event.kind === 'subagents') {
717
+ live(conversation, { subagents: structuredClone(event.subagents) });
718
+ return;
719
+ }
649
720
  if (event.kind === 'link') {
650
721
  live(conversation, { link: { connected: event.connected } });
651
722
  return;
@@ -657,8 +728,16 @@ export function createConversationStore(deps) {
657
728
  const arriving = acceptedMessage(event.message);
658
729
  if (arriving === null)
659
730
  return;
731
+ noted(conversation, arriving);
660
732
  if (replicated.has(conversation)) {
661
- void keep(conversation, arriving).catch(() => { });
733
+ if (deps.full()) {
734
+ gap(conversation);
735
+ return;
736
+ }
737
+ void keep(conversation, arriving).catch((error) => {
738
+ if (refusedForSpace(error))
739
+ gap(conversation);
740
+ });
662
741
  return;
663
742
  }
664
743
  if (sealTurns(conversation, new Set([arriving.turn])))
@@ -687,7 +766,7 @@ export function createConversationStore(deps) {
687
766
  const from = newChat(agent);
688
767
  return deps.durably(async (local) => {
689
768
  const held = drafts.get(from);
690
- const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
769
+ const earlier = held?.moves ?? [];
691
770
  if (earlier.includes(operation))
692
771
  return;
693
772
  const blank = {
@@ -702,7 +781,7 @@ export function createConversationStore(deps) {
702
781
  drafts.set(from, left);
703
782
  for (const one of taken)
704
783
  drafts.set(to, one);
705
- deps.publish(conversationChangeSet([], [from, to]));
784
+ deps.publish(conversationChangeSet([from, to]));
706
785
  });
707
786
  }
708
787
  function withDraft(conversation, next) {
@@ -711,15 +790,13 @@ export function createConversationStore(deps) {
711
790
  if (following === undefined)
712
791
  return;
713
792
  await kept(local, conversation, following);
714
- deps.publish(conversationChangeSet([], [conversation]));
793
+ deps.publish(conversationChangeSet([conversation]));
715
794
  });
716
795
  }
717
796
  async function submitted(conversation, operation, draft) {
718
797
  let submission;
719
- let earlier = false;
720
798
  await withDraft(conversation, (current) => {
721
799
  submission = current?.submitted?.find((one) => one.operation === operation);
722
- earlier = submission !== undefined;
723
800
  if (submission !== undefined)
724
801
  return undefined;
725
802
  const said = draft ?? current;
@@ -732,31 +809,24 @@ export function createConversationStore(deps) {
732
809
  submitted: [...(current?.submitted ?? []), submission],
733
810
  };
734
811
  });
735
- return { submission: submission, earlier };
736
- }
737
- function forgotten(conversation, submission) {
738
- return withDraft(conversation, (current) => {
739
- const held = current?.submitted ?? [];
740
- if (current === undefined || !held.includes(submission))
741
- return undefined;
742
- return withSubmitted(current, held.filter((one) => one !== submission));
743
- });
812
+ return submission;
744
813
  }
745
814
  async function dispatched(conversation, submission) {
746
- const dispatch = {};
815
+ const dispatch = { answered: false };
747
816
  sending.set(submission.operation, dispatch);
748
817
  try {
749
- const result = await invoke('conversation.send', conversation, {
750
- text: submission.text,
818
+ const result = await invoke('conversations/send', conversation, {
751
819
  operation_id: submission.operation,
820
+ text: submission.text,
752
821
  });
822
+ dispatch.answered = result.kind !== 'transport-uncertainty';
753
823
  if (result.kind === 'revoked-authorization')
754
824
  return result;
755
825
  if (result.kind !== 'action' && result.retryable) {
756
826
  deps.unanswered();
757
827
  return result;
758
828
  }
759
- const turn = answered(result, 'turn');
829
+ const turn = answered(result, 'message_id');
760
830
  await withDraft(conversation, async (current, local) => {
761
831
  const held = current?.submitted?.find((one) => one.operation === submission.operation);
762
832
  if (current === undefined || held === undefined || held.turn !== null)
@@ -776,25 +846,34 @@ export function createConversationStore(deps) {
776
846
  sending.delete(submission.operation);
777
847
  }
778
848
  }
779
- function invoke(name, conversation, extra = {}) {
780
- return deps.call(name, { conversation, ...extra });
849
+ function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
850
+ return deps.call(name, { conversation_id: conversation, ...extra });
781
851
  }
782
852
  const surface = {
783
- async list() {
784
- await agentsReady();
785
- 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
+ });
786
863
  },
787
864
  async read(conversation, options = {}) {
788
- const asked = askedOf(options);
865
+ const { text, ...windowed } = options;
866
+ const asked = askedOf(windowed);
867
+ const phrase = phraseOf(text);
789
868
  return deps.durably(async (local) => {
790
- const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
791
- 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;
792
871
  return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
793
872
  });
794
873
  },
795
874
  async create(input) {
796
- const result = await deps.call('conversation.create', input);
797
- const conversation = answered(result, 'conversation');
875
+ const result = await deps.call('conversations/create', input);
876
+ const conversation = answered(result, 'conversation_id');
798
877
  if (conversation === null)
799
878
  return result;
800
879
  if (!listing.has(conversation))
@@ -805,12 +884,17 @@ export function createConversationStore(deps) {
805
884
  return result;
806
885
  },
807
886
  async send(conversation, operation = changeBatchIdentity(), draft) {
887
+ if (deps.full())
888
+ return DURABLE_WRITE;
808
889
  if (deps.online()) {
809
- const { submission, earlier } = await submitted(conversation, operation, draft);
810
- if (!earlier || !deps.expired(operation))
811
- return dispatched(conversation, submission);
812
- await forgotten(conversation, submission);
813
- return EXPIRED;
890
+ const held = await submitted(conversation, operation, draft).catch((error) => {
891
+ if (refusedForSpace(error))
892
+ return null;
893
+ throw error;
894
+ });
895
+ if (held === null)
896
+ return DURABLE_WRITE;
897
+ return dispatched(conversation, held);
814
898
  }
815
899
  if (draft !== undefined) {
816
900
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
@@ -819,38 +903,20 @@ export function createConversationStore(deps) {
819
903
  }
820
904
  return OFFLINE;
821
905
  },
822
- async attach(room, file) {
823
- if (!deps.online())
824
- return OFFLINE;
825
- return deps.call('conversation.attach', {
826
- room,
827
- ...(await declaredFile(file)),
828
- blob: file.blob,
829
- });
830
- },
831
906
  answer(conversation, answer) {
832
- return invoke('conversation.answer', conversation, { answer });
907
+ return invoke('conversations/interaction/answer', conversation, {
908
+ operation_id: changeBatchIdentity(),
909
+ ...answer,
910
+ });
833
911
  },
834
912
  interrupt(conversation) {
835
- return invoke('conversation.interrupt', conversation);
913
+ return invoke('conversations/interrupt', conversation);
836
914
  },
837
915
  kill(conversation) {
838
- return invoke('conversation.kill', conversation);
839
- },
840
- async cancel(conversation, turn) {
841
- const result = await invoke('conversation.cancel', conversation, { turn });
842
- if (result.kind !== 'action')
843
- return result;
844
- await withDraft(conversation, (current) => {
845
- const held = current?.submitted?.find((one) => one.turn === turn);
846
- if (current === undefined || held === undefined)
847
- return undefined;
848
- return returned(current, held, current.submitted.filter((one) => one !== held));
849
- });
850
- return result;
916
+ return invoke('conversations/kill', conversation);
851
917
  },
852
918
  delete(conversation) {
853
- return invoke('conversation.delete', conversation);
919
+ return invoke('conversations/delete', conversation);
854
920
  },
855
921
  };
856
922
  const chatDraft = {
@@ -879,7 +945,15 @@ export function createConversationStore(deps) {
879
945
  return {
880
946
  surface,
881
947
  chatDraft,
882
- 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
+ }
883
957
  stored.clear();
884
958
  reads.clear();
885
959
  partials.clear();
@@ -893,6 +967,8 @@ export function createConversationStore(deps) {
893
967
  drafts.clear();
894
968
  for (const record of kept)
895
969
  drafts.set(record.conversation, record);
970
+ if (listHeld)
971
+ learned();
896
972
  },
897
973
  async reconcile() {
898
974
  if (!listKnown)
@@ -904,7 +980,7 @@ export function createConversationStore(deps) {
904
980
  !creations.has(conversation) &&
905
981
  (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
906
982
  for (const conversation of named) {
907
- if (!replicated.has(conversation))
983
+ if (!replicated.has(conversation) && missed(conversation))
908
984
  behind.add(conversation);
909
985
  }
910
986
  replicated = named;
@@ -913,6 +989,11 @@ export function createConversationStore(deps) {
913
989
  await remove(leaving);
914
990
  },
915
991
  async catchUp(progress) {
992
+ if (deps.full()) {
993
+ if (behind.size > 0)
994
+ progress.complete = false;
995
+ return;
996
+ }
916
997
  const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
917
998
  const read = async (conversations) => {
918
999
  if (conversations.length === 0 || deps.closed())
@@ -937,10 +1018,6 @@ export function createConversationStore(deps) {
937
1018
  continue;
938
1019
  if (deps.closed())
939
1020
  return;
940
- if (deps.expired(submission.operation)) {
941
- await forgotten(conversation, submission);
942
- continue;
943
- }
944
1021
  const result = await dispatched(conversation, submission);
945
1022
  if (result.kind === 'revoked-authorization')
946
1023
  return;
@@ -961,12 +1038,11 @@ export function createConversationStore(deps) {
961
1038
  known() {
962
1039
  return listKnown;
963
1040
  },
964
- reconnected() {
965
- for (const conversation of replicated)
966
- gap(conversation);
967
- },
968
1041
  reopened() {
969
- sending.clear();
1042
+ relisting = null;
1043
+ for (const [operation, dispatch] of sending)
1044
+ if (!dispatch.answered)
1045
+ sending.delete(operation);
970
1046
  },
971
1047
  lost() {
972
1048
  catching.clear();
@@ -985,15 +1061,15 @@ export function createConversationStore(deps) {
985
1061
  views.set(key, view);
986
1062
  const watcher = { notify: deliver, ready: false };
987
1063
  view.watchers.add(watcher);
988
- void deps
989
- .durably(async (local) => {
1064
+ const reading = new AbortController();
1065
+ void untilAnswered(() => deps.durably(async (local) => {
990
1066
  if (view.window !== null)
991
1067
  return;
992
1068
  const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
993
1069
  view.window = messages;
994
1070
  view.more = more;
995
1071
  view.total = asked.total ? await local.messageCount(conversation) : null;
996
- })
1072
+ }), reading.signal)
997
1073
  .then(() => {
998
1074
  if (deps.closed() || !view.watchers.has(watcher))
999
1075
  return;
@@ -1003,6 +1079,7 @@ export function createConversationStore(deps) {
1003
1079
  })
1004
1080
  .catch(() => { });
1005
1081
  return () => {
1082
+ reading.abort();
1006
1083
  if (!view.watchers.delete(watcher))
1007
1084
  return;
1008
1085
  if (view.watchers.size === 0)
@@ -1010,77 +1087,88 @@ export function createConversationStore(deps) {
1010
1087
  };
1011
1088
  },
1012
1089
  watchList(deliver) {
1013
- listWatchers.add(deliver);
1014
- void agentsReady().then(() => {
1015
- if (!deps.closed() && listWatchers.has(deliver))
1016
- deliver(entries());
1017
- });
1090
+ unlisted.add(deliver);
1091
+ learned();
1018
1092
  return () => {
1093
+ unlisted.delete(deliver);
1019
1094
  listWatchers.delete(deliver);
1020
1095
  };
1021
1096
  },
1022
1097
  watchAgents(deliver) {
1023
- agentWatchers.add(deliver);
1024
- void agentsReady().then(() => {
1025
- if (!deps.closed() && agentWatchers.has(deliver))
1026
- deliver(agents());
1027
- });
1098
+ unnamed.add(deliver);
1099
+ learned();
1100
+ readAgents();
1028
1101
  return () => {
1102
+ unnamed.delete(deliver);
1029
1103
  agentWatchers.delete(deliver);
1030
1104
  };
1031
1105
  },
1106
+ learned,
1032
1107
  changed(set) {
1033
1108
  for (const conversation of set.conversations)
1034
1109
  emit(conversation);
1035
- const declaration = deps.agents();
1036
- if (declaration !== undefined &&
1037
- set.documents.some((change) => change.locator === declaration.locator)) {
1038
- agentsRead = readAgents().catch(() => {
1039
- agentsRead = null;
1040
- });
1041
- }
1042
1110
  },
1043
1111
  receive(event) {
1044
1112
  if (deps.closed())
1045
1113
  return false;
1046
1114
  if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
1047
1115
  if (event.kind === 'list') {
1048
- listing.clear();
1116
+ if (event.whole)
1117
+ relisting ??= new Map();
1049
1118
  for (const entry of event.entries) {
1050
- listing.set(entry.conversation, { ...entry });
1051
- 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;
1052
1131
  }
1132
+ listCursor = event.cursor;
1133
+ deps.stream()?.hold(event.cursor);
1053
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
+ }
1054
1143
  openingEnded();
1055
1144
  }
1056
1145
  else if (event.kind === 'listed') {
1057
- listing.set(event.entry.conversation, { ...event.entry });
1058
- creations.delete(event.entry.conversation);
1146
+ listed(event.entry);
1147
+ relisting?.set(event.entry.conversation, { ...event.entry });
1059
1148
  }
1060
1149
  else {
1061
1150
  listing.delete(event.conversation);
1151
+ relisting?.delete(event.conversation);
1062
1152
  }
1153
+ keepList();
1063
1154
  emitList();
1064
- return (event.kind !== 'list' &&
1155
+ return ((event.kind !== 'list' &&
1065
1156
  listKnown &&
1066
1157
  (listing.size !== replicated.size ||
1067
- [...listing.keys()].some((conversation) => !replicated.has(conversation))));
1158
+ [...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
1159
+ (listKnown && [...drafts.keys()].some(retiredChat)));
1068
1160
  }
1069
1161
  heard(event);
1070
1162
  return false;
1071
1163
  },
1072
- searchable() {
1073
- return [...(listKnown ? listing.keys() : stored)].sort();
1074
- },
1075
- acquired(conversation) {
1076
- return reads.has(conversation) && !partials.has(conversation);
1077
- },
1078
1164
  close() {
1079
1165
  views.clear();
1080
1166
  lives.clear();
1081
1167
  unsent.clear();
1082
1168
  listWatchers.clear();
1169
+ unlisted.clear();
1083
1170
  agentWatchers.clear();
1171
+ unnamed.clear();
1084
1172
  },
1085
1173
  };
1086
1174
  }