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