@agentshouse/mdruntime 0.1.0-ca6edc1abd618 → 0.1.0-ca92b54804c33
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/README.md +2 -2
- package/dist/adapter.d.ts +101 -70
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +3 -10
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +29 -47
- package/dist/bootstrap.d.ts +16 -14
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +272 -129
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/change.d.ts +1 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/client.d.ts +13 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +54 -6
- package/dist/collection.d.ts +7 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +23 -17
- package/dist/compile.d.ts +1 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +108 -243
- package/dist/connection.d.ts +14 -11
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2264 -1933
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation.d.ts +21 -17
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +405 -253
- package/dist/durability.d.ts +6 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +3 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -8
- package/dist/file.d.ts +2 -11
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +6 -4
- package/dist/host.d.ts +3 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +26 -29
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +12 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/job.d.ts +14 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +28 -0
- package/dist/live.d.ts +5 -4
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +62 -20
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +12 -5
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +216 -85
- package/dist/messages.d.ts +3 -3
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +20 -19
- package/dist/model.d.ts +8 -10
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +5 -17
- package/dist/observation.d.ts +3 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +8 -3
- package/dist/outcome.d.ts +13 -5
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +16 -2
- package/dist/overlay.d.ts +1 -0
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +11 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -0
- package/dist/query.d.ts +8 -207
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- package/dist/reach.d.ts +18 -11
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +195 -126
- package/dist/react.d.ts +3 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/readiness.d.ts +3 -17
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +4 -10
- package/dist/recovery.d.ts +1 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +35 -1
- package/dist/refresh.d.ts +7 -5
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +33 -5
- package/dist/replica.d.ts +10 -8
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +52 -20
- package/dist/room-draft.d.ts +21 -5
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +98 -51
- package/dist/rows.d.ts +18 -16
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +45 -153
- package/dist/save.d.ts +2 -0
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +22 -13
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/shared.d.ts +0 -1
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +0 -10
- package/dist/status.d.ts +4 -5
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -7
- package/dist/store.d.ts +101 -61
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +153 -161
- package/dist/stored.d.ts +66 -55
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +341 -273
- package/dist/tables.d.ts +90 -63
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +174 -98
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/validation.d.ts +3 -4
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +42 -100
- package/dist/windows.d.ts +1 -12
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +2 -75
- package/dist/wire.d.ts +17 -8
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +5 -2
- package/package.json +2 -2
- package/dist/fence.d.ts +0 -6
- package/dist/fence.d.ts.map +0 -1
- package/dist/fence.js +0 -10
- package/dist/fulltext.d.ts +0 -27
- package/dist/fulltext.d.ts.map +0 -1
- package/dist/fulltext.js +0 -368
- package/dist/reads.d.ts +0 -11
- package/dist/reads.d.ts.map +0 -1
- package/dist/reads.js +0 -100
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -150
package/dist/conversation.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
|
|
1
2
|
import { conversationChangeSet } from './change-set.js';
|
|
2
3
|
import { runtimeError } from './failure.js';
|
|
3
4
|
import { refusedForSpace } from './durability.js';
|
|
4
|
-
import { declaredFile } from './file.js';
|
|
5
5
|
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
6
6
|
import { OFFLINE } from './outcome.js';
|
|
7
|
-
import {
|
|
7
|
+
import { untilAnswered } from './recovery.js';
|
|
8
|
+
import { capped, FROM_START, messageOrder, precedes } from './windows.js';
|
|
8
9
|
const QUIET = {
|
|
9
10
|
turn: null,
|
|
10
11
|
questions: [],
|
|
@@ -12,6 +13,7 @@ const QUIET = {
|
|
|
12
13
|
commands: [],
|
|
13
14
|
options: [],
|
|
14
15
|
starting: null,
|
|
16
|
+
subagents: [],
|
|
15
17
|
link: null,
|
|
16
18
|
deliveryUnknown: [],
|
|
17
19
|
};
|
|
@@ -21,6 +23,7 @@ const FACTS = [
|
|
|
21
23
|
'commands',
|
|
22
24
|
'options',
|
|
23
25
|
'starting',
|
|
26
|
+
'subagents',
|
|
24
27
|
'link',
|
|
25
28
|
'deliveryUnknown',
|
|
26
29
|
];
|
|
@@ -31,9 +34,11 @@ export function unheldView(conversation) {
|
|
|
31
34
|
turn: null,
|
|
32
35
|
questions: [],
|
|
33
36
|
process: null,
|
|
37
|
+
processEnded: null,
|
|
34
38
|
commands: [],
|
|
35
39
|
options: [],
|
|
36
40
|
starting: null,
|
|
41
|
+
subagents: [],
|
|
37
42
|
link: null,
|
|
38
43
|
deliveryUnknown: [],
|
|
39
44
|
chatDraft: null,
|
|
@@ -48,10 +53,8 @@ export function newChat(agent) {
|
|
|
48
53
|
function creationOf(input) {
|
|
49
54
|
if (typeof input !== 'object' || input === null)
|
|
50
55
|
return null;
|
|
51
|
-
const { agent_id: agent, operation_id: operation
|
|
52
|
-
return typeof agent === 'string' && typeof operation === 'string'
|
|
53
|
-
? { agent, operation }
|
|
54
|
-
: null;
|
|
56
|
+
const { agent_id: agent, operation_id: operation } = input;
|
|
57
|
+
return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
|
|
55
58
|
}
|
|
56
59
|
function startedTurn(turn, operation) {
|
|
57
60
|
if (operation.op === 'begin')
|
|
@@ -210,7 +213,14 @@ function askedOf(options) {
|
|
|
210
213
|
}
|
|
211
214
|
return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
|
|
212
215
|
}
|
|
213
|
-
|
|
216
|
+
function phraseOf(text) {
|
|
217
|
+
if (text === undefined)
|
|
218
|
+
return null;
|
|
219
|
+
if (typeof text !== 'string' || text === '') {
|
|
220
|
+
throw runtimeError('invalid-text', 'a conversation is found by a phrase');
|
|
221
|
+
}
|
|
222
|
+
return text;
|
|
223
|
+
}
|
|
214
224
|
const DURABLE_WRITE = {
|
|
215
225
|
kind: 'rejected',
|
|
216
226
|
code: 'durable-write',
|
|
@@ -244,10 +254,6 @@ function returned(draft, submission, others) {
|
|
|
244
254
|
}, others);
|
|
245
255
|
}
|
|
246
256
|
export function createConversationStore(deps) {
|
|
247
|
-
const stored = new Set();
|
|
248
|
-
const reads = new Map();
|
|
249
|
-
const partials = new Set();
|
|
250
|
-
const drafts = new Map();
|
|
251
257
|
const sending = new Map();
|
|
252
258
|
const behind = new Set();
|
|
253
259
|
const catching = new Map();
|
|
@@ -255,21 +261,23 @@ export function createConversationStore(deps) {
|
|
|
255
261
|
const lives = new Map();
|
|
256
262
|
const unsent = new Map();
|
|
257
263
|
const views = new Map();
|
|
258
|
-
const
|
|
264
|
+
const heardNewest = new Map();
|
|
265
|
+
let relisting = null;
|
|
259
266
|
const creations = new Set();
|
|
260
267
|
const listWatchers = new Set();
|
|
268
|
+
const unlisted = new Set();
|
|
261
269
|
let listKnown = false;
|
|
262
270
|
const agentWatchers = new Set();
|
|
263
|
-
|
|
264
|
-
let
|
|
265
|
-
let
|
|
271
|
+
const unnamed = new Set();
|
|
272
|
+
let agentsShown = null;
|
|
273
|
+
let listedAgents = null;
|
|
274
|
+
let agentsReading = false;
|
|
266
275
|
let replicated = new Set();
|
|
267
276
|
let handedReplicated;
|
|
268
277
|
function liveOf(conversation) {
|
|
269
278
|
return lives.get(conversation) ?? QUIET;
|
|
270
279
|
}
|
|
271
|
-
function viewOf(conversation, held, frame) {
|
|
272
|
-
const draft = drafts.get(conversation);
|
|
280
|
+
function viewOf(conversation, held, frame, draft, ended) {
|
|
273
281
|
const facts = liveOf(conversation);
|
|
274
282
|
return {
|
|
275
283
|
conversation,
|
|
@@ -283,14 +291,14 @@ export function createConversationStore(deps) {
|
|
|
283
291
|
turn: structuredClone(facts.turn),
|
|
284
292
|
questions: structuredClone(facts.questions),
|
|
285
293
|
process: facts.process === null ? null : { ...facts.process },
|
|
294
|
+
processEnded: ended,
|
|
286
295
|
commands: structuredClone(facts.commands),
|
|
287
296
|
options: structuredClone(facts.options),
|
|
288
297
|
starting: facts.starting,
|
|
298
|
+
subagents: structuredClone(facts.subagents),
|
|
289
299
|
link: facts.link === null ? null : { ...facts.link },
|
|
290
300
|
deliveryUnknown: [...facts.deliveryUnknown],
|
|
291
|
-
chatDraft: draft ===
|
|
292
|
-
? null
|
|
293
|
-
: { text: draft.text, revision: draft.revision },
|
|
301
|
+
chatDraft: draft === null || unwritten(draft) ? null : { text: draft.text, revision: draft.revision },
|
|
294
302
|
};
|
|
295
303
|
}
|
|
296
304
|
function emit(conversation) {
|
|
@@ -302,7 +310,7 @@ export function createConversationStore(deps) {
|
|
|
302
310
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
303
311
|
if (ready.length === 0)
|
|
304
312
|
continue;
|
|
305
|
-
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
313
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window), view.draft, view.ended);
|
|
306
314
|
for (const watcher of ready)
|
|
307
315
|
watcher.notify(value);
|
|
308
316
|
}
|
|
@@ -338,10 +346,10 @@ export function createConversationStore(deps) {
|
|
|
338
346
|
return 'asking';
|
|
339
347
|
return facts.turn === null ? 'idle' : 'working';
|
|
340
348
|
}
|
|
341
|
-
function entries() {
|
|
342
|
-
return
|
|
349
|
+
function entries(list) {
|
|
350
|
+
return list.map((entry) => ({
|
|
343
351
|
conversation: entry.conversation,
|
|
344
|
-
agent: { identity: entry.agent, name:
|
|
352
|
+
agent: { identity: entry.agent, name: entry.agentName },
|
|
345
353
|
name: entry.name,
|
|
346
354
|
created: entry.created,
|
|
347
355
|
finished: entry.finished,
|
|
@@ -351,27 +359,160 @@ export function createConversationStore(deps) {
|
|
|
351
359
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
352
360
|
}));
|
|
353
361
|
}
|
|
362
|
+
async function heldList() {
|
|
363
|
+
const stored = (await (await deps.local()).facts()).conversationList;
|
|
364
|
+
if (stored !== null)
|
|
365
|
+
return stored;
|
|
366
|
+
return !listKnown && deps.stream() !== undefined && deps.online() ? null : [];
|
|
367
|
+
}
|
|
368
|
+
async function endedIn(conversation) {
|
|
369
|
+
const list = await heldList();
|
|
370
|
+
return list?.find((entry) => entry.conversation === conversation)?.processEnded ?? null;
|
|
371
|
+
}
|
|
372
|
+
function listChanged(change) {
|
|
373
|
+
void deps
|
|
374
|
+
.durably(async (local) => {
|
|
375
|
+
const { conversationList, conversationCursor } = await local.facts();
|
|
376
|
+
const kept = change.replacing ? [] : (conversationList ?? []);
|
|
377
|
+
const held = new Map(kept.map((entry) => [entry.conversation, entry]));
|
|
378
|
+
for (const entry of change.set) {
|
|
379
|
+
held.set(entry.conversation, { ...entry });
|
|
380
|
+
creations.delete(entry.conversation);
|
|
381
|
+
}
|
|
382
|
+
if (change.gone !== null)
|
|
383
|
+
held.delete(change.gone);
|
|
384
|
+
const list = [...held.values()];
|
|
385
|
+
if (JSON.stringify(list) !== JSON.stringify(conversationList)) {
|
|
386
|
+
await local.fact('conversationList', list);
|
|
387
|
+
}
|
|
388
|
+
if (change.cursor !== undefined) {
|
|
389
|
+
if (change.cursor !== conversationCursor) {
|
|
390
|
+
await local.fact('conversationCursor', change.cursor);
|
|
391
|
+
}
|
|
392
|
+
deps.stream()?.hold(change.cursor);
|
|
393
|
+
}
|
|
394
|
+
return list;
|
|
395
|
+
})
|
|
396
|
+
.catch(async () => (await (await deps.local()).facts()).conversationList ?? [])
|
|
397
|
+
.then((list) => {
|
|
398
|
+
if (deps.closed() || change.paged === true)
|
|
399
|
+
return;
|
|
400
|
+
endedAnew(list);
|
|
401
|
+
if (change.cursor !== undefined) {
|
|
402
|
+
listKnown = true;
|
|
403
|
+
settleBehind(list);
|
|
404
|
+
openingEnded();
|
|
405
|
+
deps.settled();
|
|
406
|
+
}
|
|
407
|
+
emitList();
|
|
408
|
+
if (!listKnown)
|
|
409
|
+
return;
|
|
410
|
+
retiredDrafts(list);
|
|
411
|
+
if (change.cursor === undefined &&
|
|
412
|
+
(list.length !== replicated.size ||
|
|
413
|
+
list.some(({ conversation }) => !replicated.has(conversation)))) {
|
|
414
|
+
deps.again();
|
|
415
|
+
}
|
|
416
|
+
}, () => { });
|
|
417
|
+
}
|
|
418
|
+
function endedAnew(list) {
|
|
419
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
420
|
+
for (const view of views.values()) {
|
|
421
|
+
const ended = entries.get(view.conversation)?.processEnded ?? null;
|
|
422
|
+
if (view.window === null || ended === view.ended)
|
|
423
|
+
continue;
|
|
424
|
+
view.ended = ended;
|
|
425
|
+
emit(view.conversation);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
let listShown = Promise.resolve();
|
|
429
|
+
function shownList(work) {
|
|
430
|
+
const reading = heldList();
|
|
431
|
+
listShown = listShown.then(async () => {
|
|
432
|
+
const list = await reading.catch(() => null);
|
|
433
|
+
if (list === null || deps.closed())
|
|
434
|
+
return;
|
|
435
|
+
work(list);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
354
438
|
function emitList() {
|
|
355
|
-
if (deps.closed()
|
|
439
|
+
if (deps.closed())
|
|
356
440
|
return;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
441
|
+
if (listWatchers.size > 0) {
|
|
442
|
+
shownList((list) => {
|
|
443
|
+
const value = entries(list);
|
|
444
|
+
for (const watcher of listWatchers)
|
|
445
|
+
watcher(value);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
emitAgents();
|
|
449
|
+
learned();
|
|
450
|
+
}
|
|
451
|
+
function learned() {
|
|
452
|
+
if (deps.closed() || (unlisted.size === 0 && unnamed.size === 0))
|
|
453
|
+
return;
|
|
454
|
+
shownList((list) => {
|
|
455
|
+
if (unlisted.size > 0) {
|
|
456
|
+
const value = entries(list);
|
|
457
|
+
for (const watcher of [...unlisted]) {
|
|
458
|
+
unlisted.delete(watcher);
|
|
459
|
+
listWatchers.add(watcher);
|
|
460
|
+
watcher(value);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (unnamed.size > 0) {
|
|
464
|
+
const value = agents(list);
|
|
465
|
+
for (const watcher of [...unnamed]) {
|
|
466
|
+
unnamed.delete(watcher);
|
|
467
|
+
agentWatchers.add(watcher);
|
|
468
|
+
watcher(value);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
});
|
|
360
472
|
}
|
|
361
|
-
function agents() {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
.
|
|
473
|
+
function agents(list) {
|
|
474
|
+
const named = new Map();
|
|
475
|
+
for (const agent of listedAgents ?? [])
|
|
476
|
+
named.set(agent.agent, agent);
|
|
477
|
+
for (const entry of list) {
|
|
478
|
+
named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
|
|
479
|
+
}
|
|
480
|
+
return [...named]
|
|
481
|
+
.filter(([, agent]) => !agent.retired)
|
|
482
|
+
.map(([identity, agent]) => ({ identity, name: agent.name }))
|
|
365
483
|
.sort((left, right) => left.name === right.name
|
|
366
484
|
? left.identity.localeCompare(right.identity)
|
|
367
485
|
: left.name.localeCompare(right.name));
|
|
368
486
|
}
|
|
487
|
+
function readAgents() {
|
|
488
|
+
const stream = deps.stream();
|
|
489
|
+
if (stream?.agents === undefined || !deps.online() || agentsReading)
|
|
490
|
+
return;
|
|
491
|
+
agentsReading = true;
|
|
492
|
+
void stream
|
|
493
|
+
.agents()
|
|
494
|
+
.then((read) => {
|
|
495
|
+
if (read === null)
|
|
496
|
+
return;
|
|
497
|
+
listedAgents = read;
|
|
498
|
+
emitAgents();
|
|
499
|
+
}, () => { })
|
|
500
|
+
.finally(() => {
|
|
501
|
+
agentsReading = false;
|
|
502
|
+
});
|
|
503
|
+
}
|
|
369
504
|
function emitAgents() {
|
|
370
505
|
if (deps.closed() || agentWatchers.size === 0)
|
|
371
506
|
return;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
507
|
+
shownList((list) => {
|
|
508
|
+
const value = agents(list);
|
|
509
|
+
const shown = JSON.stringify(value);
|
|
510
|
+
if (shown === agentsShown)
|
|
511
|
+
return;
|
|
512
|
+
agentsShown = shown;
|
|
513
|
+
for (const watcher of agentWatchers)
|
|
514
|
+
watcher(value);
|
|
515
|
+
});
|
|
375
516
|
}
|
|
376
517
|
function setLive(conversation, next) {
|
|
377
518
|
const before = stateOf(conversation);
|
|
@@ -380,60 +521,14 @@ export function createConversationStore(deps) {
|
|
|
380
521
|
lives.delete(conversation);
|
|
381
522
|
else
|
|
382
523
|
lives.set(conversation, next);
|
|
383
|
-
if (!listing.has(conversation))
|
|
384
|
-
return;
|
|
385
524
|
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
386
525
|
emitList();
|
|
387
526
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const retired = new Set();
|
|
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;
|
|
416
|
-
}
|
|
417
|
-
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
418
|
-
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
419
|
-
return;
|
|
420
|
-
agentNames = names;
|
|
421
|
-
retiredAgents = retired;
|
|
422
|
-
if (named)
|
|
423
|
-
emitList();
|
|
424
|
-
emitAgents();
|
|
425
|
-
const ended = [...drafts.keys()].filter(retiredChat);
|
|
426
|
-
if (ended.length > 0)
|
|
427
|
-
await remove(ended);
|
|
428
|
-
}
|
|
429
|
-
function retiredChat(conversation) {
|
|
430
|
-
return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
|
|
431
|
-
}
|
|
432
|
-
function agentsReady() {
|
|
433
|
-
agentsRead ??= readAgents().catch(() => {
|
|
434
|
-
agentsRead = null;
|
|
435
|
-
});
|
|
436
|
-
return agentsRead;
|
|
527
|
+
function retiredChat(conversation, list) {
|
|
528
|
+
if (!conversation.startsWith(NEW_CHAT))
|
|
529
|
+
return false;
|
|
530
|
+
const agent = conversation.slice(NEW_CHAT.length);
|
|
531
|
+
return list.some((entry) => entry.agent === agent && entry.agentRetired);
|
|
437
532
|
}
|
|
438
533
|
function live(conversation, change) {
|
|
439
534
|
const owed = unsent.get(conversation);
|
|
@@ -456,6 +551,7 @@ export function createConversationStore(deps) {
|
|
|
456
551
|
commands: facts.has('commands') ? QUIET.commands : held.commands,
|
|
457
552
|
options: facts.has('options') ? QUIET.options : held.options,
|
|
458
553
|
starting: facts.has('starting') ? QUIET.starting : held.starting,
|
|
554
|
+
subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
|
|
459
555
|
link: facts.has('link') ? QUIET.link : held.link,
|
|
460
556
|
deliveryUnknown: facts.has('deliveryUnknown')
|
|
461
557
|
? QUIET.deliveryUnknown
|
|
@@ -490,17 +586,23 @@ export function createConversationStore(deps) {
|
|
|
490
586
|
}
|
|
491
587
|
async function remove(leaving) {
|
|
492
588
|
await deps.durably(async (local) => {
|
|
589
|
+
const kept = new Set([
|
|
590
|
+
...(await local.conversations(leaving)).map(({ conversation }) => conversation),
|
|
591
|
+
...(await local.chatDrafts())
|
|
592
|
+
.filter(({ conversation }) => leaving.includes(conversation))
|
|
593
|
+
.map(({ conversation }) => conversation),
|
|
594
|
+
]);
|
|
493
595
|
await local.dropConversations(leaving);
|
|
494
596
|
const changed = [];
|
|
495
597
|
for (const conversation of leaving) {
|
|
496
|
-
if (
|
|
598
|
+
if (kept.has(conversation) || lives.has(conversation))
|
|
497
599
|
changed.push(conversation);
|
|
600
|
+
for (const view of views.values()) {
|
|
601
|
+
if (view.conversation === conversation)
|
|
602
|
+
view.draft = null;
|
|
498
603
|
}
|
|
499
|
-
stored.delete(conversation);
|
|
500
|
-
reads.delete(conversation);
|
|
501
|
-
partials.delete(conversation);
|
|
502
|
-
drafts.delete(conversation);
|
|
503
604
|
behind.delete(conversation);
|
|
605
|
+
heardNewest.delete(conversation);
|
|
504
606
|
unsent.delete(conversation);
|
|
505
607
|
setLive(conversation, null);
|
|
506
608
|
await slide(local, conversation, null);
|
|
@@ -509,6 +611,69 @@ export function createConversationStore(deps) {
|
|
|
509
611
|
deps.publish(conversationChangeSet(changed));
|
|
510
612
|
});
|
|
511
613
|
}
|
|
614
|
+
function newestOfListed(entry) {
|
|
615
|
+
if (entry === undefined)
|
|
616
|
+
return undefined;
|
|
617
|
+
const heard = heardNewest.get(entry.conversation);
|
|
618
|
+
if (heard === undefined)
|
|
619
|
+
return entry.newest;
|
|
620
|
+
return entry.newest === null || heard > entry.newest ? heard : entry.newest;
|
|
621
|
+
}
|
|
622
|
+
function missed(conversation, held, entry) {
|
|
623
|
+
const read = held?.read;
|
|
624
|
+
const newest = newestOfListed(entry);
|
|
625
|
+
return (read === undefined ||
|
|
626
|
+
newest === undefined ||
|
|
627
|
+
held?.partial === true ||
|
|
628
|
+
(newest !== null && (read === null || newest > read)));
|
|
629
|
+
}
|
|
630
|
+
async function readsOf(local, conversations) {
|
|
631
|
+
return new Map((await local.conversations(conversations)).map((held) => [held.conversation, held]));
|
|
632
|
+
}
|
|
633
|
+
function retiredDrafts(list) {
|
|
634
|
+
if (!list.some((entry) => entry.agentRetired))
|
|
635
|
+
return;
|
|
636
|
+
void deps
|
|
637
|
+
.local()
|
|
638
|
+
.then((local) => local.chatDrafts())
|
|
639
|
+
.then((held) => {
|
|
640
|
+
if (!deps.closed() && held.some(({ conversation }) => retiredChat(conversation, list))) {
|
|
641
|
+
deps.again();
|
|
642
|
+
}
|
|
643
|
+
}, () => { });
|
|
644
|
+
}
|
|
645
|
+
function settleBehind(list) {
|
|
646
|
+
void deps
|
|
647
|
+
.durably(async (local) => {
|
|
648
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
649
|
+
const named = [...replicated].filter((conversation) => entries.has(conversation));
|
|
650
|
+
const held = await readsOf(local, named);
|
|
651
|
+
let owed = false;
|
|
652
|
+
for (const conversation of named) {
|
|
653
|
+
if (!missed(conversation, held.get(conversation), entries.get(conversation))) {
|
|
654
|
+
behind.delete(conversation);
|
|
655
|
+
}
|
|
656
|
+
else if (!behind.has(conversation)) {
|
|
657
|
+
gap(conversation);
|
|
658
|
+
owed = true;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return owed;
|
|
662
|
+
})
|
|
663
|
+
.then((owed) => {
|
|
664
|
+
if (deps.closed())
|
|
665
|
+
return;
|
|
666
|
+
deps.settled();
|
|
667
|
+
if (owed)
|
|
668
|
+
deps.again();
|
|
669
|
+
}, () => { });
|
|
670
|
+
}
|
|
671
|
+
function noted(conversation, message) {
|
|
672
|
+
const heard = heardNewest.get(conversation);
|
|
673
|
+
if (heard === undefined || message.position > heard) {
|
|
674
|
+
heardNewest.set(conversation, message.position);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
512
677
|
function gap(conversation) {
|
|
513
678
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
514
679
|
behind.add(conversation);
|
|
@@ -517,7 +682,7 @@ export function createConversationStore(deps) {
|
|
|
517
682
|
return deps.durably(async (local) => {
|
|
518
683
|
if (!replicated.has(conversation))
|
|
519
684
|
return;
|
|
520
|
-
const read =
|
|
685
|
+
const read = (await readsOf(local, [conversation])).get(conversation)?.read;
|
|
521
686
|
const advances = read !== undefined &&
|
|
522
687
|
!behind.has(conversation) &&
|
|
523
688
|
(read === null || message.position > read);
|
|
@@ -527,9 +692,6 @@ export function createConversationStore(deps) {
|
|
|
527
692
|
messages: [message],
|
|
528
693
|
...(advances ? { read: { at: message.position, partial: false } } : {}),
|
|
529
694
|
});
|
|
530
|
-
stored.add(conversation);
|
|
531
|
-
if (advances)
|
|
532
|
-
reads.set(conversation, message.position);
|
|
533
695
|
sealTurns(conversation, new Set([message.turn]));
|
|
534
696
|
await slide(local, conversation, [message]);
|
|
535
697
|
deps.publish(conversationChangeSet([conversation]));
|
|
@@ -537,13 +699,14 @@ export function createConversationStore(deps) {
|
|
|
537
699
|
}
|
|
538
700
|
async function readHistories(conversations, ended) {
|
|
539
701
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
702
|
+
const held = await readsOf(await deps.local(), conversations);
|
|
540
703
|
let waiting = conversations.map((conversation) => ({
|
|
541
704
|
conversation,
|
|
542
|
-
after:
|
|
705
|
+
after: held.get(conversation)?.read ?? undefined,
|
|
543
706
|
}));
|
|
544
707
|
while (waiting.length > 0) {
|
|
545
708
|
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
546
|
-
const result = await deps.
|
|
709
|
+
const result = await deps.history({
|
|
547
710
|
conversations: asking.map(({ conversation, after }) => ({
|
|
548
711
|
conversation,
|
|
549
712
|
...(after === undefined ? {} : { after }),
|
|
@@ -595,15 +758,10 @@ export function createConversationStore(deps) {
|
|
|
595
758
|
return;
|
|
596
759
|
const whole = next === undefined;
|
|
597
760
|
const cursor = next ?? newestOf(arriving, from);
|
|
598
|
-
const
|
|
761
|
+
const held = (await readsOf(local, [conversation])).get(conversation);
|
|
762
|
+
const partial = !whole && (held?.partial === true || held?.read === undefined);
|
|
599
763
|
const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
|
|
600
764
|
await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
|
|
601
|
-
stored.add(conversation);
|
|
602
|
-
reads.set(conversation, cursor);
|
|
603
|
-
if (partial)
|
|
604
|
-
partials.add(conversation);
|
|
605
|
-
else
|
|
606
|
-
partials.delete(conversation);
|
|
607
765
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
608
766
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
609
767
|
behind.delete(conversation);
|
|
@@ -646,6 +804,10 @@ export function createConversationStore(deps) {
|
|
|
646
804
|
live(conversation, { starting: event.step });
|
|
647
805
|
return;
|
|
648
806
|
}
|
|
807
|
+
if (event.kind === 'subagents') {
|
|
808
|
+
live(conversation, { subagents: structuredClone(event.subagents) });
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
649
811
|
if (event.kind === 'link') {
|
|
650
812
|
live(conversation, { link: { connected: event.connected } });
|
|
651
813
|
return;
|
|
@@ -657,6 +819,7 @@ export function createConversationStore(deps) {
|
|
|
657
819
|
const arriving = acceptedMessage(event.message);
|
|
658
820
|
if (arriving === null)
|
|
659
821
|
return;
|
|
822
|
+
noted(conversation, arriving);
|
|
660
823
|
if (replicated.has(conversation)) {
|
|
661
824
|
if (deps.full()) {
|
|
662
825
|
gap(conversation);
|
|
@@ -672,17 +835,24 @@ export function createConversationStore(deps) {
|
|
|
672
835
|
emit(conversation);
|
|
673
836
|
}
|
|
674
837
|
async function kept(local, conversation, following) {
|
|
675
|
-
|
|
838
|
+
const shown = following === null || empty(following) ? null : following;
|
|
839
|
+
if (shown === null)
|
|
676
840
|
await local.dropChatDraft(conversation);
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
841
|
+
else
|
|
842
|
+
await local.keepChatDrafts([shown]);
|
|
843
|
+
shownDraft(conversation, shown);
|
|
844
|
+
}
|
|
845
|
+
function shownDraft(conversation, draft) {
|
|
846
|
+
for (const view of views.values()) {
|
|
847
|
+
if (view.conversation === conversation)
|
|
848
|
+
view.draft = draft;
|
|
682
849
|
}
|
|
683
850
|
}
|
|
851
|
+
async function draftAt(local, conversation) {
|
|
852
|
+
return (await local.chatDraft(conversation)) ?? undefined;
|
|
853
|
+
}
|
|
684
854
|
async function sealed(local, conversation, turns) {
|
|
685
|
-
const held =
|
|
855
|
+
const held = await draftAt(local, conversation);
|
|
686
856
|
const submitted = held?.submitted ?? [];
|
|
687
857
|
const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
|
|
688
858
|
if (held === undefined || left.length === submitted.length)
|
|
@@ -693,8 +863,8 @@ export function createConversationStore(deps) {
|
|
|
693
863
|
function moved(agent, operation, to) {
|
|
694
864
|
const from = newChat(agent);
|
|
695
865
|
return deps.durably(async (local) => {
|
|
696
|
-
const held =
|
|
697
|
-
const earlier =
|
|
866
|
+
const held = await draftAt(local, from);
|
|
867
|
+
const earlier = held?.moves ?? [];
|
|
698
868
|
if (earlier.includes(operation))
|
|
699
869
|
return;
|
|
700
870
|
const blank = {
|
|
@@ -703,18 +873,20 @@ export function createConversationStore(deps) {
|
|
|
703
873
|
revision: chatDraftRevision(),
|
|
704
874
|
};
|
|
705
875
|
const { moves: _answered, ...unsent } = held ?? blank;
|
|
706
|
-
const taken =
|
|
876
|
+
const taken = (await draftAt(local, to)) !== undefined || empty(unsent)
|
|
877
|
+
? []
|
|
878
|
+
: [{ ...unsent, conversation: to }];
|
|
707
879
|
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
708
880
|
await local.keepChatDrafts([left, ...taken]);
|
|
709
|
-
|
|
881
|
+
shownDraft(from, left);
|
|
710
882
|
for (const one of taken)
|
|
711
|
-
|
|
883
|
+
shownDraft(to, one);
|
|
712
884
|
deps.publish(conversationChangeSet([from, to]));
|
|
713
885
|
});
|
|
714
886
|
}
|
|
715
887
|
function withDraft(conversation, next) {
|
|
716
888
|
return deps.durably(async (local) => {
|
|
717
|
-
const following = await next(
|
|
889
|
+
const following = await next(await draftAt(local, conversation), local);
|
|
718
890
|
if (following === undefined)
|
|
719
891
|
return;
|
|
720
892
|
await kept(local, conversation, following);
|
|
@@ -723,10 +895,8 @@ export function createConversationStore(deps) {
|
|
|
723
895
|
}
|
|
724
896
|
async function submitted(conversation, operation, draft) {
|
|
725
897
|
let submission;
|
|
726
|
-
let earlier = false;
|
|
727
898
|
await withDraft(conversation, (current) => {
|
|
728
899
|
submission = current?.submitted?.find((one) => one.operation === operation);
|
|
729
|
-
earlier = submission !== undefined;
|
|
730
900
|
if (submission !== undefined)
|
|
731
901
|
return undefined;
|
|
732
902
|
const said = draft ?? current;
|
|
@@ -739,23 +909,15 @@ export function createConversationStore(deps) {
|
|
|
739
909
|
submitted: [...(current?.submitted ?? []), submission],
|
|
740
910
|
};
|
|
741
911
|
});
|
|
742
|
-
return
|
|
743
|
-
}
|
|
744
|
-
function forgotten(conversation, submission) {
|
|
745
|
-
return withDraft(conversation, (current) => {
|
|
746
|
-
const held = current?.submitted ?? [];
|
|
747
|
-
if (current === undefined || !held.includes(submission))
|
|
748
|
-
return undefined;
|
|
749
|
-
return withSubmitted(current, held.filter((one) => one !== submission));
|
|
750
|
-
});
|
|
912
|
+
return submission;
|
|
751
913
|
}
|
|
752
914
|
async function dispatched(conversation, submission) {
|
|
753
915
|
const dispatch = { answered: false };
|
|
754
916
|
sending.set(submission.operation, dispatch);
|
|
755
917
|
try {
|
|
756
|
-
const result = await invoke('
|
|
757
|
-
text: submission.text,
|
|
918
|
+
const result = await invoke('conversations/send', conversation, {
|
|
758
919
|
operation_id: submission.operation,
|
|
920
|
+
text: submission.text,
|
|
759
921
|
});
|
|
760
922
|
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
761
923
|
if (result.kind === 'revoked-authorization')
|
|
@@ -764,7 +926,7 @@ export function createConversationStore(deps) {
|
|
|
764
926
|
deps.unanswered();
|
|
765
927
|
return result;
|
|
766
928
|
}
|
|
767
|
-
const turn = answered(result, '
|
|
929
|
+
const turn = answered(result, 'message_id');
|
|
768
930
|
await withDraft(conversation, async (current, local) => {
|
|
769
931
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
770
932
|
if (current === undefined || held === undefined || held.turn !== null)
|
|
@@ -784,29 +946,40 @@ export function createConversationStore(deps) {
|
|
|
784
946
|
sending.delete(submission.operation);
|
|
785
947
|
}
|
|
786
948
|
}
|
|
787
|
-
function invoke(name, conversation, extra = {}) {
|
|
788
|
-
return deps.call(name, { conversation, ...extra });
|
|
949
|
+
function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
|
|
950
|
+
return deps.call(name, { conversation_id: conversation, ...extra });
|
|
789
951
|
}
|
|
790
952
|
const surface = {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
953
|
+
list() {
|
|
954
|
+
return new Promise((settle) => {
|
|
955
|
+
const answer = (listed) => {
|
|
956
|
+
unlisted.delete(answer);
|
|
957
|
+
listWatchers.delete(answer);
|
|
958
|
+
settle(listed);
|
|
959
|
+
};
|
|
960
|
+
unlisted.add(answer);
|
|
961
|
+
learned();
|
|
962
|
+
});
|
|
794
963
|
},
|
|
795
964
|
async read(conversation, options = {}) {
|
|
796
|
-
const
|
|
965
|
+
const { text, ...windowed } = options;
|
|
966
|
+
const asked = askedOf(windowed);
|
|
967
|
+
const phrase = phraseOf(text);
|
|
797
968
|
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)));
|
|
969
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
|
|
970
|
+
const total = asked.total ? await local.messageCount(conversation, phrase) : null;
|
|
971
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total), (await local.chatDraft(conversation)) ?? null, await endedIn(conversation)));
|
|
801
972
|
});
|
|
802
973
|
},
|
|
803
974
|
async create(input) {
|
|
804
|
-
const result = await deps.call('
|
|
805
|
-
const conversation = answered(result, '
|
|
975
|
+
const result = await deps.call('conversations/create', input);
|
|
976
|
+
const conversation = answered(result, 'conversation_id');
|
|
806
977
|
if (conversation === null)
|
|
807
978
|
return result;
|
|
808
|
-
|
|
979
|
+
const listed = (await heldList()) ?? [];
|
|
980
|
+
if (!listed.some((entry) => entry.conversation === conversation)) {
|
|
809
981
|
creations.add(conversation);
|
|
982
|
+
}
|
|
810
983
|
const creation = creationOf(input);
|
|
811
984
|
if (creation !== null)
|
|
812
985
|
await moved(creation.agent, creation.operation, conversation);
|
|
@@ -823,11 +996,7 @@ export function createConversationStore(deps) {
|
|
|
823
996
|
});
|
|
824
997
|
if (held === null)
|
|
825
998
|
return DURABLE_WRITE;
|
|
826
|
-
|
|
827
|
-
if (!earlier || !deps.expired(operation))
|
|
828
|
-
return dispatched(conversation, submission);
|
|
829
|
-
await forgotten(conversation, submission);
|
|
830
|
-
return EXPIRED;
|
|
999
|
+
return dispatched(conversation, held);
|
|
831
1000
|
}
|
|
832
1001
|
if (draft !== undefined) {
|
|
833
1002
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
@@ -836,38 +1005,20 @@ export function createConversationStore(deps) {
|
|
|
836
1005
|
}
|
|
837
1006
|
return OFFLINE;
|
|
838
1007
|
},
|
|
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
|
-
},
|
|
848
1008
|
answer(conversation, answer) {
|
|
849
|
-
return invoke('
|
|
1009
|
+
return invoke('conversations/interaction/answer', conversation, {
|
|
1010
|
+
operation_id: changeBatchIdentity(),
|
|
1011
|
+
...answer,
|
|
1012
|
+
});
|
|
850
1013
|
},
|
|
851
1014
|
interrupt(conversation) {
|
|
852
|
-
return invoke('
|
|
1015
|
+
return invoke('conversations/interrupt', conversation);
|
|
853
1016
|
},
|
|
854
1017
|
kill(conversation) {
|
|
855
|
-
return invoke('
|
|
856
|
-
},
|
|
857
|
-
async cancel(conversation, turn) {
|
|
858
|
-
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
859
|
-
if (result.kind !== 'action')
|
|
860
|
-
return result;
|
|
861
|
-
await withDraft(conversation, (current) => {
|
|
862
|
-
const held = current?.submitted?.find((one) => one.turn === turn);
|
|
863
|
-
if (current === undefined || held === undefined)
|
|
864
|
-
return undefined;
|
|
865
|
-
return returned(current, held, current.submitted.filter((one) => one !== held));
|
|
866
|
-
});
|
|
867
|
-
return result;
|
|
1018
|
+
return invoke('conversations/kill', conversation);
|
|
868
1019
|
},
|
|
869
1020
|
delete(conversation) {
|
|
870
|
-
return invoke('
|
|
1021
|
+
return invoke('conversations/delete', conversation);
|
|
871
1022
|
},
|
|
872
1023
|
};
|
|
873
1024
|
const chatDraft = {
|
|
@@ -896,33 +1047,40 @@ export function createConversationStore(deps) {
|
|
|
896
1047
|
return {
|
|
897
1048
|
surface,
|
|
898
1049
|
chatDraft,
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
if (entry.read !== undefined)
|
|
906
|
-
reads.set(entry.conversation, entry.read);
|
|
907
|
-
if (entry.partial)
|
|
908
|
-
partials.add(entry.conversation);
|
|
909
|
-
}
|
|
910
|
-
drafts.clear();
|
|
911
|
-
for (const record of kept)
|
|
912
|
-
drafts.set(record.conversation, record);
|
|
1050
|
+
async opened(local) {
|
|
1051
|
+
const { conversationList, conversationCursor } = await local.facts();
|
|
1052
|
+
if (conversationList === null || listKnown)
|
|
1053
|
+
return;
|
|
1054
|
+
deps.stream()?.hold(conversationCursor);
|
|
1055
|
+
learned();
|
|
913
1056
|
},
|
|
914
1057
|
async reconcile() {
|
|
915
1058
|
if (!listKnown)
|
|
916
1059
|
return;
|
|
917
|
-
const
|
|
1060
|
+
const local = await deps.local();
|
|
1061
|
+
const [held, kept, { conversationList }] = await Promise.all([
|
|
1062
|
+
local.conversations(),
|
|
1063
|
+
local.chatDrafts(),
|
|
1064
|
+
local.facts(),
|
|
1065
|
+
]);
|
|
1066
|
+
const list = conversationList ?? [];
|
|
1067
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
1068
|
+
const named = new Set(entries.keys());
|
|
1069
|
+
const reads = new Map(held.map((one) => [one.conversation, one]));
|
|
918
1070
|
const leaving = [
|
|
919
|
-
...new Set([
|
|
1071
|
+
...new Set([
|
|
1072
|
+
...replicated,
|
|
1073
|
+
...reads.keys(),
|
|
1074
|
+
...kept.map(({ conversation }) => conversation),
|
|
1075
|
+
]),
|
|
920
1076
|
].filter((conversation) => !named.has(conversation) &&
|
|
921
1077
|
!creations.has(conversation) &&
|
|
922
|
-
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
1078
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation, list)));
|
|
923
1079
|
for (const conversation of named) {
|
|
924
|
-
if (!replicated.has(conversation)
|
|
1080
|
+
if (!replicated.has(conversation) &&
|
|
1081
|
+
missed(conversation, reads.get(conversation), entries.get(conversation))) {
|
|
925
1082
|
behind.add(conversation);
|
|
1083
|
+
}
|
|
926
1084
|
}
|
|
927
1085
|
replicated = named;
|
|
928
1086
|
handReplicated();
|
|
@@ -953,16 +1111,16 @@ export function createConversationStore(deps) {
|
|
|
953
1111
|
progress.complete = false;
|
|
954
1112
|
},
|
|
955
1113
|
async resend(progress) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1114
|
+
const local = await deps.local();
|
|
1115
|
+
const tried = new Set();
|
|
1116
|
+
for (const { conversation } of await local.chatDrafts()) {
|
|
1117
|
+
for (;;) {
|
|
1118
|
+
const submission = (await local.chatDraft(conversation))?.submitted?.find((one) => one.turn === null && !sending.has(one.operation) && !tried.has(one.operation));
|
|
1119
|
+
if (submission === undefined)
|
|
1120
|
+
break;
|
|
960
1121
|
if (deps.closed())
|
|
961
1122
|
return;
|
|
962
|
-
|
|
963
|
-
await forgotten(conversation, submission);
|
|
964
|
-
continue;
|
|
965
|
-
}
|
|
1123
|
+
tried.add(submission.operation);
|
|
966
1124
|
const result = await dispatched(conversation, submission);
|
|
967
1125
|
if (result.kind === 'revoked-authorization')
|
|
968
1126
|
return;
|
|
@@ -983,11 +1141,8 @@ export function createConversationStore(deps) {
|
|
|
983
1141
|
known() {
|
|
984
1142
|
return listKnown;
|
|
985
1143
|
},
|
|
986
|
-
reconnected() {
|
|
987
|
-
for (const conversation of replicated)
|
|
988
|
-
gap(conversation);
|
|
989
|
-
},
|
|
990
1144
|
reopened() {
|
|
1145
|
+
relisting = null;
|
|
991
1146
|
for (const [operation, dispatch] of sending)
|
|
992
1147
|
if (!dispatch.answered)
|
|
993
1148
|
sending.delete(operation);
|
|
@@ -1003,30 +1158,35 @@ export function createConversationStore(deps) {
|
|
|
1003
1158
|
asked,
|
|
1004
1159
|
watchers: new Set(),
|
|
1005
1160
|
window: null,
|
|
1161
|
+
draft: null,
|
|
1162
|
+
ended: null,
|
|
1006
1163
|
more: false,
|
|
1007
1164
|
total: null,
|
|
1008
1165
|
};
|
|
1009
1166
|
views.set(key, view);
|
|
1010
1167
|
const watcher = { notify: deliver, ready: false };
|
|
1011
1168
|
view.watchers.add(watcher);
|
|
1012
|
-
|
|
1013
|
-
|
|
1169
|
+
const reading = new AbortController();
|
|
1170
|
+
void untilAnswered(() => deps.durably(async (local) => {
|
|
1014
1171
|
if (view.window !== null)
|
|
1015
1172
|
return;
|
|
1016
1173
|
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1174
|
+
view.draft = (await local.chatDraft(conversation)) ?? null;
|
|
1175
|
+
view.ended = await endedIn(conversation);
|
|
1017
1176
|
view.window = messages;
|
|
1018
1177
|
view.more = more;
|
|
1019
1178
|
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
1020
|
-
})
|
|
1179
|
+
}), reading.signal)
|
|
1021
1180
|
.then(() => {
|
|
1022
1181
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
1023
1182
|
return;
|
|
1024
1183
|
watcher.ready = true;
|
|
1025
1184
|
const window = view.window ?? [];
|
|
1026
|
-
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
1185
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window), view.draft, view.ended));
|
|
1027
1186
|
})
|
|
1028
1187
|
.catch(() => { });
|
|
1029
1188
|
return () => {
|
|
1189
|
+
reading.abort();
|
|
1030
1190
|
if (!view.watchers.delete(watcher))
|
|
1031
1191
|
return;
|
|
1032
1192
|
if (view.watchers.size === 0)
|
|
@@ -1034,77 +1194,69 @@ export function createConversationStore(deps) {
|
|
|
1034
1194
|
};
|
|
1035
1195
|
},
|
|
1036
1196
|
watchList(deliver) {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (!deps.closed() && listWatchers.has(deliver))
|
|
1040
|
-
deliver(entries());
|
|
1041
|
-
});
|
|
1197
|
+
unlisted.add(deliver);
|
|
1198
|
+
learned();
|
|
1042
1199
|
return () => {
|
|
1200
|
+
unlisted.delete(deliver);
|
|
1043
1201
|
listWatchers.delete(deliver);
|
|
1044
1202
|
};
|
|
1045
1203
|
},
|
|
1046
1204
|
watchAgents(deliver) {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
deliver(agents());
|
|
1051
|
-
});
|
|
1205
|
+
unnamed.add(deliver);
|
|
1206
|
+
learned();
|
|
1207
|
+
readAgents();
|
|
1052
1208
|
return () => {
|
|
1209
|
+
unnamed.delete(deliver);
|
|
1053
1210
|
agentWatchers.delete(deliver);
|
|
1054
1211
|
};
|
|
1055
1212
|
},
|
|
1213
|
+
learned,
|
|
1056
1214
|
changed(set) {
|
|
1057
1215
|
for (const conversation of set.conversations)
|
|
1058
1216
|
emit(conversation);
|
|
1059
|
-
const declaration = deps.agents();
|
|
1060
|
-
if (declaration !== undefined &&
|
|
1061
|
-
set.documents.some((change) => change.locator === declaration.locator)) {
|
|
1062
|
-
agentsRead = readAgents().catch(() => {
|
|
1063
|
-
agentsRead = null;
|
|
1064
|
-
});
|
|
1065
|
-
}
|
|
1066
1217
|
},
|
|
1067
1218
|
receive(event) {
|
|
1068
1219
|
if (deps.closed())
|
|
1069
|
-
return
|
|
1070
|
-
if (event.kind === 'list'
|
|
1071
|
-
if (event.
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
}
|
|
1077
|
-
listKnown = true;
|
|
1078
|
-
openingEnded();
|
|
1079
|
-
}
|
|
1080
|
-
else if (event.kind === 'listed') {
|
|
1081
|
-
listing.set(event.entry.conversation, { ...event.entry });
|
|
1082
|
-
creations.delete(event.entry.conversation);
|
|
1083
|
-
}
|
|
1084
|
-
else {
|
|
1085
|
-
listing.delete(event.conversation);
|
|
1220
|
+
return;
|
|
1221
|
+
if (event.kind === 'list') {
|
|
1222
|
+
if (event.whole)
|
|
1223
|
+
relisting ??= new Map();
|
|
1224
|
+
if (relisting !== null) {
|
|
1225
|
+
for (const entry of event.entries)
|
|
1226
|
+
relisting.set(entry.conversation, { ...entry });
|
|
1086
1227
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1228
|
+
if (event.more && relisting !== null)
|
|
1229
|
+
return;
|
|
1230
|
+
const whole = relisting;
|
|
1231
|
+
relisting = null;
|
|
1232
|
+
listChanged({
|
|
1233
|
+
replacing: whole !== null,
|
|
1234
|
+
set: whole === null ? event.entries : [...whole.values()],
|
|
1235
|
+
gone: null,
|
|
1236
|
+
...(event.more ? { paged: true } : { cursor: event.cursor }),
|
|
1237
|
+
});
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
if (event.kind === 'listed') {
|
|
1241
|
+
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
1242
|
+
listChanged({ replacing: false, set: [event.entry], gone: null });
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
if (event.kind === 'unlisted') {
|
|
1246
|
+
relisting?.delete(event.conversation);
|
|
1247
|
+
listChanged({ replacing: false, set: [], gone: event.conversation });
|
|
1248
|
+
return;
|
|
1092
1249
|
}
|
|
1093
1250
|
heard(event);
|
|
1094
|
-
return false;
|
|
1095
|
-
},
|
|
1096
|
-
searchable() {
|
|
1097
|
-
return [...(listKnown ? listing.keys() : stored)].sort();
|
|
1098
|
-
},
|
|
1099
|
-
acquired(conversation) {
|
|
1100
|
-
return reads.has(conversation) && !partials.has(conversation);
|
|
1101
1251
|
},
|
|
1102
1252
|
close() {
|
|
1103
1253
|
views.clear();
|
|
1104
1254
|
lives.clear();
|
|
1105
1255
|
unsent.clear();
|
|
1106
1256
|
listWatchers.clear();
|
|
1257
|
+
unlisted.clear();
|
|
1107
1258
|
agentWatchers.clear();
|
|
1259
|
+
unnamed.clear();
|
|
1108
1260
|
},
|
|
1109
1261
|
};
|
|
1110
1262
|
}
|