@agentshouse/mdruntime 0.1.0-c91a238f9703e → 0.1.0-c9260f23cccf4

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 (229) hide show
  1. package/README.md +18 -15
  2. package/dist/adapter.d.ts +72 -37
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +5 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +38 -4
  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 +26 -8
  11. package/dist/bootstrap.d.ts.map +1 -1
  12. package/dist/bootstrap.js +174 -102
  13. package/dist/chain.d.ts +11 -0
  14. package/dist/chain.d.ts.map +1 -0
  15. package/dist/chain.js +68 -0
  16. package/dist/change-set.d.ts +9 -16
  17. package/dist/change-set.d.ts.map +1 -1
  18. package/dist/change-set.js +11 -89
  19. package/dist/change.d.ts +31 -1
  20. package/dist/change.d.ts.map +1 -1
  21. package/dist/change.js +10 -1
  22. package/dist/client.d.ts +9 -5
  23. package/dist/client.d.ts.map +1 -1
  24. package/dist/client.js +80 -6
  25. package/dist/collection.d.ts +11 -19
  26. package/dist/collection.d.ts.map +1 -1
  27. package/dist/collection.js +53 -289
  28. package/dist/compile.d.ts +16 -0
  29. package/dist/compile.d.ts.map +1 -0
  30. package/dist/compile.js +773 -0
  31. package/dist/connection.d.ts +18 -12
  32. package/dist/connection.d.ts.map +1 -1
  33. package/dist/connection.js +2729 -1085
  34. package/dist/conversation.d.ts +14 -6
  35. package/dist/conversation.d.ts.map +1 -1
  36. package/dist/conversation.js +263 -94
  37. package/dist/digest.d.ts +7 -0
  38. package/dist/digest.d.ts.map +1 -0
  39. package/dist/digest.js +113 -0
  40. package/dist/durability.d.ts +3 -5
  41. package/dist/durability.d.ts.map +1 -1
  42. package/dist/durability.js +5 -0
  43. package/dist/entry.d.ts +4 -1
  44. package/dist/entry.d.ts.map +1 -1
  45. package/dist/entry.js +18 -23
  46. package/dist/file.d.ts +2 -19
  47. package/dist/file.d.ts.map +1 -1
  48. package/dist/file.js +7 -122
  49. package/dist/fulltext.d.ts +27 -0
  50. package/dist/fulltext.d.ts.map +1 -0
  51. package/dist/fulltext.js +370 -0
  52. package/dist/host.d.ts +4 -7
  53. package/dist/host.d.ts.map +1 -1
  54. package/dist/host.js +45 -18
  55. package/dist/identity.d.ts +0 -1
  56. package/dist/identity.d.ts.map +1 -1
  57. package/dist/identity.js +0 -3
  58. package/dist/index.d.ts +12 -10
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +5 -2
  61. package/dist/live.d.ts +22 -0
  62. package/dist/live.d.ts.map +1 -0
  63. package/dist/live.js +124 -0
  64. package/dist/media.d.ts +8 -0
  65. package/dist/media.d.ts.map +1 -0
  66. package/dist/media.js +208 -0
  67. package/dist/memory.d.ts +4 -4
  68. package/dist/memory.d.ts.map +1 -1
  69. package/dist/memory.js +67 -29
  70. package/dist/messages.d.ts +28 -0
  71. package/dist/messages.d.ts.map +1 -0
  72. package/dist/messages.js +90 -0
  73. package/dist/model.d.ts +6 -0
  74. package/dist/model.d.ts.map +1 -1
  75. package/dist/model.js +7 -1
  76. package/dist/name.d.ts +4 -0
  77. package/dist/name.d.ts.map +1 -0
  78. package/dist/name.js +122 -0
  79. package/dist/observation.d.ts +13 -0
  80. package/dist/observation.d.ts.map +1 -1
  81. package/dist/observation.js +84 -0
  82. package/dist/opened.d.ts +22 -0
  83. package/dist/opened.d.ts.map +1 -0
  84. package/dist/opened.js +107 -0
  85. package/dist/outcome.d.ts +40 -5
  86. package/dist/outcome.d.ts.map +1 -1
  87. package/dist/outcome.js +38 -2
  88. package/dist/overlay.d.ts +2 -5
  89. package/dist/overlay.d.ts.map +1 -1
  90. package/dist/overlay.js +87 -172
  91. package/dist/paths.d.ts +1 -0
  92. package/dist/paths.d.ts.map +1 -1
  93. package/dist/paths.js +4 -0
  94. package/dist/query.d.ts +11 -1
  95. package/dist/query.d.ts.map +1 -1
  96. package/dist/query.js +13 -1
  97. package/dist/reach.d.ts +13 -4
  98. package/dist/reach.d.ts.map +1 -1
  99. package/dist/reach.js +201 -72
  100. package/dist/readiness.d.ts +19 -9
  101. package/dist/readiness.d.ts.map +1 -1
  102. package/dist/readiness.js +6 -6
  103. package/dist/reads.d.ts.map +1 -1
  104. package/dist/reads.js +10 -17
  105. package/dist/recovery.d.ts +2 -2
  106. package/dist/recovery.d.ts.map +1 -1
  107. package/dist/recovery.js +8 -7
  108. package/dist/refresh.d.ts +15 -14
  109. package/dist/refresh.d.ts.map +1 -1
  110. package/dist/refresh.js +113 -56
  111. package/dist/replica.d.ts +55 -36
  112. package/dist/replica.d.ts.map +1 -1
  113. package/dist/replica.js +280 -186
  114. package/dist/room-draft.d.ts +21 -3
  115. package/dist/room-draft.d.ts.map +1 -1
  116. package/dist/room-draft.js +102 -28
  117. package/dist/rows.d.ts +38 -31
  118. package/dist/rows.d.ts.map +1 -1
  119. package/dist/rows.js +173 -101
  120. package/dist/save.d.ts +52 -14
  121. package/dist/save.d.ts.map +1 -1
  122. package/dist/save.js +513 -185
  123. package/dist/search.d.ts +11 -43
  124. package/dist/search.d.ts.map +1 -1
  125. package/dist/search.js +0 -213
  126. package/dist/shared.d.ts +8 -3
  127. package/dist/shared.d.ts.map +1 -1
  128. package/dist/shared.js +129 -66
  129. package/dist/status.d.ts +5 -0
  130. package/dist/status.d.ts.map +1 -1
  131. package/dist/status.js +3 -1
  132. package/dist/store.d.ts +85 -93
  133. package/dist/store.d.ts.map +1 -1
  134. package/dist/store.js +323 -687
  135. package/dist/stored.d.ts +135 -0
  136. package/dist/stored.d.ts.map +1 -0
  137. package/dist/stored.js +569 -0
  138. package/dist/tables.d.ts +352 -0
  139. package/dist/tables.d.ts.map +1 -0
  140. package/dist/tables.js +621 -0
  141. package/dist/turn.d.ts +5 -3
  142. package/dist/turn.d.ts.map +1 -1
  143. package/dist/turn.js +51 -21
  144. package/dist/validation.d.ts +30 -0
  145. package/dist/validation.d.ts.map +1 -0
  146. package/dist/validation.js +162 -0
  147. package/dist/windows.d.ts +2 -6
  148. package/dist/windows.d.ts.map +1 -1
  149. package/dist/windows.js +30 -34
  150. package/dist/wire.d.ts +17 -1
  151. package/dist/wire.d.ts.map +1 -1
  152. package/dist/wire.js +5 -1
  153. package/dist/words.d.ts +19 -0
  154. package/dist/words.d.ts.map +1 -0
  155. package/dist/words.js +156 -0
  156. package/package.json +18 -19
  157. package/AGENTS.md +0 -305
  158. package/dist/acquisition.d.ts +0 -14
  159. package/dist/acquisition.d.ts.map +0 -1
  160. package/dist/acquisition.js +0 -24
  161. package/dist/chat/acts.d.ts +0 -33
  162. package/dist/chat/acts.d.ts.map +0 -1
  163. package/dist/chat/acts.js +0 -36
  164. package/dist/chat/context.d.ts +0 -3
  165. package/dist/chat/context.d.ts.map +0 -1
  166. package/dist/chat/context.js +0 -10
  167. package/dist/chat/earlier.d.ts +0 -10
  168. package/dist/chat/earlier.d.ts.map +0 -1
  169. package/dist/chat/earlier.js +0 -54
  170. package/dist/chat/input.d.ts +0 -9
  171. package/dist/chat/input.d.ts.map +0 -1
  172. package/dist/chat/input.js +0 -99
  173. package/dist/chat/kit.d.ts +0 -61
  174. package/dist/chat/kit.d.ts.map +0 -1
  175. package/dist/chat/kit.js +0 -123
  176. package/dist/chat/lasting.d.ts +0 -6
  177. package/dist/chat/lasting.d.ts.map +0 -1
  178. package/dist/chat/lasting.js +0 -19
  179. package/dist/chat/list.d.ts +0 -27
  180. package/dist/chat/list.d.ts.map +0 -1
  181. package/dist/chat/list.js +0 -99
  182. package/dist/chat/markdown.d.ts +0 -20
  183. package/dist/chat/markdown.d.ts.map +0 -1
  184. package/dist/chat/markdown.js +0 -188
  185. package/dist/chat/message.d.ts +0 -6
  186. package/dist/chat/message.d.ts.map +0 -1
  187. package/dist/chat/message.js +0 -192
  188. package/dist/chat/messages.d.ts +0 -22
  189. package/dist/chat/messages.d.ts.map +0 -1
  190. package/dist/chat/messages.js +0 -155
  191. package/dist/chat/name.d.ts +0 -6
  192. package/dist/chat/name.d.ts.map +0 -1
  193. package/dist/chat/name.js +0 -37
  194. package/dist/chat/opened.d.ts +0 -11
  195. package/dist/chat/opened.d.ts.map +0 -1
  196. package/dist/chat/opened.js +0 -17
  197. package/dist/chat/overlay.d.ts +0 -31
  198. package/dist/chat/overlay.d.ts.map +0 -1
  199. package/dist/chat/overlay.js +0 -97
  200. package/dist/chat/parts.d.ts +0 -57
  201. package/dist/chat/parts.d.ts.map +0 -1
  202. package/dist/chat/parts.js +0 -119
  203. package/dist/chat/process.d.ts +0 -20
  204. package/dist/chat/process.d.ts.map +0 -1
  205. package/dist/chat/process.js +0 -32
  206. package/dist/chat/provider.d.ts +0 -63
  207. package/dist/chat/provider.d.ts.map +0 -1
  208. package/dist/chat/provider.js +0 -628
  209. package/dist/chat/question.d.ts +0 -9
  210. package/dist/chat/question.d.ts.map +0 -1
  211. package/dist/chat/question.js +0 -69
  212. package/dist/chat/questions.d.ts +0 -31
  213. package/dist/chat/questions.d.ts.map +0 -1
  214. package/dist/chat/questions.js +0 -111
  215. package/dist/chat/read.d.ts +0 -5
  216. package/dist/chat/read.d.ts.map +0 -1
  217. package/dist/chat/read.js +0 -13
  218. package/dist/chat/ready.d.ts +0 -34
  219. package/dist/chat/ready.d.ts.map +0 -1
  220. package/dist/chat/ready.js +0 -91
  221. package/dist/chat/style.d.ts +0 -4
  222. package/dist/chat/style.d.ts.map +0 -1
  223. package/dist/chat/style.js +0 -302
  224. package/dist/chat.d.ts +0 -14
  225. package/dist/chat.d.ts.map +0 -1
  226. package/dist/chat.js +0 -8
  227. package/dist/read-side.d.ts +0 -25
  228. package/dist/read-side.d.ts.map +0 -1
  229. package/dist/read-side.js +0 -767
