@agentshouse/mdruntime 0.1.0-ca68e8b4bbebc → 0.1.0-ca6edc1abd618

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 (221) hide show
  1. package/README.md +18 -15
  2. package/dist/adapter.d.ts +70 -36
  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 +21 -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 -8
  11. package/dist/bootstrap.d.ts.map +1 -1
  12. package/dist/bootstrap.js +150 -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 +37 -6
  25. package/dist/collection.d.ts +6 -19
  26. package/dist/collection.d.ts.map +1 -1
  27. package/dist/collection.js +31 -289
  28. package/dist/compile.d.ts +16 -0
  29. package/dist/compile.d.ts.map +1 -0
  30. package/dist/compile.js +721 -0
  31. package/dist/connection.d.ts +22 -12
  32. package/dist/connection.d.ts.map +1 -1
  33. package/dist/connection.js +2485 -1036
  34. package/dist/conversation.d.ts +16 -3
  35. package/dist/conversation.d.ts.map +1 -1
  36. package/dist/conversation.js +238 -96
  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 -10
  47. package/dist/file.d.ts.map +1 -1
  48. package/dist/file.js +3 -120
  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 +4 -7
  53. package/dist/host.d.ts.map +1 -1
  54. package/dist/host.js +51 -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 +13 -10
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +4 -1
  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/memory.d.ts +4 -3
  65. package/dist/memory.d.ts.map +1 -1
  66. package/dist/memory.js +32 -26
  67. package/dist/messages.d.ts +28 -0
  68. package/dist/messages.d.ts.map +1 -0
  69. package/dist/messages.js +90 -0
  70. package/dist/model.d.ts.map +1 -1
  71. package/dist/model.js +1 -1
  72. package/dist/name.d.ts +4 -0
  73. package/dist/name.d.ts.map +1 -0
  74. package/dist/name.js +122 -0
  75. package/dist/observation.d.ts +14 -0
  76. package/dist/observation.d.ts.map +1 -1
  77. package/dist/observation.js +93 -3
  78. package/dist/opened.d.ts +22 -0
  79. package/dist/opened.d.ts.map +1 -0
  80. package/dist/opened.js +107 -0
  81. package/dist/outcome.d.ts +34 -4
  82. package/dist/outcome.d.ts.map +1 -1
  83. package/dist/outcome.js +25 -0
  84. package/dist/overlay.d.ts +2 -5
  85. package/dist/overlay.d.ts.map +1 -1
  86. package/dist/overlay.js +87 -172
  87. package/dist/query.d.ts +26 -2
  88. package/dist/query.d.ts.map +1 -1
  89. package/dist/query.js +41 -6
  90. package/dist/reach.d.ts +5 -3
  91. package/dist/reach.d.ts.map +1 -1
  92. package/dist/reach.js +167 -59
  93. package/dist/react.d.ts +1 -2
  94. package/dist/react.d.ts.map +1 -1
  95. package/dist/react.js +2 -14
  96. package/dist/readiness.d.ts +20 -9
  97. package/dist/readiness.d.ts.map +1 -1
  98. package/dist/readiness.js +6 -6
  99. package/dist/reads.d.ts.map +1 -1
  100. package/dist/reads.js +4 -2
  101. package/dist/recovery.d.ts +2 -2
  102. package/dist/recovery.d.ts.map +1 -1
  103. package/dist/recovery.js +8 -7
  104. package/dist/refresh.d.ts +15 -14
  105. package/dist/refresh.d.ts.map +1 -1
  106. package/dist/refresh.js +113 -56
  107. package/dist/replica.d.ts +56 -33
  108. package/dist/replica.d.ts.map +1 -1
  109. package/dist/replica.js +297 -188
  110. package/dist/room-draft.d.ts +4 -1
  111. package/dist/room-draft.d.ts.map +1 -1
  112. package/dist/room-draft.js +62 -22
  113. package/dist/rows.d.ts +29 -30
  114. package/dist/rows.d.ts.map +1 -1
  115. package/dist/rows.js +113 -64
  116. package/dist/save.d.ts +51 -14
  117. package/dist/save.d.ts.map +1 -1
  118. package/dist/save.js +518 -180
  119. package/dist/search.d.ts +23 -42
  120. package/dist/search.d.ts.map +1 -1
  121. package/dist/search.js +28 -182
  122. package/dist/seed.d.ts.map +1 -1
  123. package/dist/seed.js +18 -4
  124. package/dist/shared.d.ts +8 -3
  125. package/dist/shared.d.ts.map +1 -1
  126. package/dist/shared.js +129 -66
  127. package/dist/store.d.ts +89 -89
  128. package/dist/store.d.ts.map +1 -1
  129. package/dist/store.js +329 -669
  130. package/dist/stored.d.ts +145 -0
  131. package/dist/stored.d.ts.map +1 -0
  132. package/dist/stored.js +567 -0
  133. package/dist/tables.d.ts +354 -0
  134. package/dist/tables.d.ts.map +1 -0
  135. package/dist/tables.js +624 -0
  136. package/dist/turn.d.ts +5 -3
  137. package/dist/turn.d.ts.map +1 -1
  138. package/dist/turn.js +51 -21
  139. package/dist/validation.d.ts +30 -0
  140. package/dist/validation.d.ts.map +1 -0
  141. package/dist/validation.js +162 -0
  142. package/dist/windows.d.ts +22 -0
  143. package/dist/windows.d.ts.map +1 -0
  144. package/dist/windows.js +89 -0
  145. package/dist/wire.d.ts +29 -1
  146. package/dist/wire.d.ts.map +1 -1
  147. package/dist/wire.js +8 -1
  148. package/dist/words.d.ts +19 -0
  149. package/dist/words.d.ts.map +1 -0
  150. package/dist/words.js +150 -0
  151. package/package.json +18 -19
  152. package/AGENTS.md +0 -299
  153. package/dist/acquisition.d.ts +0 -14
  154. package/dist/acquisition.d.ts.map +0 -1
  155. package/dist/acquisition.js +0 -24
  156. package/dist/chat/acts.d.ts +0 -33
  157. package/dist/chat/acts.d.ts.map +0 -1
  158. package/dist/chat/acts.js +0 -36
  159. package/dist/chat/context.d.ts +0 -3
  160. package/dist/chat/context.d.ts.map +0 -1
  161. package/dist/chat/context.js +0 -10
  162. package/dist/chat/input.d.ts +0 -9
  163. package/dist/chat/input.d.ts.map +0 -1
  164. package/dist/chat/input.js +0 -99
  165. package/dist/chat/kit.d.ts +0 -61
  166. package/dist/chat/kit.d.ts.map +0 -1
  167. package/dist/chat/kit.js +0 -123
  168. package/dist/chat/lasting.d.ts +0 -6
  169. package/dist/chat/lasting.d.ts.map +0 -1
  170. package/dist/chat/lasting.js +0 -19
  171. package/dist/chat/list.d.ts +0 -27
  172. package/dist/chat/list.d.ts.map +0 -1
  173. package/dist/chat/list.js +0 -63
  174. package/dist/chat/markdown.d.ts +0 -20
  175. package/dist/chat/markdown.d.ts.map +0 -1
  176. package/dist/chat/markdown.js +0 -188
  177. package/dist/chat/message.d.ts +0 -6
  178. package/dist/chat/message.d.ts.map +0 -1
  179. package/dist/chat/message.js +0 -192
  180. package/dist/chat/messages.d.ts +0 -22
  181. package/dist/chat/messages.d.ts.map +0 -1
  182. package/dist/chat/messages.js +0 -155
  183. package/dist/chat/name.d.ts +0 -6
  184. package/dist/chat/name.d.ts.map +0 -1
  185. package/dist/chat/name.js +0 -37
  186. package/dist/chat/opened.d.ts +0 -11
  187. package/dist/chat/opened.d.ts.map +0 -1
  188. package/dist/chat/opened.js +0 -17
  189. package/dist/chat/overlay.d.ts +0 -31
  190. package/dist/chat/overlay.d.ts.map +0 -1
  191. package/dist/chat/overlay.js +0 -97
  192. package/dist/chat/parts.d.ts +0 -57
  193. package/dist/chat/parts.d.ts.map +0 -1
  194. package/dist/chat/parts.js +0 -119
  195. package/dist/chat/process.d.ts +0 -20
  196. package/dist/chat/process.d.ts.map +0 -1
  197. package/dist/chat/process.js +0 -32
  198. package/dist/chat/provider.d.ts +0 -63
  199. package/dist/chat/provider.d.ts.map +0 -1
  200. package/dist/chat/provider.js +0 -629
  201. package/dist/chat/question.d.ts +0 -9
  202. package/dist/chat/question.d.ts.map +0 -1
  203. package/dist/chat/question.js +0 -80
  204. package/dist/chat/questions.d.ts +0 -31
  205. package/dist/chat/questions.d.ts.map +0 -1
  206. package/dist/chat/questions.js +0 -111
  207. package/dist/chat/read.d.ts +0 -5
  208. package/dist/chat/read.d.ts.map +0 -1
  209. package/dist/chat/read.js +0 -13
  210. package/dist/chat/ready.d.ts +0 -34
  211. package/dist/chat/ready.d.ts.map +0 -1
  212. package/dist/chat/ready.js +0 -91
  213. package/dist/chat/style.d.ts +0 -4
  214. package/dist/chat/style.d.ts.map +0 -1
  215. package/dist/chat/style.js +0 -302
  216. package/dist/chat.d.ts +0 -14
  217. package/dist/chat.d.ts.map +0 -1
  218. package/dist/chat.js +0 -8
  219. package/dist/read-side.d.ts +0 -25
  220. package/dist/read-side.d.ts.map +0 -1
  221. package/dist/read-side.js +0 -642
