@agentshouse/mdruntime 0.1.0-c5d3fa6fb67bc → 0.1.0-c5d81e5d5cb58

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 +86 -34
  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 +31 -8
  11. package/dist/bootstrap.d.ts.map +1 -1
  12. package/dist/bootstrap.js +187 -85
  13. package/dist/chain.d.ts +11 -0
  14. package/dist/chain.d.ts.map +1 -0
  15. package/dist/chain.js +64 -0
  16. package/dist/change-set.d.ts +9 -16
  17. package/dist/change-set.d.ts.map +1 -1
  18. package/dist/change-set.js +11 -89
  19. package/dist/change.d.ts +31 -1
  20. package/dist/change.d.ts.map +1 -1
  21. package/dist/change.js +10 -1
  22. package/dist/client.d.ts +13 -5
  23. package/dist/client.d.ts.map +1 -1
  24. package/dist/client.js +95 -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 +28 -13
  32. package/dist/connection.d.ts.map +1 -1
  33. package/dist/connection.js +2937 -1064
  34. package/dist/conversation.d.ts +18 -3
  35. package/dist/conversation.d.ts.map +1 -1
  36. package/dist/conversation.js +340 -117
  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 +3 -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 +8 -7
  53. package/dist/host.d.ts.map +1 -1
  54. package/dist/host.js +84 -14
  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 +8 -4
  68. package/dist/memory.d.ts.map +1 -1
  69. package/dist/memory.js +79 -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 +6 -0
  74. package/dist/model.d.ts.map +1 -1
  75. package/dist/model.js +7 -1
  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 +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 +35 -2
  95. package/dist/query.d.ts.map +1 -1
  96. package/dist/query.js +46 -7
  97. package/dist/reach.d.ts +13 -4
  98. package/dist/reach.d.ts.map +1 -1
  99. package/dist/reach.js +210 -68
  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 +20 -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 +100 -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 -56
  115. package/dist/replica.d.ts +56 -33
  116. package/dist/replica.d.ts.map +1 -1
  117. package/dist/replica.js +298 -188
  118. package/dist/room-draft.d.ts +16 -1
  119. package/dist/room-draft.d.ts.map +1 -1
  120. package/dist/room-draft.js +65 -25
  121. package/dist/rows.d.ts +38 -30
  122. package/dist/rows.d.ts.map +1 -1
  123. package/dist/rows.js +173 -96
  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 +25 -42
  128. package/dist/search.d.ts.map +1 -1
  129. package/dist/search.js +30 -169
  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 +106 -90
  140. package/dist/store.d.ts.map +1 -1
  141. package/dist/store.js +355 -659
  142. package/dist/stored.d.ts +154 -0
  143. package/dist/stored.d.ts.map +1 -0
  144. package/dist/stored.js +624 -0
  145. package/dist/tables.d.ts +362 -0
  146. package/dist/tables.d.ts.map +1 -0
  147. package/dist/tables.js +627 -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 +33 -1
  158. package/dist/wire.d.ts.map +1 -1
  159. package/dist/wire.js +9 -1
  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 -299
  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 -61
  178. package/dist/chat/kit.d.ts.map +0 -1
  179. package/dist/chat/kit.js +0 -123
  180. package/dist/chat/lasting.d.ts +0 -6
  181. package/dist/chat/lasting.d.ts.map +0 -1
  182. package/dist/chat/lasting.js +0 -19
  183. package/dist/chat/list.d.ts +0 -27
  184. package/dist/chat/list.d.ts.map +0 -1
  185. package/dist/chat/list.js +0 -63
  186. package/dist/chat/markdown.d.ts +0 -20
  187. package/dist/chat/markdown.d.ts.map +0 -1
  188. package/dist/chat/markdown.js +0 -188
  189. package/dist/chat/message.d.ts +0 -6
  190. package/dist/chat/message.d.ts.map +0 -1
  191. package/dist/chat/message.js +0 -192
  192. package/dist/chat/messages.d.ts +0 -22
  193. package/dist/chat/messages.d.ts.map +0 -1
  194. package/dist/chat/messages.js +0 -155
  195. package/dist/chat/name.d.ts +0 -6
  196. package/dist/chat/name.d.ts.map +0 -1
  197. package/dist/chat/name.js +0 -37
  198. package/dist/chat/opened.d.ts +0 -11
  199. package/dist/chat/opened.d.ts.map +0 -1
  200. package/dist/chat/opened.js +0 -17
  201. package/dist/chat/overlay.d.ts +0 -31
  202. package/dist/chat/overlay.d.ts.map +0 -1
  203. package/dist/chat/overlay.js +0 -97
  204. package/dist/chat/parts.d.ts +0 -57
  205. package/dist/chat/parts.d.ts.map +0 -1
  206. package/dist/chat/parts.js +0 -119
  207. package/dist/chat/process.d.ts +0 -20
  208. package/dist/chat/process.d.ts.map +0 -1
  209. package/dist/chat/process.js +0 -32
  210. package/dist/chat/provider.d.ts +0 -63
  211. package/dist/chat/provider.d.ts.map +0 -1
  212. package/dist/chat/provider.js +0 -629
  213. package/dist/chat/question.d.ts +0 -9
  214. package/dist/chat/question.d.ts.map +0 -1
  215. package/dist/chat/question.js +0 -80
  216. package/dist/chat/questions.d.ts +0 -31
  217. package/dist/chat/questions.d.ts.map +0 -1
  218. package/dist/chat/questions.js +0 -111
  219. package/dist/chat/read.d.ts +0 -5
  220. package/dist/chat/read.d.ts.map +0 -1
  221. package/dist/chat/read.js +0 -13
  222. package/dist/chat/ready.d.ts +0 -34
  223. package/dist/chat/ready.d.ts.map +0 -1
  224. package/dist/chat/ready.js +0 -91
  225. package/dist/chat/style.d.ts +0 -4
  226. package/dist/chat/style.d.ts.map +0 -1
  227. package/dist/chat/style.js +0 -302
  228. package/dist/chat.d.ts +0 -14
  229. package/dist/chat.d.ts.map +0 -1
  230. package/dist/chat.js +0 -8
  231. package/dist/read-side.d.ts +0 -25
  232. package/dist/read-side.d.ts.map +0 -1
  233. package/dist/read-side.js +0 -642
