@agentshouse/mdruntime 0.1.0-cee9c8aa4cd86 → 0.1.0-cef5833f6c689

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 (224) hide show
  1. package/README.md +11 -9
  2. package/dist/adapter.d.ts +96 -67
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +2 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +28 -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 +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/client.d.ts +12 -12
  16. package/dist/client.d.ts.map +1 -1
  17. package/dist/client.js +54 -8
  18. package/dist/collection.d.ts +7 -4
  19. package/dist/collection.d.ts.map +1 -1
  20. package/dist/collection.js +24 -19
  21. package/dist/compile.d.ts +1 -6
  22. package/dist/compile.d.ts.map +1 -1
  23. package/dist/compile.js +118 -247
  24. package/dist/connection.d.ts +11 -11
  25. package/dist/connection.d.ts.map +1 -1
  26. package/dist/connection.js +1598 -1248
  27. package/dist/context.d.ts +2 -0
  28. package/dist/context.d.ts.map +1 -0
  29. package/dist/{chat/context.js → context.js} +1 -4
  30. package/dist/conversation.d.ts +18 -15
  31. package/dist/conversation.d.ts.map +1 -1
  32. package/dist/conversation.js +268 -171
  33. package/dist/durability.d.ts +1 -5
  34. package/dist/durability.d.ts.map +1 -1
  35. package/dist/durability.js +5 -0
  36. package/dist/entry.d.ts +3 -2
  37. package/dist/entry.d.ts.map +1 -1
  38. package/dist/entry.js +15 -6
  39. package/dist/file.d.ts +2 -11
  40. package/dist/file.d.ts.map +1 -1
  41. package/dist/file.js +6 -4
  42. package/dist/host.d.ts +3 -6
  43. package/dist/host.d.ts.map +1 -1
  44. package/dist/host.js +49 -35
  45. package/dist/identity.d.ts +0 -1
  46. package/dist/identity.d.ts.map +1 -1
  47. package/dist/identity.js +0 -3
  48. package/dist/index.d.ts +15 -12
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +6 -3
  51. package/dist/job.d.ts +14 -0
  52. package/dist/job.d.ts.map +1 -0
  53. package/dist/job.js +28 -0
  54. package/dist/live.d.ts +2 -1
  55. package/dist/live.d.ts.map +1 -1
  56. package/dist/live.js +9 -5
  57. package/dist/media.d.ts +8 -0
  58. package/dist/media.d.ts.map +1 -0
  59. package/dist/media.js +218 -0
  60. package/dist/memory.d.ts +11 -6
  61. package/dist/memory.d.ts.map +1 -1
  62. package/dist/memory.js +178 -101
  63. package/dist/messages.d.ts +2 -2
  64. package/dist/messages.d.ts.map +1 -1
  65. package/dist/messages.js +15 -16
  66. package/dist/model.d.ts +7 -3
  67. package/dist/model.d.ts.map +1 -1
  68. package/dist/model.js +7 -13
  69. package/dist/observation.d.ts +3 -2
  70. package/dist/observation.d.ts.map +1 -1
  71. package/dist/observation.js +8 -3
  72. package/dist/outcome.d.ts +13 -6
  73. package/dist/outcome.d.ts.map +1 -1
  74. package/dist/outcome.js +16 -5
  75. package/dist/overlay.js +1 -1
  76. package/dist/paths.d.ts +1 -0
  77. package/dist/paths.d.ts.map +1 -1
  78. package/dist/paths.js +4 -0
  79. package/dist/query.d.ts +10 -205
  80. package/dist/query.d.ts.map +1 -1
  81. package/dist/query.js +3 -494
  82. package/dist/reach.d.ts +14 -9
  83. package/dist/reach.d.ts.map +1 -1
  84. package/dist/reach.js +81 -71
  85. package/dist/react.d.ts +3 -1
  86. package/dist/react.d.ts.map +1 -1
  87. package/dist/readiness.d.ts +13 -16
  88. package/dist/readiness.d.ts.map +1 -1
  89. package/dist/readiness.js +2 -8
  90. package/dist/recovery.d.ts +1 -0
  91. package/dist/recovery.d.ts.map +1 -1
  92. package/dist/recovery.js +26 -0
  93. package/dist/refresh.d.ts +10 -4
  94. package/dist/refresh.d.ts.map +1 -1
  95. package/dist/refresh.js +104 -30
  96. package/dist/replica.d.ts +6 -5
  97. package/dist/replica.d.ts.map +1 -1
  98. package/dist/replica.js +8 -7
  99. package/dist/room-draft.d.ts +22 -5
  100. package/dist/room-draft.d.ts.map +1 -1
  101. package/dist/room-draft.js +131 -51
  102. package/dist/rows.d.ts +8 -15
  103. package/dist/rows.d.ts.map +1 -1
  104. package/dist/rows.js +51 -151
  105. package/dist/save.d.ts +2 -0
  106. package/dist/save.d.ts.map +1 -1
  107. package/dist/save.js +13 -7
  108. package/dist/search.d.ts +12 -63
  109. package/dist/search.d.ts.map +1 -1
  110. package/dist/search.js +9 -52
  111. package/dist/shared.d.ts +0 -1
  112. package/dist/shared.d.ts.map +1 -1
  113. package/dist/shared.js +0 -10
  114. package/dist/status.d.ts +4 -3
  115. package/dist/status.d.ts.map +1 -1
  116. package/dist/status.js +3 -1
  117. package/dist/store.d.ts +32 -31
  118. package/dist/store.d.ts.map +1 -1
  119. package/dist/store.js +67 -63
  120. package/dist/stored.d.ts +42 -18
  121. package/dist/stored.d.ts.map +1 -1
  122. package/dist/stored.js +193 -194
  123. package/dist/tables.d.ts +60 -40
  124. package/dist/tables.d.ts.map +1 -1
  125. package/dist/tables.js +78 -62
  126. package/dist/transfer.d.ts +0 -5
  127. package/dist/transfer.d.ts.map +1 -1
  128. package/dist/validation.d.ts +3 -4
  129. package/dist/validation.d.ts.map +1 -1
  130. package/dist/validation.js +39 -97
  131. package/dist/windows.d.ts +1 -12
  132. package/dist/windows.d.ts.map +1 -1
  133. package/dist/windows.js +2 -75
  134. package/dist/wire.d.ts +6 -9
  135. package/dist/wire.d.ts.map +1 -1
  136. package/dist/wire.js +3 -3
  137. package/package.json +11 -18
  138. package/AGENTS.md +0 -320
  139. package/dist/chat/acts.d.ts +0 -33
  140. package/dist/chat/acts.d.ts.map +0 -1
  141. package/dist/chat/acts.js +0 -36
  142. package/dist/chat/context.d.ts +0 -3
  143. package/dist/chat/context.d.ts.map +0 -1
  144. package/dist/chat/earlier.d.ts +0 -10
  145. package/dist/chat/earlier.d.ts.map +0 -1
  146. package/dist/chat/earlier.js +0 -54
  147. package/dist/chat/house.d.ts +0 -25
  148. package/dist/chat/house.d.ts.map +0 -1
  149. package/dist/chat/house.js +0 -38
  150. package/dist/chat/input.d.ts +0 -11
  151. package/dist/chat/input.d.ts.map +0 -1
  152. package/dist/chat/input.js +0 -92
  153. package/dist/chat/kit.d.ts +0 -61
  154. package/dist/chat/kit.d.ts.map +0 -1
  155. package/dist/chat/kit.js +0 -123
  156. package/dist/chat/lasting.d.ts +0 -6
  157. package/dist/chat/lasting.d.ts.map +0 -1
  158. package/dist/chat/lasting.js +0 -19
  159. package/dist/chat/list.d.ts +0 -28
  160. package/dist/chat/list.d.ts.map +0 -1
  161. package/dist/chat/list.js +0 -103
  162. package/dist/chat/markdown.d.ts +0 -21
  163. package/dist/chat/markdown.d.ts.map +0 -1
  164. package/dist/chat/markdown.js +0 -246
  165. package/dist/chat/message.d.ts +0 -7
  166. package/dist/chat/message.d.ts.map +0 -1
  167. package/dist/chat/message.js +0 -254
  168. package/dist/chat/messages.d.ts +0 -35
  169. package/dist/chat/messages.d.ts.map +0 -1
  170. package/dist/chat/messages.js +0 -210
  171. package/dist/chat/name.d.ts +0 -7
  172. package/dist/chat/name.d.ts.map +0 -1
  173. package/dist/chat/name.js +0 -37
  174. package/dist/chat/opened.d.ts +0 -12
  175. package/dist/chat/opened.d.ts.map +0 -1
  176. package/dist/chat/opened.js +0 -19
  177. package/dist/chat/overlay.d.ts +0 -32
  178. package/dist/chat/overlay.d.ts.map +0 -1
  179. package/dist/chat/overlay.js +0 -103
  180. package/dist/chat/parts.d.ts +0 -58
  181. package/dist/chat/parts.d.ts.map +0 -1
  182. package/dist/chat/parts.js +0 -120
  183. package/dist/chat/pieces.d.ts +0 -62
  184. package/dist/chat/pieces.d.ts.map +0 -1
  185. package/dist/chat/pieces.js +0 -1110
  186. package/dist/chat/process.d.ts +0 -20
  187. package/dist/chat/process.d.ts.map +0 -1
  188. package/dist/chat/process.js +0 -32
  189. package/dist/chat/provider.d.ts +0 -70
  190. package/dist/chat/provider.d.ts.map +0 -1
  191. package/dist/chat/provider.js +0 -635
  192. package/dist/chat/question.d.ts +0 -9
  193. package/dist/chat/question.d.ts.map +0 -1
  194. package/dist/chat/question.js +0 -69
  195. package/dist/chat/questions.d.ts +0 -31
  196. package/dist/chat/questions.d.ts.map +0 -1
  197. package/dist/chat/questions.js +0 -111
  198. package/dist/chat/read.d.ts +0 -5
  199. package/dist/chat/read.d.ts.map +0 -1
  200. package/dist/chat/read.js +0 -13
  201. package/dist/chat/ready.d.ts +0 -37
  202. package/dist/chat/ready.d.ts.map +0 -1
  203. package/dist/chat/ready.js +0 -93
  204. package/dist/chat/room-file.d.ts +0 -8
  205. package/dist/chat/room-file.d.ts.map +0 -1
  206. package/dist/chat/room-file.js +0 -72
  207. package/dist/chat/steady.d.ts +0 -3
  208. package/dist/chat/steady.d.ts.map +0 -1
  209. package/dist/chat/steady.js +0 -26
  210. package/dist/chat/style.d.ts +0 -4
  211. package/dist/chat/style.d.ts.map +0 -1
  212. package/dist/chat/style.js +0 -307
  213. package/dist/chat.d.ts +0 -15
  214. package/dist/chat.d.ts.map +0 -1
  215. package/dist/chat.js +0 -8
  216. package/dist/fulltext.d.ts +0 -27
  217. package/dist/fulltext.d.ts.map +0 -1
  218. package/dist/fulltext.js +0 -368
  219. package/dist/reads.d.ts +0 -11
  220. package/dist/reads.d.ts.map +0 -1
  221. package/dist/reads.js +0 -98
  222. package/dist/words.d.ts +0 -19
  223. package/dist/words.d.ts.map +0 -1
  224. 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
