@tangle-network/agent-app 0.45.28 → 0.45.30

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 (58) hide show
  1. package/dist/{DesignCanvas-3RRPVPCP.js → DesignCanvas-RSWQCXM3.js} +2 -2
  2. package/dist/{DesignCanvasEditor-KAMMEX7E.js → DesignCanvasEditor-XZMGIST7.js} +4 -4
  3. package/dist/{use-file-mentions-CZ-Ua_sb.d.ts → agent-session-controls-BwImzYKC.d.ts} +32 -1
  4. package/dist/alerting/index.d.ts +178 -0
  5. package/dist/alerting/index.js +9 -0
  6. package/dist/alerting/index.js.map +1 -0
  7. package/dist/assistant/index.d.ts +65 -42
  8. package/dist/assistant/index.js +31 -12
  9. package/dist/assistant/index.js.map +1 -1
  10. package/dist/chat-react/index.d.ts +20 -140
  11. package/dist/chat-react/index.js +15 -133
  12. package/dist/chat-react/index.js.map +1 -1
  13. package/dist/chat-routes/index.js +7 -7
  14. package/dist/chunk-COP2K4LF.js +190 -0
  15. package/dist/chunk-COP2K4LF.js.map +1 -0
  16. package/dist/{chunk-NC2V63EL.js → chunk-FNM7JYME.js} +88 -21
  17. package/dist/chunk-FNM7JYME.js.map +1 -0
  18. package/dist/{chunk-WC43OQMR.js → chunk-IFYJDX3J.js} +867 -1260
  19. package/dist/chunk-IFYJDX3J.js.map +1 -0
  20. package/dist/chunk-L7FHRORY.js +763 -0
  21. package/dist/chunk-L7FHRORY.js.map +1 -0
  22. package/dist/{chunk-4QVG4WJQ.js → chunk-OYTMILRK.js} +10 -3
  23. package/dist/chunk-OYTMILRK.js.map +1 -0
  24. package/dist/{chunk-OYGM5WLD.js → chunk-PURHUAOR.js} +52 -15
  25. package/dist/chunk-PURHUAOR.js.map +1 -0
  26. package/dist/{chunk-AWPK7ZDS.js → chunk-QGOU3VXH.js} +3 -3
  27. package/dist/{chunk-OTLEYHOG.js → chunk-RRH23CGS.js} +24 -4
  28. package/dist/chunk-RRH23CGS.js.map +1 -0
  29. package/dist/{chunk-5FAKBTCK.js → chunk-XDB7IBYE.js} +22 -1
  30. package/dist/chunk-XDB7IBYE.js.map +1 -0
  31. package/dist/design-canvas-react/engine.d.ts +15 -1
  32. package/dist/design-canvas-react/engine.js +6 -2
  33. package/dist/design-canvas-react/index.d.ts +13 -1
  34. package/dist/design-canvas-react/index.js +13 -8
  35. package/dist/design-canvas-react/index.js.map +1 -1
  36. package/dist/design-canvas-react/lazy.js +1 -1
  37. package/dist/preflight/cli.js +2 -1
  38. package/dist/preflight/cli.js.map +1 -1
  39. package/dist/preflight/index.d.ts +35 -2
  40. package/dist/preflight/index.js +6 -3
  41. package/dist/sandbox/index.d.ts +11 -1
  42. package/dist/sandbox/index.js +1 -1
  43. package/dist/teams/index.js +5 -5
  44. package/dist/teams/invitations-api.js +4 -4
  45. package/dist/web-react/index.d.ts +26 -35
  46. package/dist/web-react/index.js +10 -10
  47. package/package.json +19 -3
  48. package/dist/chunk-4QVG4WJQ.js.map +0 -1
  49. package/dist/chunk-5FAKBTCK.js.map +0 -1
  50. package/dist/chunk-E4DHYENH.js +0 -292
  51. package/dist/chunk-E4DHYENH.js.map +0 -1
  52. package/dist/chunk-NC2V63EL.js.map +0 -1
  53. package/dist/chunk-OTLEYHOG.js.map +0 -1
  54. package/dist/chunk-OYGM5WLD.js.map +0 -1
  55. package/dist/chunk-WC43OQMR.js.map +0 -1
  56. /package/dist/{DesignCanvas-3RRPVPCP.js.map → DesignCanvas-RSWQCXM3.js.map} +0 -0
  57. /package/dist/{DesignCanvasEditor-KAMMEX7E.js.map → DesignCanvasEditor-XZMGIST7.js.map} +0 -0
  58. /package/dist/{chunk-AWPK7ZDS.js.map → chunk-QGOU3VXH.js.map} +0 -0
