@arbidocs/react 0.5.73 → 0.5.74
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 +80 -66
- package/dist/artifacts/index.cjs +2 -2
- package/dist/artifacts/index.js +1 -1
- package/dist/calls/livekit/index.cjs +41 -41
- package/dist/calls/livekit/index.js +2 -2
- package/dist/{chunk-M3SB5TZB.js → chunk-6PNLCGMJ.js} +3 -3
- package/dist/{chunk-M3SB5TZB.js.map → chunk-6PNLCGMJ.js.map} +1 -1
- package/dist/{chunk-RS6UUPD2.cjs → chunk-BF7ABX3X.cjs} +875 -633
- package/dist/chunk-BF7ABX3X.cjs.map +1 -0
- package/dist/{chunk-HXITD6DN.cjs → chunk-C5SVEWPU.cjs} +5 -5
- package/dist/{chunk-HXITD6DN.cjs.map → chunk-C5SVEWPU.cjs.map} +1 -1
- package/dist/{chunk-QGOYIYLI.js → chunk-YWU7HEHL.js} +870 -634
- package/dist/chunk-YWU7HEHL.js.map +1 -0
- package/dist/index.cjs +470 -421
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +353 -229
- package/dist/index.d.ts +353 -229
- package/dist/index.js +30 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-QGOYIYLI.js.map +0 -1
- package/dist/chunk-RS6UUPD2.cjs.map +0 -1
|
@@ -8,6 +8,93 @@ var client = require('@arbidocs/client');
|
|
|
8
8
|
var react = require('react');
|
|
9
9
|
var reactQuery = require('@tanstack/react-query');
|
|
10
10
|
|
|
11
|
+
// src/chat/run-state.ts
|
|
12
|
+
var MIN_QUEUED_MESSAGE_VISIBILITY_MS = 800;
|
|
13
|
+
var mapQueues = (queues, update) => Object.fromEntries(Object.entries(queues).map(([id, messages]) => [id, update(messages)]));
|
|
14
|
+
function findConsumedMessages(messages, combinedContent) {
|
|
15
|
+
const text = combinedContent.trim();
|
|
16
|
+
const taken = /* @__PURE__ */ new Set();
|
|
17
|
+
let cursor = 0;
|
|
18
|
+
for (const message of messages) {
|
|
19
|
+
if (cursor >= text.length) break;
|
|
20
|
+
const content = message.content.trim();
|
|
21
|
+
if (!content) continue;
|
|
22
|
+
const separator = cursor === 0 ? "" : "\n\n";
|
|
23
|
+
if (text.startsWith(separator + content, cursor)) {
|
|
24
|
+
taken.add(message.clientId);
|
|
25
|
+
cursor += separator.length + content.length;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return cursor === text.length ? taken : /* @__PURE__ */ new Set();
|
|
29
|
+
}
|
|
30
|
+
var queuedMessages = {
|
|
31
|
+
add: (q, responseId, message) => ({
|
|
32
|
+
...q,
|
|
33
|
+
[responseId]: [...q[responseId] ?? [], message]
|
|
34
|
+
}),
|
|
35
|
+
acknowledge: (q, clientId, serverId) => mapQueues(
|
|
36
|
+
q,
|
|
37
|
+
(messages) => messages.map((m) => m.clientId === clientId ? { ...m, serverId, acknowledged: true } : m)
|
|
38
|
+
),
|
|
39
|
+
remove: (q, clientId) => mapQueues(q, (messages) => messages.filter((m) => m.clientId !== clientId)),
|
|
40
|
+
removeByServerId: (q, serverId) => mapQueues(q, (messages) => messages.filter((m) => m.serverId !== serverId)),
|
|
41
|
+
removeByContent: (q, responseId, content) => {
|
|
42
|
+
let removed = false;
|
|
43
|
+
return {
|
|
44
|
+
...q,
|
|
45
|
+
[responseId]: (q[responseId] ?? []).filter((m) => {
|
|
46
|
+
if (!removed && m.content === content) {
|
|
47
|
+
removed = true;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
})
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
/** Take out the prompts a steering interjection delivered into the run. */
|
|
55
|
+
consume: (q, responseId, combinedContent) => {
|
|
56
|
+
const messages = q[responseId] ?? [];
|
|
57
|
+
const consumed = findConsumedMessages(messages, combinedContent);
|
|
58
|
+
return { ...q, [responseId]: messages.filter((m) => !consumed.has(m.clientId)) };
|
|
59
|
+
},
|
|
60
|
+
clear: (q, responseId) => ({ ...q, [responseId]: [] }),
|
|
61
|
+
/** Move the entries still waiting on a finished turn to the turn that is now running. */
|
|
62
|
+
move: (q, fromResponseId, toResponseId) => {
|
|
63
|
+
const waiting = q[fromResponseId] ?? [];
|
|
64
|
+
if (fromResponseId === toResponseId || waiting.length === 0) return q;
|
|
65
|
+
return { ...q, [fromResponseId]: [], [toResponseId]: [...q[toResponseId] ?? [], ...waiting] };
|
|
66
|
+
},
|
|
67
|
+
/** Find a queued follow-up by its client id, under whichever turn holds it. */
|
|
68
|
+
find: (q, clientId) => Object.values(q).flat().find((m) => m.clientId === clientId)
|
|
69
|
+
};
|
|
70
|
+
var without = (record, key) => {
|
|
71
|
+
const next = { ...record };
|
|
72
|
+
delete next[key];
|
|
73
|
+
return next;
|
|
74
|
+
};
|
|
75
|
+
var agentControl = {
|
|
76
|
+
setState: (c, id, state) => ({
|
|
77
|
+
...c,
|
|
78
|
+
agentControlStates: { ...c.agentControlStates, [id]: state }
|
|
79
|
+
}),
|
|
80
|
+
/** A probe answered: the latest answer, and nothing pending any more. */
|
|
81
|
+
setProbeAnswer: (c, id, probe) => ({
|
|
82
|
+
...c,
|
|
83
|
+
agentProbeAnswers: { ...c.agentProbeAnswers, [id]: probe },
|
|
84
|
+
agentProbePending: { ...c.agentProbePending, [id]: false }
|
|
85
|
+
}),
|
|
86
|
+
setProbePending: (c, id, pending) => ({
|
|
87
|
+
...c,
|
|
88
|
+
agentProbePending: { ...c.agentProbePending, [id]: pending }
|
|
89
|
+
}),
|
|
90
|
+
/** The turn is over: drop its control state. */
|
|
91
|
+
clear: (c, id) => ({
|
|
92
|
+
agentControlStates: without(c.agentControlStates, id),
|
|
93
|
+
agentProbeAnswers: without(c.agentProbeAnswers, id),
|
|
94
|
+
agentProbePending: without(c.agentProbePending, id)
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
|
|
11
98
|
// src/session/thread-tabs.ts
|
|
12
99
|
var threadTabId = (tab) => tab.conversationId ?? "new";
|
|
13
100
|
var MAX_THREAD_TABS = 12;
|
|
@@ -86,16 +173,6 @@ function renameThreadTabIn(tabs, fromId, toId) {
|
|
|
86
173
|
)
|
|
87
174
|
};
|
|
88
175
|
}
|
|
89
|
-
function withThreadDrafts(threads, drafts) {
|
|
90
|
-
let changed = false;
|
|
91
|
-
const next = threads.map((tab) => {
|
|
92
|
-
const draft = drafts[threadTabId(tab)];
|
|
93
|
-
if (!draft || !tab.view || tab.view.draft) return tab;
|
|
94
|
-
changed = true;
|
|
95
|
-
return { ...tab, view: { ...tab.view, draft } };
|
|
96
|
-
});
|
|
97
|
-
return changed ? next : threads;
|
|
98
|
-
}
|
|
99
176
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
100
177
|
var stringOrNull = (v) => typeof v === "string" ? v : null;
|
|
101
178
|
var stringOrUndefined = (v) => typeof v === "string" ? v : void 0;
|
|
@@ -103,7 +180,6 @@ function parseView(value) {
|
|
|
103
180
|
if (!isRecord(value)) return void 0;
|
|
104
181
|
return {
|
|
105
182
|
cursor: stringOrNull(value.cursor),
|
|
106
|
-
draft: "",
|
|
107
183
|
editingMessageId: stringOrNull(value.editingMessageId),
|
|
108
184
|
preEditDraft: null,
|
|
109
185
|
preEditCursor: stringOrNull(value.preEditCursor),
|
|
@@ -128,181 +204,681 @@ function parseSavedThreadTabs(threads, activeThread) {
|
|
|
128
204
|
activeThread: active && tabs.some((tab) => threadTabId(tab) === active) ? active : null
|
|
129
205
|
};
|
|
130
206
|
}
|
|
131
|
-
var savedThreadTabs = (threads) => threads.map(
|
|
132
|
-
(tab) => tab.view ? { ...tab, view: { ...tab.view, draft: "", preEditDraft: null } } : tab
|
|
133
|
-
);
|
|
207
|
+
var savedThreadTabs = (threads) => threads.map((tab) => tab.view ? { ...tab, view: { ...tab.view, preEditDraft: null } } : tab);
|
|
134
208
|
|
|
135
|
-
// src/session/
|
|
136
|
-
var
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
209
|
+
// src/session/chat.ts
|
|
210
|
+
var EMPTY_CHAT_STATE = {
|
|
211
|
+
conversationId: null,
|
|
212
|
+
generating: {},
|
|
213
|
+
paMode: false,
|
|
214
|
+
conversationDocuments: {},
|
|
215
|
+
conversationDocumentAutoAdd: {},
|
|
216
|
+
conversationFacts: {},
|
|
217
|
+
cursor: null,
|
|
218
|
+
suggestedQuery: null,
|
|
219
|
+
queuedMessages: {},
|
|
220
|
+
agentControlStates: {},
|
|
221
|
+
agentProbeAnswers: {},
|
|
222
|
+
agentProbePending: {},
|
|
223
|
+
drafts: {},
|
|
224
|
+
editingMessageId: null,
|
|
225
|
+
deepLinkMessageId: null,
|
|
226
|
+
activeCitationKey: null,
|
|
227
|
+
chatModel: void 0,
|
|
228
|
+
chatModelName: void 0,
|
|
229
|
+
newConversationAssistantMode: "always",
|
|
230
|
+
pendingTools: null,
|
|
231
|
+
autoSendDraft: false,
|
|
232
|
+
agentChatOwners: {},
|
|
233
|
+
newAgentChat: null,
|
|
234
|
+
newChatDocumentId: null,
|
|
235
|
+
messageTimers: {}
|
|
236
|
+
};
|
|
160
237
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
161
|
-
var recordOf = (value, keep) => isRecord2(value) ? Object.fromEntries(Object.entries(value).filter(([, v]) => keep(v))) : {};
|
|
162
238
|
var isString = (v) => typeof v === "string";
|
|
163
|
-
var isNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
164
|
-
var isAny = (v) => v !== void 0;
|
|
165
|
-
var isMode = (v) => PANEL_MODES.includes(v);
|
|
166
|
-
var isSize = (v) => isRecord2(v) && isNumber(v.width) && isNumber(v.height);
|
|
167
|
-
var isList = (v) => Array.isArray(v);
|
|
168
239
|
var stringOrNull2 = (v) => isString(v) ? v : null;
|
|
169
|
-
|
|
240
|
+
var recordOf = (value, read) => {
|
|
241
|
+
if (!isRecord2(value)) return {};
|
|
242
|
+
const out = {};
|
|
243
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
244
|
+
const parsed = read(entry);
|
|
245
|
+
if (parsed !== null) out[key] = parsed;
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
};
|
|
249
|
+
var readDocuments = (value) => Array.isArray(value) ? value.filter((doc) => isRecord2(doc) && isString(doc.docId)).map((doc) => ({ docId: doc.docId, isPinned: doc.isPinned === true })) : null;
|
|
250
|
+
var readFacts = (value) => Array.isArray(value) ? Array.from(new Set(value.filter(isString))) : null;
|
|
251
|
+
function parseSavedChat(saved) {
|
|
170
252
|
if (!isRecord2(saved) || saved.version !== 1) return null;
|
|
171
|
-
const shell = isRecord2(saved.shell) ? saved.shell : {};
|
|
172
|
-
const panels = isRecord2(shell.panels) ? shell.panels : {};
|
|
173
|
-
const tabs = isRecord2(saved.tabs) ? saved.tabs : {};
|
|
174
|
-
const views = isRecord2(saved.views) ? saved.views : {};
|
|
175
|
-
const grid = isRecord2(saved.grid) ? saved.grid : {};
|
|
176
|
-
const defaults = defaultLayoutState();
|
|
177
253
|
return {
|
|
178
254
|
version: 1,
|
|
179
|
-
migrated:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
views: {
|
|
192
|
-
tabs: recordOf(views.tabs, isString),
|
|
193
|
-
filters: recordOf(views.filters, isAny),
|
|
194
|
-
values: recordOf(views.values, isAny)
|
|
195
|
-
},
|
|
196
|
-
grid: {
|
|
197
|
-
states: recordOf(grid.states, isRecord2),
|
|
198
|
-
rowHeights: recordOf(grid.rowHeights, isNumber),
|
|
199
|
-
labelWidths: recordOf(grid.labelWidths, isNumber),
|
|
200
|
-
savedViews: recordOf(grid.savedViews, isList)
|
|
201
|
-
}
|
|
255
|
+
migrated: Array.isArray(saved.migrated) ? saved.migrated.filter(isString) : [],
|
|
256
|
+
conversationId: stringOrNull2(saved.conversationId),
|
|
257
|
+
cursor: stringOrNull2(saved.cursor),
|
|
258
|
+
conversationDocuments: recordOf(saved.conversationDocuments, readDocuments),
|
|
259
|
+
conversationDocumentAutoAdd: recordOf(
|
|
260
|
+
saved.conversationDocumentAutoAdd,
|
|
261
|
+
(v) => typeof v === "boolean" ? v : null
|
|
262
|
+
),
|
|
263
|
+
conversationFacts: recordOf(saved.conversationFacts, readFacts),
|
|
264
|
+
agentChatOwners: recordOf(saved.agentChatOwners, (v) => isString(v) ? v : null),
|
|
265
|
+
agentChatOwnersUser: stringOrNull2(saved.agentChatOwnersUser),
|
|
266
|
+
chatModel: stringOrNull2(saved.chatModel)
|
|
202
267
|
};
|
|
203
268
|
}
|
|
204
|
-
function
|
|
205
|
-
const { shell, tabs, views, grid } = layout;
|
|
269
|
+
function savedChatOf(state, migrated, agentChatOwnersUser = null) {
|
|
206
270
|
return {
|
|
207
271
|
version: 1,
|
|
208
272
|
migrated,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
sizes: shell.panels.sizes
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
tabs: { threads: savedThreadTabs(tabs.threads), activeThread: tabs.activeThread },
|
|
221
|
-
views: { tabs: views.tabs, filters: views.filters, values: views.values },
|
|
222
|
-
grid
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
function restoreLayout(current, saved) {
|
|
226
|
-
return {
|
|
227
|
-
...current,
|
|
228
|
-
shell: {
|
|
229
|
-
...current.shell,
|
|
230
|
-
page: saved.shell.page,
|
|
231
|
-
sidebar: { ...current.shell.sidebar, pinned: saved.shell.sidebarPinned },
|
|
232
|
-
panels: { ...current.shell.panels, ...saved.shell.panels }
|
|
233
|
-
},
|
|
234
|
-
tabs: { ...current.tabs, ...saved.tabs },
|
|
235
|
-
views: { ...saved.views, transient: current.views.transient },
|
|
236
|
-
grid: saved.grid
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
function openPanelIn(panels, panelId, mode) {
|
|
240
|
-
const leave = (slot) => slot === panelId ? null : slot;
|
|
241
|
-
const next = {
|
|
242
|
-
...panels,
|
|
243
|
-
main: leave(panels.main),
|
|
244
|
-
docked: leave(panels.docked),
|
|
245
|
-
overlay: leave(panels.overlay),
|
|
246
|
-
popout: leave(panels.popout),
|
|
247
|
-
modes: { ...panels.modes, [panelId]: mode }
|
|
273
|
+
conversationId: state.conversationId,
|
|
274
|
+
cursor: state.cursor,
|
|
275
|
+
conversationDocuments: state.conversationDocuments,
|
|
276
|
+
conversationDocumentAutoAdd: { ...state.conversationDocumentAutoAdd },
|
|
277
|
+
conversationFacts: state.conversationFacts,
|
|
278
|
+
agentChatOwners: { ...state.agentChatOwners },
|
|
279
|
+
agentChatOwnersUser,
|
|
280
|
+
chatModel: state.chatModel ?? null
|
|
248
281
|
};
|
|
249
|
-
if (mode === "docked") return { ...next, docked: panelId, overlay: null };
|
|
250
|
-
if (mode === "overlay") return { ...next, overlay: panelId, docked: null };
|
|
251
|
-
return { ...next, [mode]: panelId };
|
|
252
282
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
283
|
+
var SAVED_FIELDS = [
|
|
284
|
+
"conversationId",
|
|
285
|
+
"cursor",
|
|
286
|
+
"conversationDocuments",
|
|
287
|
+
"conversationDocumentAutoAdd",
|
|
288
|
+
"conversationFacts",
|
|
289
|
+
"agentChatOwners",
|
|
290
|
+
"chatModel"
|
|
291
|
+
];
|
|
292
|
+
function withoutNew(record) {
|
|
293
|
+
const saved = { ...record };
|
|
294
|
+
delete saved.new;
|
|
295
|
+
return saved;
|
|
262
296
|
}
|
|
263
|
-
function
|
|
264
|
-
|
|
297
|
+
function moveKey(record, fromId, toId) {
|
|
298
|
+
if (!(fromId in record)) return record;
|
|
299
|
+
const next = { ...record, [toId]: record[fromId] };
|
|
300
|
+
delete next[fromId];
|
|
301
|
+
return next;
|
|
265
302
|
}
|
|
266
|
-
var
|
|
267
|
-
|
|
268
|
-
|
|
303
|
+
var NO_EDIT = { draft: "", cursor: null };
|
|
304
|
+
function createArbiChatStore({
|
|
305
|
+
layout,
|
|
269
306
|
persist,
|
|
307
|
+
draftStorage,
|
|
308
|
+
scrollTop = () => null,
|
|
309
|
+
user = () => draftStorage?.owner() ?? null,
|
|
270
310
|
initial
|
|
271
|
-
}
|
|
272
|
-
let
|
|
311
|
+
}) {
|
|
312
|
+
let state = { ...EMPTY_CHAT_STATE, ...initial };
|
|
273
313
|
let migrated = [];
|
|
314
|
+
let ownersUser = null;
|
|
274
315
|
let isHydrated = !persist;
|
|
275
316
|
const listeners = /* @__PURE__ */ new Set();
|
|
317
|
+
const waiting = /* @__PURE__ */ new Set();
|
|
318
|
+
let depth = 0;
|
|
319
|
+
const settle = (listener) => depth > 0 ? waiting.add(listener) : listener();
|
|
320
|
+
const notify = () => listeners.forEach(settle);
|
|
321
|
+
const batch = (change) => {
|
|
322
|
+
depth += 1;
|
|
323
|
+
try {
|
|
324
|
+
return change();
|
|
325
|
+
} finally {
|
|
326
|
+
depth -= 1;
|
|
327
|
+
if (depth === 0) {
|
|
328
|
+
const told = [...waiting];
|
|
329
|
+
waiting.clear();
|
|
330
|
+
told.forEach((listener) => listener());
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
276
334
|
const save = () => {
|
|
277
|
-
if (persist && isHydrated) persist.save(
|
|
335
|
+
if (persist && isHydrated) persist.save(savedChatOf(state, migrated, ownersUser));
|
|
278
336
|
};
|
|
279
|
-
const update = (
|
|
280
|
-
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (
|
|
337
|
+
const update = (patch) => {
|
|
338
|
+
const next = { ...state, ...patch };
|
|
339
|
+
const saved = SAVED_FIELDS.some((key) => next[key] !== state[key]);
|
|
340
|
+
state = next;
|
|
341
|
+
notify();
|
|
342
|
+
if (saved) save();
|
|
285
343
|
};
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
()
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
344
|
+
let preEdit = NO_EDIT;
|
|
345
|
+
let draftsRestored = !draftStorage;
|
|
346
|
+
let restoring = false;
|
|
347
|
+
let draftsOwner = draftStorage?.owner() ?? null;
|
|
348
|
+
const setDrafts = (drafts) => {
|
|
349
|
+
if (drafts === state.drafts) return;
|
|
350
|
+
update({ drafts });
|
|
351
|
+
if (draftsRestored) draftStorage?.save(drafts);
|
|
352
|
+
};
|
|
353
|
+
const setDraftOf = (key, text) => {
|
|
354
|
+
if ((state.drafts[key] ?? "") === text) return;
|
|
355
|
+
const drafts = { ...state.drafts };
|
|
356
|
+
if (text) drafts[key] = text;
|
|
357
|
+
else delete drafts[key];
|
|
358
|
+
setDrafts(drafts);
|
|
359
|
+
};
|
|
360
|
+
const keptDrafts = (drafts) => {
|
|
361
|
+
const open = new Set(layout.getLayout().tabs.threads.map(threadTabId));
|
|
362
|
+
open.add(state.conversationId ?? "new");
|
|
363
|
+
const keys = Object.keys(drafts);
|
|
364
|
+
if (keys.every((key) => open.has(key))) return drafts;
|
|
365
|
+
return Object.fromEntries(keys.filter((key) => open.has(key)).map((key) => [key, drafts[key]]));
|
|
366
|
+
};
|
|
367
|
+
const restoreDrafts = () => {
|
|
368
|
+
if (!draftStorage) return;
|
|
369
|
+
const owner = draftStorage.owner();
|
|
370
|
+
if (owner !== draftsOwner) {
|
|
371
|
+
if (draftsOwner !== null) update({ drafts: {} });
|
|
372
|
+
draftsOwner = owner;
|
|
373
|
+
draftsRestored = false;
|
|
374
|
+
}
|
|
375
|
+
if (draftsRestored || restoring || owner === null) return;
|
|
376
|
+
restoring = true;
|
|
377
|
+
void Promise.all([layout.hydrated(), hydration]).then(() => draftStorage.load()).then(
|
|
378
|
+
(saved) => {
|
|
379
|
+
restoring = false;
|
|
380
|
+
if (!saved || draftStorage.owner() !== owner) return;
|
|
381
|
+
draftsRestored = true;
|
|
382
|
+
setDrafts(keptDrafts({ ...saved, ...state.drafts }));
|
|
383
|
+
draftStorage.save(state.drafts);
|
|
384
|
+
},
|
|
385
|
+
() => {
|
|
386
|
+
restoring = false;
|
|
387
|
+
}
|
|
388
|
+
);
|
|
389
|
+
};
|
|
390
|
+
draftStorage?.onClear(() => {
|
|
391
|
+
draftsRestored = false;
|
|
392
|
+
draftsOwner = null;
|
|
393
|
+
preEdit = NO_EDIT;
|
|
394
|
+
if (Object.keys(state.drafts).length > 0) update({ drafts: {} });
|
|
395
|
+
});
|
|
396
|
+
const composer = () => ({
|
|
397
|
+
conversationId: state.conversationId,
|
|
398
|
+
view: {
|
|
399
|
+
cursor: state.cursor,
|
|
400
|
+
editingMessageId: state.editingMessageId,
|
|
401
|
+
preEditDraft: state.editingMessageId ? preEdit.draft : null,
|
|
402
|
+
preEditCursor: state.editingMessageId ? preEdit.cursor : null,
|
|
403
|
+
chatModel: state.chatModel,
|
|
404
|
+
chatModelName: state.chatModelName,
|
|
405
|
+
scrollTop: scrollTop()
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
const show = (next) => {
|
|
409
|
+
if (next === null) return;
|
|
410
|
+
const target = next === "list" ? { conversationId: null, view: void 0 } : next;
|
|
411
|
+
const view = target.view;
|
|
412
|
+
preEdit = view ? { draft: view.preEditDraft ?? "", cursor: view.preEditCursor ?? null } : NO_EDIT;
|
|
413
|
+
update({
|
|
414
|
+
conversationId: target.conversationId,
|
|
415
|
+
cursor: view?.cursor ?? null,
|
|
416
|
+
editingMessageId: view?.editingMessageId ?? null,
|
|
417
|
+
chatModel: view?.chatModel,
|
|
418
|
+
chatModelName: view?.chatModelName
|
|
419
|
+
});
|
|
420
|
+
if (!view) setDraftOf(target.conversationId ?? "new", "");
|
|
421
|
+
};
|
|
422
|
+
const afterTabs = () => setDrafts(keptDrafts(state.drafts));
|
|
423
|
+
const openThreadTab = (conversationId) => batch(() => {
|
|
424
|
+
update(
|
|
425
|
+
conversationId === null ? { newAgentChat: null, newChatDocumentId: null } : { newAgentChat: null }
|
|
426
|
+
);
|
|
427
|
+
const { tabId, show: next } = layout.threads.open(conversationId, composer());
|
|
428
|
+
show(next);
|
|
429
|
+
layout.setPageTab("conversations", tabId);
|
|
430
|
+
afterTabs();
|
|
431
|
+
return tabId;
|
|
432
|
+
});
|
|
433
|
+
const hydration = persist ? persist.load().then(
|
|
434
|
+
(saved) => {
|
|
435
|
+
const parsed = parseSavedChat(saved);
|
|
436
|
+
if (parsed) {
|
|
437
|
+
migrated = parsed.migrated;
|
|
438
|
+
ownersUser ??= parsed.agentChatOwnersUser;
|
|
439
|
+
const conversationId = state.conversationId ?? parsed.conversationId;
|
|
440
|
+
const savedOpen = conversationId === parsed.conversationId;
|
|
441
|
+
state = {
|
|
442
|
+
...state,
|
|
443
|
+
conversationId,
|
|
444
|
+
cursor: state.cursor ?? (savedOpen ? parsed.cursor : null),
|
|
445
|
+
chatModel: state.chatModel ?? (savedOpen ? parsed.chatModel ?? void 0 : void 0),
|
|
446
|
+
conversationDocuments: {
|
|
447
|
+
...parsed.conversationDocuments,
|
|
448
|
+
...state.conversationDocuments
|
|
449
|
+
},
|
|
450
|
+
conversationDocumentAutoAdd: {
|
|
451
|
+
...parsed.conversationDocumentAutoAdd,
|
|
452
|
+
...state.conversationDocumentAutoAdd
|
|
453
|
+
},
|
|
454
|
+
conversationFacts: { ...parsed.conversationFacts, ...state.conversationFacts },
|
|
455
|
+
agentChatOwners: { ...parsed.agentChatOwners, ...state.agentChatOwners }
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
isHydrated = true;
|
|
459
|
+
notify();
|
|
460
|
+
},
|
|
461
|
+
() => {
|
|
462
|
+
isHydrated = true;
|
|
463
|
+
notify();
|
|
464
|
+
}
|
|
465
|
+
) : Promise.resolve();
|
|
466
|
+
void Promise.all([layout.hydrated(), hydration]).then(() => {
|
|
467
|
+
const { threads, activeThread } = layout.getLayout().tabs;
|
|
468
|
+
const tab = activeThread ? threads.find((t) => threadTabId(t) === activeThread) : null;
|
|
469
|
+
if (tab && state.conversationId !== tab.conversationId)
|
|
470
|
+
update({ conversationId: tab.conversationId });
|
|
471
|
+
});
|
|
472
|
+
const generationOf = (conversationId) => state.generating[conversationId];
|
|
473
|
+
const setGeneration = (conversationId, generation) => update({ generating: { ...state.generating, [conversationId]: generation } });
|
|
474
|
+
const stopAll = () => Object.values(state.generating).forEach((generation) => generation.abortController?.abort());
|
|
475
|
+
return {
|
|
476
|
+
getState: () => state,
|
|
477
|
+
subscribe: (listener) => {
|
|
478
|
+
listeners.add(listener);
|
|
479
|
+
return () => {
|
|
480
|
+
listeners.delete(listener);
|
|
481
|
+
waiting.delete(listener);
|
|
482
|
+
};
|
|
483
|
+
},
|
|
484
|
+
settled: (listener) => () => settle(listener),
|
|
485
|
+
forget: (listener) => void waiting.delete(listener),
|
|
486
|
+
batch,
|
|
487
|
+
hydrated: () => hydration,
|
|
488
|
+
isHydrated: () => isHydrated,
|
|
489
|
+
restoreDrafts,
|
|
490
|
+
leave: () => batch(() => {
|
|
491
|
+
stopAll();
|
|
492
|
+
preEdit = NO_EDIT;
|
|
493
|
+
update({
|
|
494
|
+
conversationId: null,
|
|
495
|
+
cursor: null,
|
|
496
|
+
editingMessageId: null,
|
|
497
|
+
chatModel: void 0,
|
|
498
|
+
chatModelName: void 0,
|
|
499
|
+
newConversationAssistantMode: "always",
|
|
500
|
+
paMode: false,
|
|
501
|
+
generating: {},
|
|
502
|
+
// Saved conversations keep their documents; the unsent new chat's belong to this workspace.
|
|
503
|
+
conversationDocuments: withoutNew(state.conversationDocuments),
|
|
504
|
+
conversationDocumentAutoAdd: withoutNew(state.conversationDocumentAutoAdd),
|
|
505
|
+
conversationFacts: {},
|
|
506
|
+
newAgentChat: null,
|
|
507
|
+
newChatDocumentId: null,
|
|
508
|
+
messageTimers: {}
|
|
509
|
+
});
|
|
510
|
+
layout.threads.clear();
|
|
511
|
+
setDrafts({});
|
|
512
|
+
}),
|
|
513
|
+
reset: () => batch(() => {
|
|
514
|
+
stopAll();
|
|
515
|
+
preEdit = NO_EDIT;
|
|
516
|
+
const signedIn = user();
|
|
517
|
+
const keepOwners = signedIn === null || ownersUser === null || signedIn === ownersUser;
|
|
518
|
+
if (signedIn !== null) ownersUser = signedIn;
|
|
519
|
+
update({ ...EMPTY_CHAT_STATE, agentChatOwners: keepOwners ? state.agentChatOwners : {} });
|
|
520
|
+
}),
|
|
521
|
+
setConversationId: (id) => void openThreadTab(id),
|
|
522
|
+
openThreadTab,
|
|
523
|
+
closeThreadTab: (tabId) => batch(() => {
|
|
524
|
+
show(layout.threads.close(tabId));
|
|
525
|
+
layout.setPageTab("conversations", layout.getLayout().tabs.activeThread ?? "history");
|
|
526
|
+
afterTabs();
|
|
527
|
+
}),
|
|
528
|
+
selectThreadTab: (tabId) => batch(() => {
|
|
529
|
+
if (tabId !== null) {
|
|
530
|
+
show(layout.threads.select(tabId, composer()));
|
|
531
|
+
layout.setPageTab("conversations", tabId);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
layout.threads.showList(composer());
|
|
535
|
+
preEdit = NO_EDIT;
|
|
536
|
+
update({ conversationId: null, cursor: null, editingMessageId: null });
|
|
537
|
+
layout.setPageTab("conversations", "history");
|
|
538
|
+
afterTabs();
|
|
539
|
+
}),
|
|
540
|
+
moveConversation: (fromId, toId) => batch(() => {
|
|
541
|
+
update({
|
|
542
|
+
generating: moveKey(state.generating, fromId, toId),
|
|
543
|
+
conversationDocuments: moveKey(state.conversationDocuments, fromId, toId),
|
|
544
|
+
conversationDocumentAutoAdd: moveKey(state.conversationDocumentAutoAdd, fromId, toId),
|
|
545
|
+
conversationFacts: moveKey(state.conversationFacts, fromId, toId),
|
|
546
|
+
conversationId: state.conversationId ?? toId
|
|
547
|
+
});
|
|
548
|
+
layout.threads.rename(fromId, toId);
|
|
549
|
+
setDrafts(moveKey(state.drafts, fromId, toId));
|
|
550
|
+
}),
|
|
551
|
+
setConversationGenerating: (conversationId, isGenerating, abortController) => {
|
|
552
|
+
const current = generationOf(conversationId);
|
|
553
|
+
setGeneration(conversationId, {
|
|
554
|
+
...current,
|
|
555
|
+
isGenerating,
|
|
556
|
+
error: isGenerating ? null : current?.error ?? null,
|
|
557
|
+
// Starting with an explicit null means generation this client does not own (an attach to a
|
|
558
|
+
// run started elsewhere): it must not inherit a finished local turn's controller, or the
|
|
559
|
+
// view takes it for a local stream and detaches, which makes it attachable again (an
|
|
560
|
+
// endless loop that froze the tab after Stop). Stopping keeps the controller: the finished
|
|
561
|
+
// turn's bookkeeping still matches on it.
|
|
562
|
+
abortController: isGenerating && abortController === null ? null : abortController ?? current?.abortController ?? null,
|
|
563
|
+
// The run's start is set when the server names its answer (setStreamingAssistantId).
|
|
564
|
+
startTime: null,
|
|
565
|
+
streamingAssistantId: null,
|
|
566
|
+
...isGenerating ? { lastFailedMessage: null, backendElapsed: null } : {}
|
|
567
|
+
});
|
|
568
|
+
},
|
|
569
|
+
setStreamingAssistantId: (conversationId, streamingAssistantId, startTime = Date.now()) => setGeneration(conversationId, {
|
|
570
|
+
...generationOf(conversationId) ?? { isGenerating: false, error: null },
|
|
571
|
+
streamingAssistantId,
|
|
572
|
+
startTime
|
|
573
|
+
}),
|
|
574
|
+
setBackendElapsed: (conversationId, backendElapsed) => setGeneration(conversationId, {
|
|
575
|
+
...generationOf(conversationId) ?? { isGenerating: false, error: null, startTime: null },
|
|
576
|
+
backendElapsed
|
|
577
|
+
}),
|
|
578
|
+
setConversationError: (conversationId, error, lastFailedMessage) => {
|
|
579
|
+
const current = generationOf(conversationId);
|
|
580
|
+
setGeneration(conversationId, {
|
|
581
|
+
...current,
|
|
582
|
+
isGenerating: false,
|
|
583
|
+
error,
|
|
584
|
+
startTime: current?.startTime ?? null,
|
|
585
|
+
lastFailedMessage: lastFailedMessage ?? current?.lastFailedMessage ?? null
|
|
586
|
+
});
|
|
587
|
+
},
|
|
588
|
+
clearConversationError: (conversationId) => setGeneration(conversationId, {
|
|
589
|
+
...generationOf(conversationId) ?? { isGenerating: false, startTime: null },
|
|
590
|
+
error: null,
|
|
591
|
+
lastFailedMessage: null
|
|
592
|
+
}),
|
|
593
|
+
abortConversationGeneration: (conversationId) => {
|
|
594
|
+
const current = generationOf(conversationId);
|
|
595
|
+
current?.abortController?.abort();
|
|
596
|
+
setGeneration(conversationId, {
|
|
597
|
+
...current ?? { error: null, startTime: null },
|
|
598
|
+
isGenerating: false,
|
|
599
|
+
abortController: null
|
|
600
|
+
});
|
|
601
|
+
},
|
|
602
|
+
setPaMode: (paMode) => {
|
|
603
|
+
if (state.paMode !== paMode) update({ paMode });
|
|
604
|
+
},
|
|
605
|
+
setConversationDocuments: (conversationId, documents, autoAdd = false) => update({
|
|
606
|
+
conversationDocuments: { ...state.conversationDocuments, [conversationId]: documents },
|
|
607
|
+
conversationDocumentAutoAdd: {
|
|
608
|
+
...state.conversationDocumentAutoAdd,
|
|
609
|
+
[conversationId]: autoAdd
|
|
610
|
+
}
|
|
611
|
+
}),
|
|
612
|
+
setConversationFacts: (conversationId, factIds) => update({
|
|
613
|
+
conversationFacts: {
|
|
614
|
+
...state.conversationFacts,
|
|
615
|
+
[conversationId]: Array.from(new Set(factIds))
|
|
616
|
+
}
|
|
617
|
+
}),
|
|
618
|
+
setCursor: (cursor) => {
|
|
619
|
+
if (state.cursor !== cursor) update({ cursor });
|
|
620
|
+
},
|
|
621
|
+
setSuggestedQuery: (suggestedQuery) => update({ suggestedQuery }),
|
|
622
|
+
addQueuedMessage: (id, message) => update({ queuedMessages: queuedMessages.add(state.queuedMessages, id, message) }),
|
|
623
|
+
acknowledgeQueuedMessage: (clientId, serverId) => update({
|
|
624
|
+
queuedMessages: queuedMessages.acknowledge(state.queuedMessages, clientId, serverId)
|
|
625
|
+
}),
|
|
626
|
+
removeQueuedMessage: (clientId) => update({ queuedMessages: queuedMessages.remove(state.queuedMessages, clientId) }),
|
|
627
|
+
removeQueuedMessageByServerId: (serverId) => update({ queuedMessages: queuedMessages.removeByServerId(state.queuedMessages, serverId) }),
|
|
628
|
+
consumeQueuedMessage: (id, content) => update({ queuedMessages: queuedMessages.consume(state.queuedMessages, id, content) }),
|
|
629
|
+
clearQueuedMessages: (id) => update({ queuedMessages: queuedMessages.clear(state.queuedMessages, id) }),
|
|
630
|
+
moveQueuedMessages: (from, to) => update({ queuedMessages: queuedMessages.move(state.queuedMessages, from, to) }),
|
|
631
|
+
setAgentControlState: (id, control) => update(agentControl.setState(state, id, control)),
|
|
632
|
+
setAgentProbeAnswer: (id, probe) => update(agentControl.setProbeAnswer(state, id, probe)),
|
|
633
|
+
setAgentProbePending: (id, pending) => update(agentControl.setProbePending(state, id, pending)),
|
|
634
|
+
clearAgentControl: (id) => update(agentControl.clear(state, id)),
|
|
635
|
+
setDraft: (text) => setDraftOf(state.conversationId ?? "new", text),
|
|
636
|
+
setChatModel: (chatModel) => {
|
|
637
|
+
if (state.chatModel !== chatModel) update({ chatModel });
|
|
638
|
+
},
|
|
639
|
+
setChatModelName: (chatModelName) => {
|
|
640
|
+
if (state.chatModelName !== chatModelName) update({ chatModelName });
|
|
641
|
+
},
|
|
642
|
+
setNewConversationAssistantMode: (newConversationAssistantMode) => {
|
|
643
|
+
if (state.newConversationAssistantMode !== newConversationAssistantMode)
|
|
644
|
+
update({ newConversationAssistantMode });
|
|
645
|
+
},
|
|
646
|
+
setNewAgentChat: (newAgentChat) => {
|
|
647
|
+
const current = state.newAgentChat;
|
|
648
|
+
if (current?.agentId !== newAgentChat?.agentId || current?.workspaceId !== newAgentChat?.workspaceId)
|
|
649
|
+
update({ newAgentChat });
|
|
650
|
+
},
|
|
651
|
+
setNewChatDocumentId: (newChatDocumentId) => {
|
|
652
|
+
if (state.newChatDocumentId !== newChatDocumentId) update({ newChatDocumentId });
|
|
653
|
+
},
|
|
654
|
+
startEditing: (messageId, content) => batch(() => {
|
|
655
|
+
const key = state.conversationId ?? "new";
|
|
656
|
+
preEdit = { draft: state.drafts[key] ?? "", cursor: state.cursor };
|
|
657
|
+
update({ editingMessageId: messageId });
|
|
658
|
+
setDraftOf(key, content);
|
|
659
|
+
}),
|
|
660
|
+
cancelEditing: () => {
|
|
661
|
+
if (!state.editingMessageId) return;
|
|
662
|
+
const restore = preEdit;
|
|
663
|
+
preEdit = NO_EDIT;
|
|
664
|
+
batch(() => {
|
|
665
|
+
update({ editingMessageId: null, cursor: restore.cursor });
|
|
666
|
+
setDraftOf(state.conversationId ?? "new", restore.draft);
|
|
667
|
+
});
|
|
668
|
+
},
|
|
669
|
+
completeEditing: () => {
|
|
670
|
+
preEdit = NO_EDIT;
|
|
671
|
+
if (state.editingMessageId) update({ editingMessageId: null });
|
|
672
|
+
},
|
|
673
|
+
setDeepLinkMessageId: (deepLinkMessageId) => {
|
|
674
|
+
if (state.deepLinkMessageId !== deepLinkMessageId) update({ deepLinkMessageId });
|
|
675
|
+
},
|
|
676
|
+
setActiveCitationKey: (activeCitationKey) => {
|
|
677
|
+
if (state.activeCitationKey !== activeCitationKey) update({ activeCitationKey });
|
|
678
|
+
},
|
|
679
|
+
setPendingTools: (pendingTools) => update({ pendingTools }),
|
|
680
|
+
setAutoSendDraft: (autoSendDraft) => {
|
|
681
|
+
if (state.autoSendDraft !== autoSendDraft) update({ autoSendDraft });
|
|
682
|
+
},
|
|
683
|
+
setAgentChatOwner: (conversationId, agentId) => {
|
|
684
|
+
const signedIn = user();
|
|
685
|
+
ownersUser = signedIn ?? ownersUser;
|
|
686
|
+
update({ agentChatOwners: { ...state.agentChatOwners, [conversationId]: agentId } });
|
|
687
|
+
},
|
|
688
|
+
setMessageTimer: (key, timer) => update({ messageTimers: { ...state.messageTimers, [key]: timer } }),
|
|
689
|
+
clearMessageTimer: (key) => {
|
|
690
|
+
if (!(key in state.messageTimers)) return;
|
|
691
|
+
const messageTimers = { ...state.messageTimers };
|
|
692
|
+
delete messageTimers[key];
|
|
693
|
+
update({ messageTimers });
|
|
694
|
+
},
|
|
695
|
+
startNewConversation: () => batch(() => {
|
|
696
|
+
preEdit = NO_EDIT;
|
|
697
|
+
update({
|
|
698
|
+
conversationId: null,
|
|
699
|
+
cursor: null,
|
|
700
|
+
editingMessageId: null,
|
|
701
|
+
chatModel: void 0,
|
|
702
|
+
chatModelName: void 0,
|
|
703
|
+
newConversationAssistantMode: "always",
|
|
704
|
+
paMode: false,
|
|
705
|
+
newChatDocumentId: null
|
|
706
|
+
});
|
|
707
|
+
})
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/session/layout.ts
|
|
712
|
+
var PANEL_MODES = ["main", "docked", "overlay", "popout"];
|
|
713
|
+
var defaultPinned = () => typeof window === "undefined" || window.innerWidth >= 640;
|
|
714
|
+
function defaultLayoutState() {
|
|
715
|
+
return {
|
|
716
|
+
shell: {
|
|
717
|
+
page: "home",
|
|
718
|
+
sidebar: { pinned: defaultPinned(), hovering: false, drawerOpen: false },
|
|
719
|
+
panels: { main: null, docked: null, overlay: null, popout: null, modes: {}, sizes: {} },
|
|
720
|
+
focusedArea: "main"
|
|
721
|
+
},
|
|
722
|
+
tabs: {
|
|
723
|
+
threads: [],
|
|
724
|
+
activeThread: null,
|
|
725
|
+
threadScrollRestore: null,
|
|
726
|
+
dm: [],
|
|
727
|
+
activeDm: null,
|
|
728
|
+
dmMessageId: null,
|
|
729
|
+
projects: [],
|
|
730
|
+
activeProject: null
|
|
731
|
+
},
|
|
732
|
+
views: { tabs: {}, filters: {}, values: {}, transient: {} },
|
|
733
|
+
grid: { states: {}, rowHeights: {}, labelWidths: {}, savedViews: {} }
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
737
|
+
var recordOf2 = (value, keep) => isRecord3(value) ? Object.fromEntries(Object.entries(value).filter(([, v]) => keep(v))) : {};
|
|
738
|
+
var isString2 = (v) => typeof v === "string";
|
|
739
|
+
var isNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
740
|
+
var isAny = (v) => v !== void 0;
|
|
741
|
+
var isMode = (v) => PANEL_MODES.includes(v);
|
|
742
|
+
var isSize = (v) => isRecord3(v) && isNumber(v.width) && isNumber(v.height);
|
|
743
|
+
var isList = (v) => Array.isArray(v);
|
|
744
|
+
var stringOrNull3 = (v) => isString2(v) ? v : null;
|
|
745
|
+
function parseSavedLayout(saved) {
|
|
746
|
+
if (!isRecord3(saved) || saved.version !== 1) return null;
|
|
747
|
+
const shell = isRecord3(saved.shell) ? saved.shell : {};
|
|
748
|
+
const panels = isRecord3(shell.panels) ? shell.panels : {};
|
|
749
|
+
const tabs = isRecord3(saved.tabs) ? saved.tabs : {};
|
|
750
|
+
const views = isRecord3(saved.views) ? saved.views : {};
|
|
751
|
+
const grid = isRecord3(saved.grid) ? saved.grid : {};
|
|
752
|
+
const defaults = defaultLayoutState();
|
|
753
|
+
return {
|
|
754
|
+
version: 1,
|
|
755
|
+
migrated: isList(saved.migrated) ? saved.migrated.filter(isString2) : [],
|
|
756
|
+
shell: {
|
|
757
|
+
page: isString2(shell.page) ? shell.page : defaults.shell.page,
|
|
758
|
+
sidebarPinned: typeof shell.sidebarPinned === "boolean" ? shell.sidebarPinned : defaults.shell.sidebar.pinned,
|
|
759
|
+
panels: {
|
|
760
|
+
main: stringOrNull3(panels.main),
|
|
761
|
+
docked: stringOrNull3(panels.docked),
|
|
762
|
+
modes: recordOf2(panels.modes, isMode),
|
|
763
|
+
sizes: recordOf2(panels.sizes, isSize)
|
|
764
|
+
}
|
|
765
|
+
},
|
|
766
|
+
tabs: parseSavedThreadTabs(tabs.threads, tabs.activeThread),
|
|
767
|
+
views: {
|
|
768
|
+
tabs: recordOf2(views.tabs, isString2),
|
|
769
|
+
filters: recordOf2(views.filters, isAny),
|
|
770
|
+
values: recordOf2(views.values, isAny)
|
|
771
|
+
},
|
|
772
|
+
grid: {
|
|
773
|
+
states: recordOf2(grid.states, isRecord3),
|
|
774
|
+
rowHeights: recordOf2(grid.rowHeights, isNumber),
|
|
775
|
+
labelWidths: recordOf2(grid.labelWidths, isNumber),
|
|
776
|
+
savedViews: recordOf2(grid.savedViews, isList)
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
function savedLayoutOf(layout, migrated) {
|
|
781
|
+
const { shell, tabs, views, grid } = layout;
|
|
782
|
+
return {
|
|
783
|
+
version: 1,
|
|
784
|
+
migrated,
|
|
785
|
+
shell: {
|
|
786
|
+
page: shell.page,
|
|
787
|
+
sidebarPinned: shell.sidebar.pinned,
|
|
788
|
+
// A panel over the page or in its own window does not come back after a reload.
|
|
789
|
+
panels: {
|
|
790
|
+
main: shell.panels.main,
|
|
791
|
+
docked: shell.panels.docked,
|
|
792
|
+
modes: shell.panels.modes,
|
|
793
|
+
sizes: shell.panels.sizes
|
|
794
|
+
}
|
|
795
|
+
},
|
|
796
|
+
tabs: { threads: savedThreadTabs(tabs.threads), activeThread: tabs.activeThread },
|
|
797
|
+
views: { tabs: views.tabs, filters: views.filters, values: views.values },
|
|
798
|
+
grid
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
function restoreLayout(current, saved) {
|
|
802
|
+
return {
|
|
803
|
+
...current,
|
|
804
|
+
shell: {
|
|
805
|
+
...current.shell,
|
|
806
|
+
page: saved.shell.page,
|
|
807
|
+
sidebar: { ...current.shell.sidebar, pinned: saved.shell.sidebarPinned },
|
|
808
|
+
panels: { ...current.shell.panels, ...saved.shell.panels }
|
|
809
|
+
},
|
|
810
|
+
tabs: { ...current.tabs, ...saved.tabs },
|
|
811
|
+
views: { ...saved.views, transient: current.views.transient },
|
|
812
|
+
grid: saved.grid
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
function openPanelIn(panels, panelId, mode) {
|
|
816
|
+
const leave = (slot) => slot === panelId ? null : slot;
|
|
817
|
+
const next = {
|
|
818
|
+
...panels,
|
|
819
|
+
main: leave(panels.main),
|
|
820
|
+
docked: leave(panels.docked),
|
|
821
|
+
overlay: leave(panels.overlay),
|
|
822
|
+
popout: leave(panels.popout),
|
|
823
|
+
modes: { ...panels.modes, [panelId]: mode }
|
|
824
|
+
};
|
|
825
|
+
if (mode === "docked") return { ...next, docked: panelId, overlay: null };
|
|
826
|
+
if (mode === "overlay") return { ...next, overlay: panelId, docked: null };
|
|
827
|
+
return { ...next, [mode]: panelId };
|
|
828
|
+
}
|
|
829
|
+
function closePanelIn(panels, panelId) {
|
|
830
|
+
const leave = (slot) => slot === panelId ? null : slot;
|
|
831
|
+
return {
|
|
832
|
+
...panels,
|
|
833
|
+
main: leave(panels.main),
|
|
834
|
+
docked: leave(panels.docked),
|
|
835
|
+
overlay: leave(panels.overlay),
|
|
836
|
+
popout: leave(panels.popout)
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
function panelPlace(panels, panelId) {
|
|
840
|
+
return PANEL_MODES.find((mode) => panels[mode] === panelId) ?? null;
|
|
841
|
+
}
|
|
842
|
+
var isPanelOpen = (state, panelId) => panelPlace(state.shell.panels, panelId) !== null;
|
|
843
|
+
var dmTabId = (tab) => tab.recipientId ?? "new";
|
|
844
|
+
function createArbiLayoutStore({
|
|
845
|
+
persist,
|
|
846
|
+
initial
|
|
847
|
+
} = {}) {
|
|
848
|
+
let layout = { ...defaultLayoutState(), ...initial };
|
|
849
|
+
let migrated = [];
|
|
850
|
+
let isHydrated = !persist;
|
|
851
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
852
|
+
const save = () => {
|
|
853
|
+
if (persist && isHydrated) persist.save(savedLayoutOf(layout, migrated));
|
|
854
|
+
};
|
|
855
|
+
const update = (next) => {
|
|
856
|
+
if (next === layout) return;
|
|
857
|
+
const persisted = next.tabs.threads !== layout.tabs.threads || next.tabs.activeThread !== layout.tabs.activeThread || next.views.tabs !== layout.views.tabs || next.views.filters !== layout.views.filters || next.views.values !== layout.views.values || next.grid !== layout.grid || next.shell.page !== layout.shell.page || next.shell.sidebar.pinned !== layout.shell.sidebar.pinned || next.shell.panels !== layout.shell.panels;
|
|
858
|
+
layout = next;
|
|
859
|
+
listeners.forEach((listener) => listener());
|
|
860
|
+
if (persisted) save();
|
|
861
|
+
};
|
|
862
|
+
const shell = (change) => update({ ...layout, shell: { ...layout.shell, ...change } });
|
|
863
|
+
const tabs = (change) => update({ ...layout, tabs: { ...layout.tabs, ...change } });
|
|
864
|
+
const views = (change) => update({ ...layout, views: { ...layout.views, ...change } });
|
|
865
|
+
const grid = (change) => update({ ...layout, grid: { ...layout.grid, ...change } });
|
|
866
|
+
const hydration = persist ? persist.load().then(
|
|
867
|
+
(saved) => {
|
|
868
|
+
const parsed = parseSavedLayout(saved);
|
|
869
|
+
if (parsed) {
|
|
870
|
+
migrated = parsed.migrated;
|
|
871
|
+
layout = restoreLayout(layout, parsed);
|
|
872
|
+
}
|
|
873
|
+
isHydrated = true;
|
|
874
|
+
listeners.forEach((listener) => listener());
|
|
875
|
+
},
|
|
876
|
+
() => {
|
|
877
|
+
isHydrated = true;
|
|
878
|
+
listeners.forEach((listener) => listener());
|
|
879
|
+
}
|
|
880
|
+
) : Promise.resolve();
|
|
881
|
+
return {
|
|
306
882
|
getLayout: () => layout,
|
|
307
883
|
reset: () => update(defaultLayoutState()),
|
|
308
884
|
subscribe: (listener) => {
|
|
@@ -336,10 +912,6 @@ function createArbiLayoutStore({
|
|
|
336
912
|
if (layout.tabs.threads.length > 0 || layout.tabs.activeThread !== null)
|
|
337
913
|
tabs({ threads: [], activeThread: null });
|
|
338
914
|
},
|
|
339
|
-
restoreDrafts: (drafts) => {
|
|
340
|
-
const threads = withThreadDrafts(layout.tabs.threads, drafts);
|
|
341
|
-
if (threads !== layout.tabs.threads) tabs({ threads });
|
|
342
|
-
},
|
|
343
915
|
consumeScrollRestore: () => {
|
|
344
916
|
const value = layout.tabs.threadScrollRestore;
|
|
345
917
|
if (value !== null) tabs({ threadScrollRestore: null });
|
|
@@ -522,135 +1094,27 @@ function readWorkspaceLastOpened(lastOpenedKey = WORKSPACE_LAST_OPENED_KEY) {
|
|
|
522
1094
|
} catch {
|
|
523
1095
|
return {};
|
|
524
1096
|
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// src/
|
|
528
|
-
var
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
var queuedMessages = {
|
|
547
|
-
add: (q, responseId, message) => ({
|
|
548
|
-
...q,
|
|
549
|
-
[responseId]: [...q[responseId] ?? [], message]
|
|
550
|
-
}),
|
|
551
|
-
acknowledge: (q, clientId, serverId) => mapQueues(
|
|
552
|
-
q,
|
|
553
|
-
(messages) => messages.map((m) => m.clientId === clientId ? { ...m, serverId, acknowledged: true } : m)
|
|
554
|
-
),
|
|
555
|
-
remove: (q, clientId) => mapQueues(q, (messages) => messages.filter((m) => m.clientId !== clientId)),
|
|
556
|
-
removeByServerId: (q, serverId) => mapQueues(q, (messages) => messages.filter((m) => m.serverId !== serverId)),
|
|
557
|
-
removeByContent: (q, responseId, content) => {
|
|
558
|
-
let removed = false;
|
|
559
|
-
return {
|
|
560
|
-
...q,
|
|
561
|
-
[responseId]: (q[responseId] ?? []).filter((m) => {
|
|
562
|
-
if (!removed && m.content === content) {
|
|
563
|
-
removed = true;
|
|
564
|
-
return false;
|
|
565
|
-
}
|
|
566
|
-
return true;
|
|
567
|
-
})
|
|
568
|
-
};
|
|
569
|
-
},
|
|
570
|
-
/** Take out the prompts a steering interjection delivered into the run. */
|
|
571
|
-
consume: (q, responseId, combinedContent) => {
|
|
572
|
-
const messages = q[responseId] ?? [];
|
|
573
|
-
const consumed = findConsumedMessages(messages, combinedContent);
|
|
574
|
-
return { ...q, [responseId]: messages.filter((m) => !consumed.has(m.clientId)) };
|
|
575
|
-
},
|
|
576
|
-
clear: (q, responseId) => ({ ...q, [responseId]: [] }),
|
|
577
|
-
/** Move the entries still waiting on a finished turn to the turn that is now running. */
|
|
578
|
-
move: (q, fromResponseId, toResponseId) => {
|
|
579
|
-
const waiting = q[fromResponseId] ?? [];
|
|
580
|
-
if (fromResponseId === toResponseId || waiting.length === 0) return q;
|
|
581
|
-
return { ...q, [fromResponseId]: [], [toResponseId]: [...q[toResponseId] ?? [], ...waiting] };
|
|
582
|
-
},
|
|
583
|
-
/** Find a queued follow-up by its client id, under whichever turn holds it. */
|
|
584
|
-
find: (q, clientId) => Object.values(q).flat().find((m) => m.clientId === clientId)
|
|
585
|
-
};
|
|
586
|
-
var without = (record, key) => {
|
|
587
|
-
const next = { ...record };
|
|
588
|
-
delete next[key];
|
|
589
|
-
return next;
|
|
590
|
-
};
|
|
591
|
-
var agentControl = {
|
|
592
|
-
setState: (c, id, state) => ({
|
|
593
|
-
...c,
|
|
594
|
-
agentControlStates: { ...c.agentControlStates, [id]: state }
|
|
595
|
-
}),
|
|
596
|
-
/** A probe answered: the latest answer, and nothing pending any more. */
|
|
597
|
-
setProbeAnswer: (c, id, probe) => ({
|
|
598
|
-
...c,
|
|
599
|
-
agentProbeAnswers: { ...c.agentProbeAnswers, [id]: probe },
|
|
600
|
-
agentProbePending: { ...c.agentProbePending, [id]: false }
|
|
601
|
-
}),
|
|
602
|
-
setProbePending: (c, id, pending) => ({
|
|
603
|
-
...c,
|
|
604
|
-
agentProbePending: { ...c.agentProbePending, [id]: pending }
|
|
605
|
-
}),
|
|
606
|
-
/** The turn is over: drop its control state. */
|
|
607
|
-
clear: (c, id) => ({
|
|
608
|
-
agentControlStates: without(c.agentControlStates, id),
|
|
609
|
-
agentProbeAnswers: without(c.agentProbeAnswers, id),
|
|
610
|
-
agentProbePending: without(c.agentProbePending, id)
|
|
611
|
-
})
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
// src/session/store.ts
|
|
615
|
-
var DEFAULT_DOCUMENT_VIEWER_STATE = {
|
|
616
|
-
tab: "pdf",
|
|
617
|
-
compareId: null,
|
|
618
|
-
tabRequest: null,
|
|
619
|
-
textScale: 1,
|
|
620
|
-
phonePdfChosen: false
|
|
621
|
-
};
|
|
622
|
-
var EMPTY_SESSION_STATE = {
|
|
623
|
-
workspaceId: null,
|
|
624
|
-
workspaceStatus: "closed",
|
|
625
|
-
conversationId: null,
|
|
626
|
-
viewingDocument: null,
|
|
627
|
-
documentViewer: DEFAULT_DOCUMENT_VIEWER_STATE,
|
|
628
|
-
selectedDocumentIds: [],
|
|
629
|
-
generating: {},
|
|
630
|
-
paMode: false,
|
|
631
|
-
conversationDocuments: {},
|
|
632
|
-
conversationDocumentAutoAdd: {},
|
|
633
|
-
conversationFacts: {},
|
|
634
|
-
pinnedDocumentId: null,
|
|
635
|
-
cursor: null,
|
|
636
|
-
suggestedQuery: null,
|
|
637
|
-
queuedMessages: {},
|
|
638
|
-
agentControlStates: {},
|
|
639
|
-
agentProbeAnswers: {},
|
|
640
|
-
agentProbePending: {},
|
|
641
|
-
drafts: {},
|
|
642
|
-
editingMessageId: null,
|
|
643
|
-
deepLinkMessageId: null,
|
|
644
|
-
activeCitationKey: null,
|
|
645
|
-
chatModel: void 0,
|
|
646
|
-
chatModelName: void 0,
|
|
647
|
-
newConversationAssistantMode: "always",
|
|
648
|
-
pendingTools: null,
|
|
649
|
-
autoSendDraft: false,
|
|
650
|
-
agentChatOwners: {},
|
|
651
|
-
newAgentChat: null,
|
|
652
|
-
newChatDocumentId: null,
|
|
653
|
-
messageTimers: {},
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// src/session/store.ts
|
|
1100
|
+
var DEFAULT_DOCUMENT_VIEWER_STATE = {
|
|
1101
|
+
tab: "pdf",
|
|
1102
|
+
compareId: null,
|
|
1103
|
+
tabRequest: null,
|
|
1104
|
+
textScale: 1,
|
|
1105
|
+
phonePdfChosen: false
|
|
1106
|
+
};
|
|
1107
|
+
var EMPTY_DOCUMENT_STATE = {
|
|
1108
|
+
viewingDocument: null,
|
|
1109
|
+
documentViewer: DEFAULT_DOCUMENT_VIEWER_STATE,
|
|
1110
|
+
selectedDocumentIds: [],
|
|
1111
|
+
pinnedDocumentId: null
|
|
1112
|
+
};
|
|
1113
|
+
var EMPTY_SESSION_STATE = {
|
|
1114
|
+
workspaceId: null,
|
|
1115
|
+
workspaceStatus: "closed",
|
|
1116
|
+
...EMPTY_CHAT_STATE,
|
|
1117
|
+
...EMPTY_DOCUMENT_STATE,
|
|
654
1118
|
...defaultLayoutState()
|
|
655
1119
|
};
|
|
656
1120
|
var selectViewingDocumentId = (state) => state.viewingDocument ? chunkZWFMFOGP_cjs.parseVersionRef(state.viewingDocument.id).docId : null;
|
|
@@ -666,155 +1130,19 @@ function nextViewingDocument(current, input) {
|
|
|
666
1130
|
passageId: input.passageId !== void 0 ? input.passageId : current?.passageId ?? null
|
|
667
1131
|
};
|
|
668
1132
|
}
|
|
669
|
-
function
|
|
670
|
-
|
|
671
|
-
delete actions.getLayout;
|
|
672
|
-
delete actions.subscribe;
|
|
673
|
-
delete actions.hydrated;
|
|
674
|
-
delete actions.isHydrated;
|
|
675
|
-
delete actions.reset;
|
|
676
|
-
delete actions.threads;
|
|
677
|
-
return actions;
|
|
678
|
-
}
|
|
679
|
-
function withoutNew(record) {
|
|
680
|
-
const { new: _new, ...saved } = record;
|
|
681
|
-
return saved;
|
|
682
|
-
}
|
|
683
|
-
function createArbiSessionStore(initial = {}, {
|
|
684
|
-
draftStorage,
|
|
685
|
-
persist,
|
|
686
|
-
workspace = createArbiWorkspaceStore({ workspaceId: initial.workspaceId ?? null })
|
|
687
|
-
} = {}) {
|
|
688
|
-
const { shell, tabs, views, grid, ...rest } = initial;
|
|
689
|
-
const layout = createArbiLayoutStore({
|
|
690
|
-
persist,
|
|
691
|
-
initial: Object.fromEntries(
|
|
692
|
-
Object.entries({ shell, tabs, views, grid }).filter(([, part]) => part !== void 0)
|
|
693
|
-
)
|
|
694
|
-
});
|
|
695
|
-
if (initial.workspaceStatus) workspace.setWorkspaceStatus(initial.workspaceStatus);
|
|
696
|
-
let state = {
|
|
697
|
-
...EMPTY_SESSION_STATE,
|
|
698
|
-
...rest,
|
|
699
|
-
...layout.getLayout(),
|
|
700
|
-
...workspace.getState()
|
|
701
|
-
};
|
|
1133
|
+
function createArbiDocumentSessionStore(initial = {}) {
|
|
1134
|
+
let state = { ...EMPTY_DOCUMENT_STATE, ...initial };
|
|
702
1135
|
const listeners = /* @__PURE__ */ new Set();
|
|
703
1136
|
const update = (patch) => {
|
|
704
1137
|
state = { ...state, ...patch };
|
|
705
1138
|
listeners.forEach((listener) => listener());
|
|
706
1139
|
};
|
|
707
|
-
layout.subscribe(() => update(layout.getLayout()));
|
|
708
|
-
workspace.subscribe(() => update(workspace.getState()));
|
|
709
|
-
let preEdit = { draft: "", cursor: null };
|
|
710
|
-
let draftsRestored = !draftStorage;
|
|
711
|
-
let draftsOwner = draftStorage?.owner() ?? null;
|
|
712
|
-
const restoreDrafts = () => {
|
|
713
|
-
if (!draftStorage) return;
|
|
714
|
-
const owner = draftStorage.owner();
|
|
715
|
-
if (owner !== draftsOwner) {
|
|
716
|
-
if (draftsOwner !== null) update({ drafts: {} });
|
|
717
|
-
draftsOwner = owner;
|
|
718
|
-
draftsRestored = false;
|
|
719
|
-
}
|
|
720
|
-
if (draftsRestored) return;
|
|
721
|
-
draftsRestored = true;
|
|
722
|
-
void draftStorage.load().then(
|
|
723
|
-
(saved) => {
|
|
724
|
-
if (saved && draftStorage.owner() === owner)
|
|
725
|
-
update({ drafts: { ...saved, ...state.drafts } });
|
|
726
|
-
else draftsRestored = false;
|
|
727
|
-
},
|
|
728
|
-
() => {
|
|
729
|
-
draftsRestored = false;
|
|
730
|
-
}
|
|
731
|
-
);
|
|
732
|
-
};
|
|
733
|
-
draftStorage?.onClear(() => {
|
|
734
|
-
draftsRestored = false;
|
|
735
|
-
draftsOwner = null;
|
|
736
|
-
preEdit = { draft: "", cursor: null };
|
|
737
|
-
if (Object.keys(state.drafts).length > 0) update({ drafts: {} });
|
|
738
|
-
});
|
|
739
|
-
restoreDrafts();
|
|
740
|
-
const setDrafts = (drafts) => {
|
|
741
|
-
update({ drafts });
|
|
742
|
-
draftStorage?.save(drafts);
|
|
743
|
-
};
|
|
744
|
-
const setDraftOf = (key, text) => {
|
|
745
|
-
if ((state.drafts[key] ?? "") === text) return;
|
|
746
|
-
const drafts = { ...state.drafts };
|
|
747
|
-
if (text) drafts[key] = text;
|
|
748
|
-
else delete drafts[key];
|
|
749
|
-
setDrafts(drafts);
|
|
750
|
-
};
|
|
751
|
-
const composer = () => ({
|
|
752
|
-
conversationId: state.conversationId,
|
|
753
|
-
view: {
|
|
754
|
-
cursor: state.cursor,
|
|
755
|
-
draft: selectDraft(state),
|
|
756
|
-
editingMessageId: state.editingMessageId,
|
|
757
|
-
preEditDraft: state.editingMessageId ? preEdit.draft : null,
|
|
758
|
-
preEditCursor: state.editingMessageId ? preEdit.cursor : null,
|
|
759
|
-
chatModel: state.chatModel,
|
|
760
|
-
chatModelName: state.chatModelName,
|
|
761
|
-
scrollTop: null
|
|
762
|
-
}
|
|
763
|
-
});
|
|
764
|
-
const show = (next) => {
|
|
765
|
-
if (next === null) return;
|
|
766
|
-
if (next === "list") {
|
|
767
|
-
preEdit = { draft: "", cursor: null };
|
|
768
|
-
return update({ conversationId: null, cursor: null, editingMessageId: null });
|
|
769
|
-
}
|
|
770
|
-
const view = next.view;
|
|
771
|
-
preEdit = { draft: view?.preEditDraft ?? "", cursor: view?.preEditCursor ?? null };
|
|
772
|
-
update({
|
|
773
|
-
conversationId: next.conversationId,
|
|
774
|
-
cursor: view?.cursor ?? null,
|
|
775
|
-
editingMessageId: view?.editingMessageId ?? null,
|
|
776
|
-
chatModel: view?.chatModel,
|
|
777
|
-
chatModelName: view?.chatModelName
|
|
778
|
-
});
|
|
779
|
-
};
|
|
780
1140
|
return {
|
|
781
|
-
...layoutActionsOf(layout),
|
|
782
|
-
layoutHydrated: () => layout.hydrated(),
|
|
783
|
-
isLayoutHydrated: () => layout.isHydrated(),
|
|
784
1141
|
getState: () => state,
|
|
785
1142
|
subscribe: (listener) => {
|
|
786
1143
|
listeners.add(listener);
|
|
787
1144
|
return () => listeners.delete(listener);
|
|
788
1145
|
},
|
|
789
|
-
setWorkspaceId: (workspaceId) => {
|
|
790
|
-
workspace.setWorkspaceId(workspaceId);
|
|
791
|
-
restoreDrafts();
|
|
792
|
-
},
|
|
793
|
-
setWorkspaceStatus: (workspaceStatus) => workspace.setWorkspaceStatus(workspaceStatus),
|
|
794
|
-
leaveWorkspace: () => {
|
|
795
|
-
Object.values(state.generating).forEach((generation) => generation.abortController?.abort());
|
|
796
|
-
workspace.setWorkspaceStatus("closed");
|
|
797
|
-
update({
|
|
798
|
-
conversationId: null,
|
|
799
|
-
cursor: null,
|
|
800
|
-
viewingDocument: null,
|
|
801
|
-
selectedDocumentIds: [],
|
|
802
|
-
generating: {},
|
|
803
|
-
// Saved conversations keep their documents; the unsent new chat's belong to this workspace.
|
|
804
|
-
conversationDocuments: withoutNew(state.conversationDocuments),
|
|
805
|
-
conversationDocumentAutoAdd: withoutNew(state.conversationDocumentAutoAdd),
|
|
806
|
-
conversationFacts: {},
|
|
807
|
-
pinnedDocumentId: null,
|
|
808
|
-
newAgentChat: null,
|
|
809
|
-
newChatDocumentId: null,
|
|
810
|
-
messageTimers: {}
|
|
811
|
-
});
|
|
812
|
-
layout.threads.clear();
|
|
813
|
-
},
|
|
814
|
-
setConversationId: (conversationId) => {
|
|
815
|
-
if (state.conversationId !== conversationId)
|
|
816
|
-
update({ conversationId, cursor: null, editingMessageId: null });
|
|
817
|
-
},
|
|
818
1146
|
setViewingDocument: (input) => update({ viewingDocument: nextViewingDocument(state.viewingDocument, input) }),
|
|
819
1147
|
setDocumentViewer: (change) => {
|
|
820
1148
|
const next = { ...state.documentViewer, ...change };
|
|
@@ -823,209 +1151,117 @@ function createArbiSessionStore(initial = {}, {
|
|
|
823
1151
|
update({ documentViewer: next });
|
|
824
1152
|
},
|
|
825
1153
|
setSelectedDocumentIds: (ids) => update({ selectedDocumentIds: ids }),
|
|
826
|
-
setConversationGenerating: (conversationId, isGenerating, abortController = null) => {
|
|
827
|
-
const current = state.generating[conversationId];
|
|
828
|
-
update({
|
|
829
|
-
generating: {
|
|
830
|
-
...state.generating,
|
|
831
|
-
[conversationId]: {
|
|
832
|
-
...current,
|
|
833
|
-
isGenerating,
|
|
834
|
-
error: isGenerating ? null : current?.error ?? null,
|
|
835
|
-
startTime: isGenerating ? Date.now() : null,
|
|
836
|
-
streamingAssistantId: isGenerating ? current?.streamingAssistantId ?? null : null,
|
|
837
|
-
backendElapsed: isGenerating ? current?.backendElapsed ?? null : null,
|
|
838
|
-
abortController
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
});
|
|
842
|
-
},
|
|
843
|
-
setStreamingAssistantId: (conversationId, streamingAssistantId, startTime = Date.now()) => {
|
|
844
|
-
const current = state.generating[conversationId];
|
|
845
|
-
update({
|
|
846
|
-
generating: {
|
|
847
|
-
...state.generating,
|
|
848
|
-
[conversationId]: {
|
|
849
|
-
...current ?? { isGenerating: false, error: null },
|
|
850
|
-
streamingAssistantId,
|
|
851
|
-
startTime
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
});
|
|
855
|
-
},
|
|
856
|
-
setBackendElapsed: (conversationId, backendElapsed) => {
|
|
857
|
-
const current = state.generating[conversationId] ?? {
|
|
858
|
-
isGenerating: false,
|
|
859
|
-
error: null,
|
|
860
|
-
startTime: null
|
|
861
|
-
};
|
|
862
|
-
update({
|
|
863
|
-
generating: { ...state.generating, [conversationId]: { ...current, backendElapsed } }
|
|
864
|
-
});
|
|
865
|
-
},
|
|
866
|
-
moveConversation: (fromId, toId) => {
|
|
867
|
-
const move = (record) => {
|
|
868
|
-
const next = { ...record };
|
|
869
|
-
if (fromId in next) {
|
|
870
|
-
next[toId] = next[fromId];
|
|
871
|
-
delete next[fromId];
|
|
872
|
-
}
|
|
873
|
-
return next;
|
|
874
|
-
};
|
|
875
|
-
update({
|
|
876
|
-
generating: move(state.generating),
|
|
877
|
-
conversationDocuments: move(state.conversationDocuments),
|
|
878
|
-
conversationDocumentAutoAdd: move(state.conversationDocumentAutoAdd),
|
|
879
|
-
conversationFacts: move(state.conversationFacts),
|
|
880
|
-
conversationId: state.conversationId ?? toId
|
|
881
|
-
});
|
|
882
|
-
layout.threads.rename(fromId, toId);
|
|
883
|
-
if (fromId in state.drafts) setDrafts(move(state.drafts));
|
|
884
|
-
},
|
|
885
|
-
setConversationFacts: (conversationId, factIds) => update({ conversationFacts: { ...state.conversationFacts, [conversationId]: factIds } }),
|
|
886
1154
|
setPinnedDocumentId: (pinnedDocumentId) => update({ pinnedDocumentId }),
|
|
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
|
-
|
|
953
|
-
},
|
|
954
|
-
setActiveCitationKey: (activeCitationKey) => {
|
|
955
|
-
if (state.activeCitationKey !== activeCitationKey) update({ activeCitationKey });
|
|
956
|
-
},
|
|
957
|
-
setPendingTools: (pendingTools) => update({ pendingTools }),
|
|
958
|
-
setAutoSendDraft: (autoSendDraft) => {
|
|
959
|
-
if (state.autoSendDraft !== autoSendDraft) update({ autoSendDraft });
|
|
960
|
-
},
|
|
961
|
-
setAgentChatOwner: (conversationId, agentId) => update({ agentChatOwners: { ...state.agentChatOwners, [conversationId]: agentId } }),
|
|
962
|
-
setMessageTimer: (key, timer) => update({ messageTimers: { ...state.messageTimers, [key]: timer } }),
|
|
963
|
-
clearMessageTimer: (key) => {
|
|
964
|
-
if (!(key in state.messageTimers)) return;
|
|
965
|
-
const messageTimers = { ...state.messageTimers };
|
|
966
|
-
delete messageTimers[key];
|
|
967
|
-
update({ messageTimers });
|
|
968
|
-
},
|
|
969
|
-
setConversationError: (conversationId, error, lastFailedMessage) => {
|
|
970
|
-
const current = state.generating[conversationId];
|
|
971
|
-
update({
|
|
972
|
-
generating: {
|
|
973
|
-
...state.generating,
|
|
974
|
-
[conversationId]: {
|
|
975
|
-
...current,
|
|
976
|
-
isGenerating: false,
|
|
977
|
-
error,
|
|
978
|
-
startTime: current?.startTime ?? null,
|
|
979
|
-
lastFailedMessage: lastFailedMessage ?? current?.lastFailedMessage ?? null
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
});
|
|
983
|
-
},
|
|
984
|
-
clearConversationError: (conversationId) => {
|
|
985
|
-
const current = state.generating[conversationId];
|
|
986
|
-
update({
|
|
987
|
-
generating: {
|
|
988
|
-
...state.generating,
|
|
989
|
-
[conversationId]: {
|
|
990
|
-
...current ?? { isGenerating: false, startTime: null },
|
|
991
|
-
error: null,
|
|
992
|
-
lastFailedMessage: null
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
});
|
|
996
|
-
},
|
|
997
|
-
abortConversationGeneration: (conversationId) => {
|
|
998
|
-
const current = state.generating[conversationId];
|
|
999
|
-
current?.abortController?.abort();
|
|
1000
|
-
update({
|
|
1001
|
-
generating: {
|
|
1002
|
-
...state.generating,
|
|
1003
|
-
[conversationId]: {
|
|
1004
|
-
...current ?? { error: null, startTime: null },
|
|
1005
|
-
isGenerating: false,
|
|
1006
|
-
abortController: null
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
});
|
|
1155
|
+
leave: () => update({ viewingDocument: null, selectedDocumentIds: [], pinnedDocumentId: null })
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function layoutActionsOf(layout) {
|
|
1159
|
+
const actions = { ...layout };
|
|
1160
|
+
delete actions.getLayout;
|
|
1161
|
+
delete actions.subscribe;
|
|
1162
|
+
delete actions.hydrated;
|
|
1163
|
+
delete actions.isHydrated;
|
|
1164
|
+
delete actions.reset;
|
|
1165
|
+
delete actions.threads;
|
|
1166
|
+
return actions;
|
|
1167
|
+
}
|
|
1168
|
+
function chatActionsOf(chat) {
|
|
1169
|
+
const actions = { ...chat };
|
|
1170
|
+
delete actions.getState;
|
|
1171
|
+
delete actions.subscribe;
|
|
1172
|
+
delete actions.settled;
|
|
1173
|
+
delete actions.forget;
|
|
1174
|
+
delete actions.batch;
|
|
1175
|
+
delete actions.hydrated;
|
|
1176
|
+
delete actions.isHydrated;
|
|
1177
|
+
delete actions.restoreDrafts;
|
|
1178
|
+
delete actions.leave;
|
|
1179
|
+
delete actions.reset;
|
|
1180
|
+
return actions;
|
|
1181
|
+
}
|
|
1182
|
+
function composeArbiSessionStore({
|
|
1183
|
+
layout,
|
|
1184
|
+
workspace,
|
|
1185
|
+
chat,
|
|
1186
|
+
documents
|
|
1187
|
+
}) {
|
|
1188
|
+
let last = null;
|
|
1189
|
+
const getState = () => {
|
|
1190
|
+
const parts = [
|
|
1191
|
+
layout.getLayout(),
|
|
1192
|
+
workspace.getState(),
|
|
1193
|
+
documents.getState(),
|
|
1194
|
+
chat.getState()
|
|
1195
|
+
];
|
|
1196
|
+
if (last && parts.every((part, i) => part === last.parts[i])) return last.state;
|
|
1197
|
+
const [l, w, d, c] = parts;
|
|
1198
|
+
last = { parts, state: { ...l, ...w, ...d, ...c } };
|
|
1199
|
+
return last.state;
|
|
1200
|
+
};
|
|
1201
|
+
chat.restoreDrafts();
|
|
1202
|
+
workspace.subscribe(() => chat.restoreDrafts());
|
|
1203
|
+
return {
|
|
1204
|
+
...layoutActionsOf(layout),
|
|
1205
|
+
...chatActionsOf(chat),
|
|
1206
|
+
getState,
|
|
1207
|
+
layoutHydrated: () => layout.hydrated(),
|
|
1208
|
+
isLayoutHydrated: () => layout.isHydrated(),
|
|
1209
|
+
subscribe: (onChange) => {
|
|
1210
|
+
const told = chat.settled(onChange);
|
|
1211
|
+
const unsubscribe = [
|
|
1212
|
+
chat.subscribe(onChange),
|
|
1213
|
+
layout.subscribe(told),
|
|
1214
|
+
workspace.subscribe(told),
|
|
1215
|
+
documents.subscribe(told)
|
|
1216
|
+
];
|
|
1217
|
+
return () => {
|
|
1218
|
+
chat.forget(onChange);
|
|
1219
|
+
unsubscribe.forEach((stop) => stop());
|
|
1220
|
+
};
|
|
1010
1221
|
},
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1222
|
+
setWorkspaceId: (id) => workspace.setWorkspaceId(id),
|
|
1223
|
+
setWorkspaceStatus: (status) => workspace.setWorkspaceStatus(status),
|
|
1224
|
+
leaveWorkspace: () => chat.batch(() => {
|
|
1225
|
+
workspace.setWorkspaceStatus("closed");
|
|
1226
|
+
chat.leave();
|
|
1227
|
+
documents.leave();
|
|
1016
1228
|
}),
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
conversationDocuments: { ...state.conversationDocuments, [conversationId]: documents },
|
|
1022
|
-
conversationDocumentAutoAdd: {
|
|
1023
|
-
...state.conversationDocumentAutoAdd,
|
|
1024
|
-
[conversationId]: autoAdd
|
|
1025
|
-
}
|
|
1026
|
-
})
|
|
1229
|
+
setViewingDocument: (input) => documents.setViewingDocument(input),
|
|
1230
|
+
setDocumentViewer: (change) => documents.setDocumentViewer(change),
|
|
1231
|
+
setSelectedDocumentIds: (ids) => documents.setSelectedDocumentIds(ids),
|
|
1232
|
+
setPinnedDocumentId: (id) => documents.setPinnedDocumentId(id)
|
|
1027
1233
|
};
|
|
1028
1234
|
}
|
|
1235
|
+
function createArbiSessionStore(initial = {}, {
|
|
1236
|
+
draftStorage,
|
|
1237
|
+
persist,
|
|
1238
|
+
workspace = createArbiWorkspaceStore({ workspaceId: initial.workspaceId ?? null })
|
|
1239
|
+
} = {}) {
|
|
1240
|
+
const {
|
|
1241
|
+
shell,
|
|
1242
|
+
tabs,
|
|
1243
|
+
views,
|
|
1244
|
+
grid,
|
|
1245
|
+
workspaceStatus,
|
|
1246
|
+
viewingDocument,
|
|
1247
|
+
documentViewer,
|
|
1248
|
+
selectedDocumentIds,
|
|
1249
|
+
pinnedDocumentId,
|
|
1250
|
+
...chatInitial
|
|
1251
|
+
} = initial;
|
|
1252
|
+
delete chatInitial.workspaceId;
|
|
1253
|
+
const defined = (parts) => Object.fromEntries(Object.entries(parts).filter(([, part]) => part !== void 0));
|
|
1254
|
+
const layout = createArbiLayoutStore({ persist, initial: defined({ shell, tabs, views, grid }) });
|
|
1255
|
+
if (workspaceStatus) workspace.setWorkspaceStatus(workspaceStatus);
|
|
1256
|
+
return composeArbiSessionStore({
|
|
1257
|
+
layout,
|
|
1258
|
+
workspace,
|
|
1259
|
+
chat: createArbiChatStore({ layout, draftStorage, initial: chatInitial }),
|
|
1260
|
+
documents: createArbiDocumentSessionStore(
|
|
1261
|
+
defined({ viewingDocument, documentViewer, selectedDocumentIds, pinnedDocumentId })
|
|
1262
|
+
)
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1029
1265
|
var SAVE_DELAY_MS = 500;
|
|
1030
1266
|
var encoder = new TextEncoder();
|
|
1031
1267
|
var decoder = new TextDecoder();
|
|
@@ -1666,6 +1902,7 @@ function useUserUsageToday(options) {
|
|
|
1666
1902
|
}
|
|
1667
1903
|
|
|
1668
1904
|
exports.DEFAULT_DOCUMENT_VIEWER_STATE = DEFAULT_DOCUMENT_VIEWER_STATE;
|
|
1905
|
+
exports.EMPTY_CHAT_STATE = EMPTY_CHAT_STATE;
|
|
1669
1906
|
exports.EMPTY_SESSION_STATE = EMPTY_SESSION_STATE;
|
|
1670
1907
|
exports.MAX_THREAD_TABS = MAX_THREAD_TABS;
|
|
1671
1908
|
exports.MIN_QUEUED_MESSAGE_VISIBILITY_MS = MIN_QUEUED_MESSAGE_VISIBILITY_MS;
|
|
@@ -1676,7 +1913,10 @@ exports.agentControl = agentControl;
|
|
|
1676
1913
|
exports.browserWorkspaceStorage = browserWorkspaceStorage;
|
|
1677
1914
|
exports.clearSealedDrafts = clearSealedDrafts;
|
|
1678
1915
|
exports.closePanelIn = closePanelIn;
|
|
1916
|
+
exports.composeArbiSessionStore = composeArbiSessionStore;
|
|
1679
1917
|
exports.conversationKey = conversationKey;
|
|
1918
|
+
exports.createArbiChatStore = createArbiChatStore;
|
|
1919
|
+
exports.createArbiDocumentSessionStore = createArbiDocumentSessionStore;
|
|
1680
1920
|
exports.createArbiLayoutStore = createArbiLayoutStore;
|
|
1681
1921
|
exports.createArbiSessionStore = createArbiSessionStore;
|
|
1682
1922
|
exports.createArbiWorkspaceStore = createArbiWorkspaceStore;
|
|
@@ -1692,10 +1932,12 @@ exports.layoutActionsOf = layoutActionsOf;
|
|
|
1692
1932
|
exports.nextViewingDocument = nextViewingDocument;
|
|
1693
1933
|
exports.openPanelIn = openPanelIn;
|
|
1694
1934
|
exports.panelPlace = panelPlace;
|
|
1935
|
+
exports.parseSavedChat = parseSavedChat;
|
|
1695
1936
|
exports.parseSavedLayout = parseSavedLayout;
|
|
1696
1937
|
exports.queuedMessages = queuedMessages;
|
|
1697
1938
|
exports.readWorkspaceLastOpened = readWorkspaceLastOpened;
|
|
1698
1939
|
exports.restoreLayout = restoreLayout;
|
|
1940
|
+
exports.savedChatOf = savedChatOf;
|
|
1699
1941
|
exports.savedLayoutOf = savedLayoutOf;
|
|
1700
1942
|
exports.selectDraft = selectDraft;
|
|
1701
1943
|
exports.selectIsGenerating = selectIsGenerating;
|
|
@@ -1736,5 +1978,5 @@ exports.useTransientView = useTransientView;
|
|
|
1736
1978
|
exports.useUserUsageToday = useUserUsageToday;
|
|
1737
1979
|
exports.useWorkspaceActivity = useWorkspaceActivity;
|
|
1738
1980
|
exports.useWorkspaces = useWorkspaces;
|
|
1739
|
-
//# sourceMappingURL=chunk-
|
|
1740
|
-
//# sourceMappingURL=chunk-
|
|
1981
|
+
//# sourceMappingURL=chunk-BF7ABX3X.cjs.map
|
|
1982
|
+
//# sourceMappingURL=chunk-BF7ABX3X.cjs.map
|