- 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)
@@ -333,7 +359,7 @@ export function createConversationStore(deps) {
333
359
  function entries() {
334
360
  return [...listing.values()].map((entry) => ({
335
361
  conversation: entry.conversation,
336
- agent: { identity: entry.agent, name: agentNames.get(entry.agent) ?? null },
362
+ agent: { identity: entry.agent, name: entry.agentName },
337
363
  name: entry.name,
338
364
  created: entry.created,
339
365
  finished: entry.finished,
@@ -343,25 +369,103 @@ export function createConversationStore(deps) {
343
369
  started: lives.get(entry.conversation)?.turn?.started ?? null,
344
370
  }));
345
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
+ }
346
397
  function emitList() {
347
- if (deps.closed() || listWatchers.size === 0)
398
+ if (deps.closed())
348
399
  return;
349
- const value = entries();
350
- for (const watcher of listWatchers)
351
- 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
+ }
352
429
  }
353
430
  function agents() {
354
- return [...agentNames]
355
- .filter(([identity]) => !retiredAgents.has(identity))
356
- .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 }))
357
440
  .sort((left, right) => left.name === right.name
358
441
  ? left.identity.localeCompare(right.identity)
359
442
  : left.name.localeCompare(right.name));
360
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
+ }
361
461
  function emitAgents() {
362
462
  if (deps.closed() || agentWatchers.size === 0)
363
463
  return;
364
464
  const value = agents();
465
+ const shown = JSON.stringify(value);
466
+ if (shown === agentsShown)
467
+ return;
468
+ agentsShown = shown;
365
469
  for (const watcher of agentWatchers)
366
470
  watcher(value);
367
471
  }
