@agentshouse/mdruntime 0.1.0-c8f3163f32f93 → 0.1.0-c93103f5357ff

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 (119) hide show
  1. package/AGENTS.md +262 -262
  2. package/dist/acquisition.d.ts +14 -0
  3. package/dist/acquisition.d.ts.map +1 -0
  4. package/dist/acquisition.js +24 -0
  5. package/dist/adapter.d.ts +13 -7
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/bootstrap.d.ts.map +1 -1
  8. package/dist/bootstrap.js +4 -0
  9. package/dist/change-set.d.ts +2 -0
  10. package/dist/change-set.d.ts.map +1 -1
  11. package/dist/change-set.js +36 -10
  12. package/dist/chat/kit.d.ts +2 -1
  13. package/dist/chat/kit.d.ts.map +1 -1
  14. package/dist/chat/kit.js +3 -3
  15. package/dist/chat/list.d.ts +2 -1
  16. package/dist/chat/list.d.ts.map +1 -1
  17. package/dist/chat/list.js +4 -3
  18. package/dist/chat/message.d.ts.map +1 -1
  19. package/dist/chat/message.js +2 -2
  20. package/dist/chat/messages.d.ts +1 -0
  21. package/dist/chat/messages.d.ts.map +1 -1
  22. package/dist/chat/messages.js +4 -0
  23. package/dist/chat/provider.d.ts +1 -1
  24. package/dist/chat/provider.d.ts.map +1 -1
  25. package/dist/chat/provider.js +26 -17
  26. package/dist/chat/ready.d.ts +1 -0
  27. package/dist/chat/ready.d.ts.map +1 -1
  28. package/dist/chat/ready.js +2 -2
  29. package/dist/client.d.ts +10 -6
  30. package/dist/client.d.ts.map +1 -1
  31. package/dist/client.js +11 -2
  32. package/dist/collection.d.ts +17 -3
  33. package/dist/collection.d.ts.map +1 -1
  34. package/dist/collection.js +230 -19
  35. package/dist/connection.d.ts +23 -7
  36. package/dist/connection.d.ts.map +1 -1
  37. package/dist/connection.js +1031 -183
  38. package/dist/conversation.d.ts +10 -5
  39. package/dist/conversation.d.ts.map +1 -1
  40. package/dist/conversation.js +201 -95
  41. package/dist/durability.d.ts +7 -1
  42. package/dist/durability.d.ts.map +1 -1
  43. package/dist/entry.d.ts +2 -2
  44. package/dist/entry.d.ts.map +1 -1
  45. package/dist/entry.js +3 -3
  46. package/dist/host.d.ts +16 -2
  47. package/dist/host.d.ts.map +1 -1
  48. package/dist/host.js +82 -8
  49. package/dist/identity.d.ts +2 -0
  50. package/dist/identity.d.ts.map +1 -1
  51. package/dist/identity.js +6 -0
  52. package/dist/index.d.ts +8 -5
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +2 -0
  55. package/dist/memory.d.ts +3 -1
  56. package/dist/memory.d.ts.map +1 -1
  57. package/dist/memory.js +14 -2
  58. package/dist/model.d.ts +4 -2
  59. package/dist/model.d.ts.map +1 -1
  60. package/dist/model.js +10 -1
  61. package/dist/observation.d.ts +5 -2
  62. package/dist/observation.d.ts.map +1 -1
  63. package/dist/observation.js +22 -11
  64. package/dist/overlay.d.ts +2 -0
  65. package/dist/overlay.d.ts.map +1 -1
  66. package/dist/overlay.js +50 -55
  67. package/dist/query.d.ts +35 -3
  68. package/dist/query.d.ts.map +1 -1
  69. package/dist/query.js +8 -2
  70. package/dist/reach.d.ts +6 -3
  71. package/dist/reach.d.ts.map +1 -1
  72. package/dist/reach.js +16 -4
  73. package/dist/react.d.ts +7 -4
  74. package/dist/react.d.ts.map +1 -1
  75. package/dist/react.js +6 -4
  76. package/dist/read-side.d.ts +13 -6
  77. package/dist/read-side.d.ts.map +1 -1
  78. package/dist/read-side.js +285 -83
  79. package/dist/readiness.d.ts +53 -0
  80. package/dist/readiness.d.ts.map +1 -0
  81. package/dist/readiness.js +33 -0
  82. package/dist/recovery.d.ts +25 -0
  83. package/dist/recovery.d.ts.map +1 -0
  84. package/dist/recovery.js +197 -0
  85. package/dist/refresh.d.ts.map +1 -1
  86. package/dist/refresh.js +17 -10
  87. package/dist/replica.d.ts +1 -1
  88. package/dist/replica.d.ts.map +1 -1
  89. package/dist/replica.js +57 -20
  90. package/dist/room-draft.d.ts +13 -1
  91. package/dist/room-draft.d.ts.map +1 -1
  92. package/dist/room-draft.js +2 -2
  93. package/dist/rows.d.ts +5 -13
  94. package/dist/rows.d.ts.map +1 -1
  95. package/dist/rows.js +5 -13
  96. package/dist/save.d.ts +5 -0
  97. package/dist/save.d.ts.map +1 -1
  98. package/dist/save.js +36 -13
  99. package/dist/search.d.ts +36 -6
  100. package/dist/search.d.ts.map +1 -1
  101. package/dist/search.js +132 -46
  102. package/dist/seed.d.ts +16 -0
  103. package/dist/seed.d.ts.map +1 -0
  104. package/dist/seed.js +61 -0
  105. package/dist/shared.d.ts +11 -0
  106. package/dist/shared.d.ts.map +1 -0
  107. package/dist/shared.js +368 -0
  108. package/dist/status.d.ts +4 -0
  109. package/dist/status.d.ts.map +1 -1
  110. package/dist/store.d.ts +21 -5
  111. package/dist/store.d.ts.map +1 -1
  112. package/dist/store.js +157 -53
  113. package/dist/turn.d.ts +7 -0
  114. package/dist/turn.d.ts.map +1 -0
  115. package/dist/turn.js +45 -0
  116. package/dist/wire.d.ts +136 -32
  117. package/dist/wire.d.ts.map +1 -1
  118. package/dist/wire.js +48 -31
  119. package/package.json +2 -2
