@agentshouse/mdruntime 0.1.0-c1db74d14e5fa → 0.1.0-c1ff51b87bc3d

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