@agentshouse/mdruntime 0.1.0-c588e78cf6975 → 0.1.0-c59a5a6bd2f31
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/acceptance.d.ts +26 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +384 -0
- package/dist/adapter.d.ts +38 -25
- 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 +21 -56
- package/dist/bootstrap.d.ts +8 -4
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +209 -78
- 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/chat-types.d.ts +3 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +2 -0
- package/dist/client.d.ts +14 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -0
- package/dist/collection.d.ts +2 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/compile.d.ts +7 -7
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +229 -297
- package/dist/connection.d.ts +19 -43
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1325 -3586
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation-row.d.ts +33 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +47 -0
- package/dist/conversation.d.ts +18 -9
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +375 -224
- package/dist/durability.d.ts +6 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +2 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +17 -8
- package/dist/figures.d.ts +48 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +197 -0
- package/dist/host.d.ts +4 -5
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +22 -21
- 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 +18 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/install.d.ts +86 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +537 -0
- package/dist/job.d.ts +33 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +54 -0
- package/dist/live.d.ts +5 -4
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +62 -20
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/memory.d.ts +4 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +43 -4
- package/dist/messages.d.ts +27 -3
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +97 -19
- package/dist/model.d.ts +2 -9
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -9
- package/dist/observation.d.ts +2 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +7 -2
- package/dist/outcome.d.ts +1 -0
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +3 -0
- package/dist/overlay.d.ts +1 -0
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +11 -0
- package/dist/parts.d.ts +59 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +118 -0
- package/dist/query.d.ts +13 -210
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -508
- package/dist/questions.d.ts +32 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +118 -0
- package/dist/reach.d.ts +11 -11
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +166 -113
- package/dist/react.d.ts +4 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/read.d.ts +7 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/read.js +17 -0
- package/dist/readiness.d.ts +0 -2
- package/dist/readiness.d.ts.map +1 -1
- package/dist/reads.d.ts +35 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +128 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +9 -1
- package/dist/refresh.d.ts +1 -1
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +8 -20
- package/dist/replica.d.ts +8 -8
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +50 -18
- package/dist/room-draft.d.ts +3 -2
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +4 -4
- package/dist/room-drafts.d.ts +29 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +216 -0
- package/dist/rows.d.ts +14 -22
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +48 -189
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +11 -8
- package/dist/saves.d.ts +96 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +999 -0
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/seed.d.ts +16 -2
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +66 -0
- package/dist/socket/admission.d.ts +7 -0
- package/dist/socket/admission.d.ts.map +1 -0
- package/dist/socket/admission.js +39 -0
- package/dist/socket/answers.d.ts +6 -0
- package/dist/socket/answers.d.ts.map +1 -0
- package/dist/socket/answers.js +20 -0
- package/dist/socket/reads.d.ts +10 -0
- package/dist/socket/reads.d.ts.map +1 -0
- package/dist/socket/reads.js +31 -0
- package/dist/socket.d.ts +43 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +96 -0
- package/dist/source.d.ts +61 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +81 -0
- package/dist/status.d.ts +0 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +0 -6
- package/dist/store/fences.d.ts +16 -0
- package/dist/store/fences.d.ts.map +1 -0
- package/dist/store/fences.js +26 -0
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +34 -0
- package/dist/store/records.d.ts +212 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +408 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- package/dist/store/watchers.d.ts +15 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +36 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +78 -156
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +117 -401
- package/dist/stored.d.ts +58 -65
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +264 -243
- package/dist/tables.d.ts +107 -51
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +193 -59
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +129 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +7 -4
- 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 +14 -0
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +5 -0
- 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 -370
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -156
package/dist/conversation.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
|
|
1
2
|
import { conversationChangeSet } from './change-set.js';
|
|
3
|
+
import { heldFacts } from './conversation-row.js';
|
|
2
4
|
import { runtimeError } from './failure.js';
|
|
3
5
|
import { refusedForSpace } from './durability.js';
|
|
4
6
|
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
5
7
|
import { OFFLINE } from './outcome.js';
|
|
8
|
+
import { jobsOf, runningLine } from './parts.js';
|
|
9
|
+
import { openQuestion } from './questions.js';
|
|
6
10
|
import { untilAnswered } from './recovery.js';
|
|
7
|
-
import { capped,
|
|
11
|
+
import { capped, FROM_START, messageOrder, precedes } from './windows.js';
|
|
8
12
|
const QUIET = {
|
|
9
13
|
turn: null,
|
|
10
14
|
questions: [],
|
|
@@ -33,6 +37,7 @@ export function unheldView(conversation) {
|
|
|
33
37
|
turn: null,
|
|
34
38
|
questions: [],
|
|
35
39
|
process: null,
|
|
40
|
+
processEnded: null,
|
|
36
41
|
commands: [],
|
|
37
42
|
options: [],
|
|
38
43
|
starting: null,
|
|
@@ -40,21 +45,22 @@ export function unheldView(conversation) {
|
|
|
40
45
|
link: null,
|
|
41
46
|
deliveryUnknown: [],
|
|
42
47
|
chatDraft: null,
|
|
48
|
+
jobs: [],
|
|
43
49
|
};
|
|
44
50
|
}
|
|
45
51
|
export const SESSION_STARTED = 'Session started';
|
|
46
52
|
const NEW_CHAT = 'new:';
|
|
47
53
|
export const HISTORY_CONVERSATIONS = 64;
|
|
54
|
+
// A row is read at a glance and each telling clones the list to every observing tab, while a running line changes at each Kit flush.
|
|
55
|
+
const ROW_INTERVAL_MS = 1_000;
|
|
48
56
|
export function newChat(agent) {
|
|
49
57
|
return `${NEW_CHAT}${agent}`;
|
|
50
58
|
}
|
|
51
59
|
function creationOf(input) {
|
|
52
60
|
if (typeof input !== 'object' || input === null)
|
|
53
61
|
return null;
|
|
54
|
-
const { agent_id: agent, operation_id: operation
|
|
55
|
-
return typeof agent === 'string' && typeof operation === 'string'
|
|
56
|
-
? { agent, operation }
|
|
57
|
-
: null;
|
|
62
|
+
const { agent_id: agent, operation_id: operation } = input;
|
|
63
|
+
return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
|
|
58
64
|
}
|
|
59
65
|
function startedTurn(turn, operation) {
|
|
60
66
|
if (operation.op === 'begin')
|
|
@@ -213,7 +219,14 @@ function askedOf(options) {
|
|
|
213
219
|
}
|
|
214
220
|
return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
|
|
215
221
|
}
|
|
216
|
-
|
|
222
|
+
function phraseOf(text) {
|
|
223
|
+
if (text === undefined)
|
|
224
|
+
return null;
|
|
225
|
+
if (typeof text !== 'string' || text === '') {
|
|
226
|
+
throw runtimeError('invalid-text', 'a conversation is found by a phrase');
|
|
227
|
+
}
|
|
228
|
+
return text;
|
|
229
|
+
}
|
|
217
230
|
const DURABLE_WRITE = {
|
|
218
231
|
kind: 'rejected',
|
|
219
232
|
code: 'durable-write',
|
|
@@ -247,10 +260,6 @@ function returned(draft, submission, others) {
|
|
|
247
260
|
}, others);
|
|
248
261
|
}
|
|
249
262
|
export function createConversationStore(deps) {
|
|
250
|
-
const stored = new Set();
|
|
251
|
-
const reads = new Map();
|
|
252
|
-
const partials = new Set();
|
|
253
|
-
const drafts = new Map();
|
|
254
263
|
const sending = new Map();
|
|
255
264
|
const behind = new Set();
|
|
256
265
|
const catching = new Map();
|
|
@@ -258,15 +267,16 @@ export function createConversationStore(deps) {
|
|
|
258
267
|
const lives = new Map();
|
|
259
268
|
const unsent = new Map();
|
|
260
269
|
const views = new Map();
|
|
261
|
-
const
|
|
270
|
+
const heardNewest = new Map();
|
|
262
271
|
let relisting = null;
|
|
263
|
-
let listCursor = null;
|
|
264
272
|
const creations = new Set();
|
|
265
273
|
const listWatchers = new Set();
|
|
266
274
|
const unlisted = new Set();
|
|
267
275
|
let listKnown = false;
|
|
268
|
-
let
|
|
269
|
-
let
|
|
276
|
+
let listOwed = false;
|
|
277
|
+
let listTelling = false;
|
|
278
|
+
let rowTimer = null;
|
|
279
|
+
const closing = new AbortController();
|
|
270
280
|
const agentWatchers = new Set();
|
|
271
281
|
const unnamed = new Set();
|
|
272
282
|
let agentsShown = null;
|
|
@@ -277,8 +287,7 @@ export function createConversationStore(deps) {
|
|
|
277
287
|
function liveOf(conversation) {
|
|
278
288
|
return lives.get(conversation) ?? QUIET;
|
|
279
289
|
}
|
|
280
|
-
function viewOf(conversation, held, frame) {
|
|
281
|
-
const draft = drafts.get(conversation);
|
|
290
|
+
function viewOf(conversation, held, frame, draft, ended, marked) {
|
|
282
291
|
const facts = liveOf(conversation);
|
|
283
292
|
return {
|
|
284
293
|
conversation,
|
|
@@ -292,15 +301,17 @@ export function createConversationStore(deps) {
|
|
|
292
301
|
turn: structuredClone(facts.turn),
|
|
293
302
|
questions: structuredClone(facts.questions),
|
|
294
303
|
process: facts.process === null ? null : { ...facts.process },
|
|
304
|
+
processEnded: ended,
|
|
295
305
|
commands: structuredClone(facts.commands),
|
|
296
306
|
options: structuredClone(facts.options),
|
|
297
307
|
starting: facts.starting,
|
|
298
308
|
subagents: structuredClone(facts.subagents),
|
|
299
309
|
link: facts.link === null ? null : { ...facts.link },
|
|
300
310
|
deliveryUnknown: [...facts.deliveryUnknown],
|
|
301
|
-
chatDraft: draft ===
|
|
302
|
-
|
|
303
|
-
|
|
311
|
+
chatDraft: draft === null || unwritten(draft) ? null : { text: draft.text, revision: draft.revision },
|
|
312
|
+
jobs: facts.process?.running === false
|
|
313
|
+
? []
|
|
314
|
+
: jobsOf(marked.filter(({ at }) => ended === null || at > ended), facts.turn),
|
|
304
315
|
};
|
|
305
316
|
}
|
|
306
317
|
function emit(conversation) {
|
|
@@ -312,7 +323,7 @@ export function createConversationStore(deps) {
|
|
|
312
323
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
313
324
|
if (ready.length === 0)
|
|
314
325
|
continue;
|
|
315
|
-
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
326
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window), view.draft, view.ended, view.marked);
|
|
316
327
|
for (const watcher of ready)
|
|
317
328
|
watcher.notify(value);
|
|
318
329
|
}
|
|
@@ -322,11 +333,13 @@ export function createConversationStore(deps) {
|
|
|
322
333
|
}
|
|
323
334
|
async function slide(local, conversation, arriving) {
|
|
324
335
|
let count;
|
|
336
|
+
let marked;
|
|
325
337
|
for (const view of views.values()) {
|
|
326
338
|
if (view.conversation !== conversation || view.window === null)
|
|
327
339
|
continue;
|
|
328
340
|
if (arriving === null) {
|
|
329
341
|
view.window = [];
|
|
342
|
+
view.marked = [];
|
|
330
343
|
view.more = false;
|
|
331
344
|
if (view.total !== null)
|
|
332
345
|
view.total = 0;
|
|
@@ -336,6 +349,10 @@ export function createConversationStore(deps) {
|
|
|
336
349
|
const next = windowOf(view.window, joining, view.asked.limit);
|
|
337
350
|
view.window = next.messages;
|
|
338
351
|
view.more ||= next.dropped;
|
|
352
|
+
if (arriving.length > 0) {
|
|
353
|
+
marked ??= await local.jobMarkers(conversation);
|
|
354
|
+
view.marked = marked;
|
|
355
|
+
}
|
|
339
356
|
if (view.total !== null && arriving.length > 0) {
|
|
340
357
|
count ??= await local.messageCount(conversation);
|
|
341
358
|
view.total = count;
|
|
@@ -348,8 +365,24 @@ export function createConversationStore(deps) {
|
|
|
348
365
|
return 'asking';
|
|
349
366
|
return facts.turn === null ? 'idle' : 'working';
|
|
350
367
|
}
|
|
351
|
-
function
|
|
352
|
-
|
|
368
|
+
function rowOf(conversation) {
|
|
369
|
+
const turn = lives.get(conversation)?.turn ?? null;
|
|
370
|
+
return {
|
|
371
|
+
state: stateOf(conversation),
|
|
372
|
+
started: turn?.started ?? null,
|
|
373
|
+
line: turn === null ? null : runningLine(turn.parts),
|
|
374
|
+
question: openQuestion(liveOf(conversation).questions),
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
async function entries() {
|
|
378
|
+
const list = await heldList();
|
|
379
|
+
if (list === null)
|
|
380
|
+
return null;
|
|
381
|
+
const held = list.length === 0
|
|
382
|
+
? []
|
|
383
|
+
: await deps.durably((local) => local.conversationRows(list.map(({ conversation }) => conversation)));
|
|
384
|
+
const rows = new Map(held.map((one) => [one.conversation, one]));
|
|
385
|
+
return list.map((entry) => ({
|
|
353
386
|
conversation: entry.conversation,
|
|
354
387
|
agent: { identity: entry.agent, name: entry.agentName },
|
|
355
388
|
name: entry.name,
|
|
@@ -357,70 +390,153 @@ export function createConversationStore(deps) {
|
|
|
357
390
|
finished: entry.finished,
|
|
358
391
|
outcome: entry.outcome,
|
|
359
392
|
waiting: entry.waiting,
|
|
360
|
-
|
|
361
|
-
|
|
393
|
+
...rowOf(entry.conversation),
|
|
394
|
+
...heldFacts(rows.get(entry.conversation), entry),
|
|
362
395
|
}));
|
|
363
396
|
}
|
|
364
|
-
function
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
397
|
+
async function heldList() {
|
|
398
|
+
const stored = (await (await deps.local()).facts()).conversationList;
|
|
399
|
+
if (stored !== null)
|
|
400
|
+
return stored;
|
|
401
|
+
return !listKnown && deps.stream() !== undefined && deps.online() ? null : [];
|
|
402
|
+
}
|
|
403
|
+
async function endedIn(conversation) {
|
|
404
|
+
const list = await heldList();
|
|
405
|
+
return list?.find((entry) => entry.conversation === conversation)?.processEnded ?? null;
|
|
406
|
+
}
|
|
407
|
+
function listChanged(change) {
|
|
408
|
+
void deps
|
|
409
|
+
.durably(async (local) => {
|
|
410
|
+
const { conversationList, conversationCursor } = await local.facts();
|
|
411
|
+
const kept = change.replacing ? [] : (conversationList ?? []);
|
|
412
|
+
const held = new Map(kept.map((entry) => [entry.conversation, entry]));
|
|
413
|
+
for (const entry of change.set) {
|
|
414
|
+
held.set(entry.conversation, { ...entry });
|
|
415
|
+
creations.delete(entry.conversation);
|
|
416
|
+
}
|
|
417
|
+
if (change.gone !== null)
|
|
418
|
+
held.delete(change.gone);
|
|
419
|
+
const list = [...held.values()];
|
|
420
|
+
if (JSON.stringify(list) !== JSON.stringify(conversationList)) {
|
|
421
|
+
await local.fact('conversationList', list);
|
|
422
|
+
}
|
|
423
|
+
if (change.cursor !== undefined) {
|
|
424
|
+
if (change.cursor !== conversationCursor) {
|
|
425
|
+
await local.fact('conversationCursor', change.cursor);
|
|
426
|
+
}
|
|
427
|
+
deps.stream()?.hold(change.cursor);
|
|
428
|
+
}
|
|
429
|
+
return list;
|
|
430
|
+
})
|
|
431
|
+
.catch(async () => (await (await deps.local()).facts()).conversationList ?? [])
|
|
432
|
+
.then((list) => {
|
|
433
|
+
if (deps.closed() || change.paged === true)
|
|
371
434
|
return;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
435
|
+
endedAnew(list);
|
|
436
|
+
if (change.cursor !== undefined) {
|
|
437
|
+
listKnown = true;
|
|
438
|
+
settleBehind(list);
|
|
439
|
+
openingEnded();
|
|
440
|
+
deps.settled();
|
|
441
|
+
}
|
|
442
|
+
emitList();
|
|
443
|
+
if (!listKnown)
|
|
444
|
+
return;
|
|
445
|
+
retiredDrafts(list);
|
|
446
|
+
if (change.cursor === undefined &&
|
|
447
|
+
(list.length !== replicated.size ||
|
|
448
|
+
list.some(({ conversation }) => !replicated.has(conversation)))) {
|
|
449
|
+
deps.again();
|
|
450
|
+
}
|
|
451
|
+
}, () => { });
|
|
452
|
+
}
|
|
453
|
+
function endedAnew(list) {
|
|
454
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
455
|
+
for (const view of views.values()) {
|
|
456
|
+
const ended = entries.get(view.conversation)?.processEnded ?? null;
|
|
457
|
+
if (view.window === null || ended === view.ended)
|
|
458
|
+
continue;
|
|
459
|
+
view.ended = ended;
|
|
460
|
+
emit(view.conversation);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
let listShown = Promise.resolve();
|
|
464
|
+
function shownList(work) {
|
|
465
|
+
const reading = heldList();
|
|
466
|
+
listShown = listShown.then(async () => {
|
|
467
|
+
const list = await reading.catch(() => null);
|
|
468
|
+
if (list === null || deps.closed())
|
|
469
|
+
return;
|
|
470
|
+
work(list);
|
|
380
471
|
});
|
|
381
472
|
}
|
|
382
|
-
function
|
|
383
|
-
|
|
384
|
-
|
|
473
|
+
async function told() {
|
|
474
|
+
try {
|
|
475
|
+
while (listOwed && !deps.closed()) {
|
|
476
|
+
listOwed = false;
|
|
477
|
+
const value = await untilAnswered(entries, closing.signal);
|
|
478
|
+
if (deps.closed())
|
|
479
|
+
return;
|
|
480
|
+
if (value === null)
|
|
481
|
+
continue;
|
|
482
|
+
for (const watcher of listWatchers)
|
|
483
|
+
watcher(value);
|
|
484
|
+
for (const watcher of [...unlisted]) {
|
|
485
|
+
unlisted.delete(watcher);
|
|
486
|
+
listWatchers.add(watcher);
|
|
487
|
+
watcher(value);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
finally {
|
|
492
|
+
listTelling = false;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function tellList() {
|
|
496
|
+
if (deps.closed() || (listWatchers.size === 0 && unlisted.size === 0))
|
|
497
|
+
return;
|
|
498
|
+
listOwed = true;
|
|
499
|
+
if (listTelling)
|
|
500
|
+
return;
|
|
501
|
+
listTelling = true;
|
|
502
|
+
void told().catch(() => { });
|
|
503
|
+
}
|
|
504
|
+
function tellSoon() {
|
|
505
|
+
if (rowTimer !== null)
|
|
506
|
+
return;
|
|
507
|
+
rowTimer = setTimeout(() => {
|
|
508
|
+
rowTimer = null;
|
|
509
|
+
tellList();
|
|
510
|
+
}, ROW_INTERVAL_MS);
|
|
385
511
|
}
|
|
386
512
|
function emitList() {
|
|
387
513
|
if (deps.closed())
|
|
388
514
|
return;
|
|
389
|
-
|
|
390
|
-
const value = entries();
|
|
391
|
-
for (const watcher of listWatchers)
|
|
392
|
-
watcher(value);
|
|
393
|
-
}
|
|
515
|
+
tellList();
|
|
394
516
|
emitAgents();
|
|
395
517
|
learned();
|
|
396
518
|
}
|
|
397
519
|
function learned() {
|
|
398
520
|
if (deps.closed())
|
|
399
521
|
return;
|
|
400
|
-
if (
|
|
522
|
+
if (unlisted.size > 0)
|
|
523
|
+
tellList();
|
|
524
|
+
if (unnamed.size === 0)
|
|
401
525
|
return;
|
|
402
|
-
|
|
403
|
-
const value =
|
|
404
|
-
for (const watcher of [...unlisted]) {
|
|
405
|
-
unlisted.delete(watcher);
|
|
406
|
-
listWatchers.add(watcher);
|
|
407
|
-
watcher(value);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
if (unnamed.size > 0) {
|
|
411
|
-
const value = agents();
|
|
526
|
+
shownList((list) => {
|
|
527
|
+
const value = agents(list);
|
|
412
528
|
for (const watcher of [...unnamed]) {
|
|
413
529
|
unnamed.delete(watcher);
|
|
414
530
|
agentWatchers.add(watcher);
|
|
415
531
|
watcher(value);
|
|
416
532
|
}
|
|
417
|
-
}
|
|
533
|
+
});
|
|
418
534
|
}
|
|
419
|
-
function agents() {
|
|
535
|
+
function agents(list) {
|
|
420
536
|
const named = new Map();
|
|
421
537
|
for (const agent of listedAgents ?? [])
|
|
422
538
|
named.set(agent.agent, agent);
|
|
423
|
-
for (const entry of
|
|
539
|
+
for (const entry of list) {
|
|
424
540
|
named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
|
|
425
541
|
}
|
|
426
542
|
return [...named]
|
|
@@ -450,31 +566,33 @@ export function createConversationStore(deps) {
|
|
|
450
566
|
function emitAgents() {
|
|
451
567
|
if (deps.closed() || agentWatchers.size === 0)
|
|
452
568
|
return;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
watcher
|
|
569
|
+
shownList((list) => {
|
|
570
|
+
const value = agents(list);
|
|
571
|
+
const shown = JSON.stringify(value);
|
|
572
|
+
if (shown === agentsShown)
|
|
573
|
+
return;
|
|
574
|
+
agentsShown = shown;
|
|
575
|
+
for (const watcher of agentWatchers)
|
|
576
|
+
watcher(value);
|
|
577
|
+
});
|
|
460
578
|
}
|
|
461
579
|
function setLive(conversation, next) {
|
|
462
|
-
const before =
|
|
463
|
-
const began = lives.get(conversation)?.turn?.started;
|
|
580
|
+
const before = rowOf(conversation);
|
|
464
581
|
if (next === null)
|
|
465
582
|
lives.delete(conversation);
|
|
466
583
|
else
|
|
467
584
|
lives.set(conversation, next);
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
585
|
+
const after = rowOf(conversation);
|
|
586
|
+
if (after.state !== before.state || after.started !== before.started)
|
|
471
587
|
emitList();
|
|
588
|
+
else if (after.line !== before.line || after.question !== before.question)
|
|
589
|
+
tellSoon();
|
|
472
590
|
}
|
|
473
|
-
function retiredChat(conversation) {
|
|
591
|
+
function retiredChat(conversation, list) {
|
|
474
592
|
if (!conversation.startsWith(NEW_CHAT))
|
|
475
593
|
return false;
|
|
476
594
|
const agent = conversation.slice(NEW_CHAT.length);
|
|
477
|
-
return
|
|
595
|
+
return list.some((entry) => entry.agent === agent && entry.agentRetired);
|
|
478
596
|
}
|
|
479
597
|
function live(conversation, change) {
|
|
480
598
|
const owed = unsent.get(conversation);
|
|
@@ -532,17 +650,23 @@ export function createConversationStore(deps) {
|
|
|
532
650
|
}
|
|
533
651
|
async function remove(leaving) {
|
|
534
652
|
await deps.durably(async (local) => {
|
|
653
|
+
const kept = new Set([
|
|
654
|
+
...(await local.conversations(leaving)).map(({ conversation }) => conversation),
|
|
655
|
+
...(await local.chatDrafts())
|
|
656
|
+
.filter(({ conversation }) => leaving.includes(conversation))
|
|
657
|
+
.map(({ conversation }) => conversation),
|
|
658
|
+
]);
|
|
535
659
|
await local.dropConversations(leaving);
|
|
536
660
|
const changed = [];
|
|
537
661
|
for (const conversation of leaving) {
|
|
538
|
-
if (
|
|
662
|
+
if (kept.has(conversation) || lives.has(conversation))
|
|
539
663
|
changed.push(conversation);
|
|
664
|
+
for (const view of views.values()) {
|
|
665
|
+
if (view.conversation === conversation)
|
|
666
|
+
view.draft = null;
|
|
540
667
|
}
|
|
541
|
-
stored.delete(conversation);
|
|
542
|
-
reads.delete(conversation);
|
|
543
|
-
partials.delete(conversation);
|
|
544
|
-
drafts.delete(conversation);
|
|
545
668
|
behind.delete(conversation);
|
|
669
|
+
heardNewest.delete(conversation);
|
|
546
670
|
unsent.delete(conversation);
|
|
547
671
|
setLive(conversation, null);
|
|
548
672
|
await slide(local, conversation, null);
|
|
@@ -551,19 +675,68 @@ export function createConversationStore(deps) {
|
|
|
551
675
|
deps.publish(conversationChangeSet(changed));
|
|
552
676
|
});
|
|
553
677
|
}
|
|
554
|
-
function
|
|
555
|
-
|
|
556
|
-
|
|
678
|
+
function newestOfListed(entry) {
|
|
679
|
+
if (entry === undefined)
|
|
680
|
+
return undefined;
|
|
681
|
+
const heard = heardNewest.get(entry.conversation);
|
|
682
|
+
if (heard === undefined)
|
|
683
|
+
return entry.newest;
|
|
684
|
+
return entry.newest === null || heard > entry.newest ? heard : entry.newest;
|
|
685
|
+
}
|
|
686
|
+
function missed(conversation, held, entry) {
|
|
687
|
+
const read = held?.read;
|
|
688
|
+
const newest = newestOfListed(entry);
|
|
557
689
|
return (read === undefined ||
|
|
558
690
|
newest === undefined ||
|
|
559
|
-
|
|
691
|
+
held?.partial === true ||
|
|
560
692
|
(newest !== null && (read === null || newest > read)));
|
|
561
693
|
}
|
|
562
|
-
function
|
|
563
|
-
|
|
564
|
-
|
|
694
|
+
async function readsOf(local, conversations) {
|
|
695
|
+
return new Map((await local.conversations(conversations)).map((held) => [held.conversation, held]));
|
|
696
|
+
}
|
|
697
|
+
function retiredDrafts(list) {
|
|
698
|
+
if (!list.some((entry) => entry.agentRetired))
|
|
565
699
|
return;
|
|
566
|
-
|
|
700
|
+
void deps
|
|
701
|
+
.local()
|
|
702
|
+
.then((local) => local.chatDrafts())
|
|
703
|
+
.then((held) => {
|
|
704
|
+
if (!deps.closed() && held.some(({ conversation }) => retiredChat(conversation, list))) {
|
|
705
|
+
deps.again();
|
|
706
|
+
}
|
|
707
|
+
}, () => { });
|
|
708
|
+
}
|
|
709
|
+
function settleBehind(list) {
|
|
710
|
+
void deps
|
|
711
|
+
.durably(async (local) => {
|
|
712
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
713
|
+
const named = [...replicated].filter((conversation) => entries.has(conversation));
|
|
714
|
+
const held = await readsOf(local, named);
|
|
715
|
+
let owed = false;
|
|
716
|
+
for (const conversation of named) {
|
|
717
|
+
if (!missed(conversation, held.get(conversation), entries.get(conversation))) {
|
|
718
|
+
behind.delete(conversation);
|
|
719
|
+
}
|
|
720
|
+
else if (!behind.has(conversation)) {
|
|
721
|
+
gap(conversation);
|
|
722
|
+
owed = true;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return owed;
|
|
726
|
+
})
|
|
727
|
+
.then((owed) => {
|
|
728
|
+
if (deps.closed())
|
|
729
|
+
return;
|
|
730
|
+
deps.settled();
|
|
731
|
+
if (owed)
|
|
732
|
+
deps.again();
|
|
733
|
+
}, () => { });
|
|
734
|
+
}
|
|
735
|
+
function noted(conversation, message) {
|
|
736
|
+
const heard = heardNewest.get(conversation);
|
|
737
|
+
if (heard === undefined || message.position > heard) {
|
|
738
|
+
heardNewest.set(conversation, message.position);
|
|
739
|
+
}
|
|
567
740
|
}
|
|
568
741
|
function gap(conversation) {
|
|
569
742
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
@@ -573,7 +746,7 @@ export function createConversationStore(deps) {
|
|
|
573
746
|
return deps.durably(async (local) => {
|
|
574
747
|
if (!replicated.has(conversation))
|
|
575
748
|
return;
|
|
576
|
-
const read =
|
|
749
|
+
const read = (await readsOf(local, [conversation])).get(conversation)?.read;
|
|
577
750
|
const advances = read !== undefined &&
|
|
578
751
|
!behind.has(conversation) &&
|
|
579
752
|
(read === null || message.position > read);
|
|
@@ -583,19 +756,18 @@ export function createConversationStore(deps) {
|
|
|
583
756
|
messages: [message],
|
|
584
757
|
...(advances ? { read: { at: message.position, partial: false } } : {}),
|
|
585
758
|
});
|
|
586
|
-
stored.add(conversation);
|
|
587
|
-
if (advances)
|
|
588
|
-
reads.set(conversation, message.position);
|
|
589
759
|
sealTurns(conversation, new Set([message.turn]));
|
|
590
760
|
await slide(local, conversation, [message]);
|
|
591
761
|
deps.publish(conversationChangeSet([conversation]));
|
|
762
|
+
tellList();
|
|
592
763
|
});
|
|
593
764
|
}
|
|
594
765
|
async function readHistories(conversations, ended) {
|
|
595
766
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
767
|
+
const held = await readsOf(await deps.local(), conversations);
|
|
596
768
|
let waiting = conversations.map((conversation) => ({
|
|
597
769
|
conversation,
|
|
598
|
-
after:
|
|
770
|
+
after: held.get(conversation)?.read ?? undefined,
|
|
599
771
|
}));
|
|
600
772
|
while (waiting.length > 0) {
|
|
601
773
|
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
@@ -651,15 +823,10 @@ export function createConversationStore(deps) {
|
|
|
651
823
|
return;
|
|
652
824
|
const whole = next === undefined;
|
|
653
825
|
const cursor = next ?? newestOf(arriving, from);
|
|
654
|
-
const
|
|
826
|
+
const held = (await readsOf(local, [conversation])).get(conversation);
|
|
827
|
+
const partial = !whole && (held?.partial === true || held?.read === undefined);
|
|
655
828
|
const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
|
|
656
829
|
await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
|
|
657
|
-
stored.add(conversation);
|
|
658
|
-
reads.set(conversation, cursor);
|
|
659
|
-
if (partial)
|
|
660
|
-
partials.add(conversation);
|
|
661
|
-
else
|
|
662
|
-
partials.delete(conversation);
|
|
663
830
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
664
831
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
665
832
|
behind.delete(conversation);
|
|
@@ -667,6 +834,8 @@ export function createConversationStore(deps) {
|
|
|
667
834
|
if (arriving.length > 0 || sealing || whole) {
|
|
668
835
|
deps.publish(conversationChangeSet([conversation]));
|
|
669
836
|
}
|
|
837
|
+
if (arriving.length > 0)
|
|
838
|
+
tellSoon();
|
|
670
839
|
});
|
|
671
840
|
}
|
|
672
841
|
function heard(event) {
|
|
@@ -733,17 +902,24 @@ export function createConversationStore(deps) {
|
|
|
733
902
|
emit(conversation);
|
|
734
903
|
}
|
|
735
904
|
async function kept(local, conversation, following) {
|
|
736
|
-
|
|
905
|
+
const shown = following === null || empty(following) ? null : following;
|
|
906
|
+
if (shown === null)
|
|
737
907
|
await local.dropChatDraft(conversation);
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
908
|
+
else
|
|
909
|
+
await local.keepChatDrafts([shown]);
|
|
910
|
+
shownDraft(conversation, shown);
|
|
911
|
+
}
|
|
912
|
+
function shownDraft(conversation, draft) {
|
|
913
|
+
for (const view of views.values()) {
|
|
914
|
+
if (view.conversation === conversation)
|
|
915
|
+
view.draft = draft;
|
|
743
916
|
}
|
|
744
917
|
}
|
|
918
|
+
async function draftAt(local, conversation) {
|
|
919
|
+
return (await local.chatDraft(conversation)) ?? undefined;
|
|
920
|
+
}
|
|
745
921
|
async function sealed(local, conversation, turns) {
|
|
746
|
-
const held =
|
|
922
|
+
const held = await draftAt(local, conversation);
|
|
747
923
|
const submitted = held?.submitted ?? [];
|
|
748
924
|
const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
|
|
749
925
|
if (held === undefined || left.length === submitted.length)
|
|
@@ -754,8 +930,8 @@ export function createConversationStore(deps) {
|
|
|
754
930
|
function moved(agent, operation, to) {
|
|
755
931
|
const from = newChat(agent);
|
|
756
932
|
return deps.durably(async (local) => {
|
|
757
|
-
const held =
|
|
758
|
-
const earlier =
|
|
933
|
+
const held = await draftAt(local, from);
|
|
934
|
+
const earlier = held?.moves ?? [];
|
|
759
935
|
if (earlier.includes(operation))
|
|
760
936
|
return;
|
|
761
937
|
const blank = {
|
|
@@ -764,18 +940,20 @@ export function createConversationStore(deps) {
|
|
|
764
940
|
revision: chatDraftRevision(),
|
|
765
941
|
};
|
|
766
942
|
const { moves: _answered, ...unsent } = held ?? blank;
|
|
767
|
-
const taken =
|
|
943
|
+
const taken = (await draftAt(local, to)) !== undefined || empty(unsent)
|
|
944
|
+
? []
|
|
945
|
+
: [{ ...unsent, conversation: to }];
|
|
768
946
|
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
769
947
|
await local.keepChatDrafts([left, ...taken]);
|
|
770
|
-
|
|
948
|
+
shownDraft(from, left);
|
|
771
949
|
for (const one of taken)
|
|
772
|
-
|
|
950
|
+
shownDraft(to, one);
|
|
773
951
|
deps.publish(conversationChangeSet([from, to]));
|
|
774
952
|
});
|
|
775
953
|
}
|
|
776
954
|
function withDraft(conversation, next) {
|
|
777
955
|
return deps.durably(async (local) => {
|
|
778
|
-
const following = await next(
|
|
956
|
+
const following = await next(await draftAt(local, conversation), local);
|
|
779
957
|
if (following === undefined)
|
|
780
958
|
return;
|
|
781
959
|
await kept(local, conversation, following);
|
|
@@ -784,10 +962,8 @@ export function createConversationStore(deps) {
|
|
|
784
962
|
}
|
|
785
963
|
async function submitted(conversation, operation, draft) {
|
|
786
964
|
let submission;
|
|
787
|
-
let earlier = false;
|
|
788
965
|
await withDraft(conversation, (current) => {
|
|
789
966
|
submission = current?.submitted?.find((one) => one.operation === operation);
|
|
790
|
-
earlier = submission !== undefined;
|
|
791
967
|
if (submission !== undefined)
|
|
792
968
|
return undefined;
|
|
793
969
|
const said = draft ?? current;
|
|
@@ -800,15 +976,7 @@ export function createConversationStore(deps) {
|
|
|
800
976
|
submitted: [...(current?.submitted ?? []), submission],
|
|
801
977
|
};
|
|
802
978
|
});
|
|
803
|
-
return
|
|
804
|
-
}
|
|
805
|
-
function forgotten(conversation, submission) {
|
|
806
|
-
return withDraft(conversation, (current) => {
|
|
807
|
-
const held = current?.submitted ?? [];
|
|
808
|
-
if (current === undefined || !held.includes(submission))
|
|
809
|
-
return undefined;
|
|
810
|
-
return withSubmitted(current, held.filter((one) => one !== submission));
|
|
811
|
-
});
|
|
979
|
+
return submission;
|
|
812
980
|
}
|
|
813
981
|
async function dispatched(conversation, submission) {
|
|
814
982
|
const dispatch = { answered: false };
|
|
@@ -861,11 +1029,14 @@ export function createConversationStore(deps) {
|
|
|
861
1029
|
});
|
|
862
1030
|
},
|
|
863
1031
|
async read(conversation, options = {}) {
|
|
864
|
-
const
|
|
1032
|
+
const { text, ...windowed } = options;
|
|
1033
|
+
const asked = askedOf(windowed);
|
|
1034
|
+
const phrase = phraseOf(text);
|
|
865
1035
|
return deps.durably(async (local) => {
|
|
866
|
-
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
867
|
-
const total = asked.total ? await local.messageCount(conversation) : null;
|
|
868
|
-
|
|
1036
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
|
|
1037
|
+
const total = asked.total ? await local.messageCount(conversation, phrase) : null;
|
|
1038
|
+
const marked = await local.jobMarkers(conversation);
|
|
1039
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total), (await local.chatDraft(conversation)) ?? null, await endedIn(conversation), marked));
|
|
869
1040
|
});
|
|
870
1041
|
},
|
|
871
1042
|
async create(input) {
|
|
@@ -873,8 +1044,10 @@ export function createConversationStore(deps) {
|
|
|
873
1044
|
const conversation = answered(result, 'conversation_id');
|
|
874
1045
|
if (conversation === null)
|
|
875
1046
|
return result;
|
|
876
|
-
|
|
1047
|
+
const listed = (await heldList()) ?? [];
|
|
1048
|
+
if (!listed.some((entry) => entry.conversation === conversation)) {
|
|
877
1049
|
creations.add(conversation);
|
|
1050
|
+
}
|
|
878
1051
|
const creation = creationOf(input);
|
|
879
1052
|
if (creation !== null)
|
|
880
1053
|
await moved(creation.agent, creation.operation, conversation);
|
|
@@ -891,11 +1064,7 @@ export function createConversationStore(deps) {
|
|
|
891
1064
|
});
|
|
892
1065
|
if (held === null)
|
|
893
1066
|
return DURABLE_WRITE;
|
|
894
|
-
|
|
895
|
-
if (!earlier || !deps.expired(operation))
|
|
896
|
-
return dispatched(conversation, submission);
|
|
897
|
-
await forgotten(conversation, submission);
|
|
898
|
-
return EXPIRED;
|
|
1067
|
+
return dispatched(conversation, held);
|
|
899
1068
|
}
|
|
900
1069
|
if (draft !== undefined) {
|
|
901
1070
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
@@ -946,43 +1115,40 @@ export function createConversationStore(deps) {
|
|
|
946
1115
|
return {
|
|
947
1116
|
surface,
|
|
948
1117
|
chatDraft,
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
listCursor = cursor;
|
|
956
|
-
deps.stream()?.hold(cursor);
|
|
957
|
-
}
|
|
958
|
-
stored.clear();
|
|
959
|
-
reads.clear();
|
|
960
|
-
partials.clear();
|
|
961
|
-
for (const entry of held) {
|
|
962
|
-
stored.add(entry.conversation);
|
|
963
|
-
if (entry.read !== undefined)
|
|
964
|
-
reads.set(entry.conversation, entry.read);
|
|
965
|
-
if (entry.partial)
|
|
966
|
-
partials.add(entry.conversation);
|
|
967
|
-
}
|
|
968
|
-
drafts.clear();
|
|
969
|
-
for (const record of kept)
|
|
970
|
-
drafts.set(record.conversation, record);
|
|
971
|
-
if (listHeld)
|
|
972
|
-
learned();
|
|
1118
|
+
async opened(local) {
|
|
1119
|
+
const { conversationList, conversationCursor } = await local.facts();
|
|
1120
|
+
if (conversationList === null || listKnown)
|
|
1121
|
+
return;
|
|
1122
|
+
deps.stream()?.hold(conversationCursor);
|
|
1123
|
+
learned();
|
|
973
1124
|
},
|
|
974
1125
|
async reconcile() {
|
|
975
1126
|
if (!listKnown)
|
|
976
1127
|
return;
|
|
977
|
-
const
|
|
1128
|
+
const local = await deps.local();
|
|
1129
|
+
const [held, kept, { conversationList }] = await Promise.all([
|
|
1130
|
+
local.conversations(),
|
|
1131
|
+
local.chatDrafts(),
|
|
1132
|
+
local.facts(),
|
|
1133
|
+
]);
|
|
1134
|
+
const list = conversationList ?? [];
|
|
1135
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
1136
|
+
const named = new Set(entries.keys());
|
|
1137
|
+
const reads = new Map(held.map((one) => [one.conversation, one]));
|
|
978
1138
|
const leaving = [
|
|
979
|
-
...new Set([
|
|
1139
|
+
...new Set([
|
|
1140
|
+
...replicated,
|
|
1141
|
+
...reads.keys(),
|
|
1142
|
+
...kept.map(({ conversation }) => conversation),
|
|
1143
|
+
]),
|
|
980
1144
|
].filter((conversation) => !named.has(conversation) &&
|
|
981
1145
|
!creations.has(conversation) &&
|
|
982
|
-
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
1146
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation, list)));
|
|
983
1147
|
for (const conversation of named) {
|
|
984
|
-
if (!replicated.has(conversation) &&
|
|
1148
|
+
if (!replicated.has(conversation) &&
|
|
1149
|
+
missed(conversation, reads.get(conversation), entries.get(conversation))) {
|
|
985
1150
|
behind.add(conversation);
|
|
1151
|
+
}
|
|
986
1152
|
}
|
|
987
1153
|
replicated = named;
|
|
988
1154
|
handReplicated();
|
|
@@ -1013,16 +1179,16 @@ export function createConversationStore(deps) {
|
|
|
1013
1179
|
progress.complete = false;
|
|
1014
1180
|
},
|
|
1015
1181
|
async resend(progress) {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1182
|
+
const local = await deps.local();
|
|
1183
|
+
const tried = new Set();
|
|
1184
|
+
for (const { conversation } of await local.chatDrafts()) {
|
|
1185
|
+
for (;;) {
|
|
1186
|
+
const submission = (await local.chatDraft(conversation))?.submitted?.find((one) => one.turn === null && !sending.has(one.operation) && !tried.has(one.operation));
|
|
1187
|
+
if (submission === undefined)
|
|
1188
|
+
break;
|
|
1020
1189
|
if (deps.closed())
|
|
1021
1190
|
return;
|
|
1022
|
-
|
|
1023
|
-
await forgotten(conversation, submission);
|
|
1024
|
-
continue;
|
|
1025
|
-
}
|
|
1191
|
+
tried.add(submission.operation);
|
|
1026
1192
|
const result = await dispatched(conversation, submission);
|
|
1027
1193
|
if (result.kind === 'revoked-authorization')
|
|
1028
1194
|
return;
|
|
@@ -1060,6 +1226,9 @@ export function createConversationStore(deps) {
|
|
|
1060
1226
|
asked,
|
|
1061
1227
|
watchers: new Set(),
|
|
1062
1228
|
window: null,
|
|
1229
|
+
draft: null,
|
|
1230
|
+
ended: null,
|
|
1231
|
+
marked: [],
|
|
1063
1232
|
more: false,
|
|
1064
1233
|
total: null,
|
|
1065
1234
|
};
|
|
@@ -1071,7 +1240,10 @@ export function createConversationStore(deps) {
|
|
|
1071
1240
|
if (view.window !== null)
|
|
1072
1241
|
return;
|
|
1073
1242
|
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1243
|
+
view.draft = (await local.chatDraft(conversation)) ?? null;
|
|
1244
|
+
view.ended = await endedIn(conversation);
|
|
1074
1245
|
view.window = messages;
|
|
1246
|
+
view.marked = await local.jobMarkers(conversation);
|
|
1075
1247
|
view.more = more;
|
|
1076
1248
|
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
1077
1249
|
}), reading.signal)
|
|
@@ -1080,7 +1252,7 @@ export function createConversationStore(deps) {
|
|
|
1080
1252
|
return;
|
|
1081
1253
|
watcher.ready = true;
|
|
1082
1254
|
const window = view.window ?? [];
|
|
1083
|
-
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
1255
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window), view.draft, view.ended, view.marked));
|
|
1084
1256
|
})
|
|
1085
1257
|
.catch(() => { });
|
|
1086
1258
|
return () => {
|
|
@@ -1115,64 +1287,43 @@ export function createConversationStore(deps) {
|
|
|
1115
1287
|
},
|
|
1116
1288
|
receive(event) {
|
|
1117
1289
|
if (deps.closed())
|
|
1118
|
-
return
|
|
1119
|
-
if (event.kind === 'list'
|
|
1120
|
-
if (event.
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
for (const entry of event.entries)
|
|
1124
|
-
|
|
1125
|
-
listed(entry);
|
|
1126
|
-
else
|
|
1127
|
-
relisting.set(entry.conversation, { ...entry });
|
|
1128
|
-
}
|
|
1129
|
-
if (event.more)
|
|
1130
|
-
return false;
|
|
1131
|
-
if (relisting !== null) {
|
|
1132
|
-
listing.clear();
|
|
1133
|
-
for (const entry of relisting.values())
|
|
1134
|
-
listed(entry);
|
|
1135
|
-
relisting = null;
|
|
1136
|
-
}
|
|
1137
|
-
listCursor = event.cursor;
|
|
1138
|
-
deps.stream()?.hold(event.cursor);
|
|
1139
|
-
listKnown = true;
|
|
1140
|
-
for (const conversation of replicated) {
|
|
1141
|
-
if (!listing.has(conversation))
|
|
1142
|
-
continue;
|
|
1143
|
-
if (!missed(conversation))
|
|
1144
|
-
behind.delete(conversation);
|
|
1145
|
-
else if (!behind.has(conversation))
|
|
1146
|
-
gap(conversation);
|
|
1147
|
-
}
|
|
1148
|
-
openingEnded();
|
|
1149
|
-
}
|
|
1150
|
-
else if (event.kind === 'listed') {
|
|
1151
|
-
listed(event.entry);
|
|
1152
|
-
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
1153
|
-
}
|
|
1154
|
-
else {
|
|
1155
|
-
listing.delete(event.conversation);
|
|
1156
|
-
relisting?.delete(event.conversation);
|
|
1290
|
+
return;
|
|
1291
|
+
if (event.kind === 'list') {
|
|
1292
|
+
if (event.whole)
|
|
1293
|
+
relisting ??= new Map();
|
|
1294
|
+
if (relisting !== null) {
|
|
1295
|
+
for (const entry of event.entries)
|
|
1296
|
+
relisting.set(entry.conversation, { ...entry });
|
|
1157
1297
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1298
|
+
if (event.more && relisting !== null)
|
|
1299
|
+
return;
|
|
1300
|
+
const whole = relisting;
|
|
1301
|
+
relisting = null;
|
|
1302
|
+
listChanged({
|
|
1303
|
+
replacing: whole !== null,
|
|
1304
|
+
set: whole === null ? event.entries : [...whole.values()],
|
|
1305
|
+
gone: null,
|
|
1306
|
+
...(event.more ? { paged: true } : { cursor: event.cursor }),
|
|
1307
|
+
});
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
if (event.kind === 'listed') {
|
|
1311
|
+
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
1312
|
+
listChanged({ replacing: false, set: [event.entry], gone: null });
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
if (event.kind === 'unlisted') {
|
|
1316
|
+
relisting?.delete(event.conversation);
|
|
1317
|
+
listChanged({ replacing: false, set: [], gone: event.conversation });
|
|
1318
|
+
return;
|
|
1165
1319
|
}
|
|
1166
1320
|
heard(event);
|
|
1167
|
-
return false;
|
|
1168
|
-
},
|
|
1169
|
-
searchable() {
|
|
1170
|
-
return [...(listKnown ? listing.keys() : stored)].sort();
|
|
1171
|
-
},
|
|
1172
|
-
acquired(conversation) {
|
|
1173
|
-
return reads.has(conversation) && !partials.has(conversation);
|
|
1174
1321
|
},
|
|
1175
1322
|
close() {
|
|
1323
|
+
closing.abort();
|
|
1324
|
+
if (rowTimer !== null)
|
|
1325
|
+
clearTimeout(rowTimer);
|
|
1326
|
+
rowTimer = null;
|
|
1176
1327
|
views.clear();
|
|
1177
1328
|
lives.clear();
|
|
1178
1329
|
unsent.clear();
|