@agentshouse/mdruntime 0.1.0-ce5c5bb29be5a → 0.1.0-ce6bc9239a3ee
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 +81 -49
- 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 +9 -5
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +209 -75
- 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 +16 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/collection.d.ts +2 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/compile.d.ts +6 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +262 -318
- package/dist/connection.d.ts +20 -44
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1438 -3393
- 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 +26 -17
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +441 -307
- 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 -7
- 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 -6
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +22 -26
- 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 +63 -22
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/memory.d.ts +11 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +171 -81
- 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 +14 -209
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- 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 +12 -12
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +182 -136
- package/dist/react.d.ts +5 -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 +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 +6 -4
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +32 -4
- package/dist/replica.d.ts +8 -8
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +50 -15
- package/dist/room-draft.d.ts +4 -3
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +65 -52
- 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 -21
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +48 -184
- 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 -145
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +117 -367
- package/dist/stored.d.ts +58 -32
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +264 -187
- package/dist/tables.d.ts +107 -25
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +193 -47
- 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 +15 -4
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +6 -1
- 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: [],
|
|
@@ -12,6 +16,7 @@ const QUIET = {
|
|
|
12
16
|
commands: [],
|
|
13
17
|
options: [],
|
|
14
18
|
starting: null,
|
|
19
|
+
subagents: [],
|
|
15
20
|
link: null,
|
|
16
21
|
deliveryUnknown: [],
|
|
17
22
|
};
|
|
@@ -21,6 +26,7 @@ const FACTS = [
|
|
|
21
26
|
'commands',
|
|
22
27
|
'options',
|
|
23
28
|
'starting',
|
|
29
|
+
'subagents',
|
|
24
30
|
'link',
|
|
25
31
|
'deliveryUnknown',
|
|
26
32
|
];
|
|
@@ -31,27 +37,30 @@ export function unheldView(conversation) {
|
|
|
31
37
|
turn: null,
|
|
32
38
|
questions: [],
|
|
33
39
|
process: null,
|
|
40
|
+
processEnded: null,
|
|
34
41
|
commands: [],
|
|
35
42
|
options: [],
|
|
36
43
|
starting: null,
|
|
44
|
+
subagents: [],
|
|
37
45
|
link: null,
|
|
38
46
|
deliveryUnknown: [],
|
|
39
47
|
chatDraft: null,
|
|
48
|
+
jobs: [],
|
|
40
49
|
};
|
|
41
50
|
}
|
|
42
51
|
export const SESSION_STARTED = 'Session started';
|
|
43
52
|
const NEW_CHAT = 'new:';
|
|
44
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;
|
|
45
56
|
export function newChat(agent) {
|
|
46
57
|
return `${NEW_CHAT}${agent}`;
|
|
47
58
|
}
|
|
48
59
|
function creationOf(input) {
|
|
49
60
|
if (typeof input !== 'object' || input === null)
|
|
50
61
|
return null;
|
|
51
|
-
const { agent_id: agent, operation_id: operation
|
|
52
|
-
return typeof agent === 'string' && typeof operation === 'string'
|
|
53
|
-
? { agent, operation }
|
|
54
|
-
: null;
|
|
62
|
+
const { agent_id: agent, operation_id: operation } = input;
|
|
63
|
+
return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
|
|
55
64
|
}
|
|
56
65
|
function startedTurn(turn, operation) {
|
|
57
66
|
if (operation.op === 'begin')
|
|
@@ -210,7 +219,14 @@ function askedOf(options) {
|
|
|
210
219
|
}
|
|
211
220
|
return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
|
|
212
221
|
}
|
|
213
|
-
|
|
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
|
+
}
|
|
214
230
|
const DURABLE_WRITE = {
|
|
215
231
|
kind: 'rejected',
|
|
216
232
|
code: 'durable-write',
|
|
@@ -244,10 +260,6 @@ function returned(draft, submission, others) {
|
|
|
244
260
|
}, others);
|
|
245
261
|
}
|
|
246
262
|
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
263
|
const sending = new Map();
|
|
252
264
|
const behind = new Set();
|
|
253
265
|
const catching = new Map();
|
|
@@ -255,27 +267,27 @@ export function createConversationStore(deps) {
|
|
|
255
267
|
const lives = new Map();
|
|
256
268
|
const unsent = new Map();
|
|
257
269
|
const views = new Map();
|
|
258
|
-
const
|
|
270
|
+
const heardNewest = new Map();
|
|
271
|
+
let relisting = null;
|
|
259
272
|
const creations = new Set();
|
|
260
273
|
const listWatchers = new Set();
|
|
261
274
|
const unlisted = new Set();
|
|
262
275
|
let listKnown = false;
|
|
263
|
-
let
|
|
264
|
-
let
|
|
276
|
+
let listOwed = false;
|
|
277
|
+
let listTelling = false;
|
|
278
|
+
let rowTimer = null;
|
|
279
|
+
const closing = new AbortController();
|
|
265
280
|
const agentWatchers = new Set();
|
|
266
281
|
const unnamed = new Set();
|
|
267
|
-
let
|
|
268
|
-
let
|
|
269
|
-
let
|
|
270
|
-
let agentsRead = null;
|
|
271
|
-
let agentsFrom;
|
|
282
|
+
let agentsShown = null;
|
|
283
|
+
let listedAgents = null;
|
|
284
|
+
let agentsReading = false;
|
|
272
285
|
let replicated = new Set();
|
|
273
286
|
let handedReplicated;
|
|
274
287
|
function liveOf(conversation) {
|
|
275
288
|
return lives.get(conversation) ?? QUIET;
|
|
276
289
|
}
|
|
277
|
-
function viewOf(conversation, held, frame) {
|
|
278
|
-
const draft = drafts.get(conversation);
|
|
290
|
+
function viewOf(conversation, held, frame, draft, ended, marked) {
|
|
279
291
|
const facts = liveOf(conversation);
|
|
280
292
|
return {
|
|
281
293
|
conversation,
|
|
@@ -289,14 +301,17 @@ export function createConversationStore(deps) {
|
|
|
289
301
|
turn: structuredClone(facts.turn),
|
|
290
302
|
questions: structuredClone(facts.questions),
|
|
291
303
|
process: facts.process === null ? null : { ...facts.process },
|
|
304
|
+
processEnded: ended,
|
|
292
305
|
commands: structuredClone(facts.commands),
|
|
293
306
|
options: structuredClone(facts.options),
|
|
294
307
|
starting: facts.starting,
|
|
308
|
+
subagents: structuredClone(facts.subagents),
|
|
295
309
|
link: facts.link === null ? null : { ...facts.link },
|
|
296
310
|
deliveryUnknown: [...facts.deliveryUnknown],
|
|
297
|
-
chatDraft: draft ===
|
|
298
|
-
|
|
299
|
-
|
|
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),
|
|
300
315
|
};
|
|
301
316
|
}
|
|
302
317
|
function emit(conversation) {
|
|
@@ -308,7 +323,7 @@ export function createConversationStore(deps) {
|
|
|
308
323
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
309
324
|
if (ready.length === 0)
|
|
310
325
|
continue;
|
|
311
|
-
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);
|
|
312
327
|
for (const watcher of ready)
|
|
313
328
|
watcher.notify(value);
|
|
314
329
|
}
|
|
@@ -318,11 +333,13 @@ export function createConversationStore(deps) {
|
|
|
318
333
|
}
|
|
319
334
|
async function slide(local, conversation, arriving) {
|
|
320
335
|
let count;
|
|
336
|
+
let marked;
|
|
321
337
|
for (const view of views.values()) {
|
|
322
338
|
if (view.conversation !== conversation || view.window === null)
|
|
323
339
|
continue;
|
|
324
340
|
if (arriving === null) {
|
|
325
341
|
view.window = [];
|
|
342
|
+
view.marked = [];
|
|
326
343
|
view.more = false;
|
|
327
344
|
if (view.total !== null)
|
|
328
345
|
view.total = 0;
|
|
@@ -332,6 +349,10 @@ export function createConversationStore(deps) {
|
|
|
332
349
|
const next = windowOf(view.window, joining, view.asked.limit);
|
|
333
350
|
view.window = next.messages;
|
|
334
351
|
view.more ||= next.dropped;
|
|
352
|
+
if (arriving.length > 0) {
|
|
353
|
+
marked ??= await local.jobMarkers(conversation);
|
|
354
|
+
view.marked = marked;
|
|
355
|
+
}
|
|
335
356
|
if (view.total !== null && arriving.length > 0) {
|
|
336
357
|
count ??= await local.messageCount(conversation);
|
|
337
358
|
view.total = count;
|
|
@@ -344,148 +365,234 @@ export function createConversationStore(deps) {
|
|
|
344
365
|
return 'asking';
|
|
345
366
|
return facts.turn === null ? 'idle' : 'working';
|
|
346
367
|
}
|
|
347
|
-
function
|
|
348
|
-
|
|
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) => ({
|
|
349
386
|
conversation: entry.conversation,
|
|
350
|
-
agent: { identity: entry.agent, name:
|
|
387
|
+
agent: { identity: entry.agent, name: entry.agentName },
|
|
351
388
|
name: entry.name,
|
|
352
389
|
created: entry.created,
|
|
353
390
|
finished: entry.finished,
|
|
354
391
|
outcome: entry.outcome,
|
|
355
392
|
waiting: entry.waiting,
|
|
356
|
-
|
|
357
|
-
|
|
393
|
+
...rowOf(entry.conversation),
|
|
394
|
+
...heldFacts(rows.get(entry.conversation), entry),
|
|
358
395
|
}));
|
|
359
396
|
}
|
|
360
|
-
function
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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)
|
|
367
434
|
return;
|
|
368
|
-
|
|
369
|
-
|
|
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);
|
|
370
471
|
});
|
|
371
472
|
}
|
|
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);
|
|
511
|
+
}
|
|
372
512
|
function emitList() {
|
|
373
513
|
if (deps.closed())
|
|
374
514
|
return;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
for (const watcher of listWatchers)
|
|
378
|
-
watcher(value);
|
|
379
|
-
}
|
|
515
|
+
tellList();
|
|
516
|
+
emitAgents();
|
|
380
517
|
learned();
|
|
381
518
|
}
|
|
382
|
-
function unlearnable() {
|
|
383
|
-
return !deps.online() || (deps.listed() && deps.agents() === undefined);
|
|
384
|
-
}
|
|
385
519
|
function learned() {
|
|
386
520
|
if (deps.closed())
|
|
387
521
|
return;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
unlisted.delete(watcher);
|
|
395
|
-
listWatchers.add(watcher);
|
|
396
|
-
watcher(value);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (unnamed.size > 0 && named) {
|
|
400
|
-
const value = agents();
|
|
522
|
+
if (unlisted.size > 0)
|
|
523
|
+
tellList();
|
|
524
|
+
if (unnamed.size === 0)
|
|
525
|
+
return;
|
|
526
|
+
shownList((list) => {
|
|
527
|
+
const value = agents(list);
|
|
401
528
|
for (const watcher of [...unnamed]) {
|
|
402
529
|
unnamed.delete(watcher);
|
|
403
530
|
agentWatchers.add(watcher);
|
|
404
531
|
watcher(value);
|
|
405
532
|
}
|
|
406
|
-
}
|
|
533
|
+
});
|
|
407
534
|
}
|
|
408
|
-
function agents() {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
.
|
|
535
|
+
function agents(list) {
|
|
536
|
+
const named = new Map();
|
|
537
|
+
for (const agent of listedAgents ?? [])
|
|
538
|
+
named.set(agent.agent, agent);
|
|
539
|
+
for (const entry of list) {
|
|
540
|
+
named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
|
|
541
|
+
}
|
|
542
|
+
return [...named]
|
|
543
|
+
.filter(([, agent]) => !agent.retired)
|
|
544
|
+
.map(([identity, agent]) => ({ identity, name: agent.name }))
|
|
412
545
|
.sort((left, right) => left.name === right.name
|
|
413
546
|
? left.identity.localeCompare(right.identity)
|
|
414
547
|
: left.name.localeCompare(right.name));
|
|
415
548
|
}
|
|
549
|
+
function readAgents() {
|
|
550
|
+
const stream = deps.stream();
|
|
551
|
+
if (stream?.agents === undefined || !deps.online() || agentsReading)
|
|
552
|
+
return;
|
|
553
|
+
agentsReading = true;
|
|
554
|
+
void stream
|
|
555
|
+
.agents()
|
|
556
|
+
.then((read) => {
|
|
557
|
+
if (read === null)
|
|
558
|
+
return;
|
|
559
|
+
listedAgents = read;
|
|
560
|
+
emitAgents();
|
|
561
|
+
}, () => { })
|
|
562
|
+
.finally(() => {
|
|
563
|
+
agentsReading = false;
|
|
564
|
+
});
|
|
565
|
+
}
|
|
416
566
|
function emitAgents() {
|
|
417
567
|
if (deps.closed() || agentWatchers.size === 0)
|
|
418
568
|
return;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
+
});
|
|
422
578
|
}
|
|
423
579
|
function setLive(conversation, next) {
|
|
424
|
-
const before =
|
|
425
|
-
const began = lives.get(conversation)?.turn?.started;
|
|
580
|
+
const before = rowOf(conversation);
|
|
426
581
|
if (next === null)
|
|
427
582
|
lives.delete(conversation);
|
|
428
583
|
else
|
|
429
584
|
lives.set(conversation, next);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
433
|
-
emitList();
|
|
434
|
-
}
|
|
435
|
-
async function readAgents() {
|
|
436
|
-
const declaration = deps.agents();
|
|
437
|
-
agentsFrom = JSON.stringify(declaration);
|
|
438
|
-
if (declaration === undefined)
|
|
439
|
-
return;
|
|
440
|
-
const names = new Map();
|
|
441
|
-
const retired = new Set();
|
|
442
|
-
let covered = null;
|
|
443
|
-
for (let after;;) {
|
|
444
|
-
const read = await deps.documents(declaration, after);
|
|
445
|
-
if ('pending' in read)
|
|
446
|
-
return;
|
|
447
|
-
const coverage = JSON.stringify(read.coverage);
|
|
448
|
-
if (covered !== null && coverage !== covered)
|
|
449
|
-
return readAgents();
|
|
450
|
-
covered = coverage;
|
|
451
|
-
for (const document of read.documents) {
|
|
452
|
-
const identity = document.fields?.[declaration.identity];
|
|
453
|
-
const name = document.fields?.[declaration.name];
|
|
454
|
-
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
455
|
-
continue;
|
|
456
|
-
names.set(identity, name);
|
|
457
|
-
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
458
|
-
if (ended !== undefined && ended !== null)
|
|
459
|
-
retired.add(identity);
|
|
460
|
-
}
|
|
461
|
-
if (read.next === undefined)
|
|
462
|
-
break;
|
|
463
|
-
after = read.next;
|
|
464
|
-
}
|
|
465
|
-
agentsKnown = true;
|
|
466
|
-
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
467
|
-
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents])) {
|
|
468
|
-
learned();
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
agentNames = names;
|
|
472
|
-
retiredAgents = retired;
|
|
473
|
-
emitAgents();
|
|
474
|
-
if (named)
|
|
585
|
+
const after = rowOf(conversation);
|
|
586
|
+
if (after.state !== before.state || after.started !== before.started)
|
|
475
587
|
emitList();
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if (ended.length > 0)
|
|
479
|
-
await remove(ended);
|
|
588
|
+
else if (after.line !== before.line || after.question !== before.question)
|
|
589
|
+
tellSoon();
|
|
480
590
|
}
|
|
481
|
-
function retiredChat(conversation) {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
agentsRead = null;
|
|
487
|
-
});
|
|
488
|
-
return agentsRead;
|
|
591
|
+
function retiredChat(conversation, list) {
|
|
592
|
+
if (!conversation.startsWith(NEW_CHAT))
|
|
593
|
+
return false;
|
|
594
|
+
const agent = conversation.slice(NEW_CHAT.length);
|
|
595
|
+
return list.some((entry) => entry.agent === agent && entry.agentRetired);
|
|
489
596
|
}
|
|
490
597
|
function live(conversation, change) {
|
|
491
598
|
const owed = unsent.get(conversation);
|
|
@@ -508,6 +615,7 @@ export function createConversationStore(deps) {
|
|
|
508
615
|
commands: facts.has('commands') ? QUIET.commands : held.commands,
|
|
509
616
|
options: facts.has('options') ? QUIET.options : held.options,
|
|
510
617
|
starting: facts.has('starting') ? QUIET.starting : held.starting,
|
|
618
|
+
subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
|
|
511
619
|
link: facts.has('link') ? QUIET.link : held.link,
|
|
512
620
|
deliveryUnknown: facts.has('deliveryUnknown')
|
|
513
621
|
? QUIET.deliveryUnknown
|
|
@@ -542,17 +650,23 @@ export function createConversationStore(deps) {
|
|
|
542
650
|
}
|
|
543
651
|
async function remove(leaving) {
|
|
544
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
|
+
]);
|
|
545
659
|
await local.dropConversations(leaving);
|
|
546
660
|
const changed = [];
|
|
547
661
|
for (const conversation of leaving) {
|
|
548
|
-
if (
|
|
662
|
+
if (kept.has(conversation) || lives.has(conversation))
|
|
549
663
|
changed.push(conversation);
|
|
664
|
+
for (const view of views.values()) {
|
|
665
|
+
if (view.conversation === conversation)
|
|
666
|
+
view.draft = null;
|
|
550
667
|
}
|
|
551
|
-
stored.delete(conversation);
|
|
552
|
-
reads.delete(conversation);
|
|
553
|
-
partials.delete(conversation);
|
|
554
|
-
drafts.delete(conversation);
|
|
555
668
|
behind.delete(conversation);
|
|
669
|
+
heardNewest.delete(conversation);
|
|
556
670
|
unsent.delete(conversation);
|
|
557
671
|
setLive(conversation, null);
|
|
558
672
|
await slide(local, conversation, null);
|
|
@@ -561,19 +675,68 @@ export function createConversationStore(deps) {
|
|
|
561
675
|
deps.publish(conversationChangeSet(changed));
|
|
562
676
|
});
|
|
563
677
|
}
|
|
564
|
-
function
|
|
565
|
-
|
|
566
|
-
|
|
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);
|
|
567
689
|
return (read === undefined ||
|
|
568
690
|
newest === undefined ||
|
|
569
|
-
|
|
691
|
+
held?.partial === true ||
|
|
570
692
|
(newest !== null && (read === null || newest > read)));
|
|
571
693
|
}
|
|
572
|
-
function
|
|
573
|
-
|
|
574
|
-
|
|
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))
|
|
575
699
|
return;
|
|
576
|
-
|
|
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
|
+
}
|
|
577
740
|
}
|
|
578
741
|
function gap(conversation) {
|
|
579
742
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
@@ -583,7 +746,7 @@ export function createConversationStore(deps) {
|
|
|
583
746
|
return deps.durably(async (local) => {
|
|
584
747
|
if (!replicated.has(conversation))
|
|
585
748
|
return;
|
|
586
|
-
const read =
|
|
749
|
+
const read = (await readsOf(local, [conversation])).get(conversation)?.read;
|
|
587
750
|
const advances = read !== undefined &&
|
|
588
751
|
!behind.has(conversation) &&
|
|
589
752
|
(read === null || message.position > read);
|
|
@@ -593,23 +756,22 @@ export function createConversationStore(deps) {
|
|
|
593
756
|
messages: [message],
|
|
594
757
|
...(advances ? { read: { at: message.position, partial: false } } : {}),
|
|
595
758
|
});
|
|
596
|
-
stored.add(conversation);
|
|
597
|
-
if (advances)
|
|
598
|
-
reads.set(conversation, message.position);
|
|
599
759
|
sealTurns(conversation, new Set([message.turn]));
|
|
600
760
|
await slide(local, conversation, [message]);
|
|
601
761
|
deps.publish(conversationChangeSet([conversation]));
|
|
762
|
+
tellList();
|
|
602
763
|
});
|
|
603
764
|
}
|
|
604
765
|
async function readHistories(conversations, ended) {
|
|
605
766
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
767
|
+
const held = await readsOf(await deps.local(), conversations);
|
|
606
768
|
let waiting = conversations.map((conversation) => ({
|
|
607
769
|
conversation,
|
|
608
|
-
after:
|
|
770
|
+
after: held.get(conversation)?.read ?? undefined,
|
|
609
771
|
}));
|
|
610
772
|
while (waiting.length > 0) {
|
|
611
773
|
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
612
|
-
const result = await deps.
|
|
774
|
+
const result = await deps.history({
|
|
613
775
|
conversations: asking.map(({ conversation, after }) => ({
|
|
614
776
|
conversation,
|
|
615
777
|
...(after === undefined ? {} : { after }),
|
|
@@ -661,15 +823,10 @@ export function createConversationStore(deps) {
|
|
|
661
823
|
return;
|
|
662
824
|
const whole = next === undefined;
|
|
663
825
|
const cursor = next ?? newestOf(arriving, from);
|
|
664
|
-
const
|
|
826
|
+
const held = (await readsOf(local, [conversation])).get(conversation);
|
|
827
|
+
const partial = !whole && (held?.partial === true || held?.read === undefined);
|
|
665
828
|
const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
|
|
666
829
|
await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
|
|
667
|
-
stored.add(conversation);
|
|
668
|
-
reads.set(conversation, cursor);
|
|
669
|
-
if (partial)
|
|
670
|
-
partials.add(conversation);
|
|
671
|
-
else
|
|
672
|
-
partials.delete(conversation);
|
|
673
830
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
674
831
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
675
832
|
behind.delete(conversation);
|
|
@@ -677,6 +834,8 @@ export function createConversationStore(deps) {
|
|
|
677
834
|
if (arriving.length > 0 || sealing || whole) {
|
|
678
835
|
deps.publish(conversationChangeSet([conversation]));
|
|
679
836
|
}
|
|
837
|
+
if (arriving.length > 0)
|
|
838
|
+
tellSoon();
|
|
680
839
|
});
|
|
681
840
|
}
|
|
682
841
|
function heard(event) {
|
|
@@ -712,6 +871,10 @@ export function createConversationStore(deps) {
|
|
|
712
871
|
live(conversation, { starting: event.step });
|
|
713
872
|
return;
|
|
714
873
|
}
|
|
874
|
+
if (event.kind === 'subagents') {
|
|
875
|
+
live(conversation, { subagents: structuredClone(event.subagents) });
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
715
878
|
if (event.kind === 'link') {
|
|
716
879
|
live(conversation, { link: { connected: event.connected } });
|
|
717
880
|
return;
|
|
@@ -739,17 +902,24 @@ export function createConversationStore(deps) {
|
|
|
739
902
|
emit(conversation);
|
|
740
903
|
}
|
|
741
904
|
async function kept(local, conversation, following) {
|
|
742
|
-
|
|
905
|
+
const shown = following === null || empty(following) ? null : following;
|
|
906
|
+
if (shown === null)
|
|
743
907
|
await local.dropChatDraft(conversation);
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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;
|
|
749
916
|
}
|
|
750
917
|
}
|
|
918
|
+
async function draftAt(local, conversation) {
|
|
919
|
+
return (await local.chatDraft(conversation)) ?? undefined;
|
|
920
|
+
}
|
|
751
921
|
async function sealed(local, conversation, turns) {
|
|
752
|
-
const held =
|
|
922
|
+
const held = await draftAt(local, conversation);
|
|
753
923
|
const submitted = held?.submitted ?? [];
|
|
754
924
|
const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
|
|
755
925
|
if (held === undefined || left.length === submitted.length)
|
|
@@ -760,8 +930,8 @@ export function createConversationStore(deps) {
|
|
|
760
930
|
function moved(agent, operation, to) {
|
|
761
931
|
const from = newChat(agent);
|
|
762
932
|
return deps.durably(async (local) => {
|
|
763
|
-
const held =
|
|
764
|
-
const earlier =
|
|
933
|
+
const held = await draftAt(local, from);
|
|
934
|
+
const earlier = held?.moves ?? [];
|
|
765
935
|
if (earlier.includes(operation))
|
|
766
936
|
return;
|
|
767
937
|
const blank = {
|
|
@@ -770,18 +940,20 @@ export function createConversationStore(deps) {
|
|
|
770
940
|
revision: chatDraftRevision(),
|
|
771
941
|
};
|
|
772
942
|
const { moves: _answered, ...unsent } = held ?? blank;
|
|
773
|
-
const taken =
|
|
943
|
+
const taken = (await draftAt(local, to)) !== undefined || empty(unsent)
|
|
944
|
+
? []
|
|
945
|
+
: [{ ...unsent, conversation: to }];
|
|
774
946
|
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
775
947
|
await local.keepChatDrafts([left, ...taken]);
|
|
776
|
-
|
|
948
|
+
shownDraft(from, left);
|
|
777
949
|
for (const one of taken)
|
|
778
|
-
|
|
950
|
+
shownDraft(to, one);
|
|
779
951
|
deps.publish(conversationChangeSet([from, to]));
|
|
780
952
|
});
|
|
781
953
|
}
|
|
782
954
|
function withDraft(conversation, next) {
|
|
783
955
|
return deps.durably(async (local) => {
|
|
784
|
-
const following = await next(
|
|
956
|
+
const following = await next(await draftAt(local, conversation), local);
|
|
785
957
|
if (following === undefined)
|
|
786
958
|
return;
|
|
787
959
|
await kept(local, conversation, following);
|
|
@@ -790,10 +962,8 @@ export function createConversationStore(deps) {
|
|
|
790
962
|
}
|
|
791
963
|
async function submitted(conversation, operation, draft) {
|
|
792
964
|
let submission;
|
|
793
|
-
let earlier = false;
|
|
794
965
|
await withDraft(conversation, (current) => {
|
|
795
966
|
submission = current?.submitted?.find((one) => one.operation === operation);
|
|
796
|
-
earlier = submission !== undefined;
|
|
797
967
|
if (submission !== undefined)
|
|
798
968
|
return undefined;
|
|
799
969
|
const said = draft ?? current;
|
|
@@ -806,23 +976,15 @@ export function createConversationStore(deps) {
|
|
|
806
976
|
submitted: [...(current?.submitted ?? []), submission],
|
|
807
977
|
};
|
|
808
978
|
});
|
|
809
|
-
return
|
|
810
|
-
}
|
|
811
|
-
function forgotten(conversation, submission) {
|
|
812
|
-
return withDraft(conversation, (current) => {
|
|
813
|
-
const held = current?.submitted ?? [];
|
|
814
|
-
if (current === undefined || !held.includes(submission))
|
|
815
|
-
return undefined;
|
|
816
|
-
return withSubmitted(current, held.filter((one) => one !== submission));
|
|
817
|
-
});
|
|
979
|
+
return submission;
|
|
818
980
|
}
|
|
819
981
|
async function dispatched(conversation, submission) {
|
|
820
982
|
const dispatch = { answered: false };
|
|
821
983
|
sending.set(submission.operation, dispatch);
|
|
822
984
|
try {
|
|
823
|
-
const result = await invoke('
|
|
824
|
-
text: submission.text,
|
|
985
|
+
const result = await invoke('conversations/send', conversation, {
|
|
825
986
|
operation_id: submission.operation,
|
|
987
|
+
text: submission.text,
|
|
826
988
|
});
|
|
827
989
|
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
828
990
|
if (result.kind === 'revoked-authorization')
|
|
@@ -831,7 +993,7 @@ export function createConversationStore(deps) {
|
|
|
831
993
|
deps.unanswered();
|
|
832
994
|
return result;
|
|
833
995
|
}
|
|
834
|
-
const turn = answered(result, '
|
|
996
|
+
const turn = answered(result, 'message_id');
|
|
835
997
|
await withDraft(conversation, async (current, local) => {
|
|
836
998
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
837
999
|
if (current === undefined || held === undefined || held.turn !== null)
|
|
@@ -851,12 +1013,11 @@ export function createConversationStore(deps) {
|
|
|
851
1013
|
sending.delete(submission.operation);
|
|
852
1014
|
}
|
|
853
1015
|
}
|
|
854
|
-
function invoke(name, conversation, extra = {}) {
|
|
855
|
-
return deps.call(name, { conversation, ...extra });
|
|
1016
|
+
function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
|
|
1017
|
+
return deps.call(name, { conversation_id: conversation, ...extra });
|
|
856
1018
|
}
|
|
857
1019
|
const surface = {
|
|
858
|
-
|
|
859
|
-
await agentsReady();
|
|
1020
|
+
list() {
|
|
860
1021
|
return new Promise((settle) => {
|
|
861
1022
|
const answer = (listed) => {
|
|
862
1023
|
unlisted.delete(answer);
|
|
@@ -868,20 +1029,25 @@ export function createConversationStore(deps) {
|
|
|
868
1029
|
});
|
|
869
1030
|
},
|
|
870
1031
|
async read(conversation, options = {}) {
|
|
871
|
-
const
|
|
1032
|
+
const { text, ...windowed } = options;
|
|
1033
|
+
const asked = askedOf(windowed);
|
|
1034
|
+
const phrase = phraseOf(text);
|
|
872
1035
|
return deps.durably(async (local) => {
|
|
873
|
-
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
874
|
-
const total = asked.total ? await local.messageCount(conversation) : null;
|
|
875
|
-
|
|
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));
|
|
876
1040
|
});
|
|
877
1041
|
},
|
|
878
1042
|
async create(input) {
|
|
879
|
-
const result = await deps.call('
|
|
880
|
-
const conversation = answered(result, '
|
|
1043
|
+
const result = await deps.call('conversations/create', input);
|
|
1044
|
+
const conversation = answered(result, 'conversation_id');
|
|
881
1045
|
if (conversation === null)
|
|
882
1046
|
return result;
|
|
883
|
-
|
|
1047
|
+
const listed = (await heldList()) ?? [];
|
|
1048
|
+
if (!listed.some((entry) => entry.conversation === conversation)) {
|
|
884
1049
|
creations.add(conversation);
|
|
1050
|
+
}
|
|
885
1051
|
const creation = creationOf(input);
|
|
886
1052
|
if (creation !== null)
|
|
887
1053
|
await moved(creation.agent, creation.operation, conversation);
|
|
@@ -898,11 +1064,7 @@ export function createConversationStore(deps) {
|
|
|
898
1064
|
});
|
|
899
1065
|
if (held === null)
|
|
900
1066
|
return DURABLE_WRITE;
|
|
901
|
-
|
|
902
|
-
if (!earlier || !deps.expired(operation))
|
|
903
|
-
return dispatched(conversation, submission);
|
|
904
|
-
await forgotten(conversation, submission);
|
|
905
|
-
return EXPIRED;
|
|
1067
|
+
return dispatched(conversation, held);
|
|
906
1068
|
}
|
|
907
1069
|
if (draft !== undefined) {
|
|
908
1070
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
@@ -912,28 +1074,19 @@ export function createConversationStore(deps) {
|
|
|
912
1074
|
return OFFLINE;
|
|
913
1075
|
},
|
|
914
1076
|
answer(conversation, answer) {
|
|
915
|
-
return invoke('
|
|
1077
|
+
return invoke('conversations/interaction/answer', conversation, {
|
|
1078
|
+
operation_id: changeBatchIdentity(),
|
|
1079
|
+
...answer,
|
|
1080
|
+
});
|
|
916
1081
|
},
|
|
917
1082
|
interrupt(conversation) {
|
|
918
|
-
return invoke('
|
|
1083
|
+
return invoke('conversations/interrupt', conversation);
|
|
919
1084
|
},
|
|
920
1085
|
kill(conversation) {
|
|
921
|
-
return invoke('
|
|
922
|
-
},
|
|
923
|
-
async cancel(conversation, turn) {
|
|
924
|
-
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
925
|
-
if (result.kind !== 'action')
|
|
926
|
-
return result;
|
|
927
|
-
await withDraft(conversation, (current) => {
|
|
928
|
-
const held = current?.submitted?.find((one) => one.turn === turn);
|
|
929
|
-
if (current === undefined || held === undefined)
|
|
930
|
-
return undefined;
|
|
931
|
-
return returned(current, held, current.submitted.filter((one) => one !== held));
|
|
932
|
-
});
|
|
933
|
-
return result;
|
|
1086
|
+
return invoke('conversations/kill', conversation);
|
|
934
1087
|
},
|
|
935
1088
|
delete(conversation) {
|
|
936
|
-
return invoke('
|
|
1089
|
+
return invoke('conversations/delete', conversation);
|
|
937
1090
|
},
|
|
938
1091
|
};
|
|
939
1092
|
const chatDraft = {
|
|
@@ -962,41 +1115,40 @@ export function createConversationStore(deps) {
|
|
|
962
1115
|
return {
|
|
963
1116
|
surface,
|
|
964
1117
|
chatDraft,
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
}
|
|
972
|
-
stored.clear();
|
|
973
|
-
reads.clear();
|
|
974
|
-
partials.clear();
|
|
975
|
-
for (const entry of held) {
|
|
976
|
-
stored.add(entry.conversation);
|
|
977
|
-
if (entry.read !== undefined)
|
|
978
|
-
reads.set(entry.conversation, entry.read);
|
|
979
|
-
if (entry.partial)
|
|
980
|
-
partials.add(entry.conversation);
|
|
981
|
-
}
|
|
982
|
-
drafts.clear();
|
|
983
|
-
for (const record of kept)
|
|
984
|
-
drafts.set(record.conversation, record);
|
|
985
|
-
if (listHeld)
|
|
986
|
-
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();
|
|
987
1124
|
},
|
|
988
1125
|
async reconcile() {
|
|
989
1126
|
if (!listKnown)
|
|
990
1127
|
return;
|
|
991
|
-
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]));
|
|
992
1138
|
const leaving = [
|
|
993
|
-
...new Set([
|
|
1139
|
+
...new Set([
|
|
1140
|
+
...replicated,
|
|
1141
|
+
...reads.keys(),
|
|
1142
|
+
...kept.map(({ conversation }) => conversation),
|
|
1143
|
+
]),
|
|
994
1144
|
].filter((conversation) => !named.has(conversation) &&
|
|
995
1145
|
!creations.has(conversation) &&
|
|
996
|
-
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
1146
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation, list)));
|
|
997
1147
|
for (const conversation of named) {
|
|
998
|
-
if (!replicated.has(conversation) &&
|
|
1148
|
+
if (!replicated.has(conversation) &&
|
|
1149
|
+
missed(conversation, reads.get(conversation), entries.get(conversation))) {
|
|
999
1150
|
behind.add(conversation);
|
|
1151
|
+
}
|
|
1000
1152
|
}
|
|
1001
1153
|
replicated = named;
|
|
1002
1154
|
handReplicated();
|
|
@@ -1027,16 +1179,16 @@ export function createConversationStore(deps) {
|
|
|
1027
1179
|
progress.complete = false;
|
|
1028
1180
|
},
|
|
1029
1181
|
async resend(progress) {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
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;
|
|
1034
1189
|
if (deps.closed())
|
|
1035
1190
|
return;
|
|
1036
|
-
|
|
1037
|
-
await forgotten(conversation, submission);
|
|
1038
|
-
continue;
|
|
1039
|
-
}
|
|
1191
|
+
tried.add(submission.operation);
|
|
1040
1192
|
const result = await dispatched(conversation, submission);
|
|
1041
1193
|
if (result.kind === 'revoked-authorization')
|
|
1042
1194
|
return;
|
|
@@ -1058,6 +1210,7 @@ export function createConversationStore(deps) {
|
|
|
1058
1210
|
return listKnown;
|
|
1059
1211
|
},
|
|
1060
1212
|
reopened() {
|
|
1213
|
+
relisting = null;
|
|
1061
1214
|
for (const [operation, dispatch] of sending)
|
|
1062
1215
|
if (!dispatch.answered)
|
|
1063
1216
|
sending.delete(operation);
|
|
@@ -1073,6 +1226,9 @@ export function createConversationStore(deps) {
|
|
|
1073
1226
|
asked,
|
|
1074
1227
|
watchers: new Set(),
|
|
1075
1228
|
window: null,
|
|
1229
|
+
draft: null,
|
|
1230
|
+
ended: null,
|
|
1231
|
+
marked: [],
|
|
1076
1232
|
more: false,
|
|
1077
1233
|
total: null,
|
|
1078
1234
|
};
|
|
@@ -1084,7 +1240,10 @@ export function createConversationStore(deps) {
|
|
|
1084
1240
|
if (view.window !== null)
|
|
1085
1241
|
return;
|
|
1086
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);
|
|
1087
1245
|
view.window = messages;
|
|
1246
|
+
view.marked = await local.jobMarkers(conversation);
|
|
1088
1247
|
view.more = more;
|
|
1089
1248
|
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
1090
1249
|
}), reading.signal)
|
|
@@ -1093,7 +1252,7 @@ export function createConversationStore(deps) {
|
|
|
1093
1252
|
return;
|
|
1094
1253
|
watcher.ready = true;
|
|
1095
1254
|
const window = view.window ?? [];
|
|
1096
|
-
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
1255
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window), view.draft, view.ended, view.marked));
|
|
1097
1256
|
})
|
|
1098
1257
|
.catch(() => { });
|
|
1099
1258
|
return () => {
|
|
@@ -1105,91 +1264,66 @@ export function createConversationStore(deps) {
|
|
|
1105
1264
|
};
|
|
1106
1265
|
},
|
|
1107
1266
|
watchList(deliver) {
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
if (ended)
|
|
1111
|
-
return;
|
|
1112
|
-
unlisted.add(deliver);
|
|
1113
|
-
learned();
|
|
1114
|
-
});
|
|
1267
|
+
unlisted.add(deliver);
|
|
1268
|
+
learned();
|
|
1115
1269
|
return () => {
|
|
1116
|
-
ended = true;
|
|
1117
1270
|
unlisted.delete(deliver);
|
|
1118
1271
|
listWatchers.delete(deliver);
|
|
1119
1272
|
};
|
|
1120
1273
|
},
|
|
1121
1274
|
watchAgents(deliver) {
|
|
1122
1275
|
unnamed.add(deliver);
|
|
1123
|
-
|
|
1276
|
+
learned();
|
|
1277
|
+
readAgents();
|
|
1124
1278
|
return () => {
|
|
1125
1279
|
unnamed.delete(deliver);
|
|
1126
1280
|
agentWatchers.delete(deliver);
|
|
1127
1281
|
};
|
|
1128
1282
|
},
|
|
1129
1283
|
learned,
|
|
1130
|
-
declared() {
|
|
1131
|
-
if (JSON.stringify(deps.agents()) === agentsFrom)
|
|
1132
|
-
return;
|
|
1133
|
-
agentsRead = readAgents().catch(() => {
|
|
1134
|
-
agentsRead = null;
|
|
1135
|
-
});
|
|
1136
|
-
},
|
|
1137
1284
|
changed(set) {
|
|
1138
1285
|
for (const conversation of set.conversations)
|
|
1139
1286
|
emit(conversation);
|
|
1140
|
-
const declaration = deps.agents();
|
|
1141
|
-
if (declaration !== undefined &&
|
|
1142
|
-
set.documents.some((change) => change.locator === declaration.locator)) {
|
|
1143
|
-
agentsRead = readAgents().catch(() => {
|
|
1144
|
-
agentsRead = null;
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
1287
|
},
|
|
1148
1288
|
receive(event) {
|
|
1149
1289
|
if (deps.closed())
|
|
1150
|
-
return
|
|
1151
|
-
if (event.kind === 'list'
|
|
1152
|
-
if (event.
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
}
|
|
1158
|
-
listKnown = true;
|
|
1159
|
-
for (const conversation of replicated) {
|
|
1160
|
-
if (!listing.has(conversation))
|
|
1161
|
-
continue;
|
|
1162
|
-
if (!missed(conversation))
|
|
1163
|
-
behind.delete(conversation);
|
|
1164
|
-
else if (!behind.has(conversation))
|
|
1165
|
-
gap(conversation);
|
|
1166
|
-
}
|
|
1167
|
-
openingEnded();
|
|
1168
|
-
}
|
|
1169
|
-
else if (event.kind === 'listed') {
|
|
1170
|
-
listing.set(event.entry.conversation, { ...event.entry });
|
|
1171
|
-
creations.delete(event.entry.conversation);
|
|
1172
|
-
}
|
|
1173
|
-
else {
|
|
1174
|
-
listing.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 });
|
|
1175
1297
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
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;
|
|
1182
1319
|
}
|
|
1183
1320
|
heard(event);
|
|
1184
|
-
return false;
|
|
1185
|
-
},
|
|
1186
|
-
searchable() {
|
|
1187
|
-
return [...(listKnown ? listing.keys() : stored)].sort();
|
|
1188
|
-
},
|
|
1189
|
-
acquired(conversation) {
|
|
1190
|
-
return reads.has(conversation) && !partials.has(conversation);
|
|
1191
1321
|
},
|
|
1192
1322
|
close() {
|
|
1323
|
+
closing.abort();
|
|
1324
|
+
if (rowTimer !== null)
|
|
1325
|
+
clearTimeout(rowTimer);
|
|
1326
|
+
rowTimer = null;
|
|
1193
1327
|
views.clear();
|
|
1194
1328
|
lives.clear();
|
|
1195
1329
|
unsent.clear();
|