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