@agentshouse/mdruntime 0.1.0-c1db74d14e5fa → 0.1.0-c1ff51b87bc3d
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 +91 -39
- 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 +64 -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 +15 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +96 -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 +765 -0
- package/dist/connection.d.ts +29 -13
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2967 -1070
- package/dist/conversation.d.ts +21 -5
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +434 -126
- 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 +368 -0
- package/dist/host.d.ts +10 -8
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +97 -18
- 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 +9 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +86 -32
- 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 +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 +39 -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 +34 -4
- 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 +97 -89
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +329 -641
- package/dist/stored.d.ts +135 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +574 -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 +167 -33
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +55 -32
- 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 -300
- 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 -60
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -26
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -62
- package/dist/chat/markdown.d.ts +0 -20
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -188
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -192
- package/dist/chat/messages.d.ts +0 -21
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -151
- package/dist/chat/name.d.ts +0 -6
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -11
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -17
- package/dist/chat/overlay.d.ts +0 -31
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -97
- package/dist/chat/parts.d.ts +0 -57
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -119
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -63
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -620
- 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 -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,10 +32,26 @@ 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';
|
|
42
|
+
const NEW_CHAT = 'new:';
|
|
43
|
+
export const HISTORY_CONVERSATIONS = 64;
|
|
44
|
+
export function newChat(agent) {
|
|
45
|
+
return `${NEW_CHAT}${agent}`;
|
|
46
|
+
}
|
|
47
|
+
function creationOf(input) {
|
|
48
|
+
if (typeof input !== 'object' || input === null)
|
|
49
|
+
return null;
|
|
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;
|
|
54
|
+
}
|
|
26
55
|
function startedTurn(turn, operation) {
|
|
27
56
|
if (operation.op === 'begin')
|
|
28
57
|
return { turn, started: operation.at, parts: [], context: null };
|
|
@@ -72,19 +101,25 @@ function appliedTurn(held, operation) {
|
|
|
72
101
|
parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
|
|
73
102
|
};
|
|
74
103
|
}
|
|
75
|
-
function ordered(left, right) {
|
|
76
|
-
if (left.position !== right.position)
|
|
77
|
-
return left.position < right.position ? -1 : 1;
|
|
78
|
-
if (left.identity !== right.identity)
|
|
79
|
-
return left.identity < right.identity ? -1 : 1;
|
|
80
|
-
return 0;
|
|
81
|
-
}
|
|
82
104
|
function windowOf(held, arriving, limit) {
|
|
83
105
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
84
106
|
for (const message of arriving)
|
|
85
107
|
byIdentity.set(message.identity, message);
|
|
86
|
-
const next = [...byIdentity.values()].sort(
|
|
87
|
-
|
|
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
|
+
};
|
|
88
123
|
}
|
|
89
124
|
function newestOf(arriving, from) {
|
|
90
125
|
return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
|
|
@@ -146,40 +181,64 @@ function historyPages(result) {
|
|
|
146
181
|
}
|
|
147
182
|
return pages;
|
|
148
183
|
}
|
|
149
|
-
function
|
|
184
|
+
function answered(result, field) {
|
|
150
185
|
if (result.kind !== 'action')
|
|
151
186
|
return null;
|
|
152
187
|
const answer = result.result;
|
|
153
188
|
if (typeof answer !== 'object' || answer === null)
|
|
154
189
|
return null;
|
|
155
|
-
const
|
|
156
|
-
return typeof
|
|
190
|
+
const value = answer[field];
|
|
191
|
+
return typeof value === 'string' ? value : null;
|
|
157
192
|
}
|
|
158
|
-
function
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
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)) {
|
|
163
208
|
throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
|
|
164
209
|
}
|
|
165
|
-
return limit;
|
|
210
|
+
return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
|
|
166
211
|
}
|
|
167
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
|
+
};
|
|
168
219
|
function unwritten(draft) {
|
|
169
|
-
return draft.text === ''
|
|
220
|
+
return draft.text === '';
|
|
170
221
|
}
|
|
171
222
|
function pending(draft) {
|
|
172
|
-
return
|
|
223
|
+
return {
|
|
224
|
+
...(draft.submitted === undefined ? {} : { submitted: draft.submitted }),
|
|
225
|
+
...(draft.moves === undefined ? {} : { moves: draft.moves }),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function empty(draft) {
|
|
229
|
+
return unwritten(draft) && draft.submitted === undefined && draft.moves === undefined;
|
|
173
230
|
}
|
|
174
231
|
function withSubmitted(draft, submitted) {
|
|
175
232
|
const { submitted: _held, ...unsent } = draft;
|
|
176
233
|
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
177
234
|
}
|
|
235
|
+
function prepended(draft, said) {
|
|
236
|
+
return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
|
|
237
|
+
}
|
|
178
238
|
function returned(draft, submission, others) {
|
|
179
239
|
return withSubmitted({
|
|
180
240
|
conversation: draft.conversation,
|
|
181
|
-
|
|
182
|
-
files: [...submission.files, ...draft.files],
|
|
241
|
+
...prepended(draft, submission),
|
|
183
242
|
revision: chatDraftRevision(),
|
|
184
243
|
}, others);
|
|
185
244
|
}
|
|
@@ -190,22 +249,31 @@ export function createConversationStore(deps) {
|
|
|
190
249
|
const drafts = new Map();
|
|
191
250
|
const sending = new Map();
|
|
192
251
|
const behind = new Set();
|
|
252
|
+
const catching = new Map();
|
|
193
253
|
const gaps = new Map();
|
|
194
254
|
const lives = new Map();
|
|
255
|
+
const unsent = new Map();
|
|
195
256
|
const views = new Map();
|
|
196
257
|
const listing = new Map();
|
|
258
|
+
const creations = new Set();
|
|
197
259
|
const listWatchers = new Set();
|
|
260
|
+
const unlisted = new Set();
|
|
198
261
|
let listKnown = false;
|
|
262
|
+
let listHeld = false;
|
|
263
|
+
let listKept = false;
|
|
199
264
|
const agentWatchers = new Set();
|
|
265
|
+
const unnamed = new Set();
|
|
266
|
+
let agentsKnown = false;
|
|
200
267
|
let agentNames = new Map();
|
|
201
268
|
let retiredAgents = new Set();
|
|
202
269
|
let agentsRead = null;
|
|
270
|
+
let agentsFrom;
|
|
203
271
|
let replicated = new Set();
|
|
204
272
|
let handedReplicated;
|
|
205
273
|
function liveOf(conversation) {
|
|
206
274
|
return lives.get(conversation) ?? QUIET;
|
|
207
275
|
}
|
|
208
|
-
function viewOf(conversation, held) {
|
|
276
|
+
function viewOf(conversation, held, frame) {
|
|
209
277
|
const draft = drafts.get(conversation);
|
|
210
278
|
const facts = liveOf(conversation);
|
|
211
279
|
return {
|
|
@@ -216,14 +284,18 @@ export function createConversationStore(deps) {
|
|
|
216
284
|
text,
|
|
217
285
|
message,
|
|
218
286
|
})),
|
|
287
|
+
...frame,
|
|
219
288
|
turn: structuredClone(facts.turn),
|
|
220
289
|
questions: structuredClone(facts.questions),
|
|
221
290
|
process: facts.process === null ? null : { ...facts.process },
|
|
222
291
|
commands: structuredClone(facts.commands),
|
|
223
292
|
options: structuredClone(facts.options),
|
|
293
|
+
starting: facts.starting,
|
|
294
|
+
link: facts.link === null ? null : { ...facts.link },
|
|
295
|
+
deliveryUnknown: [...facts.deliveryUnknown],
|
|
224
296
|
chatDraft: draft === undefined || unwritten(draft)
|
|
225
297
|
? null
|
|
226
|
-
: { text: draft.text,
|
|
298
|
+
: { text: draft.text, revision: draft.revision },
|
|
227
299
|
};
|
|
228
300
|
}
|
|
229
301
|
function emit(conversation) {
|
|
@@ -235,21 +307,36 @@ export function createConversationStore(deps) {
|
|
|
235
307
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
236
308
|
if (ready.length === 0)
|
|
237
309
|
continue;
|
|
238
|
-
const value = viewOf(conversation, view.window);
|
|
310
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
239
311
|
for (const watcher of ready)
|
|
240
312
|
watcher.notify(value);
|
|
241
313
|
}
|
|
242
314
|
}
|
|
243
|
-
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;
|
|
244
320
|
for (const view of views.values()) {
|
|
245
321
|
if (view.conversation !== conversation || view.window === null)
|
|
246
322
|
continue;
|
|
247
|
-
|
|
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
|
+
}
|
|
248
338
|
}
|
|
249
339
|
}
|
|
250
|
-
function changesOf(conversation, arriving) {
|
|
251
|
-
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
252
|
-
}
|
|
253
340
|
function stateOf(conversation) {
|
|
254
341
|
const facts = liveOf(conversation);
|
|
255
342
|
if (facts.questions.length > 0)
|
|
@@ -264,16 +351,58 @@ export function createConversationStore(deps) {
|
|
|
264
351
|
created: entry.created,
|
|
265
352
|
finished: entry.finished,
|
|
266
353
|
outcome: entry.outcome,
|
|
354
|
+
waiting: entry.waiting,
|
|
267
355
|
state: stateOf(entry.conversation),
|
|
268
356
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
269
357
|
}));
|
|
270
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
|
+
}
|
|
271
371
|
function emitList() {
|
|
272
|
-
if (deps.closed()
|
|
372
|
+
if (deps.closed())
|
|
273
373
|
return;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
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
|
+
}
|
|
277
406
|
}
|
|
278
407
|
function agents() {
|
|
279
408
|
return [...agentNames]
|
|
@@ -304,34 +433,52 @@ export function createConversationStore(deps) {
|
|
|
304
433
|
}
|
|
305
434
|
async function readAgents() {
|
|
306
435
|
const declaration = deps.agents();
|
|
436
|
+
agentsFrom = JSON.stringify(declaration);
|
|
307
437
|
if (declaration === undefined)
|
|
308
438
|
return;
|
|
309
|
-
const held = await deps.documents(declaration);
|
|
310
|
-
if ('pending' in held)
|
|
311
|
-
return;
|
|
312
|
-
const { documents } = held;
|
|
313
439
|
const names = new Map();
|
|
314
440
|
const retired = new Set();
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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;
|
|
326
463
|
}
|
|
464
|
+
agentsKnown = true;
|
|
327
465
|
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
328
|
-
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
466
|
+
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents])) {
|
|
467
|
+
learned();
|
|
329
468
|
return;
|
|
469
|
+
}
|
|
330
470
|
agentNames = names;
|
|
331
471
|
retiredAgents = retired;
|
|
472
|
+
emitAgents();
|
|
332
473
|
if (named)
|
|
333
474
|
emitList();
|
|
334
|
-
|
|
475
|
+
learned();
|
|
476
|
+
const ended = [...drafts.keys()].filter(retiredChat);
|
|
477
|
+
if (ended.length > 0)
|
|
478
|
+
await remove(ended);
|
|
479
|
+
}
|
|
480
|
+
function retiredChat(conversation) {
|
|
481
|
+
return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
|
|
335
482
|
}
|
|
336
483
|
function agentsReady() {
|
|
337
484
|
agentsRead ??= readAgents().catch(() => {
|
|
@@ -340,9 +487,34 @@ export function createConversationStore(deps) {
|
|
|
340
487
|
return agentsRead;
|
|
341
488
|
}
|
|
342
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);
|
|
343
493
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
344
494
|
emit(conversation);
|
|
345
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
|
+
}
|
|
346
518
|
function relayed(conversation, turn, operation) {
|
|
347
519
|
const held = liveOf(conversation).turn;
|
|
348
520
|
const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
|
|
@@ -369,12 +541,6 @@ export function createConversationStore(deps) {
|
|
|
369
541
|
}
|
|
370
542
|
async function remove(leaving) {
|
|
371
543
|
await deps.durably(async (local) => {
|
|
372
|
-
const removed = [];
|
|
373
|
-
for (const conversation of leaving) {
|
|
374
|
-
for (const identity of await local.messageIdentities(conversation)) {
|
|
375
|
-
removed.push({ conversation, identity, text: null });
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
544
|
await local.dropConversations(leaving);
|
|
379
545
|
const changed = [];
|
|
380
546
|
for (const conversation of leaving) {
|
|
@@ -386,11 +552,12 @@ export function createConversationStore(deps) {
|
|
|
386
552
|
partials.delete(conversation);
|
|
387
553
|
drafts.delete(conversation);
|
|
388
554
|
behind.delete(conversation);
|
|
555
|
+
unsent.delete(conversation);
|
|
389
556
|
setLive(conversation, null);
|
|
390
|
-
slide(conversation, null);
|
|
557
|
+
await slide(local, conversation, null);
|
|
391
558
|
}
|
|
392
559
|
if (changed.length > 0)
|
|
393
|
-
deps.publish(conversationChangeSet(
|
|
560
|
+
deps.publish(conversationChangeSet(changed));
|
|
394
561
|
});
|
|
395
562
|
}
|
|
396
563
|
function gap(conversation) {
|
|
@@ -415,17 +582,18 @@ export function createConversationStore(deps) {
|
|
|
415
582
|
if (advances)
|
|
416
583
|
reads.set(conversation, message.position);
|
|
417
584
|
sealTurns(conversation, new Set([message.turn]));
|
|
418
|
-
slide(conversation, [message]);
|
|
419
|
-
deps.publish(conversationChangeSet(
|
|
585
|
+
await slide(local, conversation, [message]);
|
|
586
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
420
587
|
});
|
|
421
588
|
}
|
|
422
|
-
async function readHistories(conversations) {
|
|
589
|
+
async function readHistories(conversations, ended) {
|
|
423
590
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
424
|
-
let
|
|
591
|
+
let waiting = conversations.map((conversation) => ({
|
|
425
592
|
conversation,
|
|
426
593
|
after: reads.get(conversation) ?? undefined,
|
|
427
594
|
}));
|
|
428
|
-
while (
|
|
595
|
+
while (waiting.length > 0) {
|
|
596
|
+
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
429
597
|
const result = await deps.call('conversation.history', {
|
|
430
598
|
conversations: asking.map(({ conversation, after }) => ({
|
|
431
599
|
conversation,
|
|
@@ -445,10 +613,33 @@ export function createConversationStore(deps) {
|
|
|
445
613
|
following.push({ conversation: page.conversation, after: page.next });
|
|
446
614
|
}
|
|
447
615
|
}
|
|
448
|
-
asking
|
|
616
|
+
for (const { conversation } of asking) {
|
|
617
|
+
if (!following.some((next) => next.conversation === conversation))
|
|
618
|
+
ended(conversation);
|
|
619
|
+
}
|
|
620
|
+
waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
|
|
449
621
|
}
|
|
450
622
|
return null;
|
|
451
623
|
}
|
|
624
|
+
function caughtUp(conversations) {
|
|
625
|
+
const ends = new Map();
|
|
626
|
+
for (const conversation of conversations) {
|
|
627
|
+
const held = new Promise((end) => ends.set(conversation, end));
|
|
628
|
+
catching.set(conversation, held);
|
|
629
|
+
void held.then(() => {
|
|
630
|
+
if (catching.get(conversation) === held)
|
|
631
|
+
catching.delete(conversation);
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
const ended = (conversation) => ends.get(conversation)?.();
|
|
635
|
+
const read = readHistories(conversations, ended);
|
|
636
|
+
const endAll = () => {
|
|
637
|
+
for (const conversation of conversations)
|
|
638
|
+
ended(conversation);
|
|
639
|
+
};
|
|
640
|
+
read.then(endAll, endAll);
|
|
641
|
+
return read;
|
|
642
|
+
}
|
|
452
643
|
function keepPage(conversation, arriving, from, next, generation) {
|
|
453
644
|
return deps.durably(async (local) => {
|
|
454
645
|
if (!replicated.has(conversation))
|
|
@@ -467,9 +658,9 @@ export function createConversationStore(deps) {
|
|
|
467
658
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
468
659
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
469
660
|
behind.delete(conversation);
|
|
470
|
-
slide(conversation, arriving);
|
|
661
|
+
await slide(local, conversation, arriving);
|
|
471
662
|
if (arriving.length > 0 || sealing || whole) {
|
|
472
|
-
deps.publish(conversationChangeSet(
|
|
663
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
473
664
|
}
|
|
474
665
|
});
|
|
475
666
|
}
|
|
@@ -478,11 +669,8 @@ export function createConversationStore(deps) {
|
|
|
478
669
|
if (event.kind === 'gap') {
|
|
479
670
|
if (replicated.has(conversation))
|
|
480
671
|
gap(conversation);
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return;
|
|
484
|
-
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
485
|
-
emit(conversation);
|
|
672
|
+
if (lives.has(conversation))
|
|
673
|
+
unsent.set(conversation, new Set(FACTS));
|
|
486
674
|
return;
|
|
487
675
|
}
|
|
488
676
|
if (event.kind === 'turn') {
|
|
@@ -505,23 +693,42 @@ export function createConversationStore(deps) {
|
|
|
505
693
|
live(conversation, { options: structuredClone(event.options) });
|
|
506
694
|
return;
|
|
507
695
|
}
|
|
696
|
+
if (event.kind === 'starting') {
|
|
697
|
+
live(conversation, { starting: event.step });
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (event.kind === 'link') {
|
|
701
|
+
live(conversation, { link: { connected: event.connected } });
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (event.kind === 'deliveries') {
|
|
705
|
+
live(conversation, { deliveryUnknown: [...event.unknown] });
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
508
708
|
const arriving = acceptedMessage(event.message);
|
|
509
709
|
if (arriving === null)
|
|
510
710
|
return;
|
|
511
711
|
if (replicated.has(conversation)) {
|
|
512
|
-
|
|
712
|
+
if (deps.full()) {
|
|
713
|
+
gap(conversation);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
void keep(conversation, arriving).catch((error) => {
|
|
717
|
+
if (refusedForSpace(error))
|
|
718
|
+
gap(conversation);
|
|
719
|
+
});
|
|
513
720
|
return;
|
|
514
721
|
}
|
|
515
722
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
516
723
|
emit(conversation);
|
|
517
724
|
}
|
|
518
725
|
async function kept(local, conversation, following) {
|
|
519
|
-
if (following === null || (
|
|
726
|
+
if (following === null || empty(following)) {
|
|
520
727
|
await local.dropChatDraft(conversation);
|
|
521
728
|
drafts.delete(conversation);
|
|
522
729
|
}
|
|
523
730
|
else {
|
|
524
|
-
await local.
|
|
731
|
+
await local.keepChatDrafts([following]);
|
|
525
732
|
drafts.set(conversation, following);
|
|
526
733
|
}
|
|
527
734
|
}
|
|
@@ -534,13 +741,35 @@ export function createConversationStore(deps) {
|
|
|
534
741
|
await kept(local, conversation, withSubmitted(held, left));
|
|
535
742
|
return true;
|
|
536
743
|
}
|
|
744
|
+
function moved(agent, operation, to) {
|
|
745
|
+
const from = newChat(agent);
|
|
746
|
+
return deps.durably(async (local) => {
|
|
747
|
+
const held = drafts.get(from);
|
|
748
|
+
const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
|
|
749
|
+
if (earlier.includes(operation))
|
|
750
|
+
return;
|
|
751
|
+
const blank = {
|
|
752
|
+
conversation: from,
|
|
753
|
+
text: '',
|
|
754
|
+
revision: chatDraftRevision(),
|
|
755
|
+
};
|
|
756
|
+
const { moves: _answered, ...unsent } = held ?? blank;
|
|
757
|
+
const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
|
|
758
|
+
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
759
|
+
await local.keepChatDrafts([left, ...taken]);
|
|
760
|
+
drafts.set(from, left);
|
|
761
|
+
for (const one of taken)
|
|
762
|
+
drafts.set(to, one);
|
|
763
|
+
deps.publish(conversationChangeSet([from, to]));
|
|
764
|
+
});
|
|
765
|
+
}
|
|
537
766
|
function withDraft(conversation, next) {
|
|
538
767
|
return deps.durably(async (local) => {
|
|
539
768
|
const following = await next(drafts.get(conversation), local);
|
|
540
769
|
if (following === undefined)
|
|
541
770
|
return;
|
|
542
771
|
await kept(local, conversation, following);
|
|
543
|
-
deps.publish(conversationChangeSet([
|
|
772
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
544
773
|
});
|
|
545
774
|
}
|
|
546
775
|
async function submitted(conversation, operation, draft) {
|
|
@@ -552,17 +781,12 @@ export function createConversationStore(deps) {
|
|
|
552
781
|
if (submission !== undefined)
|
|
553
782
|
return undefined;
|
|
554
783
|
const said = draft ?? current;
|
|
555
|
-
submission = {
|
|
556
|
-
operation,
|
|
557
|
-
text: said?.text ?? '',
|
|
558
|
-
files: [...(said?.files ?? [])],
|
|
559
|
-
turn: null,
|
|
560
|
-
};
|
|
784
|
+
submission = { operation, text: said?.text ?? '', turn: null };
|
|
561
785
|
return {
|
|
562
786
|
conversation,
|
|
563
787
|
...(draft === undefined || current === undefined
|
|
564
|
-
? { text: '',
|
|
565
|
-
: { text: current.text,
|
|
788
|
+
? { text: '', revision: chatDraftRevision() }
|
|
789
|
+
: { text: current.text, revision: current.revision }),
|
|
566
790
|
submitted: [...(current?.submitted ?? []), submission],
|
|
567
791
|
};
|
|
568
792
|
});
|
|
@@ -577,22 +801,21 @@ export function createConversationStore(deps) {
|
|
|
577
801
|
});
|
|
578
802
|
}
|
|
579
803
|
async function dispatched(conversation, submission) {
|
|
580
|
-
const dispatch = {};
|
|
804
|
+
const dispatch = { answered: false };
|
|
581
805
|
sending.set(submission.operation, dispatch);
|
|
582
806
|
try {
|
|
583
|
-
const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
|
|
584
807
|
const result = await invoke('conversation.send', conversation, {
|
|
585
808
|
text: submission.text,
|
|
586
|
-
files,
|
|
587
809
|
operation_id: submission.operation,
|
|
588
810
|
});
|
|
811
|
+
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
589
812
|
if (result.kind === 'revoked-authorization')
|
|
590
813
|
return result;
|
|
591
814
|
if (result.kind !== 'action' && result.retryable) {
|
|
592
815
|
deps.unanswered();
|
|
593
816
|
return result;
|
|
594
817
|
}
|
|
595
|
-
const turn =
|
|
818
|
+
const turn = answered(result, 'turn');
|
|
596
819
|
await withDraft(conversation, async (current, local) => {
|
|
597
820
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
598
821
|
if (current === undefined || held === undefined || held.turn !== null)
|
|
@@ -618,18 +841,48 @@ export function createConversationStore(deps) {
|
|
|
618
841
|
const surface = {
|
|
619
842
|
async list() {
|
|
620
843
|
await agentsReady();
|
|
621
|
-
return
|
|
844
|
+
return new Promise((settle) => {
|
|
845
|
+
const answer = (listed) => {
|
|
846
|
+
unlisted.delete(answer);
|
|
847
|
+
listWatchers.delete(answer);
|
|
848
|
+
settle(listed);
|
|
849
|
+
};
|
|
850
|
+
unlisted.add(answer);
|
|
851
|
+
learned();
|
|
852
|
+
});
|
|
622
853
|
},
|
|
623
854
|
async read(conversation, options = {}) {
|
|
624
|
-
const
|
|
625
|
-
return deps.durably(async (local) =>
|
|
855
|
+
const asked = askedOf(options);
|
|
856
|
+
return deps.durably(async (local) => {
|
|
857
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
858
|
+
const total = asked.total ? await local.messageCount(conversation) : null;
|
|
859
|
+
return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
|
|
860
|
+
});
|
|
626
861
|
},
|
|
627
|
-
create(input) {
|
|
628
|
-
|
|
862
|
+
async create(input) {
|
|
863
|
+
const result = await deps.call('conversation.create', input);
|
|
864
|
+
const conversation = answered(result, 'conversation');
|
|
865
|
+
if (conversation === null)
|
|
866
|
+
return result;
|
|
867
|
+
if (!listing.has(conversation))
|
|
868
|
+
creations.add(conversation);
|
|
869
|
+
const creation = creationOf(input);
|
|
870
|
+
if (creation !== null)
|
|
871
|
+
await moved(creation.agent, creation.operation, conversation);
|
|
872
|
+
return result;
|
|
629
873
|
},
|
|
630
874
|
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
875
|
+
if (deps.full())
|
|
876
|
+
return DURABLE_WRITE;
|
|
631
877
|
if (deps.online()) {
|
|
632
|
-
const
|
|
878
|
+
const held = await submitted(conversation, operation, draft).catch((error) => {
|
|
879
|
+
if (refusedForSpace(error))
|
|
880
|
+
return null;
|
|
881
|
+
throw error;
|
|
882
|
+
});
|
|
883
|
+
if (held === null)
|
|
884
|
+
return DURABLE_WRITE;
|
|
885
|
+
const { submission, earlier } = held;
|
|
633
886
|
if (!earlier || !deps.expired(operation))
|
|
634
887
|
return dispatched(conversation, submission);
|
|
635
888
|
await forgotten(conversation, submission);
|
|
@@ -638,7 +891,7 @@ export function createConversationStore(deps) {
|
|
|
638
891
|
if (draft !== undefined) {
|
|
639
892
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
640
893
|
? undefined
|
|
641
|
-
: returned(current ?? { conversation, text: '',
|
|
894
|
+
: returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
642
895
|
}
|
|
643
896
|
return OFFLINE;
|
|
644
897
|
},
|
|
@@ -672,27 +925,34 @@ export function createConversationStore(deps) {
|
|
|
672
925
|
return withDraft(conversation, (current) => ({
|
|
673
926
|
conversation,
|
|
674
927
|
text: draft.text,
|
|
675
|
-
files: [...draft.files],
|
|
676
928
|
revision,
|
|
677
|
-
...(current
|
|
929
|
+
...(current === undefined ? {} : pending(current)),
|
|
930
|
+
}));
|
|
931
|
+
},
|
|
932
|
+
prepend(conversation, draft) {
|
|
933
|
+
return withDraft(conversation, (current) => ({
|
|
934
|
+
conversation,
|
|
935
|
+
...(current === undefined ? { text: draft.text } : prepended(current, draft)),
|
|
936
|
+
revision: chatDraftRevision(),
|
|
937
|
+
...(current === undefined ? {} : pending(current)),
|
|
678
938
|
}));
|
|
679
939
|
},
|
|
680
940
|
discard(conversation) {
|
|
681
941
|
return withDraft(conversation, (current) => current === undefined
|
|
682
942
|
? undefined
|
|
683
|
-
: {
|
|
684
|
-
conversation,
|
|
685
|
-
text: '',
|
|
686
|
-
files: [],
|
|
687
|
-
revision: chatDraftRevision(),
|
|
688
|
-
...pending(current),
|
|
689
|
-
});
|
|
943
|
+
: { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
|
|
690
944
|
},
|
|
691
945
|
};
|
|
692
946
|
return {
|
|
693
947
|
surface,
|
|
694
948
|
chatDraft,
|
|
695
|
-
hydrate(held, kept) {
|
|
949
|
+
hydrate(held, kept, list) {
|
|
950
|
+
if (list !== null && !listKnown) {
|
|
951
|
+
listing.clear();
|
|
952
|
+
for (const entry of list)
|
|
953
|
+
listing.set(entry.conversation, { ...entry });
|
|
954
|
+
listHeld = true;
|
|
955
|
+
}
|
|
696
956
|
stored.clear();
|
|
697
957
|
reads.clear();
|
|
698
958
|
partials.clear();
|
|
@@ -706,6 +966,8 @@ export function createConversationStore(deps) {
|
|
|
706
966
|
drafts.clear();
|
|
707
967
|
for (const record of kept)
|
|
708
968
|
drafts.set(record.conversation, record);
|
|
969
|
+
if (listHeld)
|
|
970
|
+
learned();
|
|
709
971
|
},
|
|
710
972
|
async reconcile() {
|
|
711
973
|
if (!listKnown)
|
|
@@ -713,7 +975,9 @@ export function createConversationStore(deps) {
|
|
|
713
975
|
const named = new Set(listing.keys());
|
|
714
976
|
const leaving = [
|
|
715
977
|
...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
|
|
716
|
-
].filter((conversation) => !named.has(conversation)
|
|
978
|
+
].filter((conversation) => !named.has(conversation) &&
|
|
979
|
+
!creations.has(conversation) &&
|
|
980
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
717
981
|
for (const conversation of named) {
|
|
718
982
|
if (!replicated.has(conversation))
|
|
719
983
|
behind.add(conversation);
|
|
@@ -724,12 +988,25 @@ export function createConversationStore(deps) {
|
|
|
724
988
|
await remove(leaving);
|
|
725
989
|
},
|
|
726
990
|
async catchUp(progress) {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
991
|
+
if (deps.full()) {
|
|
992
|
+
if (behind.size > 0)
|
|
993
|
+
progress.complete = false;
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
|
|
997
|
+
const read = async (conversations) => {
|
|
998
|
+
if (conversations.length === 0 || deps.closed())
|
|
999
|
+
return;
|
|
1000
|
+
const failed = await caughtUp(conversations);
|
|
730
1001
|
if (failed?.kind === 'transport-uncertainty')
|
|
731
1002
|
progress.reachable = false;
|
|
732
|
-
}
|
|
1003
|
+
};
|
|
1004
|
+
const asking = [...behind].filter(owed).sort();
|
|
1005
|
+
const awaited = asking.filter((conversation) => catching.has(conversation));
|
|
1006
|
+
await Promise.all([
|
|
1007
|
+
read(asking.filter((conversation) => !catching.has(conversation))),
|
|
1008
|
+
Promise.all(awaited.map((conversation) => catching.get(conversation))).then(() => read(awaited.filter((conversation) => owed(conversation) && !catching.has(conversation)))),
|
|
1009
|
+
]);
|
|
733
1010
|
if (behind.size > 0)
|
|
734
1011
|
progress.complete = false;
|
|
735
1012
|
},
|
|
@@ -769,29 +1046,42 @@ export function createConversationStore(deps) {
|
|
|
769
1046
|
gap(conversation);
|
|
770
1047
|
},
|
|
771
1048
|
reopened() {
|
|
772
|
-
sending
|
|
1049
|
+
for (const [operation, dispatch] of sending)
|
|
1050
|
+
if (!dispatch.answered)
|
|
1051
|
+
sending.delete(operation);
|
|
1052
|
+
},
|
|
1053
|
+
lost() {
|
|
1054
|
+
catching.clear();
|
|
773
1055
|
},
|
|
774
1056
|
watch(conversation, options, deliver) {
|
|
775
|
-
const
|
|
776
|
-
const key = JSON.stringify([conversation, limit ?? null]);
|
|
1057
|
+
const asked = askedOf(options);
|
|
1058
|
+
const key = JSON.stringify([conversation, asked.limit, asked.after ?? null, asked.total]);
|
|
777
1059
|
const view = views.get(key) ?? {
|
|
778
1060
|
conversation,
|
|
779
|
-
|
|
1061
|
+
asked,
|
|
780
1062
|
watchers: new Set(),
|
|
781
1063
|
window: null,
|
|
1064
|
+
more: false,
|
|
1065
|
+
total: null,
|
|
782
1066
|
};
|
|
783
1067
|
views.set(key, view);
|
|
784
1068
|
const watcher = { notify: deliver, ready: false };
|
|
785
1069
|
view.watchers.add(watcher);
|
|
786
1070
|
void deps
|
|
787
1071
|
.durably(async (local) => {
|
|
788
|
-
view.window
|
|
1072
|
+
if (view.window !== null)
|
|
1073
|
+
return;
|
|
1074
|
+
const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
|
|
1075
|
+
view.window = messages;
|
|
1076
|
+
view.more = more;
|
|
1077
|
+
view.total = asked.total ? await local.messageCount(conversation) : null;
|
|
789
1078
|
})
|
|
790
1079
|
.then(() => {
|
|
791
1080
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
792
1081
|
return;
|
|
793
1082
|
watcher.ready = true;
|
|
794
|
-
|
|
1083
|
+
const window = view.window ?? [];
|
|
1084
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
795
1085
|
})
|
|
796
1086
|
.catch(() => { });
|
|
797
1087
|
return () => {
|
|
@@ -802,25 +1092,35 @@ export function createConversationStore(deps) {
|
|
|
802
1092
|
};
|
|
803
1093
|
},
|
|
804
1094
|
watchList(deliver) {
|
|
805
|
-
|
|
1095
|
+
let ended = false;
|
|
806
1096
|
void agentsReady().then(() => {
|
|
807
|
-
if (
|
|
808
|
-
|
|
1097
|
+
if (ended)
|
|
1098
|
+
return;
|
|
1099
|
+
unlisted.add(deliver);
|
|
1100
|
+
learned();
|
|
809
1101
|
});
|
|
810
1102
|
return () => {
|
|
1103
|
+
ended = true;
|
|
1104
|
+
unlisted.delete(deliver);
|
|
811
1105
|
listWatchers.delete(deliver);
|
|
812
1106
|
};
|
|
813
1107
|
},
|
|
814
1108
|
watchAgents(deliver) {
|
|
815
|
-
|
|
816
|
-
void agentsReady().then(
|
|
817
|
-
if (!deps.closed() && agentWatchers.has(deliver))
|
|
818
|
-
deliver(agents());
|
|
819
|
-
});
|
|
1109
|
+
unnamed.add(deliver);
|
|
1110
|
+
void agentsReady().then(learned);
|
|
820
1111
|
return () => {
|
|
1112
|
+
unnamed.delete(deliver);
|
|
821
1113
|
agentWatchers.delete(deliver);
|
|
822
1114
|
};
|
|
823
1115
|
},
|
|
1116
|
+
learned,
|
|
1117
|
+
declared() {
|
|
1118
|
+
if (JSON.stringify(deps.agents()) === agentsFrom)
|
|
1119
|
+
return;
|
|
1120
|
+
agentsRead = readAgents().catch(() => {
|
|
1121
|
+
agentsRead = null;
|
|
1122
|
+
});
|
|
1123
|
+
},
|
|
824
1124
|
changed(set) {
|
|
825
1125
|
for (const conversation of set.conversations)
|
|
826
1126
|
emit(conversation);
|
|
@@ -838,16 +1138,21 @@ export function createConversationStore(deps) {
|
|
|
838
1138
|
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
839
1139
|
if (event.kind === 'list') {
|
|
840
1140
|
listing.clear();
|
|
841
|
-
for (const entry of event.entries)
|
|
1141
|
+
for (const entry of event.entries) {
|
|
842
1142
|
listing.set(entry.conversation, { ...entry });
|
|
1143
|
+
creations.delete(entry.conversation);
|
|
1144
|
+
}
|
|
843
1145
|
listKnown = true;
|
|
1146
|
+
openingEnded();
|
|
844
1147
|
}
|
|
845
1148
|
else if (event.kind === 'listed') {
|
|
846
1149
|
listing.set(event.entry.conversation, { ...event.entry });
|
|
1150
|
+
creations.delete(event.entry.conversation);
|
|
847
1151
|
}
|
|
848
1152
|
else {
|
|
849
1153
|
listing.delete(event.conversation);
|
|
850
1154
|
}
|
|
1155
|
+
keepList();
|
|
851
1156
|
emitList();
|
|
852
1157
|
return (event.kind !== 'list' &&
|
|
853
1158
|
listKnown &&
|
|
@@ -866,8 +1171,11 @@ export function createConversationStore(deps) {
|
|
|
866
1171
|
close() {
|
|
867
1172
|
views.clear();
|
|
868
1173
|
lives.clear();
|
|
1174
|
+
unsent.clear();
|
|
869
1175
|
listWatchers.clear();
|
|
1176
|
+
unlisted.clear();
|
|
870
1177
|
agentWatchers.clear();
|
|
1178
|
+
unnamed.clear();
|
|
871
1179
|
},
|
|
872
1180
|
};
|
|
873
1181
|
}
|