@@ -1,15 +1,29 @@
1
1
  import { conversationChangeSet } from './change-set.js';
2
2
  import { runtimeError } from './failure.js';
3
+ import { refusedForSpace } from './durability.js';
3
4
  import { declaredFile } from './file.js';
4
5
  import { changeBatchIdentity, chatDraftRevision } from './identity.js';
5
6
  import { OFFLINE } from './outcome.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
+ link: null,
16
+ deliveryUnknown: [],
12
17
  };
18
+ const FACTS = [
19
+ 'questions',
20
+ 'process',
21
+ 'commands',
22
+ 'options',
23
+ 'starting',
24
+ 'link',
25
+ 'deliveryUnknown',
26
+ ];
13
27
  export function unheldView(conversation) {
14
28
  return {
15
29
  conversation,
@@ -19,19 +33,25 @@ export function unheldView(conversation) {
19
33
  process: null,
20
34
  commands: [],
21
35
  options: [],
36
+ starting: null,
37
+ link: null,
38
+ deliveryUnknown: [],
22
39
  chatDraft: null,
23
40
  };
24
41
  }
25
42
  export const SESSION_STARTED = 'Session started';
26
43
  const NEW_CHAT = 'new:';
44
+ export const HISTORY_CONVERSATIONS = 64;
27
45
  export function newChat(agent) {
28
46
  return `${NEW_CHAT}${agent}`;
29
47
  }
30
48
  function creationOf(input) {
31
49
  if (typeof input !== 'object' || input === null)
32
50
  return null;
33
- const { agent_id: agent, operation_id: operation } = input;
34
- return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
51
+ const { agent_id: agent, operation_id: operation, replaces, } = input;
52
+ return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
53
+ ? { agent, operation }
54
+ : null;
35
55
  }
36
56
  function startedTurn(turn, operation) {
37
57
  if (operation.op === 'begin')
@@ -82,19 +102,25 @@ function appliedTurn(held, operation) {
82
102
  parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
83
103
  };
84
104
  }
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
105
  function windowOf(held, arriving, limit) {
93
106
  const byIdentity = new Map(held.map((message) => [message.identity, message]));
94
107
  for (const message of arriving)
95
108
  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));
