@agentshouse/mdruntime 0.1.0-c969b8dee6cc8 → 0.1.0-c96e80d38e5f0

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 (231) hide show
  1. package/README.md +19 -16
  2. package/dist/adapter.d.ts +134 -76
  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 +25 -10
  11. package/dist/bootstrap.d.ts.map +1 -1
  12. package/dist/bootstrap.js +180 -103
  13. package/dist/chain.d.ts +11 -0
  14. package/dist/chain.d.ts.map +1 -0
  15. package/dist/chain.js +64 -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 +13 -9
  23. package/dist/client.d.ts.map +1 -1
  24. package/dist/client.js +80 -7
  25. package/dist/collection.d.ts +10 -21
  26. package/dist/collection.d.ts.map +1 -1
  27. package/dist/collection.js +39 -291
  28. package/dist/compile.d.ts +16 -0
  29. package/dist/compile.d.ts.map +1 -0
  30. package/dist/compile.js +789 -0
  31. package/dist/connection.d.ts +19 -15
  32. package/dist/connection.d.ts.map +1 -1
  33. package/dist/connection.js +3020 -1417
  34. package/dist/conversation.d.ts +23 -14
  35. package/dist/conversation.d.ts.map +1 -1
  36. package/dist/conversation.js +337 -179
  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 +2 -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 +19 -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/host.d.ts +4 -10
  50. package/dist/host.d.ts.map +1 -1
  51. package/dist/host.js +49 -31
  52. package/dist/identity.d.ts +0 -1
  53. package/dist/identity.d.ts.map +1 -1
  54. package/dist/identity.js +0 -3
  55. package/dist/index.d.ts +14 -11
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +6 -2
  58. package/dist/live.d.ts +22 -0
  59. package/dist/live.d.ts.map +1 -0
  60. package/dist/live.js +123 -0
  61. package/dist/media.d.ts +8 -0
  62. package/dist/media.d.ts.map +1 -0
  63. package/dist/media.js +218 -0
  64. package/dist/memory.d.ts +11 -7
  65. package/dist/memory.d.ts.map +1 -1
  66. package/dist/memory.js +198 -107
  67. package/dist/messages.d.ts +28 -0
  68. package/dist/messages.d.ts.map +1 -0
  69. package/dist/messages.js +89 -0
  70. package/dist/model.d.ts +6 -1
  71. package/dist/model.d.ts.map +1 -1
  72. package/dist/model.js +7 -10
  73. package/dist/name.d.ts +4 -0
  74. package/dist/name.d.ts.map +1 -0
  75. package/dist/name.js +122 -0
  76. package/dist/observation.d.ts +13 -0
  77. package/dist/observation.d.ts.map +1 -1
  78. package/dist/observation.js +84 -0
  79. package/dist/opened.d.ts +22 -0
  80. package/dist/opened.d.ts.map +1 -0
  81. package/dist/opened.js +107 -0
  82. package/dist/outcome.d.ts +40 -8
  83. package/dist/outcome.d.ts.map +1 -1
  84. package/dist/outcome.js +38 -2
  85. package/dist/overlay.d.ts +2 -5
  86. package/dist/overlay.d.ts.map +1 -1
  87. package/dist/overlay.js +87 -172
  88. package/dist/paths.d.ts +1 -0
  89. package/dist/paths.d.ts.map +1 -1
  90. package/dist/paths.js +4 -0
  91. package/dist/phrase.d.ts +3 -0
  92. package/dist/phrase.d.ts.map +1 -0
  93. package/dist/phrase.js +8 -0
  94. package/dist/query.d.ts +15 -1
  95. package/dist/query.d.ts.map +1 -1
  96. package/dist/query.js +33 -2
  97. package/dist/reach.d.ts +13 -4
  98. package/dist/reach.d.ts.map +1 -1
  99. package/dist/reach.js +200 -81
  100. package/dist/react.d.ts +1 -0
  101. package/dist/react.d.ts.map +1 -1
  102. package/dist/readiness.d.ts +14 -17
  103. package/dist/readiness.d.ts.map +1 -1
  104. package/dist/readiness.js +2 -8
  105. package/dist/recovery.d.ts +3 -2
  106. package/dist/recovery.d.ts.map +1 -1
  107. package/dist/recovery.js +34 -7
  108. package/dist/refresh.d.ts +18 -15
  109. package/dist/refresh.d.ts.map +1 -1
  110. package/dist/refresh.js +155 -58
  111. package/dist/replica.d.ts +54 -36
  112. package/dist/replica.d.ts.map +1 -1
  113. package/dist/replica.js +282 -189
  114. package/dist/room-draft.d.ts +22 -4
  115. package/dist/room-draft.d.ts.map +1 -1
  116. package/dist/room-draft.js +154 -67
  117. package/dist/rows.d.ts +39 -31
  118. package/dist/rows.d.ts.map +1 -1
  119. package/dist/rows.js +178 -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 +12 -95
  124. package/dist/search.d.ts.map +1 -1
  125. package/dist/search.js +10 -266
  126. package/dist/shared.d.ts +7 -3
  127. package/dist/shared.d.ts.map +1 -1
  128. package/dist/shared.js +119 -66
  129. package/dist/status.d.ts +4 -3
  130. package/dist/status.d.ts.map +1 -1
  131. package/dist/status.js +3 -1
  132. package/dist/store.d.ts +91 -103
  133. package/dist/store.d.ts.map +1 -1
  134. package/dist/store.js +330 -693
  135. package/dist/stored.d.ts +162 -0
  136. package/dist/stored.d.ts.map +1 -0
  137. package/dist/stored.js +549 -0
  138. package/dist/tables.d.ts +353 -0
  139. package/dist/tables.d.ts.map +1 -0
  140. package/dist/tables.js +585 -0
  141. package/dist/transfer.d.ts +0 -5
  142. package/dist/transfer.d.ts.map +1 -1
  143. package/dist/turn.d.ts +5 -3
  144. package/dist/turn.d.ts.map +1 -1
  145. package/dist/turn.js +51 -21
  146. package/dist/validation.d.ts +29 -0
  147. package/dist/validation.d.ts.map +1 -0
  148. package/dist/validation.js +101 -0
  149. package/dist/windows.d.ts +2 -6
  150. package/dist/windows.d.ts.map +1 -1
  151. package/dist/windows.js +30 -34
  152. package/dist/wire.d.ts +18 -5
  153. package/dist/wire.d.ts.map +1 -1
  154. package/dist/wire.js +6 -2
  155. package/package.json +18 -19
  156. package/AGENTS.md +0 -305
  157. package/dist/acquisition.d.ts +0 -14
  158. package/dist/acquisition.d.ts.map +0 -1
  159. package/dist/acquisition.js +0 -24
  160. package/dist/chat/acts.d.ts +0 -33
  161. package/dist/chat/acts.d.ts.map +0 -1
  162. package/dist/chat/acts.js +0 -36
  163. package/dist/chat/context.d.ts +0 -3
  164. package/dist/chat/context.d.ts.map +0 -1
  165. package/dist/chat/context.js +0 -10
  166. package/dist/chat/earlier.d.ts +0 -10
  167. package/dist/chat/earlier.d.ts.map +0 -1
  168. package/dist/chat/earlier.js +0 -54
  169. package/dist/chat/input.d.ts +0 -9
  170. package/dist/chat/input.d.ts.map +0 -1
  171. package/dist/chat/input.js +0 -99
  172. package/dist/chat/kit.d.ts +0 -61
  173. package/dist/chat/kit.d.ts.map +0 -1
  174. package/dist/chat/kit.js +0 -123
  175. package/dist/chat/lasting.d.ts +0 -6
  176. package/dist/chat/lasting.d.ts.map +0 -1
  177. package/dist/chat/lasting.js +0 -19
  178. package/dist/chat/list.d.ts +0 -27
  179. package/dist/chat/list.d.ts.map +0 -1
  180. package/dist/chat/list.js +0 -99
  181. package/dist/chat/markdown.d.ts +0 -20
  182. package/dist/chat/markdown.d.ts.map +0 -1
  183. package/dist/chat/markdown.js +0 -188
  184. package/dist/chat/message.d.ts +0 -6
  185. package/dist/chat/message.d.ts.map +0 -1
  186. package/dist/chat/message.js +0 -192
  187. package/dist/chat/messages.d.ts +0 -22
  188. package/dist/chat/messages.d.ts.map +0 -1
  189. package/dist/chat/messages.js +0 -155
  190. package/dist/chat/name.d.ts +0 -6
  191. package/dist/chat/name.d.ts.map +0 -1
  192. package/dist/chat/name.js +0 -37
  193. package/dist/chat/opened.d.ts +0 -11
  194. package/dist/chat/opened.d.ts.map +0 -1
  195. package/dist/chat/opened.js +0 -17
  196. package/dist/chat/overlay.d.ts +0 -31
  197. package/dist/chat/overlay.d.ts.map +0 -1
  198. package/dist/chat/overlay.js +0 -97
  199. package/dist/chat/parts.d.ts +0 -57
  200. package/dist/chat/parts.d.ts.map +0 -1
  201. package/dist/chat/parts.js +0 -119
  202. package/dist/chat/process.d.ts +0 -20
  203. package/dist/chat/process.d.ts.map +0 -1
  204. package/dist/chat/process.js +0 -32
  205. package/dist/chat/provider.d.ts +0 -63
  206. package/dist/chat/provider.d.ts.map +0 -1
  207. package/dist/chat/provider.js +0 -628
  208. package/dist/chat/question.d.ts +0 -9
  209. package/dist/chat/question.d.ts.map +0 -1
  210. package/dist/chat/question.js +0 -69
  211. package/dist/chat/questions.d.ts +0 -31
  212. package/dist/chat/questions.d.ts.map +0 -1
  213. package/dist/chat/questions.js +0 -111
  214. package/dist/chat/read.d.ts +0 -5
  215. package/dist/chat/read.d.ts.map +0 -1
  216. package/dist/chat/read.js +0 -13
  217. package/dist/chat/ready.d.ts +0 -34
  218. package/dist/chat/ready.d.ts.map +0 -1
  219. package/dist/chat/ready.js +0 -91
  220. package/dist/chat/style.d.ts +0 -4
  221. package/dist/chat/style.d.ts.map +0 -1
  222. package/dist/chat/style.js +0 -302
  223. package/dist/chat.d.ts +0 -14
  224. package/dist/chat.d.ts.map +0 -1
  225. package/dist/chat.js +0 -8
  226. package/dist/read-side.d.ts +0 -25
  227. package/dist/read-side.d.ts.map +0 -1
  228. package/dist/read-side.js +0 -767
  229. package/dist/reads.d.ts +0 -11
  230. package/dist/reads.d.ts.map +0 -1
  231. package/dist/reads.js +0 -98