@@ -1,292 +0,0 @@
1
- import {
2
- ATTACHMENT_ACCEPT,
3
- ATTACHMENT_MAX_COUNT,
4
- MAX_ATTACHMENT_TOTAL_BYTES,
5
- MAX_BINARY_ATTACHMENT_BYTES,
6
- MAX_TEXT_ATTACHMENT_BYTES,
7
- attachmentSizeErrorMessage,
8
- attachmentTotalSizeErrorMessage,
9
- checkAttachmentType,
10
- sanitizeAttachmentFileName,
11
- sniffBinary
12
- } from "./chunk-CCVG2TL6.js";
13
-
14
- // src/web-react/use-composer-attachments.ts
15
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
16
- function newId() {
17
- const cryptoObject = globalThis.crypto;
18
- if (typeof cryptoObject?.randomUUID === "function") return cryptoObject.randomUUID();
19
- return `att-${Date.now()}-${Math.random().toString(36).slice(2)}`;
20
- }
21
- function dedupeName(name, taken) {
22
- if (!taken.has(name)) return name;
23
- const dot = name.lastIndexOf(".");
24
- const base = dot > 0 ? name.slice(0, dot) : name;
25
- const ext = dot > 0 ? name.slice(dot) : "";
26
- let n = 2;
27
- let candidate = `${base}-${n}${ext}`;
28
- while (taken.has(candidate)) {
29
- n += 1;
30
- candidate = `${base}-${n}${ext}`;
31
- }
32
- return candidate;
33
- }
34
- function kindForMime(mime) {
35
- return mime.startsWith("image/") ? "image" : "file";
36
- }
37
- function isAcceptedFileType(file, accept) {
38
- const patterns = accept.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
39
- if (patterns.length === 0) return true;
40
- const name = file.name.toLowerCase();
41
- const type = (file.type || "").toLowerCase();
42
- return patterns.some((pattern) => {
43
- const lower = pattern.toLowerCase();
44
- if (lower.startsWith(".")) return name.endsWith(lower);
45
- if (lower.endsWith("/*")) return type.startsWith(lower.slice(0, -1));
46
- return type === lower;
47
- });
48
- }
49
- async function parseUploadError(res) {
50
- const detail = await res.json().catch(() => null);
51
- if (detail && typeof detail === "object" && "error" in detail) {
52
- const error = detail.error;
53
- if (typeof error === "string" && error) return error;
54
- if (error && typeof error === "object" && "message" in error) {
55
- const message = error.message;
56
- if (typeof message === "string" && message) return message;
57
- }
58
- }
59
- return `Upload failed (${res.status})`;
60
- }
61
- var NO_UPLOAD_TARGET_MESSAGE = "No upload destination configured (pass uploadUrl or buildUploadRequest)";
62
- function useComposerAttachments(options) {
63
- const optionsRef = useRef(options);
64
- optionsRef.current = options;
65
- const [staged, setStagedState] = useState([]);
66
- const stagedRef = useRef([]);
67
- const controllersRef = useRef(/* @__PURE__ */ new Map());
68
- const setStaged = useCallback(
69
- (updater) => {
70
- const next = typeof updater === "function" ? updater(stagedRef.current) : updater;
71
- stagedRef.current = next;
72
- setStagedState(next);
73
- },
74
- []
75
- );
76
- const upload = useCallback(
77
- async (id, file, name) => {
78
- const opts = optionsRef.current;
79
- setStaged(
80
- (prev) => prev.map((s) => s.id === id ? { ...s, status: "uploading", errorMessage: void 0 } : s)
81
- );
82
- const controller = new AbortController();
83
- controllersRef.current.set(id, controller);
84
- const form = new FormData();
85
- form.append("file", file, name);
86
- const request = opts.buildUploadRequest ? opts.buildUploadRequest({ file, name, form }) : opts.uploadUrl ? { url: opts.uploadUrl } : null;
87
- if (!request) {
88
- setStaged(
89
- (prev) => prev.map(
90
- (s) => s.id === id ? { ...s, status: "error", errorMessage: NO_UPLOAD_TARGET_MESSAGE } : s
91
- )
92
- );
93
- opts.onError?.(NO_UPLOAD_TARGET_MESSAGE);
94
- controllersRef.current.delete(id);
95
- return;
96
- }
97
- try {
98
- const res = await fetch(request.url, {
99
- method: "POST",
100
- credentials: "same-origin",
101
- ...request.init,
102
- body: form,
103
- signal: controller.signal
104
- });
105
- if (!res.ok) {
106
- const message = await parseUploadError(res);
107
- setStaged(
108
- (prev) => prev.map((s) => s.id === id ? { ...s, status: "error", errorMessage: message } : s)
109
- );
110
- opts.onError?.(message);
111
- return;
112
- }
113
- const data = await res.json();
114
- const uploaded = data.files?.[0];
115
- if (!uploaded) {
116
- const message = "Upload returned no file";
117
- setStaged(
118
- (prev) => prev.map((s) => s.id === id ? { ...s, status: "error", errorMessage: message } : s)
119
- );
120
- opts.onError?.(message);
121
- return;
122
- }
123
- setStaged(
124
- (prev) => prev.map((s) => s.id === id ? { ...s, status: "ready", reference: uploaded } : s)
125
- );
126
- } catch (err) {
127
- if (err.name === "AbortError") return;
128
- const message = err instanceof Error && err.message ? err.message : "Upload failed \u2014 check your connection";
129
- setStaged(
130
- (prev) => prev.map((s) => s.id === id ? { ...s, status: "error", errorMessage: message } : s)
131
- );
132
- opts.onError?.(message);
133
- } finally {
134
- controllersRef.current.delete(id);
135
- }
136
- },
137
- [setStaged]
138
- );
139
- const addFiles = useCallback(
140
- async (files) => {
141
- const opts = optionsRef.current;
142
- const enabled2 = opts.enabled ?? true;
143
- if (!enabled2) {
144
- opts.onReject?.("Attachments are disabled");
145
- return;
146
- }
147
- const accept = opts.accept ?? ATTACHMENT_ACCEPT;
148
- const maxCount = opts.limits?.maxCount ?? ATTACHMENT_MAX_COUNT;
149
- const maxBinaryBytes = opts.limits?.maxBinaryBytes ?? MAX_BINARY_ATTACHMENT_BYTES;
150
- const maxTextBytes = opts.limits?.maxTextBytes ?? MAX_TEXT_ATTACHMENT_BYTES;
151
- const maxTotalBytes = opts.limits?.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES;
152
- const allowedKinds = opts.allowedKinds ?? ["image", "file"];
153
- const list = Array.isArray(files) ? files : Array.from(files);
154
- const currentCount = stagedRef.current.length;
155
- const countAccepted = [];
156
- for (const file of list) {
157
- if (!isAcceptedFileType(file, accept)) {
158
- opts.onReject?.(`"${file.name}" is not an accepted file type (${accept}).`, file);
159
- continue;
160
- }
161
- if (currentCount + countAccepted.length >= maxCount) {
162
- opts.onReject?.(`"${file.name}" was not added \u2014 the ${maxCount}-file limit is already reached.`, file);
163
- continue;
164
- }
165
- countAccepted.push(file);
166
- }
167
- const sizeAccepted = [];
168
- for (const file of countAccepted) {
169
- const bytes = new Uint8Array(await file.arrayBuffer());
170
- const sniff = sniffBinary(bytes);
171
- const typeCheck = checkAttachmentType(file.name, sniff);
172
- if (!typeCheck.succeeded) {
173
- opts.onReject?.(typeCheck.message, file);
174
- continue;
175
- }
176
- const limit = sniff.binary ? maxBinaryBytes : maxTextBytes;
177
- if (file.size > limit) {
178
- opts.onReject?.(attachmentSizeErrorMessage(file.name, file.size, limit), file);
179
- continue;
180
- }
181
- const mediaType = sniff.mime ?? file.type ?? "";
182
- const kind = kindForMime(mediaType);
183
- if (!allowedKinds.includes(kind)) {
184
- opts.onReject?.(`"${file.name}" is a ${kind} attachment, which isn't accepted here`, file);
185
- continue;
186
- }
187
- sizeAccepted.push(file);
188
- }
189
- const accepted = [];
190
- let totalBytes = stagedRef.current.reduce((total, s) => total + s.size, 0);
191
- for (const file of sizeAccepted) {
192
- const nextTotalBytes = totalBytes + file.size;
193
- if (nextTotalBytes > maxTotalBytes) {
194
- opts.onReject?.(attachmentTotalSizeErrorMessage(nextTotalBytes, maxTotalBytes), file);
195
- continue;
196
- }
197
- accepted.push(file);
198
- totalBytes = nextTotalBytes;
199
- }
200
- if (accepted.length === 0) return;
201
- const taken = new Set(stagedRef.current.map((s) => s.name));
202
- const entries = accepted.map((file) => {
203
- const name = dedupeName(sanitizeAttachmentFileName(file.name), taken);
204
- taken.add(name);
205
- return {
206
- id: newId(),
207
- file,
208
- name,
209
- size: file.size,
210
- status: "pending",
211
- previewUrl: file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0
212
- };
213
- });
214
- setStaged((prev) => [...prev, ...entries]);
215
- for (const entry of entries) void upload(entry.id, entry.file, entry.name);
216
- },
217
- [setStaged, upload]
218
- );
219
- const retry = useCallback(
220
- (id) => {
221
- const entry = stagedRef.current.find((s) => s.id === id);
222
- if (!entry) return;
223
- void upload(entry.id, entry.file, entry.name);
224
- },
225
- [upload]
226
- );
227
- const removeAttachment = useCallback(
228
- (id) => {
229
- controllersRef.current.get(id)?.abort();
230
- controllersRef.current.delete(id);
231
- const entry = stagedRef.current.find((s) => s.id === id);
232
- if (entry?.previewUrl) URL.revokeObjectURL(entry.previewUrl);
233
- setStaged((prev) => prev.filter((s) => s.id !== id));
234
- },
235
- [setStaged]
236
- );
237
- const clear = useCallback(() => {
238
- for (const controller of controllersRef.current.values()) controller.abort();
239
- controllersRef.current.clear();
240
- for (const entry of stagedRef.current) {
241
- if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl);
242
- }
243
- setStaged([]);
244
- }, [setStaged]);
245
- useEffect(
246
- () => () => {
247
- for (const controller of controllersRef.current.values()) controller.abort();
248
- controllersRef.current.clear();
249
- for (const entry of stagedRef.current) {
250
- if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl);
251
- }
252
- },
253
- []
254
- );
255
- const composerFiles = useMemo(
256
- () => staged.map((s) => ({
257
- id: s.id,
258
- name: s.name,
259
- size: s.size,
260
- kind: "file",
261
- status: s.status
262
- })),
263
- [staged]
264
- );
265
- const references = useMemo(
266
- () => staged.filter((s) => s.status === "ready" && !!s.reference).map((s) => s.reference),
267
- [staged]
268
- );
269
- const hasPending = useMemo(
270
- () => staged.some((s) => s.status === "pending" || s.status === "uploading"),
271
- [staged]
272
- );
273
- const hasError = useMemo(() => staged.some((s) => s.status === "error"), [staged]);
274
- const enabled = options.enabled ?? true;
275
- const blockReason = !enabled ? "Attachments are disabled" : hasPending ? "Attachments are still uploading" : hasError ? "Remove failed attachments to send" : null;
276
- return {
277
- composerFiles,
278
- references,
279
- addFiles,
280
- retry,
281
- removeAttachment,
282
- clear,
283
- hasPending,
284
- hasError,
285
- blockReason
286
- };
287
- }
288
-
289
- export {
290
- useComposerAttachments
291
- };
292
- //# sourceMappingURL=chunk-E4DHYENH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/web-react/use-composer-attachments.ts"],"sourcesContent":["/**\n * `useComposerAttachments` — the composer's staged-upload lifecycle: validate\n * selected/dropped/pasted files against the shared limits (the SAME\n * `sniffBinary`/`checkAttachmentType`/size-cap vocabulary the store-backed\n * upload route enforces server-side, `../chat-routes/attachment-validation`\n * + `../chat-routes/binary-sniff`), upload each accepted file with one POST\n * request per file (so a single failure never poisons the batch), and track\n * every file's status so a host composer can render chips and gate sending.\n *\n * Ported from gtm-agent's `src/components/composer-attachments.tsx`\n * (gtm#584/#592/#593 hardened the sniff gate and batch semantics this leans\n * on), de-gtm-ified:\n * - the hardcoded `/api/vault/upload?workspaceId=` URL becomes\n * `uploadUrl`/`buildUploadRequest` (the latter wins — it hands back both\n * the URL and a `RequestInit` override, e.g. an auth header);\n * - `sonner` toasts become `onReject` (client pre-validation, never hits the\n * network) and `onError` (a request that reached the server and failed);\n * - the sandbox-ui `validateComposerFiles` import becomes a small\n * accept-list matcher re-implemented locally (`isAcceptedFileType`,\n * mirroring its `accept`-string matching byte-for-byte) — this module\n * stays free of the sandbox-ui peer;\n * - the response is expected to be `{ files: ChatAttachmentInput[] }` (full\n * server-authoritative descriptors — size/mediaType/kind — not gtm's\n * `{path, name}`), so `references` is a verbatim pass-through with no\n * client recompute;\n * - `workspaceId`'s truthiness gate becomes `enabled` (default `true`).\n *\n * Import-free beyond React + the browser-safe `/chat-routes` validation core:\n * this module ships through `/web-react` into client bundles\n * (`tests/browser-safe-subpaths.test.ts` walks the graph), so nothing here\n * may reach a Node builtin, `sandbox-ui`, or an engine package.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ChatAttachmentInput, ChatAttachmentKind } from './chat-stream'\nimport type { ComposerFile } from './chat-composer'\nimport {\n ATTACHMENT_ACCEPT,\n ATTACHMENT_MAX_COUNT,\n MAX_ATTACHMENT_TOTAL_BYTES,\n MAX_BINARY_ATTACHMENT_BYTES,\n MAX_TEXT_ATTACHMENT_BYTES,\n attachmentSizeErrorMessage,\n attachmentTotalSizeErrorMessage,\n checkAttachmentType,\n sanitizeAttachmentFileName,\n} from '../chat-routes/attachment-validation'\nimport { sniffBinary } from '../chat-routes/binary-sniff'\n\nexport { ATTACHMENT_ACCEPT } from '../chat-routes/attachment-validation'\n\n/** One staged file and its upload lifecycle. `file` is retained so a failed\n * upload can be retried without re-selecting; `previewUrl` is an object URL\n * for image thumbnails and must be revoked when the entry leaves the queue.\n * `reference` is the server's authoritative descriptor once the upload\n * lands — stored verbatim, never recomputed client-side. */\ninterface StagedAttachment {\n id: string\n file: File\n name: string\n size: number\n status: 'pending' | 'uploading' | 'ready' | 'error'\n reference?: ChatAttachmentInput\n previewUrl?: string\n errorMessage?: string\n}\n\n/** Define options for configuring file upload behavior and handling in a composer component */\nexport interface UseComposerAttachmentsOptions {\n /** Simple upload target: every file POSTs here. Ignored when\n * `buildUploadRequest` is provided. */\n uploadUrl?: string\n /** Full request-building seam (auth headers, per-file routing, …) — wins\n * over `uploadUrl` when both are set. */\n buildUploadRequest?: (args: { file: File; name: string; form: FormData }) => {\n url: string\n init?: Omit<RequestInit, 'body' | 'signal'>\n }\n /** Client pre-validation rejections — a file that never reaches the\n * network (bad type, over a size cap, over count, disallowed kind). */\n onReject?: (reason: string, file?: File) => void\n /** A file that reached the upload endpoint and failed (HTTP error,\n * transport error, malformed response). */\n onError?: (reason: string) => void\n limits?: {\n maxCount?: number\n maxBinaryBytes?: number\n maxTextBytes?: number\n maxTotalBytes?: number\n }\n /** Attachment kinds accepted, checked against the sniffed content's\n * mime. Default: both (`['image', 'file']` — i.e. no restriction). */\n allowedKinds?: ChatAttachmentKind[]\n /** `<input accept>`-style gate for the file picker/drop/paste path.\n * Default {@link ATTACHMENT_ACCEPT}. */\n accept?: string\n /** When `false`, `addFiles` rejects every call via `onReject` (and\n * `blockReason` explains why) instead of staging anything — the\n * replacement for gtm's `workspaceId`-truthiness gate (e.g. no workspace\n * loaded yet). Default `true`. */\n enabled?: boolean\n}\n\n/** Provide staged file chips, ready attachments, and methods to add, retry, or drop composer files */\nexport interface UseComposerAttachmentsResult {\n /** Chip models for `ChatComposer`'s `pendingFiles` prop, one per staged\n * file — `kind` is always `'file'` (agent-app's `ComposerFile.kind`\n * discriminates file-vs-folder chips, not attachment media type). */\n composerFiles: ComposerFile[]\n /** Ready-to-send attachment descriptors — only files whose upload\n * succeeded, straight from the server's response (no recompute). Feed\n * this into `ChatTurnRequestPayload.attachments`. */\n references: ChatAttachmentInput[]\n /** Validate + stage + upload the given files, one request per file. */\n addFiles: (files: File[] | FileList) => Promise<void>\n /** Re-upload a failed entry using its retained `File`. */\n retry: (id: string) => void\n /** Drop one staged entry, aborting its upload and revoking its preview. */\n removeAttachment: (id: string) => void\n /** Forget every staged entry (call after a successful send). */\n clear: () => void\n /** True while any file is still pending or uploading. */\n hasPending: boolean\n /** True while any file failed to upload. */\n hasError: boolean\n /** Why a send is blocked, or `null` when the queue is clean. */\n blockReason: string | null\n}\n\nfunction newId(): string {\n const cryptoObject = globalThis.crypto\n if (typeof cryptoObject?.randomUUID === 'function') return cryptoObject.randomUUID()\n return `att-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/** Suffix a name (`report.pdf` → `report-2.pdf`) until it's unused. The\n * server writes to a name-derived store path, so identical names would\n * overwrite. The suffix stays inside the store-path charset (see\n * `sanitizeAttachmentFileName`). Ported byte-for-byte from gtm's\n * `dedupeName`. */\nfunction dedupeName(name: string, taken: Set<string>): string {\n if (!taken.has(name)) return name\n const dot = name.lastIndexOf('.')\n const base = dot > 0 ? name.slice(0, dot) : name\n const ext = dot > 0 ? name.slice(dot) : ''\n let n = 2\n let candidate = `${base}-${n}${ext}`\n while (taken.has(candidate)) {\n n += 1\n candidate = `${base}-${n}${ext}`\n }\n return candidate\n}\n\n/** `image/*` → `'image'`, everything else → `'file'`. Deliberately\n * reimplemented here (not imported from `../chat-store/parts`, which pulls\n * the drizzle-adjacent `/chat-store` barrel): this module must stay reachable\n * from a browser bundle with only the `/chat-routes` validation core as a\n * dependency. */\nfunction kindForMime(mime: string): ChatAttachmentKind {\n return mime.startsWith('image/') ? 'image' : 'file'\n}\n\n/** `<input accept>`-style matcher: extension (`.pdf`), wildcard mime\n * (`image/*`), or exact mime. Reimplemented locally (NOT imported from\n * `@tangle-network/sandbox-ui`'s `validateComposerFiles`/`isAcceptedType`) so\n * this module has no sandbox-ui dependency; the matching semantics are kept\n * identical so a rejection reads the same either side of the fence. */\nfunction isAcceptedFileType(file: File, accept: string): boolean {\n const patterns = accept.split(',').map((p) => p.trim()).filter((p) => p.length > 0)\n if (patterns.length === 0) return true\n const name = file.name.toLowerCase()\n const type = (file.type || '').toLowerCase()\n return patterns.some((pattern) => {\n const lower = pattern.toLowerCase()\n if (lower.startsWith('.')) return name.endsWith(lower)\n if (lower.endsWith('/*')) return type.startsWith(lower.slice(0, -1))\n return type === lower\n })\n}\n\n/** Pull a human-readable message out of the upload endpoint's error body.\n * Ported from gtm's `parseUploadError`: handles both `{ error: string }`\n * (size/count/access errors) and `{ error: { message } }` (the\n * `createAttachmentUploadRoute` envelope, `{error:{code,message,path?}}`). */\nasync function parseUploadError(res: Response): Promise<string> {\n const detail = await res.json().catch(() => null)\n if (detail && typeof detail === 'object' && 'error' in detail) {\n const error = (detail as { error: unknown }).error\n if (typeof error === 'string' && error) return error\n if (error && typeof error === 'object' && 'message' in error) {\n const message = (error as { message: unknown }).message\n if (typeof message === 'string' && message) return message\n }\n }\n return `Upload failed (${res.status})`\n}\n\n/** Shown when neither `uploadUrl` nor `buildUploadRequest` is configured\n * while `enabled` — a product wiring bug, not a user-facing rejection, so it\n * lands each affected entry in `error` (with `onError`) rather than blocking\n * `addFiles` outright via `onReject`: the files still stage and can be\n * retried once the product fixes its config, instead of silently vanishing. */\nconst NO_UPLOAD_TARGET_MESSAGE = 'No upload destination configured (pass uploadUrl or buildUploadRequest)'\n\n/**\n * Owns the composer's attachment lifecycle: validate selected/dropped/pasted\n * files against the shared limits, upload each accepted file to the\n * product's store (one request per file), and track every file's status so\n * the composer can render chips and gate sending.\n *\n * Failures surface loud — a rejected file calls `onReject` and is never\n * uploaded; a failed upload calls `onError` and leaves an error chip the user\n * can retry or remove. `references` only ever contains files whose upload the\n * server actually confirmed.\n */\nexport function useComposerAttachments(\n options: UseComposerAttachmentsOptions,\n): UseComposerAttachmentsResult {\n // Latest options, read from inside stable callbacks — avoids re-creating\n // `addFiles`/`upload` (and therefore breaking referential stability for\n // effects a host might hang off them) every time a caller passes a fresh\n // options object literal.\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const [staged, setStagedState] = useState<StagedAttachment[]>([])\n // Mirror of `staged` kept in lockstep so dedupe/aggregate-cap/abort read\n // current values synchronously (setState callbacks alone can't answer\n // \"what's staged right now\" mid-validation).\n const stagedRef = useRef<StagedAttachment[]>([])\n const controllersRef = useRef<Map<string, AbortController>>(new Map())\n\n // Post-unmount calls reduce to a React no-op setState; the refs they touch\n // die with the instance.\n const setStaged = useCallback(\n (updater: StagedAttachment[] | ((prev: StagedAttachment[]) => StagedAttachment[])) => {\n const next =\n typeof updater === 'function'\n ? (updater as (prev: StagedAttachment[]) => StagedAttachment[])(stagedRef.current)\n : updater\n stagedRef.current = next\n setStagedState(next)\n },\n [],\n )\n\n const upload = useCallback(\n async (id: string, file: File, name: string) => {\n const opts = optionsRef.current\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'uploading', errorMessage: undefined } : s)),\n )\n const controller = new AbortController()\n controllersRef.current.set(id, controller)\n const form = new FormData()\n form.append('file', file, name)\n\n const request = opts.buildUploadRequest\n ? opts.buildUploadRequest({ file, name, form })\n : opts.uploadUrl\n ? { url: opts.uploadUrl }\n : null\n\n if (!request) {\n setStaged((prev) =>\n prev.map((s) =>\n s.id === id ? { ...s, status: 'error', errorMessage: NO_UPLOAD_TARGET_MESSAGE } : s,\n ),\n )\n opts.onError?.(NO_UPLOAD_TARGET_MESSAGE)\n controllersRef.current.delete(id)\n return\n }\n\n try {\n const res = await fetch(request.url, {\n method: 'POST',\n credentials: 'same-origin',\n ...request.init,\n body: form,\n signal: controller.signal,\n })\n if (!res.ok) {\n const message = await parseUploadError(res)\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n return\n }\n const data = (await res.json()) as { files?: ChatAttachmentInput[] }\n const uploaded = data.files?.[0]\n if (!uploaded) {\n const message = 'Upload returned no file'\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n return\n }\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'ready', reference: uploaded } : s)),\n )\n } catch (err) {\n if ((err as Error).name === 'AbortError') return // silent removal — see removeAttachment/clear\n const message =\n err instanceof Error && err.message ? err.message : 'Upload failed — check your connection'\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n } finally {\n controllersRef.current.delete(id)\n }\n },\n [setStaged],\n )\n\n const addFiles = useCallback(\n async (files: File[] | FileList) => {\n const opts = optionsRef.current\n const enabled = opts.enabled ?? true\n if (!enabled) {\n opts.onReject?.('Attachments are disabled')\n return\n }\n\n const accept = opts.accept ?? ATTACHMENT_ACCEPT\n const maxCount = opts.limits?.maxCount ?? ATTACHMENT_MAX_COUNT\n const maxBinaryBytes = opts.limits?.maxBinaryBytes ?? MAX_BINARY_ATTACHMENT_BYTES\n const maxTextBytes = opts.limits?.maxTextBytes ?? MAX_TEXT_ATTACHMENT_BYTES\n const maxTotalBytes = opts.limits?.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES\n const allowedKinds = opts.allowedKinds ?? (['image', 'file'] as ChatAttachmentKind[])\n\n const list = Array.isArray(files) ? files : Array.from(files)\n\n // Pass 1: accept-list + count cap, mirroring sandbox-ui's\n // `validateComposerFiles` semantics (accept checked before count, count\n // checked against currently-staged + already-accepted-this-batch).\n const currentCount = stagedRef.current.length\n const countAccepted: File[] = []\n for (const file of list) {\n if (!isAcceptedFileType(file, accept)) {\n opts.onReject?.(`\"${file.name}\" is not an accepted file type (${accept}).`, file)\n continue\n }\n if (currentCount + countAccepted.length >= maxCount) {\n opts.onReject?.(`\"${file.name}\" was not added — the ${maxCount}-file limit is already reached.`, file)\n continue\n }\n countAccepted.push(file)\n }\n\n // Pass 2: real content sniff + type gate + per-kind size cap +\n // allowed-kinds gate — the SAME checks the server enforces, so a\n // rejection never differs depending on which side classified the bytes\n // first. Nothing here ever reaches the network.\n const sizeAccepted: File[] = []\n for (const file of countAccepted) {\n const bytes = new Uint8Array(await file.arrayBuffer())\n const sniff = sniffBinary(bytes)\n const typeCheck = checkAttachmentType(file.name, sniff)\n if (!typeCheck.succeeded) {\n opts.onReject?.(typeCheck.message, file)\n continue\n }\n const limit = sniff.binary ? maxBinaryBytes : maxTextBytes\n if (file.size > limit) {\n opts.onReject?.(attachmentSizeErrorMessage(file.name, file.size, limit), file)\n continue\n }\n const mediaType = sniff.mime ?? file.type ?? ''\n const kind = kindForMime(mediaType)\n if (!allowedKinds.includes(kind)) {\n opts.onReject?.(`\"${file.name}\" is a ${kind} attachment, which isn't accepted here`, file)\n continue\n }\n sizeAccepted.push(file)\n }\n\n // Pass 3: running aggregate cap across this batch + everything already\n // staged (any status) — a partial batch can still land.\n const accepted: File[] = []\n let totalBytes = stagedRef.current.reduce((total, s) => total + s.size, 0)\n for (const file of sizeAccepted) {\n const nextTotalBytes = totalBytes + file.size\n if (nextTotalBytes > maxTotalBytes) {\n opts.onReject?.(attachmentTotalSizeErrorMessage(nextTotalBytes, maxTotalBytes), file)\n continue\n }\n accepted.push(file)\n totalBytes = nextTotalBytes\n }\n if (accepted.length === 0) return\n\n // Stage under the name the server will actually store, so the chip and\n // the message's attachment references never diverge.\n const taken = new Set(stagedRef.current.map((s) => s.name))\n const entries: StagedAttachment[] = accepted.map((file) => {\n const name = dedupeName(sanitizeAttachmentFileName(file.name), taken)\n taken.add(name)\n return {\n id: newId(),\n file,\n name,\n size: file.size,\n status: 'pending',\n previewUrl: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,\n }\n })\n setStaged((prev) => [...prev, ...entries])\n for (const entry of entries) void upload(entry.id, entry.file, entry.name)\n },\n [setStaged, upload],\n )\n\n const retry = useCallback(\n (id: string) => {\n const entry = stagedRef.current.find((s) => s.id === id)\n if (!entry) return\n void upload(entry.id, entry.file, entry.name)\n },\n [upload],\n )\n\n const removeAttachment = useCallback(\n (id: string) => {\n controllersRef.current.get(id)?.abort()\n controllersRef.current.delete(id)\n const entry = stagedRef.current.find((s) => s.id === id)\n if (entry?.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n setStaged((prev) => prev.filter((s) => s.id !== id))\n },\n [setStaged],\n )\n\n const clear = useCallback(() => {\n for (const controller of controllersRef.current.values()) controller.abort()\n controllersRef.current.clear()\n for (const entry of stagedRef.current) {\n if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n }\n setStaged([])\n }, [setStaged])\n\n useEffect(\n () => () => {\n for (const controller of controllersRef.current.values()) controller.abort()\n controllersRef.current.clear()\n for (const entry of stagedRef.current) {\n if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n }\n },\n [],\n )\n\n const composerFiles = useMemo<ComposerFile[]>(\n () =>\n staged.map((s) => ({\n id: s.id,\n name: s.name,\n size: s.size,\n kind: 'file' as const,\n status: s.status,\n })),\n [staged],\n )\n\n const references = useMemo<ChatAttachmentInput[]>(\n () =>\n staged\n .filter((s): s is StagedAttachment & { reference: ChatAttachmentInput } => s.status === 'ready' && !!s.reference)\n .map((s) => s.reference),\n [staged],\n )\n\n const hasPending = useMemo(\n () => staged.some((s) => s.status === 'pending' || s.status === 'uploading'),\n [staged],\n )\n const hasError = useMemo(() => staged.some((s) => s.status === 'error'), [staged])\n const enabled = options.enabled ?? true\n const blockReason = !enabled\n ? 'Attachments are disabled'\n : hasPending\n ? 'Attachments are still uploading'\n : hasError\n ? 'Remove failed attachments to send'\n : null\n\n return {\n composerFiles,\n references,\n addFiles,\n retry,\n removeAttachment,\n clear,\n hasPending,\n hasError,\n blockReason,\n }\n}\n"],"mappings":"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","names":["enabled"]}