@@ -22,6 +22,17 @@ export function unheldView(conversation) {
22
22
  chatDraft: null,
23
23
  };
24
24
  }
25
+ export const SESSION_STARTED = 'Session started';
26
+ const NEW_CHAT = 'new:';
27
+ export function newChat(agent) {
28
+ return `${NEW_CHAT}${agent}`;
29
+ }
30
+ function creationOf(input) {
31
+ if (typeof input !== 'object' || input === null)
32
+ return null;
33
+ const { agent_id: agent, operation_id: operation } = input;
34
+ return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
35
+ }
25
36
  function startedTurn(turn, operation) {
26
37
  if (operation.op === 'begin')
27
38
  return { turn, started: operation.at, parts: [], context: null };
@@ -78,11 +89,15 @@ function ordered(left, right) {
78
89
  return left.identity < right.identity ? -1 : 1;
79
90
  return 0;
80
91
  }
81
- function merged(held, arriving) {
92
+ function windowOf(held, arriving, limit) {
82
93
  const byIdentity = new Map(held.map((message) => [message.identity, message]));
83
94
  for (const message of arriving)
84
95
  byIdentity.set(message.identity, message);
85
- return [...byIdentity.values()].sort(ordered);
96
+ const next = [...byIdentity.values()].sort(ordered);
97
+ return limit === undefined ? next : next.slice(Math.max(0, next.length - limit));
98
+ }
99
+ function newestOf(arriving, from) {
100
+ return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
86
101
  }
87
102
  function acceptedMessage(value) {
88
103
  if (typeof value !== 'object' || value === null)
@@ -141,14 +156,14 @@ function historyPages(result) {
141
156
  }
142
157
  return pages;
143
158
  }
144
- function turnOf(result) {
159
+ function answered(result, field) {
145
160
  if (result.kind !== 'action')
146
161
  return null;
147
162
  const answer = result.result;
148
163
  if (typeof answer !== 'object' || answer === null)
149
164
  return null;
150
- const record = answer;
151
- return typeof record.turn === 'string' ? record.turn : null;
165
+ const value = answer[field];
166
+ return typeof value === 'string' ? value : null;
152
167
  }
153
168
  function limitOf(options) {
154
169
  const { limit } = options;
@@ -159,11 +174,18 @@ function limitOf(options) {
159
174
  }
160
175
  return limit;
161
176
  }
177
+ const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
162
178
  function unwritten(draft) {
163
179
  return draft.text === '' && draft.files.length === 0;
164
180
  }
165
181
  function pending(draft) {
166
- return draft.submitted === undefined ? {} : { submitted: draft.submitted };
182
+ return {
183
+ ...(draft.submitted === undefined ? {} : { submitted: draft.submitted }),
184
+ ...(draft.moves === undefined ? {} : { moves: draft.moves }),
185
+ };
186
+ }
187
+ function empty(draft) {
188
+ return unwritten(draft) && draft.submitted === undefined && draft.moves === undefined;
167
189
  }
168
190
  function withSubmitted(draft, submitted) {
169
191
  const { submitted: _held, ...unsent } = draft;
@@ -178,15 +200,17 @@ function returned(draft, submission, others) {
178
200
  }, others);
179
201
  }
180
202
  export function createConversationStore(deps) {
181
- const messages = new Map();
203
+ const stored = new Set();
182
204
  const reads = new Map();
205
+ const partials = new Set();
183
206
  const drafts = new Map();
184
- const sending = new Set();
207
+ const sending = new Map();
185
208
  const behind = new Set();
186
209
  const gaps = new Map();
187
210
  const lives = new Map();
188
211
  const views = new Map();
189
212
  const listing = new Map();
213
+ const creations = new Set();
190
214
  const listWatchers = new Set();
191
215
  let listKnown = false;
192
216
  const agentWatchers = new Set();
@@ -198,22 +222,17 @@ export function createConversationStore(deps) {
198
222
  function liveOf(conversation) {
199
223
  return lives.get(conversation) ?? QUIET;
200
224
  }
201
- function shownMessages(conversation, limit) {
202
- const held = messages.get(conversation) ?? [];
203
- const shown = limit === undefined ? held : held.slice(Math.max(0, held.length - limit));
204
- return shown.map(({ identity, turn, text, message }) => ({
205
- identity,
206
- turn,
207
- text,
208
- message: structuredClone(message),
209
- }));
210
- }
211
- function viewOf(conversation, limit, shown = shownMessages(conversation, limit)) {
225
+ function viewOf(conversation, held) {
212
226
  const draft = drafts.get(conversation);
213
227
  const facts = liveOf(conversation);
214
228
  return {
215
229
  conversation,
216
- messages: shown,
230
+ messages: held.map(({ identity, turn, text, message }) => ({
231
+ identity,
232
+ turn,
233
+ text,
234
+ message,
235
+ })),
217
236
  turn: structuredClone(facts.turn),
218
237
  questions: structuredClone(facts.questions),
219
238
  process: facts.process === null ? null : { ...facts.process },
@@ -224,23 +243,30 @@ export function createConversationStore(deps) {
224
243
  : { text: draft.text, files: [...draft.files], revision: draft.revision },
225
244
  };
226
245
  }
227
- function emit(conversation, messagesChanged = true) {
246
+ function emit(conversation) {
228
247
  if (deps.closed())
229
248
  return;
230
249
  for (const view of views.values()) {
231
- if (view.conversation !== conversation)
250
+ if (view.conversation !== conversation || view.window === null)
232
251
  continue;
233
- if (messagesChanged)
234
- view.messages = null;
235
252
  const ready = [...view.watchers].filter((watcher) => watcher.ready);
236
253
  if (ready.length === 0)
237
254
  continue;
238
- view.messages ??= shownMessages(conversation, view.limit);
239
- const value = viewOf(conversation, view.limit, view.messages);
255
+ const value = viewOf(conversation, view.window);
240
256
  for (const watcher of ready)
241
257
  watcher.notify(value);
242
258
  }
243
259
  }
260
+ function slide(conversation, arriving) {
261
+ for (const view of views.values()) {
262
+ if (view.conversation !== conversation || view.window === null)
263
+ continue;
264
+ view.window = arriving === null ? [] : windowOf(view.window, arriving, view.limit);
265
+ }
266
+ }
267
+ function changesOf(conversation, arriving) {
268
+ return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
269
+ }
244
270
  function stateOf(conversation) {
245
271
  const facts = liveOf(conversation);
246
272
  if (facts.questions.length > 0)
@@ -297,7 +323,10 @@ export function createConversationStore(deps) {
297
323
  const declaration = deps.agents();
298
324
  if (declaration === undefined)
299
325
  return;
300
- const { documents } = await deps.documents(declaration.locator);
326
+ const held = await deps.documents(declaration);
327
+ if ('pending' in held)
328
+ return;
329
+ const { documents } = held;
301
330
  const names = new Map();
302
331
  const retired = new Set();
303
332
  for (const document of documents) {
@@ -320,6 +349,12 @@ export function createConversationStore(deps) {
320
349
  if (named)
321
350
  emitList();
322
351
  emitAgents();
352
+ const ended = [...drafts.keys()].filter(retiredChat);
353
+ if (ended.length > 0)
354
+ await remove(ended);
355
+ }
356
+ function retiredChat(conversation) {
357
+ return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
323
358
  }
324
359
  function agentsReady() {
325
360
  agentsRead ??= readAgents().catch(() => {
@@ -329,7 +364,7 @@ export function createConversationStore(deps) {
329
364
  }
330
365
  function live(conversation, change) {
331
366
  setLive(conversation, { ...liveOf(conversation), ...change });
332
- emit(conversation, false);
367
+ emit(conversation);
333
368
  }
334
369
  function relayed(conversation, turn, operation) {
335
370
  const held = liveOf(conversation).turn;
@@ -357,21 +392,25 @@ export function createConversationStore(deps) {
357
392
  }
358
393
  async function remove(leaving) {
359
394
  await deps.durably(async (local) => {
360
- await local.dropConversations(leaving);
361
395
  const removed = [];
362
- const changed = [];
363
396
  for (const conversation of leaving) {
364
- for (const message of messages.get(conversation) ?? []) {
365
- removed.push({ conversation, identity: message.identity });
397
+ for (const identity of await local.messageIdentities(conversation)) {
398
+ removed.push({ conversation, identity, text: null });
366
399
  }
367
- if (messages.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
400
+ }
401
+ await local.dropConversations(leaving);
402
+ const changed = [];
403
+ for (const conversation of leaving) {
404
+ if (stored.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
368
405
  changed.push(conversation);
369
406
  }
370
- messages.delete(conversation);
407
+ stored.delete(conversation);
371
408
  reads.delete(conversation);
409
+ partials.delete(conversation);
372
410
  drafts.delete(conversation);
373
411
  behind.delete(conversation);
374
412
  setLive(conversation, null);
413
+ slide(conversation, null);
375
414
  }
376
415
  if (changed.length > 0)
377
416
  deps.publish(conversationChangeSet(removed, changed));
@@ -393,23 +432,21 @@ export function createConversationStore(deps) {
393
432
  await local.keepMessages({
394
433
  conversation,
395
434
  messages: [message],
396
- ...(advances ? { read: message.position } : {}),
435
+ ...(advances ? { read: { at: message.position, partial: false } } : {}),
397
436
  });
398
- messages.set(conversation, merged(messages.get(conversation) ?? [], [message]));
437
+ stored.add(conversation);
399
438
  if (advances)
400
439
  reads.set(conversation, message.position);
401
440
  sealTurns(conversation, new Set([message.turn]));
402
- deps.publish(conversationChangeSet([{ conversation, identity: message.identity }], [conversation]));
441
+ slide(conversation, [message]);
442
+ deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
403
443
  });
404
444
  }
405
445
  async function readHistories(conversations) {
406
446
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
407
- const starts = new Map(conversations.map((one) => [one, reads.get(one)]));
408
- const arriving = new Map();
409
- const complete = new Set();
410
447
  let asking = conversations.map((conversation) => ({
411
448
  conversation,
412
- after: starts.get(conversation) ?? undefined,
449
+ after: reads.get(conversation) ?? undefined,
413
450
  }));
414
451
  while (asking.length > 0) {
415
452
  const result = await deps.call('conversation.history', {
@@ -421,43 +458,41 @@ export function createConversationStore(deps) {
421
458
  const pages = historyPages(result);
422
459
  if (pages === null)
423
460
  return result;
424
- const asked = new Set(asking.map(({ conversation }) => conversation));
461
+ const asked = new Map(asking.map(({ conversation, after }) => [conversation, after]));
425
462
  const following = [];
426
463
  for (const page of pages) {
427
464
  if (!asked.has(page.conversation))
428
465
  continue;
429
- arriving.set(page.conversation, [
430
- ...(arriving.get(page.conversation) ?? []),
431
- ...page.messages,
432
- ]);
433
- if (page.next === undefined)
434
- complete.add(page.conversation);
435
- else
466
+ await keepPage(page.conversation, page.messages, asked.get(page.conversation) ?? null, page.next, generations.get(page.conversation) ?? 0);
467
+ if (page.next !== undefined) {
436
468
  following.push({ conversation: page.conversation, after: page.next });
469
+ }
437
470
  }
438
471
  asking = following;
439
472
  }
440
- for (const [conversation, held] of arriving) {
441
- if (!complete.has(conversation))
442
- continue;
443
- await keepHistory(conversation, held, starts.get(conversation), generations.get(conversation) ?? 0);
444
- }
445
473
  return null;
446
474
  }
447
- function keepHistory(conversation, arriving, from, generation) {
475
+ function keepPage(conversation, arriving, from, next, generation) {
448
476
  return deps.durably(async (local) => {
449
477
  if (!replicated.has(conversation))
450
478
  return;
451
- const newest = arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from ?? null);
479
+ const whole = next === undefined;
480
+ const cursor = next ?? newestOf(arriving, from);
481
+ const partial = !whole && (partials.has(conversation) || !reads.has(conversation));
452
482
  const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
453
- await local.keepMessages({ conversation, messages: arriving, read: newest });
454
- messages.set(conversation, merged(messages.get(conversation) ?? [], arriving));
455
- reads.set(conversation, newest);
483
+ await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
484
+ stored.add(conversation);
485
+ reads.set(conversation, cursor);
486
+ if (partial)
487
+ partials.add(conversation);
488
+ else
489
+ partials.delete(conversation);
456
490
  sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
457
- if ((gaps.get(conversation) ?? 0) === generation)
491
+ if (whole && (gaps.get(conversation) ?? 0) === generation)
458
492
  behind.delete(conversation);
459
- if (arriving.length > 0 || sealing) {
460
- deps.publish(conversationChangeSet(arriving.map((message) => ({ conversation, identity: message.identity })), [conversation]));
493
+ slide(conversation, arriving);
494
+ if (arriving.length > 0 || sealing || whole) {
495
+ deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
461
496
  }
462
497
  });
463
498
  }
@@ -470,7 +505,7 @@ export function createConversationStore(deps) {
470
505
  if (held === undefined)
471
506
  return;
472
507
  setLive(conversation, { ...QUIET, turn: held.turn });
473
- emit(conversation, false);
508
+ emit(conversation);
474
509
  return;
475
510
  }
476
511
  if (event.kind === 'turn') {
@@ -504,12 +539,12 @@ export function createConversationStore(deps) {
504
539
  emit(conversation);
505
540
  }
506
541
  async function kept(local, conversation, following) {
507
- if (following === null || (unwritten(following) && following.submitted === undefined)) {
542
+ if (following === null || empty(following)) {
508
543
  await local.dropChatDraft(conversation);
509
544
  drafts.delete(conversation);
510
545
  }
511
546
  else {
512
- await local.keepChatDraft(following);
547
+ await local.keepChatDrafts([following]);
513
548
  drafts.set(conversation, following);
514
549
  }
515
550
  }
@@ -522,9 +557,32 @@ export function createConversationStore(deps) {
522
557
  await kept(local, conversation, withSubmitted(held, left));
523
558
  return true;
524
559
  }
560
+ function moved(agent, operation, to) {
561
+ const from = newChat(agent);
562
+ return deps.durably(async (local) => {
563
+ const held = drafts.get(from);
564
+ const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
565
+ if (earlier.includes(operation))
566
+ return;
567
+ const blank = {
568
+ conversation: from,
569
+ text: '',
570
+ files: [],
571
+ revision: chatDraftRevision(),
572
+ };
573
+ const { moves: _answered, ...unsent } = held ?? blank;
574
+ const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
575
+ const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
576
+ await local.keepChatDrafts([left, ...taken]);
577
+ drafts.set(from, left);
578
+ for (const one of taken)
579
+ drafts.set(to, one);
580
+ deps.publish(conversationChangeSet([], [from, to]));
581
+ });
582
+ }
525
583
  function withDraft(conversation, next) {
526
584
  return deps.durably(async (local) => {
527
- const following = next(drafts.get(conversation));
585
+ const following = await next(drafts.get(conversation), local);
528
586
  if (following === undefined)
529
587
  return;
530
588
  await kept(local, conversation, following);
@@ -533,8 +591,10 @@ export function createConversationStore(deps) {
533
591
  }
534
592
  async function submitted(conversation, operation, draft) {
535
593
  let submission;
594
+ let earlier = false;
536
595
  await withDraft(conversation, (current) => {
537
596
  submission = current?.submitted?.find((one) => one.operation === operation);
597
+ earlier = submission !== undefined;
538
598
  if (submission !== undefined)
539
599
  return undefined;
540
600
  const said = draft ?? current;
@@ -552,10 +612,19 @@ export function createConversationStore(deps) {
552
612
  submitted: [...(current?.submitted ?? []), submission],
553
613
  };
554
614
  });
555
- return submission;
615
+ return { submission: submission, earlier };
616
+ }
617
+ function forgotten(conversation, submission) {
618
+ return withDraft(conversation, (current) => {
619
+ const held = current?.submitted ?? [];
620
+ if (current === undefined || !held.includes(submission))
621
+ return undefined;
622
+ return withSubmitted(current, held.filter((one) => one !== submission));
623
+ });
556
624
  }
557
625
  async function dispatched(conversation, submission) {
558
- sending.add(submission.operation);
626
+ const dispatch = {};
627
+ sending.set(submission.operation, dispatch);
559
628
  try {
560
629
  const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
561
630
  const result = await invoke('conversation.send', conversation, {
@@ -565,17 +634,19 @@ export function createConversationStore(deps) {
565
634
  });
566
635
  if (result.kind === 'revoked-authorization')
567
636
  return result;
568
- if (result.kind !== 'action' && result.retryable)
637
+ if (result.kind !== 'action' && result.retryable) {
638
+ deps.unanswered();
569
639
  return result;
570
- const turn = turnOf(result);
571
- await withDraft(conversation, (current) => {
640
+ }
641
+ const turn = answered(result, 'turn');
642
+ await withDraft(conversation, async (current, local) => {
572
643
  const held = current?.submitted?.find((one) => one.operation === submission.operation);
573
644
  if (current === undefined || held === undefined || held.turn !== null)
574
645
  return undefined;
575
646
  const others = current.submitted.filter((one) => one !== held);
576
647
  if (result.kind !== 'action')
577
648
  return returned(current, held, others);
578
- const arrived = (messages.get(conversation) ?? []).some((message) => message.turn === turn);
649
+ const arrived = turn !== null && (await local.turnArrived(conversation, turn));
579
650
  return withSubmitted(current, turn === null || arrived
580
651
  ? others
581
652
  : current.submitted.map((one) => (one === held ? { ...held, turn } : one)));
@@ -583,7 +654,8 @@ export function createConversationStore(deps) {
583
654
  return result;
584
655
  }
585
656
  finally {
586
- sending.delete(submission.operation);
657
+ if (sending.get(submission.operation) === dispatch)
658
+ sending.delete(submission.operation);
587
659
  }
588
660
  }
589
661
  function invoke(name, conversation, extra = {}) {
@@ -596,14 +668,27 @@ export function createConversationStore(deps) {
596
668
  },
597
669
  async read(conversation, options = {}) {
598
670
  const limit = limitOf(options);
599
- return deps.durably(() => Promise.resolve(viewOf(conversation, limit)));
671
+ return deps.durably(async (local) => structuredClone(viewOf(conversation, await local.messageWindow(conversation, limit))));
600
672
  },
601
- create(input) {
602
- return deps.call('conversation.create', input);
673
+ async create(input) {
674
+ const result = await deps.call('conversation.create', input);
675
+ const conversation = answered(result, 'conversation');
676
+ if (conversation === null)
677
+ return result;
678
+ if (!listing.has(conversation))
679
+ creations.add(conversation);
680
+ const creation = creationOf(input);
681
+ if (creation !== null)
682
+ await moved(creation.agent, creation.operation, conversation);
683
+ return result;
603
684
  },
604
685
  async send(conversation, operation = changeBatchIdentity(), draft) {
605
686
  if (deps.online()) {
606
- return dispatched(conversation, await submitted(conversation, operation, draft));
687
+ const { submission, earlier } = await submitted(conversation, operation, draft);
688
+ if (!earlier || !deps.expired(operation))
689
+ return dispatched(conversation, submission);
690
+ await forgotten(conversation, submission);
691
+ return EXPIRED;
607
692
  }
608
693
  if (draft !== undefined) {
609
694
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
@@ -644,7 +729,7 @@ export function createConversationStore(deps) {
644
729
  text: draft.text,
645
730
  files: [...draft.files],
646
731
  revision,
647
- ...(current?.submitted === undefined ? {} : { submitted: current.submitted }),
732
+ ...(current === undefined ? {} : pending(current)),
648
733
  }));
649
734
  },
650
735
  discard(conversation) {
@@ -662,18 +747,19 @@ export function createConversationStore(deps) {
662
747
  return {
663
748
  surface,
664
749
  chatDraft,
665
- hydrate(held, stored) {
666
- messages.clear();
750
+ hydrate(held, kept) {
751
+ stored.clear();
667
752
  reads.clear();
668
- for (const view of views.values())
669
- view.messages = null;
753
+ partials.clear();
670
754
  for (const entry of held) {
671
- messages.set(entry.conversation, [...entry.messages].sort(ordered));
755
+ stored.add(entry.conversation);
672
756
  if (entry.read !== undefined)
673
757
  reads.set(entry.conversation, entry.read);
758
+ if (entry.partial)
759
+ partials.add(entry.conversation);
674
760
  }
675
761
  drafts.clear();
676
- for (const record of stored)
762
+ for (const record of kept)
677
763
  drafts.set(record.conversation, record);
678
764
  },
679
765
  async reconcile() {
@@ -681,8 +767,10 @@ export function createConversationStore(deps) {
681
767
  return;
682
768
  const named = new Set(listing.keys());
683
769
  const leaving = [
684
- ...new Set([...replicated, ...messages.keys(), ...reads.keys(), ...drafts.keys()]),
685
- ].filter((conversation) => !named.has(conversation));
770
+ ...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
771
+ ].filter((conversation) => !named.has(conversation) &&
772
+ !creations.has(conversation) &&
773
+ (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
686
774
  for (const conversation of named) {
687
775
  if (!replicated.has(conversation))
688
776
  behind.add(conversation);
@@ -709,6 +797,10 @@ export function createConversationStore(deps) {
709
797
  continue;
710
798
  if (deps.closed())
711
799
  return;
800
+ if (deps.expired(submission.operation)) {
801
+ await forgotten(conversation, submission);
802
+ continue;
803
+ }
712
804
  const result = await dispatched(conversation, submission);
713
805
  if (result.kind === 'revoked-authorization')
714
806
  return;
@@ -716,16 +808,26 @@ export function createConversationStore(deps) {
716
808
  progress.reachable = false;
717
809
  return;
718
810
  }
811
+ if (result.kind !== 'action' && result.retryable) {
812
+ progress.complete = false;
813
+ return;
814
+ }
719
815
  }
720
816
  }
721
817
  },
722
818
  current() {
723
819
  return behind.size === 0;
724
820
  },
821
+ known() {
822
+ return listKnown;
823
+ },
725
824
  reconnected() {
726
825
  for (const conversation of replicated)
727
826
  gap(conversation);
728
827
  },
828
+ reopened() {
829
+ sending.clear();
830
+ },
729
831
  watch(conversation, options, deliver) {
730
832
  const limit = limitOf(options);
731
833
  const key = JSON.stringify([conversation, limit ?? null]);
@@ -733,18 +835,20 @@ export function createConversationStore(deps) {
733
835
  conversation,
734
836
  limit,
735
837
  watchers: new Set(),
736
- messages: null,
838
+ window: null,
737
839
  };
738
840
  views.set(key, view);
739
841
  const watcher = { notify: deliver, ready: false };
740
842
  view.watchers.add(watcher);
741
843
  void deps
742
- .durably(() => Promise.resolve())
844
+ .durably(async (local) => {
845
+ view.window ??= await local.messageWindow(conversation, limit);
846
+ })
743
847
  .then(() => {
744
848
  if (deps.closed() || !view.watchers.has(watcher))
745
849
  return;
746
850
  watcher.ready = true;
747
- watcher.notify(viewOf(conversation, limit));
851
+ watcher.notify(viewOf(conversation, view.window ?? []));
748
852
  })
749
853
  .catch(() => { });
750
854
  return () => {
@@ -791,12 +895,15 @@ export function createConversationStore(deps) {
791
895
  if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
792
896
  if (event.kind === 'list') {
793
897
  listing.clear();
794
- for (const entry of event.entries)
898
+ for (const entry of event.entries) {
795
899
  listing.set(entry.conversation, { ...entry });
900
+ creations.delete(entry.conversation);
901
+ }
796
902
  listKnown = true;
797
903
  }
798
904
  else if (event.kind === 'listed') {
799
905
  listing.set(event.entry.conversation, { ...event.entry });
906
+ creations.delete(event.entry.conversation);
800
907
  }
801
908
  else {
802
909
  listing.delete(event.conversation);
@@ -810,12 +917,11 @@ export function createConversationStore(deps) {
810
917
  heard(event);
811
918
  return false;
812
919
  },
813
- messages() {
814
- return [...messages].flatMap(([conversation, held]) => held.map(({ identity, text }) => ({ conversation, identity, text })));
920
+ searchable() {
921
+ return [...(listKnown ? listing.keys() : stored)].sort();
815
922
  },
816
- message(conversation, identity) {
817
- const held = messages.get(conversation)?.find((message) => message.identity === identity);
818
- return held === undefined ? null : { conversation, identity, text: held.text };
923
+ acquired(conversation) {
924
+ return reads.has(conversation) && !partials.has(conversation);
819
925
  },
820
926
  close() {
821
927
  views.clear();
@@ -1,8 +1,14 @@
1
+ import type { Persistence } from './readiness.js';
2
+ export type StorageEstimate = {
3
+ readonly usage: number;
4
+ readonly quota: number;
5
+ };
1
6
  export type RuntimeShell = {
2
- persistentStorage: boolean;
7
+ persistence: Persistence;
3
8
  generation: string | null;
4
9
  keepGeneration(value: string): void | Promise<void>;
5
10
  dropGeneration(): void | Promise<void>;
11
+ estimate?(): Promise<StorageEstimate | null>;
6
12
  };
7
13
  export declare function detectStorageLoss(stored: string | null, reported: string | null): string | null;
8
14
  export declare function nextGeneration(stored: string | null, reported: string | null): string;
@@ -1 +1 @@
1
- {"version":3,"file":"durability.d.ts","sourceRoot":"","sources":["../src/durability.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IACzB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,cAAc,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAG/F;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGrF"}
1
+ {"version":3,"file":"durability.d.ts","sourceRoot":"","sources":["../src/durability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAIlD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,WAAW,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,cAAc,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,QAAQ,CAAC,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;CAC9C,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAG/F;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGrF"}
package/dist/entry.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type QueryResult } from './query.js';
1
+ import { type QueryRows } from './query.js';
2
2
  export { QueryError } from './query.js';
3
3
  export type EntryRoom = {
4
4
  locator: string;
@@ -11,7 +11,7 @@ export declare function boundQuery(template: unknown, args: EntryArguments): unk
11
11
  export declare function entryParameters(template: unknown): readonly string[];
12
12
  export declare function entryRefusal(template: unknown): string | null;
13
13
  export type EntryScope = {
14
- results(queries: Readonly<Record<string, unknown>>): Promise<Record<string, QueryResult>>;
14
+ results(queries: Readonly<Record<string, unknown>>): Promise<Record<string, QueryRows>>;
15
15
  };
16
16
  export declare function entryScope(rooms: readonly EntryRoom[]): EntryScope;
17
17
  //# sourceMappingURL=entry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAGA,OAAO,EAML,KAAK,WAAW,EAEjB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE9D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AA0D7E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAE3E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAMpE;AAkCD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQ7D;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;CAC3F,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CAqDlE"}
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAGA,OAAO,EAML,KAAK,SAAS,EAEf,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE9D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AA0D7E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAE3E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAMpE;AAkCD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQ7D;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;CACzF,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CAsDlE"}