@@ -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,19 +32,25 @@ 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';
26
42
  const NEW_CHAT = 'new:';
43
+ export const HISTORY_CONVERSATIONS = 64;
27
44
  export function newChat(agent) {
28
45
  return `${NEW_CHAT}${agent}`;
29
46
  }
30
47
  function creationOf(input) {
31
48
  if (typeof input !== 'object' || input === null)
32
49
  return null;
33
- const { agent_id: agent, operation_id: operation } = input;
34
- return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : 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;
35
54
  }
36
55
  function startedTurn(turn, operation) {
37
56
  if (operation.op === 'begin')
@@ -82,19 +101,25 @@ function appliedTurn(held, operation) {
82
101
  parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
83
102
  };
84
103
  }
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
104
  function windowOf(held, arriving, limit) {
93
105
  const byIdentity = new Map(held.map((message) => [message.identity, message]));
94
106
  for (const message of arriving)
95
107
  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));
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
+ };
98
123
  }
99
124
  function newestOf(arriving, from) {
100
125
  return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
@@ -165,18 +190,34 @@ function answered(result, field) {
165
190
  const value = answer[field];
166
191
  return typeof value === 'string' ? value : null;
167
192
  }
168
- function limitOf(options) {
169
- const { limit } = options;
170
- if (limit === undefined)
171
- return undefined;
172
- 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)) {
173
208
  throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
174
209
  }
175
- return limit;
210
+ return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
176
211
  }
177
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
+ };
178
219
  function unwritten(draft) {
179
- return draft.text === '' && draft.files.length === 0;
220
+ return draft.text === '';
180
221
  }
