@agentshouse/mdruntime 0.1.0-ca9afacd5aebd → 0.1.0-cb0eb0d87ff49
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.
- package/AGENTS.md +114 -123
- package/dist/adapter.d.ts +26 -3
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attempt.d.ts.map +1 -1
- package/dist/attempt.js +14 -1
- package/dist/bootstrap.d.ts +2 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +28 -6
- package/dist/change-set.d.ts +2 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +34 -9
- package/dist/chat/acts.d.ts +3 -4
- package/dist/chat/acts.d.ts.map +1 -1
- package/dist/chat/acts.js +10 -10
- package/dist/chat/list.d.ts +6 -0
- package/dist/chat/list.d.ts.map +1 -1
- package/dist/chat/list.js +6 -3
- package/dist/chat/markdown.d.ts +2 -1
- package/dist/chat/markdown.d.ts.map +1 -1
- package/dist/chat/markdown.js +7 -6
- package/dist/chat/message.d.ts.map +1 -1
- package/dist/chat/message.js +2 -2
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +25 -50
- package/dist/chat/ready.d.ts.map +1 -1
- package/dist/chat/ready.js +2 -2
- package/dist/chat/style.js +3 -3
- package/dist/client.d.ts +7 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -1
- package/dist/collection.d.ts +17 -3
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +230 -19
- package/dist/connection.d.ts +8 -5
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +858 -301
- package/dist/conversation.d.ts +12 -7
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +258 -130
- package/dist/entry.d.ts +17 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +156 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/host.d.ts +4 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +9 -5
- package/dist/identity.d.ts +2 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +6 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/memory.d.ts +1 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +10 -1
- package/dist/model.d.ts +1 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +9 -0
- package/dist/observation.d.ts +5 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +22 -11
- package/dist/outcome.d.ts +4 -0
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +1 -0
- package/dist/overlay.d.ts +1 -0
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +33 -54
- package/dist/query.d.ts +28 -3
- package/dist/query.d.ts.map +1 -1
- package/dist/reach.js +2 -2
- package/dist/react.d.ts +3 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/read-side.d.ts +13 -6
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +282 -83
- package/dist/recovery.d.ts +25 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +197 -0
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +11 -10
- package/dist/replica.d.ts +1 -1
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +49 -32
- package/dist/rows.d.ts +4 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +2 -12
- package/dist/save.d.ts +6 -2
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +42 -18
- package/dist/search.d.ts +28 -6
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +109 -44
- package/dist/shared.d.ts +11 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +368 -0
- package/dist/store.d.ts +35 -10
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +275 -74
- package/dist/turn.d.ts +7 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +45 -0
- package/package.json +12 -8
package/dist/conversation.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { conversationChangeSet } from './change-set.js';
|
|
2
2
|
import { runtimeError } from './failure.js';
|
|
3
3
|
import { declaredFile } from './file.js';
|
|
4
|
-
import { chatDraftRevision } from './identity.js';
|
|
4
|
+
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
5
|
+
import { OFFLINE } from './outcome.js';
|
|
5
6
|
const QUIET = {
|
|
6
7
|
turn: null,
|
|
7
8
|
questions: [],
|
|
@@ -21,6 +22,7 @@ export function unheldView(conversation) {
|
|
|
21
22
|
chatDraft: null,
|
|
22
23
|
};
|
|
23
24
|
}
|
|
25
|
+
export const SESSION_STARTED = 'Session started';
|
|
24
26
|
function startedTurn(turn, operation) {
|
|
25
27
|
if (operation.op === 'begin')
|
|
26
28
|
return { turn, started: operation.at, parts: [], context: null };
|
|
@@ -77,11 +79,15 @@ function ordered(left, right) {
|
|
|
77
79
|
return left.identity < right.identity ? -1 : 1;
|
|
78
80
|
return 0;
|
|
79
81
|
}
|
|
80
|
-
function
|
|
82
|
+
function windowOf(held, arriving, limit) {
|
|
81
83
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
82
84
|
for (const message of arriving)
|
|
83
85
|
byIdentity.set(message.identity, message);
|
|
84
|
-
|
|
86
|
+
const next = [...byIdentity.values()].sort(ordered);
|
|
87
|
+
return limit === undefined ? next : next.slice(Math.max(0, next.length - limit));
|
|
88
|
+
}
|
|
89
|
+
function newestOf(arriving, from) {
|
|
90
|
+
return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
|
|
85
91
|
}
|
|
86
92
|
function acceptedMessage(value) {
|
|
87
93
|
if (typeof value !== 'object' || value === null)
|
|
@@ -158,9 +164,29 @@ function limitOf(options) {
|
|
|
158
164
|
}
|
|
159
165
|
return limit;
|
|
160
166
|
}
|
|
167
|
+
const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
|
|
168
|
+
function unwritten(draft) {
|
|
169
|
+
return draft.text === '' && draft.files.length === 0;
|
|
170
|
+
}
|
|
171
|
+
function pending(draft) {
|
|
172
|
+
return draft.submitted === undefined ? {} : { submitted: draft.submitted };
|
|
173
|
+
}
|
|
174
|
+
function withSubmitted(draft, submitted) {
|
|
175
|
+
const { submitted: _held, ...unsent } = draft;
|
|
176
|
+
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
177
|
+
}
|
|
178
|
+
function returned(draft, submission, others) {
|
|
179
|
+
return withSubmitted({
|
|
180
|
+
conversation: draft.conversation,
|
|
181
|
+
text: [submission.text, draft.text].filter((one) => one !== '').join('\n'),
|
|
182
|
+
files: [...submission.files, ...draft.files],
|
|
183
|
+
revision: chatDraftRevision(),
|
|
184
|
+
}, others);
|
|
185
|
+
}
|
|
161
186
|
export function createConversationStore(deps) {
|
|
162
|
-
const
|
|
187
|
+
const stored = new Set();
|
|
163
188
|
const reads = new Map();
|
|
189
|
+
const partials = new Set();
|
|
164
190
|
const drafts = new Map();
|
|
165
191
|
const sending = new Map();
|
|
166
192
|
const behind = new Set();
|
|
@@ -179,49 +205,51 @@ export function createConversationStore(deps) {
|
|
|
179
205
|
function liveOf(conversation) {
|
|
180
206
|
return lives.get(conversation) ?? QUIET;
|
|
181
207
|
}
|
|
182
|
-
function
|
|
183
|
-
const held = messages.get(conversation) ?? [];
|
|
184
|
-
const shown = limit === undefined ? held : held.slice(Math.max(0, held.length - limit));
|
|
185
|
-
return shown.map(({ identity, turn, text, message }) => ({
|
|
186
|
-
identity,
|
|
187
|
-
turn,
|
|
188
|
-
text,
|
|
189
|
-
message: structuredClone(message),
|
|
190
|
-
}));
|
|
191
|
-
}
|
|
192
|
-
function viewOf(conversation, limit, shown = shownMessages(conversation, limit)) {
|
|
208
|
+
function viewOf(conversation, window) {
|
|
193
209
|
const draft = drafts.get(conversation);
|
|
194
210
|
const facts = liveOf(conversation);
|
|
195
211
|
return {
|
|
196
212
|
conversation,
|
|
197
|
-
messages:
|
|
213
|
+
messages: window.map(({ identity, turn, text, message }) => ({
|
|
214
|
+
identity,
|
|
215
|
+
turn,
|
|
216
|
+
text,
|
|
217
|
+
message,
|
|
218
|
+
})),
|
|
198
219
|
turn: structuredClone(facts.turn),
|
|
199
220
|
questions: structuredClone(facts.questions),
|
|
200
221
|
process: facts.process === null ? null : { ...facts.process },
|
|
201
222
|
commands: structuredClone(facts.commands),
|
|
202
223
|
options: structuredClone(facts.options),
|
|
203
|
-
chatDraft: draft === undefined || draft
|
|
224
|
+
chatDraft: draft === undefined || unwritten(draft)
|
|
204
225
|
? null
|
|
205
226
|
: { text: draft.text, files: [...draft.files], revision: draft.revision },
|
|
206
227
|
};
|
|
207
228
|
}
|
|
208
|
-
function emit(conversation
|
|
229
|
+
function emit(conversation) {
|
|
209
230
|
if (deps.closed())
|
|
210
231
|
return;
|
|
211
232
|
for (const view of views.values()) {
|
|
212
|
-
if (view.conversation !== conversation)
|
|
233
|
+
if (view.conversation !== conversation || view.window === null)
|
|
213
234
|
continue;
|
|
214
|
-
if (messagesChanged)
|
|
215
|
-
view.messages = null;
|
|
216
235
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
217
236
|
if (ready.length === 0)
|
|
218
237
|
continue;
|
|
219
|
-
|
|
220
|
-
const value = viewOf(conversation, view.limit, view.messages);
|
|
238
|
+
const value = viewOf(conversation, view.window);
|
|
221
239
|
for (const watcher of ready)
|
|
222
240
|
watcher.notify(value);
|
|
223
241
|
}
|
|
224
242
|
}
|
|
243
|
+
function slide(conversation, arriving) {
|
|
244
|
+
for (const view of views.values()) {
|
|
245
|
+
if (view.conversation !== conversation || view.window === null)
|
|
246
|
+
continue;
|
|
247
|
+
view.window = arriving === null ? [] : windowOf(view.window, arriving, view.limit);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function changesOf(conversation, arriving) {
|
|
251
|
+
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
252
|
+
}
|
|
225
253
|
function stateOf(conversation) {
|
|
226
254
|
const facts = liveOf(conversation);
|
|
227
255
|
if (facts.questions.length > 0)
|
|
@@ -278,7 +306,10 @@ export function createConversationStore(deps) {
|
|
|
278
306
|
const declaration = deps.agents();
|
|
279
307
|
if (declaration === undefined)
|
|
280
308
|
return;
|
|
281
|
-
const
|
|
309
|
+
const held = await deps.documents(declaration);
|
|
310
|
+
if ('pending' in held)
|
|
311
|
+
return;
|
|
312
|
+
const { documents } = held;
|
|
282
313
|
const names = new Map();
|
|
283
314
|
const retired = new Set();
|
|
284
315
|
for (const document of documents) {
|
|
@@ -310,7 +341,7 @@ export function createConversationStore(deps) {
|
|
|
310
341
|
}
|
|
311
342
|
function live(conversation, change) {
|
|
312
343
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
313
|
-
emit(conversation
|
|
344
|
+
emit(conversation);
|
|
314
345
|
}
|
|
315
346
|
function relayed(conversation, turn, operation) {
|
|
316
347
|
const held = liveOf(conversation).turn;
|
|
@@ -338,21 +369,25 @@ export function createConversationStore(deps) {
|
|
|
338
369
|
}
|
|
339
370
|
async function remove(leaving) {
|
|
340
371
|
await deps.durably(async (local) => {
|
|
341
|
-
await local.dropConversations(leaving);
|
|
342
372
|
const removed = [];
|
|
343
|
-
const changed = [];
|
|
344
373
|
for (const conversation of leaving) {
|
|
345
|
-
for (const
|
|
346
|
-
removed.push({ conversation, identity:
|
|
374
|
+
for (const identity of await local.messageIdentities(conversation)) {
|
|
375
|
+
removed.push({ conversation, identity, text: null });
|
|
347
376
|
}
|
|
348
|
-
|
|
377
|
+
}
|
|
378
|
+
await local.dropConversations(leaving);
|
|
379
|
+
const changed = [];
|
|
380
|
+
for (const conversation of leaving) {
|
|
381
|
+
if (stored.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
|
|
349
382
|
changed.push(conversation);
|
|
350
383
|
}
|
|
351
|
-
|
|
384
|
+
stored.delete(conversation);
|
|
352
385
|
reads.delete(conversation);
|
|
386
|
+
partials.delete(conversation);
|
|
353
387
|
drafts.delete(conversation);
|
|
354
388
|
behind.delete(conversation);
|
|
355
389
|
setLive(conversation, null);
|
|
390
|
+
slide(conversation, null);
|
|
356
391
|
}
|
|
357
392
|
if (changed.length > 0)
|
|
358
393
|
deps.publish(conversationChangeSet(removed, changed));
|
|
@@ -370,30 +405,25 @@ export function createConversationStore(deps) {
|
|
|
370
405
|
const advances = read !== undefined &&
|
|
371
406
|
!behind.has(conversation) &&
|
|
372
407
|
(read === null || message.position > read);
|
|
373
|
-
|
|
408
|
+
await sealed(local, conversation, new Set([message.turn]));
|
|
374
409
|
await local.keepMessages({
|
|
375
410
|
conversation,
|
|
376
411
|
messages: [message],
|
|
377
|
-
...(advances ? { read: message.position } : {}),
|
|
378
|
-
sealed,
|
|
412
|
+
...(advances ? { read: { at: message.position, partial: false } } : {}),
|
|
379
413
|
});
|
|
380
|
-
|
|
414
|
+
stored.add(conversation);
|
|
381
415
|
if (advances)
|
|
382
416
|
reads.set(conversation, message.position);
|
|
383
|
-
if (sealed)
|
|
384
|
-
drafts.delete(conversation);
|
|
385
417
|
sealTurns(conversation, new Set([message.turn]));
|
|
386
|
-
|
|
418
|
+
slide(conversation, [message]);
|
|
419
|
+
deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
|
|
387
420
|
});
|
|
388
421
|
}
|
|
389
422
|
async function readHistories(conversations) {
|
|
390
423
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
391
|
-
const starts = new Map(conversations.map((one) => [one, reads.get(one)]));
|
|
392
|
-
const arriving = new Map();
|
|
393
|
-
const complete = new Set();
|
|
394
424
|
let asking = conversations.map((conversation) => ({
|
|
395
425
|
conversation,
|
|
396
|
-
after:
|
|
426
|
+
after: reads.get(conversation) ?? undefined,
|
|
397
427
|
}));
|
|
398
428
|
while (asking.length > 0) {
|
|
399
429
|
const result = await deps.call('conversation.history', {
|
|
@@ -405,46 +435,41 @@ export function createConversationStore(deps) {
|
|
|
405
435
|
const pages = historyPages(result);
|
|
406
436
|
if (pages === null)
|
|
407
437
|
return result;
|
|
408
|
-
const asked = new
|
|
438
|
+
const asked = new Map(asking.map(({ conversation, after }) => [conversation, after]));
|
|
409
439
|
const following = [];
|
|
410
440
|
for (const page of pages) {
|
|
411
441
|
if (!asked.has(page.conversation))
|
|
412
442
|
continue;
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
...page.messages,
|
|
416
|
-
]);
|
|
417
|
-
if (page.next === undefined)
|
|
418
|
-
complete.add(page.conversation);
|
|
419
|
-
else
|
|
443
|
+
await keepPage(page.conversation, page.messages, asked.get(page.conversation) ?? null, page.next, generations.get(page.conversation) ?? 0);
|
|
444
|
+
if (page.next !== undefined) {
|
|
420
445
|
following.push({ conversation: page.conversation, after: page.next });
|
|
446
|
+
}
|
|
421
447
|
}
|
|
422
448
|
asking = following;
|
|
423
449
|
}
|
|
424
|
-
for (const [conversation, held] of arriving) {
|
|
425
|
-
if (!complete.has(conversation))
|
|
426
|
-
continue;
|
|
427
|
-
await keepHistory(conversation, held, starts.get(conversation), generations.get(conversation) ?? 0);
|
|
428
|
-
}
|
|
429
450
|
return null;
|
|
430
451
|
}
|
|
431
|
-
function
|
|
452
|
+
function keepPage(conversation, arriving, from, next, generation) {
|
|
432
453
|
return deps.durably(async (local) => {
|
|
433
454
|
if (!replicated.has(conversation))
|
|
434
455
|
return;
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
await local
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
456
|
+
const whole = next === undefined;
|
|
457
|
+
const cursor = next ?? newestOf(arriving, from);
|
|
458
|
+
const partial = !whole && (partials.has(conversation) || !reads.has(conversation));
|
|
459
|
+
const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
|
|
460
|
+
await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
|
|
461
|
+
stored.add(conversation);
|
|
462
|
+
reads.set(conversation, cursor);
|
|
463
|
+
if (partial)
|
|
464
|
+
partials.add(conversation);
|
|
465
|
+
else
|
|
466
|
+
partials.delete(conversation);
|
|
443
467
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
444
|
-
if ((gaps.get(conversation) ?? 0) === generation)
|
|
468
|
+
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
445
469
|
behind.delete(conversation);
|
|
446
|
-
|
|
447
|
-
|
|
470
|
+
slide(conversation, arriving);
|
|
471
|
+
if (arriving.length > 0 || sealing || whole) {
|
|
472
|
+
deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
|
|
448
473
|
}
|
|
449
474
|
});
|
|
450
475
|
}
|
|
@@ -457,7 +482,7 @@ export function createConversationStore(deps) {
|
|
|
457
482
|
if (held === undefined)
|
|
458
483
|
return;
|
|
459
484
|
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
460
|
-
emit(conversation
|
|
485
|
+
emit(conversation);
|
|
461
486
|
return;
|
|
462
487
|
}
|
|
463
488
|
if (event.kind === 'turn') {
|
|
@@ -490,22 +515,103 @@ export function createConversationStore(deps) {
|
|
|
490
515
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
491
516
|
emit(conversation);
|
|
492
517
|
}
|
|
518
|
+
async function kept(local, conversation, following) {
|
|
519
|
+
if (following === null || (unwritten(following) && following.submitted === undefined)) {
|
|
520
|
+
await local.dropChatDraft(conversation);
|
|
521
|
+
drafts.delete(conversation);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
await local.keepChatDraft(following);
|
|
525
|
+
drafts.set(conversation, following);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async function sealed(local, conversation, turns) {
|
|
529
|
+
const held = drafts.get(conversation);
|
|
530
|
+
const submitted = held?.submitted ?? [];
|
|
531
|
+
const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
|
|
532
|
+
if (held === undefined || left.length === submitted.length)
|
|
533
|
+
return false;
|
|
534
|
+
await kept(local, conversation, withSubmitted(held, left));
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
493
537
|
function withDraft(conversation, next) {
|
|
494
538
|
return deps.durably(async (local) => {
|
|
495
|
-
const following = next(drafts.get(conversation));
|
|
539
|
+
const following = await next(drafts.get(conversation), local);
|
|
496
540
|
if (following === undefined)
|
|
497
541
|
return;
|
|
498
|
-
|
|
499
|
-
await local.dropChatDraft(conversation);
|
|
500
|
-
drafts.delete(conversation);
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
await local.keepChatDraft(following);
|
|
504
|
-
drafts.set(conversation, following);
|
|
505
|
-
}
|
|
542
|
+
await kept(local, conversation, following);
|
|
506
543
|
deps.publish(conversationChangeSet([], [conversation]));
|
|
507
544
|
});
|
|
508
545
|
}
|
|
546
|
+
async function submitted(conversation, operation, draft) {
|
|
547
|
+
let submission;
|
|
548
|
+
let earlier = false;
|
|
549
|
+
await withDraft(conversation, (current) => {
|
|
550
|
+
submission = current?.submitted?.find((one) => one.operation === operation);
|
|
551
|
+
earlier = submission !== undefined;
|
|
552
|
+
if (submission !== undefined)
|
|
553
|
+
return undefined;
|
|
554
|
+
const said = draft ?? current;
|
|
555
|
+
submission = {
|
|
556
|
+
operation,
|
|
557
|
+
text: said?.text ?? '',
|
|
558
|
+
files: [...(said?.files ?? [])],
|
|
559
|
+
turn: null,
|
|
560
|
+
};
|
|
561
|
+
return {
|
|
562
|
+
conversation,
|
|
563
|
+
...(draft === undefined || current === undefined
|
|
564
|
+
? { text: '', files: [], revision: chatDraftRevision() }
|
|
565
|
+
: { text: current.text, files: current.files, revision: current.revision }),
|
|
566
|
+
submitted: [...(current?.submitted ?? []), submission],
|
|
567
|
+
};
|
|
568
|
+
});
|
|
569
|
+
return { submission: submission, earlier };
|
|
570
|
+
}
|
|
571
|
+
function forgotten(conversation, submission) {
|
|
572
|
+
return withDraft(conversation, (current) => {
|
|
573
|
+
const held = current?.submitted ?? [];
|
|
574
|
+
if (current === undefined || !held.includes(submission))
|
|
575
|
+
return undefined;
|
|
576
|
+
return withSubmitted(current, held.filter((one) => one !== submission));
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
async function dispatched(conversation, submission) {
|
|
580
|
+
const dispatch = {};
|
|
581
|
+
sending.set(submission.operation, dispatch);
|
|
582
|
+
try {
|
|
583
|
+
const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
|
|
584
|
+
const result = await invoke('conversation.send', conversation, {
|
|
585
|
+
text: submission.text,
|
|
586
|
+
files,
|
|
587
|
+
operation_id: submission.operation,
|
|
588
|
+
});
|
|
589
|
+
if (result.kind === 'revoked-authorization')
|
|
590
|
+
return result;
|
|
591
|
+
if (result.kind !== 'action' && result.retryable) {
|
|
592
|
+
deps.unanswered();
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
const turn = turnOf(result);
|
|
596
|
+
await withDraft(conversation, async (current, local) => {
|
|
597
|
+
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
598
|
+
if (current === undefined || held === undefined || held.turn !== null)
|
|
599
|
+
return undefined;
|
|
600
|
+
const others = current.submitted.filter((one) => one !== held);
|
|
601
|
+
if (result.kind !== 'action')
|
|
602
|
+
return returned(current, held, others);
|
|
603
|
+
const arrived = turn !== null && (await local.turnArrived(conversation, turn));
|
|
604
|
+
return withSubmitted(current, turn === null || arrived
|
|
605
|
+
? others
|
|
606
|
+
: current.submitted.map((one) => (one === held ? { ...held, turn } : one)));
|
|
607
|
+
});
|
|
608
|
+
return result;
|
|
609
|
+
}
|
|
610
|
+
finally {
|
|
611
|
+
if (sending.get(submission.operation) === dispatch)
|
|
612
|
+
sending.delete(submission.operation);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
509
615
|
function invoke(name, conversation, extra = {}) {
|
|
510
616
|
return deps.call(name, { conversation, ...extra });
|
|
511
617
|
}
|
|
@@ -516,46 +622,25 @@ export function createConversationStore(deps) {
|
|
|
516
622
|
},
|
|
517
623
|
async read(conversation, options = {}) {
|
|
518
624
|
const limit = limitOf(options);
|
|
519
|
-
return deps.durably(() =>
|
|
625
|
+
return deps.durably(async (local) => structuredClone(viewOf(conversation, await local.messageWindow(conversation, limit))));
|
|
520
626
|
},
|
|
521
627
|
create(input) {
|
|
522
628
|
return deps.call('conversation.create', input);
|
|
523
629
|
},
|
|
524
|
-
async send(conversation) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
try {
|
|
532
|
-
const files = await Promise.all((draft?.files ?? []).map(async (file) => ({
|
|
533
|
-
...(await declaredFile(file)),
|
|
534
|
-
blob: file.blob,
|
|
535
|
-
})));
|
|
536
|
-
const result = await invoke('conversation.send', conversation, {
|
|
537
|
-
text: draft?.text ?? '',
|
|
538
|
-
files,
|
|
539
|
-
});
|
|
540
|
-
const turn = turnOf(result);
|
|
541
|
-
if (turn === null || draft === undefined)
|
|
542
|
-
return result;
|
|
543
|
-
await withDraft(conversation, (current) => {
|
|
544
|
-
if (current !== draft)
|
|
545
|
-
return undefined;
|
|
546
|
-
const sealed = (messages.get(conversation) ?? []).some((message) => message.turn === turn);
|
|
547
|
-
return sealed ? null : { ...draft, turn };
|
|
548
|
-
});
|
|
549
|
-
return result;
|
|
630
|
+
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
631
|
+
if (deps.online()) {
|
|
632
|
+
const { submission, earlier } = await submitted(conversation, operation, draft);
|
|
633
|
+
if (!earlier || !deps.expired(operation))
|
|
634
|
+
return dispatched(conversation, submission);
|
|
635
|
+
await forgotten(conversation, submission);
|
|
636
|
+
return EXPIRED;
|
|
550
637
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
deps.publish(conversationChangeSet([], [conversation]));
|
|
556
|
-
}
|
|
557
|
-
}
|
|
638
|
+
if (draft !== undefined) {
|
|
639
|
+
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
640
|
+
? undefined
|
|
641
|
+
: returned(current ?? { conversation, text: '', files: [], revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
558
642
|
}
|
|
643
|
+
return OFFLINE;
|
|
559
644
|
},
|
|
560
645
|
answer(conversation, answer) {
|
|
561
646
|
return invoke('conversation.answer', conversation, { answer });
|
|
@@ -570,7 +655,12 @@ export function createConversationStore(deps) {
|
|
|
570
655
|
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
571
656
|
if (result.kind !== 'action')
|
|
572
657
|
return result;
|
|
573
|
-
await withDraft(conversation, (current) =>
|
|
658
|
+
await withDraft(conversation, (current) => {
|
|
659
|
+
const held = current?.submitted?.find((one) => one.turn === turn);
|
|
660
|
+
if (current === undefined || held === undefined)
|
|
661
|
+
return undefined;
|
|
662
|
+
return returned(current, held, current.submitted.filter((one) => one !== held));
|
|
663
|
+
});
|
|
574
664
|
return result;
|
|
575
665
|
},
|
|
576
666
|
delete(conversation) {
|
|
@@ -579,33 +669,42 @@ export function createConversationStore(deps) {
|
|
|
579
669
|
};
|
|
580
670
|
const chatDraft = {
|
|
581
671
|
write(conversation, draft, revision = chatDraftRevision()) {
|
|
582
|
-
return withDraft(conversation, () => ({
|
|
672
|
+
return withDraft(conversation, (current) => ({
|
|
583
673
|
conversation,
|
|
584
674
|
text: draft.text,
|
|
585
675
|
files: [...draft.files],
|
|
586
|
-
turn: null,
|
|
587
676
|
revision,
|
|
677
|
+
...(current?.submitted === undefined ? {} : { submitted: current.submitted }),
|
|
588
678
|
}));
|
|
589
679
|
},
|
|
590
680
|
discard(conversation) {
|
|
591
|
-
return withDraft(conversation, (current) =>
|
|
681
|
+
return withDraft(conversation, (current) => current === undefined
|
|
682
|
+
? undefined
|
|
683
|
+
: {
|
|
684
|
+
conversation,
|
|
685
|
+
text: '',
|
|
686
|
+
files: [],
|
|
687
|
+
revision: chatDraftRevision(),
|
|
688
|
+
...pending(current),
|
|
689
|
+
});
|
|
592
690
|
},
|
|
593
691
|
};
|
|
594
692
|
return {
|
|
595
693
|
surface,
|
|
596
694
|
chatDraft,
|
|
597
|
-
hydrate(held,
|
|
598
|
-
|
|
695
|
+
hydrate(held, kept) {
|
|
696
|
+
stored.clear();
|
|
599
697
|
reads.clear();
|
|
600
|
-
|
|
601
|
-
view.messages = null;
|
|
698
|
+
partials.clear();
|
|
602
699
|
for (const entry of held) {
|
|
603
|
-
|
|
700
|
+
stored.add(entry.conversation);
|
|
604
701
|
if (entry.read !== undefined)
|
|
605
702
|
reads.set(entry.conversation, entry.read);
|
|
703
|
+
if (entry.partial)
|
|
704
|
+
partials.add(entry.conversation);
|
|
606
705
|
}
|
|
607
706
|
drafts.clear();
|
|
608
|
-
for (const record of
|
|
707
|
+
for (const record of kept)
|
|
609
708
|
drafts.set(record.conversation, record);
|
|
610
709
|
},
|
|
611
710
|
async reconcile() {
|
|
@@ -613,7 +712,7 @@ export function createConversationStore(deps) {
|
|
|
613
712
|
return;
|
|
614
713
|
const named = new Set(listing.keys());
|
|
615
714
|
const leaving = [
|
|
616
|
-
...new Set([...replicated, ...
|
|
715
|
+
...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
|
|
617
716
|
].filter((conversation) => !named.has(conversation));
|
|
618
717
|
for (const conversation of named) {
|
|
619
718
|
if (!replicated.has(conversation))
|
|
@@ -634,6 +733,31 @@ export function createConversationStore(deps) {
|
|
|
634
733
|
if (behind.size > 0)
|
|
635
734
|
progress.complete = false;
|
|
636
735
|
},
|
|
736
|
+
async resend(progress) {
|
|
737
|
+
for (const [conversation, held] of [...drafts]) {
|
|
738
|
+
for (const submission of held.submitted ?? []) {
|
|
739
|
+
if (submission.turn !== null || sending.has(submission.operation))
|
|
740
|
+
continue;
|
|
741
|
+
if (deps.closed())
|
|
742
|
+
return;
|
|
743
|
+
if (deps.expired(submission.operation)) {
|
|
744
|
+
await forgotten(conversation, submission);
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
const result = await dispatched(conversation, submission);
|
|
748
|
+
if (result.kind === 'revoked-authorization')
|
|
749
|
+
return;
|
|
750
|
+
if (result.kind === 'transport-uncertainty') {
|
|
751
|
+
progress.reachable = false;
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
if (result.kind !== 'action' && result.retryable) {
|
|
755
|
+
progress.complete = false;
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
},
|
|
637
761
|
current() {
|
|
638
762
|
return behind.size === 0;
|
|
639
763
|
},
|
|
@@ -641,6 +765,9 @@ export function createConversationStore(deps) {
|
|
|
641
765
|
for (const conversation of replicated)
|
|
642
766
|
gap(conversation);
|
|
643
767
|
},
|
|
768
|
+
reopened() {
|
|
769
|
+
sending.clear();
|
|
770
|
+
},
|
|
644
771
|
watch(conversation, options, deliver) {
|
|
645
772
|
const limit = limitOf(options);
|
|
646
773
|
const key = JSON.stringify([conversation, limit ?? null]);
|
|
@@ -648,18 +775,20 @@ export function createConversationStore(deps) {
|
|
|
648
775
|
conversation,
|
|
649
776
|
limit,
|
|
650
777
|
watchers: new Set(),
|
|
651
|
-
|
|
778
|
+
window: null,
|
|
652
779
|
};
|
|
653
780
|
views.set(key, view);
|
|
654
781
|
const watcher = { notify: deliver, ready: false };
|
|
655
782
|
view.watchers.add(watcher);
|
|
656
783
|
void deps
|
|
657
|
-
.durably(() =>
|
|
784
|
+
.durably(async (local) => {
|
|
785
|
+
view.window ??= await local.messageWindow(conversation, limit);
|
|
786
|
+
})
|
|
658
787
|
.then(() => {
|
|
659
788
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
660
789
|
return;
|
|
661
790
|
watcher.ready = true;
|
|
662
|
-
watcher.notify(viewOf(conversation,
|
|
791
|
+
watcher.notify(viewOf(conversation, view.window ?? []));
|
|
663
792
|
})
|
|
664
793
|
.catch(() => { });
|
|
665
794
|
return () => {
|
|
@@ -725,12 +854,11 @@ export function createConversationStore(deps) {
|
|
|
725
854
|
heard(event);
|
|
726
855
|
return false;
|
|
727
856
|
},
|
|
728
|
-
|
|
729
|
-
return [...
|
|
857
|
+
searchable() {
|
|
858
|
+
return [...(listKnown ? listing.keys() : stored)].sort();
|
|
730
859
|
},
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
return held === undefined ? null : { conversation, identity, text: held.text };
|
|
860
|
+
acquired(conversation) {
|
|
861
|
+
return reads.has(conversation) && !partials.has(conversation);
|
|
734
862
|
},
|
|
735
863
|
close() {
|
|
736
864
|
views.clear();
|
package/dist/entry.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type QueryRows } from './query.js';
|
|
2
|
+
export { QueryError } from './query.js';
|
|
3
|
+
export type EntryRoom = {
|
|
4
|
+
locator: string;
|
|
5
|
+
schema: string | null;
|
|
6
|
+
documents: Readonly<Record<string, string>>;
|
|
7
|
+
};
|
|
8
|
+
export type EntryArguments = Readonly<Record<string, string>>;
|
|
9
|
+
export type EntryParameterKind = 'string' | 'integer' | 'number' | 'boolean';
|
|
10
|
+
export declare function boundQuery(template: unknown, args: EntryArguments): unknown;
|
|
11
|
+
export declare function entryParameters(template: unknown): readonly string[];
|
|
12
|
+
export declare function entryRefusal(template: unknown): string | null;
|
|
13
|
+
export type EntryScope = {
|
|
14
|
+
results(queries: Readonly<Record<string, unknown>>): Promise<Record<string, QueryRows>>;
|
|
15
|
+
};
|
|
16
|
+
export declare function entryScope(rooms: readonly EntryRoom[]): EntryScope;
|
|
17
|
+
//# sourceMappingURL=entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,CAqDlE"}
|