@agentshouse/mdruntime 0.1.0-cac707d698ca2 → 0.1.0-cad267257ceaf
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 +1 -1
- package/dist/acceptance.d.ts +6 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +389 -0
- package/dist/actions.d.ts +20 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +250 -0
- package/dist/adapter.d.ts +17 -16
- package/dist/adapter.d.ts.map +1 -1
- package/dist/bootstrap.d.ts +8 -4
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +206 -76
- package/dist/cell.d.ts +6 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +9 -0
- package/dist/change.d.ts +1 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/chat-types.d.ts +5 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +13 -0
- package/dist/client.d.ts +12 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -0
- package/dist/compile.d.ts +5 -0
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +186 -55
- package/dist/connection.d.ts +5 -143
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +98 -5016
- package/dist/conversation-row.d.ts +33 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +53 -0
- package/dist/conversation.d.ts +15 -5
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +358 -193
- package/dist/discovery.d.ts +10 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +175 -0
- package/dist/durability.d.ts +9 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +1 -0
- package/dist/figures.d.ts +48 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +197 -0
- package/dist/holdings.d.ts +29 -0
- package/dist/holdings.d.ts.map +1 -0
- package/dist/holdings.js +95 -0
- package/dist/host.d.ts +1 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +7 -4
- package/dist/index.d.ts +18 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/install.d.ts +56 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +560 -0
- package/dist/job.d.ts +20 -1
- package/dist/job.d.ts.map +1 -1
- package/dist/job.js +26 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +267 -0
- package/dist/listing.d.ts +53 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +175 -0
- package/dist/live.d.ts +4 -4
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +53 -15
- package/dist/locks.d.ts +9 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +61 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/lost.d.ts +5 -0
- package/dist/lost.d.ts.map +1 -0
- package/dist/lost.js +61 -0
- package/dist/memory.d.ts +1 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +13 -5
- package/dist/messages.d.ts +25 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +82 -3
- package/dist/model.d.ts +0 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -6
- package/dist/models.d.ts +14 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +193 -0
- package/dist/opening.d.ts +4 -0
- package/dist/opening.d.ts.map +1 -0
- package/dist/opening.js +56 -0
- package/dist/outcome.d.ts +4 -1
- 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/preparation.d.ts +20 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +226 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +272 -0
- package/dist/query.d.ts +10 -0
- package/dist/query.d.ts.map +1 -1
- package/dist/questions.d.ts +34 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +122 -0
- package/dist/reach.d.ts +8 -6
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +156 -99
- package/dist/react.d.ts +7 -1
- 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/reads.d.ts +36 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +136 -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 +4 -0
- package/dist/refusal.d.ts +5 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +56 -0
- package/dist/replica.d.ts +0 -3
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +23 -6
- package/dist/room-drafts.d.ts +24 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +216 -0
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +2 -2
- package/dist/runtime.d.ts +42 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +11 -8
- package/dist/saves.d.ts +70 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +1004 -0
- package/dist/seed.d.ts +11 -2
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +71 -0
- package/dist/shown.d.ts +31 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +294 -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/lanes.d.ts +20 -0
- package/dist/socket/lanes.d.ts.map +1 -0
- package/dist/socket/lanes.js +77 -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/rooms.d.ts +43 -0
- package/dist/socket/rooms.d.ts.map +1 -0
- package/dist/socket/rooms.js +90 -0
- package/dist/socket/session.d.ts +39 -0
- package/dist/socket/session.d.ts.map +1 -0
- package/dist/socket/session.js +23 -0
- package/dist/socket.d.ts +70 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +175 -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/readiness.d.ts +10 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +52 -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/space.d.ts +11 -0
- package/dist/store/space.d.ts.map +1 -0
- package/dist/store/space.js +43 -0
- package/dist/store/watchers.d.ts +17 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +57 -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 +107 -142
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +143 -361
- package/dist/stored.d.ts +55 -28
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +210 -79
- package/dist/surface.d.ts +88 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +222 -0
- package/dist/sync.d.ts +20 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +531 -0
- package/dist/tables.d.ts +108 -20
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +189 -36
- 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 +4 -4
- package/dist/wire.d.ts +8 -1
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +4 -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/conversation.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
|
|
2
2
|
import { conversationChangeSet } from './change-set.js';
|
|
3
|
+
import { heldFacts } from './conversation-row.js';
|
|
3
4
|
import { runtimeError } from './failure.js';
|
|
4
5
|
import { refusedForSpace } from './durability.js';
|
|
5
6
|
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
6
7
|
import { OFFLINE } from './outcome.js';
|
|
8
|
+
import { jobsOf, runningLine } from './parts.js';
|
|
9
|
+
import { openQuestion, questionAsked } from './questions.js';
|
|
7
10
|
import { untilAnswered } from './recovery.js';
|
|
8
11
|
import { capped, FROM_START, messageOrder, precedes } from './windows.js';
|
|
9
12
|
const QUIET = {
|
|
@@ -42,11 +45,14 @@ export function unheldView(conversation) {
|
|
|
42
45
|
link: null,
|
|
43
46
|
deliveryUnknown: [],
|
|
44
47
|
chatDraft: null,
|
|
48
|
+
jobs: [],
|
|
45
49
|
};
|
|
46
50
|
}
|
|
47
51
|
export const SESSION_STARTED = 'Session started';
|
|
48
52
|
const NEW_CHAT = 'new:';
|
|
49
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;
|
|
50
56
|
export function newChat(agent) {
|
|
51
57
|
return `${NEW_CHAT}${agent}`;
|
|
52
58
|
}
|
|
@@ -254,10 +260,6 @@ function returned(draft, submission, others) {
|
|
|
254
260
|
}, others);
|
|
255
261
|
}
|
|
256
262
|
export function createConversationStore(deps) {
|
|
257
|
-
const stored = new Set();
|
|
258
|
-
const reads = new Map();
|
|
259
|
-
const partials = new Set();
|
|
260
|
-
const drafts = new Map();
|
|
261
263
|
const sending = new Map();
|
|
262
264
|
const behind = new Set();
|
|
263
265
|
const catching = new Map();
|
|
@@ -265,15 +267,16 @@ export function createConversationStore(deps) {
|
|
|
265
267
|
const lives = new Map();
|
|
266
268
|
const unsent = new Map();
|
|
267
269
|
const views = new Map();
|
|
268
|
-
const
|
|
270
|
+
const heardNewest = new Map();
|
|
269
271
|
let relisting = null;
|
|
270
|
-
let listCursor = null;
|
|
271
272
|
const creations = new Set();
|
|
272
273
|
const listWatchers = new Set();
|
|
273
274
|
const unlisted = new Set();
|
|
274
275
|
let listKnown = false;
|
|
275
|
-
let
|
|
276
|
-
let
|
|
276
|
+
let listOwed = false;
|
|
277
|
+
let listTelling = false;
|
|
278
|
+
let rowTimer = null;
|
|
279
|
+
const closing = new AbortController();
|
|
277
280
|
const agentWatchers = new Set();
|
|
278
281
|
const unnamed = new Set();
|
|
279
282
|
let agentsShown = null;
|
|
@@ -284,8 +287,7 @@ export function createConversationStore(deps) {
|
|
|
284
287
|
function liveOf(conversation) {
|
|
285
288
|
return lives.get(conversation) ?? QUIET;
|
|
286
289
|
}
|
|
287
|
-
function viewOf(conversation, held, frame) {
|
|
288
|
-
const draft = drafts.get(conversation);
|
|
290
|
+
function viewOf(conversation, held, frame, draft, ended, marked) {
|
|
289
291
|
const facts = liveOf(conversation);
|
|
290
292
|
return {
|
|
291
293
|
conversation,
|
|
@@ -299,16 +301,17 @@ export function createConversationStore(deps) {
|
|
|
299
301
|
turn: structuredClone(facts.turn),
|
|
300
302
|
questions: structuredClone(facts.questions),
|
|
301
303
|
process: facts.process === null ? null : { ...facts.process },
|
|
302
|
-
processEnded:
|
|
304
|
+
processEnded: ended,
|
|
303
305
|
commands: structuredClone(facts.commands),
|
|
304
306
|
options: structuredClone(facts.options),
|
|
305
307
|
starting: facts.starting,
|
|
306
308
|
subagents: structuredClone(facts.subagents),
|
|
307
309
|
link: facts.link === null ? null : { ...facts.link },
|
|
308
310
|
deliveryUnknown: [...facts.deliveryUnknown],
|
|
309
|
-
chatDraft: draft ===
|
|
310
|
-
|
|
311
|
-
|
|
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),
|
|
312
315
|
};
|
|
313
316
|
}
|
|
314
317
|
function emit(conversation) {
|
|
@@ -320,7 +323,7 @@ export function createConversationStore(deps) {
|
|
|
320
323
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
321
324
|
if (ready.length === 0)
|
|
322
325
|
continue;
|
|
323
|
-
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);
|
|
324
327
|
for (const watcher of ready)
|
|
325
328
|
watcher.notify(value);
|
|
326
329
|
}
|
|
@@ -330,11 +333,13 @@ export function createConversationStore(deps) {
|
|
|
330
333
|
}
|
|
331
334
|
async function slide(local, conversation, arriving) {
|
|
332
335
|
let count;
|
|
336
|
+
let marked;
|
|
333
337
|
for (const view of views.values()) {
|
|
334
338
|
if (view.conversation !== conversation || view.window === null)
|
|
335
339
|
continue;
|
|
336
340
|
if (arriving === null) {
|
|
337
341
|
view.window = [];
|
|
342
|
+
view.marked = [];
|
|
338
343
|
view.more = false;
|
|
339
344
|
if (view.total !== null)
|
|
340
345
|
view.total = 0;
|
|
@@ -344,6 +349,10 @@ export function createConversationStore(deps) {
|
|
|
344
349
|
const next = windowOf(view.window, joining, view.asked.limit);
|
|
345
350
|
view.window = next.messages;
|
|
346
351
|
view.more ||= next.dropped;
|
|
352
|
+
if (arriving.length > 0) {
|
|
353
|
+
marked ??= await local.jobMarkers(conversation);
|
|
354
|
+
view.marked = marked;
|
|
355
|
+
}
|
|
347
356
|
if (view.total !== null && arriving.length > 0) {
|
|
348
357
|
count ??= await local.messageCount(conversation);
|
|
349
358
|
view.total = count;
|
|
@@ -356,82 +365,179 @@ export function createConversationStore(deps) {
|
|
|
356
365
|
return 'asking';
|
|
357
366
|
return facts.turn === null ? 'idle' : 'working';
|
|
358
367
|
}
|
|
359
|
-
function
|
|
360
|
-
|
|
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
|
+
asked: questionAsked(liveOf(conversation).questions),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
async function entries() {
|
|
379
|
+
const list = await heldList();
|
|
380
|
+
if (list === null)
|
|
381
|
+
return null;
|
|
382
|
+
const held = list.length === 0
|
|
383
|
+
? []
|
|
384
|
+
: await deps.durably((local) => local.conversationRows(list.map(({ conversation }) => conversation)));
|
|
385
|
+
const rows = new Map(held.map((one) => [one.conversation, one]));
|
|
386
|
+
return list.map((entry) => ({
|
|
361
387
|
conversation: entry.conversation,
|
|
362
|
-
agent: { identity: entry.agent, name: entry.agentName },
|
|
388
|
+
agent: { identity: entry.agent, name: entry.agentName, retired: entry.agentRetired },
|
|
363
389
|
name: entry.name,
|
|
364
390
|
created: entry.created,
|
|
365
391
|
finished: entry.finished,
|
|
366
392
|
outcome: entry.outcome,
|
|
367
393
|
waiting: entry.waiting,
|
|
368
|
-
|
|
369
|
-
|
|
394
|
+
...rowOf(entry.conversation),
|
|
395
|
+
...heldFacts(rows.get(entry.conversation), entry),
|
|
370
396
|
}));
|
|
371
397
|
}
|
|
372
|
-
function
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
398
|
+
async function heldList() {
|
|
399
|
+
const stored = (await (await deps.local()).facts()).conversationList;
|
|
400
|
+
if (stored !== null)
|
|
401
|
+
return stored;
|
|
402
|
+
return !listKnown && deps.stream() !== undefined && deps.online() ? null : [];
|
|
403
|
+
}
|
|
404
|
+
async function endedIn(conversation) {
|
|
405
|
+
const list = await heldList();
|
|
406
|
+
return list?.find((entry) => entry.conversation === conversation)?.processEnded ?? null;
|
|
407
|
+
}
|
|
408
|
+
function listChanged(change) {
|
|
409
|
+
void deps
|
|
410
|
+
.durably(async (local) => {
|
|
411
|
+
const { conversationList, conversationCursor } = await local.facts();
|
|
412
|
+
const kept = change.replacing ? [] : (conversationList ?? []);
|
|
413
|
+
const held = new Map(kept.map((entry) => [entry.conversation, entry]));
|
|
414
|
+
for (const entry of change.set) {
|
|
415
|
+
held.set(entry.conversation, { ...entry });
|
|
416
|
+
creations.delete(entry.conversation);
|
|
417
|
+
}
|
|
418
|
+
if (change.gone !== null)
|
|
419
|
+
held.delete(change.gone);
|
|
420
|
+
const list = [...held.values()];
|
|
421
|
+
if (JSON.stringify(list) !== JSON.stringify(conversationList)) {
|
|
422
|
+
await local.fact('conversationList', list);
|
|
423
|
+
}
|
|
424
|
+
if (change.cursor !== undefined) {
|
|
425
|
+
if (change.cursor !== conversationCursor) {
|
|
426
|
+
await local.fact('conversationCursor', change.cursor);
|
|
427
|
+
}
|
|
428
|
+
deps.stream()?.hold(change.cursor);
|
|
429
|
+
}
|
|
430
|
+
return list;
|
|
431
|
+
})
|
|
432
|
+
.catch(async () => (await (await deps.local()).facts()).conversationList ?? [])
|
|
433
|
+
.then((list) => {
|
|
434
|
+
if (deps.closed() || change.paged === true)
|
|
379
435
|
return;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
|
|
436
|
+
endedAnew(list);
|
|
437
|
+
if (change.cursor !== undefined) {
|
|
438
|
+
listKnown = true;
|
|
439
|
+
settleBehind(list);
|
|
440
|
+
openingEnded();
|
|
441
|
+
deps.settled();
|
|
442
|
+
}
|
|
443
|
+
emitList();
|
|
444
|
+
if (!listKnown)
|
|
445
|
+
return;
|
|
446
|
+
retiredDrafts(list);
|
|
447
|
+
if (change.cursor === undefined &&
|
|
448
|
+
(list.length !== replicated.size ||
|
|
449
|
+
list.some(({ conversation }) => !replicated.has(conversation)))) {
|
|
450
|
+
deps.again();
|
|
451
|
+
}
|
|
452
|
+
}, () => { });
|
|
453
|
+
}
|
|
454
|
+
function endedAnew(list) {
|
|
455
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
456
|
+
for (const view of views.values()) {
|
|
457
|
+
const ended = entries.get(view.conversation)?.processEnded ?? null;
|
|
458
|
+
if (view.window === null || ended === view.ended)
|
|
459
|
+
continue;
|
|
460
|
+
view.ended = ended;
|
|
461
|
+
emit(view.conversation);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
let listShown = Promise.resolve();
|
|
465
|
+
function shownList(work) {
|
|
466
|
+
const reading = heldList();
|
|
467
|
+
listShown = listShown.then(async () => {
|
|
468
|
+
const list = await reading.catch(() => null);
|
|
469
|
+
if (list === null || deps.closed())
|
|
470
|
+
return;
|
|
471
|
+
work(list);
|
|
388
472
|
});
|
|
389
473
|
}
|
|
390
|
-
function
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
474
|
+
async function told() {
|
|
475
|
+
try {
|
|
476
|
+
while (listOwed && !deps.closed()) {
|
|
477
|
+
listOwed = false;
|
|
478
|
+
const value = await untilAnswered(entries, closing.signal);
|
|
479
|
+
if (deps.closed())
|
|
480
|
+
return;
|
|
481
|
+
if (value === null)
|
|
482
|
+
continue;
|
|
483
|
+
for (const watcher of listWatchers)
|
|
484
|
+
watcher(value);
|
|
485
|
+
for (const watcher of [...unlisted]) {
|
|
486
|
+
unlisted.delete(watcher);
|
|
487
|
+
listWatchers.add(watcher);
|
|
488
|
+
watcher(value);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
finally {
|
|
493
|
+
listTelling = false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function tellList() {
|
|
497
|
+
if (deps.closed() || (listWatchers.size === 0 && unlisted.size === 0))
|
|
498
|
+
return;
|
|
499
|
+
listOwed = true;
|
|
500
|
+
if (listTelling)
|
|
501
|
+
return;
|
|
502
|
+
listTelling = true;
|
|
503
|
+
void told().catch(() => { });
|
|
504
|
+
}
|
|
505
|
+
function tellSoon() {
|
|
506
|
+
if (rowTimer !== null)
|
|
507
|
+
return;
|
|
508
|
+
rowTimer = setTimeout(() => {
|
|
509
|
+
rowTimer = null;
|
|
510
|
+
tellList();
|
|
511
|
+
}, ROW_INTERVAL_MS);
|
|
396
512
|
}
|
|
397
513
|
function emitList() {
|
|
398
514
|
if (deps.closed())
|
|
399
515
|
return;
|
|
400
|
-
|
|
401
|
-
const value = entries();
|
|
402
|
-
for (const watcher of listWatchers)
|
|
403
|
-
watcher(value);
|
|
404
|
-
}
|
|
516
|
+
tellList();
|
|
405
517
|
emitAgents();
|
|
406
518
|
learned();
|
|
407
519
|
}
|
|
408
520
|
function learned() {
|
|
409
521
|
if (deps.closed())
|
|
410
522
|
return;
|
|
411
|
-
if (
|
|
523
|
+
if (unlisted.size > 0)
|
|
524
|
+
tellList();
|
|
525
|
+
if (unnamed.size === 0)
|
|
412
526
|
return;
|
|
413
|
-
|
|
414
|
-
const value =
|
|
415
|
-
for (const watcher of [...unlisted]) {
|
|
416
|
-
unlisted.delete(watcher);
|
|
417
|
-
listWatchers.add(watcher);
|
|
418
|
-
watcher(value);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
if (unnamed.size > 0) {
|
|
422
|
-
const value = agents();
|
|
527
|
+
shownList((list) => {
|
|
528
|
+
const value = agents(list);
|
|
423
529
|
for (const watcher of [...unnamed]) {
|
|
424
530
|
unnamed.delete(watcher);
|
|
425
531
|
agentWatchers.add(watcher);
|
|
426
532
|
watcher(value);
|
|
427
533
|
}
|
|
428
|
-
}
|
|
534
|
+
});
|
|
429
535
|
}
|
|
430
|
-
function agents() {
|
|
536
|
+
function agents(list) {
|
|
431
537
|
const named = new Map();
|
|
432
538
|
for (const agent of listedAgents ?? [])
|
|
433
539
|
named.set(agent.agent, agent);
|
|
434
|
-
for (const entry of
|
|
540
|
+
for (const entry of list) {
|
|
435
541
|
named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
|
|
436
542
|
}
|
|
437
543
|
return [...named]
|
|
@@ -461,31 +567,35 @@ export function createConversationStore(deps) {
|
|
|
461
567
|
function emitAgents() {
|
|
462
568
|
if (deps.closed() || agentWatchers.size === 0)
|
|
463
569
|
return;
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
watcher
|
|
570
|
+
shownList((list) => {
|
|
571
|
+
const value = agents(list);
|
|
572
|
+
const shown = JSON.stringify(value);
|
|
573
|
+
if (shown === agentsShown)
|
|
574
|
+
return;
|
|
575
|
+
agentsShown = shown;
|
|
576
|
+
for (const watcher of agentWatchers)
|
|
577
|
+
watcher(value);
|
|
578
|
+
});
|
|
471
579
|
}
|
|
472
580
|
function setLive(conversation, next) {
|
|
473
|
-
const before =
|
|
474
|
-
const began = lives.get(conversation)?.turn?.started;
|
|
581
|
+
const before = rowOf(conversation);
|
|
475
582
|
if (next === null)
|
|
476
583
|
lives.delete(conversation);
|
|
477
584
|
else
|
|
478
585
|
lives.set(conversation, next);
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
586
|
+
const after = rowOf(conversation);
|
|
587
|
+
if (after.state !== before.state || after.started !== before.started)
|
|
482
588
|
emitList();
|
|
589
|
+
else if (after.line !== before.line ||
|
|
590
|
+
after.question !== before.question ||
|
|
591
|
+
after.asked !== before.asked)
|
|
592
|
+
tellSoon();
|
|
483
593
|
}
|
|
484
|
-
function retiredChat(conversation) {
|
|
594
|
+
function retiredChat(conversation, list) {
|
|
485
595
|
if (!conversation.startsWith(NEW_CHAT))
|
|
486
596
|
return false;
|
|
487
597
|
const agent = conversation.slice(NEW_CHAT.length);
|
|
488
|
-
return
|
|
598
|
+
return list.some((entry) => entry.agent === agent && entry.agentRetired);
|
|
489
599
|
}
|
|
490
600
|
function live(conversation, change) {
|
|
491
601
|
const owed = unsent.get(conversation);
|
|
@@ -543,17 +653,23 @@ export function createConversationStore(deps) {
|
|
|
543
653
|
}
|
|
544
654
|
async function remove(leaving) {
|
|
545
655
|
await deps.durably(async (local) => {
|
|
656
|
+
const kept = new Set([
|
|
657
|
+
...(await local.conversations(leaving)).map(({ conversation }) => conversation),
|
|
658
|
+
...(await local.chatDrafts())
|
|
659
|
+
.filter(({ conversation }) => leaving.includes(conversation))
|
|
660
|
+
.map(({ conversation }) => conversation),
|
|
661
|
+
]);
|
|
546
662
|
await local.dropConversations(leaving);
|
|
547
663
|
const changed = [];
|
|
548
664
|
for (const conversation of leaving) {
|
|
549
|
-
if (
|
|
665
|
+
if (kept.has(conversation) || lives.has(conversation))
|
|
550
666
|
changed.push(conversation);
|
|
667
|
+
for (const view of views.values()) {
|
|
668
|
+
if (view.conversation === conversation)
|
|
669
|
+
view.draft = null;
|
|
551
670
|
}
|
|
552
|
-
stored.delete(conversation);
|
|
553
|
-
reads.delete(conversation);
|
|
554
|
-
partials.delete(conversation);
|
|
555
|
-
drafts.delete(conversation);
|
|
556
671
|
behind.delete(conversation);
|
|
672
|
+
heardNewest.delete(conversation);
|
|
557
673
|
unsent.delete(conversation);
|
|
558
674
|
setLive(conversation, null);
|
|
559
675
|
await slide(local, conversation, null);
|
|
@@ -562,19 +678,68 @@ export function createConversationStore(deps) {
|
|
|
562
678
|
deps.publish(conversationChangeSet(changed));
|
|
563
679
|
});
|
|
564
680
|
}
|
|
565
|
-
function
|
|
566
|
-
|
|
567
|
-
|
|
681
|
+
function newestOfListed(entry) {
|
|
682
|
+
if (entry === undefined)
|
|
683
|
+
return undefined;
|
|
684
|
+
const heard = heardNewest.get(entry.conversation);
|
|
685
|
+
if (heard === undefined)
|
|
686
|
+
return entry.newest;
|
|
687
|
+
return entry.newest === null || heard > entry.newest ? heard : entry.newest;
|
|
688
|
+
}
|
|
689
|
+
function missed(conversation, held, entry) {
|
|
690
|
+
const read = held?.read;
|
|
691
|
+
const newest = newestOfListed(entry);
|
|
568
692
|
return (read === undefined ||
|
|
569
693
|
newest === undefined ||
|
|
570
|
-
|
|
694
|
+
held?.partial === true ||
|
|
571
695
|
(newest !== null && (read === null || newest > read)));
|
|
572
696
|
}
|
|
573
|
-
function
|
|
574
|
-
|
|
575
|
-
|
|
697
|
+
async function readsOf(local, conversations) {
|
|
698
|
+
return new Map((await local.conversations(conversations)).map((held) => [held.conversation, held]));
|
|
699
|
+
}
|
|
700
|
+
function retiredDrafts(list) {
|
|
701
|
+
if (!list.some((entry) => entry.agentRetired))
|
|
576
702
|
return;
|
|
577
|
-
|
|
703
|
+
void deps
|
|
704
|
+
.local()
|
|
705
|
+
.then((local) => local.chatDrafts())
|
|
706
|
+
.then((held) => {
|
|
707
|
+
if (!deps.closed() && held.some(({ conversation }) => retiredChat(conversation, list))) {
|
|
708
|
+
deps.again();
|
|
709
|
+
}
|
|
710
|
+
}, () => { });
|
|
711
|
+
}
|
|
712
|
+
function settleBehind(list) {
|
|
713
|
+
void deps
|
|
714
|
+
.durably(async (local) => {
|
|
715
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
716
|
+
const named = [...replicated].filter((conversation) => entries.has(conversation));
|
|
717
|
+
const held = await readsOf(local, named);
|
|
718
|
+
let owed = false;
|
|
719
|
+
for (const conversation of named) {
|
|
720
|
+
if (!missed(conversation, held.get(conversation), entries.get(conversation))) {
|
|
721
|
+
behind.delete(conversation);
|
|
722
|
+
}
|
|
723
|
+
else if (!behind.has(conversation)) {
|
|
724
|
+
gap(conversation);
|
|
725
|
+
owed = true;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return owed;
|
|
729
|
+
})
|
|
730
|
+
.then((owed) => {
|
|
731
|
+
if (deps.closed())
|
|
732
|
+
return;
|
|
733
|
+
deps.settled();
|
|
734
|
+
if (owed)
|
|
735
|
+
deps.again();
|
|
736
|
+
}, () => { });
|
|
737
|
+
}
|
|
738
|
+
function noted(conversation, message) {
|
|
739
|
+
const heard = heardNewest.get(conversation);
|
|
740
|
+
if (heard === undefined || message.position > heard) {
|
|
741
|
+
heardNewest.set(conversation, message.position);
|
|
742
|
+
}
|
|
578
743
|
}
|
|
579
744
|
function gap(conversation) {
|
|
580
745
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
@@ -584,7 +749,7 @@ export function createConversationStore(deps) {
|
|
|
584
749
|
return deps.durably(async (local) => {
|
|
585
750
|
if (!replicated.has(conversation))
|
|
586
751
|
return;
|
|
587
|
-
const read =
|
|
752
|
+
const read = (await readsOf(local, [conversation])).get(conversation)?.read;
|
|
588
753
|
const advances = read !== undefined &&
|
|
589
754
|
!behind.has(conversation) &&
|
|
590
755
|
(read === null || message.position > read);
|
|
@@ -594,19 +759,18 @@ export function createConversationStore(deps) {
|
|
|
594
759
|
messages: [message],
|
|
595
760
|
...(advances ? { read: { at: message.position, partial: false } } : {}),
|
|
596
761
|
});
|
|
597
|
-
stored.add(conversation);
|
|
598
|
-
if (advances)
|
|
599
|
-
reads.set(conversation, message.position);
|
|
600
762
|
sealTurns(conversation, new Set([message.turn]));
|
|
601
763
|
await slide(local, conversation, [message]);
|
|
602
764
|
deps.publish(conversationChangeSet([conversation]));
|
|
765
|
+
tellList();
|
|
603
766
|
});
|
|
604
767
|
}
|
|
605
768
|
async function readHistories(conversations, ended) {
|
|
606
769
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
770
|
+
const held = await readsOf(await deps.local(), conversations);
|
|
607
771
|
let waiting = conversations.map((conversation) => ({
|
|
608
772
|
conversation,
|
|
609
|
-
after:
|
|
773
|
+
after: held.get(conversation)?.read ?? undefined,
|
|
610
774
|
}));
|
|
611
775
|
while (waiting.length > 0) {
|
|
612
776
|
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
@@ -662,15 +826,10 @@ export function createConversationStore(deps) {
|
|
|
662
826
|
return;
|
|
663
827
|
const whole = next === undefined;
|
|
664
828
|
const cursor = next ?? newestOf(arriving, from);
|
|
665
|
-
const
|
|
829
|
+
const held = (await readsOf(local, [conversation])).get(conversation);
|
|
830
|
+
const partial = !whole && (held?.partial === true || held?.read === undefined);
|
|
666
831
|
const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
|
|
667
832
|
await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
|
|
668
|
-
stored.add(conversation);
|
|
669
|
-
reads.set(conversation, cursor);
|
|
670
|
-
if (partial)
|
|
671
|
-
partials.add(conversation);
|
|
672
|
-
else
|
|
673
|
-
partials.delete(conversation);
|
|
674
833
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
675
834
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
676
835
|
behind.delete(conversation);
|
|
@@ -678,6 +837,8 @@ export function createConversationStore(deps) {
|
|
|
678
837
|
if (arriving.length > 0 || sealing || whole) {
|
|
679
838
|
deps.publish(conversationChangeSet([conversation]));
|
|
680
839
|
}
|
|
840
|
+
if (arriving.length > 0)
|
|
841
|
+
tellSoon();
|
|
681
842
|
});
|
|
682
843
|
}
|
|
683
844
|
function heard(event) {
|
|
@@ -744,17 +905,24 @@ export function createConversationStore(deps) {
|
|
|
744
905
|
emit(conversation);
|
|
745
906
|
}
|
|
746
907
|
async function kept(local, conversation, following) {
|
|
747
|
-
|
|
908
|
+
const shown = following === null || empty(following) ? null : following;
|
|
909
|
+
if (shown === null)
|
|
748
910
|
await local.dropChatDraft(conversation);
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
911
|
+
else
|
|
912
|
+
await local.keepChatDrafts([shown]);
|
|
913
|
+
shownDraft(conversation, shown);
|
|
914
|
+
}
|
|
915
|
+
function shownDraft(conversation, draft) {
|
|
916
|
+
for (const view of views.values()) {
|
|
917
|
+
if (view.conversation === conversation)
|
|
918
|
+
view.draft = draft;
|
|
754
919
|
}
|
|
755
920
|
}
|
|
921
|
+
async function draftAt(local, conversation) {
|
|
922
|
+
return (await local.chatDraft(conversation)) ?? undefined;
|
|
923
|
+
}
|
|
756
924
|
async function sealed(local, conversation, turns) {
|
|
757
|
-
const held =
|
|
925
|
+
const held = await draftAt(local, conversation);
|
|
758
926
|
const submitted = held?.submitted ?? [];
|
|
759
927
|
const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
|
|
760
928
|
if (held === undefined || left.length === submitted.length)
|
|
@@ -765,7 +933,7 @@ export function createConversationStore(deps) {
|
|
|
765
933
|
function moved(agent, operation, to) {
|
|
766
934
|
const from = newChat(agent);
|
|
767
935
|
return deps.durably(async (local) => {
|
|
768
|
-
const held =
|
|
936
|
+
const held = await draftAt(local, from);
|
|
769
937
|
const earlier = held?.moves ?? [];
|
|
770
938
|
if (earlier.includes(operation))
|
|
771
939
|
return;
|
|
@@ -775,18 +943,20 @@ export function createConversationStore(deps) {
|
|
|
775
943
|
revision: chatDraftRevision(),
|
|
776
944
|
};
|
|
777
945
|
const { moves: _answered, ...unsent } = held ?? blank;
|
|
778
|
-
const taken =
|
|
946
|
+
const taken = (await draftAt(local, to)) !== undefined || empty(unsent)
|
|
947
|
+
? []
|
|
948
|
+
: [{ ...unsent, conversation: to }];
|
|
779
949
|
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
780
950
|
await local.keepChatDrafts([left, ...taken]);
|
|
781
|
-
|
|
951
|
+
shownDraft(from, left);
|
|
782
952
|
for (const one of taken)
|
|
783
|
-
|
|
953
|
+
shownDraft(to, one);
|
|
784
954
|
deps.publish(conversationChangeSet([from, to]));
|
|
785
955
|
});
|
|
786
956
|
}
|
|
787
957
|
function withDraft(conversation, next) {
|
|
788
958
|
return deps.durably(async (local) => {
|
|
789
|
-
const following = await next(
|
|
959
|
+
const following = await next(await draftAt(local, conversation), local);
|
|
790
960
|
if (following === undefined)
|
|
791
961
|
return;
|
|
792
962
|
await kept(local, conversation, following);
|
|
@@ -868,7 +1038,8 @@ export function createConversationStore(deps) {
|
|
|
868
1038
|
return deps.durably(async (local) => {
|
|
869
1039
|
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
|
|
870
1040
|
const total = asked.total ? await local.messageCount(conversation, phrase) : null;
|
|
871
|
-
|
|
1041
|
+
const marked = await local.jobMarkers(conversation);
|
|
1042
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total), (await local.chatDraft(conversation)) ?? null, await endedIn(conversation), marked));
|
|
872
1043
|
});
|
|
873
1044
|
},
|
|
874
1045
|
async create(input) {
|
|
@@ -876,8 +1047,10 @@ export function createConversationStore(deps) {
|
|
|
876
1047
|
const conversation = answered(result, 'conversation_id');
|
|
877
1048
|
if (conversation === null)
|
|
878
1049
|
return result;
|
|
879
|
-
|
|
1050
|
+
const listed = (await heldList()) ?? [];
|
|
1051
|
+
if (!listed.some((entry) => entry.conversation === conversation)) {
|
|
880
1052
|
creations.add(conversation);
|
|
1053
|
+
}
|
|
881
1054
|
const creation = creationOf(input);
|
|
882
1055
|
if (creation !== null)
|
|
883
1056
|
await moved(creation.agent, creation.operation, conversation);
|
|
@@ -945,43 +1118,40 @@ export function createConversationStore(deps) {
|
|
|
945
1118
|
return {
|
|
946
1119
|
surface,
|
|
947
1120
|
chatDraft,
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
listCursor = cursor;
|
|
955
|
-
deps.stream()?.hold(cursor);
|
|
956
|
-
}
|
|
957
|
-
stored.clear();
|
|
958
|
-
reads.clear();
|
|
959
|
-
partials.clear();
|
|
960
|
-
for (const entry of held) {
|
|
961
|
-
stored.add(entry.conversation);
|
|
962
|
-
if (entry.read !== undefined)
|
|
963
|
-
reads.set(entry.conversation, entry.read);
|
|
964
|
-
if (entry.partial)
|
|
965
|
-
partials.add(entry.conversation);
|
|
966
|
-
}
|
|
967
|
-
drafts.clear();
|
|
968
|
-
for (const record of kept)
|
|
969
|
-
drafts.set(record.conversation, record);
|
|
970
|
-
if (listHeld)
|
|
971
|
-
learned();
|
|
1121
|
+
async opened(local) {
|
|
1122
|
+
const { conversationList, conversationCursor } = await local.facts();
|
|
1123
|
+
if (conversationList === null || listKnown)
|
|
1124
|
+
return;
|
|
1125
|
+
deps.stream()?.hold(conversationCursor);
|
|
1126
|
+
learned();
|
|
972
1127
|
},
|
|
973
1128
|
async reconcile() {
|
|
974
1129
|
if (!listKnown)
|
|
975
1130
|
return;
|
|
976
|
-
const
|
|
1131
|
+
const local = await deps.local();
|
|
1132
|
+
const [held, kept, { conversationList }] = await Promise.all([
|
|
1133
|
+
local.conversations(),
|
|
1134
|
+
local.chatDrafts(),
|
|
1135
|
+
local.facts(),
|
|
1136
|
+
]);
|
|
1137
|
+
const list = conversationList ?? [];
|
|
1138
|
+
const entries = new Map(list.map((entry) => [entry.conversation, entry]));
|
|
1139
|
+
const named = new Set(entries.keys());
|
|
1140
|
+
const reads = new Map(held.map((one) => [one.conversation, one]));
|
|
977
1141
|
const leaving = [
|
|
978
|
-
...new Set([
|
|
1142
|
+
...new Set([
|
|
1143
|
+
...replicated,
|
|
1144
|
+
...reads.keys(),
|
|
1145
|
+
...kept.map(({ conversation }) => conversation),
|
|
1146
|
+
]),
|
|
979
1147
|
].filter((conversation) => !named.has(conversation) &&
|
|
980
1148
|
!creations.has(conversation) &&
|
|
981
|
-
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
1149
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation, list)));
|
|
982
1150
|
for (const conversation of named) {
|
|
983
|
-
if (!replicated.has(conversation) &&
|
|
1151
|
+
if (!replicated.has(conversation) &&
|
|
1152
|
+
missed(conversation, reads.get(conversation), entries.get(conversation))) {
|
|
984
1153
|
behind.add(conversation);
|
|
1154
|
+
}
|
|
985
1155
|
}
|
|
986
1156
|
replicated = named;
|
|
987
1157
|
handReplicated();
|
|
@@ -1012,12 +1182,16 @@ export function createConversationStore(deps) {
|
|
|
1012
1182
|
progress.complete = false;
|
|
1013
1183
|
},
|
|
1014
1184
|
async resend(progress) {
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1185
|
+
const local = await deps.local();
|
|
1186
|
+
const tried = new Set();
|
|
1187
|
+
for (const { conversation } of await local.chatDrafts()) {
|
|
1188
|
+
for (;;) {
|
|
1189
|
+
const submission = (await local.chatDraft(conversation))?.submitted?.find((one) => one.turn === null && !sending.has(one.operation) && !tried.has(one.operation));
|
|
1190
|
+
if (submission === undefined)
|
|
1191
|
+
break;
|
|
1019
1192
|
if (deps.closed())
|
|
1020
1193
|
return;
|
|
1194
|
+
tried.add(submission.operation);
|
|
1021
1195
|
const result = await dispatched(conversation, submission);
|
|
1022
1196
|
if (result.kind === 'revoked-authorization')
|
|
1023
1197
|
return;
|
|
@@ -1055,6 +1229,9 @@ export function createConversationStore(deps) {
|
|
|
1055
1229
|
asked,
|
|
1056
1230
|
watchers: new Set(),
|
|
1057
1231
|
window: null,
|
|
1232
|
+
draft: null,
|
|
1233
|
+
ended: null,
|
|
1234
|
+
marked: [],
|
|
1058
1235
|
more: false,
|
|
1059
1236
|
total: null,
|
|
1060
1237
|
};
|
|
@@ -1066,7 +1243,10 @@ export function createConversationStore(deps) {
|
|
|
1066
1243
|
if (view.window !== null)
|
|
1067
1244
|
return;
|
|
1068
1245
|
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1246
|
+
view.draft = (await local.chatDraft(conversation)) ?? null;
|
|
1247
|
+
view.ended = await endedIn(conversation);
|
|
1069
1248
|
view.window = messages;
|
|
1249
|
+
view.marked = await local.jobMarkers(conversation);
|
|
1070
1250
|
view.more = more;
|
|
1071
1251
|
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
1072
1252
|
}), reading.signal)
|
|
@@ -1075,7 +1255,7 @@ export function createConversationStore(deps) {
|
|
|
1075
1255
|
return;
|
|
1076
1256
|
watcher.ready = true;
|
|
1077
1257
|
const window = view.window ?? [];
|
|
1078
|
-
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
1258
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window), view.draft, view.ended, view.marked));
|
|
1079
1259
|
})
|
|
1080
1260
|
.catch(() => { });
|
|
1081
1261
|
return () => {
|
|
@@ -1110,58 +1290,43 @@ export function createConversationStore(deps) {
|
|
|
1110
1290
|
},
|
|
1111
1291
|
receive(event) {
|
|
1112
1292
|
if (deps.closed())
|
|
1113
|
-
return
|
|
1114
|
-
if (event.kind === 'list'
|
|
1115
|
-
if (event.
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
for (const entry of event.entries)
|
|
1119
|
-
|
|
1120
|
-
listed(entry);
|
|
1121
|
-
else
|
|
1122
|
-
relisting.set(entry.conversation, { ...entry });
|
|
1123
|
-
}
|
|
1124
|
-
if (event.more)
|
|
1125
|
-
return false;
|
|
1126
|
-
if (relisting !== null) {
|
|
1127
|
-
listing.clear();
|
|
1128
|
-
for (const entry of relisting.values())
|
|
1129
|
-
listed(entry);
|
|
1130
|
-
relisting = null;
|
|
1131
|
-
}
|
|
1132
|
-
listCursor = event.cursor;
|
|
1133
|
-
deps.stream()?.hold(event.cursor);
|
|
1134
|
-
listKnown = true;
|
|
1135
|
-
for (const conversation of replicated) {
|
|
1136
|
-
if (!listing.has(conversation))
|
|
1137
|
-
continue;
|
|
1138
|
-
if (!missed(conversation))
|
|
1139
|
-
behind.delete(conversation);
|
|
1140
|
-
else if (!behind.has(conversation))
|
|
1141
|
-
gap(conversation);
|
|
1142
|
-
}
|
|
1143
|
-
openingEnded();
|
|
1144
|
-
}
|
|
1145
|
-
else if (event.kind === 'listed') {
|
|
1146
|
-
listed(event.entry);
|
|
1147
|
-
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
1148
|
-
}
|
|
1149
|
-
else {
|
|
1150
|
-
listing.delete(event.conversation);
|
|
1151
|
-
relisting?.delete(event.conversation);
|
|
1293
|
+
return;
|
|
1294
|
+
if (event.kind === 'list') {
|
|
1295
|
+
if (event.whole)
|
|
1296
|
+
relisting ??= new Map();
|
|
1297
|
+
if (relisting !== null) {
|
|
1298
|
+
for (const entry of event.entries)
|
|
1299
|
+
relisting.set(entry.conversation, { ...entry });
|
|
1152
1300
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1301
|
+
if (event.more && relisting !== null)
|
|
1302
|
+
return;
|
|
1303
|
+
const whole = relisting;
|
|
1304
|
+
relisting = null;
|
|
1305
|
+
listChanged({
|
|
1306
|
+
replacing: whole !== null,
|
|
1307
|
+
set: whole === null ? event.entries : [...whole.values()],
|
|
1308
|
+
gone: null,
|
|
1309
|
+
...(event.more ? { paged: true } : { cursor: event.cursor }),
|
|
1310
|
+
});
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
if (event.kind === 'listed') {
|
|
1314
|
+
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
1315
|
+
listChanged({ replacing: false, set: [event.entry], gone: null });
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
if (event.kind === 'unlisted') {
|
|
1319
|
+
relisting?.delete(event.conversation);
|
|
1320
|
+
listChanged({ replacing: false, set: [], gone: event.conversation });
|
|
1321
|
+
return;
|
|
1160
1322
|
}
|
|
1161
1323
|
heard(event);
|
|
1162
|
-
return false;
|
|
1163
1324
|
},
|
|
1164
1325
|
close() {
|
|
1326
|
+
closing.abort();
|
|
1327
|
+
if (rowTimer !== null)
|
|
1328
|
+
clearTimeout(rowTimer);
|
|
1329
|
+
rowTimer = null;
|
|
1165
1330
|
views.clear();
|
|
1166
1331
|
lives.clear();
|
|
1167
1332
|
unsent.clear();
|