@agentshouse/mdruntime 0.1.0-ca9afacd5aebd → 0.1.0-cac707d698ca2
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 +19 -16
- package/dist/adapter.d.ts +153 -57
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +7 -10
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +40 -41
- package/dist/attempt.d.ts.map +1 -1
- package/dist/attempt.js +14 -1
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +27 -11
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +185 -81
- package/dist/chain.d.ts +12 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +9 -14
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -63
- package/dist/change.d.ts +31 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +10 -1
- package/dist/client.d.ts +32 -16
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +100 -12
- package/dist/collection.d.ts +11 -7
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +38 -79
- package/dist/compile.d.ts +11 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +586 -0
- package/dist/connection.d.ts +47 -17
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +3601 -992
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/{chat/context.js → context.js} +1 -4
- package/dist/conversation.d.ts +39 -17
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +691 -259
- package/dist/digest.d.ts +7 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +113 -0
- package/dist/durability.d.ts +10 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +20 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +160 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/host.d.ts +11 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +126 -19
- package/dist/index.d.ts +20 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/job.d.ts +14 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +28 -0
- package/dist/live.d.ts +23 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +127 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +18 -5
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +245 -94
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +89 -0
- package/dist/model.d.ts +11 -9
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +8 -5
- package/dist/name.d.ts +4 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +122 -0
- package/dist/observation.d.ts +22 -4
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +120 -14
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/outcome.d.ts +49 -7
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +40 -0
- package/dist/overlay.d.ts +3 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +100 -182
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -0
- package/dist/query.d.ts +40 -183
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -464
- package/dist/reach.d.ts +18 -6
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +223 -80
- package/dist/react.d.ts +10 -6
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/readiness.d.ts +50 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +27 -0
- package/dist/recovery.d.ts +26 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +224 -0
- package/dist/refresh.d.ts +17 -14
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +139 -51
- package/dist/replica.d.ts +58 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +315 -165
- package/dist/room-draft.d.ts +37 -6
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +161 -74
- package/dist/rows.d.ts +30 -37
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +59 -126
- package/dist/save.d.ts +52 -9
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +539 -171
- package/dist/search.d.ts +15 -55
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +10 -121
- package/dist/seed.d.ts +16 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +75 -0
- package/dist/shared.d.ts +15 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +421 -0
- package/dist/status.d.ts +5 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -1
- package/dist/store.d.ts +100 -76
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +309 -443
- package/dist/stored.d.ts +129 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +506 -0
- package/dist/tables.d.ts +327 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +587 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +75 -0
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +104 -0
- package/dist/windows.d.ts +11 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +16 -0
- package/dist/wire.d.ts +173 -32
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +58 -31
- package/package.json +27 -24
- package/AGENTS.md +0 -318
- package/dist/chat/acts.d.ts +0 -34
- package/dist/chat/acts.d.ts.map +0 -1
- package/dist/chat/acts.js +0 -36
- package/dist/chat/context.d.ts +0 -3
- package/dist/chat/context.d.ts.map +0 -1
- package/dist/chat/input.d.ts +0 -9
- package/dist/chat/input.d.ts.map +0 -1
- package/dist/chat/input.js +0 -99
- package/dist/chat/kit.d.ts +0 -60
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -20
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -59
- package/dist/chat/markdown.d.ts +0 -19
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -187
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -192
- package/dist/chat/messages.d.ts +0 -21
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -151
- package/dist/chat/name.d.ts +0 -6
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -11
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -17
- package/dist/chat/overlay.d.ts +0 -31
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -97
- package/dist/chat/parts.d.ts +0 -57
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -119
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -63
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -645
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -80
- package/dist/chat/questions.d.ts +0 -31
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/questions.js +0 -111
- package/dist/chat/read.d.ts +0 -5
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/read.js +0 -13
- package/dist/chat/ready.d.ts +0 -33
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -302
- package/dist/chat.d.ts +0 -14
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
- package/dist/read-side.d.ts +0 -18
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -440
package/dist/conversation.js
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
|
+
import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
|
|
1
2
|
import { conversationChangeSet } from './change-set.js';
|
|
2
3
|
import { runtimeError } from './failure.js';
|
|
3
|
-
import {
|
|
4
|
-
import { chatDraftRevision } from './identity.js';
|
|
4
|
+
import { refusedForSpace } from './durability.js';
|
|
5
|
+
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
6
|
+
import { OFFLINE } from './outcome.js';
|
|
7
|
+
import { untilAnswered } from './recovery.js';
|
|
8
|
+
import { capped, FROM_START, messageOrder, precedes } from './windows.js';
|
|
5
9
|
const QUIET = {
|
|
6
10
|
turn: null,
|
|
7
11
|
questions: [],
|
|
8
12
|
process: null,
|
|
9
13
|
commands: [],
|
|
10
14
|
options: [],
|
|
15
|
+
starting: null,
|
|
16
|
+
subagents: [],
|
|
17
|
+
link: null,
|
|
18
|
+
deliveryUnknown: [],
|
|
11
19
|
};
|
|
20
|
+
const FACTS = [
|
|
21
|
+
'questions',
|
|
22
|
+
'process',
|
|
23
|
+
'commands',
|
|
24
|
+
'options',
|
|
25
|
+
'starting',
|
|
26
|
+
'subagents',
|
|
27
|
+
'link',
|
|
28
|
+
'deliveryUnknown',
|
|
29
|
+
];
|
|
12
30
|
export function unheldView(conversation) {
|
|
13
31
|
return {
|
|
14
32
|
conversation,
|
|
@@ -16,11 +34,28 @@ export function unheldView(conversation) {
|
|
|
16
34
|
turn: null,
|
|
17
35
|
questions: [],
|
|
18
36
|
process: null,
|
|
37
|
+
processEnded: null,
|
|
19
38
|
commands: [],
|
|
20
39
|
options: [],
|
|
40
|
+
starting: null,
|
|
41
|
+
subagents: [],
|
|
42
|
+
link: null,
|
|
43
|
+
deliveryUnknown: [],
|
|
21
44
|
chatDraft: null,
|
|
22
45
|
};
|
|
23
46
|
}
|
|
47
|
+
export const SESSION_STARTED = 'Session started';
|
|
48
|
+
const NEW_CHAT = 'new:';
|
|
49
|
+
export const HISTORY_CONVERSATIONS = 64;
|
|
50
|
+
export function newChat(agent) {
|
|
51
|
+
return `${NEW_CHAT}${agent}`;
|
|
52
|
+
}
|
|
53
|
+
function creationOf(input) {
|
|
54
|
+
if (typeof input !== 'object' || input === null)
|
|
55
|
+
return null;
|
|
56
|
+
const { agent_id: agent, operation_id: operation } = input;
|
|
57
|
+
return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
|
|
58
|
+
}
|
|
24
59
|
function startedTurn(turn, operation) {
|
|
25
60
|
if (operation.op === 'begin')
|
|
26
61
|
return { turn, started: operation.at, parts: [], context: null };
|
|
@@ -70,18 +105,28 @@ function appliedTurn(held, operation) {
|
|
|
70
105
|
parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
|
|
71
106
|
};
|
|
72
107
|
}
|
|
73
|
-
function
|
|
74
|
-
if (left.position !== right.position)
|
|
75
|
-
return left.position < right.position ? -1 : 1;
|
|
76
|
-
if (left.identity !== right.identity)
|
|
77
|
-
return left.identity < right.identity ? -1 : 1;
|
|
78
|
-
return 0;
|
|
79
|
-
}
|
|
80
|
-
function merged(held, arriving) {
|
|
108
|
+
function windowOf(held, arriving, limit) {
|
|
81
109
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
82
110
|
for (const message of arriving)
|
|
83
111
|
byIdentity.set(message.identity, message);
|
|
84
|
-
|
|
112
|
+
const next = [...byIdentity.values()].sort(messageOrder);
|
|
113
|
+
const start = Math.max(0, next.length - limit);
|
|
114
|
+
return { messages: next.slice(start), dropped: start > 0 };
|
|
115
|
+
}
|
|
116
|
+
function frameOf(asked, messages, more, total) {
|
|
117
|
+
const oldest = messages[0];
|
|
118
|
+
const next = !more
|
|
119
|
+
? undefined
|
|
120
|
+
: oldest === undefined
|
|
121
|
+
? (asked.after ?? FROM_START)
|
|
122
|
+
: cursorOf([oldest.position, oldest.identity]);
|
|
123
|
+
return {
|
|
124
|
+
...(next === undefined ? {} : { next }),
|
|
125
|
+
...(total === null ? {} : { total }),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function newestOf(arriving, from) {
|
|
129
|
+
return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
|
|
85
130
|
}
|
|
86
131
|
function acceptedMessage(value) {
|
|
87
132
|
if (typeof value !== 'object' || value === null)
|
|
@@ -140,88 +185,171 @@ function historyPages(result) {
|
|
|
140
185
|
}
|
|
141
186
|
return pages;
|
|
142
187
|
}
|
|
143
|
-
function
|
|
188
|
+
function answered(result, field) {
|
|
144
189
|
if (result.kind !== 'action')
|
|
145
190
|
return null;
|
|
146
191
|
const answer = result.result;
|
|
147
192
|
if (typeof answer !== 'object' || answer === null)
|
|
148
193
|
return null;
|
|
149
|
-
const
|
|
150
|
-
return typeof
|
|
194
|
+
const value = answer[field];
|
|
195
|
+
return typeof value === 'string' ? value : null;
|
|
196
|
+
}
|
|
197
|
+
function beforeOf(after) {
|
|
198
|
+
if (after === undefined)
|
|
199
|
+
return null;
|
|
200
|
+
const decoded = typeof after === 'string' ? decodedCursor(after) : null;
|
|
201
|
+
if (decoded?.length === 0)
|
|
202
|
+
return null;
|
|
203
|
+
const [position, identity] = decoded ?? [];
|
|
204
|
+
if (decoded?.length !== 2 || typeof position !== 'string' || typeof identity !== 'string') {
|
|
205
|
+
throw runtimeError('invalid-after', `conversation after must be the next an earlier view returned, got ${String(after)}`);
|
|
206
|
+
}
|
|
207
|
+
return [position, identity];
|
|
151
208
|
}
|
|
152
|
-
function
|
|
153
|
-
const { limit } = options;
|
|
154
|
-
if (limit
|
|
155
|
-
return undefined;
|
|
156
|
-
if (!Number.isInteger(limit) || limit < 0) {
|
|
209
|
+
function askedOf(options) {
|
|
210
|
+
const { limit, after, total } = options;
|
|
211
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit < 0)) {
|
|
157
212
|
throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
|
|
158
213
|
}
|
|
159
|
-
return limit;
|
|
214
|
+
return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
|
|
215
|
+
}
|
|
216
|
+
function phraseOf(text) {
|
|
217
|
+
if (text === undefined)
|
|
218
|
+
return null;
|
|
219
|
+
if (typeof text !== 'string' || text === '') {
|
|
220
|
+
throw runtimeError('invalid-text', 'a conversation is found by a phrase');
|
|
221
|
+
}
|
|
222
|
+
return text;
|
|
223
|
+
}
|
|
224
|
+
const DURABLE_WRITE = {
|
|
225
|
+
kind: 'rejected',
|
|
226
|
+
code: 'durable-write',
|
|
227
|
+
fields: {},
|
|
228
|
+
retryable: true,
|
|
229
|
+
};
|
|
230
|
+
function unwritten(draft) {
|
|
231
|
+
return draft.text === '';
|
|
232
|
+
}
|
|
233
|
+
function pending(draft) {
|
|
234
|
+
return {
|
|
235
|
+
...(draft.submitted === undefined ? {} : { submitted: draft.submitted }),
|
|
236
|
+
...(draft.moves === undefined ? {} : { moves: draft.moves }),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function empty(draft) {
|
|
240
|
+
return unwritten(draft) && draft.submitted === undefined && draft.moves === undefined;
|
|
241
|
+
}
|
|
242
|
+
function withSubmitted(draft, submitted) {
|
|
243
|
+
const { submitted: _held, ...unsent } = draft;
|
|
244
|
+
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
245
|
+
}
|
|
246
|
+
function prepended(draft, said) {
|
|
247
|
+
return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
|
|
248
|
+
}
|
|
249
|
+
function returned(draft, submission, others) {
|
|
250
|
+
return withSubmitted({
|
|
251
|
+
conversation: draft.conversation,
|
|
252
|
+
...prepended(draft, submission),
|
|
253
|
+
revision: chatDraftRevision(),
|
|
254
|
+
}, others);
|
|
160
255
|
}
|
|
161
256
|
export function createConversationStore(deps) {
|
|
162
|
-
const
|
|
257
|
+
const stored = new Set();
|
|
163
258
|
const reads = new Map();
|
|
259
|
+
const partials = new Set();
|
|
164
260
|
const drafts = new Map();
|
|
165
261
|
const sending = new Map();
|
|
166
262
|
const behind = new Set();
|
|
263
|
+
const catching = new Map();
|
|
167
264
|
const gaps = new Map();
|
|
168
265
|
const lives = new Map();
|
|
266
|
+
const unsent = new Map();
|
|
169
267
|
const views = new Map();
|
|
170
268
|
const listing = new Map();
|
|
269
|
+
let relisting = null;
|
|
270
|
+
let listCursor = null;
|
|
271
|
+
const creations = new Set();
|
|
171
272
|
const listWatchers = new Set();
|
|
273
|
+
const unlisted = new Set();
|
|
172
274
|
let listKnown = false;
|
|
275
|
+
let listHeld = false;
|
|
276
|
+
let listKept = false;
|
|
173
277
|
const agentWatchers = new Set();
|
|
174
|
-
|
|
175
|
-
let
|
|
176
|
-
let
|
|
278
|
+
const unnamed = new Set();
|
|
279
|
+
let agentsShown = null;
|
|
280
|
+
let listedAgents = null;
|
|
281
|
+
let agentsReading = false;
|
|
177
282
|
let replicated = new Set();
|
|
178
283
|
let handedReplicated;
|
|
179
284
|
function liveOf(conversation) {
|
|
180
285
|
return lives.get(conversation) ?? QUIET;
|
|
181
286
|
}
|
|
182
|
-
function
|
|
183
|
-
const held = messages.get(conversation) ?? [];
|
|
184
|
-
const shown = limit === undefined ? held : held.slice(Math.max(0, held.length - limit));
|
|
185
|
-
return shown.map(({ identity, turn, text, message }) => ({
|
|
186
|
-
identity,
|
|
187
|
-
turn,
|
|
188
|
-
text,
|
|
189
|
-
message: structuredClone(message),
|
|
190
|
-
}));
|
|
191
|
-
}
|
|
192
|
-
function viewOf(conversation, limit, shown = shownMessages(conversation, limit)) {
|
|
287
|
+
function viewOf(conversation, held, frame) {
|
|
193
288
|
const draft = drafts.get(conversation);
|
|
194
289
|
const facts = liveOf(conversation);
|
|
195
290
|
return {
|
|
196
291
|
conversation,
|
|
197
|
-
messages:
|
|
292
|
+
messages: held.map(({ identity, turn, text, message }) => ({
|
|
293
|
+
identity,
|
|
294
|
+
turn,
|
|
295
|
+
text,
|
|
296
|
+
message,
|
|
297
|
+
})),
|
|
298
|
+
...frame,
|
|
198
299
|
turn: structuredClone(facts.turn),
|
|
199
300
|
questions: structuredClone(facts.questions),
|
|
200
301
|
process: facts.process === null ? null : { ...facts.process },
|
|
302
|
+
processEnded: listing.get(conversation)?.processEnded ?? null,
|
|
201
303
|
commands: structuredClone(facts.commands),
|
|
202
304
|
options: structuredClone(facts.options),
|
|
203
|
-
|
|
305
|
+
starting: facts.starting,
|
|
306
|
+
subagents: structuredClone(facts.subagents),
|
|
307
|
+
link: facts.link === null ? null : { ...facts.link },
|
|
308
|
+
deliveryUnknown: [...facts.deliveryUnknown],
|
|
309
|
+
chatDraft: draft === undefined || unwritten(draft)
|
|
204
310
|
? null
|
|
205
|
-
: { text: draft.text,
|
|
311
|
+
: { text: draft.text, revision: draft.revision },
|
|
206
312
|
};
|
|
207
313
|
}
|
|
208
|
-
function emit(conversation
|
|
314
|
+
function emit(conversation) {
|
|
209
315
|
if (deps.closed())
|
|
210
316
|
return;
|
|
211
317
|
for (const view of views.values()) {
|
|
212
|
-
if (view.conversation !== conversation)
|
|
318
|
+
if (view.conversation !== conversation || view.window === null)
|
|
213
319
|
continue;
|
|
214
|
-
if (messagesChanged)
|
|
215
|
-
view.messages = null;
|
|
216
320
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
217
321
|
if (ready.length === 0)
|
|
218
322
|
continue;
|
|
219
|
-
|
|
220
|
-
const value = viewOf(conversation, view.limit, view.messages);
|
|
323
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
221
324
|
for (const watcher of ready)
|
|
222
325
|
watcher.notify(value);
|
|
223
326
|
}
|
|
224
327
|
}
|
|
328
|
+
function sharedFrame(view, window) {
|
|
329
|
+
return frameOf(view.asked, window, view.more, view.total);
|
|
330
|
+
}
|
|
331
|
+
async function slide(local, conversation, arriving) {
|
|
332
|
+
let count;
|
|
333
|
+
for (const view of views.values()) {
|
|
334
|
+
if (view.conversation !== conversation || view.window === null)
|
|
335
|
+
continue;
|
|
336
|
+
if (arriving === null) {
|
|
337
|
+
view.window = [];
|
|
338
|
+
view.more = false;
|
|
339
|
+
if (view.total !== null)
|
|
340
|
+
view.total = 0;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const joining = arriving.filter((message) => precedes(message, view.asked.before));
|
|
344
|
+
const next = windowOf(view.window, joining, view.asked.limit);
|
|
345
|
+
view.window = next.messages;
|
|
346
|
+
view.more ||= next.dropped;
|
|
347
|
+
if (view.total !== null && arriving.length > 0) {
|
|
348
|
+
count ??= await local.messageCount(conversation);
|
|
349
|
+
view.total = count;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
225
353
|
function stateOf(conversation) {
|
|
226
354
|
const facts = liveOf(conversation);
|
|
227
355
|
if (facts.questions.length > 0)
|
|
@@ -231,34 +359,113 @@ export function createConversationStore(deps) {
|
|
|
231
359
|
function entries() {
|
|
232
360
|
return [...listing.values()].map((entry) => ({
|
|
233
361
|
conversation: entry.conversation,
|
|
234
|
-
agent: { identity: entry.agent, name:
|
|
362
|
+
agent: { identity: entry.agent, name: entry.agentName },
|
|
235
363
|
name: entry.name,
|
|
236
364
|
created: entry.created,
|
|
237
365
|
finished: entry.finished,
|
|
238
366
|
outcome: entry.outcome,
|
|
367
|
+
waiting: entry.waiting,
|
|
239
368
|
state: stateOf(entry.conversation),
|
|
240
369
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
241
370
|
}));
|
|
242
371
|
}
|
|
372
|
+
function keepList() {
|
|
373
|
+
if (!listKnown || listKept)
|
|
374
|
+
return;
|
|
375
|
+
listKept = true;
|
|
376
|
+
queueMicrotask(() => {
|
|
377
|
+
listKept = false;
|
|
378
|
+
if (deps.closed())
|
|
379
|
+
return;
|
|
380
|
+
const entries = [...listing.values()];
|
|
381
|
+
const cursor = listCursor;
|
|
382
|
+
void deps
|
|
383
|
+
.durably(async (local) => {
|
|
384
|
+
await local.fact('conversationList', entries);
|
|
385
|
+
await local.fact('conversationCursor', cursor);
|
|
386
|
+
})
|
|
387
|
+
.catch(() => { });
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
function listed(entry) {
|
|
391
|
+
const ended = listing.get(entry.conversation)?.processEnded ?? null;
|
|
392
|
+
listing.set(entry.conversation, { ...entry });
|
|
393
|
+
creations.delete(entry.conversation);
|
|
394
|
+
if (entry.processEnded !== ended)
|
|
395
|
+
emit(entry.conversation);
|
|
396
|
+
}
|
|
243
397
|
function emitList() {
|
|
244
|
-
if (deps.closed()
|
|
398
|
+
if (deps.closed())
|
|
245
399
|
return;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
watcher
|
|
400
|
+
if (listWatchers.size > 0) {
|
|
401
|
+
const value = entries();
|
|
402
|
+
for (const watcher of listWatchers)
|
|
403
|
+
watcher(value);
|
|
404
|
+
}
|
|
405
|
+
emitAgents();
|
|
406
|
+
learned();
|
|
407
|
+
}
|
|
408
|
+
function learned() {
|
|
409
|
+
if (deps.closed())
|
|
410
|
+
return;
|
|
411
|
+
if (!listKnown && !listHeld && deps.stream() !== undefined && deps.online())
|
|
412
|
+
return;
|
|
413
|
+
if (unlisted.size > 0) {
|
|
414
|
+
const value = entries();
|
|
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();
|
|
423
|
+
for (const watcher of [...unnamed]) {
|
|
424
|
+
unnamed.delete(watcher);
|
|
425
|
+
agentWatchers.add(watcher);
|
|
426
|
+
watcher(value);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
249
429
|
}
|
|
250
430
|
function agents() {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
.
|
|
431
|
+
const named = new Map();
|
|
432
|
+
for (const agent of listedAgents ?? [])
|
|
433
|
+
named.set(agent.agent, agent);
|
|
434
|
+
for (const entry of listing.values()) {
|
|
435
|
+
named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
|
|
436
|
+
}
|
|
437
|
+
return [...named]
|
|
438
|
+
.filter(([, agent]) => !agent.retired)
|
|
439
|
+
.map(([identity, agent]) => ({ identity, name: agent.name }))
|
|
254
440
|
.sort((left, right) => left.name === right.name
|
|
255
441
|
? left.identity.localeCompare(right.identity)
|
|
256
442
|
: left.name.localeCompare(right.name));
|
|
257
443
|
}
|
|
444
|
+
function readAgents() {
|
|
445
|
+
const stream = deps.stream();
|
|
446
|
+
if (stream?.agents === undefined || !deps.online() || agentsReading)
|
|
447
|
+
return;
|
|
448
|
+
agentsReading = true;
|
|
449
|
+
void stream
|
|
450
|
+
.agents()
|
|
451
|
+
.then((read) => {
|
|
452
|
+
if (read === null)
|
|
453
|
+
return;
|
|
454
|
+
listedAgents = read;
|
|
455
|
+
emitAgents();
|
|
456
|
+
}, () => { })
|
|
457
|
+
.finally(() => {
|
|
458
|
+
agentsReading = false;
|
|
459
|
+
});
|
|
460
|
+
}
|
|
258
461
|
function emitAgents() {
|
|
259
462
|
if (deps.closed() || agentWatchers.size === 0)
|
|
260
463
|
return;
|
|
261
464
|
const value = agents();
|
|
465
|
+
const shown = JSON.stringify(value);
|
|
466
|
+
if (shown === agentsShown)
|
|
467
|
+
return;
|
|
468
|
+
agentsShown = shown;
|
|
262
469
|
for (const watcher of agentWatchers)
|
|
263
470
|
watcher(value);
|
|
264
471
|
}
|
|
@@ -274,43 +481,41 @@ export function createConversationStore(deps) {
|
|
|
274
481
|
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
275
482
|
emitList();
|
|
276
483
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const names = new Map();
|
|
283
|
-
const retired = new Set();
|
|
284
|
-
for (const document of documents) {
|
|
285
|
-
if (document.type !== declaration.type)
|
|
286
|
-
continue;
|
|
287
|
-
const identity = document.fields?.[declaration.identity];
|
|
288
|
-
const name = document.fields?.[declaration.name];
|
|
289
|
-
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
290
|
-
continue;
|
|
291
|
-
names.set(identity, name);
|
|
292
|
-
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
293
|
-
if (ended !== undefined && ended !== null)
|
|
294
|
-
retired.add(identity);
|
|
295
|
-
}
|
|
296
|
-
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
297
|
-
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
298
|
-
return;
|
|
299
|
-
agentNames = names;
|
|
300
|
-
retiredAgents = retired;
|
|
301
|
-
if (named)
|
|
302
|
-
emitList();
|
|
303
|
-
emitAgents();
|
|
304
|
-
}
|
|
305
|
-
function agentsReady() {
|
|
306
|
-
agentsRead ??= readAgents().catch(() => {
|
|
307
|
-
agentsRead = null;
|
|
308
|
-
});
|
|
309
|
-
return agentsRead;
|
|
484
|
+
function retiredChat(conversation) {
|
|
485
|
+
if (!conversation.startsWith(NEW_CHAT))
|
|
486
|
+
return false;
|
|
487
|
+
const agent = conversation.slice(NEW_CHAT.length);
|
|
488
|
+
return [...listing.values()].some((entry) => entry.agent === agent && entry.agentRetired);
|
|
310
489
|
}
|
|
311
490
|
function live(conversation, change) {
|
|
491
|
+
const owed = unsent.get(conversation);
|
|
492
|
+
for (const fact of FACTS.filter((one) => one in change))
|
|
493
|
+
owed?.delete(fact);
|
|
312
494
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
313
|
-
emit(conversation
|
|
495
|
+
emit(conversation);
|
|
496
|
+
}
|
|
497
|
+
function openingEnded() {
|
|
498
|
+
const owed = [...unsent];
|
|
499
|
+
unsent.clear();
|
|
500
|
+
for (const [conversation, facts] of owed) {
|
|
501
|
+
const held = lives.get(conversation);
|
|
502
|
+
if (held === undefined || facts.size === 0)
|
|
503
|
+
continue;
|
|
504
|
+
setLive(conversation, {
|
|
505
|
+
turn: held.turn,
|
|
506
|
+
questions: facts.has('questions') ? QUIET.questions : held.questions,
|
|
507
|
+
process: facts.has('process') ? QUIET.process : held.process,
|
|
508
|
+
commands: facts.has('commands') ? QUIET.commands : held.commands,
|
|
509
|
+
options: facts.has('options') ? QUIET.options : held.options,
|
|
510
|
+
starting: facts.has('starting') ? QUIET.starting : held.starting,
|
|
511
|
+
subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
|
|
512
|
+
link: facts.has('link') ? QUIET.link : held.link,
|
|
513
|
+
deliveryUnknown: facts.has('deliveryUnknown')
|
|
514
|
+
? QUIET.deliveryUnknown
|
|
515
|
+
: held.deliveryUnknown,
|
|
516
|
+
});
|
|
517
|
+
emit(conversation);
|
|
518
|
+
}
|
|
314
519
|
}
|
|
315
520
|
function relayed(conversation, turn, operation) {
|
|
316
521
|
const held = liveOf(conversation).turn;
|
|
@@ -339,25 +544,38 @@ export function createConversationStore(deps) {
|
|
|
339
544
|
async function remove(leaving) {
|
|
340
545
|
await deps.durably(async (local) => {
|
|
341
546
|
await local.dropConversations(leaving);
|
|
342
|
-
const removed = [];
|
|
343
547
|
const changed = [];
|
|
344
548
|
for (const conversation of leaving) {
|
|
345
|
-
|
|
346
|
-
removed.push({ conversation, identity: message.identity });
|
|
347
|
-
}
|
|
348
|
-
if (messages.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
|
|
549
|
+
if (stored.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
|
|
349
550
|
changed.push(conversation);
|
|
350
551
|
}
|
|
351
|
-
|
|
552
|
+
stored.delete(conversation);
|
|
352
553
|
reads.delete(conversation);
|
|
554
|
+
partials.delete(conversation);
|
|
353
555
|
drafts.delete(conversation);
|
|
354
556
|
behind.delete(conversation);
|
|
557
|
+
unsent.delete(conversation);
|
|
355
558
|
setLive(conversation, null);
|
|
559
|
+
await slide(local, conversation, null);
|
|
356
560
|
}
|
|
357
561
|
if (changed.length > 0)
|
|
358
|
-
deps.publish(conversationChangeSet(
|
|
562
|
+
deps.publish(conversationChangeSet(changed));
|
|
359
563
|
});
|
|
360
564
|
}
|
|
565
|
+
function missed(conversation) {
|
|
566
|
+
const read = reads.get(conversation);
|
|
567
|
+
const newest = listing.get(conversation)?.newest;
|
|
568
|
+
return (read === undefined ||
|
|
569
|
+
newest === undefined ||
|
|
570
|
+
partials.has(conversation) ||
|
|
571
|
+
(newest !== null && (read === null || newest > read)));
|
|
572
|
+
}
|
|
573
|
+
function noted(conversation, message) {
|
|
574
|
+
const entry = listing.get(conversation);
|
|
575
|
+
if (entry === undefined || (entry.newest !== null && entry.newest >= message.position))
|
|
576
|
+
return;
|
|
577
|
+
listing.set(conversation, { ...entry, newest: message.position });
|
|
578
|
+
}
|
|
361
579
|
function gap(conversation) {
|
|
362
580
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
363
581
|
behind.add(conversation);
|
|
@@ -370,33 +588,29 @@ export function createConversationStore(deps) {
|
|
|
370
588
|
const advances = read !== undefined &&
|
|
371
589
|
!behind.has(conversation) &&
|
|
372
590
|
(read === null || message.position > read);
|
|
373
|
-
|
|
591
|
+
await sealed(local, conversation, new Set([message.turn]));
|
|
374
592
|
await local.keepMessages({
|
|
375
593
|
conversation,
|
|
376
594
|
messages: [message],
|
|
377
|
-
...(advances ? { read: message.position } : {}),
|
|
378
|
-
sealed,
|
|
595
|
+
...(advances ? { read: { at: message.position, partial: false } } : {}),
|
|
379
596
|
});
|
|
380
|
-
|
|
597
|
+
stored.add(conversation);
|
|
381
598
|
if (advances)
|
|
382
599
|
reads.set(conversation, message.position);
|
|
383
|
-
if (sealed)
|
|
384
|
-
drafts.delete(conversation);
|
|
385
600
|
sealTurns(conversation, new Set([message.turn]));
|
|
386
|
-
|
|
601
|
+
await slide(local, conversation, [message]);
|
|
602
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
387
603
|
});
|
|
388
604
|
}
|
|
389
|
-
async function readHistories(conversations) {
|
|
605
|
+
async function readHistories(conversations, ended) {
|
|
390
606
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
391
|
-
|
|
392
|
-
const arriving = new Map();
|
|
393
|
-
const complete = new Set();
|
|
394
|
-
let asking = conversations.map((conversation) => ({
|
|
607
|
+
let waiting = conversations.map((conversation) => ({
|
|
395
608
|
conversation,
|
|
396
|
-
after:
|
|
609
|
+
after: reads.get(conversation) ?? undefined,
|
|
397
610
|
}));
|
|
398
|
-
while (
|
|
399
|
-
const
|
|
611
|
+
while (waiting.length > 0) {
|
|
612
|
+
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
613
|
+
const result = await deps.history({
|
|
400
614
|
conversations: asking.map(({ conversation, after }) => ({
|
|
401
615
|
conversation,
|
|
402
616
|
...(after === undefined ? {} : { after }),
|
|
@@ -405,46 +619,64 @@ export function createConversationStore(deps) {
|
|
|
405
619
|
const pages = historyPages(result);
|
|
406
620
|
if (pages === null)
|
|
407
621
|
return result;
|
|
408
|
-
const asked = new
|
|
622
|
+
const asked = new Map(asking.map(({ conversation, after }) => [conversation, after]));
|
|
409
623
|
const following = [];
|
|
410
624
|
for (const page of pages) {
|
|
411
625
|
if (!asked.has(page.conversation))
|
|
412
626
|
continue;
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
...page.messages,
|
|
416
|
-
]);
|
|
417
|
-
if (page.next === undefined)
|
|
418
|
-
complete.add(page.conversation);
|
|
419
|
-
else
|
|
627
|
+
await keepPage(page.conversation, page.messages, asked.get(page.conversation) ?? null, page.next, generations.get(page.conversation) ?? 0);
|
|
628
|
+
if (page.next !== undefined) {
|
|
420
629
|
following.push({ conversation: page.conversation, after: page.next });
|
|
630
|
+
}
|
|
421
631
|
}
|
|
422
|
-
asking
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
await keepHistory(conversation, held, starts.get(conversation), generations.get(conversation) ?? 0);
|
|
632
|
+
for (const { conversation } of asking) {
|
|
633
|
+
if (!following.some((next) => next.conversation === conversation))
|
|
634
|
+
ended(conversation);
|
|
635
|
+
}
|
|
636
|
+
waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
|
|
428
637
|
}
|
|
429
638
|
return null;
|
|
430
639
|
}
|
|
431
|
-
function
|
|
640
|
+
function caughtUp(conversations) {
|
|
641
|
+
const ends = new Map();
|
|
642
|
+
for (const conversation of conversations) {
|
|
643
|
+
const held = new Promise((end) => ends.set(conversation, end));
|
|
644
|
+
catching.set(conversation, held);
|
|
645
|
+
void held.then(() => {
|
|
646
|
+
if (catching.get(conversation) === held)
|
|
647
|
+
catching.delete(conversation);
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
const ended = (conversation) => ends.get(conversation)?.();
|
|
651
|
+
const read = readHistories(conversations, ended);
|
|
652
|
+
const endAll = () => {
|
|
653
|
+
for (const conversation of conversations)
|
|
654
|
+
ended(conversation);
|
|
655
|
+
};
|
|
656
|
+
read.then(endAll, endAll);
|
|
657
|
+
return read;
|
|
658
|
+
}
|
|
659
|
+
function keepPage(conversation, arriving, from, next, generation) {
|
|
432
660
|
return deps.durably(async (local) => {
|
|
433
661
|
if (!replicated.has(conversation))
|
|
434
662
|
return;
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
await local
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
663
|
+
const whole = next === undefined;
|
|
664
|
+
const cursor = next ?? newestOf(arriving, from);
|
|
665
|
+
const partial = !whole && (partials.has(conversation) || !reads.has(conversation));
|
|
666
|
+
const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
|
|
667
|
+
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);
|
|
443
674
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
444
|
-
if ((gaps.get(conversation) ?? 0) === generation)
|
|
675
|
+
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
445
676
|
behind.delete(conversation);
|
|
446
|
-
|
|
447
|
-
|
|
677
|
+
await slide(local, conversation, arriving);
|
|
678
|
+
if (arriving.length > 0 || sealing || whole) {
|
|
679
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
448
680
|
}
|
|
449
681
|
});
|
|
450
682
|
}
|
|
@@ -453,11 +685,8 @@ export function createConversationStore(deps) {
|
|
|
453
685
|
if (event.kind === 'gap') {
|
|
454
686
|
if (replicated.has(conversation))
|
|
455
687
|
gap(conversation);
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
return;
|
|
459
|
-
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
460
|
-
emit(conversation, false);
|
|
688
|
+
if (lives.has(conversation))
|
|
689
|
+
unsent.set(conversation, new Set(FACTS));
|
|
461
690
|
return;
|
|
462
691
|
}
|
|
463
692
|
if (event.kind === 'turn') {
|
|
@@ -480,143 +709,278 @@ export function createConversationStore(deps) {
|
|
|
480
709
|
live(conversation, { options: structuredClone(event.options) });
|
|
481
710
|
return;
|
|
482
711
|
}
|
|
712
|
+
if (event.kind === 'starting') {
|
|
713
|
+
live(conversation, { starting: event.step });
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (event.kind === 'subagents') {
|
|
717
|
+
live(conversation, { subagents: structuredClone(event.subagents) });
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (event.kind === 'link') {
|
|
721
|
+
live(conversation, { link: { connected: event.connected } });
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (event.kind === 'deliveries') {
|
|
725
|
+
live(conversation, { deliveryUnknown: [...event.unknown] });
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
483
728
|
const arriving = acceptedMessage(event.message);
|
|
484
729
|
if (arriving === null)
|
|
485
730
|
return;
|
|
731
|
+
noted(conversation, arriving);
|
|
486
732
|
if (replicated.has(conversation)) {
|
|
487
|
-
|
|
733
|
+
if (deps.full()) {
|
|
734
|
+
gap(conversation);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
void keep(conversation, arriving).catch((error) => {
|
|
738
|
+
if (refusedForSpace(error))
|
|
739
|
+
gap(conversation);
|
|
740
|
+
});
|
|
488
741
|
return;
|
|
489
742
|
}
|
|
490
743
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
491
744
|
emit(conversation);
|
|
492
745
|
}
|
|
746
|
+
async function kept(local, conversation, following) {
|
|
747
|
+
if (following === null || empty(following)) {
|
|
748
|
+
await local.dropChatDraft(conversation);
|
|
749
|
+
drafts.delete(conversation);
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
await local.keepChatDrafts([following]);
|
|
753
|
+
drafts.set(conversation, following);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
async function sealed(local, conversation, turns) {
|
|
757
|
+
const held = drafts.get(conversation);
|
|
758
|
+
const submitted = held?.submitted ?? [];
|
|
759
|
+
const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
|
|
760
|
+
if (held === undefined || left.length === submitted.length)
|
|
761
|
+
return false;
|
|
762
|
+
await kept(local, conversation, withSubmitted(held, left));
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
function moved(agent, operation, to) {
|
|
766
|
+
const from = newChat(agent);
|
|
767
|
+
return deps.durably(async (local) => {
|
|
768
|
+
const held = drafts.get(from);
|
|
769
|
+
const earlier = held?.moves ?? [];
|
|
770
|
+
if (earlier.includes(operation))
|
|
771
|
+
return;
|
|
772
|
+
const blank = {
|
|
773
|
+
conversation: from,
|
|
774
|
+
text: '',
|
|
775
|
+
revision: chatDraftRevision(),
|
|
776
|
+
};
|
|
777
|
+
const { moves: _answered, ...unsent } = held ?? blank;
|
|
778
|
+
const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
|
|
779
|
+
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
780
|
+
await local.keepChatDrafts([left, ...taken]);
|
|
781
|
+
drafts.set(from, left);
|
|
782
|
+
for (const one of taken)
|
|
783
|
+
drafts.set(to, one);
|
|
784
|
+
deps.publish(conversationChangeSet([from, to]));
|
|
785
|
+
});
|
|
786
|
+
}
|
|
493
787
|
function withDraft(conversation, next) {
|
|
494
788
|
return deps.durably(async (local) => {
|
|
495
|
-
const following = next(drafts.get(conversation));
|
|
789
|
+
const following = await next(drafts.get(conversation), local);
|
|
496
790
|
if (following === undefined)
|
|
497
791
|
return;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
drafts.delete(conversation);
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
await local.keepChatDraft(following);
|
|
504
|
-
drafts.set(conversation, following);
|
|
505
|
-
}
|
|
506
|
-
deps.publish(conversationChangeSet([], [conversation]));
|
|
792
|
+
await kept(local, conversation, following);
|
|
793
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
507
794
|
});
|
|
508
795
|
}
|
|
509
|
-
function
|
|
510
|
-
|
|
796
|
+
async function submitted(conversation, operation, draft) {
|
|
797
|
+
let submission;
|
|
798
|
+
await withDraft(conversation, (current) => {
|
|
799
|
+
submission = current?.submitted?.find((one) => one.operation === operation);
|
|
800
|
+
if (submission !== undefined)
|
|
801
|
+
return undefined;
|
|
802
|
+
const said = draft ?? current;
|
|
803
|
+
submission = { operation, text: said?.text ?? '', turn: null };
|
|
804
|
+
return {
|
|
805
|
+
conversation,
|
|
806
|
+
...(draft === undefined || current === undefined
|
|
807
|
+
? { text: '', revision: chatDraftRevision() }
|
|
808
|
+
: { text: current.text, revision: current.revision }),
|
|
809
|
+
submitted: [...(current?.submitted ?? []), submission],
|
|
810
|
+
};
|
|
811
|
+
});
|
|
812
|
+
return submission;
|
|
813
|
+
}
|
|
814
|
+
async function dispatched(conversation, submission) {
|
|
815
|
+
const dispatch = { answered: false };
|
|
816
|
+
sending.set(submission.operation, dispatch);
|
|
817
|
+
try {
|
|
818
|
+
const result = await invoke('conversations/send', conversation, {
|
|
819
|
+
operation_id: submission.operation,
|
|
820
|
+
text: submission.text,
|
|
821
|
+
});
|
|
822
|
+
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
823
|
+
if (result.kind === 'revoked-authorization')
|
|
824
|
+
return result;
|
|
825
|
+
if (result.kind !== 'action' && result.retryable) {
|
|
826
|
+
deps.unanswered();
|
|
827
|
+
return result;
|
|
828
|
+
}
|
|
829
|
+
const turn = answered(result, 'message_id');
|
|
830
|
+
await withDraft(conversation, async (current, local) => {
|
|
831
|
+
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
832
|
+
if (current === undefined || held === undefined || held.turn !== null)
|
|
833
|
+
return undefined;
|
|
834
|
+
const others = current.submitted.filter((one) => one !== held);
|
|
835
|
+
if (result.kind !== 'action')
|
|
836
|
+
return returned(current, held, others);
|
|
837
|
+
const arrived = turn !== null && (await local.turnArrived(conversation, turn));
|
|
838
|
+
return withSubmitted(current, turn === null || arrived
|
|
839
|
+
? others
|
|
840
|
+
: current.submitted.map((one) => (one === held ? { ...held, turn } : one)));
|
|
841
|
+
});
|
|
842
|
+
return result;
|
|
843
|
+
}
|
|
844
|
+
finally {
|
|
845
|
+
if (sending.get(submission.operation) === dispatch)
|
|
846
|
+
sending.delete(submission.operation);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
|
|
850
|
+
return deps.call(name, { conversation_id: conversation, ...extra });
|
|
511
851
|
}
|
|
512
852
|
const surface = {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
853
|
+
list() {
|
|
854
|
+
return new Promise((settle) => {
|
|
855
|
+
const answer = (listed) => {
|
|
856
|
+
unlisted.delete(answer);
|
|
857
|
+
listWatchers.delete(answer);
|
|
858
|
+
settle(listed);
|
|
859
|
+
};
|
|
860
|
+
unlisted.add(answer);
|
|
861
|
+
learned();
|
|
862
|
+
});
|
|
516
863
|
},
|
|
517
864
|
async read(conversation, options = {}) {
|
|
518
|
-
const
|
|
519
|
-
|
|
865
|
+
const { text, ...windowed } = options;
|
|
866
|
+
const asked = askedOf(windowed);
|
|
867
|
+
const phrase = phraseOf(text);
|
|
868
|
+
return deps.durably(async (local) => {
|
|
869
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
|
|
870
|
+
const total = asked.total ? await local.messageCount(conversation, phrase) : null;
|
|
871
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
|
|
872
|
+
});
|
|
520
873
|
},
|
|
521
|
-
create(input) {
|
|
522
|
-
|
|
874
|
+
async create(input) {
|
|
875
|
+
const result = await deps.call('conversations/create', input);
|
|
876
|
+
const conversation = answered(result, 'conversation_id');
|
|
877
|
+
if (conversation === null)
|
|
878
|
+
return result;
|
|
879
|
+
if (!listing.has(conversation))
|
|
880
|
+
creations.add(conversation);
|
|
881
|
+
const creation = creationOf(input);
|
|
882
|
+
if (creation !== null)
|
|
883
|
+
await moved(creation.agent, creation.operation, conversation);
|
|
884
|
+
return result;
|
|
523
885
|
},
|
|
524
|
-
async send(conversation) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
if (
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const files = await Promise.all((draft?.files ?? []).map(async (file) => ({
|
|
533
|
-
...(await declaredFile(file)),
|
|
534
|
-
blob: file.blob,
|
|
535
|
-
})));
|
|
536
|
-
const result = await invoke('conversation.send', conversation, {
|
|
537
|
-
text: draft?.text ?? '',
|
|
538
|
-
files,
|
|
539
|
-
});
|
|
540
|
-
const turn = turnOf(result);
|
|
541
|
-
if (turn === null || draft === undefined)
|
|
542
|
-
return result;
|
|
543
|
-
await withDraft(conversation, (current) => {
|
|
544
|
-
if (current !== draft)
|
|
545
|
-
return undefined;
|
|
546
|
-
const sealed = (messages.get(conversation) ?? []).some((message) => message.turn === turn);
|
|
547
|
-
return sealed ? null : { ...draft, turn };
|
|
886
|
+
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
887
|
+
if (deps.full())
|
|
888
|
+
return DURABLE_WRITE;
|
|
889
|
+
if (deps.online()) {
|
|
890
|
+
const held = await submitted(conversation, operation, draft).catch((error) => {
|
|
891
|
+
if (refusedForSpace(error))
|
|
892
|
+
return null;
|
|
893
|
+
throw error;
|
|
548
894
|
});
|
|
549
|
-
|
|
895
|
+
if (held === null)
|
|
896
|
+
return DURABLE_WRITE;
|
|
897
|
+
return dispatched(conversation, held);
|
|
550
898
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
deps.publish(conversationChangeSet([], [conversation]));
|
|
556
|
-
}
|
|
557
|
-
}
|
|
899
|
+
if (draft !== undefined) {
|
|
900
|
+
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
901
|
+
? undefined
|
|
902
|
+
: returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
558
903
|
}
|
|
904
|
+
return OFFLINE;
|
|
559
905
|
},
|
|
560
906
|
answer(conversation, answer) {
|
|
561
|
-
return invoke('
|
|
907
|
+
return invoke('conversations/interaction/answer', conversation, {
|
|
908
|
+
operation_id: changeBatchIdentity(),
|
|
909
|
+
...answer,
|
|
910
|
+
});
|
|
562
911
|
},
|
|
563
912
|
interrupt(conversation) {
|
|
564
|
-
return invoke('
|
|
913
|
+
return invoke('conversations/interrupt', conversation);
|
|
565
914
|
},
|
|
566
915
|
kill(conversation) {
|
|
567
|
-
return invoke('
|
|
568
|
-
},
|
|
569
|
-
async cancel(conversation, turn) {
|
|
570
|
-
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
571
|
-
if (result.kind !== 'action')
|
|
572
|
-
return result;
|
|
573
|
-
await withDraft(conversation, (current) => current === undefined || current.turn !== turn ? undefined : { ...current, turn: null });
|
|
574
|
-
return result;
|
|
916
|
+
return invoke('conversations/kill', conversation);
|
|
575
917
|
},
|
|
576
918
|
delete(conversation) {
|
|
577
|
-
return invoke('
|
|
919
|
+
return invoke('conversations/delete', conversation);
|
|
578
920
|
},
|
|
579
921
|
};
|
|
580
922
|
const chatDraft = {
|
|
581
923
|
write(conversation, draft, revision = chatDraftRevision()) {
|
|
582
|
-
return withDraft(conversation, () => ({
|
|
924
|
+
return withDraft(conversation, (current) => ({
|
|
583
925
|
conversation,
|
|
584
926
|
text: draft.text,
|
|
585
|
-
files: [...draft.files],
|
|
586
|
-
turn: null,
|
|
587
927
|
revision,
|
|
928
|
+
...(current === undefined ? {} : pending(current)),
|
|
929
|
+
}));
|
|
930
|
+
},
|
|
931
|
+
prepend(conversation, draft) {
|
|
932
|
+
return withDraft(conversation, (current) => ({
|
|
933
|
+
conversation,
|
|
934
|
+
...(current === undefined ? { text: draft.text } : prepended(current, draft)),
|
|
935
|
+
revision: chatDraftRevision(),
|
|
936
|
+
...(current === undefined ? {} : pending(current)),
|
|
588
937
|
}));
|
|
589
938
|
},
|
|
590
939
|
discard(conversation) {
|
|
591
|
-
return withDraft(conversation, (current) =>
|
|
940
|
+
return withDraft(conversation, (current) => current === undefined
|
|
941
|
+
? undefined
|
|
942
|
+
: { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
|
|
592
943
|
},
|
|
593
944
|
};
|
|
594
945
|
return {
|
|
595
946
|
surface,
|
|
596
947
|
chatDraft,
|
|
597
|
-
hydrate(held,
|
|
598
|
-
|
|
948
|
+
hydrate(held, kept, list, cursor) {
|
|
949
|
+
if (list !== null && !listKnown) {
|
|
950
|
+
listing.clear();
|
|
951
|
+
for (const entry of list)
|
|
952
|
+
listing.set(entry.conversation, { ...entry });
|
|
953
|
+
listHeld = true;
|
|
954
|
+
listCursor = cursor;
|
|
955
|
+
deps.stream()?.hold(cursor);
|
|
956
|
+
}
|
|
957
|
+
stored.clear();
|
|
599
958
|
reads.clear();
|
|
600
|
-
|
|
601
|
-
view.messages = null;
|
|
959
|
+
partials.clear();
|
|
602
960
|
for (const entry of held) {
|
|
603
|
-
|
|
961
|
+
stored.add(entry.conversation);
|
|
604
962
|
if (entry.read !== undefined)
|
|
605
963
|
reads.set(entry.conversation, entry.read);
|
|
964
|
+
if (entry.partial)
|
|
965
|
+
partials.add(entry.conversation);
|
|
606
966
|
}
|
|
607
967
|
drafts.clear();
|
|
608
|
-
for (const record of
|
|
968
|
+
for (const record of kept)
|
|
609
969
|
drafts.set(record.conversation, record);
|
|
970
|
+
if (listHeld)
|
|
971
|
+
learned();
|
|
610
972
|
},
|
|
611
973
|
async reconcile() {
|
|
612
974
|
if (!listKnown)
|
|
613
975
|
return;
|
|
614
976
|
const named = new Set(listing.keys());
|
|
615
977
|
const leaving = [
|
|
616
|
-
...new Set([...replicated, ...
|
|
617
|
-
].filter((conversation) => !named.has(conversation)
|
|
978
|
+
...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
|
|
979
|
+
].filter((conversation) => !named.has(conversation) &&
|
|
980
|
+
!creations.has(conversation) &&
|
|
981
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
618
982
|
for (const conversation of named) {
|
|
619
|
-
if (!replicated.has(conversation))
|
|
983
|
+
if (!replicated.has(conversation) && missed(conversation))
|
|
620
984
|
behind.add(conversation);
|
|
621
985
|
}
|
|
622
986
|
replicated = named;
|
|
@@ -625,44 +989,97 @@ export function createConversationStore(deps) {
|
|
|
625
989
|
await remove(leaving);
|
|
626
990
|
},
|
|
627
991
|
async catchUp(progress) {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
992
|
+
if (deps.full()) {
|
|
993
|
+
if (behind.size > 0)
|
|
994
|
+
progress.complete = false;
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
|
|
998
|
+
const read = async (conversations) => {
|
|
999
|
+
if (conversations.length === 0 || deps.closed())
|
|
1000
|
+
return;
|
|
1001
|
+
const failed = await caughtUp(conversations);
|
|
631
1002
|
if (failed?.kind === 'transport-uncertainty')
|
|
632
1003
|
progress.reachable = false;
|
|
633
|
-
}
|
|
1004
|
+
};
|
|
1005
|
+
const asking = [...behind].filter(owed).sort();
|
|
1006
|
+
const awaited = asking.filter((conversation) => catching.has(conversation));
|
|
1007
|
+
await Promise.all([
|
|
1008
|
+
read(asking.filter((conversation) => !catching.has(conversation))),
|
|
1009
|
+
Promise.all(awaited.map((conversation) => catching.get(conversation))).then(() => read(awaited.filter((conversation) => owed(conversation) && !catching.has(conversation)))),
|
|
1010
|
+
]);
|
|
634
1011
|
if (behind.size > 0)
|
|
635
1012
|
progress.complete = false;
|
|
636
1013
|
},
|
|
1014
|
+
async resend(progress) {
|
|
1015
|
+
for (const [conversation, held] of [...drafts]) {
|
|
1016
|
+
for (const submission of held.submitted ?? []) {
|
|
1017
|
+
if (submission.turn !== null || sending.has(submission.operation))
|
|
1018
|
+
continue;
|
|
1019
|
+
if (deps.closed())
|
|
1020
|
+
return;
|
|
1021
|
+
const result = await dispatched(conversation, submission);
|
|
1022
|
+
if (result.kind === 'revoked-authorization')
|
|
1023
|
+
return;
|
|
1024
|
+
if (result.kind === 'transport-uncertainty') {
|
|
1025
|
+
progress.reachable = false;
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
if (result.kind !== 'action' && result.retryable) {
|
|
1029
|
+
progress.complete = false;
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
},
|
|
637
1035
|
current() {
|
|
638
1036
|
return behind.size === 0;
|
|
639
1037
|
},
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1038
|
+
known() {
|
|
1039
|
+
return listKnown;
|
|
1040
|
+
},
|
|
1041
|
+
reopened() {
|
|
1042
|
+
relisting = null;
|
|
1043
|
+
for (const [operation, dispatch] of sending)
|
|
1044
|
+
if (!dispatch.answered)
|
|
1045
|
+
sending.delete(operation);
|
|
1046
|
+
},
|
|
1047
|
+
lost() {
|
|
1048
|
+
catching.clear();
|
|
643
1049
|
},
|
|
644
1050
|
watch(conversation, options, deliver) {
|
|
645
|
-
const
|
|
646
|
-
const key = JSON.stringify([conversation, limit ?? null]);
|
|
1051
|
+
const asked = askedOf(options);
|
|
1052
|
+
const key = JSON.stringify([conversation, asked.limit, asked.after ?? null, asked.total]);
|
|
647
1053
|
const view = views.get(key) ?? {
|
|
648
1054
|
conversation,
|
|
649
|
-
|
|
1055
|
+
asked,
|
|
650
1056
|
watchers: new Set(),
|
|
651
|
-
|
|
1057
|
+
window: null,
|
|
1058
|
+
more: false,
|
|
1059
|
+
total: null,
|
|
652
1060
|
};
|
|
653
1061
|
views.set(key, view);
|
|
654
1062
|
const watcher = { notify: deliver, ready: false };
|
|
655
1063
|
view.watchers.add(watcher);
|
|
656
|
-
|
|
657
|
-
|
|
1064
|
+
const reading = new AbortController();
|
|
1065
|
+
void untilAnswered(() => deps.durably(async (local) => {
|
|
1066
|
+
if (view.window !== null)
|
|
1067
|
+
return;
|
|
1068
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1069
|
+
view.window = messages;
|
|
1070
|
+
view.more = more;
|
|
1071
|
+
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
1072
|
+
}), reading.signal)
|
|
658
1073
|
.then(() => {
|
|
659
1074
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
660
1075
|
return;
|
|
661
1076
|
watcher.ready = true;
|
|
662
|
-
|
|
1077
|
+
const window = view.window ?? [];
|
|
1078
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
663
1079
|
})
|
|
664
1080
|
.catch(() => { });
|
|
665
1081
|
return () => {
|
|
1082
|
+
reading.abort();
|
|
666
1083
|
if (!view.watchers.delete(watcher))
|
|
667
1084
|
return;
|
|
668
1085
|
if (view.watchers.size === 0)
|
|
@@ -670,73 +1087,88 @@ export function createConversationStore(deps) {
|
|
|
670
1087
|
};
|
|
671
1088
|
},
|
|
672
1089
|
watchList(deliver) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (!deps.closed() && listWatchers.has(deliver))
|
|
676
|
-
deliver(entries());
|
|
677
|
-
});
|
|
1090
|
+
unlisted.add(deliver);
|
|
1091
|
+
learned();
|
|
678
1092
|
return () => {
|
|
1093
|
+
unlisted.delete(deliver);
|
|
679
1094
|
listWatchers.delete(deliver);
|
|
680
1095
|
};
|
|
681
1096
|
},
|
|
682
1097
|
watchAgents(deliver) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
deliver(agents());
|
|
687
|
-
});
|
|
1098
|
+
unnamed.add(deliver);
|
|
1099
|
+
learned();
|
|
1100
|
+
readAgents();
|
|
688
1101
|
return () => {
|
|
1102
|
+
unnamed.delete(deliver);
|
|
689
1103
|
agentWatchers.delete(deliver);
|
|
690
1104
|
};
|
|
691
1105
|
},
|
|
1106
|
+
learned,
|
|
692
1107
|
changed(set) {
|
|
693
1108
|
for (const conversation of set.conversations)
|
|
694
1109
|
emit(conversation);
|
|
695
|
-
const declaration = deps.agents();
|
|
696
|
-
if (declaration !== undefined &&
|
|
697
|
-
set.documents.some((change) => change.locator === declaration.locator)) {
|
|
698
|
-
agentsRead = readAgents().catch(() => {
|
|
699
|
-
agentsRead = null;
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
1110
|
},
|
|
703
1111
|
receive(event) {
|
|
704
1112
|
if (deps.closed())
|
|
705
1113
|
return false;
|
|
706
1114
|
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
707
1115
|
if (event.kind === 'list') {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
1116
|
+
if (event.whole)
|
|
1117
|
+
relisting ??= new Map();
|
|
1118
|
+
for (const entry of event.entries) {
|
|
1119
|
+
if (relisting === null)
|
|
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);
|
|
711
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();
|
|
712
1144
|
}
|
|
713
1145
|
else if (event.kind === 'listed') {
|
|
714
|
-
|
|
1146
|
+
listed(event.entry);
|
|
1147
|
+
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
715
1148
|
}
|
|
716
1149
|
else {
|
|
717
1150
|
listing.delete(event.conversation);
|
|
1151
|
+
relisting?.delete(event.conversation);
|
|
718
1152
|
}
|
|
1153
|
+
keepList();
|
|
719
1154
|
emitList();
|
|
720
|
-
return (event.kind !== 'list' &&
|
|
1155
|
+
return ((event.kind !== 'list' &&
|
|
721
1156
|
listKnown &&
|
|
722
1157
|
(listing.size !== replicated.size ||
|
|
723
|
-
[...listing.keys()].some((conversation) => !replicated.has(conversation))))
|
|
1158
|
+
[...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
|
|
1159
|
+
(listKnown && [...drafts.keys()].some(retiredChat)));
|
|
724
1160
|
}
|
|
725
1161
|
heard(event);
|
|
726
1162
|
return false;
|
|
727
1163
|
},
|
|
728
|
-
messages() {
|
|
729
|
-
return [...messages].flatMap(([conversation, held]) => held.map(({ identity, text }) => ({ conversation, identity, text })));
|
|
730
|
-
},
|
|
731
|
-
message(conversation, identity) {
|
|
732
|
-
const held = messages.get(conversation)?.find((message) => message.identity === identity);
|
|
733
|
-
return held === undefined ? null : { conversation, identity, text: held.text };
|
|
734
|
-
},
|
|
735
1164
|
close() {
|
|
736
1165
|
views.clear();
|
|
737
1166
|
lives.clear();
|
|
1167
|
+
unsent.clear();
|
|
738
1168
|
listWatchers.clear();
|
|
1169
|
+
unlisted.clear();
|
|
739
1170
|
agentWatchers.clear();
|
|
1171
|
+
unnamed.clear();
|
|
740
1172
|
},
|
|
741
1173
|
};
|
|
742
1174
|
}
|