109
+ const next = [...byIdentity.values()].sort(messageOrder);
110
+ const start = Math.max(0, next.length - limit);
111
+ return { messages: next.slice(start), dropped: start > 0 };
112
+ }
113
+ function frameOf(asked, messages, more, total) {
114
+ const oldest = messages[0];
115
+ const next = !more
116
+ ? undefined
117
+ : oldest === undefined
118
+ ? (asked.after ?? FROM_START)
119
+ : cursorOf([oldest.position, oldest.identity]);
120
+ return {
121
+ ...(next === undefined ? {} : { next }),
122
+ ...(total === null ? {} : { total }),
123
+ };
98
124
  }
99
125
  function newestOf(arriving, from) {
100
126
  return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
@@ -165,18 +191,34 @@ function answered(result, field) {
165
191
  const value = answer[field];
166
192
  return typeof value === 'string' ? value : null;
167
193
  }
168
- function limitOf(options) {
169
- const { limit } = options;
170
- if (limit === undefined)
171
- return undefined;
172
- if (!Number.isInteger(limit) || limit < 0) {
194
+ function beforeOf(after) {
195
+ if (after === undefined)
196
+ return null;
197
+ const decoded = typeof after === 'string' ? decodedCursor(after) : null;
198
+ if (decoded?.length === 0)
199
+ return null;
200
+ const [position, identity] = decoded ?? [];
201
+ if (decoded?.length !== 2 || typeof position !== 'string' || typeof identity !== 'string') {
202
+ throw runtimeError('invalid-after', `conversation after must be the next an earlier view returned, got ${String(after)}`);
203
+ }
204
+ return [position, identity];
205
+ }
206
+ function askedOf(options) {
207
+ const { limit, after, total } = options;
208
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 0)) {
173
209
  throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
174
210
  }
175
- return limit;
211
+ return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
176
212
  }
177
213
  const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
214
+ const DURABLE_WRITE = {
215
+ kind: 'rejected',
216
+ code: 'durable-write',
217
+ fields: {},
218
+ retryable: true,
219
+ };
178
220
  function unwritten(draft) {
179
- return draft.text === '' && draft.files.length === 0;
221
+ return draft.text === '';
180
222
  }
181
223
  function pending(draft) {
182
224
  return {
@@ -191,11 +233,13 @@ function withSubmitted(draft, submitted) {
191
233
  const { submitted: _held, ...unsent } = draft;
192
234
  return submitted.length === 0 ? unsent : { ...unsent, submitted };
193
235
  }
236
+ function prepended(draft, said) {
237
+ return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
238
+ }
194
239
  function returned(draft, submission, others) {
195
240
  return withSubmitted({
196
241
  conversation: draft.conversation,
197
- text: [submission.text, draft.text].filter((one) => one !== '').join('\n'),
198
- files: [...submission.files, ...draft.files],
242
+ ...prepended(draft, submission),
199
243
  revision: chatDraftRevision(),
200
244
  }, others);
201
245
  }
@@ -209,6 +253,7 @@ export function createConversationStore(deps) {
209
253
  const catching = new Map();
210
254
  const gaps = new Map();
211
255
  const lives = new Map();
256
+ const unsent = new Map();
212
257
  const views = new Map();
213
258
  const listing = new Map();
214
259
  const creations = new Set();
@@ -223,7 +268,7 @@ export function createConversationStore(deps) {
223
268
  function liveOf(conversation) {
224
269
  return lives.get(conversation) ?? QUIET;
225
270
  }
226
- function viewOf(conversation, held) {
271
+ function viewOf(conversation, held, frame) {
227
272
  const draft = drafts.get(conversation);
228
273
  const facts = liveOf(conversation);
229
274
  return {
@@ -234,14 +279,18 @@ export function createConversationStore(deps) {
234
279
  text,
235
280
  message,
236
281
  })),
282
+ ...frame,
237
283
  turn: structuredClone(facts.turn),
238
284
  questions: structuredClone(facts.questions),
239
285
  process: facts.process === null ? null : { ...facts.process },
240
286
  commands: structuredClone(facts.commands),
241
287
  options: structuredClone(facts.options),
288
+ starting: facts.starting,
289
+ link: facts.link === null ? null : { ...facts.link },
290
+ deliveryUnknown: [...facts.deliveryUnknown],
242
291
  chatDraft: draft === undefined || unwritten(draft)
243
292
  ? null
244
- : { text: draft.text, files: [...draft.files], revision: draft.revision },
293
+ : { text: draft.text, revision: draft.revision },
245
294
  };
246
295
  }
247
296
  function emit(conversation) {
@@ -253,21 +302,36 @@ export function createConversationStore(deps) {
253
302
  const ready = [...view.watchers].filter((watcher) => watcher.ready);
254
303
  if (ready.length === 0)
255
304
  continue;
256
- const value = viewOf(conversation, view.window);
305
+ const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
257
306
  for (const watcher of ready)
258
307
  watcher.notify(value);
259
308
  }
260
309
  }
261
- function slide(conversation, arriving) {
310
+ function sharedFrame(view, window) {
311
+ return frameOf(view.asked, window, view.more, view.total);
312
+ }
313
+ async function slide(local, conversation, arriving) {
314
+ let count;
262
315
  for (const view of views.values()) {
263
316
  if (view.conversation !== conversation || view.window === null)
264
317
  continue;
265
- view.window = arriving === null ? [] : windowOf(view.window, arriving, view.limit);
318
+ if (arriving === null) {
319
+ view.window = [];
320
+ view.more = false;
321
+ if (view.total !== null)
322
+ view.total = 0;
323
+ continue;
324
+ }
325
+ const joining = arriving.filter((message) => precedes(message, view.asked.before));
326
+ const next = windowOf(view.window, joining, view.asked.limit);
327
+ view.window = next.messages;
328
+ view.more ||= next.dropped;
329
+ if (view.total !== null && arriving.length > 0) {
330
+ count ??= await local.messageCount(conversation);
331
+ view.total = count;
332
+ }
266
333
  }
267
334
  }
268
- function changesOf(conversation, arriving) {
269
- return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
270
- }
271
335
  function stateOf(conversation) {
272
336
  const facts = liveOf(conversation);
273
337
  if (facts.questions.length > 0)
@@ -282,6 +346,7 @@ export function createConversationStore(deps) {
282
346
  created: entry.created,
283
347
  finished: entry.finished,
284
348
  outcome: entry.outcome,
349
+ waiting: entry.waiting,
285
350
  state: stateOf(entry.conversation),
286
351
  started: lives.get(entry.conversation)?.turn?.started ?? null,
287
352
  }));
@@ -324,23 +389,30 @@ export function createConversationStore(deps) {
324
389
  const declaration = deps.agents();
325
390
  if (declaration === undefined)
326
391
  return;
327
- const held = await deps.documents(declaration);
328
- if ('pending' in held)
329
- return;
330
- const { documents } = held;
331
392
  const names = new Map();
332
393
  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);
394
+ let covered = null;
395
+ for (let after;;) {
396
+ const read = await deps.documents(declaration, after);
397
+ if ('pending' in read)
398
+ return;
399
+ const coverage = JSON.stringify(read.coverage);
400
+ if (covered !== null && coverage !== covered)
401
+ return readAgents();
402
+ covered = coverage;
403
+ for (const document of read.documents) {
404
+ const identity = document.fields?.[declaration.identity];
405
+ const name = document.fields?.[declaration.name];
406
+ if (typeof identity !== 'string' || typeof name !== 'string')
407
+ continue;
408
+ names.set(identity, name);
409
+ const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
410
+ if (ended !== undefined && ended !== null)
411
+ retired.add(identity);
412
+ }
413
+ if (read.next === undefined)
414
+ break;
415
+ after = read.next;
344
416
  }
345
417
  const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
346
418
  if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
@@ -364,9 +436,34 @@ export function createConversationStore(deps) {
364
436
  return agentsRead;
365
437
  }
366
438
  function live(conversation, change) {
439
+ const owed = unsent.get(conversation);
440
+ for (const fact of FACTS.filter((one) => one in change))
441
+ owed?.delete(fact);
367
442
  setLive(conversation, { ...liveOf(conversation), ...change });
368
443
  emit(conversation);
369
444
  }
445
+ function openingEnded() {
446
+ const owed = [...unsent];
447
+ unsent.clear();
448
+ for (const [conversation, facts] of owed) {
449
+ const held = lives.get(conversation);
450
+ if (held === undefined || facts.size === 0)
451
+ continue;
452
+ setLive(conversation, {
453
+ turn: held.turn,
454
+ questions: facts.has('questions') ? QUIET.questions : held.questions,
455
+ process: facts.has('process') ? QUIET.process : held.process,
456
+ commands: facts.has('commands') ? QUIET.commands : held.commands,
457
+ options: facts.has('options') ? QUIET.options : held.options,
458
+ starting: facts.has('starting') ? QUIET.starting : held.starting,
459
+ link: facts.has('link') ? QUIET.link : held.link,
460
+ deliveryUnknown: facts.has('deliveryUnknown')
461
+ ? QUIET.deliveryUnknown
462
+ : held.deliveryUnknown,
463
+ });
464
+ emit(conversation);
465
+ }
466
+ }
370
467
  function relayed(conversation, turn, operation) {
371
468
  const held = liveOf(conversation).turn;
372
469
  const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
@@ -393,12 +490,6 @@ export function createConversationStore(deps) {
393
490
  }
394
491
  async function remove(leaving) {
395
492
  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
493
  await local.dropConversations(leaving);
403
494
  const changed = [];
404
495
  for (const conversation of leaving) {
@@ -410,11 +501,12 @@ export function createConversationStore(deps) {
410
501
  partials.delete(conversation);
411
502
  drafts.delete(conversation);
412
503
  behind.delete(conversation);
504
+ unsent.delete(conversation);
413
505
  setLive(conversation, null);
414
- slide(conversation, null);
506
+ await slide(local, conversation, null);
415
507
  }
416
508
  if (changed.length > 0)
417
- deps.publish(conversationChangeSet(removed, changed));
509
+ deps.publish(conversationChangeSet(changed));
418
510
  });
419
511
  }
420
512
  function gap(conversation) {
@@ -439,17 +531,18 @@ export function createConversationStore(deps) {
439
531
  if (advances)
440
532
  reads.set(conversation, message.position);
441
533
  sealTurns(conversation, new Set([message.turn]));
442
- slide(conversation, [message]);
443
- deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
534
+ await slide(local, conversation, [message]);
535
+ deps.publish(conversationChangeSet([conversation]));
444
536
  });
445
537
  }
446
538
  async function readHistories(conversations, ended) {
447
539
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
448
- let asking = conversations.map((conversation) => ({
540
+ let waiting = conversations.map((conversation) => ({
449
541
  conversation,
450
542
  after: reads.get(conversation) ?? undefined,
451
543
  }));
452
- while (asking.length > 0) {
544
+ while (waiting.length > 0) {
545
+ const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
453
546
  const result = await deps.call('conversation.history', {
454
547
  conversations: asking.map(({ conversation, after }) => ({
455
548
  conversation,
@@ -473,7 +566,7 @@ export function createConversationStore(deps) {
473
566
  if (!following.some((next) => next.conversation === conversation))
474
567
  ended(conversation);
475
568
  }
476
- asking = following;
569
+ waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
477
570
  }
478
571
  return null;
479
572
  }
@@ -514,9 +607,9 @@ export function createConversationStore(deps) {
514
607
  sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
515
608
  if (whole && (gaps.get(conversation) ?? 0) === generation)
516
609
  behind.delete(conversation);
517
- slide(conversation, arriving);
610
+ await slide(local, conversation, arriving);
518
611
  if (arriving.length > 0 || sealing || whole) {
519
- deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
612
+ deps.publish(conversationChangeSet([conversation]));
520
613
  }
521
614
  });
522
615
  }
@@ -525,11 +618,8 @@ export function createConversationStore(deps) {
525
618
  if (event.kind === 'gap') {
526
619
  if (replicated.has(conversation))
527
620
  gap(conversation);
528
- const held = lives.get(conversation);
529
- if (held === undefined)
530
- return;
531
- setLive(conversation, { ...QUIET, turn: held.turn });
532
- emit(conversation);
621
+ if (lives.has(conversation))
622
+ unsent.set(conversation, new Set(FACTS));
533
623
  return;
534
624
  }
535
625
  if (event.kind === 'turn') {
@@ -552,11 +642,30 @@ export function createConversationStore(deps) {
552
642
  live(conversation, { options: structuredClone(event.options) });
553
643
  return;
554
644
  }
645
+ if (event.kind === 'starting') {
646
+ live(conversation, { starting: event.step });
647
+ return;
648
+ }
649
+ if (event.kind === 'link') {
650
+ live(conversation, { link: { connected: event.connected } });
651
+ return;
652
+ }
653
+ if (event.kind === 'deliveries') {
654
+ live(conversation, { deliveryUnknown: [...event.unknown] });
655
+ return;
656
+ }
555
657
  const arriving = acceptedMessage(event.message);
556
658
  if (arriving === null)
557
659
  return;
558
660
  if (replicated.has(conversation)) {
559
- void keep(conversation, arriving).catch(() => { });
661
+ if (deps.full()) {
662
+ gap(conversation);
663
+ return;
664
+ }
665
+ void keep(conversation, arriving).catch((error) => {
666
+ if (refusedForSpace(error))
667
+ gap(conversation);
668
+ });
560
669
  return;
561
670
  }
562
671
  if (sealTurns(conversation, new Set([arriving.turn])))
@@ -591,7 +700,6 @@ export function createConversationStore(deps) {
591
700
  const blank = {
592
701
  conversation: from,
593
702
  text: '',
594
- files: [],
595
703
  revision: chatDraftRevision(),
596
704
  };
597
705
  const { moves: _answered, ...unsent } = held ?? blank;
@@ -601,7 +709,7 @@ export function createConversationStore(deps) {
601
709
  drafts.set(from, left);
602
710
  for (const one of taken)
603
711
  drafts.set(to, one);
604
- deps.publish(conversationChangeSet([], [from, to]));
712
+ deps.publish(conversationChangeSet([from, to]));
605
713
  });
606
714
  }
607
715
  function withDraft(conversation, next) {
@@ -610,7 +718,7 @@ export function createConversationStore(deps) {
610
718
  if (following === undefined)
611
719
  return;
612
720
  await kept(local, conversation, following);
613
- deps.publish(conversationChangeSet([], [conversation]));
721
+ deps.publish(conversationChangeSet([conversation]));
614
722
  });
615
723
  }
616
724
  async function submitted(conversation, operation, draft) {
@@ -622,17 +730,12 @@ export function createConversationStore(deps) {
622
730
  if (submission !== undefined)
623
731
  return undefined;
624
732
  const said = draft ?? current;
625
- submission = {
626
- operation,
627
- text: said?.text ?? '',
628
- files: [...(said?.files ?? [])],
629
- turn: null,
630
- };
733
+ submission = { operation, text: said?.text ?? '', turn: null };
631
734
  return {
632
735
  conversation,
633
736
  ...(draft === undefined || current === undefined
634
- ? { text: '', files: [], revision: chatDraftRevision() }
635
- : { text: current.text, files: current.files, revision: current.revision }),
737
+ ? { text: '', revision: chatDraftRevision() }
738
+ : { text: current.text, revision: current.revision }),
636
739
  submitted: [...(current?.submitted ?? []), submission],
637
740
  };
638
741
  });
@@ -647,15 +750,14 @@ export function createConversationStore(deps) {
647
750
  });
648
751
  }
649
752
  async function dispatched(conversation, submission) {
650
- const dispatch = {};
753
+ const dispatch = { answered: false };
651
754
  sending.set(submission.operation, dispatch);
652
755
  try {
653
- const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
654
756
  const result = await invoke('conversation.send', conversation, {
655
757
  text: submission.text,
656
- files,
657
758
  operation_id: submission.operation,
658
759
  });
760
+ dispatch.answered = result.kind !== 'transport-uncertainty';
659
761
  if (result.kind === 'revoked-authorization')
660
762
  return result;
661
763
  if (result.kind !== 'action' && result.retryable) {
@@ -691,8 +793,12 @@ export function createConversationStore(deps) {
691
793
  return entries();
692
794
  },
693
795
  async read(conversation, options = {}) {
694
- const limit = limitOf(options);
695
- return deps.durably(async (local) => structuredClone(viewOf(conversation, await local.messageWindow(conversation, limit))));
796
+ const asked = askedOf(options);
797
+ return deps.durably(async (local) => {
798
+ const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
799
+ const total = asked.total ? await local.messageCount(conversation) : null;
800
+ return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
801
+ });
696
802
  },
697
803
  async create(input) {
698
804
  const result = await deps.call('conversation.create', input);
@@ -707,8 +813,17 @@ export function createConversationStore(deps) {
707
813
  return result;
708
814
  },
709
815
  async send(conversation, operation = changeBatchIdentity(), draft) {
816
+ if (deps.full())
817
+ return DURABLE_WRITE;
710
818
  if (deps.online()) {
711
- const { submission, earlier } = await submitted(conversation, operation, draft);
819
+ const held = await submitted(conversation, operation, draft).catch((error) => {
820
+ if (refusedForSpace(error))
821
+ return null;
822
+ throw error;
823
+ });
824
+ if (held === null)
825
+ return DURABLE_WRITE;
826
+ const { submission, earlier } = held;
712
827
  if (!earlier || !deps.expired(operation))
713
828
  return dispatched(conversation, submission);
714
829
  await forgotten(conversation, submission);
@@ -717,10 +832,19 @@ export function createConversationStore(deps) {
717
832
  if (draft !== undefined) {
718
833
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
719
834
  ? undefined
720
- : returned(current ?? { conversation, text: '', files: [], revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
835
+ : returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
721
836
  }
722
837
  return OFFLINE;
723
838
  },
839
+ async attach(room, file) {
840
+ if (!deps.online())
841
+ return OFFLINE;
842
+ return deps.call('conversation.attach', {
843
+ room,
844
+ ...(await declaredFile(file)),
845
+ blob: file.blob,
846
+ });
847
+ },
724
848
  answer(conversation, answer) {
725
849
  return invoke('conversation.answer', conversation, { answer });
726
850
  },
@@ -751,21 +875,22 @@ export function createConversationStore(deps) {
751
875
  return withDraft(conversation, (current) => ({
752
876
  conversation,
753
877
  text: draft.text,
754
- files: [...draft.files],
755
878
  revision,
756
879
  ...(current === undefined ? {} : pending(current)),
757
880
  }));
758
881
  },
882
+ prepend(conversation, draft) {
883
+ return withDraft(conversation, (current) => ({
884
+ conversation,
885
+ ...(current === undefined ? { text: draft.text } : prepended(current, draft)),
886
+ revision: chatDraftRevision(),
887
+ ...(current === undefined ? {} : pending(current)),
888
+ }));
889
+ },
759
890
  discard(conversation) {
760
891
  return withDraft(conversation, (current) => current === undefined
761
892
  ? undefined
762
- : {
763
- conversation,
764
- text: '',
765
- files: [],
766
- revision: chatDraftRevision(),
767
- ...pending(current),
768
- });
893
+ : { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
769
894
  },
770
895
  };
771
896
  return {
@@ -805,6 +930,11 @@ export function createConversationStore(deps) {
805
930
  await remove(leaving);
806
931
  },
807
932
  async catchUp(progress) {
933
+ if (deps.full()) {
934
+ if (behind.size > 0)
935
+ progress.complete = false;
936
+ return;
937
+ }
808
938
  const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
809
939
  const read = async (conversations) => {
810
940
  if (conversations.length === 0 || deps.closed())
@@ -858,32 +988,42 @@ export function createConversationStore(deps) {
858
988
  gap(conversation);
859
989
  },
860
990
  reopened() {
861
- sending.clear();
991
+ for (const [operation, dispatch] of sending)
992
+ if (!dispatch.answered)
993
+ sending.delete(operation);
862
994
  },
863
995
  lost() {
864
996
  catching.clear();
865
997
  },
866
998
  watch(conversation, options, deliver) {
867
- const limit = limitOf(options);
868
- const key = JSON.stringify([conversation, limit ?? null]);
999
+ const asked = askedOf(options);
1000
+ const key = JSON.stringify([conversation, asked.limit, asked.after ?? null, asked.total]);
869
1001
  const view = views.get(key) ?? {
870
1002
  conversation,
871
- limit,
1003
+ asked,
872
1004
  watchers: new Set(),
873
1005
  window: null,
1006
+ more: false,
1007
+ total: null,
874
1008
  };
875
1009
  views.set(key, view);
876
1010
  const watcher = { notify: deliver, ready: false };
877
1011
  view.watchers.add(watcher);
878
1012
  void deps
879
1013
  .durably(async (local) => {
880
- view.window ??= await local.messageWindow(conversation, limit);
1014
+ if (view.window !== null)
1015
+ return;
1016
+ const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
1017
+ view.window = messages;
1018
+ view.more = more;
1019
+ view.total = asked.total ? await local.messageCount(conversation) : null;
881
1020
  })
882
1021
  .then(() => {
883
1022
  if (deps.closed() || !view.watchers.has(watcher))
884
1023
  return;
885
1024
  watcher.ready = true;
886
- watcher.notify(viewOf(conversation, view.window ?? []));
1025
+ const window = view.window ?? [];
1026
+ watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
887
1027
  })
888
1028
  .catch(() => { });
889
1029
  return () => {
@@ -935,6 +1075,7 @@ export function createConversationStore(deps) {
935
1075
  creations.delete(entry.conversation);
936
1076
  }
937
1077
  listKnown = true;
1078
+ openingEnded();
938
1079
  }
939
1080
  else if (event.kind === 'listed') {
940
1081
  listing.set(event.entry.conversation, { ...event.entry });
@@ -961,6 +1102,7 @@ export function createConversationStore(deps) {
961
1102
  close() {
962
1103
  views.clear();
963
1104
  lives.clear();
1105
+ unsent.clear();
964
1106
  listWatchers.clear();
965
1107
  agentWatchers.clear();
966
1108
  },
@@ -0,0 +1,7 @@
1
+ export type Digest = {
2
+ update(bytes: Uint8Array): void;
3
+ hex(): string;
4
+ };
5
+ export declare function digest(): Digest;
6
+ export declare function sha256(blob: Blob): Promise<string>;
7
+ //# sourceMappingURL=digest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../src/digest.ts"],"names":[],"mappings":"AA0EA,MAAM,MAAM,MAAM,GAAG;IACnB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,GAAG,IAAI,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,MAAM,IAAI,MAAM,CAoC/B;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAMxD"}