@burdenoff/fe-libs 2026.904.2 → 2026.904.4

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.
Files changed (41) hide show
  1. package/dist/shared/assistant/attachments.d.ts +340 -0
  2. package/dist/shared/assistant/attachments.d.ts.map +1 -0
  3. package/dist/shared/assistant/attachments.js +370 -0
  4. package/dist/shared/assistant/conversation.d.ts +153 -0
  5. package/dist/shared/assistant/conversation.d.ts.map +1 -0
  6. package/dist/shared/assistant/documentText.d.ts +261 -0
  7. package/dist/shared/assistant/documentText.d.ts.map +1 -0
  8. package/dist/shared/assistant/documentText.js +235 -0
  9. package/dist/shared/assistant/gatewayLimits.d.ts +25 -0
  10. package/dist/shared/assistant/gatewayLimits.d.ts.map +1 -0
  11. package/dist/shared/assistant/gatewayLimits.js +4 -0
  12. package/dist/shared/assistant/index.d.ts +6 -0
  13. package/dist/shared/assistant/index.d.ts.map +1 -1
  14. package/dist/shared/assistant/store.d.ts +114 -0
  15. package/dist/shared/assistant/store.d.ts.map +1 -0
  16. package/dist/shared/assistant/store.js +198 -0
  17. package/dist/shared/assistant/turnState.d.ts +16 -0
  18. package/dist/shared/assistant/turnState.d.ts.map +1 -0
  19. package/dist/shared/assistant/turnState.js +6 -0
  20. package/dist/shared/config/authBridgeUrls.d.ts.map +1 -1
  21. package/dist/shared/config/authBridgeUrls.js +16 -15
  22. package/dist/shared/config/gatewayUrls.d.ts +29 -0
  23. package/dist/shared/config/gatewayUrls.d.ts.map +1 -1
  24. package/dist/shared/config/gatewayUrls.js +44 -20
  25. package/dist/shared/config/index.d.ts.map +1 -1
  26. package/dist/shared/native/capacitor.d.ts +49 -0
  27. package/dist/shared/native/capacitor.d.ts.map +1 -0
  28. package/dist/shared/native/capacitor.js +31 -0
  29. package/dist/shared/native/index.d.ts +2 -0
  30. package/dist/shared/native/index.d.ts.map +1 -1
  31. package/dist/shared/providers/shell/MultiGatewayProvider.d.ts.map +1 -1
  32. package/dist/shared/providers/shell/MultiGatewayProvider.js +101 -100
  33. package/dist/shared/utils/authCookie.d.ts.map +1 -1
  34. package/dist/shared/utils/authCookie.js +35 -34
  35. package/dist/shared/utils/gatewayFetchRewrite.d.ts.map +1 -1
  36. package/dist/shared/utils/gatewayFetchRewrite.js +95 -87
  37. package/dist/shared-assistant.js +6 -1
  38. package/dist/shared-config.js +9 -1
  39. package/dist/shared-native.js +23 -22
  40. package/dist/shared.js +124 -123
  41. package/package.json +2 -2