@@ -1,8 +1,9 @@
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
+ import { untilAnswered } from './recovery.js';
6
7
  import { capped, cursorOf, decodedCursor, FROM_START, messageOrder, precedes } from './windows.js';
7
8
  const QUIET = {
8
9
  turn: null,
@@ -10,7 +11,21 @@ const QUIET = {
10
11
  process: null,
11
12
  commands: [],
12
13
  options: [],
14
+ starting: null,
15
+ subagents: [],
16
+ link: null,
17
+ deliveryUnknown: [],
13
18
  };
19
+ const FACTS = [
20
+ 'questions',
21
+ 'process',
22
+ 'commands',
23
+ 'options',
24
+ 'starting',
25
+ 'subagents',
26
+ 'link',
27
+ 'deliveryUnknown',
28
+ ];
14
29
  export function unheldView(conversation) {
15
30
  return {
16
31
  conversation,
@@ -20,19 +35,26 @@ export function unheldView(conversation) {
20
35
  process: null,
21
36
  commands: [],
22
37
  options: [],
38
+ starting: null,
39
+ subagents: [],
40
+ link: null,
41
+ deliveryUnknown: [],
23
42
  chatDraft: null,
24
43
  };
25
44
  }
26
45
  export const SESSION_STARTED = 'Session started';
27
46
  const NEW_CHAT = 'new:';
47
+ export const HISTORY_CONVERSATIONS = 64;
28
48
  export function newChat(agent) {
29
49
  return `${NEW_CHAT}${agent}`;
30
50
  }
31
51
  function creationOf(input) {
32
52
  if (typeof input !== 'object' || input === null)
33
53
  return null;
34
- const { agent_id: agent, operation_id: operation } = input;
35
- return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
54
+ const { agent_id: agent, operation_id: operation, replaces, } = input;
55
+ return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
56
+ ? { agent, operation }
57
+ : null;
36
58
  }
37
59
  function startedTurn(turn, operation) {
38
60
  if (operation.op === 'begin')
@@ -191,9 +213,23 @@ function askedOf(options) {
191
213
  }
192
214
  return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
193
215
  }
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
+ }
194
224
  const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
225
+ const DURABLE_WRITE = {
226
+ kind: 'rejected',
227
+ code: 'durable-write',
228
+ fields: {},
229
+ retryable: true,
230
+ };
195
231
  function unwritten(draft) {
196
- return draft.text === '' && draft.files.length === 0;
232
+ return draft.text === '';
197
233
  }
198
234
  function pending(draft) {
199
235
  return {
@@ -209,10 +245,7 @@ function withSubmitted(draft, submitted) {
209
245
  return submitted.length === 0 ? unsent : { ...unsent, submitted };
210
246
  }
211
247
  function prepended(draft, said) {
212
- return {
213
- text: [said.text, draft.text].filter((one) => one !== '').join('\n'),
214
- files: [...said.files, ...draft.files],
215
- };
248
+ return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
216
249
  }
217
250
  function returned(draft, submission, others) {
218
251
  return withSubmitted({
@@ -231,15 +264,22 @@ export function createConversationStore(deps) {
231
264
  const catching = new Map();
232
265
  const gaps = new Map();
233
266
  const lives = new Map();
267
+ const unsent = new Map();
234
268
  const views = new Map();
235
269
  const listing = new Map();
270
+ let relisting = null;
271
+ let listCursor = null;
236
272
  const creations = new Set();
237
273
  const listWatchers = new Set();
274
+ const unlisted = new Set();
238
275
  let listKnown = false;
276
+ let listHeld = false;
277
+ let listKept = false;
239
278
  const agentWatchers = new Set();
240
- let agentNames = new Map();
241
- let retiredAgents = new Set();
242
- let agentsRead = null;
279
+ const unnamed = new Set();
280
+ let agentsShown = null;
281
+ let listedAgents = null;
282
+ let agentsReading = false;
243
283
  let replicated = new Set();
244
284
  let handedReplicated;
245
285
  function liveOf(conversation) {
@@ -262,9 +302,13 @@ export function createConversationStore(deps) {
262
302
  process: facts.process === null ? null : { ...facts.process },
263
303
  commands: structuredClone(facts.commands),
264
304
  options: structuredClone(facts.options),
305
+ starting: facts.starting,
306
+ subagents: structuredClone(facts.subagents),
307
+ link: facts.link === null ? null : { ...facts.link },
308
+ deliveryUnknown: [...facts.deliveryUnknown],
265
309
  chatDraft: draft === undefined || unwritten(draft)
266
310
  ? null
267
- : { text: draft.text, files: [...draft.files], revision: draft.revision },
311
+ : { text: draft.text, revision: draft.revision },
268
312
  };
269
313
  }
270
314
  function emit(conversation) {
@@ -306,9 +350,6 @@ export function createConversationStore(deps) {
306
350
  }
307
351
  }
308
352
  }
309
- function changesOf(conversation, arriving) {
310
- return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
311
- }
312
353
  function stateOf(conversation) {
313
354
  const facts = liveOf(conversation);
314
355
  if (facts.questions.length > 0)
@@ -318,34 +359,110 @@ export function createConversationStore(deps) {
318
359
  function entries() {
319
360
  return [...listing.values()].map((entry) => ({
320
361
  conversation: entry.conversation,
321
- agent: { identity: entry.agent, name: agentNames.get(entry.agent) ?? null },
362
+ agent: { identity: entry.agent, name: entry.agentName },
322
363
  name: entry.name,
323
364
  created: entry.created,
324
365
  finished: entry.finished,
325
366
  outcome: entry.outcome,
367
+ waiting: entry.waiting,
326
368
  state: stateOf(entry.conversation),
327
369
  started: lives.get(entry.conversation)?.turn?.started ?? null,
328
370
  }));
329
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
+ listing.set(entry.conversation, { ...entry });
392
+ creations.delete(entry.conversation);
393
+ }
330
394
  function emitList() {
331
- if (deps.closed() || listWatchers.size === 0)
395
+ if (deps.closed())
332
396
  return;
333
- const value = entries();
334
- for (const watcher of listWatchers)
335
- watcher(value);
397
+ if (listWatchers.size > 0) {
398
+ const value = entries();
399
+ for (const watcher of listWatchers)
400
+ watcher(value);
401
+ }
402
+ emitAgents();
403
+ learned();
404
+ }
405
+ function learned() {
406
+ if (deps.closed())
407
+ return;
408
+ if (!listKnown && !listHeld && deps.stream() !== undefined && deps.online())
409
+ return;
410
+ if (unlisted.size > 0) {
411
+ const value = entries();
412
+ for (const watcher of [...unlisted]) {
413
+ unlisted.delete(watcher);
414
+ listWatchers.add(watcher);
415
+ watcher(value);
416
+ }
417
+ }
418
+ if (unnamed.size > 0) {
419
+ const value = agents();
420
+ for (const watcher of [...unnamed]) {
421
+ unnamed.delete(watcher);
422
+ agentWatchers.add(watcher);
423
+ watcher(value);
424
+ }
425
+ }
336
426
  }
337
427
  function agents() {
338
- return [...agentNames]
339
- .filter(([identity]) => !retiredAgents.has(identity))
340
- .map(([identity, name]) => ({ identity, name }))
428
+ const named = new Map();
429
+ for (const agent of listedAgents ?? [])
430
+ named.set(agent.agent, agent);
431
+ for (const entry of listing.values()) {
432
+ named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
433
+ }
434
+ return [...named]
435
+ .filter(([, agent]) => !agent.retired)
436
+ .map(([identity, agent]) => ({ identity, name: agent.name }))
341
437
  .sort((left, right) => left.name === right.name
342
438
  ? left.identity.localeCompare(right.identity)
343
439
  : left.name.localeCompare(right.name));
344
440
  }
441
+ function readAgents() {
442
+ const stream = deps.stream();
443
+ if (stream?.agents === undefined || !deps.online() || agentsReading)
444
+ return;
445
+ agentsReading = true;
446
+ void stream
447
+ .agents()
448
+ .then((read) => {
449
+ if (read === null)
450
+ return;
451
+ listedAgents = read;
452
+ emitAgents();
453
+ }, () => { })
454
+ .finally(() => {
455
+ agentsReading = false;
456
+ });
457
+ }
345
458
  function emitAgents() {
346
459
  if (deps.closed() || agentWatchers.size === 0)
347
460
  return;
348
461
  const value = agents();
462
+ const shown = JSON.stringify(value);
463
+ if (shown === agentsShown)
464
+ return;
465
+ agentsShown = shown;
349
466
  for (const watcher of agentWatchers)
350
467
  watcher(value);
351
468
  }
@@ -361,53 +478,42 @@ export function createConversationStore(deps) {
361
478
  if (stateOf(conversation) !== before || next?.turn?.started !== began)
362
479
  emitList();
363
480
  }
364
- async function readAgents() {
365
- const declaration = deps.agents();
366
- if (declaration === undefined)
367
- return;
368
- const held = await deps.documents(declaration);
369
- if ('pending' in held)
370
- return;
371
- const { documents } = held;
372
- const names = new Map();
373
- 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);
385
- }
386
- const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
387
- if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
388
- return;
389
- agentNames = names;
390
- retiredAgents = retired;
391
- if (named)
392
- emitList();
393
- emitAgents();
394
- const ended = [...drafts.keys()].filter(retiredChat);
395
- if (ended.length > 0)
396
- await remove(ended);
397
- }
398
481
  function retiredChat(conversation) {
399
- return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
400
- }
401
- function agentsReady() {
402
- agentsRead ??= readAgents().catch(() => {
403
- agentsRead = null;
404
- });
405
- return agentsRead;
482
+ if (!conversation.startsWith(NEW_CHAT))
483
+ return false;
484
+ const agent = conversation.slice(NEW_CHAT.length);
485
+ return [...listing.values()].some((entry) => entry.agent === agent && entry.agentRetired);
406
486
  }
407
487
  function live(conversation, change) {
488
+ const owed = unsent.get(conversation);
489
+ for (const fact of FACTS.filter((one) => one in change))
490
+ owed?.delete(fact);
408
491
  setLive(conversation, { ...liveOf(conversation), ...change });
409
492
  emit(conversation);
410
493
  }
494
+ function openingEnded() {
495
+ const owed = [...unsent];
496
+ unsent.clear();
497
+ for (const [conversation, facts] of owed) {
498
+ const held = lives.get(conversation);
499
+ if (held === undefined || facts.size === 0)
500
+ continue;
501
+ setLive(conversation, {
502
+ turn: held.turn,
503
+ questions: facts.has('questions') ? QUIET.questions : held.questions,
504
+ process: facts.has('process') ? QUIET.process : held.process,
505
+ commands: facts.has('commands') ? QUIET.commands : held.commands,
506
+ options: facts.has('options') ? QUIET.options : held.options,
507
+ starting: facts.has('starting') ? QUIET.starting : held.starting,
508
+ subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
509
+ link: facts.has('link') ? QUIET.link : held.link,
510
+ deliveryUnknown: facts.has('deliveryUnknown')
511
+ ? QUIET.deliveryUnknown
512
+ : held.deliveryUnknown,
513
+ });
514
+ emit(conversation);
515
+ }
516
+ }
411
517
  function relayed(conversation, turn, operation) {
412
518
  const held = liveOf(conversation).turn;
413
519
  const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
@@ -434,12 +540,6 @@ export function createConversationStore(deps) {
434
540
  }
435
541
  async function remove(leaving) {
436
542
  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
543
  await local.dropConversations(leaving);
444
544
  const changed = [];
445
545
  for (const conversation of leaving) {
@@ -451,13 +551,28 @@ export function createConversationStore(deps) {
451
551
  partials.delete(conversation);
452
552
  drafts.delete(conversation);
453
553
  behind.delete(conversation);
554
+ unsent.delete(conversation);
454
555
  setLive(conversation, null);
455
556
  await slide(local, conversation, null);
456
557
  }
457
558
  if (changed.length > 0)
458
- deps.publish(conversationChangeSet(removed, changed));
559
+ deps.publish(conversationChangeSet(changed));
459
560
  });
460
561
  }
562
+ function missed(conversation) {
563
+ const read = reads.get(conversation);
564
+ const newest = listing.get(conversation)?.newest;
565
+ return (read === undefined ||
566
+ newest === undefined ||
567
+ partials.has(conversation) ||
568
+ (newest !== null && (read === null || newest > read)));
569
+ }
570
+ function noted(conversation, message) {
571
+ const entry = listing.get(conversation);
572
+ if (entry === undefined || (entry.newest !== null && entry.newest >= message.position))
573
+ return;
574
+ listing.set(conversation, { ...entry, newest: message.position });
575
+ }
461
576
  function gap(conversation) {
462
577
  gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
463
578
  behind.add(conversation);
@@ -481,17 +596,18 @@ export function createConversationStore(deps) {
481
596
  reads.set(conversation, message.position);
482
597
  sealTurns(conversation, new Set([message.turn]));
483
598
  await slide(local, conversation, [message]);
484
- deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
599
+ deps.publish(conversationChangeSet([conversation]));
485
600
  });
486
601
  }
487
602
  async function readHistories(conversations, ended) {
488
603
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
489
- let asking = conversations.map((conversation) => ({
604
+ let waiting = conversations.map((conversation) => ({
490
605
  conversation,
491
606
  after: reads.get(conversation) ?? undefined,
492
607
  }));
493
- while (asking.length > 0) {
494
- const result = await deps.call('conversation.history', {
608
+ while (waiting.length > 0) {
609
+ const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
610
+ const result = await deps.history({
495
611
  conversations: asking.map(({ conversation, after }) => ({
496
612
  conversation,
497
613
  ...(after === undefined ? {} : { after }),
@@ -514,7 +630,7 @@ export function createConversationStore(deps) {
514
630
  if (!following.some((next) => next.conversation === conversation))
515
631
  ended(conversation);
516
632
  }
517
- asking = following;
633
+ waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
518
634
  }
519
635
  return null;
520
636
  }
@@ -557,7 +673,7 @@ export function createConversationStore(deps) {
557
673
  behind.delete(conversation);
558
674
  await slide(local, conversation, arriving);
559
675
  if (arriving.length > 0 || sealing || whole) {
560
- deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
676
+ deps.publish(conversationChangeSet([conversation]));
561
677
  }
562
678
  });
563
679
  }
@@ -566,11 +682,8 @@ export function createConversationStore(deps) {
566
682
  if (event.kind === 'gap') {
567
683
  if (replicated.has(conversation))
568
684
  gap(conversation);
569
- const held = lives.get(conversation);
570
- if (held === undefined)
571
- return;
572
- setLive(conversation, { ...QUIET, turn: held.turn });
573
- emit(conversation);
685
+ if (lives.has(conversation))
686
+ unsent.set(conversation, new Set(FACTS));
574
687
  return;
575
688
  }
576
689
  if (event.kind === 'turn') {
@@ -593,11 +706,35 @@ export function createConversationStore(deps) {
593
706
  live(conversation, { options: structuredClone(event.options) });
594
707
  return;
595
708
  }
709
+ if (event.kind === 'starting') {
710
+ live(conversation, { starting: event.step });
711
+ return;
712
+ }
713
+ if (event.kind === 'subagents') {
714
+ live(conversation, { subagents: structuredClone(event.subagents) });
715
+ return;
716
+ }
717
+ if (event.kind === 'link') {
718
+ live(conversation, { link: { connected: event.connected } });
719
+ return;
720
+ }
721
+ if (event.kind === 'deliveries') {
722
+ live(conversation, { deliveryUnknown: [...event.unknown] });
723
+ return;
724
+ }
596
725
  const arriving = acceptedMessage(event.message);
597
726
  if (arriving === null)
598
727
  return;
728
+ noted(conversation, arriving);
599
729
  if (replicated.has(conversation)) {
600
- void keep(conversation, arriving).catch(() => { });
730
+ if (deps.full()) {
731
+ gap(conversation);
732
+ return;
733
+ }
734
+ void keep(conversation, arriving).catch((error) => {
735
+ if (refusedForSpace(error))
736
+ gap(conversation);
737
+ });
601
738
  return;
602
739
  }
603
740
  if (sealTurns(conversation, new Set([arriving.turn])))
@@ -632,7 +769,6 @@ export function createConversationStore(deps) {
632
769
  const blank = {
633
770
  conversation: from,
634
771
  text: '',
635
- files: [],
636
772
  revision: chatDraftRevision(),
637
773
  };
638
774
  const { moves: _answered, ...unsent } = held ?? blank;
@@ -642,7 +778,7 @@ export function createConversationStore(deps) {
642
778
  drafts.set(from, left);
643
779
  for (const one of taken)
644
780
  drafts.set(to, one);
645
- deps.publish(conversationChangeSet([], [from, to]));
781
+ deps.publish(conversationChangeSet([from, to]));
646
782
  });
647
783
  }
648
784
  function withDraft(conversation, next) {
@@ -651,7 +787,7 @@ export function createConversationStore(deps) {
651
787
  if (following === undefined)
652
788
  return;
653
789
  await kept(local, conversation, following);
654
- deps.publish(conversationChangeSet([], [conversation]));
790
+ deps.publish(conversationChangeSet([conversation]));
655
791
  });
656
792
  }
657
793
  async function submitted(conversation, operation, draft) {
@@ -663,17 +799,12 @@ export function createConversationStore(deps) {
663
799
  if (submission !== undefined)
664
800
  return undefined;
665
801
  const said = draft ?? current;
666
- submission = {
667
- operation,
668
- text: said?.text ?? '',
669
- files: [...(said?.files ?? [])],
670
- turn: null,
671
- };
802
+ submission = { operation, text: said?.text ?? '', turn: null };
672
803
  return {
673
804
  conversation,
674
805
  ...(draft === undefined || current === undefined
675
- ? { text: '', files: [], revision: chatDraftRevision() }
676
- : { text: current.text, files: current.files, revision: current.revision }),
806
+ ? { text: '', revision: chatDraftRevision() }
807
+ : { text: current.text, revision: current.revision }),
677
808
  submitted: [...(current?.submitted ?? []), submission],
678
809
  };
679
810
  });
@@ -688,22 +819,21 @@ export function createConversationStore(deps) {
688
819
  });
689
820
  }
690
821
  async function dispatched(conversation, submission) {
691
- const dispatch = {};
822
+ const dispatch = { answered: false };
692
823
  sending.set(submission.operation, dispatch);
693
824
  try {
694
- const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
695
- const result = await invoke('conversation.send', conversation, {
696
- text: submission.text,
697
- files,
825
+ const result = await invoke('conversations/send', conversation, {
698
826
  operation_id: submission.operation,
827
+ text: submission.text,
699
828
  });
829
+ dispatch.answered = result.kind !== 'transport-uncertainty';
700
830
  if (result.kind === 'revoked-authorization')
701
831
  return result;
702
832
  if (result.kind !== 'action' && result.retryable) {
703
833
  deps.unanswered();
704
834
  return result;
705
835
  }
706
- const turn = answered(result, 'turn');
836
+ const turn = answered(result, 'message_id');
707
837
  await withDraft(conversation, async (current, local) => {
708
838
  const held = current?.submitted?.find((one) => one.operation === submission.operation);
709
839
  if (current === undefined || held === undefined || held.turn !== null)
@@ -723,25 +853,34 @@ export function createConversationStore(deps) {
723
853
  sending.delete(submission.operation);
724
854
  }
725
855
  }
726
- function invoke(name, conversation, extra = {}) {
727
- return deps.call(name, { conversation, ...extra });
856
+ function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
857
+ return deps.call(name, { conversation_id: conversation, ...extra });
728
858
  }
729
859
  const surface = {
730
- async list() {
731
- await agentsReady();
732
- return entries();
860
+ list() {
861
+ return new Promise((settle) => {
862
+ const answer = (listed) => {
863
+ unlisted.delete(answer);
864
+ listWatchers.delete(answer);
865
+ settle(listed);
866
+ };
867
+ unlisted.add(answer);
868
+ learned();
869
+ });
733
870
  },
734
871
  async read(conversation, options = {}) {
735
- const asked = askedOf(options);
872
+ const { text, ...windowed } = options;
873
+ const asked = askedOf(windowed);
874
+ const phrase = phraseOf(text);
736
875
  return deps.durably(async (local) => {
737
- const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
738
- const total = asked.total ? await local.messageCount(conversation) : null;
876
+ const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
877
+ const total = asked.total ? await local.messageCount(conversation, phrase) : null;
739
878
  return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
740
879
  });
741
880
  },
742
881
  async create(input) {
743
- const result = await deps.call('conversation.create', input);
744
- const conversation = answered(result, 'conversation');
882
+ const result = await deps.call('conversations/create', input);
883
+ const conversation = answered(result, 'conversation_id');
745
884
  if (conversation === null)
746
885
  return result;
747
886
  if (!listing.has(conversation))
@@ -752,8 +891,17 @@ export function createConversationStore(deps) {
752
891
  return result;
753
892
  },
754
893
  async send(conversation, operation = changeBatchIdentity(), draft) {
894
+ if (deps.full())
895
+ return DURABLE_WRITE;
755
896
  if (deps.online()) {
756
- const { submission, earlier } = await submitted(conversation, operation, draft);
897
+ const held = await submitted(conversation, operation, draft).catch((error) => {
898
+ if (refusedForSpace(error))
899
+ return null;
900
+ throw error;
901
+ });
902
+ if (held === null)
903
+ return DURABLE_WRITE;
904
+ const { submission, earlier } = held;
757
905
  if (!earlier || !deps.expired(operation))
758
906
  return dispatched(conversation, submission);
759
907
  await forgotten(conversation, submission);
@@ -762,33 +910,24 @@ export function createConversationStore(deps) {
762
910
  if (draft !== undefined) {
763
911
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
764
912
  ? undefined
765
- : returned(current ?? { conversation, text: '', files: [], revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
913
+ : returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
766
914
  }
767
915
  return OFFLINE;
768
916
  },
769
917
  answer(conversation, answer) {
770
- return invoke('conversation.answer', conversation, { answer });
918
+ return invoke('conversations/interaction/answer', conversation, {
919
+ operation_id: changeBatchIdentity(),
920
+ ...answer,
921
+ });
771
922
  },
772
923
  interrupt(conversation) {
773
- return invoke('conversation.interrupt', conversation);
924
+ return invoke('conversations/interrupt', conversation);
774
925
  },
775
926
  kill(conversation) {
776
- return invoke('conversation.kill', conversation);
777
- },
778
- async cancel(conversation, turn) {
779
- const result = await invoke('conversation.cancel', conversation, { turn });
780
- if (result.kind !== 'action')
781
- return result;
782
- await withDraft(conversation, (current) => {
783
- const held = current?.submitted?.find((one) => one.turn === turn);
784
- if (current === undefined || held === undefined)
785
- return undefined;
786
- return returned(current, held, current.submitted.filter((one) => one !== held));
787
- });
788
- return result;
927
+ return invoke('conversations/kill', conversation);
789
928
  },
790
929
  delete(conversation) {
791
- return invoke('conversation.delete', conversation);
930
+ return invoke('conversations/delete', conversation);
792
931
  },
793
932
  };
794
933
  const chatDraft = {
@@ -796,7 +935,6 @@ export function createConversationStore(deps) {
796
935
  return withDraft(conversation, (current) => ({
797
936
  conversation,
798
937
  text: draft.text,
799
- files: [...draft.files],
800
938
  revision,
801
939
  ...(current === undefined ? {} : pending(current)),
802
940
  }));
@@ -804,9 +942,7 @@ export function createConversationStore(deps) {
804
942
  prepend(conversation, draft) {
805
943
  return withDraft(conversation, (current) => ({
806
944
  conversation,
807
- ...(current === undefined
808
- ? { text: draft.text, files: [...draft.files] }
809
- : prepended(current, draft)),
945
+ ...(current === undefined ? { text: draft.text } : prepended(current, draft)),
810
946
  revision: chatDraftRevision(),
811
947
  ...(current === undefined ? {} : pending(current)),
812
948
  }));
@@ -814,19 +950,21 @@ export function createConversationStore(deps) {
814
950
  discard(conversation) {
815
951
  return withDraft(conversation, (current) => current === undefined
816
952
  ? undefined
817
- : {
818
- conversation,
819
- text: '',
820
- files: [],
821
- revision: chatDraftRevision(),
822
- ...pending(current),
823
- });
953
+ : { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
824
954
  },
825
955
  };
826
956
  return {
827
957
  surface,
828
958
  chatDraft,
829
- hydrate(held, kept) {
959
+ hydrate(held, kept, list, cursor) {
960
+ if (list !== null && !listKnown) {
961
+ listing.clear();
962
+ for (const entry of list)
963
+ listing.set(entry.conversation, { ...entry });
964
+ listHeld = true;
965
+ listCursor = cursor;
966
+ deps.stream()?.hold(cursor);
967
+ }
830
968
  stored.clear();
831
969
  reads.clear();
832
970
  partials.clear();
@@ -840,6 +978,8 @@ export function createConversationStore(deps) {
840
978
  drafts.clear();
841
979
  for (const record of kept)
842
980
  drafts.set(record.conversation, record);
981
+ if (listHeld)
982
+ learned();
843
983
  },
844
984
  async reconcile() {
845
985
  if (!listKnown)
@@ -851,7 +991,7 @@ export function createConversationStore(deps) {
851
991
  !creations.has(conversation) &&
852
992
  (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
853
993
  for (const conversation of named) {
854
- if (!replicated.has(conversation))
994
+ if (!replicated.has(conversation) && missed(conversation))
855
995
  behind.add(conversation);
856
996
  }
857
997
  replicated = named;
@@ -860,6 +1000,11 @@ export function createConversationStore(deps) {
860
1000
  await remove(leaving);
861
1001
  },
862
1002
  async catchUp(progress) {
1003
+ if (deps.full()) {
1004
+ if (behind.size > 0)
1005
+ progress.complete = false;
1006
+ return;
1007
+ }
863
1008
  const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
864
1009
  const read = async (conversations) => {
865
1010
  if (conversations.length === 0 || deps.closed())
@@ -908,12 +1053,11 @@ export function createConversationStore(deps) {
908
1053
  known() {
909
1054
  return listKnown;
910
1055
  },
911
- reconnected() {
912
- for (const conversation of replicated)
913
- gap(conversation);
914
- },
915
1056
  reopened() {
916
- sending.clear();
1057
+ relisting = null;
1058
+ for (const [operation, dispatch] of sending)
1059
+ if (!dispatch.answered)
1060
+ sending.delete(operation);
917
1061
  },
918
1062
  lost() {
919
1063
  catching.clear();
@@ -932,15 +1076,15 @@ export function createConversationStore(deps) {
932
1076
  views.set(key, view);
933
1077
  const watcher = { notify: deliver, ready: false };
934
1078
  view.watchers.add(watcher);
935
- void deps
936
- .durably(async (local) => {
1079
+ const reading = new AbortController();
1080
+ void untilAnswered(() => deps.durably(async (local) => {
937
1081
  if (view.window !== null)
938
1082
  return;
939
1083
  const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
940
1084
  view.window = messages;
941
1085
  view.more = more;
942
1086
  view.total = asked.total ? await local.messageCount(conversation) : null;
943
- })
1087
+ }), reading.signal)
944
1088
  .then(() => {
945
1089
  if (deps.closed() || !view.watchers.has(watcher))
946
1090
  return;
@@ -950,6 +1094,7 @@ export function createConversationStore(deps) {
950
1094
  })
951
1095
  .catch(() => { });
952
1096
  return () => {
1097
+ reading.abort();
953
1098
  if (!view.watchers.delete(watcher))
954
1099
  return;
955
1100
  if (view.watchers.size === 0)
@@ -957,75 +1102,88 @@ export function createConversationStore(deps) {
957
1102
  };
958
1103
  },
959
1104
  watchList(deliver) {
960
- listWatchers.add(deliver);
961
- void agentsReady().then(() => {
962
- if (!deps.closed() && listWatchers.has(deliver))
963
- deliver(entries());
964
- });
1105
+ unlisted.add(deliver);
1106
+ learned();
965
1107
  return () => {
1108
+ unlisted.delete(deliver);
966
1109
  listWatchers.delete(deliver);
967
1110
  };
968
1111
  },
969
1112
  watchAgents(deliver) {
970
- agentWatchers.add(deliver);
971
- void agentsReady().then(() => {
972
- if (!deps.closed() && agentWatchers.has(deliver))
973
- deliver(agents());
974
- });
1113
+ unnamed.add(deliver);
1114
+ learned();
1115
+ readAgents();
975
1116
  return () => {
1117
+ unnamed.delete(deliver);
976
1118
  agentWatchers.delete(deliver);
977
1119
  };
978
1120
  },
1121
+ learned,
979
1122
  changed(set) {
980
1123
  for (const conversation of set.conversations)
981
1124
  emit(conversation);
982
- const declaration = deps.agents();
983
- if (declaration !== undefined &&
984
- set.documents.some((change) => change.locator === declaration.locator)) {
985
- agentsRead = readAgents().catch(() => {
986
- agentsRead = null;
987
- });
988
- }
989
1125
  },
990
1126
  receive(event) {
991
1127
  if (deps.closed())
992
1128
  return false;
993
1129
  if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
994
1130
  if (event.kind === 'list') {
995
- listing.clear();
1131
+ if (event.whole)
1132
+ relisting ??= new Map();
996
1133
  for (const entry of event.entries) {
997
- listing.set(entry.conversation, { ...entry });
998
- creations.delete(entry.conversation);
1134
+ if (relisting === null)
1135
+ listed(entry);
1136
+ else
1137
+ relisting.set(entry.conversation, { ...entry });
1138
+ }
1139
+ if (event.more)
1140
+ return false;
1141
+ if (relisting !== null) {
1142
+ listing.clear();
1143
+ for (const entry of relisting.values())
1144
+ listed(entry);
1145
+ relisting = null;
999
1146
  }
1147
+ listCursor = event.cursor;
1148
+ deps.stream()?.hold(event.cursor);
1000
1149
  listKnown = true;
1150
+ for (const conversation of replicated) {
1151
+ if (!listing.has(conversation))
1152
+ continue;
1153
+ if (!missed(conversation))
1154
+ behind.delete(conversation);
1155
+ else if (!behind.has(conversation))
1156
+ gap(conversation);
1157
+ }
1158
+ openingEnded();
1001
1159
  }
1002
1160
  else if (event.kind === 'listed') {
1003
- listing.set(event.entry.conversation, { ...event.entry });
1004
- creations.delete(event.entry.conversation);
1161
+ listed(event.entry);
1162
+ relisting?.set(event.entry.conversation, { ...event.entry });
1005
1163
  }
1006
1164
  else {
1007
1165
  listing.delete(event.conversation);
1166
+ relisting?.delete(event.conversation);
1008
1167
  }
1168
+ keepList();
1009
1169
  emitList();
1010
- return (event.kind !== 'list' &&
1170
+ return ((event.kind !== 'list' &&
1011
1171
  listKnown &&
1012
1172
  (listing.size !== replicated.size ||
1013
- [...listing.keys()].some((conversation) => !replicated.has(conversation))));
1173
+ [...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
1174
+ (listKnown && [...drafts.keys()].some(retiredChat)));
1014
1175
  }
1015
1176
  heard(event);
1016
1177
  return false;
1017
1178
  },
1018
- searchable() {
1019
- return [...(listKnown ? listing.keys() : stored)].sort();
1020
- },
1021
- acquired(conversation) {
1022
- return reads.has(conversation) && !partials.has(conversation);
1023
- },
1024
1179
  close() {
1025
1180
  views.clear();
1026
1181
  lives.clear();
1182
+ unsent.clear();
1027
1183
  listWatchers.clear();
1184
+ unlisted.clear();
1028
1185
  agentWatchers.clear();
1186
+ unnamed.clear();
1029
1187
  },
1030
1188
  };
1031
1189
  }