@agentshouse/mdruntime 0.1.0-c0d767f8f799a → 0.1.0-c0ffc146f48dd

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