@@ -0,0 +1,370 @@
1
+ import { MAX_REQUEST_BODY_BYTES as e } from "./gatewayLimits.js";
2
+ import { formatPageRanges as t, isMostlyImagePages as n, truncatePagesForInline as r } from "./documentText.js";
3
+ //#region src/shared/assistant/attachments.ts
4
+ var i = 5, a = 10 * 1024 * 1024, o = 8e3, s = 2e4, c = class extends Error {
5
+ constructor(e) {
6
+ super(e), this.reason = e, this.name = "AttachmentRejectedError";
7
+ }
8
+ };
9
+ function l(e) {
10
+ return {
11
+ id: e.id,
12
+ filename: e.filename,
13
+ mimeType: e.mimeType,
14
+ size: e.size,
15
+ kind: e.kind,
16
+ fileId: e.fileId
17
+ };
18
+ }
19
+ var u = new Set([
20
+ "txt",
21
+ "md",
22
+ "markdown",
23
+ "csv",
24
+ "tsv",
25
+ "json",
26
+ "yaml",
27
+ "yml",
28
+ "xml",
29
+ "log",
30
+ "sql",
31
+ "html",
32
+ "css",
33
+ "js",
34
+ "ts",
35
+ "tsx",
36
+ "jsx",
37
+ "py",
38
+ "sh",
39
+ "ini",
40
+ "toml",
41
+ "conf"
42
+ ]);
43
+ function d(e) {
44
+ let t = e.lastIndexOf(".");
45
+ return t >= 0 ? e.slice(t + 1).toLowerCase() : "";
46
+ }
47
+ function f(e, t) {
48
+ return e.startsWith("image/") ? "image" : e.startsWith("text/") || e === "application/json" || e === "application/xml" || e === "application/x-yaml" || u.has(d(t)) ? "text" : "document";
49
+ }
50
+ var p = {
51
+ "application/pdf": "pdf",
52
+ "application/x-pdf": "pdf",
53
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
54
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx"
55
+ }, m = {
56
+ pdf: "pdf",
57
+ docx: "docx",
58
+ xlsx: "xlsx"
59
+ };
60
+ function h(e, t) {
61
+ return p[e.toLowerCase()] ?? m[d(t)] ?? null;
62
+ }
63
+ var g = [
64
+ "image/*",
65
+ "text/*",
66
+ ...[...u].map((e) => `.${e}`),
67
+ ...Object.keys(m).map((e) => `.${e}`)
68
+ ].join(",");
69
+ function _(e) {
70
+ return e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${(e / 1024).toFixed(1)} KB` : `${(e / (1024 * 1024)).toFixed(1)} MB`;
71
+ }
72
+ function v(e, t) {
73
+ return e > 0 || t.some((e) => e.status === "pending" || e.status === "uploading");
74
+ }
75
+ function y(e) {
76
+ return e.filter((e) => e.status === "ready");
77
+ }
78
+ function b(e, t) {
79
+ return v(e, t) ? "in-flight" : t.some((e) => e.status === "error") ? "failed" : null;
80
+ }
81
+ function x(e, t) {
82
+ return t >= 5 ? "too-many" : e.size === 0 ? "empty" : e.size > 10485760 && !e.type.startsWith("image/") ? "too-large" : null;
83
+ }
84
+ function S(e, t) {
85
+ if (e.length <= t) return {
86
+ text: e,
87
+ truncated: !1
88
+ };
89
+ let n = e.slice(0, t), r = n.lastIndexOf("\n");
90
+ return {
91
+ text: r > t * .5 ? n.slice(0, r) : n,
92
+ truncated: !0
93
+ };
94
+ }
95
+ function C(e) {
96
+ return [
97
+ " ```",
98
+ ...e.split("\n").map((e) => ` ${e}`),
99
+ " ```"
100
+ ];
101
+ }
102
+ function w(e) {
103
+ return e?.pageNumbers?.length ? e.pageNumbers : e?.pagesIncluded ? Array.from({ length: e.pagesIncluded }, (e, t) => t + 1) : [];
104
+ }
105
+ function T(e) {
106
+ let n = e.extraction, r = [];
107
+ if (n?.pageCount) {
108
+ let e = w(n);
109
+ e.length >= n.pageCount ? r.push(`${n.pageCount} page${n.pageCount === 1 ? "" : "s"}`) : e.length === 1 ? r.push(`page ${e[0]} of ${n.pageCount}`) : r.push(`pages ${t(e)} of ${n.pageCount}`);
110
+ }
111
+ if (n?.sheetNames?.length && r.push(`sheets: ${n.sheetNames.join(", ")}`), e.textTruncated && e.textContent) {
112
+ let t = n?.sourceChars;
113
+ r.push(t && t > e.textContent.length ? `first ${e.textContent.length} characters of ${t}; truncated` : `first ${e.textContent.length} characters; truncated`);
114
+ }
115
+ return r.length > 0 ? ` (${r.join("; ")})` : "";
116
+ }
117
+ var E = " Do NOT guess at, summarise or characterise its contents — you have not seen them.", D = [" Fetch the URL above if you can; otherwise ask the user to paste the relevant section as text.", E];
118
+ function O(e) {
119
+ let n = e.extraction;
120
+ if (!n?.pageCount) return [];
121
+ let r = new Set(w(n)), i = new Set(n.imagePages ?? []), a = [...i].filter((e) => !r.has(e)).sort((e, t) => e - t), o = [];
122
+ if (a.length > 0) {
123
+ let e = a.length === 1 ? `Page ${a[0]}` : `Pages ${t(a)}`;
124
+ o.push(` ${e} of this document contain no extractable text — they are images.`, " Nothing about those pages is available to you.");
125
+ }
126
+ let s = [];
127
+ for (let e = 1; e <= n.pageCount; e += 1) !r.has(e) && !i.has(e) && s.push(e);
128
+ if (s.length > 0) {
129
+ let e = s.length === 1 ? `Page ${s[0]}` : `Pages ${t(s)}`;
130
+ o.push(` ${e} of this document ${s.length === 1 ? "was" : "were"} not read at all.`);
131
+ }
132
+ return o.length > 0 && o.push(E), o;
133
+ }
134
+ function k(e) {
135
+ let t = e.extraction;
136
+ if (!t || t.status === "unsupported") return [" Binary file — contents not inlined. Fetch the URL above if you can; otherwise ask the user", " to paste the relevant section as text."];
137
+ if (t.status === "no-text") return t.format === "docx" || t.format === "xlsx" ? [
138
+ " NO TEXT AVAILABLE — this document holds no readable text; it is either empty or contains only",
139
+ " images, charts or drawings, none of which carry text. No text could be extracted.",
140
+ ...D
141
+ ] : [
142
+ ` NO TEXT AVAILABLE — this document appears to be scanned images; no text could be extracted${t.pagesScanned && t.pageCount && t.pagesScanned < t.pageCount ? ` (the first ${t.pagesScanned} of ${t.pageCount} pages were checked)` : ""}.`,
143
+ " There is no text layer to read, so nothing about its contents is available to you.",
144
+ ...D
145
+ ];
146
+ if (t.status === "encrypted") return [
147
+ " NO TEXT AVAILABLE — this document is encrypted or password-protected; it could not be opened",
148
+ " and no text could be extracted.",
149
+ ...D
150
+ ];
151
+ if (t.status === "corrupt") return [` NO TEXT AVAILABLE — this file could not be read${t.detail ? ` (${t.detail})` : ""}; no text could be extracted.`, ...D];
152
+ if (!e.textContent) return [
153
+ " Text WAS extracted from this document but is omitted because this turn ran out of inline budget,",
154
+ " not because it is unavailable. Ask the user which part matters so they can re-send just that",
155
+ " section, or fetch the URL above if you can.",
156
+ E
157
+ ];
158
+ let r = t.pagesScanned ?? t.pageCount ?? 0, i = t.textPageCount;
159
+ if (t.pageCount && i !== void 0 && n(i, r)) {
160
+ let n = r - i;
161
+ return [
162
+ ` NOT READ — this document is mostly images. Only ${i} of its ${t.pageCount} page${t.pageCount === 1 ? "" : "s"} carr${i === 1 ? "ies" : "y"} a text layer;`,
163
+ ` the other ${n} contain no extractable text at all. Treat this as a document you have NOT`,
164
+ " read: the fragment below is the whole of what was available, not a summary of the document.",
165
+ ...O(e),
166
+ ` The only extractable text${T(e)}:`,
167
+ ...C(e.textContent)
168
+ ];
169
+ }
170
+ return [
171
+ ` Extracted text${T(e)}:`,
172
+ ...C(e.textContent),
173
+ ...O(e)
174
+ ];
175
+ }
176
+ function A(e, t, n = j) {
177
+ let r = [`[${t + 1}] ${e.filename} — ${e.mimeType}, ${_(e.size)}`];
178
+ return e.downloadUrl && r.push(` URL: ${e.downloadUrl}`), e.kind === "text" && e.textContent ? r.push(e.textTruncated ? ` Contents (first ${e.textContent.length} characters of ${_(e.size)}; truncated):` : " Contents:", ...C(e.textContent)) : e.kind === "document" ? r.push(...k(e)) : e.kind === "image" ? n.has(e.id) ? r.push(" NOT VIEWABLE IN THIS MESSAGE — this image did not fit the request-size budget, so only its URL", " travelled; its pixels are NOT attached. Fetch the URL above if you can; otherwise ask the user", " to describe it.", E) : r.push(" If this image is attached to the message as an image you can view, answer from what you see in it.", " If you cannot view attached images, say so plainly and ask the user to describe it — never guess.") : e.kind === "text" ? r.push(" Text file — contents omitted because this turn ran out of inline budget, not because they are", " unavailable. Fetch the URL above if you can; otherwise ask the user which part matters so they", " can re-send just that section.") : r.push(" Binary file — contents not inlined. Fetch the URL above if you can; otherwise ask the user", " to paste the relevant section as text."), r.join("\n");
179
+ }
180
+ var j = /* @__PURE__ */ new Set();
181
+ function M(e, t) {
182
+ let n = s, i = t, a = [...e], c = (e) => {
183
+ let t = !!e.extraction, a = Math.min(n, o, t ? i : Infinity);
184
+ if (a <= 0) return {
185
+ ...e,
186
+ textContent: void 0,
187
+ textTruncated: !0
188
+ };
189
+ if (e.documentPages?.length) {
190
+ let t = r(e.documentPages, a);
191
+ return t.text === "" ? {
192
+ ...e,
193
+ textContent: void 0,
194
+ textTruncated: !0
195
+ } : (n -= t.text.length, i -= t.text.length, {
196
+ ...e,
197
+ textContent: t.text,
198
+ textTruncated: e.textTruncated || t.truncated,
199
+ ...e.extraction ? { extraction: {
200
+ ...e.extraction,
201
+ pagesIncluded: t.pagesIncluded,
202
+ pageNumbers: t.pageNumbers
203
+ } } : {}
204
+ });
205
+ }
206
+ let { text: s, truncated: c } = S(e.textContent ?? "", a);
207
+ return n -= s.length, t && (i -= s.length), {
208
+ ...e,
209
+ textContent: s,
210
+ textTruncated: e.textTruncated || c
211
+ };
212
+ };
213
+ for (let t of [!1, !0]) e.forEach((e, n) => {
214
+ e.textContent && !!e.extraction === t && (a[n] = c(e));
215
+ });
216
+ return a;
217
+ }
218
+ function N(e, t, n) {
219
+ let r = M(e, t);
220
+ return [
221
+ "",
222
+ `--- Attachments (${e.length}) ---`,
223
+ "The user attached the following file(s) to this message.",
224
+ ...r.map((e, t) => A(e, t, n)),
225
+ "--- End attachments ---"
226
+ ].join("\n");
227
+ }
228
+ var P = 2048;
229
+ function F(e, t) {
230
+ let n = Z(e);
231
+ if (n.length === 0) return {
232
+ documentChars: s,
233
+ demotedImageIds: j
234
+ };
235
+ let r = (r, i, a) => {
236
+ let o = new Set(n.slice(0, r).map((e) => e.id)), s = N(e, i, a), c = t ? `${t}\n${s}` : s.trimStart(), { media: l } = Q(e, o);
237
+ return q(c, [{
238
+ type: "text",
239
+ text: c
240
+ }, ...l]) + P <= H;
241
+ };
242
+ for (let e = n.length; e >= 0; --e) {
243
+ let t = new Set(n.slice(e).map((e) => e.id));
244
+ if (r(e, 2e4, t)) return {
245
+ documentChars: s,
246
+ demotedImageIds: t
247
+ };
248
+ if (!r(e, 0, t)) continue;
249
+ let i = 0, a = s;
250
+ for (; i < a;) {
251
+ let n = Math.ceil((i + a) / 2);
252
+ r(e, n, t) ? i = n : a = n - 1;
253
+ }
254
+ return {
255
+ documentChars: i,
256
+ demotedImageIds: t
257
+ };
258
+ }
259
+ return {
260
+ documentChars: s,
261
+ demotedImageIds: new Set(n.map((e) => e.id))
262
+ };
263
+ }
264
+ function I(e, t = "") {
265
+ let n = y(e);
266
+ if (n.length === 0) return "";
267
+ let r = F(n, t);
268
+ return N(n, r.documentChars, r.demotedImageIds);
269
+ }
270
+ function L(e, t) {
271
+ let n = I(t, e);
272
+ return n ? e ? `${e}\n${n}` : n.trimStart() : e;
273
+ }
274
+ var R = 3, z = 15e5, B = e, V = 32768, H = B - V, U = 768, W = .6, G = typeof TextEncoder == "function" ? new TextEncoder() : null;
275
+ function K(e) {
276
+ return G ? G.encode(e).length : e.length;
277
+ }
278
+ function q(e, t) {
279
+ return K(JSON.stringify({
280
+ prompt: e,
281
+ parts: t ?? []
282
+ }));
283
+ }
284
+ function J(e) {
285
+ return y(e).filter((e) => e.kind === "image" && !!(e.inlineDataUrl ?? e.previewUrl)).length;
286
+ }
287
+ function Y(e) {
288
+ return (e ?? []).filter((e) => e.type === "image" && !!e.data).length;
289
+ }
290
+ function X(e) {
291
+ let t = /^data:([a-z0-9.+/-]+);base64,(.*)$/i.exec(e);
292
+ return t ? {
293
+ mimeType: t[1],
294
+ base64: t[2]
295
+ } : null;
296
+ }
297
+ function Z(e) {
298
+ let t = [], n = 0;
299
+ for (let r of e) {
300
+ if (r.kind !== "image") continue;
301
+ let e = r.inlineDataUrl ?? r.previewUrl, i = e ? X(e) : null;
302
+ i && (t.length >= 3 || n + i.base64.length > 15e5 || (t.push(r), n += i.base64.length));
303
+ }
304
+ return t;
305
+ }
306
+ function Q(e, t) {
307
+ let n = [], r = [];
308
+ for (let i of e) {
309
+ if (i.kind === "image") {
310
+ let e = i.inlineDataUrl ?? i.previewUrl, a = e ? X(e) : null;
311
+ if (a && t.has(i.id)) {
312
+ r.push(n.length), n.push({
313
+ type: "image",
314
+ mimeType: a.mimeType,
315
+ data: a.base64,
316
+ ...i.downloadUrl ? { url: i.downloadUrl } : {},
317
+ name: i.filename
318
+ });
319
+ continue;
320
+ }
321
+ i.downloadUrl && n.push({
322
+ type: "image",
323
+ mimeType: i.mimeType,
324
+ url: i.downloadUrl,
325
+ name: i.filename
326
+ });
327
+ continue;
328
+ }
329
+ i.downloadUrl && n.push({
330
+ type: "file",
331
+ mimeType: i.mimeType,
332
+ url: i.downloadUrl,
333
+ name: i.filename
334
+ });
335
+ }
336
+ return {
337
+ media: n,
338
+ inlinedIndices: r
339
+ };
340
+ }
341
+ function $(e, t) {
342
+ let n = y(t);
343
+ if (n.length === 0) return;
344
+ let { media: r, inlinedIndices: i } = Q(n, new Set(Z(n).map((e) => e.id)));
345
+ if (r.length !== 0) {
346
+ for (; i.length > 0 && q(e, [{
347
+ type: "text",
348
+ text: e
349
+ }, ...r]) > H;) {
350
+ let e = i.pop();
351
+ if (e === void 0) break;
352
+ let t = r[e];
353
+ !t || t.type === "text" || (t.url ? r[e] = {
354
+ type: t.type,
355
+ mimeType: t.mimeType,
356
+ url: t.url,
357
+ name: t.name
358
+ } : r.splice(e, 1));
359
+ }
360
+ if (r.length !== 0 && !(q(e, [{
361
+ type: "text",
362
+ text: e
363
+ }, ...r]) > H)) return [{
364
+ type: "text",
365
+ text: e
366
+ }, ...r];
367
+ }
368
+ }
369
+ //#endregion
370
+ export { g as ATTACHMENT_ACCEPT, c as AttachmentRejectedError, U as INLINE_IMAGE_MAX_DIM, W as INLINE_IMAGE_QUALITY, i as MAX_ATTACHMENTS, a as MAX_FILE_BYTES, o as MAX_INLINE_CHARS_PER_FILE, s as MAX_INLINE_CHARS_TOTAL, z as MAX_INLINE_IMAGE_BASE64_CHARS, R as MAX_INLINE_IMAGE_PARTS, H as MAX_INLINE_TURN_BODY_BYTES, B as MAX_TURN_BODY_BYTES, P as PROMPT_ENVELOPE_RESERVE_BYTES, V as TURN_BODY_HEADROOM_BYTES, I as buildAttachmentPromptBlock, $ as buildPromptParts, L as buildPromptWithAttachments, f as classifyAttachment, J as countInlineImageCandidates, Y as countInlinedImageParts, h as detectDocumentFormat, q as estimateTurnBodyBytes, _ as formatBytes, b as getAttachmentSendBlocker, d as getExtension, y as getSendableAttachments, v as hasAttachmentWorkInFlight, X as parseDataUrl, l as toMessageAttachment, S as truncateForInline, x as validateFile };
@@ -0,0 +1,153 @@
1
+ import { PendingWrite } from './pendingWrite';
2
+ import { AssistantMessageAttachment } from './attachments';
3
+ import { AssistantMode } from './types';
4
+ export type AssistantSandboxStatus = "none" | "provisioning" | "running" | "stopping" | "error";
5
+ export type AssistantConversationStatus = "idle" | "provisioning" | "connecting" | "streaming" | "complete" | "error";
6
+ /**
7
+ * ★ `AssistantPanelArtifact`, not `AssistantArtifact`. The transport already
8
+ * exports an `AssistantArtifact` in `./types` and the two genuinely differ —
9
+ * this one narrows `type` to a union and makes `downloadPath` optional. They
10
+ * were never reconciled, and reconciling them here would be a silent behaviour
11
+ * change smuggled into a move. Two identically-named exports would also make
12
+ * `export *` drop the name from the barrel with no error at all.
13
+ *
14
+ * The product re-exports this under its historical name, so no call site changes.
15
+ */
16
+ export interface AssistantPanelArtifact {
17
+ name: string;
18
+ path: string;
19
+ type: "file" | "directory";
20
+ size: number;
21
+ downloadPath?: string;
22
+ }
23
+ export interface AssistantMessageMetadata {
24
+ artifacts?: AssistantPanelArtifact[];
25
+ mode?: AssistantMode;
26
+ sandboxId?: string;
27
+ sessionId?: string;
28
+ /**
29
+ * Files the user attached to this turn. Deliberately the SLIM descriptor —
30
+ * conversations are persisted to localStorage, so preview data URLs and
31
+ * inlined text content must never land here (see AssistantMessageAttachment).
32
+ */
33
+ attachments?: AssistantMessageAttachment[];
34
+ /**
35
+ * The language this turn was generated under (BOFF-7108), as an assistant
36
+ * language code.
37
+ *
38
+ * Fixed at send time, because it is a property of the TURN and not of the
39
+ * current preference. `resolveSpeechLocale` cannot tell English from Spanish
40
+ * or French — they share a script — so it falls back to whatever language is
41
+ * selected NOW. Without this, a French reply starts being read by a Spanish
42
+ * voice the moment the user switches, even with the right voice installed.
43
+ */
44
+ lang?: string;
45
+ /**
46
+ * This turn happened inside a live VOICE session, not in the chat panel.
47
+ *
48
+ * ★ The two surfaces share one conversation and one backend session — voice
49
+ * was built as an overlay over the chat thread, so `submitVoiceTurn` calls
50
+ * the same `handleSend` the composer does. Without a marker there is no way
51
+ * to tell a spoken turn from a typed one once it is in the store, and every
52
+ * voice exchange therefore appeared in the normal chat: the operator's own
53
+ * report, "I see the same chat in the normal conversation tab, both are
54
+ * separate, don't confuse".
55
+ *
56
+ * Stamped at send time and persisted, so the distinction survives a reload —
57
+ * the chat transcript is never rehydrated from the backend, so the store is
58
+ * the only source of truth for what the panel shows.
59
+ */
60
+ voice?: boolean;
61
+ /**
62
+ * WHICH live voice session produced this turn.
63
+ *
64
+ * ★ Not the same thing as `sessionId` above, which is the BACKEND session and
65
+ * is shared with the typed thread. This one identifies a single
66
+ * microphone-on-to-End, so the transcript can say where one spoken exchange
67
+ * stopped and the next began.
68
+ *
69
+ * `voice: true` alone could never do that: it is a boolean, so a second voice
70
+ * session was indistinguishable from the first and the transcript silently ran
71
+ * them together. Two comments claimed the transcript was scoped to "this
72
+ * session" while it was in fact showing the conversation's entire spoken
73
+ * history.
74
+ *
75
+ * ★ OPTIONAL, and it has to stay optional. Every spoken turn recorded before
76
+ * this field existed has `voice: true` and no id, and those conversations are
77
+ * in users' localStorage right now. Anything that REQUIRED an id would erase
78
+ * their history — so an absent id means "some earlier session", which is
79
+ * exactly what it is, and never "not a voice turn".
80
+ */
81
+ voiceSessionId?: string;
82
+ /**
83
+ * A write this turn asked permission for, read from the refusal the sandbox
84
+ * returned rather than from the model's description of it.
85
+ *
86
+ * ★ Never contains the confirmation token. The token is the capability — a
87
+ * write goes through if it is echoed back — so it must not be persisted to
88
+ * localStorage or rendered. See `utils/pendingWrite`.
89
+ */
90
+ pendingWrite?: PendingWrite;
91
+ [key: string]: unknown;
92
+ }
93
+ export interface Message {
94
+ id: string;
95
+ role: "user" | "assistant" | "system";
96
+ content: string;
97
+ timestamp: string;
98
+ tokens?: {
99
+ input: number;
100
+ output: number;
101
+ };
102
+ metadata?: AssistantMessageMetadata;
103
+ }
104
+ export interface Conversation {
105
+ id: string;
106
+ title: string;
107
+ mode: AssistantMode;
108
+ /**
109
+ * EVERY turn, both surfaces — typed and spoken.
110
+ *
111
+ * ★ `readonly` on purpose, and it is load-bearing rather than hygiene. Voice
112
+ * and chat share this one array; what separates them is `metadata.voice` plus
113
+ * a filter at every reader, so the failure mode is a new reader that renders
114
+ * the array as-is and silently spills spoken content into the typed thread.
115
+ * Nothing about that is a type error, and it stays green in CI.
116
+ *
117
+ * Being readonly makes it one: components take `Message[]`, so handing them
118
+ * this field directly no longer compiles. The way through is `chatTurns()` or
119
+ * `voiceTurnsOnly()` (see `utils/voiceTurnVisibility`), both of which return a
120
+ * fresh mutable array — which means picking a surface is now a step you cannot
121
+ * skip by accident.
122
+ *
123
+ * Counting is still fine: `.length` answers "is this conversation used at
124
+ * all", which is a question about the conversation and not about either
125
+ * surface.
126
+ */
127
+ messages: readonly Message[];
128
+ createdAt: string;
129
+ updatedAt: string;
130
+ /** Backend session ID for this conversation */
131
+ backendSessionId?: string | null;
132
+ }
133
+ export interface AssistantSandboxRuntime {
134
+ sandboxId: string;
135
+ status: AssistantSandboxStatus;
136
+ }
137
+ export interface AssistantConversationProcessing {
138
+ status: AssistantConversationStatus;
139
+ messageId: string | null;
140
+ error?: string | null;
141
+ updatedAt: string;
142
+ }
143
+ export interface AssistantModeConfig {
144
+ id: AssistantMode;
145
+ label: string;
146
+ description: string;
147
+ suggestions: readonly string[];
148
+ /** Only offered to a global Owner/Admin (see useAssistantApiAccess). */
149
+ requiresAdmin?: boolean;
150
+ /** Executes mutations on live data → triggers the confirm-before-mutate guard. */
151
+ writeCapable?: boolean;
152
+ }
153
+ //# sourceMappingURL=conversation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/conversation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAIhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,MAAM,MAAM,sBAAsB,GAC9B,MAAM,GACN,cAAc,GACd,SAAS,GACT,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,MAAM,2BAA2B,GACnC,MAAM,GACN,cAAc,GACd,YAAY,GACZ,WAAW,GACX,UAAU,GACV,OAAO,CAAC;AAEZ;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACrC,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,0BAA0B,EAAE,CAAC;IAC3C;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,QAAQ,CAAC,EAAE,wBAAwB,CAAC;CACrC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,2BAA2B,CAAC;IACpC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,aAAa,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB"}