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