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