@agentshouse/mdruntime 0.1.0-cfcbb238a5fa0 → 0.1.0-cfcf93be6310b

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