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