@@ -1,6 +1,6 @@
1
1
  import { conversationChangeSet } from './change-set.js';
2
2
  import { runtimeError } from './failure.js';
3
- import { declaredFile } from './file.js';
3
+ import { refusedForSpace } from './durability.js';
4
4
  import { changeBatchIdentity, chatDraftRevision } from './identity.js';
5
5
  import { OFFLINE } from './outcome.js';
6
6
  import { capped, cursorOf, decodedCursor, FROM_START, messageOrder, precedes } from './windows.js';
@@ -10,7 +10,19 @@ const QUIET = {
10
10
  process: null,
11
11
  commands: [],
12
12
  options: [],
13
+ starting: null,
14
+ link: null,
15
+ deliveryUnknown: [],
13
16
  };
17
+ const FACTS = [
18
+ 'questions',
19
+ 'process',
20
+ 'commands',
21
+ 'options',
22
+ 'starting',
23
+ 'link',
24
+ 'deliveryUnknown',
25
+ ];
14
26
  export function unheldView(conversation) {
15
27
  return {
16
28
  conversation,
@@ -20,19 +32,25 @@ export function unheldView(conversation) {
20
32
  process: null,
21
33
  commands: [],
22
34
  options: [],
35
+ starting: null,
36
+ link: null,
37
+ deliveryUnknown: [],
23
38
  chatDraft: null,
24
39
  };
25
40
  }
26
41
  export const SESSION_STARTED = 'Session started';
27
42
  const NEW_CHAT = 'new:';
43
+ export const HISTORY_CONVERSATIONS = 64;
28
44
  export function newChat(agent) {
29
45
  return `${NEW_CHAT}${agent}`;
30
46
  }
31
47
  function creationOf(input) {
32
48
  if (typeof input !== 'object' || input === null)
33
49
  return null;
34
- const { agent_id: agent, operation_id: operation } = input;
35
- return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : 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;
36
54
  }
37
55
  function startedTurn(turn, operation) {
38
56
  if (operation.op === 'begin')
@@ -192,8 +210,14 @@ function askedOf(options) {
192
210
  return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
193
211
  }
194
212
  const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
213
+ const DURABLE_WRITE = {
214
+ kind: 'rejected',
215
+ code: 'durable-write',
216
+ fields: {},
217
+ retryable: true,
218
+ };
195
219
  function unwritten(draft) {
196
- return draft.text === '' && draft.files.length === 0;
220
+ return draft.text === '';
197
221
  }
198
222
  function pending(draft) {
199
223
  return {
@@ -209,10 +233,7 @@ function withSubmitted(draft, submitted) {
209
233
  return submitted.length === 0 ? unsent : { ...unsent, submitted };
210
234
  }
211
235
  function prepended(draft, said) {
212
- return {
213
- text: [said.text, draft.text].filter((one) => one !== '').join('\n'),
214
- files: [...said.files, ...draft.files],
215
- };
236
+ return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
216
237
  }
217
238
  function returned(draft, submission, others) {
218
239
  return withSubmitted({
@@ -231,15 +252,22 @@ export function createConversationStore(deps) {
231
252
  const catching = new Map();
232
253
  const gaps = new Map();
233
254
  const lives = new Map();
255
+ const unsent = new Map();
234
256
  const views = new Map();
235
257
  const listing = new Map();
236
258
  const creations = new Set();
237
259
  const listWatchers = new Set();
260
+ const unlisted = new Set();
238
261
  let listKnown = false;
262
+ let listHeld = false;
263
+ let listKept = false;
239
264
  const agentWatchers = new Set();
265
+ const unnamed = new Set();
266
+ let agentsKnown = false;
240
267
  let agentNames = new Map();
241
268
  let retiredAgents = new Set();
242
269
  let agentsRead = null;
270
+ let agentsFrom;
243
271
  let replicated = new Set();
244
272
  let handedReplicated;
245
273
  function liveOf(conversation) {
@@ -262,9 +290,12 @@ export function createConversationStore(deps) {
262
290
  process: facts.process === null ? null : { ...facts.process },
263
291
  commands: structuredClone(facts.commands),
264
292
  options: structuredClone(facts.options),
293
+ starting: facts.starting,
294
+ link: facts.link === null ? null : { ...facts.link },
295
+ deliveryUnknown: [...facts.deliveryUnknown],
265
296
  chatDraft: draft === undefined || unwritten(draft)
266
297
  ? null
267
- : { text: draft.text, files: [...draft.files], revision: draft.revision },
298
+ : { text: draft.text, revision: draft.revision },
268
299
  };
269
300
  }
270
301
  function emit(conversation) {
@@ -306,9 +337,6 @@ export function createConversationStore(deps) {
306
337
  }
307
338
  }
308
339
  }
309
- function changesOf(conversation, arriving) {
310
- return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
311
- }
312
340
  function stateOf(conversation) {
313
341
  const facts = liveOf(conversation);
314
342
  if (facts.questions.length > 0)
@@ -323,16 +351,58 @@ export function createConversationStore(deps) {
323
351
  created: entry.created,
324
352
  finished: entry.finished,
325
353
  outcome: entry.outcome,
354
+ waiting: entry.waiting,
326
355
  state: stateOf(entry.conversation),
327
356
  started: lives.get(entry.conversation)?.turn?.started ?? null,
328
357
  }));
329
358
  }
359
+ function keepList() {
360
+ if (!listKnown || listKept)
361
+ return;
362
+ listKept = true;
363
+ queueMicrotask(() => {
364
+ listKept = false;
365
+ if (deps.closed())
366
+ return;
367
+ const entries = [...listing.values()];
368
+ void deps.durably((local) => local.fact('conversationList', entries)).catch(() => { });
369
+ });
370
+ }
330
371
  function emitList() {
331
- if (deps.closed() || listWatchers.size === 0)
372
+ if (deps.closed())
332
373
  return;
333
- const value = entries();
334
- for (const watcher of listWatchers)
335
- watcher(value);
374
+ if (listWatchers.size > 0) {
375
+ const value = entries();
376
+ for (const watcher of listWatchers)
377
+ watcher(value);
378
+ }
379
+ learned();
380
+ }
381
+ function unlearnable() {
382
+ return !deps.online() || (deps.listed() && deps.agents() === undefined);
383
+ }
384
+ function learned() {
385
+ if (deps.closed())
386
+ return;
387
+ const named = agentsKnown || unlearnable();
388
+ if (unlisted.size > 0 &&
389
+ named &&
390
+ (listKnown || listHeld || deps.stream() === undefined || unlearnable())) {
391
+ const value = entries();
392
+ for (const watcher of [...unlisted]) {
393
+ unlisted.delete(watcher);
394
+ listWatchers.add(watcher);
395
+ watcher(value);
396
+ }
397
+ }
398
+ if (unnamed.size > 0 && named) {
399
+ const value = agents();
400
+ for (const watcher of [...unnamed]) {
401
+ unnamed.delete(watcher);
402
+ agentWatchers.add(watcher);
403
+ watcher(value);
404
+ }
405
+ }
336
406
  }
337
407
  function agents() {
338
408
  return [...agentNames]
@@ -363,34 +433,46 @@ export function createConversationStore(deps) {
363
433
  }
364
434
  async function readAgents() {
365
435
  const declaration = deps.agents();
436
+ agentsFrom = JSON.stringify(declaration);
366
437
  if (declaration === undefined)
367
438
  return;
368
- const held = await deps.documents(declaration);
369
- if ('pending' in held)
370
- return;
371
- const { documents } = held;
372
439
  const names = new Map();
373
440
  const retired = new Set();
374
- for (const document of documents) {
375
- if (document.type !== declaration.type)
376
- continue;
377
- const identity = document.fields?.[declaration.identity];
378
- const name = document.fields?.[declaration.name];
379
- if (typeof identity !== 'string' || typeof name !== 'string')
380
- continue;
381
- names.set(identity, name);
382
- const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
383
- if (ended !== undefined && ended !== null)
384
- retired.add(identity);
441
+ let covered = null;
442
+ for (let after;;) {
443
+ const read = await deps.documents(declaration, after);
444
+ if ('pending' in read)
445
+ return;
446
+ const coverage = JSON.stringify(read.coverage);
447
+ if (covered !== null && coverage !== covered)
448
+ return readAgents();
449
+ covered = coverage;
450
+ for (const document of read.documents) {
451
+ const identity = document.fields?.[declaration.identity];
452
+ const name = document.fields?.[declaration.name];
453
+ if (typeof identity !== 'string' || typeof name !== 'string')
454
+ continue;
455
+ names.set(identity, name);
456
+ const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
457
+ if (ended !== undefined && ended !== null)
458
+ retired.add(identity);
459
+ }
460
+ if (read.next === undefined)
461
+ break;
462
+ after = read.next;
385
463
  }
464
+ agentsKnown = true;
386
465
  const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
387
- if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
466
+ if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents])) {
467
+ learned();
388
468
  return;
469
+ }
389
470
  agentNames = names;
390
471
  retiredAgents = retired;
472
+ emitAgents();
391
473
  if (named)
392
474
  emitList();
393
- emitAgents();
475
+ learned();
394
476
  const ended = [...drafts.keys()].filter(retiredChat);
395
477
  if (ended.length > 0)
396
478
  await remove(ended);
@@ -405,9 +487,34 @@ export function createConversationStore(deps) {
405
487
  return agentsRead;
406
488
  }
407
489
  function live(conversation, change) {
490
+ const owed = unsent.get(conversation);
491
+ for (const fact of FACTS.filter((one) => one in change))
492
+ owed?.delete(fact);
408
493
  setLive(conversation, { ...liveOf(conversation), ...change });
409
494
  emit(conversation);
410
495
  }
496
+ function openingEnded() {
497
+ const owed = [...unsent];
498
+ unsent.clear();
499
+ for (const [conversation, facts] of owed) {
500
+ const held = lives.get(conversation);
501
+ if (held === undefined || facts.size === 0)
502
+ continue;
503
+ setLive(conversation, {
504
+ turn: held.turn,
505
+ questions: facts.has('questions') ? QUIET.questions : held.questions,
506
+ process: facts.has('process') ? QUIET.process : held.process,
507
+ commands: facts.has('commands') ? QUIET.commands : held.commands,
508
+ options: facts.has('options') ? QUIET.options : held.options,
509
+ starting: facts.has('starting') ? QUIET.starting : held.starting,
510
+ link: facts.has('link') ? QUIET.link : held.link,
511
+ deliveryUnknown: facts.has('deliveryUnknown')
512
+ ? QUIET.deliveryUnknown
513
+ : held.deliveryUnknown,
514
+ });
515
+ emit(conversation);
516
+ }
517
+ }
411
518
  function relayed(conversation, turn, operation) {
412
519
  const held = liveOf(conversation).turn;
413
520
  const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
@@ -434,12 +541,6 @@ export function createConversationStore(deps) {
434
541
  }
435
542
  async function remove(leaving) {
436
543
  await deps.durably(async (local) => {
437
- const removed = [];
438
- for (const conversation of leaving) {
439
- for (const identity of await local.messageIdentities(conversation)) {
440
- removed.push({ conversation, identity, text: null });
441
- }
442
- }
443
544
  await local.dropConversations(leaving);
444
545
  const changed = [];
445
546
  for (const conversation of leaving) {
@@ -451,13 +552,28 @@ export function createConversationStore(deps) {
451
552
  partials.delete(conversation);
452
553
  drafts.delete(conversation);
453
554
  behind.delete(conversation);
555
+ unsent.delete(conversation);
454
556
  setLive(conversation, null);
455
557
  await slide(local, conversation, null);
456
558
  }
457
559
  if (changed.length > 0)
458
- deps.publish(conversationChangeSet(removed, changed));
560
+ deps.publish(conversationChangeSet(changed));
459
561
  });
460
562
  }
563
+ function missed(conversation) {
564
+ const read = reads.get(conversation);
565
+ const newest = listing.get(conversation)?.newest;
566
+ return (read === undefined ||
567
+ newest === undefined ||
568
+ partials.has(conversation) ||
569
+ (newest !== null && (read === null || newest > read)));
570
+ }
571
+ function noted(conversation, message) {
572
+ const entry = listing.get(conversation);
573
+ if (entry === undefined || (entry.newest !== null && entry.newest >= message.position))
574
+ return;
575
+ listing.set(conversation, { ...entry, newest: message.position });
576
+ }
461
577
  function gap(conversation) {
462
578
  gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
463
579
  behind.add(conversation);
@@ -481,16 +597,17 @@ export function createConversationStore(deps) {
481
597
  reads.set(conversation, message.position);
482
598
  sealTurns(conversation, new Set([message.turn]));
483
599
  await slide(local, conversation, [message]);
484
- deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
600
+ deps.publish(conversationChangeSet([conversation]));
485
601
  });
486
602
  }
487
603
  async function readHistories(conversations, ended) {
488
604
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
489
- let asking = conversations.map((conversation) => ({
605
+ let waiting = conversations.map((conversation) => ({
490
606
  conversation,
491
607
  after: reads.get(conversation) ?? undefined,
492
608
  }));
493
- while (asking.length > 0) {
609
+ while (waiting.length > 0) {
610
+ const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
494
611
  const result = await deps.call('conversation.history', {
495
612
  conversations: asking.map(({ conversation, after }) => ({
496
613
  conversation,
@@ -514,7 +631,7 @@ export function createConversationStore(deps) {
514
631
  if (!following.some((next) => next.conversation === conversation))
515
632
  ended(conversation);
516
633
  }
517
- asking = following;
634
+ waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
518
635
  }
519
636
  return null;
520
637
  }
@@ -557,7 +674,7 @@ export function createConversationStore(deps) {
557
674
  behind.delete(conversation);
558
675
  await slide(local, conversation, arriving);
559
676
  if (arriving.length > 0 || sealing || whole) {
560
- deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
677
+ deps.publish(conversationChangeSet([conversation]));
561
678
  }
562
679
  });
563
680
  }
@@ -566,11 +683,8 @@ export function createConversationStore(deps) {
566
683
  if (event.kind === 'gap') {
567
684
  if (replicated.has(conversation))
568
685
  gap(conversation);
569
- const held = lives.get(conversation);
570
- if (held === undefined)
571
- return;
572
- setLive(conversation, { ...QUIET, turn: held.turn });
573
- emit(conversation);
686
+ if (lives.has(conversation))
687
+ unsent.set(conversation, new Set(FACTS));
574
688
  return;
575
689
  }
576
690
  if (event.kind === 'turn') {
@@ -593,11 +707,31 @@ export function createConversationStore(deps) {
593
707
  live(conversation, { options: structuredClone(event.options) });
594
708
  return;
595
709
  }
710
+ if (event.kind === 'starting') {
711
+ live(conversation, { starting: event.step });
712
+ return;
713
+ }
714
+ if (event.kind === 'link') {
715
+ live(conversation, { link: { connected: event.connected } });
716
+ return;
717
+ }
718
+ if (event.kind === 'deliveries') {
719
+ live(conversation, { deliveryUnknown: [...event.unknown] });
720
+ return;
721
+ }
596
722
  const arriving = acceptedMessage(event.message);
597
723
  if (arriving === null)
598
724
  return;
725
+ noted(conversation, arriving);
599
726
  if (replicated.has(conversation)) {
600
- void keep(conversation, arriving).catch(() => { });
727
+ if (deps.full()) {
728
+ gap(conversation);
729
+ return;
730
+ }
731
+ void keep(conversation, arriving).catch((error) => {
732
+ if (refusedForSpace(error))
733
+ gap(conversation);
734
+ });
601
735
  return;
602
736
  }
603
737
  if (sealTurns(conversation, new Set([arriving.turn])))
@@ -632,7 +766,6 @@ export function createConversationStore(deps) {
632
766
  const blank = {
633
767
  conversation: from,
634
768
  text: '',
635
- files: [],
636
769
  revision: chatDraftRevision(),
637
770
  };
638
771
  const { moves: _answered, ...unsent } = held ?? blank;
@@ -642,7 +775,7 @@ export function createConversationStore(deps) {
642
775
  drafts.set(from, left);
643
776
  for (const one of taken)
644
777
  drafts.set(to, one);
645
- deps.publish(conversationChangeSet([], [from, to]));
778
+ deps.publish(conversationChangeSet([from, to]));
646
779
  });
647
780
  }
648
781
  function withDraft(conversation, next) {
@@ -651,7 +784,7 @@ export function createConversationStore(deps) {
651
784
  if (following === undefined)
652
785
  return;
653
786
  await kept(local, conversation, following);
654
- deps.publish(conversationChangeSet([], [conversation]));
787
+ deps.publish(conversationChangeSet([conversation]));
655
788
  });
656
789
  }
657
790
  async function submitted(conversation, operation, draft) {
@@ -663,17 +796,12 @@ export function createConversationStore(deps) {
663
796
  if (submission !== undefined)
664
797
  return undefined;
665
798
  const said = draft ?? current;
666
- submission = {
667
- operation,
668
- text: said?.text ?? '',
669
- files: [...(said?.files ?? [])],
670
- turn: null,
671
- };
799
+ submission = { operation, text: said?.text ?? '', turn: null };
672
800
  return {
673
801
  conversation,
674
802
  ...(draft === undefined || current === undefined
675
- ? { text: '', files: [], revision: chatDraftRevision() }
676
- : { text: current.text, files: current.files, revision: current.revision }),
803
+ ? { text: '', revision: chatDraftRevision() }
804
+ : { text: current.text, revision: current.revision }),
677
805
  submitted: [...(current?.submitted ?? []), submission],
678
806
  };
679
807
  });
@@ -688,15 +816,14 @@ export function createConversationStore(deps) {
688
816
  });
689
817
  }
690
818
  async function dispatched(conversation, submission) {
691
- const dispatch = {};
819
+ const dispatch = { answered: false };
692
820
  sending.set(submission.operation, dispatch);
693
821
  try {
694
- const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
695
822
  const result = await invoke('conversation.send', conversation, {
696
823
  text: submission.text,
697
- files,
698
824
  operation_id: submission.operation,
699
825
  });
826
+ dispatch.answered = result.kind !== 'transport-uncertainty';
700
827
  if (result.kind === 'revoked-authorization')
701
828
  return result;
702
829
  if (result.kind !== 'action' && result.retryable) {
@@ -729,7 +856,15 @@ export function createConversationStore(deps) {
729
856
  const surface = {
730
857
  async list() {
731
858
  await agentsReady();
732
- return entries();
859
+ return new Promise((settle) => {
860
+ const answer = (listed) => {
861
+ unlisted.delete(answer);
862
+ listWatchers.delete(answer);
863
+ settle(listed);
864
+ };
865
+ unlisted.add(answer);
866
+ learned();
867
+ });
733
868
  },
734
869
  async read(conversation, options = {}) {
735
870
  const asked = askedOf(options);
@@ -752,8 +887,17 @@ export function createConversationStore(deps) {
752
887
  return result;
753
888
  },
754
889
  async send(conversation, operation = changeBatchIdentity(), draft) {
890
+ if (deps.full())
891
+ return DURABLE_WRITE;
755
892
  if (deps.online()) {
756
- const { submission, earlier } = await submitted(conversation, operation, draft);
893
+ const held = await submitted(conversation, operation, draft).catch((error) => {
894
+ if (refusedForSpace(error))
895
+ return null;
896
+ throw error;
897
+ });
898
+ if (held === null)
899
+ return DURABLE_WRITE;
900
+ const { submission, earlier } = held;
757
901
  if (!earlier || !deps.expired(operation))
758
902
  return dispatched(conversation, submission);
759
903
  await forgotten(conversation, submission);
@@ -762,7 +906,7 @@ export function createConversationStore(deps) {
762
906
  if (draft !== undefined) {
763
907
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
764
908
  ? undefined
765
- : returned(current ?? { conversation, text: '', files: [], revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
909
+ : returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
766
910
  }
767
911
  return OFFLINE;
768
912
  },
@@ -796,7 +940,6 @@ export function createConversationStore(deps) {
796
940
  return withDraft(conversation, (current) => ({
797
941
  conversation,
798
942
  text: draft.text,
799
- files: [...draft.files],
800
943
  revision,
801
944
  ...(current === undefined ? {} : pending(current)),
802
945
  }));
@@ -804,9 +947,7 @@ export function createConversationStore(deps) {
804
947
  prepend(conversation, draft) {
805
948
  return withDraft(conversation, (current) => ({
806
949
  conversation,
807
- ...(current === undefined
808
- ? { text: draft.text, files: [...draft.files] }
809
- : prepended(current, draft)),
950
+ ...(current === undefined ? { text: draft.text } : prepended(current, draft)),
810
951
  revision: chatDraftRevision(),
811
952
  ...(current === undefined ? {} : pending(current)),
812
953
  }));
@@ -814,19 +955,19 @@ export function createConversationStore(deps) {
814
955
  discard(conversation) {
815
956
  return withDraft(conversation, (current) => current === undefined
816
957
  ? undefined
817
- : {
818
- conversation,
819
- text: '',
820
- files: [],
821
- revision: chatDraftRevision(),
822
- ...pending(current),
823
- });
958
+ : { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
824
959
  },
825
960
  };
826
961
  return {
827
962
  surface,
828
963
  chatDraft,
829
- hydrate(held, kept) {
964
+ hydrate(held, kept, list) {
965
+ if (list !== null && !listKnown) {
966
+ listing.clear();
967
+ for (const entry of list)
968
+ listing.set(entry.conversation, { ...entry });
969
+ listHeld = true;
970
+ }
830
971
  stored.clear();
831
972
  reads.clear();
832
973
  partials.clear();
@@ -840,6 +981,8 @@ export function createConversationStore(deps) {
840
981
  drafts.clear();
841
982
  for (const record of kept)
842
983
  drafts.set(record.conversation, record);
984
+ if (listHeld)
985
+ learned();
843
986
  },
844
987
  async reconcile() {
845
988
  if (!listKnown)
@@ -851,7 +994,7 @@ export function createConversationStore(deps) {
851
994
  !creations.has(conversation) &&
852
995
  (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
853
996
  for (const conversation of named) {
854
- if (!replicated.has(conversation))
997
+ if (!replicated.has(conversation) && missed(conversation))
855
998
  behind.add(conversation);
856
999
  }
857
1000
  replicated = named;
@@ -860,6 +1003,11 @@ export function createConversationStore(deps) {
860
1003
  await remove(leaving);
861
1004
  },
862
1005
  async catchUp(progress) {
1006
+ if (deps.full()) {
1007
+ if (behind.size > 0)
1008
+ progress.complete = false;
1009
+ return;
1010
+ }
863
1011
  const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
864
1012
  const read = async (conversations) => {
865
1013
  if (conversations.length === 0 || deps.closed())
@@ -908,12 +1056,10 @@ export function createConversationStore(deps) {
908
1056
  known() {
909
1057
  return listKnown;
910
1058
  },
911
- reconnected() {
912
- for (const conversation of replicated)
913
- gap(conversation);
914
- },
915
1059
  reopened() {
916
- sending.clear();
1060
+ for (const [operation, dispatch] of sending)
1061
+ if (!dispatch.answered)
1062
+ sending.delete(operation);
917
1063
  },
918
1064
  lost() {
919
1065
  catching.clear();
@@ -957,25 +1103,35 @@ export function createConversationStore(deps) {
957
1103
  };
958
1104
  },
959
1105
  watchList(deliver) {
960
- listWatchers.add(deliver);
1106
+ let ended = false;
961
1107
  void agentsReady().then(() => {
962
- if (!deps.closed() && listWatchers.has(deliver))
963
- deliver(entries());
1108
+ if (ended)
1109
+ return;
1110
+ unlisted.add(deliver);
1111
+ learned();
964
1112
  });
965
1113
  return () => {
1114
+ ended = true;
1115
+ unlisted.delete(deliver);
966
1116
  listWatchers.delete(deliver);
967
1117
  };
968
1118
  },
969
1119
  watchAgents(deliver) {
970
- agentWatchers.add(deliver);
971
- void agentsReady().then(() => {
972
- if (!deps.closed() && agentWatchers.has(deliver))
973
- deliver(agents());
974
- });
1120
+ unnamed.add(deliver);
1121
+ void agentsReady().then(learned);
975
1122
  return () => {
1123
+ unnamed.delete(deliver);
976
1124
  agentWatchers.delete(deliver);
977
1125
  };
978
1126
  },
1127
+ learned,
1128
+ declared() {
1129
+ if (JSON.stringify(deps.agents()) === agentsFrom)
1130
+ return;
1131
+ agentsRead = readAgents().catch(() => {
1132
+ agentsRead = null;
1133
+ });
1134
+ },
979
1135
  changed(set) {
980
1136
  for (const conversation of set.conversations)
981
1137
  emit(conversation);
@@ -998,6 +1154,15 @@ export function createConversationStore(deps) {
998
1154
  creations.delete(entry.conversation);
999
1155
  }
1000
1156
  listKnown = true;
1157
+ for (const conversation of replicated) {
1158
+ if (!listing.has(conversation))
1159
+ continue;
1160
+ if (!missed(conversation))
1161
+ behind.delete(conversation);
1162
+ else if (!behind.has(conversation))
1163
+ gap(conversation);
1164
+ }
1165
+ openingEnded();
1001
1166
  }
1002
1167
  else if (event.kind === 'listed') {
1003
1168
  listing.set(event.entry.conversation, { ...event.entry });
@@ -1006,6 +1171,7 @@ export function createConversationStore(deps) {
1006
1171
  else {
1007
1172
  listing.delete(event.conversation);
1008
1173
  }
1174
+ keepList();
1009
1175
  emitList();
1010
1176
  return (event.kind !== 'list' &&
1011
1177
  listKnown &&
@@ -1024,8 +1190,11 @@ export function createConversationStore(deps) {
1024
1190
  close() {
1025
1191
  views.clear();
1026
1192
  lives.clear();
1193
+ unsent.clear();
1027
1194
  listWatchers.clear();
1195
+ unlisted.clear();
1028
1196
  agentWatchers.clear();
1197
+ unnamed.clear();
1029
1198
  },
1030
1199
  };
1031
1200
  }