@@ -377,55 +481,11 @@ export function createConversationStore(deps) {
377
481
  if (stateOf(conversation) !== before || next?.turn?.started !== began)
378
482
  emitList();
379
483
  }
380
- async function readAgents() {
381
- const declaration = deps.agents();
382
- if (declaration === undefined)
383
- return;
384
- const names = new Map();
385
- const retired = new Set();
386
- let covered = null;
387
- for (let after;;) {
388
- const read = await deps.documents(declaration, after);
389
- if ('pending' in read)
390
- return;
391
- const coverage = JSON.stringify(read.coverage);
392
- if (covered !== null && coverage !== covered)
393
- return readAgents();
394
- covered = coverage;
395
- for (const document of read.documents) {
396
- const identity = document.fields?.[declaration.identity];
397
- const name = document.fields?.[declaration.name];
398
- if (typeof identity !== 'string' || typeof name !== 'string')
399
- continue;
400
- names.set(identity, name);
401
- const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
402
- if (ended !== undefined && ended !== null)
403
- retired.add(identity);
404
- }
405
- if (read.next === undefined)
406
- break;
407
- after = read.next;
408
- }
409
- const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
410
- if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
411
- return;
412
- agentNames = names;
413
- retiredAgents = retired;
414
- if (named)
415
- emitList();
416
- emitAgents();
417
- const ended = [...drafts.keys()].filter(retiredChat);
418
- if (ended.length > 0)
419
- await remove(ended);
420
- }
421
484
  function retiredChat(conversation) {
422
- return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
423
- }
424
- function agentsReady() {
425
- agentsRead ??= readAgents().catch(() => {
426
- agentsRead = null;
427
- });
428
- 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);
429
489
  }
