@arbidocs/react 0.5.73 → 0.5.75
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 +85 -66
- package/dist/artifacts/index.cjs +9 -9
- package/dist/artifacts/index.js +3 -3
- package/dist/calls/livekit/index.cjs +48 -48
- package/dist/calls/livekit/index.js +3 -3
- package/dist/{chunk-M3SB5TZB.js → chunk-2MECTZLS.js} +9 -5
- package/dist/chunk-2MECTZLS.js.map +1 -0
- package/dist/{chunk-R7YNABZY.js → chunk-2Y6LJNWA.js} +2 -2
- package/dist/{chunk-R7YNABZY.js.map → chunk-2Y6LJNWA.js.map} +1 -1
- package/dist/{chunk-QGOYIYLI.js → chunk-33PVGKXT.js} +819 -509
- package/dist/chunk-33PVGKXT.js.map +1 -0
- package/dist/{chunk-Y7S2SQH3.cjs → chunk-5YGUBXXH.cjs} +2 -2
- package/dist/{chunk-Y7S2SQH3.cjs.map → chunk-5YGUBXXH.cjs.map} +1 -1
- package/dist/{chunk-RS6UUPD2.cjs → chunk-6RSVGLQY.cjs} +853 -536
- package/dist/chunk-6RSVGLQY.cjs.map +1 -0
- package/dist/{chunk-2LV53J46.js → chunk-JHN4PKWX.js} +3 -3
- package/dist/{chunk-2LV53J46.js.map → chunk-JHN4PKWX.js.map} +1 -1
- package/dist/{chunk-ZTPJNKF2.js → chunk-LC276BI7.js} +5 -5
- package/dist/chunk-LC276BI7.js.map +1 -0
- package/dist/{chunk-GOJHU7RZ.cjs → chunk-MWW54PY6.cjs} +25 -25
- package/dist/{chunk-GOJHU7RZ.cjs.map → chunk-MWW54PY6.cjs.map} +1 -1
- package/dist/{chunk-4DTTEUVY.js → chunk-O2HDONQE.js} +4 -4
- package/dist/{chunk-4DTTEUVY.js.map → chunk-O2HDONQE.js.map} +1 -1
- package/dist/{chunk-SI7DJWBT.js → chunk-OR6B7IL2.js} +3 -3
- package/dist/{chunk-SI7DJWBT.js.map → chunk-OR6B7IL2.js.map} +1 -1
- package/dist/{chunk-LMW46NBD.cjs → chunk-OY4PYCJH.cjs} +5 -5
- package/dist/chunk-OY4PYCJH.cjs.map +1 -0
- package/dist/{chunk-HXITD6DN.cjs → chunk-PMHFLOXY.cjs} +27 -22
- package/dist/chunk-PMHFLOXY.cjs.map +1 -0
- package/dist/{chunk-TGSEQGM3.cjs → chunk-QEVM3F5X.cjs} +4 -4
- package/dist/{chunk-TGSEQGM3.cjs.map → chunk-QEVM3F5X.cjs.map} +1 -1
- package/dist/{chunk-ZNQPUPX6.cjs → chunk-RDERZWMO.cjs} +16 -16
- package/dist/{chunk-ZNQPUPX6.cjs.map → chunk-RDERZWMO.cjs.map} +1 -1
- package/dist/document-tools/core.cjs +99 -99
- package/dist/document-tools/core.js +3 -3
- package/dist/document-tools/index.cjs +101 -101
- package/dist/document-tools/index.js +4 -4
- package/dist/index.cjs +1036 -891
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +411 -240
- package/dist/index.d.ts +411 -240
- package/dist/index.js +143 -32
- package/dist/index.js.map +1 -1
- package/dist/shell/index.cjs +32 -32
- package/dist/shell/index.js +1 -1
- package/dist/viewers/index.cjs +2 -2
- package/dist/viewers/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-HXITD6DN.cjs.map +0 -1
- package/dist/chunk-LMW46NBD.cjs.map +0 -1
- package/dist/chunk-M3SB5TZB.js.map +0 -1
- package/dist/chunk-QGOYIYLI.js.map +0 -1
- package/dist/chunk-RS6UUPD2.cjs.map +0 -1
- package/dist/chunk-ZTPJNKF2.js.map +0 -1
|
@@ -1,11 +1,98 @@
|
|
|
1
1
|
import { arbiQueryKeys } from './chunk-AOEJ744V.js';
|
|
2
|
-
import { useArbiHost, useArbi } from './chunk-
|
|
2
|
+
import { useArbiHost, useArbi } from './chunk-2Y6LJNWA.js';
|
|
3
3
|
import { OWN_SESSION_SLOT } from './chunk-DPYBZDJB.js';
|
|
4
4
|
import { parseVersionRef } from './chunk-NNWMHMEC.js';
|
|
5
5
|
import { getUserEncryptionKeypair, sealedBoxDecrypt, sealedBoxEncrypt } from '@arbidocs/client';
|
|
6
6
|
import { useSyncExternalStore, useCallback, useMemo, useRef, useEffect } from 'react';
|
|
7
7
|
import { useQuery, useMutation, useQueryClient, useInfiniteQuery, useQueries } from '@tanstack/react-query';
|
|
8
8
|
|
|
9
|
+
// src/chat/run-state.ts
|
|
10
|
+
var MIN_QUEUED_MESSAGE_VISIBILITY_MS = 800;
|
|
11
|
+
var mapQueues = (queues, update) => Object.fromEntries(Object.entries(queues).map(([id, messages]) => [id, update(messages)]));
|
|
12
|
+
function findConsumedMessages(messages, combinedContent) {
|
|
13
|
+
const text = combinedContent.trim();
|
|
14
|
+
const taken = /* @__PURE__ */ new Set();
|
|
15
|
+
let cursor = 0;
|
|
16
|
+
for (const message of messages) {
|
|
17
|
+
if (cursor >= text.length) break;
|
|
18
|
+
const content = message.content.trim();
|
|
19
|
+
if (!content) continue;
|
|
20
|
+
const separator = cursor === 0 ? "" : "\n\n";
|
|
21
|
+
if (text.startsWith(separator + content, cursor)) {
|
|
22
|
+
taken.add(message.clientId);
|
|
23
|
+
cursor += separator.length + content.length;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return cursor === text.length ? taken : /* @__PURE__ */ new Set();
|
|
27
|
+
}
|
|
28
|
+
var queuedMessages = {
|
|
29
|
+
add: (q, responseId, message) => ({
|
|
30
|
+
...q,
|
|
31
|
+
[responseId]: [...q[responseId] ?? [], message]
|
|
32
|
+
}),
|
|
33
|
+
acknowledge: (q, clientId, serverId) => mapQueues(
|
|
34
|
+
q,
|
|
35
|
+
(messages) => messages.map((m) => m.clientId === clientId ? { ...m, serverId, acknowledged: true } : m)
|
|
36
|
+
),
|
|
37
|
+
remove: (q, clientId) => mapQueues(q, (messages) => messages.filter((m) => m.clientId !== clientId)),
|
|
38
|
+
removeByServerId: (q, serverId) => mapQueues(q, (messages) => messages.filter((m) => m.serverId !== serverId)),
|
|
39
|
+
removeByContent: (q, responseId, content) => {
|
|
40
|
+
let removed = false;
|
|
41
|
+
return {
|
|
42
|
+
...q,
|
|
43
|
+
[responseId]: (q[responseId] ?? []).filter((m) => {
|
|
44
|
+
if (!removed && m.content === content) {
|
|
45
|
+
removed = true;
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
})
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
/** Take out the prompts a steering interjection delivered into the run. */
|
|
53
|
+
consume: (q, responseId, combinedContent) => {
|
|
54
|
+
const messages = q[responseId] ?? [];
|
|
55
|
+
const consumed = findConsumedMessages(messages, combinedContent);
|
|
56
|
+
return { ...q, [responseId]: messages.filter((m) => !consumed.has(m.clientId)) };
|
|
57
|
+
},
|
|
58
|
+
clear: (q, responseId) => ({ ...q, [responseId]: [] }),
|
|
59
|
+
/** Move the entries still waiting on a finished turn to the turn that is now running. */
|
|
60
|
+
move: (q, fromResponseId, toResponseId) => {
|
|
61
|
+
const waiting = q[fromResponseId] ?? [];
|
|
62
|
+
if (fromResponseId === toResponseId || waiting.length === 0) return q;
|
|
63
|
+
return { ...q, [fromResponseId]: [], [toResponseId]: [...q[toResponseId] ?? [], ...waiting] };
|
|
64
|
+
},
|
|
65
|
+
/** Find a queued follow-up by its client id, under whichever turn holds it. */
|
|
66
|
+
find: (q, clientId) => Object.values(q).flat().find((m) => m.clientId === clientId)
|
|
67
|
+
};
|
|
68
|
+
var without = (record, key) => {
|
|
69
|
+
const next = { ...record };
|
|
70
|
+
delete next[key];
|
|
71
|
+
return next;
|
|
72
|
+
};
|
|
73
|
+
var agentControl = {
|
|
74
|
+
setState: (c, id, state) => ({
|
|
75
|
+
...c,
|
|
76
|
+
agentControlStates: { ...c.agentControlStates, [id]: state }
|
|
77
|
+
}),
|
|
78
|
+
/** A probe answered: the latest answer, and nothing pending any more. */
|
|
79
|
+
setProbeAnswer: (c, id, probe) => ({
|
|
80
|
+
...c,
|
|
81
|
+
agentProbeAnswers: { ...c.agentProbeAnswers, [id]: probe },
|
|
82
|
+
agentProbePending: { ...c.agentProbePending, [id]: false }
|
|
83
|
+
}),
|
|
84
|
+
setProbePending: (c, id, pending) => ({
|
|
85
|
+
...c,
|
|
86
|
+
agentProbePending: { ...c.agentProbePending, [id]: pending }
|
|
87
|
+
}),
|
|
88
|
+
/** The turn is over: drop its control state. */
|
|
89
|
+
clear: (c, id) => ({
|
|
90
|
+
agentControlStates: without(c.agentControlStates, id),
|
|
91
|
+
agentProbeAnswers: without(c.agentProbeAnswers, id),
|
|
92
|
+
agentProbePending: without(c.agentProbePending, id)
|
|
93
|
+
})
|
|
94
|
+
};
|
|
95
|
+
|
|
9
96
|
// src/session/thread-tabs.ts
|
|
10
97
|
var threadTabId = (tab) => tab.conversationId ?? "new";
|
|
11
98
|
var MAX_THREAD_TABS = 12;
|
|
@@ -84,16 +171,6 @@ function renameThreadTabIn(tabs, fromId, toId) {
|
|
|
84
171
|
)
|
|
85
172
|
};
|
|
86
173
|
}
|
|
87
|
-
function withThreadDrafts(threads, drafts) {
|
|
88
|
-
let changed = false;
|
|
89
|
-
const next = threads.map((tab) => {
|
|
90
|
-
const draft = drafts[threadTabId(tab)];
|
|
91
|
-
if (!draft || !tab.view || tab.view.draft) return tab;
|
|
92
|
-
changed = true;
|
|
93
|
-
return { ...tab, view: { ...tab.view, draft } };
|
|
94
|
-
});
|
|
95
|
-
return changed ? next : threads;
|
|
96
|
-
}
|
|
97
174
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
98
175
|
var stringOrNull = (v) => typeof v === "string" ? v : null;
|
|
99
176
|
var stringOrUndefined = (v) => typeof v === "string" ? v : void 0;
|
|
@@ -101,7 +178,6 @@ function parseView(value) {
|
|
|
101
178
|
if (!isRecord(value)) return void 0;
|
|
102
179
|
return {
|
|
103
180
|
cursor: stringOrNull(value.cursor),
|
|
104
|
-
draft: "",
|
|
105
181
|
editingMessageId: stringOrNull(value.editingMessageId),
|
|
106
182
|
preEditDraft: null,
|
|
107
183
|
preEditCursor: stringOrNull(value.preEditCursor),
|
|
@@ -126,9 +202,509 @@ function parseSavedThreadTabs(threads, activeThread) {
|
|
|
126
202
|
activeThread: active && tabs.some((tab) => threadTabId(tab) === active) ? active : null
|
|
127
203
|
};
|
|
128
204
|
}
|
|
129
|
-
var savedThreadTabs = (threads) => threads.map(
|
|
130
|
-
|
|
131
|
-
|
|
205
|
+
var savedThreadTabs = (threads) => threads.map((tab) => tab.view ? { ...tab, view: { ...tab.view, preEditDraft: null } } : tab);
|
|
206
|
+
|
|
207
|
+
// src/session/chat.ts
|
|
208
|
+
var EMPTY_CHAT_STATE = {
|
|
209
|
+
conversationId: null,
|
|
210
|
+
generating: {},
|
|
211
|
+
paMode: false,
|
|
212
|
+
conversationDocuments: {},
|
|
213
|
+
conversationDocumentAutoAdd: {},
|
|
214
|
+
conversationFacts: {},
|
|
215
|
+
cursor: null,
|
|
216
|
+
suggestedQuery: null,
|
|
217
|
+
queuedMessages: {},
|
|
218
|
+
agentControlStates: {},
|
|
219
|
+
agentProbeAnswers: {},
|
|
220
|
+
agentProbePending: {},
|
|
221
|
+
drafts: {},
|
|
222
|
+
editingMessageId: null,
|
|
223
|
+
deepLinkMessageId: null,
|
|
224
|
+
activeCitationKey: null,
|
|
225
|
+
chatModel: void 0,
|
|
226
|
+
chatModelName: void 0,
|
|
227
|
+
newConversationAssistantMode: "always",
|
|
228
|
+
pendingTools: null,
|
|
229
|
+
autoSendDraft: false,
|
|
230
|
+
agentChatOwners: {},
|
|
231
|
+
newAgentChat: null,
|
|
232
|
+
newChatDocumentId: null,
|
|
233
|
+
messageTimers: {}
|
|
234
|
+
};
|
|
235
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
236
|
+
var isString = (v) => typeof v === "string";
|
|
237
|
+
var stringOrNull2 = (v) => isString(v) ? v : null;
|
|
238
|
+
var recordOf = (value, read) => {
|
|
239
|
+
if (!isRecord2(value)) return {};
|
|
240
|
+
const out = {};
|
|
241
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
242
|
+
const parsed = read(entry);
|
|
243
|
+
if (parsed !== null) out[key] = parsed;
|
|
244
|
+
}
|
|
245
|
+
return out;
|
|
246
|
+
};
|
|
247
|
+
var readDocuments = (value) => Array.isArray(value) ? value.filter((doc) => isRecord2(doc) && isString(doc.docId)).map((doc) => ({ docId: doc.docId, isPinned: doc.isPinned === true })) : null;
|
|
248
|
+
var readFacts = (value) => Array.isArray(value) ? Array.from(new Set(value.filter(isString))) : null;
|
|
249
|
+
function parseSavedChat(saved) {
|
|
250
|
+
if (!isRecord2(saved) || saved.version !== 1) return null;
|
|
251
|
+
return {
|
|
252
|
+
version: 1,
|
|
253
|
+
migrated: Array.isArray(saved.migrated) ? saved.migrated.filter(isString) : [],
|
|
254
|
+
conversationId: stringOrNull2(saved.conversationId),
|
|
255
|
+
cursor: stringOrNull2(saved.cursor),
|
|
256
|
+
conversationDocuments: recordOf(saved.conversationDocuments, readDocuments),
|
|
257
|
+
conversationDocumentAutoAdd: recordOf(
|
|
258
|
+
saved.conversationDocumentAutoAdd,
|
|
259
|
+
(v) => typeof v === "boolean" ? v : null
|
|
260
|
+
),
|
|
261
|
+
conversationFacts: recordOf(saved.conversationFacts, readFacts),
|
|
262
|
+
agentChatOwners: recordOf(saved.agentChatOwners, (v) => isString(v) ? v : null),
|
|
263
|
+
agentChatOwnersUser: stringOrNull2(saved.agentChatOwnersUser),
|
|
264
|
+
chatModel: stringOrNull2(saved.chatModel)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function savedChatOf(state, migrated, agentChatOwnersUser = null) {
|
|
268
|
+
return {
|
|
269
|
+
version: 1,
|
|
270
|
+
migrated,
|
|
271
|
+
conversationId: state.conversationId,
|
|
272
|
+
cursor: state.cursor,
|
|
273
|
+
conversationDocuments: state.conversationDocuments,
|
|
274
|
+
conversationDocumentAutoAdd: { ...state.conversationDocumentAutoAdd },
|
|
275
|
+
conversationFacts: state.conversationFacts,
|
|
276
|
+
agentChatOwners: { ...state.agentChatOwners },
|
|
277
|
+
agentChatOwnersUser,
|
|
278
|
+
chatModel: state.chatModel ?? null
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
var SAVED_FIELDS = [
|
|
282
|
+
"conversationId",
|
|
283
|
+
"cursor",
|
|
284
|
+
"conversationDocuments",
|
|
285
|
+
"conversationDocumentAutoAdd",
|
|
286
|
+
"conversationFacts",
|
|
287
|
+
"agentChatOwners",
|
|
288
|
+
"chatModel"
|
|
289
|
+
];
|
|
290
|
+
function withoutNew(record) {
|
|
291
|
+
const saved = { ...record };
|
|
292
|
+
delete saved.new;
|
|
293
|
+
return saved;
|
|
294
|
+
}
|
|
295
|
+
function moveKey(record, fromId, toId) {
|
|
296
|
+
if (!(fromId in record)) return record;
|
|
297
|
+
const next = { ...record, [toId]: record[fromId] };
|
|
298
|
+
delete next[fromId];
|
|
299
|
+
return next;
|
|
300
|
+
}
|
|
301
|
+
var NO_EDIT = { draft: "", cursor: null };
|
|
302
|
+
function createArbiChatStore({
|
|
303
|
+
layout,
|
|
304
|
+
persist,
|
|
305
|
+
draftStorage,
|
|
306
|
+
scrollTop = () => null,
|
|
307
|
+
user = () => draftStorage?.owner() ?? null,
|
|
308
|
+
initial
|
|
309
|
+
}) {
|
|
310
|
+
let state = { ...EMPTY_CHAT_STATE, ...initial };
|
|
311
|
+
let migrated = [];
|
|
312
|
+
let ownersUser = null;
|
|
313
|
+
let isHydrated = !persist;
|
|
314
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
315
|
+
const waiting = /* @__PURE__ */ new Set();
|
|
316
|
+
let depth = 0;
|
|
317
|
+
const settle = (listener) => depth > 0 ? waiting.add(listener) : listener();
|
|
318
|
+
const notify = () => listeners.forEach(settle);
|
|
319
|
+
const batch = (change) => {
|
|
320
|
+
depth += 1;
|
|
321
|
+
try {
|
|
322
|
+
return change();
|
|
323
|
+
} finally {
|
|
324
|
+
depth -= 1;
|
|
325
|
+
if (depth === 0) {
|
|
326
|
+
const told = [...waiting];
|
|
327
|
+
waiting.clear();
|
|
328
|
+
told.forEach((listener) => listener());
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const save = () => {
|
|
333
|
+
if (persist && isHydrated) persist.save(savedChatOf(state, migrated, ownersUser));
|
|
334
|
+
};
|
|
335
|
+
const update = (patch) => {
|
|
336
|
+
const next = { ...state, ...patch };
|
|
337
|
+
const saved = SAVED_FIELDS.some((key) => next[key] !== state[key]);
|
|
338
|
+
state = next;
|
|
339
|
+
notify();
|
|
340
|
+
if (saved) save();
|
|
341
|
+
};
|
|
342
|
+
let preEdit = NO_EDIT;
|
|
343
|
+
let draftsRestored = !draftStorage;
|
|
344
|
+
let restoring = false;
|
|
345
|
+
let draftsOwner = draftStorage?.owner() ?? null;
|
|
346
|
+
const setDrafts = (drafts) => {
|
|
347
|
+
if (drafts === state.drafts) return;
|
|
348
|
+
update({ drafts });
|
|
349
|
+
if (draftsRestored) draftStorage?.save(drafts);
|
|
350
|
+
};
|
|
351
|
+
const setDraftOf = (key, text) => {
|
|
352
|
+
if ((state.drafts[key] ?? "") === text) return;
|
|
353
|
+
const drafts = { ...state.drafts };
|
|
354
|
+
if (text) drafts[key] = text;
|
|
355
|
+
else delete drafts[key];
|
|
356
|
+
setDrafts(drafts);
|
|
357
|
+
};
|
|
358
|
+
const keptDrafts = (drafts) => {
|
|
359
|
+
const open = new Set(layout.getLayout().tabs.threads.map(threadTabId));
|
|
360
|
+
open.add(state.conversationId ?? "new");
|
|
361
|
+
const keys = Object.keys(drafts);
|
|
362
|
+
if (keys.every((key) => open.has(key))) return drafts;
|
|
363
|
+
return Object.fromEntries(keys.filter((key) => open.has(key)).map((key) => [key, drafts[key]]));
|
|
364
|
+
};
|
|
365
|
+
const restoreDrafts = () => {
|
|
366
|
+
if (!draftStorage) return;
|
|
367
|
+
const owner = draftStorage.owner();
|
|
368
|
+
if (owner !== draftsOwner) {
|
|
369
|
+
if (draftsOwner !== null) update({ drafts: {} });
|
|
370
|
+
draftsOwner = owner;
|
|
371
|
+
draftsRestored = false;
|
|
372
|
+
}
|
|
373
|
+
if (draftsRestored || restoring || owner === null) return;
|
|
374
|
+
restoring = true;
|
|
375
|
+
void Promise.all([layout.hydrated(), hydration]).then(() => draftStorage.load()).then(
|
|
376
|
+
(saved) => {
|
|
377
|
+
restoring = false;
|
|
378
|
+
if (!saved || draftStorage.owner() !== owner) return;
|
|
379
|
+
draftsRestored = true;
|
|
380
|
+
setDrafts(keptDrafts({ ...saved, ...state.drafts }));
|
|
381
|
+
draftStorage.save(state.drafts);
|
|
382
|
+
},
|
|
383
|
+
() => {
|
|
384
|
+
restoring = false;
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
};
|
|
388
|
+
draftStorage?.onClear(() => {
|
|
389
|
+
draftsRestored = false;
|
|
390
|
+
draftsOwner = null;
|
|
391
|
+
preEdit = NO_EDIT;
|
|
392
|
+
if (Object.keys(state.drafts).length > 0) update({ drafts: {} });
|
|
393
|
+
});
|
|
394
|
+
const composer = () => ({
|
|
395
|
+
conversationId: state.conversationId,
|
|
396
|
+
view: {
|
|
397
|
+
cursor: state.cursor,
|
|
398
|
+
editingMessageId: state.editingMessageId,
|
|
399
|
+
preEditDraft: state.editingMessageId ? preEdit.draft : null,
|
|
400
|
+
preEditCursor: state.editingMessageId ? preEdit.cursor : null,
|
|
401
|
+
chatModel: state.chatModel,
|
|
402
|
+
chatModelName: state.chatModelName,
|
|
403
|
+
scrollTop: scrollTop()
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
const show = (next) => {
|
|
407
|
+
if (next === null) return;
|
|
408
|
+
const target = next === "list" ? { conversationId: null, view: void 0 } : next;
|
|
409
|
+
const view = target.view;
|
|
410
|
+
preEdit = view ? { draft: view.preEditDraft ?? "", cursor: view.preEditCursor ?? null } : NO_EDIT;
|
|
411
|
+
update({
|
|
412
|
+
conversationId: target.conversationId,
|
|
413
|
+
cursor: view?.cursor ?? null,
|
|
414
|
+
editingMessageId: view?.editingMessageId ?? null,
|
|
415
|
+
chatModel: view?.chatModel,
|
|
416
|
+
chatModelName: view?.chatModelName
|
|
417
|
+
});
|
|
418
|
+
if (!view) setDraftOf(target.conversationId ?? "new", "");
|
|
419
|
+
};
|
|
420
|
+
const afterTabs = () => setDrafts(keptDrafts(state.drafts));
|
|
421
|
+
const openThreadTab = (conversationId) => batch(() => {
|
|
422
|
+
update(
|
|
423
|
+
conversationId === null ? { newAgentChat: null, newChatDocumentId: null } : { newAgentChat: null }
|
|
424
|
+
);
|
|
425
|
+
const { tabId, show: next } = layout.threads.open(conversationId, composer());
|
|
426
|
+
show(next);
|
|
427
|
+
layout.setPageTab("conversations", tabId);
|
|
428
|
+
afterTabs();
|
|
429
|
+
return tabId;
|
|
430
|
+
});
|
|
431
|
+
const hydration = persist ? persist.load().then(
|
|
432
|
+
(saved) => {
|
|
433
|
+
const parsed = parseSavedChat(saved);
|
|
434
|
+
if (parsed) {
|
|
435
|
+
migrated = parsed.migrated;
|
|
436
|
+
ownersUser ??= parsed.agentChatOwnersUser;
|
|
437
|
+
const conversationId = state.conversationId ?? parsed.conversationId;
|
|
438
|
+
const savedOpen = conversationId === parsed.conversationId;
|
|
439
|
+
state = {
|
|
440
|
+
...state,
|
|
441
|
+
conversationId,
|
|
442
|
+
cursor: state.cursor ?? (savedOpen ? parsed.cursor : null),
|
|
443
|
+
chatModel: state.chatModel ?? (savedOpen ? parsed.chatModel ?? void 0 : void 0),
|
|
444
|
+
conversationDocuments: {
|
|
445
|
+
...parsed.conversationDocuments,
|
|
446
|
+
...state.conversationDocuments
|
|
447
|
+
},
|
|
448
|
+
conversationDocumentAutoAdd: {
|
|
449
|
+
...parsed.conversationDocumentAutoAdd,
|
|
450
|
+
...state.conversationDocumentAutoAdd
|
|
451
|
+
},
|
|
452
|
+
conversationFacts: { ...parsed.conversationFacts, ...state.conversationFacts },
|
|
453
|
+
agentChatOwners: { ...parsed.agentChatOwners, ...state.agentChatOwners }
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
isHydrated = true;
|
|
457
|
+
notify();
|
|
458
|
+
},
|
|
459
|
+
() => {
|
|
460
|
+
isHydrated = true;
|
|
461
|
+
notify();
|
|
462
|
+
}
|
|
463
|
+
) : Promise.resolve();
|
|
464
|
+
void Promise.all([layout.hydrated(), hydration]).then(() => {
|
|
465
|
+
const { threads, activeThread } = layout.getLayout().tabs;
|
|
466
|
+
const tab = activeThread ? threads.find((t) => threadTabId(t) === activeThread) : null;
|
|
467
|
+
if (tab && state.conversationId !== tab.conversationId)
|
|
468
|
+
update({ conversationId: tab.conversationId });
|
|
469
|
+
});
|
|
470
|
+
const generationOf = (conversationId) => state.generating[conversationId];
|
|
471
|
+
const setGeneration = (conversationId, generation) => update({ generating: { ...state.generating, [conversationId]: generation } });
|
|
472
|
+
const stopAll = () => Object.values(state.generating).forEach((generation) => generation.abortController?.abort());
|
|
473
|
+
return {
|
|
474
|
+
getState: () => state,
|
|
475
|
+
subscribe: (listener) => {
|
|
476
|
+
listeners.add(listener);
|
|
477
|
+
return () => {
|
|
478
|
+
listeners.delete(listener);
|
|
479
|
+
waiting.delete(listener);
|
|
480
|
+
};
|
|
481
|
+
},
|
|
482
|
+
settled: (listener) => () => settle(listener),
|
|
483
|
+
forget: (listener) => void waiting.delete(listener),
|
|
484
|
+
batch,
|
|
485
|
+
hydrated: () => hydration,
|
|
486
|
+
isHydrated: () => isHydrated,
|
|
487
|
+
restoreDrafts,
|
|
488
|
+
leave: () => batch(() => {
|
|
489
|
+
stopAll();
|
|
490
|
+
preEdit = NO_EDIT;
|
|
491
|
+
update({
|
|
492
|
+
conversationId: null,
|
|
493
|
+
cursor: null,
|
|
494
|
+
editingMessageId: null,
|
|
495
|
+
chatModel: void 0,
|
|
496
|
+
chatModelName: void 0,
|
|
497
|
+
newConversationAssistantMode: "always",
|
|
498
|
+
paMode: false,
|
|
499
|
+
generating: {},
|
|
500
|
+
// Saved conversations keep their documents; the unsent new chat's belong to this workspace.
|
|
501
|
+
conversationDocuments: withoutNew(state.conversationDocuments),
|
|
502
|
+
conversationDocumentAutoAdd: withoutNew(state.conversationDocumentAutoAdd),
|
|
503
|
+
conversationFacts: {},
|
|
504
|
+
newAgentChat: null,
|
|
505
|
+
newChatDocumentId: null,
|
|
506
|
+
messageTimers: {}
|
|
507
|
+
});
|
|
508
|
+
layout.threads.clear();
|
|
509
|
+
setDrafts({});
|
|
510
|
+
}),
|
|
511
|
+
reset: () => batch(() => {
|
|
512
|
+
stopAll();
|
|
513
|
+
preEdit = NO_EDIT;
|
|
514
|
+
const signedIn = user();
|
|
515
|
+
const keepOwners = signedIn === null || ownersUser === null || signedIn === ownersUser;
|
|
516
|
+
if (signedIn !== null) ownersUser = signedIn;
|
|
517
|
+
update({ ...EMPTY_CHAT_STATE, agentChatOwners: keepOwners ? state.agentChatOwners : {} });
|
|
518
|
+
}),
|
|
519
|
+
setConversationId: (id) => void openThreadTab(id),
|
|
520
|
+
openThreadTab,
|
|
521
|
+
closeThreadTab: (tabId) => batch(() => {
|
|
522
|
+
show(layout.threads.close(tabId));
|
|
523
|
+
layout.setPageTab("conversations", layout.getLayout().tabs.activeThread ?? "history");
|
|
524
|
+
afterTabs();
|
|
525
|
+
}),
|
|
526
|
+
selectThreadTab: (tabId) => batch(() => {
|
|
527
|
+
if (tabId !== null) {
|
|
528
|
+
show(layout.threads.select(tabId, composer()));
|
|
529
|
+
layout.setPageTab("conversations", tabId);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
layout.threads.showList(composer());
|
|
533
|
+
preEdit = NO_EDIT;
|
|
534
|
+
update({ conversationId: null, cursor: null, editingMessageId: null });
|
|
535
|
+
layout.setPageTab("conversations", "history");
|
|
536
|
+
afterTabs();
|
|
537
|
+
}),
|
|
538
|
+
moveConversation: (fromId, toId) => batch(() => {
|
|
539
|
+
update({
|
|
540
|
+
generating: moveKey(state.generating, fromId, toId),
|
|
541
|
+
conversationDocuments: moveKey(state.conversationDocuments, fromId, toId),
|
|
542
|
+
conversationDocumentAutoAdd: moveKey(state.conversationDocumentAutoAdd, fromId, toId),
|
|
543
|
+
conversationFacts: moveKey(state.conversationFacts, fromId, toId),
|
|
544
|
+
conversationId: state.conversationId ?? toId
|
|
545
|
+
});
|
|
546
|
+
layout.threads.rename(fromId, toId);
|
|
547
|
+
setDrafts(moveKey(state.drafts, fromId, toId));
|
|
548
|
+
}),
|
|
549
|
+
setConversationGenerating: (conversationId, isGenerating, abortController) => {
|
|
550
|
+
const current = generationOf(conversationId);
|
|
551
|
+
setGeneration(conversationId, {
|
|
552
|
+
...current,
|
|
553
|
+
isGenerating,
|
|
554
|
+
error: isGenerating ? null : current?.error ?? null,
|
|
555
|
+
// Starting with an explicit null means generation this client does not own (an attach to a
|
|
556
|
+
// run started elsewhere): it must not inherit a finished local turn's controller, or the
|
|
557
|
+
// view takes it for a local stream and detaches, which makes it attachable again (an
|
|
558
|
+
// endless loop that froze the tab after Stop). Stopping keeps the controller: the finished
|
|
559
|
+
// turn's bookkeeping still matches on it.
|
|
560
|
+
abortController: isGenerating && abortController === null ? null : abortController ?? current?.abortController ?? null,
|
|
561
|
+
// The run's start is set when the server names its answer (setStreamingAssistantId).
|
|
562
|
+
startTime: null,
|
|
563
|
+
streamingAssistantId: null,
|
|
564
|
+
...isGenerating ? { lastFailedMessage: null, backendElapsed: null } : {}
|
|
565
|
+
});
|
|
566
|
+
},
|
|
567
|
+
setStreamingAssistantId: (conversationId, streamingAssistantId, startTime = Date.now()) => setGeneration(conversationId, {
|
|
568
|
+
...generationOf(conversationId) ?? { isGenerating: false, error: null },
|
|
569
|
+
streamingAssistantId,
|
|
570
|
+
startTime
|
|
571
|
+
}),
|
|
572
|
+
setBackendElapsed: (conversationId, backendElapsed) => setGeneration(conversationId, {
|
|
573
|
+
...generationOf(conversationId) ?? { isGenerating: false, error: null, startTime: null },
|
|
574
|
+
backendElapsed
|
|
575
|
+
}),
|
|
576
|
+
setConversationError: (conversationId, error, lastFailedMessage) => {
|
|
577
|
+
const current = generationOf(conversationId);
|
|
578
|
+
setGeneration(conversationId, {
|
|
579
|
+
...current,
|
|
580
|
+
isGenerating: false,
|
|
581
|
+
error,
|
|
582
|
+
startTime: current?.startTime ?? null,
|
|
583
|
+
lastFailedMessage: lastFailedMessage ?? current?.lastFailedMessage ?? null
|
|
584
|
+
});
|
|
585
|
+
},
|
|
586
|
+
clearConversationError: (conversationId) => setGeneration(conversationId, {
|
|
587
|
+
...generationOf(conversationId) ?? { isGenerating: false, startTime: null },
|
|
588
|
+
error: null,
|
|
589
|
+
lastFailedMessage: null
|
|
590
|
+
}),
|
|
591
|
+
abortConversationGeneration: (conversationId) => {
|
|
592
|
+
const current = generationOf(conversationId);
|
|
593
|
+
current?.abortController?.abort();
|
|
594
|
+
setGeneration(conversationId, {
|
|
595
|
+
...current ?? { error: null, startTime: null },
|
|
596
|
+
isGenerating: false,
|
|
597
|
+
abortController: null
|
|
598
|
+
});
|
|
599
|
+
},
|
|
600
|
+
setPaMode: (paMode) => {
|
|
601
|
+
if (state.paMode !== paMode) update({ paMode });
|
|
602
|
+
},
|
|
603
|
+
setConversationDocuments: (conversationId, documents, autoAdd = false) => update({
|
|
604
|
+
conversationDocuments: { ...state.conversationDocuments, [conversationId]: documents },
|
|
605
|
+
conversationDocumentAutoAdd: {
|
|
606
|
+
...state.conversationDocumentAutoAdd,
|
|
607
|
+
[conversationId]: autoAdd
|
|
608
|
+
}
|
|
609
|
+
}),
|
|
610
|
+
setConversationFacts: (conversationId, factIds) => update({
|
|
611
|
+
conversationFacts: {
|
|
612
|
+
...state.conversationFacts,
|
|
613
|
+
[conversationId]: Array.from(new Set(factIds))
|
|
614
|
+
}
|
|
615
|
+
}),
|
|
616
|
+
setCursor: (cursor) => {
|
|
617
|
+
if (state.cursor !== cursor) update({ cursor });
|
|
618
|
+
},
|
|
619
|
+
setSuggestedQuery: (suggestedQuery) => update({ suggestedQuery }),
|
|
620
|
+
addQueuedMessage: (id, message) => update({ queuedMessages: queuedMessages.add(state.queuedMessages, id, message) }),
|
|
621
|
+
acknowledgeQueuedMessage: (clientId, serverId) => update({
|
|
622
|
+
queuedMessages: queuedMessages.acknowledge(state.queuedMessages, clientId, serverId)
|
|
623
|
+
}),
|
|
624
|
+
removeQueuedMessage: (clientId) => update({ queuedMessages: queuedMessages.remove(state.queuedMessages, clientId) }),
|
|
625
|
+
removeQueuedMessageByServerId: (serverId) => update({ queuedMessages: queuedMessages.removeByServerId(state.queuedMessages, serverId) }),
|
|
626
|
+
consumeQueuedMessage: (id, content) => update({ queuedMessages: queuedMessages.consume(state.queuedMessages, id, content) }),
|
|
627
|
+
clearQueuedMessages: (id) => update({ queuedMessages: queuedMessages.clear(state.queuedMessages, id) }),
|
|
628
|
+
moveQueuedMessages: (from, to) => update({ queuedMessages: queuedMessages.move(state.queuedMessages, from, to) }),
|
|
629
|
+
setAgentControlState: (id, control) => update(agentControl.setState(state, id, control)),
|
|
630
|
+
setAgentProbeAnswer: (id, probe) => update(agentControl.setProbeAnswer(state, id, probe)),
|
|
631
|
+
setAgentProbePending: (id, pending) => update(agentControl.setProbePending(state, id, pending)),
|
|
632
|
+
clearAgentControl: (id) => update(agentControl.clear(state, id)),
|
|
633
|
+
setDraft: (text) => setDraftOf(state.conversationId ?? "new", text),
|
|
634
|
+
setChatModel: (chatModel) => {
|
|
635
|
+
if (state.chatModel !== chatModel) update({ chatModel });
|
|
636
|
+
},
|
|
637
|
+
setChatModelName: (chatModelName) => {
|
|
638
|
+
if (state.chatModelName !== chatModelName) update({ chatModelName });
|
|
639
|
+
},
|
|
640
|
+
setNewConversationAssistantMode: (newConversationAssistantMode) => {
|
|
641
|
+
if (state.newConversationAssistantMode !== newConversationAssistantMode)
|
|
642
|
+
update({ newConversationAssistantMode });
|
|
643
|
+
},
|
|
644
|
+
setNewAgentChat: (newAgentChat) => {
|
|
645
|
+
const current = state.newAgentChat;
|
|
646
|
+
if (current?.agentId !== newAgentChat?.agentId || current?.workspaceId !== newAgentChat?.workspaceId)
|
|
647
|
+
update({ newAgentChat });
|
|
648
|
+
},
|
|
649
|
+
setNewChatDocumentId: (newChatDocumentId) => {
|
|
650
|
+
if (state.newChatDocumentId !== newChatDocumentId) update({ newChatDocumentId });
|
|
651
|
+
},
|
|
652
|
+
startEditing: (messageId, content) => batch(() => {
|
|
653
|
+
const key = state.conversationId ?? "new";
|
|
654
|
+
preEdit = { draft: state.drafts[key] ?? "", cursor: state.cursor };
|
|
655
|
+
update({ editingMessageId: messageId });
|
|
656
|
+
setDraftOf(key, content);
|
|
657
|
+
}),
|
|
658
|
+
cancelEditing: () => {
|
|
659
|
+
if (!state.editingMessageId) return;
|
|
660
|
+
const restore = preEdit;
|
|
661
|
+
preEdit = NO_EDIT;
|
|
662
|
+
batch(() => {
|
|
663
|
+
update({ editingMessageId: null, cursor: restore.cursor });
|
|
664
|
+
setDraftOf(state.conversationId ?? "new", restore.draft);
|
|
665
|
+
});
|
|
666
|
+
},
|
|
667
|
+
completeEditing: () => {
|
|
668
|
+
preEdit = NO_EDIT;
|
|
669
|
+
if (state.editingMessageId) update({ editingMessageId: null });
|
|
670
|
+
},
|
|
671
|
+
setDeepLinkMessageId: (deepLinkMessageId) => {
|
|
672
|
+
if (state.deepLinkMessageId !== deepLinkMessageId) update({ deepLinkMessageId });
|
|
673
|
+
},
|
|
674
|
+
setActiveCitationKey: (activeCitationKey) => {
|
|
675
|
+
if (state.activeCitationKey !== activeCitationKey) update({ activeCitationKey });
|
|
676
|
+
},
|
|
677
|
+
setPendingTools: (pendingTools) => update({ pendingTools }),
|
|
678
|
+
setAutoSendDraft: (autoSendDraft) => {
|
|
679
|
+
if (state.autoSendDraft !== autoSendDraft) update({ autoSendDraft });
|
|
680
|
+
},
|
|
681
|
+
setAgentChatOwner: (conversationId, agentId) => {
|
|
682
|
+
const signedIn = user();
|
|
683
|
+
ownersUser = signedIn ?? ownersUser;
|
|
684
|
+
update({ agentChatOwners: { ...state.agentChatOwners, [conversationId]: agentId } });
|
|
685
|
+
},
|
|
686
|
+
setMessageTimer: (key, timer) => update({ messageTimers: { ...state.messageTimers, [key]: timer } }),
|
|
687
|
+
clearMessageTimer: (key) => {
|
|
688
|
+
if (!(key in state.messageTimers)) return;
|
|
689
|
+
const messageTimers = { ...state.messageTimers };
|
|
690
|
+
delete messageTimers[key];
|
|
691
|
+
update({ messageTimers });
|
|
692
|
+
},
|
|
693
|
+
startNewConversation: () => batch(() => {
|
|
694
|
+
preEdit = NO_EDIT;
|
|
695
|
+
update({
|
|
696
|
+
conversationId: null,
|
|
697
|
+
cursor: null,
|
|
698
|
+
editingMessageId: null,
|
|
699
|
+
chatModel: void 0,
|
|
700
|
+
chatModelName: void 0,
|
|
701
|
+
newConversationAssistantMode: "always",
|
|
702
|
+
paMode: false,
|
|
703
|
+
newChatDocumentId: null
|
|
704
|
+
});
|
|
705
|
+
})
|
|
706
|
+
};
|
|
707
|
+
}
|
|
132
708
|
|
|
133
709
|
// src/session/layout.ts
|
|
134
710
|
var PANEL_MODES = ["main", "docked", "overlay", "popout"];
|
|
@@ -155,47 +731,47 @@ function defaultLayoutState() {
|
|
|
155
731
|
grid: { states: {}, rowHeights: {}, labelWidths: {}, savedViews: {} }
|
|
156
732
|
};
|
|
157
733
|
}
|
|
158
|
-
var
|
|
159
|
-
var
|
|
160
|
-
var
|
|
734
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
735
|
+
var recordOf2 = (value, keep) => isRecord3(value) ? Object.fromEntries(Object.entries(value).filter(([, v]) => keep(v))) : {};
|
|
736
|
+
var isString2 = (v) => typeof v === "string";
|
|
161
737
|
var isNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
162
738
|
var isAny = (v) => v !== void 0;
|
|
163
739
|
var isMode = (v) => PANEL_MODES.includes(v);
|
|
164
|
-
var isSize = (v) =>
|
|
740
|
+
var isSize = (v) => isRecord3(v) && isNumber(v.width) && isNumber(v.height);
|
|
165
741
|
var isList = (v) => Array.isArray(v);
|
|
166
|
-
var
|
|
742
|
+
var stringOrNull3 = (v) => isString2(v) ? v : null;
|
|
167
743
|
function parseSavedLayout(saved) {
|
|
168
|
-
if (!
|
|
169
|
-
const shell =
|
|
170
|
-
const panels =
|
|
171
|
-
const tabs =
|
|
172
|
-
const views =
|
|
173
|
-
const grid =
|
|
744
|
+
if (!isRecord3(saved) || saved.version !== 1) return null;
|
|
745
|
+
const shell = isRecord3(saved.shell) ? saved.shell : {};
|
|
746
|
+
const panels = isRecord3(shell.panels) ? shell.panels : {};
|
|
747
|
+
const tabs = isRecord3(saved.tabs) ? saved.tabs : {};
|
|
748
|
+
const views = isRecord3(saved.views) ? saved.views : {};
|
|
749
|
+
const grid = isRecord3(saved.grid) ? saved.grid : {};
|
|
174
750
|
const defaults = defaultLayoutState();
|
|
175
751
|
return {
|
|
176
752
|
version: 1,
|
|
177
|
-
migrated: isList(saved.migrated) ? saved.migrated.filter(
|
|
753
|
+
migrated: isList(saved.migrated) ? saved.migrated.filter(isString2) : [],
|
|
178
754
|
shell: {
|
|
179
|
-
page:
|
|
755
|
+
page: isString2(shell.page) ? shell.page : defaults.shell.page,
|
|
180
756
|
sidebarPinned: typeof shell.sidebarPinned === "boolean" ? shell.sidebarPinned : defaults.shell.sidebar.pinned,
|
|
181
757
|
panels: {
|
|
182
|
-
main:
|
|
183
|
-
docked:
|
|
184
|
-
modes:
|
|
185
|
-
sizes:
|
|
758
|
+
main: stringOrNull3(panels.main),
|
|
759
|
+
docked: stringOrNull3(panels.docked),
|
|
760
|
+
modes: recordOf2(panels.modes, isMode),
|
|
761
|
+
sizes: recordOf2(panels.sizes, isSize)
|
|
186
762
|
}
|
|
187
763
|
},
|
|
188
764
|
tabs: parseSavedThreadTabs(tabs.threads, tabs.activeThread),
|
|
189
765
|
views: {
|
|
190
|
-
tabs:
|
|
191
|
-
filters:
|
|
192
|
-
values:
|
|
766
|
+
tabs: recordOf2(views.tabs, isString2),
|
|
767
|
+
filters: recordOf2(views.filters, isAny),
|
|
768
|
+
values: recordOf2(views.values, isAny)
|
|
193
769
|
},
|
|
194
770
|
grid: {
|
|
195
|
-
states:
|
|
196
|
-
rowHeights:
|
|
197
|
-
labelWidths:
|
|
198
|
-
savedViews:
|
|
771
|
+
states: recordOf2(grid.states, isRecord3),
|
|
772
|
+
rowHeights: recordOf2(grid.rowHeights, isNumber),
|
|
773
|
+
labelWidths: recordOf2(grid.labelWidths, isNumber),
|
|
774
|
+
savedViews: recordOf2(grid.savedViews, isList)
|
|
199
775
|
}
|
|
200
776
|
};
|
|
201
777
|
}
|
|
@@ -334,10 +910,6 @@ function createArbiLayoutStore({
|
|
|
334
910
|
if (layout.tabs.threads.length > 0 || layout.tabs.activeThread !== null)
|
|
335
911
|
tabs({ threads: [], activeThread: null });
|
|
336
912
|
},
|
|
337
|
-
restoreDrafts: (drafts) => {
|
|
338
|
-
const threads = withThreadDrafts(layout.tabs.threads, drafts);
|
|
339
|
-
if (threads !== layout.tabs.threads) tabs({ threads });
|
|
340
|
-
},
|
|
341
913
|
consumeScrollRestore: () => {
|
|
342
914
|
const value = layout.tabs.threadScrollRestore;
|
|
343
915
|
if (value !== null) tabs({ threadScrollRestore: null });
|
|
@@ -522,93 +1094,6 @@ function readWorkspaceLastOpened(lastOpenedKey = WORKSPACE_LAST_OPENED_KEY) {
|
|
|
522
1094
|
}
|
|
523
1095
|
}
|
|
524
1096
|
|
|
525
|
-
// src/chat/run-state.ts
|
|
526
|
-
var MIN_QUEUED_MESSAGE_VISIBILITY_MS = 800;
|
|
527
|
-
var mapQueues = (queues, update) => Object.fromEntries(Object.entries(queues).map(([id, messages]) => [id, update(messages)]));
|
|
528
|
-
function findConsumedMessages(messages, combinedContent) {
|
|
529
|
-
const text = combinedContent.trim();
|
|
530
|
-
const taken = /* @__PURE__ */ new Set();
|
|
531
|
-
let cursor = 0;
|
|
532
|
-
for (const message of messages) {
|
|
533
|
-
if (cursor >= text.length) break;
|
|
534
|
-
const content = message.content.trim();
|
|
535
|
-
if (!content) continue;
|
|
536
|
-
const separator = cursor === 0 ? "" : "\n\n";
|
|
537
|
-
if (text.startsWith(separator + content, cursor)) {
|
|
538
|
-
taken.add(message.clientId);
|
|
539
|
-
cursor += separator.length + content.length;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
return cursor === text.length ? taken : /* @__PURE__ */ new Set();
|
|
543
|
-
}
|
|
544
|
-
var queuedMessages = {
|
|
545
|
-
add: (q, responseId, message) => ({
|
|
546
|
-
...q,
|
|
547
|
-
[responseId]: [...q[responseId] ?? [], message]
|
|
548
|
-
}),
|
|
549
|
-
acknowledge: (q, clientId, serverId) => mapQueues(
|
|
550
|
-
q,
|
|
551
|
-
(messages) => messages.map((m) => m.clientId === clientId ? { ...m, serverId, acknowledged: true } : m)
|
|
552
|
-
),
|
|
553
|
-
remove: (q, clientId) => mapQueues(q, (messages) => messages.filter((m) => m.clientId !== clientId)),
|
|
554
|
-
removeByServerId: (q, serverId) => mapQueues(q, (messages) => messages.filter((m) => m.serverId !== serverId)),
|
|
555
|
-
removeByContent: (q, responseId, content) => {
|
|
556
|
-
let removed = false;
|
|
557
|
-
return {
|
|
558
|
-
...q,
|
|
559
|
-
[responseId]: (q[responseId] ?? []).filter((m) => {
|
|
560
|
-
if (!removed && m.content === content) {
|
|
561
|
-
removed = true;
|
|
562
|
-
return false;
|
|
563
|
-
}
|
|
564
|
-
return true;
|
|
565
|
-
})
|
|
566
|
-
};
|
|
567
|
-
},
|
|
568
|
-
/** Take out the prompts a steering interjection delivered into the run. */
|
|
569
|
-
consume: (q, responseId, combinedContent) => {
|
|
570
|
-
const messages = q[responseId] ?? [];
|
|
571
|
-
const consumed = findConsumedMessages(messages, combinedContent);
|
|
572
|
-
return { ...q, [responseId]: messages.filter((m) => !consumed.has(m.clientId)) };
|
|
573
|
-
},
|
|
574
|
-
clear: (q, responseId) => ({ ...q, [responseId]: [] }),
|
|
575
|
-
/** Move the entries still waiting on a finished turn to the turn that is now running. */
|
|
576
|
-
move: (q, fromResponseId, toResponseId) => {
|
|
577
|
-
const waiting = q[fromResponseId] ?? [];
|
|
578
|
-
if (fromResponseId === toResponseId || waiting.length === 0) return q;
|
|
579
|
-
return { ...q, [fromResponseId]: [], [toResponseId]: [...q[toResponseId] ?? [], ...waiting] };
|
|
580
|
-
},
|
|
581
|
-
/** Find a queued follow-up by its client id, under whichever turn holds it. */
|
|
582
|
-
find: (q, clientId) => Object.values(q).flat().find((m) => m.clientId === clientId)
|
|
583
|
-
};
|
|
584
|
-
var without = (record, key) => {
|
|
585
|
-
const next = { ...record };
|
|
586
|
-
delete next[key];
|
|
587
|
-
return next;
|
|
588
|
-
};
|
|
589
|
-
var agentControl = {
|
|
590
|
-
setState: (c, id, state) => ({
|
|
591
|
-
...c,
|
|
592
|
-
agentControlStates: { ...c.agentControlStates, [id]: state }
|
|
593
|
-
}),
|
|
594
|
-
/** A probe answered: the latest answer, and nothing pending any more. */
|
|
595
|
-
setProbeAnswer: (c, id, probe) => ({
|
|
596
|
-
...c,
|
|
597
|
-
agentProbeAnswers: { ...c.agentProbeAnswers, [id]: probe },
|
|
598
|
-
agentProbePending: { ...c.agentProbePending, [id]: false }
|
|
599
|
-
}),
|
|
600
|
-
setProbePending: (c, id, pending) => ({
|
|
601
|
-
...c,
|
|
602
|
-
agentProbePending: { ...c.agentProbePending, [id]: pending }
|
|
603
|
-
}),
|
|
604
|
-
/** The turn is over: drop its control state. */
|
|
605
|
-
clear: (c, id) => ({
|
|
606
|
-
agentControlStates: without(c.agentControlStates, id),
|
|
607
|
-
agentProbeAnswers: without(c.agentProbeAnswers, id),
|
|
608
|
-
agentProbePending: without(c.agentProbePending, id)
|
|
609
|
-
})
|
|
610
|
-
};
|
|
611
|
-
|
|
612
1097
|
// src/session/store.ts
|
|
613
1098
|
var DEFAULT_DOCUMENT_VIEWER_STATE = {
|
|
614
1099
|
tab: "pdf",
|
|
@@ -617,202 +1102,45 @@ var DEFAULT_DOCUMENT_VIEWER_STATE = {
|
|
|
617
1102
|
textScale: 1,
|
|
618
1103
|
phonePdfChosen: false
|
|
619
1104
|
};
|
|
620
|
-
var
|
|
621
|
-
workspaceId: null,
|
|
622
|
-
workspaceStatus: "closed",
|
|
623
|
-
conversationId: null,
|
|
1105
|
+
var EMPTY_DOCUMENT_STATE = {
|
|
624
1106
|
viewingDocument: null,
|
|
625
1107
|
documentViewer: DEFAULT_DOCUMENT_VIEWER_STATE,
|
|
626
1108
|
selectedDocumentIds: [],
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
suggestedQuery: null,
|
|
635
|
-
queuedMessages: {},
|
|
636
|
-
agentControlStates: {},
|
|
637
|
-
agentProbeAnswers: {},
|
|
638
|
-
agentProbePending: {},
|
|
639
|
-
drafts: {},
|
|
640
|
-
editingMessageId: null,
|
|
641
|
-
deepLinkMessageId: null,
|
|
642
|
-
activeCitationKey: null,
|
|
643
|
-
chatModel: void 0,
|
|
644
|
-
chatModelName: void 0,
|
|
645
|
-
newConversationAssistantMode: "always",
|
|
646
|
-
pendingTools: null,
|
|
647
|
-
autoSendDraft: false,
|
|
648
|
-
agentChatOwners: {},
|
|
649
|
-
newAgentChat: null,
|
|
650
|
-
newChatDocumentId: null,
|
|
651
|
-
messageTimers: {},
|
|
1109
|
+
pinnedDocumentId: null
|
|
1110
|
+
};
|
|
1111
|
+
var EMPTY_SESSION_STATE = {
|
|
1112
|
+
workspaceId: null,
|
|
1113
|
+
workspaceStatus: "closed",
|
|
1114
|
+
...EMPTY_CHAT_STATE,
|
|
1115
|
+
...EMPTY_DOCUMENT_STATE,
|
|
652
1116
|
...defaultLayoutState()
|
|
653
1117
|
};
|
|
654
1118
|
var selectViewingDocumentId = (state) => state.viewingDocument ? parseVersionRef(state.viewingDocument.id).docId : null;
|
|
655
|
-
var conversationKey = (state) => state.conversationId ?? "new";
|
|
656
|
-
var selectDraft = (state) => state.drafts[conversationKey(state)] ?? "";
|
|
657
|
-
var selectIsGenerating = (conversationId) => (state) => !!conversationId && !!state.generating[conversationId]?.isGenerating;
|
|
658
|
-
function nextViewingDocument(current, input) {
|
|
659
|
-
if (!input) return null;
|
|
660
|
-
const same = current?.id === input.id;
|
|
661
|
-
return {
|
|
662
|
-
id: input.id,
|
|
663
|
-
page: input.page !== void 0 ? input.page : same ? current?.page ?? null : null,
|
|
664
|
-
passageId: input.passageId !== void 0 ? input.passageId : current?.passageId ?? null
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
function layoutActionsOf(layout) {
|
|
668
|
-
const actions = { ...layout };
|
|
669
|
-
delete actions.getLayout;
|
|
670
|
-
delete actions.subscribe;
|
|
671
|
-
delete actions.hydrated;
|
|
672
|
-
delete actions.isHydrated;
|
|
673
|
-
delete actions.reset;
|
|
674
|
-
delete actions.threads;
|
|
675
|
-
return actions;
|
|
676
|
-
}
|
|
677
|
-
function withoutNew(record) {
|
|
678
|
-
const { new: _new, ...saved } = record;
|
|
679
|
-
return saved;
|
|
680
|
-
}
|
|
681
|
-
function createArbiSessionStore(initial = {}, {
|
|
682
|
-
draftStorage,
|
|
683
|
-
persist,
|
|
684
|
-
workspace = createArbiWorkspaceStore({ workspaceId: initial.workspaceId ?? null })
|
|
685
|
-
} = {}) {
|
|
686
|
-
const { shell, tabs, views, grid, ...rest } = initial;
|
|
687
|
-
const layout = createArbiLayoutStore({
|
|
688
|
-
persist,
|
|
689
|
-
initial: Object.fromEntries(
|
|
690
|
-
Object.entries({ shell, tabs, views, grid }).filter(([, part]) => part !== void 0)
|
|
691
|
-
)
|
|
692
|
-
});
|
|
693
|
-
if (initial.workspaceStatus) workspace.setWorkspaceStatus(initial.workspaceStatus);
|
|
694
|
-
let state = {
|
|
695
|
-
...EMPTY_SESSION_STATE,
|
|
696
|
-
...rest,
|
|
697
|
-
...layout.getLayout(),
|
|
698
|
-
...workspace.getState()
|
|
1119
|
+
var conversationKey = (state) => state.conversationId ?? "new";
|
|
1120
|
+
var selectDraft = (state) => state.drafts[conversationKey(state)] ?? "";
|
|
1121
|
+
var selectIsGenerating = (conversationId) => (state) => !!conversationId && !!state.generating[conversationId]?.isGenerating;
|
|
1122
|
+
function nextViewingDocument(current, input) {
|
|
1123
|
+
if (!input) return null;
|
|
1124
|
+
const same = current?.id === input.id;
|
|
1125
|
+
return {
|
|
1126
|
+
id: input.id,
|
|
1127
|
+
page: input.page !== void 0 ? input.page : same ? current?.page ?? null : null,
|
|
1128
|
+
passageId: input.passageId !== void 0 ? input.passageId : current?.passageId ?? null
|
|
699
1129
|
};
|
|
1130
|
+
}
|
|
1131
|
+
function createArbiDocumentSessionStore(initial = {}) {
|
|
1132
|
+
let state = { ...EMPTY_DOCUMENT_STATE, ...initial };
|
|
700
1133
|
const listeners = /* @__PURE__ */ new Set();
|
|
701
1134
|
const update = (patch) => {
|
|
702
1135
|
state = { ...state, ...patch };
|
|
703
1136
|
listeners.forEach((listener) => listener());
|
|
704
1137
|
};
|
|
705
|
-
layout.subscribe(() => update(layout.getLayout()));
|
|
706
|
-
workspace.subscribe(() => update(workspace.getState()));
|
|
707
|
-
let preEdit = { draft: "", cursor: null };
|
|
708
|
-
let draftsRestored = !draftStorage;
|
|
709
|
-
let draftsOwner = draftStorage?.owner() ?? null;
|
|
710
|
-
const restoreDrafts = () => {
|
|
711
|
-
if (!draftStorage) return;
|
|
712
|
-
const owner = draftStorage.owner();
|
|
713
|
-
if (owner !== draftsOwner) {
|
|
714
|
-
if (draftsOwner !== null) update({ drafts: {} });
|
|
715
|
-
draftsOwner = owner;
|
|
716
|
-
draftsRestored = false;
|
|
717
|
-
}
|
|
718
|
-
if (draftsRestored) return;
|
|
719
|
-
draftsRestored = true;
|
|
720
|
-
void draftStorage.load().then(
|
|
721
|
-
(saved) => {
|
|
722
|
-
if (saved && draftStorage.owner() === owner)
|
|
723
|
-
update({ drafts: { ...saved, ...state.drafts } });
|
|
724
|
-
else draftsRestored = false;
|
|
725
|
-
},
|
|
726
|
-
() => {
|
|
727
|
-
draftsRestored = false;
|
|
728
|
-
}
|
|
729
|
-
);
|
|
730
|
-
};
|
|
731
|
-
draftStorage?.onClear(() => {
|
|
732
|
-
draftsRestored = false;
|
|
733
|
-
draftsOwner = null;
|
|
734
|
-
preEdit = { draft: "", cursor: null };
|
|
735
|
-
if (Object.keys(state.drafts).length > 0) update({ drafts: {} });
|
|
736
|
-
});
|
|
737
|
-
restoreDrafts();
|
|
738
|
-
const setDrafts = (drafts) => {
|
|
739
|
-
update({ drafts });
|
|
740
|
-
draftStorage?.save(drafts);
|
|
741
|
-
};
|
|
742
|
-
const setDraftOf = (key, text) => {
|
|
743
|
-
if ((state.drafts[key] ?? "") === text) return;
|
|
744
|
-
const drafts = { ...state.drafts };
|
|
745
|
-
if (text) drafts[key] = text;
|
|
746
|
-
else delete drafts[key];
|
|
747
|
-
setDrafts(drafts);
|
|
748
|
-
};
|
|
749
|
-
const composer = () => ({
|
|
750
|
-
conversationId: state.conversationId,
|
|
751
|
-
view: {
|
|
752
|
-
cursor: state.cursor,
|
|
753
|
-
draft: selectDraft(state),
|
|
754
|
-
editingMessageId: state.editingMessageId,
|
|
755
|
-
preEditDraft: state.editingMessageId ? preEdit.draft : null,
|
|
756
|
-
preEditCursor: state.editingMessageId ? preEdit.cursor : null,
|
|
757
|
-
chatModel: state.chatModel,
|
|
758
|
-
chatModelName: state.chatModelName,
|
|
759
|
-
scrollTop: null
|
|
760
|
-
}
|
|
761
|
-
});
|
|
762
|
-
const show = (next) => {
|
|
763
|
-
if (next === null) return;
|
|
764
|
-
if (next === "list") {
|
|
765
|
-
preEdit = { draft: "", cursor: null };
|
|
766
|
-
return update({ conversationId: null, cursor: null, editingMessageId: null });
|
|
767
|
-
}
|
|
768
|
-
const view = next.view;
|
|
769
|
-
preEdit = { draft: view?.preEditDraft ?? "", cursor: view?.preEditCursor ?? null };
|
|
770
|
-
update({
|
|
771
|
-
conversationId: next.conversationId,
|
|
772
|
-
cursor: view?.cursor ?? null,
|
|
773
|
-
editingMessageId: view?.editingMessageId ?? null,
|
|
774
|
-
chatModel: view?.chatModel,
|
|
775
|
-
chatModelName: view?.chatModelName
|
|
776
|
-
});
|
|
777
|
-
};
|
|
778
1138
|
return {
|
|
779
|
-
...layoutActionsOf(layout),
|
|
780
|
-
layoutHydrated: () => layout.hydrated(),
|
|
781
|
-
isLayoutHydrated: () => layout.isHydrated(),
|
|
782
1139
|
getState: () => state,
|
|
783
1140
|
subscribe: (listener) => {
|
|
784
1141
|
listeners.add(listener);
|
|
785
1142
|
return () => listeners.delete(listener);
|
|
786
1143
|
},
|
|
787
|
-
setWorkspaceId: (workspaceId) => {
|
|
788
|
-
workspace.setWorkspaceId(workspaceId);
|
|
789
|
-
restoreDrafts();
|
|
790
|
-
},
|
|
791
|
-
setWorkspaceStatus: (workspaceStatus) => workspace.setWorkspaceStatus(workspaceStatus),
|
|
792
|
-
leaveWorkspace: () => {
|
|
793
|
-
Object.values(state.generating).forEach((generation) => generation.abortController?.abort());
|
|
794
|
-
workspace.setWorkspaceStatus("closed");
|
|
795
|
-
update({
|
|
796
|
-
conversationId: null,
|
|
797
|
-
cursor: null,
|
|
798
|
-
viewingDocument: null,
|
|
799
|
-
selectedDocumentIds: [],
|
|
800
|
-
generating: {},
|
|
801
|
-
// Saved conversations keep their documents; the unsent new chat's belong to this workspace.
|
|
802
|
-
conversationDocuments: withoutNew(state.conversationDocuments),
|
|
803
|
-
conversationDocumentAutoAdd: withoutNew(state.conversationDocumentAutoAdd),
|
|
804
|
-
conversationFacts: {},
|
|
805
|
-
pinnedDocumentId: null,
|
|
806
|
-
newAgentChat: null,
|
|
807
|
-
newChatDocumentId: null,
|
|
808
|
-
messageTimers: {}
|
|
809
|
-
});
|
|
810
|
-
layout.threads.clear();
|
|
811
|
-
},
|
|
812
|
-
setConversationId: (conversationId) => {
|
|
813
|
-
if (state.conversationId !== conversationId)
|
|
814
|
-
update({ conversationId, cursor: null, editingMessageId: null });
|
|
815
|
-
},
|
|
816
1144
|
setViewingDocument: (input) => update({ viewingDocument: nextViewingDocument(state.viewingDocument, input) }),
|
|
817
1145
|
setDocumentViewer: (change) => {
|
|
818
1146
|
const next = { ...state.documentViewer, ...change };
|
|
@@ -821,209 +1149,117 @@ function createArbiSessionStore(initial = {}, {
|
|
|
821
1149
|
update({ documentViewer: next });
|
|
822
1150
|
},
|
|
823
1151
|
setSelectedDocumentIds: (ids) => update({ selectedDocumentIds: ids }),
|
|
824
|
-
setConversationGenerating: (conversationId, isGenerating, abortController = null) => {
|
|
825
|
-
const current = state.generating[conversationId];
|
|
826
|
-
update({
|
|
827
|
-
generating: {
|
|
828
|
-
...state.generating,
|
|
829
|
-
[conversationId]: {
|
|
830
|
-
...current,
|
|
831
|
-
isGenerating,
|
|
832
|
-
error: isGenerating ? null : current?.error ?? null,
|
|
833
|
-
startTime: isGenerating ? Date.now() : null,
|
|
834
|
-
streamingAssistantId: isGenerating ? current?.streamingAssistantId ?? null : null,
|
|
835
|
-
backendElapsed: isGenerating ? current?.backendElapsed ?? null : null,
|
|
836
|
-
abortController
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
});
|
|
840
|
-
},
|
|
841
|
-
setStreamingAssistantId: (conversationId, streamingAssistantId, startTime = Date.now()) => {
|
|
842
|
-
const current = state.generating[conversationId];
|
|
843
|
-
update({
|
|
844
|
-
generating: {
|
|
845
|
-
...state.generating,
|
|
846
|
-
[conversationId]: {
|
|
847
|
-
...current ?? { isGenerating: false, error: null },
|
|
848
|
-
streamingAssistantId,
|
|
849
|
-
startTime
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
});
|
|
853
|
-
},
|
|
854
|
-
setBackendElapsed: (conversationId, backendElapsed) => {
|
|
855
|
-
const current = state.generating[conversationId] ?? {
|
|
856
|
-
isGenerating: false,
|
|
857
|
-
error: null,
|
|
858
|
-
startTime: null
|
|
859
|
-
};
|
|
860
|
-
update({
|
|
861
|
-
generating: { ...state.generating, [conversationId]: { ...current, backendElapsed } }
|
|
862
|
-
});
|
|
863
|
-
},
|
|
864
|
-
moveConversation: (fromId, toId) => {
|
|
865
|
-
const move = (record) => {
|
|
866
|
-
const next = { ...record };
|
|
867
|
-
if (fromId in next) {
|
|
868
|
-
next[toId] = next[fromId];
|
|
869
|
-
delete next[fromId];
|
|
870
|
-
}
|
|
871
|
-
return next;
|
|
872
|
-
};
|
|
873
|
-
update({
|
|
874
|
-
generating: move(state.generating),
|
|
875
|
-
conversationDocuments: move(state.conversationDocuments),
|
|
876
|
-
conversationDocumentAutoAdd: move(state.conversationDocumentAutoAdd),
|
|
877
|
-
conversationFacts: move(state.conversationFacts),
|
|
878
|
-
conversationId: state.conversationId ?? toId
|
|
879
|
-
});
|
|
880
|
-
layout.threads.rename(fromId, toId);
|
|
881
|
-
if (fromId in state.drafts) setDrafts(move(state.drafts));
|
|
882
|
-
},
|
|
883
|
-
setConversationFacts: (conversationId, factIds) => update({ conversationFacts: { ...state.conversationFacts, [conversationId]: factIds } }),
|
|
884
1152
|
setPinnedDocumentId: (pinnedDocumentId) => update({ pinnedDocumentId }),
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
},
|
|
952
|
-
setActiveCitationKey: (activeCitationKey) => {
|
|
953
|
-
if (state.activeCitationKey !== activeCitationKey) update({ activeCitationKey });
|
|
954
|
-
},
|
|
955
|
-
setPendingTools: (pendingTools) => update({ pendingTools }),
|
|
956
|
-
setAutoSendDraft: (autoSendDraft) => {
|
|
957
|
-
if (state.autoSendDraft !== autoSendDraft) update({ autoSendDraft });
|
|
958
|
-
},
|
|
959
|
-
setAgentChatOwner: (conversationId, agentId) => update({ agentChatOwners: { ...state.agentChatOwners, [conversationId]: agentId } }),
|
|
960
|
-
setMessageTimer: (key, timer) => update({ messageTimers: { ...state.messageTimers, [key]: timer } }),
|
|
961
|
-
clearMessageTimer: (key) => {
|
|
962
|
-
if (!(key in state.messageTimers)) return;
|
|
963
|
-
const messageTimers = { ...state.messageTimers };
|
|
964
|
-
delete messageTimers[key];
|
|
965
|
-
update({ messageTimers });
|
|
966
|
-
},
|
|
967
|
-
setConversationError: (conversationId, error, lastFailedMessage) => {
|
|
968
|
-
const current = state.generating[conversationId];
|
|
969
|
-
update({
|
|
970
|
-
generating: {
|
|
971
|
-
...state.generating,
|
|
972
|
-
[conversationId]: {
|
|
973
|
-
...current,
|
|
974
|
-
isGenerating: false,
|
|
975
|
-
error,
|
|
976
|
-
startTime: current?.startTime ?? null,
|
|
977
|
-
lastFailedMessage: lastFailedMessage ?? current?.lastFailedMessage ?? null
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
});
|
|
981
|
-
},
|
|
982
|
-
clearConversationError: (conversationId) => {
|
|
983
|
-
const current = state.generating[conversationId];
|
|
984
|
-
update({
|
|
985
|
-
generating: {
|
|
986
|
-
...state.generating,
|
|
987
|
-
[conversationId]: {
|
|
988
|
-
...current ?? { isGenerating: false, startTime: null },
|
|
989
|
-
error: null,
|
|
990
|
-
lastFailedMessage: null
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
});
|
|
994
|
-
},
|
|
995
|
-
abortConversationGeneration: (conversationId) => {
|
|
996
|
-
const current = state.generating[conversationId];
|
|
997
|
-
current?.abortController?.abort();
|
|
998
|
-
update({
|
|
999
|
-
generating: {
|
|
1000
|
-
...state.generating,
|
|
1001
|
-
[conversationId]: {
|
|
1002
|
-
...current ?? { error: null, startTime: null },
|
|
1003
|
-
isGenerating: false,
|
|
1004
|
-
abortController: null
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
});
|
|
1153
|
+
leave: () => update({ viewingDocument: null, selectedDocumentIds: [], pinnedDocumentId: null })
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
function layoutActionsOf(layout) {
|
|
1157
|
+
const actions = { ...layout };
|
|
1158
|
+
delete actions.getLayout;
|
|
1159
|
+
delete actions.subscribe;
|
|
1160
|
+
delete actions.hydrated;
|
|
1161
|
+
delete actions.isHydrated;
|
|
1162
|
+
delete actions.reset;
|
|
1163
|
+
delete actions.threads;
|
|
1164
|
+
return actions;
|
|
1165
|
+
}
|
|
1166
|
+
function chatActionsOf(chat) {
|
|
1167
|
+
const actions = { ...chat };
|
|
1168
|
+
delete actions.getState;
|
|
1169
|
+
delete actions.subscribe;
|
|
1170
|
+
delete actions.settled;
|
|
1171
|
+
delete actions.forget;
|
|
1172
|
+
delete actions.batch;
|
|
1173
|
+
delete actions.hydrated;
|
|
1174
|
+
delete actions.isHydrated;
|
|
1175
|
+
delete actions.restoreDrafts;
|
|
1176
|
+
delete actions.leave;
|
|
1177
|
+
delete actions.reset;
|
|
1178
|
+
return actions;
|
|
1179
|
+
}
|
|
1180
|
+
function composeArbiSessionStore({
|
|
1181
|
+
layout,
|
|
1182
|
+
workspace,
|
|
1183
|
+
chat,
|
|
1184
|
+
documents
|
|
1185
|
+
}) {
|
|
1186
|
+
let last = null;
|
|
1187
|
+
const getState = () => {
|
|
1188
|
+
const parts = [
|
|
1189
|
+
layout.getLayout(),
|
|
1190
|
+
workspace.getState(),
|
|
1191
|
+
documents.getState(),
|
|
1192
|
+
chat.getState()
|
|
1193
|
+
];
|
|
1194
|
+
if (last && parts.every((part, i) => part === last.parts[i])) return last.state;
|
|
1195
|
+
const [l, w, d, c] = parts;
|
|
1196
|
+
last = { parts, state: { ...l, ...w, ...d, ...c } };
|
|
1197
|
+
return last.state;
|
|
1198
|
+
};
|
|
1199
|
+
chat.restoreDrafts();
|
|
1200
|
+
workspace.subscribe(() => chat.restoreDrafts());
|
|
1201
|
+
return {
|
|
1202
|
+
...layoutActionsOf(layout),
|
|
1203
|
+
...chatActionsOf(chat),
|
|
1204
|
+
getState,
|
|
1205
|
+
layoutHydrated: () => layout.hydrated(),
|
|
1206
|
+
isLayoutHydrated: () => layout.isHydrated(),
|
|
1207
|
+
subscribe: (onChange) => {
|
|
1208
|
+
const told = chat.settled(onChange);
|
|
1209
|
+
const unsubscribe = [
|
|
1210
|
+
chat.subscribe(onChange),
|
|
1211
|
+
layout.subscribe(told),
|
|
1212
|
+
workspace.subscribe(told),
|
|
1213
|
+
documents.subscribe(told)
|
|
1214
|
+
];
|
|
1215
|
+
return () => {
|
|
1216
|
+
chat.forget(onChange);
|
|
1217
|
+
unsubscribe.forEach((stop) => stop());
|
|
1218
|
+
};
|
|
1008
1219
|
},
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1220
|
+
setWorkspaceId: (id) => workspace.setWorkspaceId(id),
|
|
1221
|
+
setWorkspaceStatus: (status) => workspace.setWorkspaceStatus(status),
|
|
1222
|
+
leaveWorkspace: () => chat.batch(() => {
|
|
1223
|
+
workspace.setWorkspaceStatus("closed");
|
|
1224
|
+
chat.leave();
|
|
1225
|
+
documents.leave();
|
|
1014
1226
|
}),
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
conversationDocuments: { ...state.conversationDocuments, [conversationId]: documents },
|
|
1020
|
-
conversationDocumentAutoAdd: {
|
|
1021
|
-
...state.conversationDocumentAutoAdd,
|
|
1022
|
-
[conversationId]: autoAdd
|
|
1023
|
-
}
|
|
1024
|
-
})
|
|
1227
|
+
setViewingDocument: (input) => documents.setViewingDocument(input),
|
|
1228
|
+
setDocumentViewer: (change) => documents.setDocumentViewer(change),
|
|
1229
|
+
setSelectedDocumentIds: (ids) => documents.setSelectedDocumentIds(ids),
|
|
1230
|
+
setPinnedDocumentId: (id) => documents.setPinnedDocumentId(id)
|
|
1025
1231
|
};
|
|
1026
1232
|
}
|
|
1233
|
+
function createArbiSessionStore(initial = {}, {
|
|
1234
|
+
draftStorage,
|
|
1235
|
+
persist,
|
|
1236
|
+
workspace = createArbiWorkspaceStore({ workspaceId: initial.workspaceId ?? null })
|
|
1237
|
+
} = {}) {
|
|
1238
|
+
const {
|
|
1239
|
+
shell,
|
|
1240
|
+
tabs,
|
|
1241
|
+
views,
|
|
1242
|
+
grid,
|
|
1243
|
+
workspaceStatus,
|
|
1244
|
+
viewingDocument,
|
|
1245
|
+
documentViewer,
|
|
1246
|
+
selectedDocumentIds,
|
|
1247
|
+
pinnedDocumentId,
|
|
1248
|
+
...chatInitial
|
|
1249
|
+
} = initial;
|
|
1250
|
+
delete chatInitial.workspaceId;
|
|
1251
|
+
const defined = (parts) => Object.fromEntries(Object.entries(parts).filter(([, part]) => part !== void 0));
|
|
1252
|
+
const layout = createArbiLayoutStore({ persist, initial: defined({ shell, tabs, views, grid }) });
|
|
1253
|
+
if (workspaceStatus) workspace.setWorkspaceStatus(workspaceStatus);
|
|
1254
|
+
return composeArbiSessionStore({
|
|
1255
|
+
layout,
|
|
1256
|
+
workspace,
|
|
1257
|
+
chat: createArbiChatStore({ layout, draftStorage, initial: chatInitial }),
|
|
1258
|
+
documents: createArbiDocumentSessionStore(
|
|
1259
|
+
defined({ viewingDocument, documentViewer, selectedDocumentIds, pinnedDocumentId })
|
|
1260
|
+
)
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1027
1263
|
var SAVE_DELAY_MS = 500;
|
|
1028
1264
|
var encoder = new TextEncoder();
|
|
1029
1265
|
var decoder = new TextDecoder();
|
|
@@ -1293,6 +1529,80 @@ function createStoragePersist({
|
|
|
1293
1529
|
};
|
|
1294
1530
|
}
|
|
1295
1531
|
|
|
1532
|
+
// src/session/ui-state-storage.ts
|
|
1533
|
+
function createUiStateStorage(options = {}) {
|
|
1534
|
+
const debounceMs = options.debounceMs ?? 250;
|
|
1535
|
+
const maxWaitMs = options.maxWaitMs ?? 1e3;
|
|
1536
|
+
const backing = options.backing ?? (() => {
|
|
1537
|
+
try {
|
|
1538
|
+
return typeof localStorage === "undefined" ? void 0 : localStorage;
|
|
1539
|
+
} catch {
|
|
1540
|
+
return void 0;
|
|
1541
|
+
}
|
|
1542
|
+
});
|
|
1543
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1544
|
+
let timer = null;
|
|
1545
|
+
let firstQueuedAt = null;
|
|
1546
|
+
const write = (name, value) => {
|
|
1547
|
+
try {
|
|
1548
|
+
backing()?.setItem(name, JSON.stringify(value));
|
|
1549
|
+
} catch (error) {
|
|
1550
|
+
console.warn("[ui-state] Could not save UI state; keeping it in memory", error);
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1553
|
+
const flush = () => {
|
|
1554
|
+
if (timer) clearTimeout(timer);
|
|
1555
|
+
timer = null;
|
|
1556
|
+
firstQueuedAt = null;
|
|
1557
|
+
const due = Array.from(pending.entries());
|
|
1558
|
+
pending.clear();
|
|
1559
|
+
for (const [name, value] of due) write(name, value);
|
|
1560
|
+
};
|
|
1561
|
+
return {
|
|
1562
|
+
getItem: (name) => {
|
|
1563
|
+
const unsaved = pending.get(name);
|
|
1564
|
+
if (unsaved) return unsaved;
|
|
1565
|
+
let raw;
|
|
1566
|
+
try {
|
|
1567
|
+
raw = backing()?.getItem(name);
|
|
1568
|
+
return raw ? JSON.parse(raw) : null;
|
|
1569
|
+
} catch (error) {
|
|
1570
|
+
console.warn("[ui-state] Saved UI state is unreadable; starting from defaults", error);
|
|
1571
|
+
if (raw !== void 0) {
|
|
1572
|
+
try {
|
|
1573
|
+
backing()?.removeItem(name);
|
|
1574
|
+
} catch {
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
},
|
|
1580
|
+
setItem: (name, value) => {
|
|
1581
|
+
pending.set(name, value);
|
|
1582
|
+
if (timer) clearTimeout(timer);
|
|
1583
|
+
firstQueuedAt ??= Date.now();
|
|
1584
|
+
timer = setTimeout(
|
|
1585
|
+
flush,
|
|
1586
|
+
Math.max(0, Math.min(debounceMs, firstQueuedAt + maxWaitMs - Date.now()))
|
|
1587
|
+
);
|
|
1588
|
+
},
|
|
1589
|
+
removeItem: (name) => {
|
|
1590
|
+
pending.delete(name);
|
|
1591
|
+
if (pending.size === 0) {
|
|
1592
|
+
if (timer) clearTimeout(timer);
|
|
1593
|
+
timer = null;
|
|
1594
|
+
firstQueuedAt = null;
|
|
1595
|
+
}
|
|
1596
|
+
try {
|
|
1597
|
+
backing()?.removeItem(name);
|
|
1598
|
+
} catch (error) {
|
|
1599
|
+
console.warn("[ui-state] Could not remove saved UI state", error);
|
|
1600
|
+
}
|
|
1601
|
+
},
|
|
1602
|
+
flush
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1296
1606
|
// src/usage-metered.ts
|
|
1297
1607
|
function isUsageMetered(usage, subscription) {
|
|
1298
1608
|
const empty = usage.total_spend === 0 && usage.total_tokens === 0 && usage.total_requests === 0;
|
|
@@ -1663,6 +1973,6 @@ function useUserUsageToday(options) {
|
|
|
1663
1973
|
});
|
|
1664
1974
|
}
|
|
1665
1975
|
|
|
1666
|
-
export { DEFAULT_DOCUMENT_VIEWER_STATE, EMPTY_SESSION_STATE, MAX_THREAD_TABS, MIN_QUEUED_MESSAGE_VISIBILITY_MS, PROJECT_CREATION_DENIED_REASON, WORKSPACE_LAST_OPENED_KEY, WORKSPACE_STORAGE_KEY, agentControl, browserWorkspaceStorage, clearSealedDrafts, closePanelIn, conversationKey, createArbiLayoutStore, createArbiSessionStore, createArbiWorkspaceStore, createDebouncedWriter, createSealedDraftStorage, createStoragePersist, defaultLayoutState, dmTabId, flushOnPageExit, isPanelOpen, isUsageMetered, layoutActionsOf, nextViewingDocument, openPanelIn, panelPlace, parseSavedLayout, queuedMessages, readWorkspaceLastOpened, restoreLayout, savedLayoutOf, selectDraft, selectIsGenerating, selectViewingDocumentId, threadTabId, useActivity, useAgents, useArbiSession, useArbiSessionState, useArbiSessionStore, useAssistantQuery, useAvailableModels, useBoxRole, useBuiltinCommands, useCanCreateProjects, useConfig, useConfigs, useConversationThreads, useConversations, useCurrentUser, useDeleteAgents, useDocumentVersionList, useFirmPool, useHealth, useMessageDetails, useParsedDocument, useProjectInvoices, useProjectUsage, useProjects, useProjectsUsage, useSessionPageTab, useSessionView, useSignedInSession, useTags, useThumbnails, useTopUpFirmPool, useTransientView, useUserUsageToday, useWorkspaceActivity, useWorkspaces };
|
|
1667
|
-
//# sourceMappingURL=chunk-
|
|
1668
|
-
//# sourceMappingURL=chunk-
|
|
1976
|
+
export { DEFAULT_DOCUMENT_VIEWER_STATE, EMPTY_CHAT_STATE, EMPTY_SESSION_STATE, MAX_THREAD_TABS, MIN_QUEUED_MESSAGE_VISIBILITY_MS, PROJECT_CREATION_DENIED_REASON, WORKSPACE_LAST_OPENED_KEY, WORKSPACE_STORAGE_KEY, agentControl, browserWorkspaceStorage, clearSealedDrafts, closePanelIn, composeArbiSessionStore, conversationKey, createArbiChatStore, createArbiDocumentSessionStore, createArbiLayoutStore, createArbiSessionStore, createArbiWorkspaceStore, createDebouncedWriter, createSealedDraftStorage, createStoragePersist, createUiStateStorage, defaultLayoutState, dmTabId, flushOnPageExit, isPanelOpen, isUsageMetered, layoutActionsOf, nextViewingDocument, openPanelIn, panelPlace, parseSavedChat, parseSavedLayout, queuedMessages, readWorkspaceLastOpened, restoreLayout, savedChatOf, savedLayoutOf, selectDraft, selectIsGenerating, selectViewingDocumentId, threadTabId, useActivity, useAgents, useArbiSession, useArbiSessionState, useArbiSessionStore, useAssistantQuery, useAvailableModels, useBoxRole, useBuiltinCommands, useCanCreateProjects, useConfig, useConfigs, useConversationThreads, useConversations, useCurrentUser, useDeleteAgents, useDocumentVersionList, useFirmPool, useHealth, useMessageDetails, useParsedDocument, useProjectInvoices, useProjectUsage, useProjects, useProjectsUsage, useSessionPageTab, useSessionView, useSignedInSession, useTags, useThumbnails, useTopUpFirmPool, useTransientView, useUserUsageToday, useWorkspaceActivity, useWorkspaces };
|
|
1977
|
+
//# sourceMappingURL=chunk-33PVGKXT.js.map
|
|
1978
|
+
//# sourceMappingURL=chunk-33PVGKXT.js.map
|