@agentshouse/mdruntime 0.1.0-c454894e67f74 → 0.1.0-c4ca57eefb98a
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 +90 -34
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +5 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +38 -4
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +27 -8
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +174 -98
- package/dist/chain.d.ts +11 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +68 -0
- package/dist/change-set.d.ts +9 -16
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -88
- 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 +13 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +95 -10
- package/dist/collection.d.ts +11 -19
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +53 -289
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +773 -0
- package/dist/connection.d.ts +29 -13
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +3055 -1090
- package/dist/conversation.d.ts +20 -6
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +390 -123
- 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 +3 -5
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +4 -1
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -23
- 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 +10 -8
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +95 -16
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +14 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/live.d.ts +22 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +124 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +208 -0
- package/dist/memory.d.ts +10 -5
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +89 -33
- 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 +6 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +7 -1
- 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 +15 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +93 -3
- 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 +45 -4
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +39 -0
- package/dist/overlay.d.ts +3 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +95 -156
- 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 +38 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +48 -7
- package/dist/reach.d.ts +14 -6
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +213 -71
- package/dist/react.d.ts +3 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/readiness.d.ts +19 -9
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +6 -6
- package/dist/reads.d.ts +11 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +91 -0
- package/dist/recovery.d.ts +2 -2
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +8 -7
- package/dist/refresh.d.ts +16 -15
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +113 -50
- package/dist/replica.d.ts +56 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +298 -179
- package/dist/room-draft.d.ts +33 -3
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +105 -31
- package/dist/rows.d.ts +38 -29
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +173 -94
- package/dist/save.d.ts +53 -14
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +529 -185
- package/dist/search.d.ts +32 -43
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +37 -168
- 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 +8 -3
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +129 -66
- 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 +95 -89
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +329 -654
- package/dist/stored.d.ts +135 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +569 -0
- package/dist/tables.d.ts +352 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +621 -0
- package/dist/turn.d.ts +5 -3
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +51 -21
- package/dist/validation.d.ts +30 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +162 -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 +33 -1
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +9 -1
- package/dist/words.d.ts +19 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +156 -0
- package/package.json +18 -19
- package/AGENTS.md +0 -302
- package/dist/acquisition.d.ts +0 -14
- package/dist/acquisition.d.ts.map +0 -1
- package/dist/acquisition.js +0 -24
- package/dist/chat/acts.d.ts +0 -33
- 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 -61
- 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 -27
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -63
- package/dist/chat/markdown.d.ts +0 -20
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -188
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -192
- package/dist/chat/messages.d.ts +0 -22
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -155
- 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 -629
- 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 -34
- 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 -25
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -639
package/dist/conversation.js
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import { conversationChangeSet } from './change-set.js';
|
|
2
2
|
import { runtimeError } from './failure.js';
|
|
3
|
-
import {
|
|
3
|
+
import { refusedForSpace } from './durability.js';
|
|
4
4
|
import { changeBatchIdentity, chatDraftRevision } from './identity.js';
|
|
5
5
|
import { OFFLINE } from './outcome.js';
|
|
6
|
+
import { capped, cursorOf, decodedCursor, FROM_START, messageOrder, precedes } from './windows.js';
|
|
6
7
|
const QUIET = {
|
|
7
8
|
turn: null,
|
|
8
9
|
questions: [],
|
|
9
10
|
process: null,
|
|
10
11
|
commands: [],
|
|
11
12
|
options: [],
|
|
13
|
+
starting: null,
|
|
14
|
+
link: null,
|
|
15
|
+
deliveryUnknown: [],
|
|
12
16
|
};
|
|
17
|
+
const FACTS = [
|
|
18
|
+
'questions',
|
|
19
|
+
'process',
|
|
20
|
+
'commands',
|
|
21
|
+
'options',
|
|
22
|
+
'starting',
|
|
23
|
+
'link',
|
|
24
|
+
'deliveryUnknown',
|
|
25
|
+
];
|
|
13
26
|
export function unheldView(conversation) {
|
|
14
27
|
return {
|
|
15
28
|
conversation,
|
|
@@ -19,19 +32,25 @@ export function unheldView(conversation) {
|
|
|
19
32
|
process: null,
|
|
20
33
|
commands: [],
|
|
21
34
|
options: [],
|
|
35
|
+
starting: null,
|
|
36
|
+
link: null,
|
|
37
|
+
deliveryUnknown: [],
|
|
22
38
|
chatDraft: null,
|
|
23
39
|
};
|
|
24
40
|
}
|
|
25
41
|
export const SESSION_STARTED = 'Session started';
|
|
26
42
|
const NEW_CHAT = 'new:';
|
|
43
|
+
export const HISTORY_CONVERSATIONS = 64;
|
|
27
44
|
export function newChat(agent) {
|
|
28
45
|
return `${NEW_CHAT}${agent}`;
|
|
29
46
|
}
|
|
30
47
|
function creationOf(input) {
|
|
31
48
|
if (typeof input !== 'object' || input === null)
|
|
32
49
|
return null;
|
|
33
|
-
const { agent_id: agent, operation_id: operation } = input;
|
|
34
|
-
return typeof agent === 'string' && typeof operation === 'string'
|
|
50
|
+
const { agent_id: agent, operation_id: operation, replaces, } = input;
|
|
51
|
+
return typeof agent === 'string' && typeof operation === 'string' && replaces === undefined
|
|
52
|
+
? { agent, operation }
|
|
53
|
+
: null;
|
|
35
54
|
}
|
|
36
55
|
function startedTurn(turn, operation) {
|
|
37
56
|
if (operation.op === 'begin')
|
|
@@ -82,19 +101,25 @@ function appliedTurn(held, operation) {
|
|
|
82
101
|
parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
|
|
83
102
|
};
|
|
84
103
|
}
|
|
85
|
-
function ordered(left, right) {
|
|
86
|
-
if (left.position !== right.position)
|
|
87
|
-
return left.position < right.position ? -1 : 1;
|
|
88
|
-
if (left.identity !== right.identity)
|
|
89
|
-
return left.identity < right.identity ? -1 : 1;
|
|
90
|
-
return 0;
|
|
91
|
-
}
|
|
92
104
|
function windowOf(held, arriving, limit) {
|
|
93
105
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
94
106
|
for (const message of arriving)
|
|
95
107
|
byIdentity.set(message.identity, message);
|
|
96
|
-
const next = [...byIdentity.values()].sort(
|
|
97
|
-
|
|
108
|
+
const next = [...byIdentity.values()].sort(messageOrder);
|
|
109
|
+
const start = Math.max(0, next.length - limit);
|
|
110
|
+
return { messages: next.slice(start), dropped: start > 0 };
|
|
111
|
+
}
|
|
112
|
+
function frameOf(asked, messages, more, total) {
|
|
113
|
+
const oldest = messages[0];
|
|
114
|
+
const next = !more
|
|
115
|
+
? undefined
|
|
116
|
+
: oldest === undefined
|
|
117
|
+
? (asked.after ?? FROM_START)
|
|
118
|
+
: cursorOf([oldest.position, oldest.identity]);
|
|
119
|
+
return {
|
|
120
|
+
...(next === undefined ? {} : { next }),
|
|
121
|
+
...(total === null ? {} : { total }),
|
|
122
|
+
};
|
|
98
123
|
}
|
|
99
124
|
function newestOf(arriving, from) {
|
|
100
125
|
return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
|
|
@@ -165,18 +190,34 @@ function answered(result, field) {
|
|
|
165
190
|
const value = answer[field];
|
|
166
191
|
return typeof value === 'string' ? value : null;
|
|
167
192
|
}
|
|
168
|
-
function
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (
|
|
193
|
+
function beforeOf(after) {
|
|
194
|
+
if (after === undefined)
|
|
195
|
+
return null;
|
|
196
|
+
const decoded = typeof after === 'string' ? decodedCursor(after) : null;
|
|
197
|
+
if (decoded?.length === 0)
|
|
198
|
+
return null;
|
|
199
|
+
const [position, identity] = decoded ?? [];
|
|
200
|
+
if (decoded?.length !== 2 || typeof position !== 'string' || typeof identity !== 'string') {
|
|
201
|
+
throw runtimeError('invalid-after', `conversation after must be the next an earlier view returned, got ${String(after)}`);
|
|
202
|
+
}
|
|
203
|
+
return [position, identity];
|
|
204
|
+
}
|
|
205
|
+
function askedOf(options) {
|
|
206
|
+
const { limit, after, total } = options;
|
|
207
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit < 0)) {
|
|
173
208
|
throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
|
|
174
209
|
}
|
|
175
|
-
return limit;
|
|
210
|
+
return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
|
|
176
211
|
}
|
|
177
212
|
const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
|
|
213
|
+
const DURABLE_WRITE = {
|
|
214
|
+
kind: 'rejected',
|
|
215
|
+
code: 'durable-write',
|
|
216
|
+
fields: {},
|
|
217
|
+
retryable: true,
|
|
218
|
+
};
|
|
178
219
|
function unwritten(draft) {
|
|
179
|
-
return draft.text === ''
|
|
220
|
+
return draft.text === '';
|
|
180
221
|
}
|
|
181
222
|
function pending(draft) {
|
|
182
223
|
return {
|
|
@@ -191,11 +232,13 @@ function withSubmitted(draft, submitted) {
|
|
|
191
232
|
const { submitted: _held, ...unsent } = draft;
|
|
192
233
|
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
193
234
|
}
|
|
235
|
+
function prepended(draft, said) {
|
|
236
|
+
return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
|
|
237
|
+
}
|
|
194
238
|
function returned(draft, submission, others) {
|
|
195
239
|
return withSubmitted({
|
|
196
240
|
conversation: draft.conversation,
|
|
197
|
-
|
|
198
|
-
files: [...submission.files, ...draft.files],
|
|
241
|
+
...prepended(draft, submission),
|
|
199
242
|
revision: chatDraftRevision(),
|
|
200
243
|
}, others);
|
|
201
244
|
}
|
|
@@ -206,23 +249,31 @@ export function createConversationStore(deps) {
|
|
|
206
249
|
const drafts = new Map();
|
|
207
250
|
const sending = new Map();
|
|
208
251
|
const behind = new Set();
|
|
252
|
+
const catching = new Map();
|
|
209
253
|
const gaps = new Map();
|
|
210
254
|
const lives = new Map();
|
|
255
|
+
const unsent = new Map();
|
|
211
256
|
const views = new Map();
|
|
212
257
|
const listing = new Map();
|
|
213
258
|
const creations = new Set();
|
|
214
259
|
const listWatchers = new Set();
|
|
260
|
+
const unlisted = new Set();
|
|
215
261
|
let listKnown = false;
|
|
262
|
+
let listHeld = false;
|
|
263
|
+
let listKept = false;
|
|
216
264
|
const agentWatchers = new Set();
|
|
265
|
+
const unnamed = new Set();
|
|
266
|
+
let agentsKnown = false;
|
|
217
267
|
let agentNames = new Map();
|
|
218
268
|
let retiredAgents = new Set();
|
|
219
269
|
let agentsRead = null;
|
|
270
|
+
let agentsFrom;
|
|
220
271
|
let replicated = new Set();
|
|
221
272
|
let handedReplicated;
|
|
222
273
|
function liveOf(conversation) {
|
|
223
274
|
return lives.get(conversation) ?? QUIET;
|
|
224
275
|
}
|
|
225
|
-
function viewOf(conversation, held) {
|
|
276
|
+
function viewOf(conversation, held, frame) {
|
|
226
277
|
const draft = drafts.get(conversation);
|
|
227
278
|
const facts = liveOf(conversation);
|
|
228
279
|
return {
|
|
@@ -233,14 +284,18 @@ export function createConversationStore(deps) {
|
|
|
233
284
|
text,
|
|
234
285
|
message,
|
|
235
286
|
})),
|
|
287
|
+
...frame,
|
|
236
288
|
turn: structuredClone(facts.turn),
|
|
237
289
|
questions: structuredClone(facts.questions),
|
|
238
290
|
process: facts.process === null ? null : { ...facts.process },
|
|
239
291
|
commands: structuredClone(facts.commands),
|
|
240
292
|
options: structuredClone(facts.options),
|
|
293
|
+
starting: facts.starting,
|
|
294
|
+
link: facts.link === null ? null : { ...facts.link },
|
|
295
|
+
deliveryUnknown: [...facts.deliveryUnknown],
|
|
241
296
|
chatDraft: draft === undefined || unwritten(draft)
|
|
242
297
|
? null
|
|
243
|
-
: { text: draft.text,
|
|
298
|
+
: { text: draft.text, revision: draft.revision },
|
|
244
299
|
};
|
|
245
300
|
}
|
|
246
301
|
function emit(conversation) {
|
|
@@ -252,21 +307,36 @@ export function createConversationStore(deps) {
|
|
|
252
307
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
253
308
|
if (ready.length === 0)
|
|
254
309
|
continue;
|
|
255
|
-
const value = viewOf(conversation, view.window);
|
|
310
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
256
311
|
for (const watcher of ready)
|
|
257
312
|
watcher.notify(value);
|
|
258
313
|
}
|
|
259
314
|
}
|
|
260
|
-
function
|
|
315
|
+
function sharedFrame(view, window) {
|
|
316
|
+
return frameOf(view.asked, window, view.more, view.total);
|
|
317
|
+
}
|
|
318
|
+
async function slide(local, conversation, arriving) {
|
|
319
|
+
let count;
|
|
261
320
|
for (const view of views.values()) {
|
|
262
321
|
if (view.conversation !== conversation || view.window === null)
|
|
263
322
|
continue;
|
|
264
|
-
|
|
323
|
+
if (arriving === null) {
|
|
324
|
+
view.window = [];
|
|
325
|
+
view.more = false;
|
|
326
|
+
if (view.total !== null)
|
|
327
|
+
view.total = 0;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const joining = arriving.filter((message) => precedes(message, view.asked.before));
|
|
331
|
+
const next = windowOf(view.window, joining, view.asked.limit);
|
|
332
|
+
view.window = next.messages;
|
|
333
|
+
view.more ||= next.dropped;
|
|
334
|
+
if (view.total !== null && arriving.length > 0) {
|
|
335
|
+
count ??= await local.messageCount(conversation);
|
|
336
|
+
view.total = count;
|
|
337
|
+
}
|
|
265
338
|
}
|
|
266
339
|
}
|
|
267
|
-
function changesOf(conversation, arriving) {
|
|
268
|
-
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
269
|
-
}
|
|
270
340
|
function stateOf(conversation) {
|
|
271
341
|
const facts = liveOf(conversation);
|
|
272
342
|
if (facts.questions.length > 0)
|
|
@@ -281,16 +351,58 @@ export function createConversationStore(deps) {
|
|
|
281
351
|
created: entry.created,
|
|
282
352
|
finished: entry.finished,
|
|
283
353
|
outcome: entry.outcome,
|
|
354
|
+
waiting: entry.waiting,
|
|
284
355
|
state: stateOf(entry.conversation),
|
|
285
356
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
286
357
|
}));
|
|
287
358
|
}
|
|
359
|
+
function keepList() {
|
|
360
|
+
if (!listKnown || listKept)
|
|
361
|
+
return;
|
|
362
|
+
listKept = true;
|
|
363
|
+
queueMicrotask(() => {
|
|
364
|
+
listKept = false;
|
|
365
|
+
if (deps.closed())
|
|
366
|
+
return;
|
|
367
|
+
const entries = [...listing.values()];
|
|
368
|
+
void deps.durably((local) => local.fact('conversationList', entries)).catch(() => { });
|
|
369
|
+
});
|
|
370
|
+
}
|
|
288
371
|
function emitList() {
|
|
289
|
-
if (deps.closed()
|
|
372
|
+
if (deps.closed())
|
|
290
373
|
return;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
watcher
|
|
374
|
+
if (listWatchers.size > 0) {
|
|
375
|
+
const value = entries();
|
|
376
|
+
for (const watcher of listWatchers)
|
|
377
|
+
watcher(value);
|
|
378
|
+
}
|
|
379
|
+
learned();
|
|
380
|
+
}
|
|
381
|
+
function unlearnable() {
|
|
382
|
+
return !deps.online() || (deps.listed() && deps.agents() === undefined);
|
|
383
|
+
}
|
|
384
|
+
function learned() {
|
|
385
|
+
if (deps.closed())
|
|
386
|
+
return;
|
|
387
|
+
const named = agentsKnown || unlearnable();
|
|
388
|
+
if (unlisted.size > 0 &&
|
|
389
|
+
named &&
|
|
390
|
+
(listKnown || listHeld || deps.stream() === undefined || unlearnable())) {
|
|
391
|
+
const value = entries();
|
|
392
|
+
for (const watcher of [...unlisted]) {
|
|
393
|
+
unlisted.delete(watcher);
|
|
394
|
+
listWatchers.add(watcher);
|
|
395
|
+
watcher(value);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (unnamed.size > 0 && named) {
|
|
399
|
+
const value = agents();
|
|
400
|
+
for (const watcher of [...unnamed]) {
|
|
401
|
+
unnamed.delete(watcher);
|
|
402
|
+
agentWatchers.add(watcher);
|
|
403
|
+
watcher(value);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
294
406
|
}
|
|
295
407
|
function agents() {
|
|
296
408
|
return [...agentNames]
|
|
@@ -321,34 +433,46 @@ export function createConversationStore(deps) {
|
|
|
321
433
|
}
|
|
322
434
|
async function readAgents() {
|
|
323
435
|
const declaration = deps.agents();
|
|
436
|
+
agentsFrom = JSON.stringify(declaration);
|
|
324
437
|
if (declaration === undefined)
|
|
325
438
|
return;
|
|
326
|
-
const held = await deps.documents(declaration);
|
|
327
|
-
if ('pending' in held)
|
|
328
|
-
return;
|
|
329
|
-
const { documents } = held;
|
|
330
439
|
const names = new Map();
|
|
331
440
|
const retired = new Set();
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
441
|
+
let covered = null;
|
|
442
|
+
for (let after;;) {
|
|
443
|
+
const read = await deps.documents(declaration, after);
|
|
444
|
+
if ('pending' in read)
|
|
445
|
+
return;
|
|
446
|
+
const coverage = JSON.stringify(read.coverage);
|
|
447
|
+
if (covered !== null && coverage !== covered)
|
|
448
|
+
return readAgents();
|
|
449
|
+
covered = coverage;
|
|
450
|
+
for (const document of read.documents) {
|
|
451
|
+
const identity = document.fields?.[declaration.identity];
|
|
452
|
+
const name = document.fields?.[declaration.name];
|
|
453
|
+
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
454
|
+
continue;
|
|
455
|
+
names.set(identity, name);
|
|
456
|
+
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
457
|
+
if (ended !== undefined && ended !== null)
|
|
458
|
+
retired.add(identity);
|
|
459
|
+
}
|
|
460
|
+
if (read.next === undefined)
|
|
461
|
+
break;
|
|
462
|
+
after = read.next;
|
|
343
463
|
}
|
|
464
|
+
agentsKnown = true;
|
|
344
465
|
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
345
|
-
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
466
|
+
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents])) {
|
|
467
|
+
learned();
|
|
346
468
|
return;
|
|
469
|
+
}
|
|
347
470
|
agentNames = names;
|
|
348
471
|
retiredAgents = retired;
|
|
472
|
+
emitAgents();
|
|
349
473
|
if (named)
|
|
350
474
|
emitList();
|
|
351
|
-
|
|
475
|
+
learned();
|
|
352
476
|
const ended = [...drafts.keys()].filter(retiredChat);
|
|
353
477
|
if (ended.length > 0)
|
|
354
478
|
await remove(ended);
|
|
@@ -363,9 +487,34 @@ export function createConversationStore(deps) {
|
|
|
363
487
|
return agentsRead;
|
|
364
488
|
}
|
|
365
489
|
function live(conversation, change) {
|
|
490
|
+
const owed = unsent.get(conversation);
|
|
491
|
+
for (const fact of FACTS.filter((one) => one in change))
|
|
492
|
+
owed?.delete(fact);
|
|
366
493
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
367
494
|
emit(conversation);
|
|
368
495
|
}
|
|
496
|
+
function openingEnded() {
|
|
497
|
+
const owed = [...unsent];
|
|
498
|
+
unsent.clear();
|
|
499
|
+
for (const [conversation, facts] of owed) {
|
|
500
|
+
const held = lives.get(conversation);
|
|
501
|
+
if (held === undefined || facts.size === 0)
|
|
502
|
+
continue;
|
|
503
|
+
setLive(conversation, {
|
|
504
|
+
turn: held.turn,
|
|
505
|
+
questions: facts.has('questions') ? QUIET.questions : held.questions,
|
|
506
|
+
process: facts.has('process') ? QUIET.process : held.process,
|
|
507
|
+
commands: facts.has('commands') ? QUIET.commands : held.commands,
|
|
508
|
+
options: facts.has('options') ? QUIET.options : held.options,
|
|
509
|
+
starting: facts.has('starting') ? QUIET.starting : held.starting,
|
|
510
|
+
link: facts.has('link') ? QUIET.link : held.link,
|
|
511
|
+
deliveryUnknown: facts.has('deliveryUnknown')
|
|
512
|
+
? QUIET.deliveryUnknown
|
|
513
|
+
: held.deliveryUnknown,
|
|
514
|
+
});
|
|
515
|
+
emit(conversation);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
369
518
|
function relayed(conversation, turn, operation) {
|
|
370
519
|
const held = liveOf(conversation).turn;
|
|
371
520
|
const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
|
|
@@ -392,12 +541,6 @@ export function createConversationStore(deps) {
|
|
|
392
541
|
}
|
|
393
542
|
async function remove(leaving) {
|
|
394
543
|
await deps.durably(async (local) => {
|
|
395
|
-
const removed = [];
|
|
396
|
-
for (const conversation of leaving) {
|
|
397
|
-
for (const identity of await local.messageIdentities(conversation)) {
|
|
398
|
-
removed.push({ conversation, identity, text: null });
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
544
|
await local.dropConversations(leaving);
|
|
402
545
|
const changed = [];
|
|
403
546
|
for (const conversation of leaving) {
|
|
@@ -409,13 +552,28 @@ export function createConversationStore(deps) {
|
|
|
409
552
|
partials.delete(conversation);
|
|
410
553
|
drafts.delete(conversation);
|
|
411
554
|
behind.delete(conversation);
|
|
555
|
+
unsent.delete(conversation);
|
|
412
556
|
setLive(conversation, null);
|
|
413
|
-
slide(conversation, null);
|
|
557
|
+
await slide(local, conversation, null);
|
|
414
558
|
}
|
|
415
559
|
if (changed.length > 0)
|
|
416
|
-
deps.publish(conversationChangeSet(
|
|
560
|
+
deps.publish(conversationChangeSet(changed));
|
|
417
561
|
});
|
|
418
562
|
}
|
|
563
|
+
function missed(conversation) {
|
|
564
|
+
const read = reads.get(conversation);
|
|
565
|
+
const newest = listing.get(conversation)?.newest;
|
|
566
|
+
return (read === undefined ||
|
|
567
|
+
newest === undefined ||
|
|
568
|
+
partials.has(conversation) ||
|
|
569
|
+
(newest !== null && (read === null || newest > read)));
|
|
570
|
+
}
|
|
571
|
+
function noted(conversation, message) {
|
|
572
|
+
const entry = listing.get(conversation);
|
|
573
|
+
if (entry === undefined || (entry.newest !== null && entry.newest >= message.position))
|
|
574
|
+
return;
|
|
575
|
+
listing.set(conversation, { ...entry, newest: message.position });
|
|
576
|
+
}
|
|
419
577
|
function gap(conversation) {
|
|
420
578
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
421
579
|
behind.add(conversation);
|
|
@@ -438,17 +596,18 @@ export function createConversationStore(deps) {
|
|
|
438
596
|
if (advances)
|
|
439
597
|
reads.set(conversation, message.position);
|
|
440
598
|
sealTurns(conversation, new Set([message.turn]));
|
|
441
|
-
slide(conversation, [message]);
|
|
442
|
-
deps.publish(conversationChangeSet(
|
|
599
|
+
await slide(local, conversation, [message]);
|
|
600
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
443
601
|
});
|
|
444
602
|
}
|
|
445
|
-
async function readHistories(conversations) {
|
|
603
|
+
async function readHistories(conversations, ended) {
|
|
446
604
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
447
|
-
let
|
|
605
|
+
let waiting = conversations.map((conversation) => ({
|
|
448
606
|
conversation,
|
|
449
607
|
after: reads.get(conversation) ?? undefined,
|
|
450
608
|
}));
|
|
451
|
-
while (
|
|
609
|
+
while (waiting.length > 0) {
|
|
610
|
+
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
452
611
|
const result = await deps.call('conversation.history', {
|
|
453
612
|
conversations: asking.map(({ conversation, after }) => ({
|
|
454
613
|
conversation,
|
|
@@ -468,10 +627,33 @@ export function createConversationStore(deps) {
|
|
|
468
627
|
following.push({ conversation: page.conversation, after: page.next });
|
|
469
628
|
}
|
|
470
629
|
}
|
|
471
|
-
asking
|
|
630
|
+
for (const { conversation } of asking) {
|
|
631
|
+
if (!following.some((next) => next.conversation === conversation))
|
|
632
|
+
ended(conversation);
|
|
633
|
+
}
|
|
634
|
+
waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
|
|
472
635
|
}
|
|
473
636
|
return null;
|
|
474
637
|
}
|
|
638
|
+
function caughtUp(conversations) {
|
|
639
|
+
const ends = new Map();
|
|
640
|
+
for (const conversation of conversations) {
|
|
641
|
+
const held = new Promise((end) => ends.set(conversation, end));
|
|
642
|
+
catching.set(conversation, held);
|
|
643
|
+
void held.then(() => {
|
|
644
|
+
if (catching.get(conversation) === held)
|
|
645
|
+
catching.delete(conversation);
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
const ended = (conversation) => ends.get(conversation)?.();
|
|
649
|
+
const read = readHistories(conversations, ended);
|
|
650
|
+
const endAll = () => {
|
|
651
|
+
for (const conversation of conversations)
|
|
652
|
+
ended(conversation);
|
|
653
|
+
};
|
|
654
|
+
read.then(endAll, endAll);
|
|
655
|
+
return read;
|
|
656
|
+
}
|
|
475
657
|
function keepPage(conversation, arriving, from, next, generation) {
|
|
476
658
|
return deps.durably(async (local) => {
|
|
477
659
|
if (!replicated.has(conversation))
|
|
@@ -490,9 +672,9 @@ export function createConversationStore(deps) {
|
|
|
490
672
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
491
673
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
492
674
|
behind.delete(conversation);
|
|
493
|
-
slide(conversation, arriving);
|
|
675
|
+
await slide(local, conversation, arriving);
|
|
494
676
|
if (arriving.length > 0 || sealing || whole) {
|
|
495
|
-
deps.publish(conversationChangeSet(
|
|
677
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
496
678
|
}
|
|
497
679
|
});
|
|
498
680
|
}
|
|
@@ -501,11 +683,8 @@ export function createConversationStore(deps) {
|
|
|
501
683
|
if (event.kind === 'gap') {
|
|
502
684
|
if (replicated.has(conversation))
|
|
503
685
|
gap(conversation);
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
return;
|
|
507
|
-
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
508
|
-
emit(conversation);
|
|
686
|
+
if (lives.has(conversation))
|
|
687
|
+
unsent.set(conversation, new Set(FACTS));
|
|
509
688
|
return;
|
|
510
689
|
}
|
|
511
690
|
if (event.kind === 'turn') {
|
|
@@ -528,11 +707,31 @@ export function createConversationStore(deps) {
|
|
|
528
707
|
live(conversation, { options: structuredClone(event.options) });
|
|
529
708
|
return;
|
|
530
709
|
}
|
|
710
|
+
if (event.kind === 'starting') {
|
|
711
|
+
live(conversation, { starting: event.step });
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (event.kind === 'link') {
|
|
715
|
+
live(conversation, { link: { connected: event.connected } });
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (event.kind === 'deliveries') {
|
|
719
|
+
live(conversation, { deliveryUnknown: [...event.unknown] });
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
531
722
|
const arriving = acceptedMessage(event.message);
|
|
532
723
|
if (arriving === null)
|
|
533
724
|
return;
|
|
725
|
+
noted(conversation, arriving);
|
|
534
726
|
if (replicated.has(conversation)) {
|
|
535
|
-
|
|
727
|
+
if (deps.full()) {
|
|
728
|
+
gap(conversation);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
void keep(conversation, arriving).catch((error) => {
|
|
732
|
+
if (refusedForSpace(error))
|
|
733
|
+
gap(conversation);
|
|
734
|
+
});
|
|
536
735
|
return;
|
|
537
736
|
}
|
|
538
737
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
@@ -567,7 +766,6 @@ export function createConversationStore(deps) {
|
|
|
567
766
|
const blank = {
|
|
568
767
|
conversation: from,
|
|
569
768
|
text: '',
|
|
570
|
-
files: [],
|
|
571
769
|
revision: chatDraftRevision(),
|
|
572
770
|
};
|
|
573
771
|
const { moves: _answered, ...unsent } = held ?? blank;
|
|
@@ -577,7 +775,7 @@ export function createConversationStore(deps) {
|
|
|
577
775
|
drafts.set(from, left);
|
|
578
776
|
for (const one of taken)
|
|
579
777
|
drafts.set(to, one);
|
|
580
|
-
deps.publish(conversationChangeSet([
|
|
778
|
+
deps.publish(conversationChangeSet([from, to]));
|
|
581
779
|
});
|
|
582
780
|
}
|
|
583
781
|
function withDraft(conversation, next) {
|
|
@@ -586,7 +784,7 @@ export function createConversationStore(deps) {
|
|
|
586
784
|
if (following === undefined)
|
|
587
785
|
return;
|
|
588
786
|
await kept(local, conversation, following);
|
|
589
|
-
deps.publish(conversationChangeSet([
|
|
787
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
590
788
|
});
|
|
591
789
|
}
|
|
592
790
|
async function submitted(conversation, operation, draft) {
|
|
@@ -598,17 +796,12 @@ export function createConversationStore(deps) {
|
|
|
598
796
|
if (submission !== undefined)
|
|
599
797
|
return undefined;
|
|
600
798
|
const said = draft ?? current;
|
|
601
|
-
submission = {
|
|
602
|
-
operation,
|
|
603
|
-
text: said?.text ?? '',
|
|
604
|
-
files: [...(said?.files ?? [])],
|
|
605
|
-
turn: null,
|
|
606
|
-
};
|
|
799
|
+
submission = { operation, text: said?.text ?? '', turn: null };
|
|
607
800
|
return {
|
|
608
801
|
conversation,
|
|
609
802
|
...(draft === undefined || current === undefined
|
|
610
|
-
? { text: '',
|
|
611
|
-
: { text: current.text,
|
|
803
|
+
? { text: '', revision: chatDraftRevision() }
|
|
804
|
+
: { text: current.text, revision: current.revision }),
|
|
612
805
|
submitted: [...(current?.submitted ?? []), submission],
|
|
613
806
|
};
|
|
614
807
|
});
|
|
@@ -623,15 +816,14 @@ export function createConversationStore(deps) {
|
|
|
623
816
|
});
|
|
624
817
|
}
|
|
625
818
|
async function dispatched(conversation, submission) {
|
|
626
|
-
const dispatch = {};
|
|
819
|
+
const dispatch = { answered: false };
|
|
627
820
|
sending.set(submission.operation, dispatch);
|
|
628
821
|
try {
|
|
629
|
-
const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
|
|
630
822
|
const result = await invoke('conversation.send', conversation, {
|
|
631
823
|
text: submission.text,
|
|
632
|
-
files,
|
|
633
824
|
operation_id: submission.operation,
|
|
634
825
|
});
|
|
826
|
+
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
635
827
|
if (result.kind === 'revoked-authorization')
|
|
636
828
|
return result;
|
|
637
829
|
if (result.kind !== 'action' && result.retryable) {
|
|
@@ -664,11 +856,23 @@ export function createConversationStore(deps) {
|
|
|
664
856
|
const surface = {
|
|
665
857
|
async list() {
|
|
666
858
|
await agentsReady();
|
|
667
|
-
return
|
|
859
|
+
return new Promise((settle) => {
|
|
860
|
+
const answer = (listed) => {
|
|
861
|
+
unlisted.delete(answer);
|
|
862
|
+
listWatchers.delete(answer);
|
|
863
|
+
settle(listed);
|
|
864
|
+
};
|
|
865
|
+
unlisted.add(answer);
|
|
866
|
+
learned();
|
|
867
|
+
});
|
|
668
868
|
},
|
|
669
869
|
async read(conversation, options = {}) {
|
|
670
|
-
const
|
|
671
|
-
return deps.durably(async (local) =>
|
|
870
|
+
const asked = askedOf(options);
|
|
871
|
+
return deps.durably(async (local) => {
|
|
872
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
873
|
+
const total = asked.total ? await local.messageCount(conversation) : null;
|
|
874
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
|
|
875
|
+
});
|
|
672
876
|
},
|
|
673
877
|
async create(input) {
|
|
674
878
|
const result = await deps.call('conversation.create', input);
|
|
@@ -683,8 +887,17 @@ export function createConversationStore(deps) {
|
|
|
683
887
|
return result;
|
|
684
888
|
},
|
|
685
889
|
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
890
|
+
if (deps.full())
|
|
891
|
+
return DURABLE_WRITE;
|
|
686
892
|
if (deps.online()) {
|
|
687
|
-
const
|
|
893
|
+
const held = await submitted(conversation, operation, draft).catch((error) => {
|
|
894
|
+
if (refusedForSpace(error))
|
|
895
|
+
return null;
|
|
896
|
+
throw error;
|
|
897
|
+
});
|
|
898
|
+
if (held === null)
|
|
899
|
+
return DURABLE_WRITE;
|
|
900
|
+
const { submission, earlier } = held;
|
|
688
901
|
if (!earlier || !deps.expired(operation))
|
|
689
902
|
return dispatched(conversation, submission);
|
|
690
903
|
await forgotten(conversation, submission);
|
|
@@ -693,7 +906,7 @@ export function createConversationStore(deps) {
|
|
|
693
906
|
if (draft !== undefined) {
|
|
694
907
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
695
908
|
? undefined
|
|
696
|
-
: returned(current ?? { conversation, text: '',
|
|
909
|
+
: returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
697
910
|
}
|
|
698
911
|
return OFFLINE;
|
|
699
912
|
},
|
|
@@ -727,27 +940,34 @@ export function createConversationStore(deps) {
|
|
|
727
940
|
return withDraft(conversation, (current) => ({
|
|
728
941
|
conversation,
|
|
729
942
|
text: draft.text,
|
|
730
|
-
files: [...draft.files],
|
|
731
943
|
revision,
|
|
732
944
|
...(current === undefined ? {} : pending(current)),
|
|
733
945
|
}));
|
|
734
946
|
},
|
|
947
|
+
prepend(conversation, draft) {
|
|
948
|
+
return withDraft(conversation, (current) => ({
|
|
949
|
+
conversation,
|
|
950
|
+
...(current === undefined ? { text: draft.text } : prepended(current, draft)),
|
|
951
|
+
revision: chatDraftRevision(),
|
|
952
|
+
...(current === undefined ? {} : pending(current)),
|
|
953
|
+
}));
|
|
954
|
+
},
|
|
735
955
|
discard(conversation) {
|
|
736
956
|
return withDraft(conversation, (current) => current === undefined
|
|
737
957
|
? undefined
|
|
738
|
-
: {
|
|
739
|
-
conversation,
|
|
740
|
-
text: '',
|
|
741
|
-
files: [],
|
|
742
|
-
revision: chatDraftRevision(),
|
|
743
|
-
...pending(current),
|
|
744
|
-
});
|
|
958
|
+
: { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
|
|
745
959
|
},
|
|
746
960
|
};
|
|
747
961
|
return {
|
|
748
962
|
surface,
|
|
749
963
|
chatDraft,
|
|
750
|
-
hydrate(held, kept) {
|
|
964
|
+
hydrate(held, kept, list) {
|
|
965
|
+
if (list !== null && !listKnown) {
|
|
966
|
+
listing.clear();
|
|
967
|
+
for (const entry of list)
|
|
968
|
+
listing.set(entry.conversation, { ...entry });
|
|
969
|
+
listHeld = true;
|
|
970
|
+
}
|
|
751
971
|
stored.clear();
|
|
752
972
|
reads.clear();
|
|
753
973
|
partials.clear();
|
|
@@ -761,6 +981,8 @@ export function createConversationStore(deps) {
|
|
|
761
981
|
drafts.clear();
|
|
762
982
|
for (const record of kept)
|
|
763
983
|
drafts.set(record.conversation, record);
|
|
984
|
+
if (listHeld)
|
|
985
|
+
learned();
|
|
764
986
|
},
|
|
765
987
|
async reconcile() {
|
|
766
988
|
if (!listKnown)
|
|
@@ -772,7 +994,7 @@ export function createConversationStore(deps) {
|
|
|
772
994
|
!creations.has(conversation) &&
|
|
773
995
|
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
774
996
|
for (const conversation of named) {
|
|
775
|
-
if (!replicated.has(conversation))
|
|
997
|
+
if (!replicated.has(conversation) && missed(conversation))
|
|
776
998
|
behind.add(conversation);
|
|
777
999
|
}
|
|
778
1000
|
replicated = named;
|
|
@@ -781,12 +1003,25 @@ export function createConversationStore(deps) {
|
|
|
781
1003
|
await remove(leaving);
|
|
782
1004
|
},
|
|
783
1005
|
async catchUp(progress) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1006
|
+
if (deps.full()) {
|
|
1007
|
+
if (behind.size > 0)
|
|
1008
|
+
progress.complete = false;
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
|
|
1012
|
+
const read = async (conversations) => {
|
|
1013
|
+
if (conversations.length === 0 || deps.closed())
|
|
1014
|
+
return;
|
|
1015
|
+
const failed = await caughtUp(conversations);
|
|
787
1016
|
if (failed?.kind === 'transport-uncertainty')
|
|
788
1017
|
progress.reachable = false;
|
|
789
|
-
}
|
|
1018
|
+
};
|
|
1019
|
+
const asking = [...behind].filter(owed).sort();
|
|
1020
|
+
const awaited = asking.filter((conversation) => catching.has(conversation));
|
|
1021
|
+
await Promise.all([
|
|
1022
|
+
read(asking.filter((conversation) => !catching.has(conversation))),
|
|
1023
|
+
Promise.all(awaited.map((conversation) => catching.get(conversation))).then(() => read(awaited.filter((conversation) => owed(conversation) && !catching.has(conversation)))),
|
|
1024
|
+
]);
|
|
790
1025
|
if (behind.size > 0)
|
|
791
1026
|
progress.complete = false;
|
|
792
1027
|
},
|
|
@@ -821,34 +1056,43 @@ export function createConversationStore(deps) {
|
|
|
821
1056
|
known() {
|
|
822
1057
|
return listKnown;
|
|
823
1058
|
},
|
|
824
|
-
reconnected() {
|
|
825
|
-
for (const conversation of replicated)
|
|
826
|
-
gap(conversation);
|
|
827
|
-
},
|
|
828
1059
|
reopened() {
|
|
829
|
-
sending
|
|
1060
|
+
for (const [operation, dispatch] of sending)
|
|
1061
|
+
if (!dispatch.answered)
|
|
1062
|
+
sending.delete(operation);
|
|
1063
|
+
},
|
|
1064
|
+
lost() {
|
|
1065
|
+
catching.clear();
|
|
830
1066
|
},
|
|
831
1067
|
watch(conversation, options, deliver) {
|
|
832
|
-
const
|
|
833
|
-
const key = JSON.stringify([conversation, limit ?? null]);
|
|
1068
|
+
const asked = askedOf(options);
|
|
1069
|
+
const key = JSON.stringify([conversation, asked.limit, asked.after ?? null, asked.total]);
|
|
834
1070
|
const view = views.get(key) ?? {
|
|
835
1071
|
conversation,
|
|
836
|
-
|
|
1072
|
+
asked,
|
|
837
1073
|
watchers: new Set(),
|
|
838
1074
|
window: null,
|
|
1075
|
+
more: false,
|
|
1076
|
+
total: null,
|
|
839
1077
|
};
|
|
840
1078
|
views.set(key, view);
|
|
841
1079
|
const watcher = { notify: deliver, ready: false };
|
|
842
1080
|
view.watchers.add(watcher);
|
|
843
1081
|
void deps
|
|
844
1082
|
.durably(async (local) => {
|
|
845
|
-
view.window
|
|
1083
|
+
if (view.window !== null)
|
|
1084
|
+
return;
|
|
1085
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1086
|
+
view.window = messages;
|
|
1087
|
+
view.more = more;
|
|
1088
|
+
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
846
1089
|
})
|
|
847
1090
|
.then(() => {
|
|
848
1091
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
849
1092
|
return;
|
|
850
1093
|
watcher.ready = true;
|
|
851
|
-
|
|
1094
|
+
const window = view.window ?? [];
|
|
1095
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
852
1096
|
})
|
|
853
1097
|
.catch(() => { });
|
|
854
1098
|
return () => {
|
|
@@ -859,25 +1103,35 @@ export function createConversationStore(deps) {
|
|
|
859
1103
|
};
|
|
860
1104
|
},
|
|
861
1105
|
watchList(deliver) {
|
|
862
|
-
|
|
1106
|
+
let ended = false;
|
|
863
1107
|
void agentsReady().then(() => {
|
|
864
|
-
if (
|
|
865
|
-
|
|
1108
|
+
if (ended)
|
|
1109
|
+
return;
|
|
1110
|
+
unlisted.add(deliver);
|
|
1111
|
+
learned();
|
|
866
1112
|
});
|
|
867
1113
|
return () => {
|
|
1114
|
+
ended = true;
|
|
1115
|
+
unlisted.delete(deliver);
|
|
868
1116
|
listWatchers.delete(deliver);
|
|
869
1117
|
};
|
|
870
1118
|
},
|
|
871
1119
|
watchAgents(deliver) {
|
|
872
|
-
|
|
873
|
-
void agentsReady().then(
|
|
874
|
-
if (!deps.closed() && agentWatchers.has(deliver))
|
|
875
|
-
deliver(agents());
|
|
876
|
-
});
|
|
1120
|
+
unnamed.add(deliver);
|
|
1121
|
+
void agentsReady().then(learned);
|
|
877
1122
|
return () => {
|
|
1123
|
+
unnamed.delete(deliver);
|
|
878
1124
|
agentWatchers.delete(deliver);
|
|
879
1125
|
};
|
|
880
1126
|
},
|
|
1127
|
+
learned,
|
|
1128
|
+
declared() {
|
|
1129
|
+
if (JSON.stringify(deps.agents()) === agentsFrom)
|
|
1130
|
+
return;
|
|
1131
|
+
agentsRead = readAgents().catch(() => {
|
|
1132
|
+
agentsRead = null;
|
|
1133
|
+
});
|
|
1134
|
+
},
|
|
881
1135
|
changed(set) {
|
|
882
1136
|
for (const conversation of set.conversations)
|
|
883
1137
|
emit(conversation);
|
|
@@ -900,6 +1154,15 @@ export function createConversationStore(deps) {
|
|
|
900
1154
|
creations.delete(entry.conversation);
|
|
901
1155
|
}
|
|
902
1156
|
listKnown = true;
|
|
1157
|
+
for (const conversation of replicated) {
|
|
1158
|
+
if (!listing.has(conversation))
|
|
1159
|
+
continue;
|
|
1160
|
+
if (!missed(conversation))
|
|
1161
|
+
behind.delete(conversation);
|
|
1162
|
+
else if (!behind.has(conversation))
|
|
1163
|
+
gap(conversation);
|
|
1164
|
+
}
|
|
1165
|
+
openingEnded();
|
|
903
1166
|
}
|
|
904
1167
|
else if (event.kind === 'listed') {
|
|
905
1168
|
listing.set(event.entry.conversation, { ...event.entry });
|
|
@@ -908,6 +1171,7 @@ export function createConversationStore(deps) {
|
|
|
908
1171
|
else {
|
|
909
1172
|
listing.delete(event.conversation);
|
|
910
1173
|
}
|
|
1174
|
+
keepList();
|
|
911
1175
|
emitList();
|
|
912
1176
|
return (event.kind !== 'list' &&
|
|
913
1177
|
listKnown &&
|
|
@@ -926,8 +1190,11 @@ export function createConversationStore(deps) {
|
|
|
926
1190
|
close() {
|
|
927
1191
|
views.clear();
|
|
928
1192
|
lives.clear();
|
|
1193
|
+
unsent.clear();
|
|
929
1194
|
listWatchers.clear();
|
|
1195
|
+
unlisted.clear();
|
|
930
1196
|
agentWatchers.clear();
|
|
1197
|
+
unnamed.clear();
|
|
931
1198
|
},
|
|
932
1199
|
};
|
|
933
1200
|
}
|