@agentshouse/mdruntime 0.1.0-cb0eb0d87ff49 → 0.1.0-cb1f56e16291b
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 +147 -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 +22 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +97 -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 +37 -12
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +3429 -1114
- 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 +32 -14
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +525 -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 +4 -1
- 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 +10 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +125 -19
- 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 +17 -10
- 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 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +207 -96
- 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 +9 -3
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +8 -11
- 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 +43 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +67 -7
- package/dist/reach.d.ts +19 -6
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +226 -79
- package/dist/react.d.ts +6 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/readiness.d.ts +50 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +27 -0
- 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 +36 -5
- 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 -186
- 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 +5 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -1
- package/dist/store.d.ts +104 -94
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +337 -637
- 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 +165 -32
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +56 -31
- package/package.json +18 -19
- package/AGENTS.md +0 -309
- 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 -60
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -26
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -62
- package/dist/chat/markdown.d.ts +0 -20
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -188
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -192
- package/dist/chat/messages.d.ts +0 -21
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -151
- package/dist/chat/name.d.ts +0 -6
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -11
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -17
- package/dist/chat/overlay.d.ts +0 -31
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -97
- package/dist/chat/parts.d.ts +0 -57
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -119
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -63
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -620
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -80
- package/dist/chat/questions.d.ts +0 -31
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/questions.js +0 -111
- package/dist/chat/read.d.ts +0 -5
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/read.js +0 -13
- package/dist/chat/ready.d.ts +0 -33
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -302
- package/dist/chat.d.ts +0 -14
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
- package/dist/read-side.d.ts +0 -25
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -639
package/dist/conversation.js
CHANGED
|
@@ -1,15 +1,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,12 +33,28 @@ 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';
|
|
47
|
+
const NEW_CHAT = 'new:';
|
|
48
|
+
export const HISTORY_CONVERSATIONS = 64;
|
|
49
|
+
export function newChat(agent) {
|
|
50
|
+
return `${NEW_CHAT}${agent}`;
|
|
51
|
+
}
|
|
52
|
+
function creationOf(input) {
|
|
53
|
+
if (typeof input !== 'object' || input === null)
|
|
54
|
+
return null;
|
|
55
|
+
const { agent_id: agent, operation_id: operation } = input;
|
|
56
|
+
return typeof agent === 'string' && typeof operation === 'string' ? { agent, operation } : null;
|
|
57
|
+
}
|
|
26
58
|
function startedTurn(turn, operation) {
|
|
27
59
|
if (operation.op === 'begin')
|
|
28
60
|
return { turn, started: operation.at, parts: [], context: null };
|
|
@@ -72,19 +104,25 @@ function appliedTurn(held, operation) {
|
|
|
72
104
|
parts: held.parts.map((kept, at) => (at === operation.index ? changed : kept)),
|
|
73
105
|
};
|
|
74
106
|
}
|
|
75
|
-
function ordered(left, right) {
|
|
76
|
-
if (left.position !== right.position)
|
|
77
|
-
return left.position < right.position ? -1 : 1;
|
|
78
|
-
if (left.identity !== right.identity)
|
|
79
|
-
return left.identity < right.identity ? -1 : 1;
|
|
80
|
-
return 0;
|
|
81
|
-
}
|
|
82
107
|
function windowOf(held, arriving, limit) {
|
|
83
108
|
const byIdentity = new Map(held.map((message) => [message.identity, message]));
|
|
84
109
|
for (const message of arriving)
|
|
85
110
|
byIdentity.set(message.identity, message);
|
|
86
|
-
const next = [...byIdentity.values()].sort(
|
|
87
|
-
|
|
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
|
+
};
|
|
88
126
|
}
|
|
89
127
|
function newestOf(arriving, from) {
|
|
90
128
|
return arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from);
|
|
@@ -146,40 +184,72 @@ function historyPages(result) {
|
|
|
146
184
|
}
|
|
147
185
|
return pages;
|
|
148
186
|
}
|
|
149
|
-
function
|
|
187
|
+
function answered(result, field) {
|
|
150
188
|
if (result.kind !== 'action')
|
|
151
189
|
return null;
|
|
152
190
|
const answer = result.result;
|
|
153
191
|
if (typeof answer !== 'object' || answer === null)
|
|
154
192
|
return null;
|
|
155
|
-
const
|
|
156
|
-
return typeof
|
|
193
|
+
const value = answer[field];
|
|
194
|
+
return typeof value === 'string' ? value : null;
|
|
195
|
+
}
|
|
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];
|
|
157
207
|
}
|
|
158
|
-
function
|
|
159
|
-
const { limit } = options;
|
|
160
|
-
if (limit
|
|
161
|
-
return undefined;
|
|
162
|
-
if (!Number.isInteger(limit) || limit < 0) {
|
|
208
|
+
function askedOf(options) {
|
|
209
|
+
const { limit, after, total } = options;
|
|
210
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit < 0)) {
|
|
163
211
|
throw runtimeError('invalid-limit', `conversation limit must be a non-negative integer, got ${String(limit)}`);
|
|
164
212
|
}
|
|
165
|
-
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;
|
|
166
222
|
}
|
|
167
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
|
+
};
|
|
168
230
|
function unwritten(draft) {
|
|
169
|
-
return draft.text === ''
|
|
231
|
+
return draft.text === '';
|
|
170
232
|
}
|
|
171
233
|
function pending(draft) {
|
|
172
|
-
return
|
|
234
|
+
return {
|
|
235
|
+
...(draft.submitted === undefined ? {} : { submitted: draft.submitted }),
|
|
236
|
+
...(draft.moves === undefined ? {} : { moves: draft.moves }),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function empty(draft) {
|
|
240
|
+
return unwritten(draft) && draft.submitted === undefined && draft.moves === undefined;
|
|
173
241
|
}
|
|
174
242
|
function withSubmitted(draft, submitted) {
|
|
175
243
|
const { submitted: _held, ...unsent } = draft;
|
|
176
244
|
return submitted.length === 0 ? unsent : { ...unsent, submitted };
|
|
177
245
|
}
|
|
246
|
+
function prepended(draft, said) {
|
|
247
|
+
return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
|
|
248
|
+
}
|
|
178
249
|
function returned(draft, submission, others) {
|
|
179
250
|
return withSubmitted({
|
|
180
251
|
conversation: draft.conversation,
|
|
181
|
-
|
|
182
|
-
files: [...submission.files, ...draft.files],
|
|
252
|
+
...prepended(draft, submission),
|
|
183
253
|
revision: chatDraftRevision(),
|
|
184
254
|
}, others);
|
|
185
255
|
}
|
|
@@ -190,40 +260,55 @@ export function createConversationStore(deps) {
|
|
|
190
260
|
const drafts = new Map();
|
|
191
261
|
const sending = new Map();
|
|
192
262
|
const behind = new Set();
|
|
263
|
+
const catching = new Map();
|
|
193
264
|
const gaps = new Map();
|
|
194
265
|
const lives = new Map();
|
|
266
|
+
const unsent = new Map();
|
|
195
267
|
const views = new Map();
|
|
196
268
|
const listing = new Map();
|
|
269
|
+
let relisting = null;
|
|
270
|
+
let listCursor = null;
|
|
271
|
+
const creations = new Set();
|
|
197
272
|
const listWatchers = new Set();
|
|
273
|
+
const unlisted = new Set();
|
|
198
274
|
let listKnown = false;
|
|
275
|
+
let listHeld = false;
|
|
276
|
+
let listKept = false;
|
|
199
277
|
const agentWatchers = new Set();
|
|
200
|
-
|
|
201
|
-
let
|
|
202
|
-
let
|
|
278
|
+
const unnamed = new Set();
|
|
279
|
+
let agentsShown = null;
|
|
280
|
+
let listedAgents = null;
|
|
281
|
+
let agentsReading = false;
|
|
203
282
|
let replicated = new Set();
|
|
204
283
|
let handedReplicated;
|
|
205
284
|
function liveOf(conversation) {
|
|
206
285
|
return lives.get(conversation) ?? QUIET;
|
|
207
286
|
}
|
|
208
|
-
function viewOf(conversation,
|
|
287
|
+
function viewOf(conversation, held, frame) {
|
|
209
288
|
const draft = drafts.get(conversation);
|
|
210
289
|
const facts = liveOf(conversation);
|
|
211
290
|
return {
|
|
212
291
|
conversation,
|
|
213
|
-
messages:
|
|
292
|
+
messages: held.map(({ identity, turn, text, message }) => ({
|
|
214
293
|
identity,
|
|
215
294
|
turn,
|
|
216
295
|
text,
|
|
217
296
|
message,
|
|
218
297
|
})),
|
|
298
|
+
...frame,
|
|
219
299
|
turn: structuredClone(facts.turn),
|
|
220
300
|
questions: structuredClone(facts.questions),
|
|
221
301
|
process: facts.process === null ? null : { ...facts.process },
|
|
302
|
+
processEnded: listing.get(conversation)?.processEnded ?? null,
|
|
222
303
|
commands: structuredClone(facts.commands),
|
|
223
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],
|
|
224
309
|
chatDraft: draft === undefined || unwritten(draft)
|
|
225
310
|
? null
|
|
226
|
-
: { text: draft.text,
|
|
311
|
+
: { text: draft.text, revision: draft.revision },
|
|
227
312
|
};
|
|
228
313
|
}
|
|
229
314
|
function emit(conversation) {
|
|
@@ -235,21 +320,36 @@ export function createConversationStore(deps) {
|
|
|
235
320
|
const ready = [...view.watchers].filter((watcher) => watcher.ready);
|
|
236
321
|
if (ready.length === 0)
|
|
237
322
|
continue;
|
|
238
|
-
const value = viewOf(conversation, view.window);
|
|
323
|
+
const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
|
|
239
324
|
for (const watcher of ready)
|
|
240
325
|
watcher.notify(value);
|
|
241
326
|
}
|
|
242
327
|
}
|
|
243
|
-
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;
|
|
244
333
|
for (const view of views.values()) {
|
|
245
334
|
if (view.conversation !== conversation || view.window === null)
|
|
246
335
|
continue;
|
|
247
|
-
|
|
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
|
+
}
|
|
248
351
|
}
|
|
249
352
|
}
|
|
250
|
-
function changesOf(conversation, arriving) {
|
|
251
|
-
return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
|
|
252
|
-
}
|
|
253
353
|
function stateOf(conversation) {
|
|
254
354
|
const facts = liveOf(conversation);
|
|
255
355
|
if (facts.questions.length > 0)
|
|
@@ -259,34 +359,113 @@ export function createConversationStore(deps) {
|
|
|
259
359
|
function entries() {
|
|
260
360
|
return [...listing.values()].map((entry) => ({
|
|
261
361
|
conversation: entry.conversation,
|
|
262
|
-
agent: { identity: entry.agent, name:
|
|
362
|
+
agent: { identity: entry.agent, name: entry.agentName },
|
|
263
363
|
name: entry.name,
|
|
264
364
|
created: entry.created,
|
|
265
365
|
finished: entry.finished,
|
|
266
366
|
outcome: entry.outcome,
|
|
367
|
+
waiting: entry.waiting,
|
|
267
368
|
state: stateOf(entry.conversation),
|
|
268
369
|
started: lives.get(entry.conversation)?.turn?.started ?? null,
|
|
269
370
|
}));
|
|
270
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
|
+
}
|
|
271
397
|
function emitList() {
|
|
272
|
-
if (deps.closed()
|
|
398
|
+
if (deps.closed())
|
|
273
399
|
return;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
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
|
+
}
|
|
277
429
|
}
|
|
278
430
|
function agents() {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
.
|
|
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 }))
|
|
282
440
|
.sort((left, right) => left.name === right.name
|
|
283
441
|
? left.identity.localeCompare(right.identity)
|
|
284
442
|
: left.name.localeCompare(right.name));
|
|
285
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
|
+
}
|
|
286
461
|
function emitAgents() {
|
|
287
462
|
if (deps.closed() || agentWatchers.size === 0)
|
|
288
463
|
return;
|
|
289
464
|
const value = agents();
|
|
465
|
+
const shown = JSON.stringify(value);
|
|
466
|
+
if (shown === agentsShown)
|
|
467
|
+
return;
|
|
468
|
+
agentsShown = shown;
|
|
290
469
|
for (const watcher of agentWatchers)
|
|
291
470
|
watcher(value);
|
|
292
471
|
}
|
|
@@ -302,47 +481,42 @@ export function createConversationStore(deps) {
|
|
|
302
481
|
if (stateOf(conversation) !== before || next?.turn?.started !== began)
|
|
303
482
|
emitList();
|
|
304
483
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if ('pending' in held)
|
|
311
|
-
return;
|
|
312
|
-
const { documents } = held;
|
|
313
|
-
const names = new Map();
|
|
314
|
-
const retired = new Set();
|
|
315
|
-
for (const document of documents) {
|
|
316
|
-
if (document.type !== declaration.type)
|
|
317
|
-
continue;
|
|
318
|
-
const identity = document.fields?.[declaration.identity];
|
|
319
|
-
const name = document.fields?.[declaration.name];
|
|
320
|
-
if (typeof identity !== 'string' || typeof name !== 'string')
|
|
321
|
-
continue;
|
|
322
|
-
names.set(identity, name);
|
|
323
|
-
const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
|
|
324
|
-
if (ended !== undefined && ended !== null)
|
|
325
|
-
retired.add(identity);
|
|
326
|
-
}
|
|
327
|
-
const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
|
|
328
|
-
if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
|
|
329
|
-
return;
|
|
330
|
-
agentNames = names;
|
|
331
|
-
retiredAgents = retired;
|
|
332
|
-
if (named)
|
|
333
|
-
emitList();
|
|
334
|
-
emitAgents();
|
|
335
|
-
}
|
|
336
|
-
function agentsReady() {
|
|
337
|
-
agentsRead ??= readAgents().catch(() => {
|
|
338
|
-
agentsRead = null;
|
|
339
|
-
});
|
|
340
|
-
return agentsRead;
|
|
484
|
+
function retiredChat(conversation) {
|
|
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);
|
|
341
489
|
}
|
|
342
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);
|
|
343
494
|
setLive(conversation, { ...liveOf(conversation), ...change });
|
|
344
495
|
emit(conversation);
|
|
345
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
|
+
}
|
|
346
520
|
function relayed(conversation, turn, operation) {
|
|
347
521
|
const held = liveOf(conversation).turn;
|
|
348
522
|
const next = held?.turn === turn ? appliedTurn(held, operation) : startedTurn(turn, operation);
|
|
@@ -369,12 +543,6 @@ export function createConversationStore(deps) {
|
|
|
369
543
|
}
|
|
370
544
|
async function remove(leaving) {
|
|
371
545
|
await deps.durably(async (local) => {
|
|
372
|
-
const removed = [];
|
|
373
|
-
for (const conversation of leaving) {
|
|
374
|
-
for (const identity of await local.messageIdentities(conversation)) {
|
|
375
|
-
removed.push({ conversation, identity, text: null });
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
546
|
await local.dropConversations(leaving);
|
|
379
547
|
const changed = [];
|
|
380
548
|
for (const conversation of leaving) {
|
|
@@ -386,13 +554,28 @@ export function createConversationStore(deps) {
|
|
|
386
554
|
partials.delete(conversation);
|
|
387
555
|
drafts.delete(conversation);
|
|
388
556
|
behind.delete(conversation);
|
|
557
|
+
unsent.delete(conversation);
|
|
389
558
|
setLive(conversation, null);
|
|
390
|
-
slide(conversation, null);
|
|
559
|
+
await slide(local, conversation, null);
|
|
391
560
|
}
|
|
392
561
|
if (changed.length > 0)
|
|
393
|
-
deps.publish(conversationChangeSet(
|
|
562
|
+
deps.publish(conversationChangeSet(changed));
|
|
394
563
|
});
|
|
395
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
|
+
}
|
|
396
579
|
function gap(conversation) {
|
|
397
580
|
gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
|
|
398
581
|
behind.add(conversation);
|
|
@@ -415,18 +598,19 @@ export function createConversationStore(deps) {
|
|
|
415
598
|
if (advances)
|
|
416
599
|
reads.set(conversation, message.position);
|
|
417
600
|
sealTurns(conversation, new Set([message.turn]));
|
|
418
|
-
slide(conversation, [message]);
|
|
419
|
-
deps.publish(conversationChangeSet(
|
|
601
|
+
await slide(local, conversation, [message]);
|
|
602
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
420
603
|
});
|
|
421
604
|
}
|
|
422
|
-
async function readHistories(conversations) {
|
|
605
|
+
async function readHistories(conversations, ended) {
|
|
423
606
|
const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
|
|
424
|
-
let
|
|
607
|
+
let waiting = conversations.map((conversation) => ({
|
|
425
608
|
conversation,
|
|
426
609
|
after: reads.get(conversation) ?? undefined,
|
|
427
610
|
}));
|
|
428
|
-
while (
|
|
429
|
-
const
|
|
611
|
+
while (waiting.length > 0) {
|
|
612
|
+
const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
|
|
613
|
+
const result = await deps.history({
|
|
430
614
|
conversations: asking.map(({ conversation, after }) => ({
|
|
431
615
|
conversation,
|
|
432
616
|
...(after === undefined ? {} : { after }),
|
|
@@ -445,10 +629,33 @@ export function createConversationStore(deps) {
|
|
|
445
629
|
following.push({ conversation: page.conversation, after: page.next });
|
|
446
630
|
}
|
|
447
631
|
}
|
|
448
|
-
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];
|
|
449
637
|
}
|
|
450
638
|
return null;
|
|
451
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
|
+
}
|
|
452
659
|
function keepPage(conversation, arriving, from, next, generation) {
|
|
453
660
|
return deps.durably(async (local) => {
|
|
454
661
|
if (!replicated.has(conversation))
|
|
@@ -467,9 +674,9 @@ export function createConversationStore(deps) {
|
|
|
467
674
|
sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
|
|
468
675
|
if (whole && (gaps.get(conversation) ?? 0) === generation)
|
|
469
676
|
behind.delete(conversation);
|
|
470
|
-
slide(conversation, arriving);
|
|
677
|
+
await slide(local, conversation, arriving);
|
|
471
678
|
if (arriving.length > 0 || sealing || whole) {
|
|
472
|
-
deps.publish(conversationChangeSet(
|
|
679
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
473
680
|
}
|
|
474
681
|
});
|
|
475
682
|
}
|
|
@@ -478,11 +685,8 @@ export function createConversationStore(deps) {
|
|
|
478
685
|
if (event.kind === 'gap') {
|
|
479
686
|
if (replicated.has(conversation))
|
|
480
687
|
gap(conversation);
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return;
|
|
484
|
-
setLive(conversation, { ...QUIET, turn: held.turn });
|
|
485
|
-
emit(conversation);
|
|
688
|
+
if (lives.has(conversation))
|
|
689
|
+
unsent.set(conversation, new Set(FACTS));
|
|
486
690
|
return;
|
|
487
691
|
}
|
|
488
692
|
if (event.kind === 'turn') {
|
|
@@ -505,23 +709,47 @@ export function createConversationStore(deps) {
|
|
|
505
709
|
live(conversation, { options: structuredClone(event.options) });
|
|
506
710
|
return;
|
|
507
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
|
+
}
|
|
508
728
|
const arriving = acceptedMessage(event.message);
|
|
509
729
|
if (arriving === null)
|
|
510
730
|
return;
|
|
731
|
+
noted(conversation, arriving);
|
|
511
732
|
if (replicated.has(conversation)) {
|
|
512
|
-
|
|
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
|
+
});
|
|
513
741
|
return;
|
|
514
742
|
}
|
|
515
743
|
if (sealTurns(conversation, new Set([arriving.turn])))
|
|
516
744
|
emit(conversation);
|
|
517
745
|
}
|
|
518
746
|
async function kept(local, conversation, following) {
|
|
519
|
-
if (following === null || (
|
|
747
|
+
if (following === null || empty(following)) {
|
|
520
748
|
await local.dropChatDraft(conversation);
|
|
521
749
|
drafts.delete(conversation);
|
|
522
750
|
}
|
|
523
751
|
else {
|
|
524
|
-
await local.
|
|
752
|
+
await local.keepChatDrafts([following]);
|
|
525
753
|
drafts.set(conversation, following);
|
|
526
754
|
}
|
|
527
755
|
}
|
|
@@ -534,13 +762,35 @@ export function createConversationStore(deps) {
|
|
|
534
762
|
await kept(local, conversation, withSubmitted(held, left));
|
|
535
763
|
return true;
|
|
536
764
|
}
|
|
765
|
+
function moved(agent, operation, to) {
|
|
766
|
+
const from = newChat(agent);
|
|
767
|
+
return deps.durably(async (local) => {
|
|
768
|
+
const held = drafts.get(from);
|
|
769
|
+
const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
|
|
770
|
+
if (earlier.includes(operation))
|
|
771
|
+
return;
|
|
772
|
+
const blank = {
|
|
773
|
+
conversation: from,
|
|
774
|
+
text: '',
|
|
775
|
+
revision: chatDraftRevision(),
|
|
776
|
+
};
|
|
777
|
+
const { moves: _answered, ...unsent } = held ?? blank;
|
|
778
|
+
const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
|
|
779
|
+
const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
|
|
780
|
+
await local.keepChatDrafts([left, ...taken]);
|
|
781
|
+
drafts.set(from, left);
|
|
782
|
+
for (const one of taken)
|
|
783
|
+
drafts.set(to, one);
|
|
784
|
+
deps.publish(conversationChangeSet([from, to]));
|
|
785
|
+
});
|
|
786
|
+
}
|
|
537
787
|
function withDraft(conversation, next) {
|
|
538
788
|
return deps.durably(async (local) => {
|
|
539
789
|
const following = await next(drafts.get(conversation), local);
|
|
540
790
|
if (following === undefined)
|
|
541
791
|
return;
|
|
542
792
|
await kept(local, conversation, following);
|
|
543
|
-
deps.publish(conversationChangeSet([
|
|
793
|
+
deps.publish(conversationChangeSet([conversation]));
|
|
544
794
|
});
|
|
545
795
|
}
|
|
546
796
|
async function submitted(conversation, operation, draft) {
|
|
@@ -552,17 +802,12 @@ export function createConversationStore(deps) {
|
|
|
552
802
|
if (submission !== undefined)
|
|
553
803
|
return undefined;
|
|
554
804
|
const said = draft ?? current;
|
|
555
|
-
submission = {
|
|
556
|
-
operation,
|
|
557
|
-
text: said?.text ?? '',
|
|
558
|
-
files: [...(said?.files ?? [])],
|
|
559
|
-
turn: null,
|
|
560
|
-
};
|
|
805
|
+
submission = { operation, text: said?.text ?? '', turn: null };
|
|
561
806
|
return {
|
|
562
807
|
conversation,
|
|
563
808
|
...(draft === undefined || current === undefined
|
|
564
|
-
? { text: '',
|
|
565
|
-
: { text: current.text,
|
|
809
|
+
? { text: '', revision: chatDraftRevision() }
|
|
810
|
+
: { text: current.text, revision: current.revision }),
|
|
566
811
|
submitted: [...(current?.submitted ?? []), submission],
|
|
567
812
|
};
|
|
568
813
|
});
|
|
@@ -577,22 +822,21 @@ export function createConversationStore(deps) {
|
|
|
577
822
|
});
|
|
578
823
|
}
|
|
579
824
|
async function dispatched(conversation, submission) {
|
|
580
|
-
const dispatch = {};
|
|
825
|
+
const dispatch = { answered: false };
|
|
581
826
|
sending.set(submission.operation, dispatch);
|
|
582
827
|
try {
|
|
583
|
-
const
|
|
584
|
-
const result = await invoke('conversation.send', conversation, {
|
|
585
|
-
text: submission.text,
|
|
586
|
-
files,
|
|
828
|
+
const result = await invoke('conversations/send', conversation, {
|
|
587
829
|
operation_id: submission.operation,
|
|
830
|
+
text: submission.text,
|
|
588
831
|
});
|
|
832
|
+
dispatch.answered = result.kind !== 'transport-uncertainty';
|
|
589
833
|
if (result.kind === 'revoked-authorization')
|
|
590
834
|
return result;
|
|
591
835
|
if (result.kind !== 'action' && result.retryable) {
|
|
592
836
|
deps.unanswered();
|
|
593
837
|
return result;
|
|
594
838
|
}
|
|
595
|
-
const turn =
|
|
839
|
+
const turn = answered(result, 'message_id');
|
|
596
840
|
await withDraft(conversation, async (current, local) => {
|
|
597
841
|
const held = current?.submitted?.find((one) => one.operation === submission.operation);
|
|
598
842
|
if (current === undefined || held === undefined || held.turn !== null)
|
|
@@ -612,24 +856,55 @@ export function createConversationStore(deps) {
|
|
|
612
856
|
sending.delete(submission.operation);
|
|
613
857
|
}
|
|
614
858
|
}
|
|
615
|
-
function invoke(name, conversation, extra = {}) {
|
|
616
|
-
return deps.call(name, { conversation, ...extra });
|
|
859
|
+
function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
|
|
860
|
+
return deps.call(name, { conversation_id: conversation, ...extra });
|
|
617
861
|
}
|
|
618
862
|
const surface = {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
+
});
|
|
622
873
|
},
|
|
623
874
|
async read(conversation, options = {}) {
|
|
624
|
-
const
|
|
625
|
-
|
|
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
|
+
});
|
|
626
883
|
},
|
|
627
|
-
create(input) {
|
|
628
|
-
|
|
884
|
+
async create(input) {
|
|
885
|
+
const result = await deps.call('conversations/create', input);
|
|
886
|
+
const conversation = answered(result, 'conversation_id');
|
|
887
|
+
if (conversation === null)
|
|
888
|
+
return result;
|
|
889
|
+
if (!listing.has(conversation))
|
|
890
|
+
creations.add(conversation);
|
|
891
|
+
const creation = creationOf(input);
|
|
892
|
+
if (creation !== null)
|
|
893
|
+
await moved(creation.agent, creation.operation, conversation);
|
|
894
|
+
return result;
|
|
629
895
|
},
|
|
630
896
|
async send(conversation, operation = changeBatchIdentity(), draft) {
|
|
897
|
+
if (deps.full())
|
|
898
|
+
return DURABLE_WRITE;
|
|
631
899
|
if (deps.online()) {
|
|
632
|
-
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;
|
|
633
908
|
if (!earlier || !deps.expired(operation))
|
|
634
909
|
return dispatched(conversation, submission);
|
|
635
910
|
await forgotten(conversation, submission);
|
|
@@ -638,33 +913,24 @@ export function createConversationStore(deps) {
|
|
|
638
913
|
if (draft !== undefined) {
|
|
639
914
|
await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
|
|
640
915
|
? undefined
|
|
641
|
-
: returned(current ?? { conversation, text: '',
|
|
916
|
+
: returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
|
|
642
917
|
}
|
|
643
918
|
return OFFLINE;
|
|
644
919
|
},
|
|
645
920
|
answer(conversation, answer) {
|
|
646
|
-
return invoke('
|
|
921
|
+
return invoke('conversations/interaction/answer', conversation, {
|
|
922
|
+
operation_id: changeBatchIdentity(),
|
|
923
|
+
...answer,
|
|
924
|
+
});
|
|
647
925
|
},
|
|
648
926
|
interrupt(conversation) {
|
|
649
|
-
return invoke('
|
|
927
|
+
return invoke('conversations/interrupt', conversation);
|
|
650
928
|
},
|
|
651
929
|
kill(conversation) {
|
|
652
|
-
return invoke('
|
|
653
|
-
},
|
|
654
|
-
async cancel(conversation, turn) {
|
|
655
|
-
const result = await invoke('conversation.cancel', conversation, { turn });
|
|
656
|
-
if (result.kind !== 'action')
|
|
657
|
-
return result;
|
|
658
|
-
await withDraft(conversation, (current) => {
|
|
659
|
-
const held = current?.submitted?.find((one) => one.turn === turn);
|
|
660
|
-
if (current === undefined || held === undefined)
|
|
661
|
-
return undefined;
|
|
662
|
-
return returned(current, held, current.submitted.filter((one) => one !== held));
|
|
663
|
-
});
|
|
664
|
-
return result;
|
|
930
|
+
return invoke('conversations/kill', conversation);
|
|
665
931
|
},
|
|
666
932
|
delete(conversation) {
|
|
667
|
-
return invoke('
|
|
933
|
+
return invoke('conversations/delete', conversation);
|
|
668
934
|
},
|
|
669
935
|
};
|
|
670
936
|
const chatDraft = {
|
|
@@ -672,27 +938,36 @@ export function createConversationStore(deps) {
|
|
|
672
938
|
return withDraft(conversation, (current) => ({
|
|
673
939
|
conversation,
|
|
674
940
|
text: draft.text,
|
|
675
|
-
files: [...draft.files],
|
|
676
941
|
revision,
|
|
677
|
-
...(current
|
|
942
|
+
...(current === undefined ? {} : pending(current)),
|
|
943
|
+
}));
|
|
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)),
|
|
678
951
|
}));
|
|
679
952
|
},
|
|
680
953
|
discard(conversation) {
|
|
681
954
|
return withDraft(conversation, (current) => current === undefined
|
|
682
955
|
? undefined
|
|
683
|
-
: {
|
|
684
|
-
conversation,
|
|
685
|
-
text: '',
|
|
686
|
-
files: [],
|
|
687
|
-
revision: chatDraftRevision(),
|
|
688
|
-
...pending(current),
|
|
689
|
-
});
|
|
956
|
+
: { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
|
|
690
957
|
},
|
|
691
958
|
};
|
|
692
959
|
return {
|
|
693
960
|
surface,
|
|
694
961
|
chatDraft,
|
|
695
|
-
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
|
+
}
|
|
696
971
|
stored.clear();
|
|
697
972
|
reads.clear();
|
|
698
973
|
partials.clear();
|
|
@@ -706,6 +981,8 @@ export function createConversationStore(deps) {
|
|
|
706
981
|
drafts.clear();
|
|
707
982
|
for (const record of kept)
|
|
708
983
|
drafts.set(record.conversation, record);
|
|
984
|
+
if (listHeld)
|
|
985
|
+
learned();
|
|
709
986
|
},
|
|
710
987
|
async reconcile() {
|
|
711
988
|
if (!listKnown)
|
|
@@ -713,9 +990,11 @@ export function createConversationStore(deps) {
|
|
|
713
990
|
const named = new Set(listing.keys());
|
|
714
991
|
const leaving = [
|
|
715
992
|
...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
|
|
716
|
-
].filter((conversation) => !named.has(conversation)
|
|
993
|
+
].filter((conversation) => !named.has(conversation) &&
|
|
994
|
+
!creations.has(conversation) &&
|
|
995
|
+
(!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
|
|
717
996
|
for (const conversation of named) {
|
|
718
|
-
if (!replicated.has(conversation))
|
|
997
|
+
if (!replicated.has(conversation) && missed(conversation))
|
|
719
998
|
behind.add(conversation);
|
|
720
999
|
}
|
|
721
1000
|
replicated = named;
|
|
@@ -724,12 +1003,25 @@ export function createConversationStore(deps) {
|
|
|
724
1003
|
await remove(leaving);
|
|
725
1004
|
},
|
|
726
1005
|
async catchUp(progress) {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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);
|
|
730
1016
|
if (failed?.kind === 'transport-uncertainty')
|
|
731
1017
|
progress.reachable = false;
|
|
732
|
-
}
|
|
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
|
+
]);
|
|
733
1025
|
if (behind.size > 0)
|
|
734
1026
|
progress.complete = false;
|
|
735
1027
|
},
|
|
@@ -761,37 +1053,51 @@ export function createConversationStore(deps) {
|
|
|
761
1053
|
current() {
|
|
762
1054
|
return behind.size === 0;
|
|
763
1055
|
},
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
gap(conversation);
|
|
1056
|
+
known() {
|
|
1057
|
+
return listKnown;
|
|
767
1058
|
},
|
|
768
1059
|
reopened() {
|
|
769
|
-
|
|
1060
|
+
relisting = null;
|
|
1061
|
+
for (const [operation, dispatch] of sending)
|
|
1062
|
+
if (!dispatch.answered)
|
|
1063
|
+
sending.delete(operation);
|
|
1064
|
+
},
|
|
1065
|
+
lost() {
|
|
1066
|
+
catching.clear();
|
|
770
1067
|
},
|
|
771
1068
|
watch(conversation, options, deliver) {
|
|
772
|
-
const
|
|
773
|
-
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]);
|
|
774
1071
|
const view = views.get(key) ?? {
|
|
775
1072
|
conversation,
|
|
776
|
-
|
|
1073
|
+
asked,
|
|
777
1074
|
watchers: new Set(),
|
|
778
1075
|
window: null,
|
|
1076
|
+
more: false,
|
|
1077
|
+
total: null,
|
|
779
1078
|
};
|
|
780
1079
|
views.set(key, view);
|
|
781
1080
|
const watcher = { notify: deliver, ready: false };
|
|
782
1081
|
view.watchers.add(watcher);
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
view.window
|
|
786
|
-
|
|
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)
|
|
787
1091
|
.then(() => {
|
|
788
1092
|
if (deps.closed() || !view.watchers.has(watcher))
|
|
789
1093
|
return;
|
|
790
1094
|
watcher.ready = true;
|
|
791
|
-
|
|
1095
|
+
const window = view.window ?? [];
|
|
1096
|
+
watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
|
|
792
1097
|
})
|
|
793
1098
|
.catch(() => { });
|
|
794
1099
|
return () => {
|
|
1100
|
+
reading.abort();
|
|
795
1101
|
if (!view.watchers.delete(watcher))
|
|
796
1102
|
return;
|
|
797
1103
|
if (view.watchers.size === 0)
|
|
@@ -799,72 +1105,88 @@ export function createConversationStore(deps) {
|
|
|
799
1105
|
};
|
|
800
1106
|
},
|
|
801
1107
|
watchList(deliver) {
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if (!deps.closed() && listWatchers.has(deliver))
|
|
805
|
-
deliver(entries());
|
|
806
|
-
});
|
|
1108
|
+
unlisted.add(deliver);
|
|
1109
|
+
learned();
|
|
807
1110
|
return () => {
|
|
1111
|
+
unlisted.delete(deliver);
|
|
808
1112
|
listWatchers.delete(deliver);
|
|
809
1113
|
};
|
|
810
1114
|
},
|
|
811
1115
|
watchAgents(deliver) {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
deliver(agents());
|
|
816
|
-
});
|
|
1116
|
+
unnamed.add(deliver);
|
|
1117
|
+
learned();
|
|
1118
|
+
readAgents();
|
|
817
1119
|
return () => {
|
|
1120
|
+
unnamed.delete(deliver);
|
|
818
1121
|
agentWatchers.delete(deliver);
|
|
819
1122
|
};
|
|
820
1123
|
},
|
|
1124
|
+
learned,
|
|
821
1125
|
changed(set) {
|
|
822
1126
|
for (const conversation of set.conversations)
|
|
823
1127
|
emit(conversation);
|
|
824
|
-
const declaration = deps.agents();
|
|
825
|
-
if (declaration !== undefined &&
|
|
826
|
-
set.documents.some((change) => change.locator === declaration.locator)) {
|
|
827
|
-
agentsRead = readAgents().catch(() => {
|
|
828
|
-
agentsRead = null;
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
1128
|
},
|
|
832
1129
|
receive(event) {
|
|
833
1130
|
if (deps.closed())
|
|
834
1131
|
return false;
|
|
835
1132
|
if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
|
|
836
1133
|
if (event.kind === 'list') {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
1134
|
+
if (event.whole)
|
|
1135
|
+
relisting ??= new Map();
|
|
1136
|
+
for (const entry of event.entries) {
|
|
1137
|
+
if (relisting === null)
|
|
1138
|
+
listed(entry);
|
|
1139
|
+
else
|
|
1140
|
+
relisting.set(entry.conversation, { ...entry });
|
|
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);
|
|
840
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();
|
|
841
1162
|
}
|
|
842
1163
|
else if (event.kind === 'listed') {
|
|
843
|
-
|
|
1164
|
+
listed(event.entry);
|
|
1165
|
+
relisting?.set(event.entry.conversation, { ...event.entry });
|
|
844
1166
|
}
|
|
845
1167
|
else {
|
|
846
1168
|
listing.delete(event.conversation);
|
|
1169
|
+
relisting?.delete(event.conversation);
|
|
847
1170
|
}
|
|
1171
|
+
keepList();
|
|
848
1172
|
emitList();
|
|
849
|
-
return (event.kind !== 'list' &&
|
|
1173
|
+
return ((event.kind !== 'list' &&
|
|
850
1174
|
listKnown &&
|
|
851
1175
|
(listing.size !== replicated.size ||
|
|
852
|
-
[...listing.keys()].some((conversation) => !replicated.has(conversation))))
|
|
1176
|
+
[...listing.keys()].some((conversation) => !replicated.has(conversation)))) ||
|
|
1177
|
+
(listKnown && [...drafts.keys()].some(retiredChat)));
|
|
853
1178
|
}
|
|
854
1179
|
heard(event);
|
|
855
1180
|
return false;
|
|
856
1181
|
},
|
|
857
|
-
searchable() {
|
|
858
|
-
return [...(listKnown ? listing.keys() : stored)].sort();
|
|
859
|
-
},
|
|
860
|
-
acquired(conversation) {
|
|
861
|
-
return reads.has(conversation) && !partials.has(conversation);
|
|
862
|
-
},
|
|
863
1182
|
close() {
|
|
864
1183
|
views.clear();
|
|
865
1184
|
lives.clear();
|
|
1185
|
+
unsent.clear();
|
|
866
1186
|
listWatchers.clear();
|
|
1187
|
+
unlisted.clear();
|
|
867
1188
|
agentWatchers.clear();
|
|
1189
|
+
unnamed.clear();
|
|
868
1190
|
},
|
|
869
1191
|
};
|
|
870
1192
|
}
|