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