181
222
  function pending(draft) {
182
223
  return {
@@ -191,11 +232,13 @@ function withSubmitted(draft, submitted) {
191
232
  const { submitted: _held, ...unsent } = draft;
192
233
  return submitted.length === 0 ? unsent : { ...unsent, submitted };
193
234
  }
235
+ function prepended(draft, said) {
236
+ return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
237
+ }
194
238
  function returned(draft, submission, others) {
195
239
  return withSubmitted({
196
240
  conversation: draft.conversation,
197
- text: [submission.text, draft.text].filter((one) => one !== '').join('\n'),
198
- files: [...submission.files, ...draft.files],
241
+ ...prepended(draft, submission),
199
242
  revision: chatDraftRevision(),
200
243
  }, others);
201
244
  }
@@ -206,23 +249,29 @@ export function createConversationStore(deps) {
206
249
  const drafts = new Map();
207
250
  const sending = new Map();
208
251
  const behind = new Set();
252
+ const catching = new Map();
209
253
  const gaps = new Map();
210
254
  const lives = new Map();
255
+ const unsent = new Map();
211
256
  const views = new Map();
212
257
  const listing = new Map();
213
258
  const creations = new Set();
214
259
  const listWatchers = new Set();
260
+ const unlisted = new Set();
215
261
  let listKnown = false;
216
262
  const agentWatchers = new Set();
263
+ const unnamed = new Set();
264
+ let agentsKnown = false;
217
265
  let agentNames = new Map();
218
266
  let retiredAgents = new Set();
219
267
  let agentsRead = null;
268
+ let agentsFrom;
220
269
  let replicated = new Set();
221
270
  let handedReplicated;
222
271
  function liveOf(conversation) {
223
272
  return lives.get(conversation) ?? QUIET;
224
273
  }
225
- function viewOf(conversation, held) {
274
+ function viewOf(conversation, held, frame) {
226
275
  const draft = drafts.get(conversation);
227
276
  const facts = liveOf(conversation);
228
277
  return {
@@ -233,14 +282,18 @@ export function createConversationStore(deps) {
233
282
  text,
234
283
  message,
235
284
  })),
285
+ ...frame,
236
286
  turn: structuredClone(facts.turn),
237
287
  questions: structuredClone(facts.questions),
238
288
  process: facts.process === null ? null : { ...facts.process },
239
289
  commands: structuredClone(facts.commands),
240
290
  options: structuredClone(facts.options),
291
+ starting: facts.starting,
292
+ link: facts.link === null ? null : { ...facts.link },
293
+ deliveryUnknown: [...facts.deliveryUnknown],
241
294
  chatDraft: draft === undefined || unwritten(draft)
242
295
  ? null
243
- : { text: draft.text, files: [...draft.files], revision: draft.revision },
296
+ : { text: draft.text, revision: draft.revision },
244
297
  };
245
298
  }
246
299
  function emit(conversation) {
@@ -252,21 +305,36 @@ export function createConversationStore(deps) {
252
305
  const ready = [...view.watchers].filter((watcher) => watcher.ready);
253
306
  if (ready.length === 0)
254
307
  continue;
255
- const value = viewOf(conversation, view.window);
308
+ const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
256
309
  for (const watcher of ready)
257
310
  watcher.notify(value);
258
311
  }
259
312
  }
260
- function slide(conversation, arriving) {
313
+ function sharedFrame(view, window) {
314
+ return frameOf(view.asked, window, view.more, view.total);
315
+ }
316
+ async function slide(local, conversation, arriving) {
317
+ let count;
261
318
  for (const view of views.values()) {
262
319
  if (view.conversation !== conversation || view.window === null)
263
320
  continue;
264
- view.window = arriving === null ? [] : windowOf(view.window, arriving, view.limit);
321
+ if (arriving === null) {
322
+ view.window = [];
323
+ view.more = false;
324
+ if (view.total !== null)
325
+ view.total = 0;
326
+ continue;
327
+ }
328
+ const joining = arriving.filter((message) => precedes(message, view.asked.before));
329
+ const next = windowOf(view.window, joining, view.asked.limit);
330
+ view.window = next.messages;
331
+ view.more ||= next.dropped;
332
+ if (view.total !== null && arriving.length > 0) {
333
+ count ??= await local.messageCount(conversation);
334
+ view.total = count;
335
+ }
265
336
  }
266
337
  }
267
- function changesOf(conversation, arriving) {
268
- return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
269
- }
270
338
  function stateOf(conversation) {
271
339
  const facts = liveOf(conversation);
272
340
  if (facts.questions.length > 0)
@@ -281,16 +349,44 @@ export function createConversationStore(deps) {
281
349
  created: entry.created,
282
350
  finished: entry.finished,
283
351
  outcome: entry.outcome,
352
+ waiting: entry.waiting,
284
353
  state: stateOf(entry.conversation),
285
354
  started: lives.get(entry.conversation)?.turn?.started ?? null,
286
355
  }));
287
356
  }
288
357
  function emitList() {
289
- if (deps.closed() || listWatchers.size === 0)
358
+ if (deps.closed())
290
359
  return;
291
- const value = entries();
292
- for (const watcher of listWatchers)
293
- watcher(value);
360
+ if (listWatchers.size > 0) {
361
+ const value = entries();
362
+ for (const watcher of listWatchers)
363
+ watcher(value);
364
+ }
365
+ learned();
366
+ }
367
+ function unlearnable() {
368
+ return !deps.online() || (deps.listed() && deps.agents() === undefined);
369
+ }
370
+ function learned() {
371
+ if (deps.closed())
372
+ return;
373
+ const named = agentsKnown || unlearnable();
374
+ if (unlisted.size > 0 && named && (listKnown || deps.stream() === undefined || unlearnable())) {
375
+ const value = entries();
376
+ for (const watcher of [...unlisted]) {
377
+ unlisted.delete(watcher);
378
+ listWatchers.add(watcher);
379
+ watcher(value);
380
+ }
381
+ }
382
+ if (unnamed.size > 0 && named) {
383
+ const value = agents();
384
+ for (const watcher of [...unnamed]) {
385
+ unnamed.delete(watcher);
386
+ agentWatchers.add(watcher);
387
+ watcher(value);
388
+ }
389
+ }
294
390
  }
295
391
  function agents() {
296
392
  return [...agentNames]
@@ -321,34 +417,46 @@ export function createConversationStore(deps) {
321
417
  }
322
418
  async function readAgents() {
323
419
  const declaration = deps.agents();
420
+ agentsFrom = JSON.stringify(declaration);
324
421
  if (declaration === undefined)
325
422
  return;
326
- const held = await deps.documents(declaration);
327
- if ('pending' in held)
328
- return;
329
- const { documents } = held;
330
423
  const names = new Map();
331
424
  const retired = new Set();
332
- for (const document of documents) {
333
- if (document.type !== declaration.type)
334
- continue;
335
- const identity = document.fields?.[declaration.identity];
336
- const name = document.fields?.[declaration.name];
337
- if (typeof identity !== 'string' || typeof name !== 'string')
338
- continue;
339
- names.set(identity, name);
340
- const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
341
- if (ended !== undefined && ended !== null)
342
- retired.add(identity);
425
+ let covered = null;
426
+ for (let after;;) {
427
+ const read = await deps.documents(declaration, after);
428
+ if ('pending' in read)
429
+ return;
430
+ const coverage = JSON.stringify(read.coverage);
431
+ if (covered !== null && coverage !== covered)
432
+ return readAgents();
433
+ covered = coverage;
434
+ for (const document of read.documents) {
435
+ const identity = document.fields?.[declaration.identity];
436
+ const name = document.fields?.[declaration.name];
437
+ if (typeof identity !== 'string' || typeof name !== 'string')
438
+ continue;
439
+ names.set(identity, name);
440
+ const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
441
+ if (ended !== undefined && ended !== null)
442
+ retired.add(identity);
443
+ }
444
+ if (read.next === undefined)
445
+ break;
446
+ after = read.next;
343
447
  }
448
+ agentsKnown = true;
344
449
  const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
345
- if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
450
+ if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents])) {
451
+ learned();
346
452
  return;
453
+ }
347
454
  agentNames = names;
348
455
  retiredAgents = retired;
456
+ emitAgents();
349
457
  if (named)
350
458
  emitList();
351
- emitAgents();
459
+ learned();
352
460
  const ended = [...drafts.keys()].filter(retiredChat);
353
461
  if (ended.length > 0)
354
462
  await remove(ended);
@@ -363,9 +471,34 @@ export function createConversationStore(deps) {
363
471
  return agentsRead;
364
472
  }
365
473
  function live(conversation, change) {
474
+ const owed = unsent.get(conversation);
475
+ for (const fact of FACTS.filter((one) => one in change))
476
+ owed?.delete(fact);
366
477
  setLive(conversation, { ...liveOf(conversation), ...change });
367
478
  emit(conversation);
368
479
  }
480
+ function openingEnded() {
481
+ const owed = [...unsent];
482
+ unsent.clear();
483
+ for (const [conversation, facts] of owed) {
484
+ const held = lives.get(conversation);
485
+ if (held === undefined || facts.size === 0)
486
+ continue;
487
+ setLive(conversation, {
488
+ turn: held.turn,
489
+ questions: facts.has('questions') ? QUIET.questions : held.questions,
490
+ process: facts.has('process') ? QUIET.process : held.process,
491
+ commands: facts.has('commands') ? QUIET.commands : held.commands,
492
+ options: facts.has('options') ? QUIET.options : held.options,
493
+ starting: facts.has('starting') ? QUIET.starting : held.starting,
494
+ link: facts.has('link') ? QUIET.link : held.link,
495
+ deliveryUnknown: facts.has('deliveryUnknown')
496
+ ? QUIET.deliveryUnknown
497
+ : held.deliveryUnknown,
498
+ });
499
+ emit(conversation);
500
+ }
501
+ }
369
502
  function relayed(conversation, turn, operation) {
370
503
  const held = liveOf(conversation).turn;
371
504
  const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
@@ -392,12 +525,6 @@ export function createConversationStore(deps) {
392
525
  }
393
526
  async function remove(leaving) {
394
527
  await deps.durably(async (local) => {
395
- const removed = [];
396
- for (const conversation of leaving) {
397
- for (const identity of await local.messageIdentities(conversation)) {
398
- removed.push({ conversation, identity, text: null });
399
- }
400
- }
401
528
  await local.dropConversations(leaving);
402
529
  const changed = [];
403
530
  for (const conversation of leaving) {
@@ -409,11 +536,12 @@ export function createConversationStore(deps) {
409
536
  partials.delete(conversation);
410
537
  drafts.delete(conversation);
411
538
  behind.delete(conversation);
539
+ unsent.delete(conversation);
412
540
  setLive(conversation, null);
413
- slide(conversation, null);
541
+ await slide(local, conversation, null);
414
542
  }
415
543
  if (changed.length > 0)
416
- deps.publish(conversationChangeSet(removed, changed));
544
+ deps.publish(conversationChangeSet(changed));
417
545
  });
418
546
  }
419
547
  function gap(conversation) {
@@ -438,17 +566,18 @@ export function createConversationStore(deps) {
438
566
  if (advances)
439
567
  reads.set(conversation, message.position);
440
568
  sealTurns(conversation, new Set([message.turn]));
441
- slide(conversation, [message]);
442
- deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
569
+ await slide(local, conversation, [message]);
570
+ deps.publish(conversationChangeSet([conversation]));
443
571
  });
444
572
  }
445
- async function readHistories(conversations) {
573
+ async function readHistories(conversations, ended) {
446
574
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
447
- let asking = conversations.map((conversation) => ({
575
+ let waiting = conversations.map((conversation) => ({
448
576
  conversation,
449
577
  after: reads.get(conversation) ?? undefined,
450
578
  }));
451
- while (asking.length > 0) {
579
+ while (waiting.length > 0) {
580
+ const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
452
581
  const result = await deps.call('conversation.history', {
453
582
  conversations: asking.map(({ conversation, after }) => ({
454
583
  conversation,
@@ -468,10 +597,33 @@ export function createConversationStore(deps) {
468
597
  following.push({ conversation: page.conversation, after: page.next });
469
598
  }
470
599
  }
471
- asking = following;
600
+ for (const { conversation } of asking) {
601
+ if (!following.some((next) => next.conversation === conversation))
602
+ ended(conversation);
603
+ }
604
+ waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
472
605
  }
473
606
  return null;
474
607
  }
608
+ function caughtUp(conversations) {
609
+ const ends = new Map();
610
+ for (const conversation of conversations) {
611
+ const held = new Promise((end) => ends.set(conversation, end));
612
+ catching.set(conversation, held);
613
+ void held.then(() => {
614
+ if (catching.get(conversation) === held)
615
+ catching.delete(conversation);
616
+ });
617
+ }
618
+ const ended = (conversation) => ends.get(conversation)?.();
619
+ const read = readHistories(conversations, ended);
620
+ const endAll = () => {
621
+ for (const conversation of conversations)
622
+ ended(conversation);
623
+ };
624
+ read.then(endAll, endAll);
625
+ return read;
626
+ }
475
627
  function keepPage(conversation, arriving, from, next, generation) {
476
628
  return deps.durably(async (local) => {
477
629
  if (!replicated.has(conversation))
@@ -490,9 +642,9 @@ export function createConversationStore(deps) {
490
642
  sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
491
643
  if (whole && (gaps.get(conversation) ?? 0) === generation)
492
644
  behind.delete(conversation);
493
- slide(conversation, arriving);
645
+ await slide(local, conversation, arriving);
494
646
  if (arriving.length > 0 || sealing || whole) {
495
- deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
647
+ deps.publish(conversationChangeSet([conversation]));
496
648
  }
497
649
  });
498
650
  }
@@ -501,11 +653,8 @@ export function createConversationStore(deps) {
501
653
  if (event.kind === 'gap') {
502
654
  if (replicated.has(conversation))
503
655
  gap(conversation);
504
- const held = lives.get(conversation);
505
- if (held === undefined)
506
- return;
507
- setLive(conversation, { ...QUIET, turn: held.turn });
508
- emit(conversation);
656
+ if (lives.has(conversation))
657
+ unsent.set(conversation, new Set(FACTS));
509
658
  return;
510
659
  }
511
660
  if (event.kind === 'turn') {
@@ -528,11 +677,30 @@ export function createConversationStore(deps) {
528
677
  live(conversation, { options: structuredClone(event.options) });
529
678
  return;
530
679
  }
680
+ if (event.kind === 'starting') {
681
+ live(conversation, { starting: event.step });
682
+ return;
683
+ }
684
+ if (event.kind === 'link') {
685
+ live(conversation, { link: { connected: event.connected } });
686
+ return;
687
+ }
688
+ if (event.kind === 'deliveries') {
689
+ live(conversation, { deliveryUnknown: [...event.unknown] });
690
+ return;
691
+ }
531
692
  const arriving = acceptedMessage(event.message);
532
693
  if (arriving === null)
533
694
  return;
534
695
  if (replicated.has(conversation)) {
535
- void keep(conversation, arriving).catch(() => { });
696
+ if (deps.full()) {
697
+ gap(conversation);
698
+ return;
699
+ }
700
+ void keep(conversation, arriving).catch((error) => {
701
+ if (refusedForSpace(error))
702
+ gap(conversation);
703
+ });
536
704
  return;
537
705
  }
538
706
  if (sealTurns(conversation, new Set([arriving.turn])))
@@ -567,7 +735,6 @@ export function createConversationStore(deps) {
567
735
  const blank = {
568
736
  conversation: from,
569
737
  text: '',
570
- files: [],
571
738
  revision: chatDraftRevision(),
572
739
  };
573
740
  const { moves: _answered, ...unsent } = held ?? blank;
@@ -577,7 +744,7 @@ export function createConversationStore(deps) {
577
744
  drafts.set(from, left);
578
745
  for (const one of taken)
579
746
  drafts.set(to, one);
580
- deps.publish(conversationChangeSet([], [from, to]));
747
+ deps.publish(conversationChangeSet([from, to]));
581
748
  });
582
749
  }
583
750
  function withDraft(conversation, next) {
@@ -586,7 +753,7 @@ export function createConversationStore(deps) {
586
753
  if (following === undefined)
587
754
  return;
588
755
  await kept(local, conversation, following);
589
- deps.publish(conversationChangeSet([], [conversation]));
756
+ deps.publish(conversationChangeSet([conversation]));
590
757
  });
591
758
  }
592
759
  async function submitted(conversation, operation, draft) {
@@ -598,17 +765,12 @@ export function createConversationStore(deps) {
598
765
  if (submission !== undefined)
599
766
  return undefined;
600
767
  const said = draft ?? current;
601
- submission = {
602
- operation,
603
- text: said?.text ?? '',
604
- files: [...(said?.files ?? [])],
605
- turn: null,
606
- };
768
+ submission = { operation, text: said?.text ?? '', turn: null };
607
769
  return {
608
770
  conversation,
609
771
  ...(draft === undefined || current === undefined
610
- ? { text: '', files: [], revision: chatDraftRevision() }
611
- : { text: current.text, files: current.files, revision: current.revision }),
772
+ ? { text: '', revision: chatDraftRevision() }
773
+ : { text: current.text, revision: current.revision }),
612
774
  submitted: [...(current?.submitted ?? []), submission],
613
775
  };
614
776
  });
@@ -623,15 +785,14 @@ export function createConversationStore(deps) {
623
785
  });
624
786
  }
625
787
  async function dispatched(conversation, submission) {
626
- const dispatch = {};
788
+ const dispatch = { answered: false };
627
789
  sending.set(submission.operation, dispatch);
628
790
  try {
629
- const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
630
791
  const result = await invoke('conversation.send', conversation, {
631
792
  text: submission.text,
632
- files,
633
793
  operation_id: submission.operation,
634
794
  });
795
+ dispatch.answered = result.kind !== 'transport-uncertainty';
635
796
  if (result.kind === 'revoked-authorization')
636
797
  return result;
637
798
  if (result.kind !== 'action' && result.retryable) {
@@ -664,11 +825,23 @@ export function createConversationStore(deps) {
664
825
  const surface = {
665
826
  async list() {
666
827
  await agentsReady();
667
- return entries();
828
+ return new Promise((settle) => {
829
+ const answer = (listed) => {
830
+ unlisted.delete(answer);
831
+ listWatchers.delete(answer);
832
+ settle(listed);
833
+ };
834
+ unlisted.add(answer);
835
+ learned();
836
+ });
668
837
  },
669
838
  async read(conversation, options = {}) {
670
- const limit = limitOf(options);
671
- return deps.durably(async (local) => structuredClone(viewOf(conversation, await local.messageWindow(conversation, limit))));
839
+ const asked = askedOf(options);
840
+ return deps.durably(async (local) => {
841
+ const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
842
+ const total = asked.total ? await local.messageCount(conversation) : null;
843
+ return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
844
+ });
672
845
  },
673
846
  async create(input) {
674
847
  const result = await deps.call('conversation.create', input);
@@ -683,8 +856,17 @@ export function createConversationStore(deps) {
683
856
  return result;
684
857
  },
685
858
  async send(conversation, operation = changeBatchIdentity(), draft) {
859
+ if (deps.full())
860
+ return DURABLE_WRITE;
686
861
  if (deps.online()) {
687
- const { submission, earlier } = await submitted(conversation, operation, draft);
862
+ const held = await submitted(conversation, operation, draft).catch((error) => {
863
+ if (refusedForSpace(error))
864
+ return null;
865
+ throw error;
866
+ });
867
+ if (held === null)
868
+ return DURABLE_WRITE;
869
+ const { submission, earlier } = held;
688
870
  if (!earlier || !deps.expired(operation))
689
871
  return dispatched(conversation, submission);
690
872
  await forgotten(conversation, submission);
@@ -693,7 +875,7 @@ export function createConversationStore(deps) {
693
875
  if (draft !== undefined) {
694
876
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
695
877
  ? undefined
696
- : returned(current ?? { conversation, text: '', files: [], revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
878
+ : returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
697
879
  }
698
880
  return OFFLINE;
699
881
  },
@@ -727,21 +909,22 @@ export function createConversationStore(deps) {
727
909
  return withDraft(conversation, (current) => ({
728
910
  conversation,
729
911
  text: draft.text,
730
- files: [...draft.files],
731
912
  revision,
732
913
  ...(current === undefined ? {} : pending(current)),
733
914
  }));
734
915
  },
916
+ prepend(conversation, draft) {
917
+ return withDraft(conversation, (current) => ({
918
+ conversation,
919
+ ...(current === undefined ? { text: draft.text } : prepended(current, draft)),
920
+ revision: chatDraftRevision(),
921
+ ...(current === undefined ? {} : pending(current)),
922
+ }));
923
+ },
735
924
  discard(conversation) {
736
925
  return withDraft(conversation, (current) => current === undefined
737
926
  ? undefined
738
- : {
739
- conversation,
740
- text: '',
741
- files: [],
742
- revision: chatDraftRevision(),
743
- ...pending(current),
744
- });
927
+ : { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
745
928
  },
746
929
  };
747
930
  return {
@@ -781,12 +964,25 @@ export function createConversationStore(deps) {
781
964
  await remove(leaving);
782
965
  },
783
966
  async catchUp(progress) {
784
- const reading = [...behind].filter((conversation) => replicated.has(conversation)).sort();
785
- if (reading.length > 0 && !deps.closed()) {
786
- const failed = await readHistories(reading);
967
+ if (deps.full()) {
968
+ if (behind.size > 0)
969
+ progress.complete = false;
970
+ return;
971
+ }
972
+ const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
973
+ const read = async (conversations) => {
974
+ if (conversations.length === 0 || deps.closed())
975
+ return;
976
+ const failed = await caughtUp(conversations);
787
977
  if (failed?.kind === 'transport-uncertainty')
788
978
  progress.reachable = false;
789
- }
979
+ };
980
+ const asking = [...behind].filter(owed).sort();
981
+ const awaited = asking.filter((conversation) => catching.has(conversation));
982
+ await Promise.all([
983
+ read(asking.filter((conversation) => !catching.has(conversation))),
984
+ Promise.all(awaited.map((conversation) => catching.get(conversation))).then(() => read(awaited.filter((conversation) => owed(conversation) && !catching.has(conversation)))),
985
+ ]);
790
986
  if (behind.size > 0)
791
987
  progress.complete = false;
792
988
  },
@@ -826,29 +1022,42 @@ export function createConversationStore(deps) {
826
1022
  gap(conversation);
827
1023
  },
828
1024
  reopened() {
829
- sending.clear();
1025
+ for (const [operation, dispatch] of sending)
1026
+ if (!dispatch.answered)
1027
+ sending.delete(operation);
1028
+ },
1029
+ lost() {
1030
+ catching.clear();
830
1031
  },
831
1032
  watch(conversation, options, deliver) {
832
- const limit = limitOf(options);
833
- const key = JSON.stringify([conversation, limit ?? null]);
1033
+ const asked = askedOf(options);
1034
+ const key = JSON.stringify([conversation, asked.limit, asked.after ?? null, asked.total]);
834
1035
  const view = views.get(key) ?? {
835
1036
  conversation,
836
- limit,
1037
+ asked,
837
1038
  watchers: new Set(),
838
1039
  window: null,
1040
+ more: false,
1041
+ total: null,
839
1042
  };
840
1043
  views.set(key, view);
841
1044
  const watcher = { notify: deliver, ready: false };
842
1045
  view.watchers.add(watcher);
843
1046
  void deps
844
1047
  .durably(async (local) => {
845
- view.window ??= await local.messageWindow(conversation, limit);
1048
+ if (view.window !== null)
1049
+ return;
1050
+ const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
1051
+ view.window = messages;
1052
+ view.more = more;
1053
+ view.total = asked.total ? await local.messageCount(conversation) : null;
846
1054
  })
847
1055
  .then(() => {
848
1056
  if (deps.closed() || !view.watchers.has(watcher))
849
1057
  return;
850
1058
  watcher.ready = true;
851
- watcher.notify(viewOf(conversation, view.window ?? []));
1059
+ const window = view.window ?? [];
1060
+ watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
852
1061
  })
853
1062
  .catch(() => { });
854
1063
  return () => {
@@ -859,25 +1068,35 @@ export function createConversationStore(deps) {
859
1068
  };
860
1069
  },
861
1070
  watchList(deliver) {
862
- listWatchers.add(deliver);
1071
+ let ended = false;
863
1072
  void agentsReady().then(() => {
864
- if (!deps.closed() && listWatchers.has(deliver))
865
- deliver(entries());
1073
+ if (ended)
1074
+ return;
1075
+ unlisted.add(deliver);
1076
+ learned();
866
1077
  });
867
1078
  return () => {
1079
+ ended = true;
1080
+ unlisted.delete(deliver);
868
1081
  listWatchers.delete(deliver);
869
1082
  };
870
1083
  },
871
1084
  watchAgents(deliver) {
872
- agentWatchers.add(deliver);
873
- void agentsReady().then(() => {
874
- if (!deps.closed() && agentWatchers.has(deliver))
875
- deliver(agents());
876
- });
1085
+ unnamed.add(deliver);
1086
+ void agentsReady().then(learned);
877
1087
  return () => {
1088
+ unnamed.delete(deliver);
878
1089
  agentWatchers.delete(deliver);
879
1090
  };
880
1091
  },
1092
+ learned,
1093
+ declared() {
1094
+ if (JSON.stringify(deps.agents()) === agentsFrom)
1095
+ return;
1096
+ agentsRead = readAgents().catch(() => {
1097
+ agentsRead = null;
1098
+ });
1099
+ },
881
1100
  changed(set) {
882
1101
  for (const conversation of set.conversations)
883
1102
  emit(conversation);
@@ -900,6 +1119,7 @@ export function createConversationStore(deps) {
900
1119
  creations.delete(entry.conversation);
901
1120
  }
902
1121
  listKnown = true;
1122
+ openingEnded();
903
1123
  }
904
1124
  else if (event.kind === 'listed') {
905
1125
  listing.set(event.entry.conversation, { ...event.entry });
@@ -926,8 +1146,11 @@ export function createConversationStore(deps) {
926
1146
  close() {
927
1147
  views.clear();
928
1148
  lives.clear();
1149
+ unsent.clear();
929
1150
  listWatchers.clear();
1151
+ unlisted.clear();
930
1152
  agentWatchers.clear();
1153
+ unnamed.clear();
931
1154
  },
932
1155
  };
933
1156
  }