430
490
  function live(conversation, change) {
431
491
  const owed = unsent.get(conversation);
@@ -448,6 +508,7 @@ export function createConversationStore(deps) {
448
508
  commands: facts.has('commands') ? QUIET.commands : held.commands,
449
509
  options: facts.has('options') ? QUIET.options : held.options,
450
510
  starting: facts.has('starting') ? QUIET.starting : held.starting,
511
+ subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
451
512
  link: facts.has('link') ? QUIET.link : held.link,
452
513
  deliveryUnknown: facts.has('deliveryUnknown')
453
514
  ? QUIET.deliveryUnknown
@@ -501,6 +562,20 @@ export function createConversationStore(deps) {
501
562
  deps.publish(conversationChangeSet(changed));
502
563
  });
503
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
+ }
504
579
  function gap(conversation) {
505
580
  gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
506
581
  behind.add(conversation);
@@ -529,12 +604,13 @@ export function createConversationStore(deps) {
529
604
  }
530
605
  async function readHistories(conversations, ended) {
531
606
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
532
- let asking = conversations.map((conversation) => ({
607
+ let waiting = conversations.map((conversation) => ({
533
608
  conversation,
534
609
  after: reads.get(conversation) ?? undefined,
535
610
  }));
536
- while (asking.length > 0) {
537
- 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({
538
614
  conversations: asking.map(({ conversation, after }) => ({
539
615
  conversation,
540
616
  ...(after === undefined ? {} : { after }),
@@ -557,7 +633,7 @@ export function createConversationStore(deps) {
557
633
  if (!following.some((next) => next.conversation === conversation))
558
634
  ended(conversation);
559
635
  }
560
- asking = following;
636
+ waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
561
637
  }
562
638
  return null;
563
639
  }
@@ -637,6 +713,10 @@ export function createConversationStore(deps) {
637
713
  live(conversation, { starting: event.step });
638
714
  return;
639
715
  }
716
+ if (event.kind === 'subagents') {
717
+ live(conversation, { subagents: structuredClone(event.subagents) });
718
+ return;
719
+ }
640
720
  if (event.kind === 'link') {
641
721
  live(conversation, { link: { connected: event.connected } });
642
722
  return;
@@ -648,8 +728,16 @@ export function createConversationStore(deps) {
648
728
  const arriving = acceptedMessage(event.message);
649
729
  if (arriving === null)
650
730
  return;
731
+ noted(conversation, arriving);
651
732
  if (replicated.has(conversation)) {
652
- 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
+ });
653
741
  return;
654
742
  }
655
743
  if (sealTurns(conversation, new Set([arriving.turn])))
@@ -678,7 +766,7 @@ export function createConversationStore(deps) {
678
766
  const from = newChat(agent);
679
767
  return deps.durably(async (local) => {
680
768
  const held = drafts.get(from);
681
- const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
769
+ const earlier = held?.moves ?? [];
682
770
  if (earlier.includes(operation))
683
771
  return;
684
772
  const blank = {
@@ -707,10 +795,8 @@ export function createConversationStore(deps) {
707
795
  }
708
796
  async function submitted(conversation, operation, draft) {
709
797
  let submission;
710
- let earlier = false;
711
798
  await withDraft(conversation, (current) => {
712
799
  submission = current?.submitted?.find((one) => one.operation === operation);
713
- earlier = submission !== undefined;
714
800
  if (submission !== undefined)
715
801
  return undefined;
716
802
  const said = draft ?? current;
@@ -723,31 +809,24 @@ export function createConversationStore(deps) {
723
809
  submitted: [...(current?.submitted ?? []), submission],
724
810
  };
725
811
  });
726
- return { submission: submission, earlier };
727
- }
728
- function forgotten(conversation, submission) {
729
- return withDraft(conversation, (current) => {
730
- const held = current?.submitted ?? [];
731
- if (current === undefined || !held.includes(submission))
732
- return undefined;
733
- return withSubmitted(current, held.filter((one) => one !== submission));
734
- });
812
+ return submission;
735
813
  }
736
814
  async function dispatched(conversation, submission) {
737
- const dispatch = {};
815
+ const dispatch = { answered: false };
738
816
  sending.set(submission.operation, dispatch);
739
817
  try {
740
- const result = await invoke('conversation.send', conversation, {
741
- text: submission.text,
818
+ const result = await invoke('conversations/send', conversation, {
742
819
  operation_id: submission.operation,
820
+ text: submission.text,
743
821
  });
822
+ dispatch.answered = result.kind !== 'transport-uncertainty';
744
823
  if (result.kind === 'revoked-authorization')
745
824
  return result;
746
825
  if (result.kind !== 'action' && result.retryable) {
747
826
  deps.unanswered();
748
827
  return result;
749
828
  }
750
- const turn = answered(result, 'turn');
829
+ const turn = answered(result, 'message_id');
751
830
  await withDraft(conversation, async (current, local) => {
752
831
  const held = current?.submitted?.find((one) => one.operation === submission.operation);
753
832
  if (current === undefined || held === undefined || held.turn !== null)
@@ -767,25 +846,34 @@ export function createConversationStore(deps) {
767
846
  sending.delete(submission.operation);
768
847
  }
769
848
  }
770
- function invoke(name, conversation, extra = {}) {
771
- return deps.call(name, { conversation, ...extra });
849
+ function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
850
+ return deps.call(name, { conversation_id: conversation, ...extra });
772
851
  }
773
852
  const surface = {
774
- async list() {
775
- await agentsReady();
776
- 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
+ });
777
863
  },
778
864
  async read(conversation, options = {}) {
779
- const asked = askedOf(options);
865
+ const { text, ...windowed } = options;
866
+ const asked = askedOf(windowed);
867
+ const phrase = phraseOf(text);
780
868
  return deps.durably(async (local) => {
781
- const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
782
- 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;
783
871
  return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
784
872
  });
785
873
  },
786
874
  async create(input) {
787
- const result = await deps.call('conversation.create', input);
788
- const conversation = answered(result, 'conversation');
875
+ const result = await deps.call('conversations/create', input);
876
+ const conversation = answered(result, 'conversation_id');
789
877
  if (conversation === null)
790
878
  return result;
791
879
  if (!listing.has(conversation))
@@ -796,12 +884,17 @@ export function createConversationStore(deps) {
796
884
  return result;
797
885
  },
798
886
  async send(conversation, operation = changeBatchIdentity(), draft) {
887
+ if (deps.full())
888
+ return DURABLE_WRITE;
799
889
  if (deps.online()) {
800
- const { submission, earlier } = await submitted(conversation, operation, draft);
801
- if (!earlier || !deps.expired(operation))
802
- return dispatched(conversation, submission);
803
- await forgotten(conversation, submission);
804
- 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);
805
898
  }
806
899
  if (draft !== undefined) {
807
900
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
@@ -810,38 +903,20 @@ export function createConversationStore(deps) {
810
903
  }
811
904
  return OFFLINE;
812
905
  },
813
- async attach(room, file) {
814
- if (!deps.online())
815
- return OFFLINE;
816
- return deps.call('conversation.attach', {
817
- room,
818
- ...(await declaredFile(file)),
819
- blob: file.blob,
820
- });
821
- },
822
906
  answer(conversation, answer) {
823
- return invoke('conversation.answer', conversation, { answer });
907
+ return invoke('conversations/interaction/answer', conversation, {
908
+ operation_id: changeBatchIdentity(),
909
+ ...answer,
910
+ });
824
911
  },
825
912
  interrupt(conversation) {
826
- return invoke('conversation.interrupt', conversation);
913
+ return invoke('conversations/interrupt', conversation);
827
914
  },
828
915
  kill(conversation) {
829
- return invoke('conversation.kill', conversation);
830
- },
831
- async cancel(conversation, turn) {
832
- const result = await invoke('conversation.cancel', conversation, { turn });
833
- if (result.kind !== 'action')
834
- return result;
835
- await withDraft(conversation, (current) => {
836
- const held = current?.submitted?.find((one) => one.turn === turn);
837
- if (current === undefined || held === undefined)
838
- return undefined;
839
- return returned(current, held, current.submitted.filter((one) => one !== held));
840
- });
841
- return result;
916
+ return invoke('conversations/kill', conversation);
842
917
  },
843
918
  delete(conversation) {
844
- return invoke('conversation.delete', conversation);
919
+ return invoke('conversations/delete', conversation);
845
920
  },
846
921
  };
847
922
  const chatDraft = {
@@ -870,7 +945,15 @@ export function createConversationStore(deps) {
870
945
  return {
871
946
  surface,
872
947
  chatDraft,
873
- 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
+ }
874
957
  stored.clear();
875
958
  reads.clear();
876
959
  partials.clear();
@@ -884,6 +967,8 @@ export function createConversationStore(deps) {
884
967
  drafts.clear();
885
968
  for (const record of kept)
886
969
  drafts.set(record.conversation, record);
970
+ if (listHeld)
971
+ learned();
887
972
  },
888
973
  async reconcile() {
889
974
  if (!listKnown)
@@ -895,7 +980,7 @@ export function createConversationStore(deps) {
895
980
  !creations.has(conversation) &&
896
981
  (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
897
982
  for (const conversation of named) {
898
- if (!replicated.has(conversation))
983
+ if (!replicated.has(conversation) && missed(conversation))
899
984
  behind.add(conversation);
900
985
  }
901
986
  replicated = named;
@@ -904,6 +989,11 @@ export function createConversationStore(deps) {
904
989
  await remove(leaving);
905
990
  },
906
991
  async catchUp(progress) {
992
+ if (deps.full()) {
993
+ if (behind.size > 0)
994
+ progress.complete = false;
995
+ return;
996
+ }
907
997
  const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
908
998
  const read = async (conversations) => {
909
999
  if (conversations.length === 0 || deps.closed())
@@ -928,10 +1018,6 @@ export function createConversationStore(deps) {
928
1018
  continue;
929
1019
  if (deps.closed())
930
1020
  return;
931
- if (deps.expired(submission.operation)) {
932
- await forgotten(conversation, submission);
933
- continue;
934
- }
935
1021
  const result = await dispatched(conversation, submission);
936
1022
  if (result.kind === 'revoked-authorization')
937
1023
  return;
@@ -952,12 +1038,11 @@ export function createConversationStore(deps) {
952
1038
  known() {
953
1039
  return listKnown;
954
1040
  },
955
- reconnected() {
956
- for (const conversation of replicated)
957
- gap(conversation);
958
- },
959
1041
  reopened() {
960
- sending.clear();
1042
+ relisting = null;
1043
+ for (const [operation, dispatch] of sending)
1044
+ if (!dispatch.answered)
1045
+ sending.delete(operation);
961
1046
  },
962
1047
  lost() {
963
1048
  catching.clear();
@@ -976,15 +1061,15 @@ export function createConversationStore(deps) {
976
1061
  views.set(key, view);
977
1062
  const watcher = { notify: deliver, ready: false };
978
1063
  view.watchers.add(watcher);
979
- void deps
980
- .durably(async (local) => {
1064
+ const reading = new AbortController();
1065
+ void untilAnswered(() => deps.durably(async (local) => {
981
1066
  if (view.window !== null)
982
1067
  return;
983
1068
  const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
984
1069
  view.window = messages;
985
1070
  view.more = more;
986
1071
  view.total = asked.total ? await local.messageCount(conversation) : null;
987
- })
1072
+ }), reading.signal)
988
1073
  .then(() => {
989
1074
  if (deps.closed() || !view.watchers.has(watcher))
990
1075
  return;
@@ -994,6 +1079,7 @@ export function createConversationStore(deps) {
994
1079
  })
995
1080
  .catch(() => { });
996
1081
  return () => {
1082
+ reading.abort();
997
1083
  if (!view.watchers.delete(watcher))
998
1084
  return;
999
1085
  if (view.watchers.size === 0)
@@ -1001,77 +1087,88 @@ export function createConversationStore(deps) {
1001
1087
  };
1002
1088
  },
1003
1089
  watchList(deliver) {
1004
- listWatchers.add(deliver);
1005
- void agentsReady().then(() => {
1006
- if (!deps.closed() && listWatchers.has(deliver))
1007
- deliver(entries());
1008
- });
1090
+ unlisted.add(deliver);
1091
+ learned();
1009
1092
  return () => {
1093
+ unlisted.delete(deliver);
1010
1094
  listWatchers.delete(deliver);
1011
1095
  };
1012
1096
  },
1013
1097
  watchAgents(deliver) {
1014
- agentWatchers.add(deliver);
1015
- void agentsReady().then(() => {
1016
- if (!deps.closed() && agentWatchers.has(deliver))
1017
- deliver(agents());
1018
- });
1098
+ unnamed.add(deliver);
1099
+ learned();
1100
+ readAgents();
1019
1101
  return () => {
1102
+ unnamed.delete(deliver);
1020
1103
  agentWatchers.delete(deliver);
1021
1104
  };
1022
1105
  },
1106
+ learned,
1023
1107
  changed(set) {
1024
1108
  for (const conversation of set.conversations)
1025
1109
  emit(conversation);
1026
- const declaration = deps.agents();
1027
- if (declaration !== undefined &&
1028
- set.documents.some((change) => change.locator === declaration.locator)) {
1029
- agentsRead = readAgents().catch(() => {
1030
- agentsRead = null;
1031
- });
1032
- }
1033
1110
  },
1034
1111
  receive(event) {
1035
1112
  if (deps.closed())
1036
1113
  return false;
1037
1114
  if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
1038
1115
  if (event.kind === 'list') {
1039
- listing.clear();
1116
+ if (event.whole)
1117
+ relisting ??= new Map();
1040
1118
  for (const entry of event.entries) {
1041
- listing.set(entry.conversation, { ...entry });
1042
- 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;
1043
1131
  }
1132
+ listCursor = event.cursor;
1133
+ deps.stream()?.hold(event.cursor);
1044
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
+ }
1045
1143
  openingEnded();
1046
1144
  }
1047
1145
  else if (event.kind === 'listed') {
1048
- listing.set(event.entry.conversation, { ...event.entry });
1049
- creations.delete(event.entry.conversation);
1146
+ listed(event.entry);
1147
+ relisting?.set(event.entry.conversation, { ...event.entry });
1050
1148
  }
1051
1149
  else {
1052
1150
  listing.delete(event.conversation);
1151
+ relisting?.delete(event.conversation);
1053
1152
  }
1153
+ keepList();
1054
1154
  emitList();
1055
- return (event.kind !== 'list' &&
1155
+ return ((event.kind !== 'list' &&
1056
1156
  listKnown &&
1057
1157
  (listing.size !== replicated.size ||
1058
- [...listing.keys()].some((conversation) => !replicated.has(conversation))));
1158
+ [...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
1159
+ (listKnown && [...drafts.keys()].some(retiredChat)));
1059
1160
  }
1060
1161
  heard(event);
1061
1162
  return false;
1062
1163
  },
1063
- searchable() {
1064
- return [...(listKnown ? listing.keys() : stored)].sort();
1065
- },
1066
- acquired(conversation) {
1067
- return reads.has(conversation) && !partials.has(conversation);
1068
- },
1069
1164
  close() {
1070
1165
  views.clear();
1071
1166
  lives.clear();
1072
1167
  unsent.clear();
1073
1168
  listWatchers.clear();
1169
+ unlisted.clear();
1074
1170
  agentWatchers.clear();
1171
+ unnamed.clear();
1075
1172
  },
1076
1173
  };
1077
1174
  }