@openchambery/web 1.19.13-beta.1 → 1.19.13-beta.2

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 (146) hide show
  1. package/dist/assets/{AgentAvatar-CiQIIM0d.js → AgentAvatar-mbvX7YlW.js} +1 -1
  2. package/dist/assets/AssistantView-D5c557Oe.js +2 -0
  3. package/dist/assets/DiagramView-CjNUrHsU.js +1 -0
  4. package/dist/assets/FadeInOnReveal-moBbL8Ll.js +1 -0
  5. package/dist/assets/{Icon-CeKHYyKZ.js → Icon-CkzzhaLr.js} +1 -1
  6. package/dist/assets/MarkdownRenderer-BHfbAwVO.js +2 -0
  7. package/dist/assets/MarkdownRendererImpl-CkBoUibc.js +7 -0
  8. package/dist/assets/{MarkstreamRendererImpl-B-odd2Gz.js → MarkstreamRendererImpl-BrXUOI9g.js} +1 -1
  9. package/dist/assets/MobileDetailNavigation-aY9-HwBN.js +1 -0
  10. package/dist/assets/MobileShareBridge-CRt_8DmM.js +4 -0
  11. package/dist/assets/MobileSurface-CIhtF-CU.js +2 -0
  12. package/dist/assets/{MultiRunWindow-BT5Dwgzu.js → MultiRunWindow-DDq6C8t7.js} +1 -1
  13. package/dist/assets/OnboardingScreen-j0iCyH7f.js +1 -0
  14. package/dist/assets/{OverlayScrollbar-DUghqFGg.js → OverlayScrollbar-CDC5B3RH.js} +1 -1
  15. package/dist/assets/SettingsView-DFWWMvAj.js +25 -0
  16. package/dist/assets/{SettingsWindow-DfEGfyr8.js → SettingsWindow-CnYfBplB.js} +1 -1
  17. package/dist/assets/TerminalView-D5CQFVVm.js +8 -0
  18. package/dist/assets/ToolOutputDialog-VMI7TQFT.js +10 -0
  19. package/dist/assets/{appThemeRegistry-CIZ-shMD.js → appThemeRegistry-UIrP0-5h.js} +1 -1
  20. package/dist/assets/assistant-attachment-cache-C0aRnFkj.js +1 -0
  21. package/dist/assets/{button-DFPOdZ0g.js → button-xx1UOBA5.js} +1 -1
  22. package/dist/assets/{clipboard-C05anBwU.js → clipboard-CUasgPiF.js} +1 -1
  23. package/dist/assets/{collapsible-oGbEvbe1.js → collapsible-CACHBRYZ.js} +1 -1
  24. package/dist/assets/{context-menu-DGz4_2hs.js → context-menu-CYbm4wBZ.js} +1 -1
  25. package/dist/assets/desktopHosts-DILvrAok.js +1 -0
  26. package/dist/assets/desktopRelayRestore-t8asCr3m.js +1 -0
  27. package/dist/assets/{diagram-Csk5GpDC.js → diagram-u0BC9uuY.js} +1 -1
  28. package/dist/assets/{dialog-rrqB-H6q.js → dialog-BwQ3rY_x.js} +1 -1
  29. package/dist/assets/dualLimitLru-D8Sphgua.js +1 -0
  30. package/dist/assets/{es-FE0sJ9EC.js → es-CRjOwvYM.js} +1 -1
  31. package/dist/assets/fileQueries-Bgly-E8f.js +1 -0
  32. package/dist/assets/{fr-DNDCSfGt.js → fr-D9taEN9L.js} +1 -1
  33. package/dist/assets/{gitApi-_P0FsoXe.js → gitApi-Bu4W3bgD.js} +1 -1
  34. package/dist/assets/imageSource-BlAJJUEI.js +1 -0
  35. package/dist/assets/{input-BO3r20op.js → input-CI730njs.js} +1 -1
  36. package/dist/assets/insertionBoundaries-B1AqxsBD.js +4 -0
  37. package/dist/assets/{ja-BDwrk0cM.js → ja-CYQ5aoWp.js} +1 -1
  38. package/dist/assets/{ko-BeeU2o4I.js → ko-BwASV0Q0.js} +1 -1
  39. package/dist/assets/main-CqcB6tKc.js +169 -0
  40. package/dist/assets/main-DG-k0j8A.js +2 -0
  41. package/dist/assets/markdown-worker-CptoICmq.js +2 -0
  42. package/dist/assets/markdownHeightCache-DEjGKLv8.js +3 -0
  43. package/dist/assets/markdownTheme-DOfGg-A7.js +1 -0
  44. package/dist/assets/miniChat-BQtYQrsh.js +2 -0
  45. package/dist/assets/mobile-8iyqtXkr.js +2 -0
  46. package/dist/assets/{multirun-CxhTsmRq.js → multirun-CG_FCb_Z.js} +1 -1
  47. package/dist/assets/persistence-YbQKzHUQ.js +3 -0
  48. package/dist/assets/{pl-BYuzpcI5.js → pl-DUI1vPil.js} +1 -1
  49. package/dist/assets/platform-DzsGqlGW.js +2 -0
  50. package/dist/assets/projectMeta-DMpMDWE1.js +3 -0
  51. package/dist/assets/{pt-BR-Bn7cwEzv.js → pt-BR-CZQFJllP.js} +1 -1
  52. package/dist/assets/relay-image-stream-DM7atqJs.js +2 -0
  53. package/dist/assets/renderElectronMiniChatApp-DBzRdJl8.js +2 -0
  54. package/dist/assets/renderMobileApp-CIseqCyn.js +28 -0
  55. package/dist/assets/runtime-auth-D66yq6ie.js +1 -0
  56. package/dist/assets/runtime-fetch-zIalUnbb.js +1 -0
  57. package/dist/assets/runtime-switch-LvWDU-SL.js +1 -0
  58. package/dist/assets/runtimeConfig-D8nJYB-e.js +2 -0
  59. package/dist/assets/{runtimeEndpointReset-BBLFgl85.js → runtimeEndpointReset-CSub_ecV.js} +6 -6
  60. package/dist/assets/{sessionChangeSummary-DDVT7O5J.js → sessionChangeSummary-DQDZ5HW-.js} +1 -1
  61. package/dist/assets/{sessionLookup-C8VZn0gJ.js → sessionLookup-BiBD-4wC.js} +1 -1
  62. package/dist/assets/sortable-tabs-strip-Dom6Sthu.js +1 -0
  63. package/dist/assets/src-AA-5nXsF.css +2 -0
  64. package/dist/assets/{store-n94dSb3M.js → store-CSnH_QXM.js} +2 -2
  65. package/dist/assets/terminalApi-noa4Cdbh.js +1 -0
  66. package/dist/assets/{textarea-BjX-KhMN.js → textarea-BfNtUdJE.js} +1 -1
  67. package/dist/assets/{tooltip-CQGqGAG_.js → tooltip-DPxumadF.js} +1 -1
  68. package/dist/assets/{uk-CwpbHEa3.js → uk-C1FYZlmA.js} +1 -1
  69. package/dist/assets/{url-BMjqccCo.js → url-CVhsI9kR.js} +1 -1
  70. package/dist/assets/{useAppFontEffects-BIFw36SA.js → useAppFontEffects-qHt5uaFN.js} +32 -32
  71. package/dist/assets/useAssistantUIStore-CLPCNoj_.js +1 -0
  72. package/dist/assets/useDesktopWindowControlsLayout-BipyIXiK.js +1 -0
  73. package/dist/assets/{useEffectiveDirectory-DJSYYkVp.js → useEffectiveDirectory-DSNCCbCt.js} +1 -1
  74. package/dist/assets/useFontPreferences-CB-cGo3I.js +1 -0
  75. package/dist/assets/useMobileNavigationStore-C1NJGAik.js +413 -0
  76. package/dist/assets/useSessionAutoCleanup-Dcn6Unvd.js +2 -0
  77. package/dist/assets/useUIStore-CxOoCv7B.js +1 -0
  78. package/dist/assets/useWorkerHighlightedLines-CzpT4M7V.js +7 -0
  79. package/dist/assets/{utils-81nUI_xp.js → utils-BHTVlVov.js} +1 -1
  80. package/dist/assets/{zh-CN-BpzFZ-LU.js → zh-CN-C96qP1ip.js} +1 -1
  81. package/dist/assets/{zh-TW-Bopgc5Eo.js → zh-TW-BsRk1Rbb.js} +1 -1
  82. package/dist/index.html +12 -9
  83. package/dist/mini-chat.html +12 -9
  84. package/dist/mobile.html +12 -9
  85. package/package.json +1 -1
  86. package/server/lib/assistant-delivery-parts.js +19 -1
  87. package/server/lib/assistants/DOCUMENTATION.md +39 -7
  88. package/server/lib/assistants/cards.js +16 -2
  89. package/server/lib/assistants/cards.test.js +16 -1
  90. package/server/lib/assistants/contact-attachments.http.test.js +436 -0
  91. package/server/lib/assistants/contact-attachments.js +814 -0
  92. package/server/lib/assistants/contact-attachments.p1.test.js +307 -0
  93. package/server/lib/assistants/contact-attachments.test.js +249 -0
  94. package/server/lib/assistants/contact-store.js +494 -25
  95. package/server/lib/assistants/contact-store.test.js +403 -1
  96. package/server/lib/assistants/contact-tools.js +6 -5
  97. package/server/lib/assistants/contact-tools.test.js +24 -6
  98. package/server/lib/assistants/contracts.js +41 -1
  99. package/server/lib/assistants/harness.test.js +124 -0
  100. package/server/lib/assistants/routes.js +46 -1
  101. package/server/lib/assistants/service.js +258 -66
  102. package/server/lib/assistants/service.test.js +400 -30
  103. package/server/lib/fs/prompt-attachment-store.js +663 -0
  104. package/server/lib/fs/prompt-attachment-store.test.js +263 -0
  105. package/server/lib/fs/routes.js +28 -72
  106. package/server/lib/fs/routes.test.js +15 -30
  107. package/server/lib/opencode/core-routes.js +2 -1
  108. package/dist/assets/AssistantView-YmeyxehG.js +0 -2
  109. package/dist/assets/DiagramView-BpZb-G9P.js +0 -1
  110. package/dist/assets/FadeInOnReveal-nf6-ZCrs.js +0 -1
  111. package/dist/assets/MarkdownRenderer-wpeGdciL.js +0 -2
  112. package/dist/assets/MarkdownRendererImpl-Peu0GVSQ.js +0 -7
  113. package/dist/assets/MobileDetailNavigation-CtYke_1C.js +0 -1
  114. package/dist/assets/MobileShareBridge-DIfOl8EZ.js +0 -4
  115. package/dist/assets/MobileSurface-_KoX75Ff.js +0 -2
  116. package/dist/assets/OnboardingScreen-BPusUgFG.js +0 -1
  117. package/dist/assets/SettingsView-BwQpbIZl.js +0 -25
  118. package/dist/assets/TerminalView-8402vpcO.js +0 -8
  119. package/dist/assets/ToolOutputDialog-B5RdkhsG.js +0 -10
  120. package/dist/assets/desktopHosts-BVJXQq1S.js +0 -1
  121. package/dist/assets/desktopRelayRestore-RDfuj1M7.js +0 -1
  122. package/dist/assets/fileQueries-BcdWOPlD.js +0 -1
  123. package/dist/assets/insertionBoundaries-CWoioTdE.js +0 -4
  124. package/dist/assets/main-CLj_WS1o.js +0 -2
  125. package/dist/assets/main-CnSZ89pD.js +0 -169
  126. package/dist/assets/markdown-worker-CZWE1F1M.js +0 -2
  127. package/dist/assets/markdownHeightCache-Cdd3hkMl.js +0 -3
  128. package/dist/assets/markdownTheme-DIB8WVmz.js +0 -2
  129. package/dist/assets/miniChat-B9c3y2pD.js +0 -2
  130. package/dist/assets/mobile-DvbXUfNS.js +0 -2
  131. package/dist/assets/persistence-CVTWFaqe.js +0 -3
  132. package/dist/assets/platform-BY_Gtsqr.js +0 -2
  133. package/dist/assets/projectMeta-Dl5iPCB6.js +0 -3
  134. package/dist/assets/renderElectronMiniChatApp-CkJ51qCh.js +0 -2
  135. package/dist/assets/renderMobileApp-DcjQPhWw.js +0 -28
  136. package/dist/assets/runtimeConfig-Bsk2kWsW.js +0 -2
  137. package/dist/assets/sortable-tabs-strip-s7UUxsa6.js +0 -1
  138. package/dist/assets/src-BpHDMgud.css +0 -2
  139. package/dist/assets/terminalApi-C54_rKEK.js +0 -1
  140. package/dist/assets/useAssistantUIStore-Cg9bsT4b.js +0 -1
  141. package/dist/assets/useDesktopWindowControlsLayout-DoygEcN4.js +0 -1
  142. package/dist/assets/useFontPreferences-B5_apbBC.js +0 -1
  143. package/dist/assets/useMobileNavigationStore-DuTu0Mzh.js +0 -413
  144. package/dist/assets/useSessionAutoCleanup-C-L9teva.js +0 -1
  145. package/dist/assets/useUIStore-DwpQTjLO.js +0 -1
  146. package/dist/assets/useWorkerHighlightedLines-DxTfgdEi.js +0 -7
@@ -0,0 +1,263 @@
1
+ import fs from 'node:fs';
2
+ import fsp from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { createHash } from 'node:crypto';
6
+ import { afterEach, describe, expect, it, vi } from 'vitest';
7
+ import {
8
+ assertImageContentMatchesMime,
9
+ decodeDataUrlStrict,
10
+ relativeObjectPath,
11
+ storePromptAttachmentBytes,
12
+ readPromptAttachmentBytes,
13
+ promptAttachmentsRoot,
14
+ } from './prompt-attachment-store.js';
15
+
16
+ const png = Buffer.from([
17
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
18
+ 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
19
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
20
+ 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53,
21
+ 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41,
22
+ 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xcf, 0xc0, 0x00,
23
+ 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x05, 0xfe,
24
+ 0xd4, 0xef, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
25
+ 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
26
+ ]);
27
+
28
+ const sha256 = (buffer) => createHash('sha256').update(buffer).digest('hex');
29
+
30
+ const makeDataDir = () => fs.mkdtempSync(path.join(os.tmpdir(), 'pas-'));
31
+
32
+ describe('prompt-attachment-store', () => {
33
+ afterEach(() => {
34
+ vi.restoreAllMocks();
35
+ });
36
+
37
+ it('stores content-addressed bytes and rejects path traversal relative paths', async () => {
38
+ const dataDir = makeDataDir();
39
+ const digest = sha256(png);
40
+ const stored = await storePromptAttachmentBytes({
41
+ dataDir,
42
+ buffer: png,
43
+ expectedSha256: digest,
44
+ mime: 'image/png',
45
+ filename: 'x.png',
46
+ });
47
+ expect(stored.relativePath).toBe(relativeObjectPath(digest, 'image/png'));
48
+ expect(stored.sha256).toBe(digest);
49
+ const again = await storePromptAttachmentBytes({
50
+ dataDir,
51
+ buffer: png,
52
+ expectedSha256: digest,
53
+ mime: 'image/png',
54
+ filename: 'x.png',
55
+ });
56
+ expect(again.absolutePath).toBe(stored.absolutePath);
57
+ const read = await readPromptAttachmentBytes(dataDir, stored.relativePath);
58
+ expect(read.buffer.equals(png)).toBe(true);
59
+ await expect(readPromptAttachmentBytes(dataDir, '../etc/passwd')).rejects.toMatchObject({
60
+ code: 'attachment_unsafe_path',
61
+ });
62
+ await fsp.rm(dataDir, { recursive: true, force: true });
63
+ });
64
+
65
+ it('rejects image mime/signature mismatch', () => {
66
+ expect(() => assertImageContentMatchesMime(Buffer.from('not-an-image'), 'image/png')).toThrowError(
67
+ expect.objectContaining({ code: 'attachment_mime_mismatch' }),
68
+ );
69
+ expect(() => assertImageContentMatchesMime(png, 'image/png')).not.toThrow();
70
+ });
71
+
72
+ describe('decodeDataUrlStrict', () => {
73
+ it('preserves literal plus in non-base64 data URLs (not form-urlencoded spaces)', () => {
74
+ const decoded = decodeDataUrlStrict('data:text/plain,a+b');
75
+ expect(decoded.buffer.toString('utf8')).toBe('a+b');
76
+ expect(decoded.mime).toBe('text/plain');
77
+ });
78
+
79
+ it('percent-decodes %2B to plus and rejects malformed percent sequences', () => {
80
+ expect(decodeDataUrlStrict('data:text/plain,a%2Bb').buffer.toString('utf8')).toBe('a+b');
81
+ expect(() => decodeDataUrlStrict('data:text/plain,a%zz')).toThrowError(
82
+ expect.objectContaining({ code: 'validation_error' }),
83
+ );
84
+ expect(() => decodeDataUrlStrict('data:text/plain,a%2')).toThrowError(
85
+ expect.objectContaining({ code: 'validation_error' }),
86
+ );
87
+ });
88
+
89
+ it('rejects invalid base64 alphabet and broken padding', () => {
90
+ expect(() => decodeDataUrlStrict('data:text/plain;base64,!!!')).toThrowError(
91
+ expect.objectContaining({ code: 'validation_error' }),
92
+ );
93
+ expect(() => decodeDataUrlStrict('data:text/plain;base64,YWJ')).toThrowError(
94
+ expect.objectContaining({ code: 'validation_error' }),
95
+ );
96
+ expect(() => decodeDataUrlStrict('data:text/plain;base64,YWJj====')).toThrowError(
97
+ expect.objectContaining({ code: 'validation_error' }),
98
+ );
99
+ });
100
+
101
+ it('allows html and svg data URLs for ordinary chat source uploads', () => {
102
+ const html = decodeDataUrlStrict('data:text/html,<h1>x</h1>');
103
+ expect(html.buffer.toString('utf8')).toBe('<h1>x</h1>');
104
+ expect(html.mime).toBe('text/html');
105
+ const svg = decodeDataUrlStrict('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"></svg>');
106
+ expect(svg.mime).toBe('image/svg+xml');
107
+ expect(svg.buffer.toString('utf8')).toContain('<svg');
108
+ });
109
+ });
110
+
111
+ it('allows HTML and SVG file storage (display safety is not this module)', async () => {
112
+ const dataDir = makeDataDir();
113
+ const html = Buffer.from('<!doctype html><title>t</title>', 'utf8');
114
+ const svg = Buffer.from('<svg xmlns="http://www.w3.org/2000/svg"/>', 'utf8');
115
+ const htmlStored = await storePromptAttachmentBytes({
116
+ dataDir,
117
+ buffer: html,
118
+ mime: 'text/html',
119
+ validateImage: false,
120
+ });
121
+ const svgStored = await storePromptAttachmentBytes({
122
+ dataDir,
123
+ buffer: svg,
124
+ mime: 'image/svg+xml',
125
+ validateImage: false,
126
+ });
127
+ expect(htmlStored.sha256).toBe(sha256(html));
128
+ expect(svgStored.sha256).toBe(sha256(svg));
129
+ const htmlRead = await readPromptAttachmentBytes(dataDir, htmlStored.relativePath);
130
+ const svgRead = await readPromptAttachmentBytes(dataDir, svgStored.relativePath);
131
+ expect(htmlRead.buffer.equals(html)).toBe(true);
132
+ expect(svgRead.buffer.equals(svg)).toBe(true);
133
+ await fsp.rm(dataDir, { recursive: true, force: true });
134
+ });
135
+
136
+ it('completes short writes instead of leaving truncated objects', async () => {
137
+ const dataDir = makeDataDir();
138
+ const body = Buffer.from('0123456789abcdefghijklmnopqrstuvwxyz', 'utf8');
139
+ const digest = sha256(body);
140
+ const openOrig = fsp.open.bind(fsp);
141
+ let shortWrote = false;
142
+ vi.spyOn(fsp, 'open').mockImplementation(async (filePath, flags, mode) => {
143
+ const handle = await openOrig(filePath, flags, mode);
144
+ if (typeof filePath === 'string' && filePath.includes('.tmp-')) {
145
+ const writeOrig = handle.write.bind(handle);
146
+ handle.write = async (buffer, offset = 0, length = buffer.length - offset, position = null) => {
147
+ if (!shortWrote && length > 1) {
148
+ shortWrote = true;
149
+ return writeOrig(buffer, offset, 1, position);
150
+ }
151
+ return writeOrig(buffer, offset, length, position);
152
+ };
153
+ }
154
+ return handle;
155
+ });
156
+
157
+ const stored = await storePromptAttachmentBytes({
158
+ dataDir,
159
+ buffer: body,
160
+ expectedSha256: digest,
161
+ mime: 'text/plain',
162
+ validateImage: false,
163
+ });
164
+ expect(shortWrote).toBe(true);
165
+ expect(stored.size).toBe(body.length);
166
+ const onDisk = await fsp.readFile(stored.absolutePath);
167
+ expect(onDisk.equals(body)).toBe(true);
168
+ expect(sha256(onDisk)).toBe(digest);
169
+ await fsp.rm(dataDir, { recursive: true, force: true });
170
+ });
171
+
172
+ it('rejects store-root and hash-prefix symlink escape (NOFOLLOW alone is not enough)', async () => {
173
+ const base = makeDataDir();
174
+ const dataDir = path.join(base, 'data');
175
+ const outside = path.join(base, 'outside-secret');
176
+ fs.mkdirSync(dataDir, { recursive: true });
177
+ fs.writeFileSync(outside, 'secret-leak');
178
+
179
+ const digest = sha256(png);
180
+ const prefix = digest.slice(0, 2);
181
+ const storeRoot = promptAttachmentsRoot(dataDir);
182
+ fs.mkdirSync(storeRoot, { recursive: true });
183
+ const prefixPath = path.join(storeRoot, prefix);
184
+ try {
185
+ fs.symlinkSync(path.dirname(outside), prefixPath);
186
+ } catch {
187
+ await fsp.rm(base, { recursive: true, force: true });
188
+ return;
189
+ }
190
+
191
+ await expect(storePromptAttachmentBytes({
192
+ dataDir,
193
+ buffer: png,
194
+ expectedSha256: digest,
195
+ mime: 'image/png',
196
+ validateImage: true,
197
+ })).rejects.toMatchObject({ code: 'attachment_unsafe_path' });
198
+
199
+ // Must not have written the object through the escaped prefix.
200
+ const escapedCandidate = path.join(path.dirname(outside), `${digest}.png`);
201
+ expect(fs.existsSync(escapedCandidate)).toBe(false);
202
+
203
+ await expect(readPromptAttachmentBytes(
204
+ dataDir,
205
+ relativeObjectPath(digest, 'image/png'),
206
+ )).rejects.toMatchObject({
207
+ code: expect.stringMatching(/attachment_unsafe_path|attachment_not_found/),
208
+ });
209
+
210
+ await fsp.rm(base, { recursive: true, force: true });
211
+ });
212
+
213
+ it('rejects corrupt existing object whose bytes do not match addressing hash', async () => {
214
+ const dataDir = makeDataDir();
215
+ const digest = sha256(png);
216
+ const relativePath = relativeObjectPath(digest, 'image/png');
217
+ const storeRoot = promptAttachmentsRoot(dataDir);
218
+ const absolutePath = path.join(storeRoot, relativePath);
219
+ fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
220
+ fs.writeFileSync(absolutePath, Buffer.from('not-the-png-bytes'));
221
+
222
+ await expect(storePromptAttachmentBytes({
223
+ dataDir,
224
+ buffer: png,
225
+ expectedSha256: digest,
226
+ mime: 'image/png',
227
+ validateImage: true,
228
+ })).rejects.toMatchObject({ code: 'attachment_hash_mismatch' });
229
+
230
+ await fsp.rm(dataDir, { recursive: true, force: true });
231
+ });
232
+
233
+ it('rejects read when on-disk bytes diverge from path addressing hash or expected hash/size', async () => {
234
+ const dataDir = makeDataDir();
235
+ const digest = sha256(png);
236
+ const relativePath = relativeObjectPath(digest, 'image/png');
237
+ const storeRoot = promptAttachmentsRoot(dataDir);
238
+ const absolutePath = path.join(storeRoot, relativePath);
239
+ fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
240
+ fs.writeFileSync(absolutePath, Buffer.from('corrupt-payload'));
241
+
242
+ await expect(readPromptAttachmentBytes(dataDir, relativePath)).rejects.toMatchObject({
243
+ code: 'attachment_hash_mismatch',
244
+ });
245
+
246
+ // Plant correct bytes then fail optional caller expectations.
247
+ fs.writeFileSync(absolutePath, png);
248
+ await expect(readPromptAttachmentBytes(dataDir, relativePath, {
249
+ expectedSha256: 'a'.repeat(64),
250
+ })).rejects.toMatchObject({ code: 'attachment_hash_mismatch' });
251
+ await expect(readPromptAttachmentBytes(dataDir, relativePath, {
252
+ expectedSize: png.length + 1,
253
+ })).rejects.toMatchObject({ code: 'PROMPT_ATTACHMENT_SIZE_MISMATCH' });
254
+
255
+ const ok = await readPromptAttachmentBytes(dataDir, relativePath, {
256
+ expectedSha256: digest,
257
+ expectedSize: png.length,
258
+ });
259
+ expect(ok.buffer.equals(png)).toBe(true);
260
+
261
+ await fsp.rm(dataDir, { recursive: true, force: true });
262
+ });
263
+ });
@@ -2,6 +2,15 @@ import { createHash } from 'node:crypto';
2
2
  import { createRealpathCache } from '../path-realpath-cache.js';
3
3
  import nodeFsPromises from 'node:fs/promises';
4
4
  import nodePath from 'node:path';
5
+ import {
6
+ MAX_PROMPT_ATTACHMENT_BYTES,
7
+ PROMPT_ATTACHMENT_ID,
8
+ PROMPT_ATTACHMENT_MIME,
9
+ PROMPT_ATTACHMENT_SHA256,
10
+ collectRequestBytes,
11
+ promptAttachmentExtension,
12
+ storePromptAttachmentBytes,
13
+ } from './prompt-attachment-store.js';
5
14
 
6
15
  const EXEC_JOB_TTL_MS = 30 * 60 * 1000;
7
16
  const OUTSIDE_FILE_GRANT_TTL_MS = 10 * 60 * 1000;
@@ -363,58 +372,11 @@ const runCommandInDirectory = ({ shell, shellFlag, command, resolvedCwd, spawn,
363
372
  });
364
373
  };
365
374
 
366
- const MAX_PROMPT_ATTACHMENT_BYTES = 25 * 1024 * 1024;
367
- const PROMPT_ATTACHMENT_ID = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
368
- const PROMPT_ATTACHMENT_SHA256 = /^[a-f0-9]{64}$/;
369
- const PROMPT_ATTACHMENT_MIME = /^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/i;
370
-
371
375
  const headerValue = (value) => {
372
376
  if (Array.isArray(value)) return value[0];
373
377
  return typeof value === 'string' ? value : undefined;
374
378
  };
375
379
 
376
- const promptAttachmentExtension = (mime, filename) => {
377
- const fromName = typeof filename === 'string' ? nodePath.posix.extname(filename).toLowerCase() : '';
378
- if (fromName && fromName.length <= 16 && /^\.[a-z0-9.]+$/i.test(fromName)) return fromName;
379
- const lowerMime = typeof mime === 'string' ? mime.toLowerCase() : '';
380
- if (lowerMime === 'image/jpeg') return '.jpg';
381
- if (lowerMime === 'image/png') return '.png';
382
- if (lowerMime === 'image/gif') return '.gif';
383
- if (lowerMime === 'image/webp') return '.webp';
384
- if (lowerMime === 'image/heic') return '.heic';
385
- if (lowerMime === 'image/heif') return '.heif';
386
- if (lowerMime === 'image/avif') return '.avif';
387
- if (lowerMime === 'image/bmp') return '.bmp';
388
- if (lowerMime === 'image/svg+xml') return '.svg';
389
- return '.bin';
390
- };
391
-
392
- const collectRequestBytes = async (req, { expectedSize, maxBytes, signal } = {}) => {
393
- const chunks = [];
394
- let size = 0;
395
- for await (const chunk of req) {
396
- if (signal?.aborted) {
397
- const error = new Error('Upload aborted');
398
- error.code = 'PROMPT_ATTACHMENT_ABORTED';
399
- throw error;
400
- }
401
- const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
402
- size += bytes.byteLength;
403
- if (size > maxBytes) {
404
- const error = new Error('Attachment too large');
405
- error.code = 'PROMPT_ATTACHMENT_TOO_LARGE';
406
- throw error;
407
- }
408
- chunks.push(bytes);
409
- }
410
- if (expectedSize !== undefined && size !== expectedSize) {
411
- const error = new Error('Attachment size mismatch');
412
- error.code = 'PROMPT_ATTACHMENT_SIZE_MISMATCH';
413
- throw error;
414
- }
415
- return { buffer: Buffer.concat(chunks, size), size };
416
- };
417
-
418
380
  export const registerFsRoutes = (app, dependencies) => {
419
381
  const {
420
382
  os,
@@ -1109,38 +1071,29 @@ export const registerFsRoutes = (app, dependencies) => {
1109
1071
  const controller = new AbortController();
1110
1072
  req.once?.('aborted', () => controller.abort());
1111
1073
 
1112
- let tmpPath;
1113
1074
  try {
1114
1075
  const { buffer, size } = await collectRequestBytes(req, {
1115
1076
  expectedSize,
1116
1077
  maxBytes: MAX_PROMPT_ATTACHMENT_BYTES,
1117
1078
  signal: controller.signal,
1118
1079
  });
1119
- const digest = createHash('sha256').update(buffer).digest('hex');
1120
- if (digest !== expectedSha256) {
1121
- return res.status(400).json({ error: 'Attachment digest mismatch' });
1122
- }
1123
-
1124
- const storeRoot = path.resolve(dataDir, 'prompt-attachments');
1125
- const destinationDir = path.join(storeRoot, digest.slice(0, 2));
1126
- const destination = path.join(destinationDir, `${digest}${promptAttachmentExtension(mimeType, filename)}`);
1127
- await fsPromises.mkdir(destinationDir, { recursive: true, mode: 0o700 });
1128
- tmpPath = `${destination}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1129
- await fsPromises.writeFile(tmpPath, buffer, { mode: 0o600 });
1130
- try {
1131
- await fsPromises.rename(tmpPath, destination);
1132
- } catch (error) {
1133
- if (error && typeof error === 'object' && (error.code === 'EEXIST' || error.code === 'ENOTEMPTY')) {
1134
- await fsPromises.unlink(tmpPath).catch(() => {});
1135
- tmpPath = undefined;
1136
- } else {
1137
- throw error;
1138
- }
1139
- }
1140
- tmpPath = undefined;
1141
- return res.json({ success: true, path: destination, size, mime: mimeType, sha256: digest });
1080
+ const stored = await storePromptAttachmentBytes({
1081
+ dataDir,
1082
+ buffer,
1083
+ expectedSha256,
1084
+ mime: mimeType,
1085
+ filename,
1086
+ maxBytes: MAX_PROMPT_ATTACHMENT_BYTES,
1087
+ validateImage: false,
1088
+ });
1089
+ return res.json({
1090
+ success: true,
1091
+ path: stored.absolutePath,
1092
+ size: stored.size,
1093
+ mime: stored.mime,
1094
+ sha256: stored.sha256,
1095
+ });
1142
1096
  } catch (error) {
1143
- if (tmpPath) await fsPromises.unlink(tmpPath).catch(() => {});
1144
1097
  const code = error && typeof error === 'object' ? error.code : undefined;
1145
1098
  if (code === 'PROMPT_ATTACHMENT_TOO_LARGE') {
1146
1099
  return res.status(413).json({ error: 'Attachment too large' });
@@ -1151,6 +1104,9 @@ export const registerFsRoutes = (app, dependencies) => {
1151
1104
  if (code === 'PROMPT_ATTACHMENT_ABORTED') {
1152
1105
  return res.status(499).json({ error: 'Upload aborted' });
1153
1106
  }
1107
+ if (code === 'attachment_hash_mismatch') {
1108
+ return res.status(400).json({ error: 'Attachment digest mismatch' });
1109
+ }
1154
1110
  console.error('Failed to store prompt attachment:', error);
1155
1111
  return res.status(500).json({ error: 'Failed to store prompt attachment' });
1156
1112
  }
@@ -1,4 +1,7 @@
1
1
  import { EventEmitter } from 'events';
2
+ import { createHash } from 'node:crypto';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
2
5
  import path from 'path';
3
6
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
7
 
@@ -323,15 +326,10 @@ describe('fs stat', () => {
323
326
 
324
327
  describe('fs prompt attachments', () => {
325
328
  it('stores binary bytes under the data-dir content-addressed path', async () => {
326
- const fsPromises = {
327
- mkdir: vi.fn(async () => undefined),
328
- writeFile: vi.fn(async () => undefined),
329
- rename: vi.fn(async () => undefined),
330
- unlink: vi.fn(async () => undefined),
331
- };
332
- const handler = registerPromptAttachment(fsPromises);
329
+ const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fs-prompt-att-'));
330
+ const handler = registerPromptAttachment({}, { openchamberDataDir: dataDir });
333
331
  const body = Buffer.from('hello-image');
334
- const digest = 'a9a8fa077e26f69bcc4a7eeec6e1827a07ae07281952ae755766b9f06c8185e1';
332
+ const digest = createHash('sha256').update(body).digest('hex');
335
333
  const req = {
336
334
  params: { attachmentID: 'att-1' },
337
335
  headers: {
@@ -347,34 +345,20 @@ describe('fs prompt attachments', () => {
347
345
  const res = createMockResponse();
348
346
  await handler(req, res);
349
347
 
350
- expect(res.body).toEqual({
348
+ expect(res.body).toMatchObject({
351
349
  success: true,
352
- path: `/data/openchamber/prompt-attachments/${digest.slice(0, 2)}/${digest}.png`,
353
350
  size: body.length,
354
351
  mime: 'image/png',
355
352
  sha256: digest,
356
353
  });
357
- expect(fsPromises.mkdir).toHaveBeenCalledWith(
358
- `/data/openchamber/prompt-attachments/${digest.slice(0, 2)}`,
359
- { recursive: true, mode: 0o700 },
360
- );
361
- const tmp = fsPromises.writeFile.mock.calls[0][0];
362
- expect(tmp).toMatch(new RegExp(`^/data/openchamber/prompt-attachments/${digest.slice(0, 2)}/${digest}\\.png\\.tmp-`));
363
- expect(fsPromises.writeFile).toHaveBeenCalledWith(tmp, body, { mode: 0o600 });
364
- expect(fsPromises.rename).toHaveBeenCalledWith(
365
- tmp,
366
- `/data/openchamber/prompt-attachments/${digest.slice(0, 2)}/${digest}.png`,
367
- );
354
+ expect(fs.existsSync(res.body.path)).toBe(true);
355
+ expect(path.basename(res.body.path)).toBe(`${digest}.png`);
356
+ fs.rmSync(dataDir, { recursive: true, force: true });
368
357
  });
369
358
 
370
359
  it('rejects digest mismatches without writing a durable file', async () => {
371
- const fsPromises = {
372
- mkdir: vi.fn(async () => undefined),
373
- writeFile: vi.fn(async () => undefined),
374
- rename: vi.fn(async () => undefined),
375
- unlink: vi.fn(async () => undefined),
376
- };
377
- const handler = registerPromptAttachment(fsPromises);
360
+ const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fs-prompt-att-bad-'));
361
+ const handler = registerPromptAttachment({}, { openchamberDataDir: dataDir });
378
362
  const body = Buffer.from('hello-image');
379
363
  const req = {
380
364
  params: { attachmentID: 'att-1' },
@@ -391,8 +375,9 @@ describe('fs prompt attachments', () => {
391
375
  await handler(req, res);
392
376
  expect(res.statusCode).toBe(400);
393
377
  expect(res.body).toEqual({ error: 'Attachment digest mismatch' });
394
- expect(fsPromises.writeFile).not.toHaveBeenCalled();
395
- expect(fsPromises.rename).not.toHaveBeenCalled();
378
+ const storeRoot = path.join(dataDir, 'prompt-attachments');
379
+ expect(fs.existsSync(storeRoot) ? fs.readdirSync(storeRoot).length : 0).toBe(0);
380
+ fs.rmSync(dataDir, { recursive: true, force: true });
396
381
  });
397
382
 
398
383
  it('reports unavailable storage when the data dir is missing', async () => {
@@ -1157,10 +1157,11 @@ export const registerCommonRequestMiddleware = (app, dependencies) => {
1157
1157
  } else if (
1158
1158
  (req.method === 'PUT' && req.path.match(/^\/api\/openchamber\/message-queue\/attachments\/uploads\/[^/]+\/?$/))
1159
1159
  || (req.method === 'PUT' && req.path.match(/^\/api\/fs\/prompt-attachments\/[^/]+\/?$/))
1160
+ || (req.method === 'PUT' && req.path.match(/^\/api\/openchamber\/assistants\/[^/]+\/contact\/attachments\/[^/]+\/?$/))
1160
1161
  || (req.method === 'PUT' && req.path.match(/^\/api\/openchamber\/config-sync\/put\/[^/]+\/?$/))
1161
1162
  ) {
1162
1163
  // Binary attachment uploads stream as the request body. Parsing JSON here
1163
- // would buffer the entire payload and block the relay tunnel.
1164
+ // would buffer the entire payload and block the relay tunnel / corrupt bytes.
1164
1165
  next();
1165
1166
  } else if (req.path.startsWith('/api/openchamber/message-queue')) {
1166
1167
  express.json({ limit: req.method === 'POST' && req.path === '/api/openchamber/message-queue/items' ? MESSAGE_QUEUE_ADMISSION_HTTP_MAX_BYTES : '1mb' })(req, res, next);
@@ -1,2 +0,0 @@
1
- import{o as e,r as t}from"./rolldown-runtime-C0FnF6B9.js";import{A as n,E as r,t as i}from"./useUIStore-DwpQTjLO.js";import{et as a}from"./persistence-CVTWFaqe.js";import{a as o}from"./vendor-zustand-B4dlGEM2.js";import{nt as s,rt as c}from"./vendor-base-ui-CnHfUmDN.js";import{r as l}from"./platform-BY_Gtsqr.js";import{n as u}from"./vendor-react-DLPElpoQ.js";import{r as d}from"./vendor-sonner-DVzioF3d.js";import{n as f,r as p,u as m}from"./vendor-reactuses-core-DDoDDyM9.js";import{n as h}from"./clipboard-C05anBwU.js";import{a as g,n as _}from"./MobileShareBridge-DIfOl8EZ.js";import{Oo as v,dr as y,qt as b,r as x,vn as S}from"./useMobileNavigationStore-DuTu0Mzh.js";import{g as C,i as w,l as T,m as E,n as D,o as O,p as k,t as A,x as j,y as M}from"./MobileDetailNavigation-CtYke_1C.js";import{i as N,n as P,r as F}from"./useAssistantUIStore-Cg9bsT4b.js";import{a as I,i as L,n as R,o as z,r as B,t as V}from"./dialog-rrqB-H6q.js";import{t as H}from"./utils-81nUI_xp.js";import{t as U}from"./AgentAvatar-CiQIIM0d.js";import{n as W}from"./Icon-CeKHYyKZ.js";import{t as G}from"./button-DFPOdZ0g.js";import{i as ee}from"./OverlayScrollbar-DUghqFGg.js";import{t as te}from"./MarkdownRenderer-wpeGdciL.js";import{a as ne,c as re,i as ie,n as ae,o as oe,p as se,r as ce,t as le}from"./sessionChangeSummary-DDVT7O5J.js";import{a as K,i as ue,n as de,r as q,t as fe}from"./context-menu-DGz4_2hs.js";import{t as pe}from"./sessionLookup-C8VZn0gJ.js";var J=u(),Y=e(c(),1),X=s(),me=e=>{let t=(0,J.c)(17),{name:n,emoji:r,size:i,label:a,working:o,className:s}=e,c=i===void 0?24:i,l=o!==void 0&&o,{t:u}=h(),d=`calc(${c}px * var(--dpt-n, 1))`,f;t[0]===s?f=t[1]:(f=H(`relative inline-block shrink-0 align-middle leading-none`,s),t[0]=s,t[1]=f);let p;t[2]===d?p=t[3]:(p={width:d,height:d},t[2]=d,t[3]=p);let m;t[4]!==r||t[5]!==a||t[6]!==n||t[7]!==c?(m=(0,X.jsx)(U,{name:n,emoji:r,size:c,label:a,shape:`circle`}),t[4]=r,t[5]=a,t[6]=n,t[7]=c,t[8]=m):m=t[8];let g;t[9]!==u||t[10]!==l?(g=l?(0,X.jsx)(`span`,{className:`pointer-events-none absolute right-0 bottom-0 size-2 translate-x-1/4 translate-y-1/4 rounded-full bg-[var(--status-success)] ring-2 ring-background`,"data-assistant-working-dot":``,"aria-label":u(`assistants.contact.working`)}):null,t[9]=u,t[10]=l,t[11]=g):g=t[11];let _;return t[12]!==f||t[13]!==p||t[14]!==m||t[15]!==g?(_=(0,X.jsxs)(`span`,{className:f,style:p,"data-assistant-working-avatar":``,children:[m,g]}),t[12]=f,t[13]=p,t[14]=m,t[15]=g,t[16]=_):_=t[16],_},he=o(e=>({workingByID:{},setWorking:(t,n)=>e(e=>({workingByID:{...e.workingByID,[t]:n}}))})),ge=({sending:e=!1,processing:t=!1,serverWorking:n=!1})=>e||t||n,_e=(e,t)=>{let n=(0,J.c)(5),r=t!==void 0&&t,i;n[0]===e?i=n[1]:(i=t=>!!t.workingByID[e],n[0]=e,n[1]=i);let a=he(i),o;return n[2]!==a||n[3]!==r?(o=ge({processing:a,serverWorking:r}),n[2]=a,n[3]=r,n[4]=o):o=n[4],o},ve=[],ye=e=>e.flatMap(e=>e.type===`text`&&e.text.trim()?[e.text]:[]).join(`
2
- `),be=(e,t,n,r=Date.now())=>({assistantID:e,messageID:t,parts:n,status:`sending`,createdAt:r}),Z=(e,t)=>{let n=e.filter(e=>e.assistantID===t);return n.length===e.length?e:n},Q=(e,t)=>{let n=e.filter(e=>e.assistantID===t);return n.length===e.length?e:n},$=(e,t,n,r)=>({messageID:t?`${e.turnID}:preview:${t.bubbleIndex}`:`${e.turnID}:preview:${r}`,assistantID:e.assistantID,role:`assistant`,turnID:e.turnID,bubbleIndex:t?.bubbleIndex??0,createdAt:t?.occurredAt??e.occurredAt,ordinal:n,status:r,fromAssistantID:null,fromAssistantName:null,parts:t?[{type:`text`,text:t.text}]:[],text:t?.text??``,cards:[]}),xe=(e,t,n)=>{let r=e.bubbles.filter(e=>!t.has(e.bubbleIndex)).map((t,r)=>$(e,t,n+r,t.done?`complete`:`streaming`));return e.status===`failed`?[...r,$(e,null,n+r.length,`failed`)]:e.status===`admitted`||e.status===`streaming`?[...r,$(e,null,n+r.length,`admitted`)]:r},Se=(e,t,n,r=[])=>{let i=new Set(e.map(e=>e.messageID)),a=t.flatMap((t,r)=>t.assistantID!==n||i.has(t.messageID)?[]:[{messageID:t.messageID,assistantID:n,role:`user`,turnID:t.messageID,bubbleIndex:0,createdAt:t.createdAt,ordinal:e.length+r,status:t.status,fromAssistantID:null,fromAssistantName:null,parts:[...t.parts],text:ye(t.parts),cards:[]}]),o=a.length===0?e:[...e,...a],s=r.filter(e=>e.assistantID===n);if(s.length===0)return o;let c=new Map,l=new Map;o.forEach((e,t)=>{if(c.set(e.turnID,t),e.role!==`assistant`||e.messageID.includes(`:preview:`))return;let n=l.get(e.turnID)??new Set;n.add(e.bubbleIndex),l.set(e.turnID,n)});let u=new Map,d=[];for(let e of s){let t=xe(e,l.get(e.turnID)??new Set,o.length+d.length);if(t.length===0)continue;let n=c.get(e.turnID);if(n===void 0){d.push(...t);continue}let r=u.get(n)??[];r.push(...t),u.set(n,r)}return u.size===0&&d.length===0?o:o.flatMap((e,t)=>[e,...u.get(t)??[]]).concat(d)},Ce=(e,t)=>{let n=new Set(t.map(e=>e.messageID)),r=e.filter(e=>!n.has(e.messageID));return r.length===e.length?e:r},we=(e,t)=>e.map(e=>e.messageID===t?{...e,status:`admitted`}:e),Te=(e,t,n,r=Date.now())=>e.some(e=>e.assistantID===t&&e.turnID===n)?e:[...e,{assistantID:t,turnID:n,status:`admitted`,bubbles:[],occurredAt:r}],Ee=(e,t,n)=>!t?.turnID||!t.assistantID||n?.has(t.turnID)?e:Te(e,t.assistantID,t.turnID,t.admittedAt),De=(e,t)=>{let n=t.assistantID,r=t.pendingSendTurnIDs??new Set,i=t.settledTurnIDs??new Set,a=typeof t.snapshotRevision==`number`&&Number.isFinite(t.snapshotRevision)?t.snapshotRevision:null,o=t.admissionRevisionByTurnID instanceof Map?t.admissionRevisionByTurnID:new Map(Object.entries(t.admissionRevisionByTurnID??{}).map(([e,t])=>[e,Number(t)])),s=t.occurredAt??Date.now(),c=t.activeContactTurn&&t.activeContactTurn.turnID?t.activeContactTurn:null,l=!!(t.serverWorking||c),u=Ee(e,c?{assistantID:n,turnID:c.turnID,admittedAt:c.admittedAt}:null,i),d=Array.isArray(t.messages)?t.messages:[];for(let e of d)e.role===`assistant`&&e.status===`error`&&(r.has(e.turnID)||i.has(e.turnID)||(u=ke(u,{assistantID:n,turnID:e.turnID,status:`error`,error:e.text||`upstream_error`,occurredAt:s})));if(u=Ae(u,d),l){let t=u.filter(e=>{if(e.assistantID!==n||e.status!==`admitted`&&e.status!==`streaming`||r.has(e.turnID)||c&&e.turnID===c.turnID)return!0;let t=o.get(e.turnID);return typeof t==`number`&&a!=null&&a<t?!0:typeof t==`number`&&a!=null&&a>=t&&e.turnID!==c?.turnID?!1:!c||e.turnID===c.turnID});return t.length===e.length&&t.every((t,n)=>t===e[n])?e:t}if(a==null)return u.length===e.length&&u.every((t,n)=>t===e[n])?e:u;let f=u.filter(e=>{if(e.assistantID!==n||e.status!==`admitted`&&e.status!==`streaming`||r.has(e.turnID))return!0;if(i.has(e.turnID))return!1;let t=o.get(e.turnID);return typeof t==`number`&&a<t});return f.length===e.length&&f.every((t,n)=>t===e[n])?e:f.length===u.length?u:f},Oe=(e,t)=>Te(e,t.assistantID,t.turnID,t.occurredAt).map(e=>{if(e.assistantID!==t.assistantID||e.turnID!==t.turnID||e.status===`failed`||e.status===`complete`)return e;let n=e.bubbles.find(e=>e.bubbleIndex===t.bubbleIndex);if(n?.done)return e;let r=n?{...n,text:`${n.text}${t.delta}`,done:t.done,occurredAt:t.occurredAt}:{bubbleIndex:t.bubbleIndex,text:t.delta,done:t.done,occurredAt:t.occurredAt},i=n?e.bubbles.map(e=>e.bubbleIndex===t.bubbleIndex?r:e):[...e.bubbles,r].sort((e,t)=>e.bubbleIndex-t.bubbleIndex);return{...e,status:`streaming`,bubbles:i}}),ke=(e,t,n={})=>n.requireExisting&&!e.some(e=>e.assistantID===t.assistantID&&e.turnID===t.turnID)?e:Te(e,t.assistantID,t.turnID,t.occurredAt).map(e=>e.assistantID!==t.assistantID||e.turnID!==t.turnID||e.status===`complete`||e.status===`failed`?e:{...e,status:t.status===`error`?`failed`:`complete`,...t.error?{error:t.error}:{}}),Ae=(e,t)=>{let n=new Set(t.flatMap(e=>e.role===`assistant`?[e.turnID]:[])),r=e.filter(e=>e.status!==`complete`||!n.has(e.turnID));return r.length===e.length?e:r},je=(e,t,n)=>e.map(e=>e.messageID===t?{...e,status:`failed`,error:n}:e),Me=(e,t)=>{if(e instanceof M){if(e.code===`no_provider`)return t.noProvider;if(e.code===`admission_timeout`||e.code===`generate_timeout`)return t.timedOut;if(e.message&&e.message!==e.code)return e.message}return j(e)?t.timedOut:t.sendFailed},Ne=()=>{let e=!1;return{tryAcquire:()=>!e&&(e=!0,!0),release:()=>{e=!1}}},Pe=e=>{let t=e.text.trim();if(e.sending||!t&&e.attachments.length===0||!e.gate.tryAcquire())return{ok:!1};let n=[...t?[{type:`text`,text:t}]:[],...e.attachments.map(e=>({type:`file`,mime:e.mime,url:e.url,filename:e.name}))],r=e.createMessageID();return{ok:!0,messageID:r,parts:n,turn:be(e.assistantID,r,n,e.createdAt)}},Fe=e=>e.some(e=>e.status===`sending`),Ie=e=>e.some(e=>e.status===`admitted`||e.status===`streaming`),Le=[`cursor-pointer rounded-[1.35rem] bg-[var(--surface-elevated)] ring-1 ring-inset ring-[var(--surface-subtle)]`,`px-3.5 py-3 text-left transition-[background-color,box-shadow,transform] duration-200 ease-out motion-reduce:transition-none`,`hover:-translate-y-px hover:bg-interactive-hover/45 hover:shadow-sm active:translate-y-0 motion-reduce:hover:translate-y-0`,`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--interactive-focus-ring)]`].join(` `),Re=`${Le} w-fit min-w-0 max-w-[min(100%,15rem)]`,ze=`${Le} w-fit min-w-0 max-w-[min(100%,20rem)]`;function Be(e,t){(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),t())}var Ve=e=>{let t=(0,J.c)(26),{card:n}=e,{t:r}=h(),i=se(),a;t[0]===i?a=t[1]:(a=i&&!l(),t[0]=i,t[1]=a);let o=!!a,s=n.name,c=`${n.providerID}/${n.modelID}`,u;t[2]!==n.mode||t[3]!==c?(u=[c,n.mode].filter(Boolean),t[2]=n.mode,t[3]=c,t[4]=u):u=t[4];let d=u,p;t[5]!==n.assistantID||t[6]!==o?(p=()=>{if(o){x.getState().openAssistant(n.assistantID);return}P(n.assistantID)},t[5]=n.assistantID,t[6]=o,t[7]=p):p=t[7];let m=f(p),g;t[8]===m?g=t[9]:(g=e=>{Be(e,m)},t[8]=m,t[9]=g);let _=f(g),v;t[10]!==r||t[11]!==s?(v=r(`assistants.contact.card.assistant.aria`,{name:s}),t[10]=r,t[11]=s,t[12]=v):v=t[12];let y;t[13]===Symbol.for(`react.memo_cache_sentinel`)?(y=(0,X.jsx)(`span`,{className:`relative inline-block size-6 shrink-0 leading-none`,children:(0,X.jsx)(`span`,{className:`flex size-6 items-center justify-center rounded-lg bg-[var(--surface-muted)] text-muted-foreground`,children:(0,X.jsx)(W,{name:`robot-2`,className:`size-3`})})}),t[13]=y):y=t[13];let b;t[14]===s?b=t[15]:(b=(0,X.jsx)(`h3`,{className:`min-w-0 truncate typography-ui-label font-medium text-foreground`,children:s}),t[14]=s,t[15]=b);let S;t[16]===d?S=t[17]:(S=d.length>0?(0,X.jsx)(`p`,{className:`truncate typography-micro text-muted-foreground`,children:d.join(` · `)}):null,t[16]=d,t[17]=S);let C;t[18]!==b||t[19]!==S?(C=(0,X.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[y,(0,X.jsxs)(`div`,{className:`min-w-0 flex-1 space-y-1`,children:[b,S]})]}),t[18]=b,t[19]=S,t[20]=C):C=t[20];let w;return t[21]!==_||t[22]!==m||t[23]!==C||t[24]!==v?(w=(0,X.jsx)(`article`,{className:Re,role:`button`,tabIndex:0,"aria-label":v,"data-assistant-contact-card":`assistant`,onClick:m,onKeyDown:_,children:C}),t[21]=_,t[22]=m,t[23]=C,t[24]=v,t[25]=w):w=t[25],w},He=e=>{let t=(0,J.c)(25),{card:n}=e,{t:r}=h(),i=n.name,a;t[0]!==n.kind||t[1]!==n.time||t[2]!==n.timezone?(a=[n.kind,n.time,n.timezone].filter(Boolean),t[0]=n.kind,t[1]=n.time,t[2]=n.timezone,t[3]=a):a=t[3];let o=a.join(` `),s;t[4]!==n.prompt||t[5]!==o?(s=o.trim()||(typeof n.prompt==`string`?n.prompt.trim():``),t[4]=n.prompt,t[5]=o,t[6]=s):s=t[6];let c=s,l=f(Ue),u;t[7]===l?u=t[8]:(u=e=>{Be(e,l)},t[7]=l,t[8]=u);let d=f(u),p;t[9]!==r||t[10]!==i?(p=r(`assistants.contact.card.schedule.aria`,{name:i}),t[9]=r,t[10]=i,t[11]=p):p=t[11];let m;t[12]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,X.jsx)(`span`,{className:`relative inline-block size-6 shrink-0 leading-none`,children:(0,X.jsx)(`span`,{className:`flex size-6 items-center justify-center rounded-lg bg-[var(--surface-muted)] text-muted-foreground`,children:(0,X.jsx)(W,{name:`calendar`,className:`size-3`})})}),t[12]=m):m=t[12];let g;t[13]===i?g=t[14]:(g=(0,X.jsx)(`h3`,{className:`min-w-0 truncate typography-ui-label font-medium text-foreground`,children:i}),t[13]=i,t[14]=g);let _;t[15]===c?_=t[16]:(_=c?(0,X.jsx)(`p`,{className:`truncate typography-micro text-muted-foreground`,children:c}):null,t[15]=c,t[16]=_);let v;t[17]!==g||t[18]!==_?(v=(0,X.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[m,(0,X.jsxs)(`div`,{className:`min-w-0 flex-1 space-y-1`,children:[g,_]})]}),t[17]=g,t[18]=_,t[19]=v):v=t[19];let y;return t[20]!==d||t[21]!==l||t[22]!==p||t[23]!==v?(y=(0,X.jsx)(`article`,{className:Re,role:`button`,tabIndex:0,"aria-label":p,"data-assistant-contact-card":`schedule`,onClick:l,onKeyDown:d,children:v}),t[20]=d,t[21]=l,t[22]=p,t[23]=v,t[24]=y):y=t[24],y};function Ue(){i.getState().setActiveMainTab(`schedule`),i.getState().setScheduledTasksDialogOpen(!0)}var We=(e,t)=>e===`busy`||e===`retry`?e:t===`error`||t===`question`||t===`complete`?t:e===`idle`?`complete`:t===`busy`||t===`retry`||t===`idle`?t:t||`busy`,Ge=e=>e===`busy`?`assistants.contact.card.session.status.busy`:e===`retry`?`assistants.contact.card.session.status.retry`:e===`idle`?`assistants.contact.card.session.status.idle`:e===`complete`?`assistants.contact.card.session.status.complete`:e===`error`?`assistants.contact.card.session.status.error`:e===`question`?`assistants.contact.card.session.status.question`:null,Ke=e=>e===`busy`?`bg-[var(--status-info)]/10 text-[var(--status-info)]`:e===`retry`||e===`question`?`bg-[var(--status-warning)]/10 text-[var(--status-warning)]`:e===`error`?`bg-[var(--status-error)]/10 text-[var(--status-error)]`:e===`idle`||e===`complete`?`bg-[var(--status-success)]/10 text-[var(--status-success)]`:`bg-[var(--surface-muted)] text-muted-foreground`,qe=e=>e.split(/[/\\]/u).filter(Boolean).at(-1)||e,Je=e=>{let t=(0,J.c)(35),{card:n}=e,{t:r}=h(),a=se(),o;t[0]===a?o=t[1]:(o=a&&!l(),t[0]=a,t[1]=o);let s=!!o,c;t[2]===n.sessionID?c=t[3]:(c=e=>e.activeSessions.find(e=>e.id===n.sessionID)??e.archivedSessions.find(e=>e.id===n.sessionID)??null,t[2]=n.sessionID,t[3]=c);let u=y(c),d=b(n.sessionID),p=(typeof u?.title==`string`&&u.title.trim()?u.title.trim():null)||n.title||r(`assistants.contact.card.session.untitled`),m=We(d?.type,n.status),g=Ge(m),_=m===`busy`||m===`retry`,v=qe(typeof u?.directory==`string`&&u.directory.trim()?u.directory.trim():n.directory),x=ae(u)||n.branch,C=ie(u),w=ce(u),T=le(w),E=[v,C,x].filter(Ye),D;t[4]!==n.directory||t[5]!==n.sessionID||t[6]!==s?(D=()=>{let e=pe(n.sessionID)?.directory||n.directory;S(n.sessionID,e,{phoneShell:s,switchToChat:!0}),s||i.getState().setActiveMainTab(`chat`)},t[4]=n.directory,t[5]=n.sessionID,t[6]=s,t[7]=D):D=t[7];let O=f(D),k;t[8]===O?k=t[9]:(k=e=>{Be(e,O)},t[8]=O,t[9]=k);let A=f(k),j=ze,M=r(`assistants.contact.card.session.aria`,{title:p}),N;t[10]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,X.jsx)(`span`,{className:`flex size-6 items-center justify-center rounded-lg bg-[var(--surface-muted)] text-muted-foreground`,children:(0,X.jsx)(W,{name:`chat-3`,className:`size-3`})}),t[10]=N):N=t[10];let P;t[11]===_?P=t[12]:(P=_?(0,X.jsx)(`span`,{className:`pointer-events-none absolute right-0 bottom-0 size-2 translate-x-1/4 translate-y-1/4 rounded-full bg-[var(--status-success)] ring-2 ring-[var(--surface-elevated)]`,"data-assistant-working-dot":``,"aria-hidden":!0}):null,t[11]=_,t[12]=P);let F;t[13]===P?F=t[14]:(F=(0,X.jsxs)(`span`,{className:`relative inline-block size-6 shrink-0 leading-none`,children:[N,P]}),t[13]=P,t[14]=F);let I;t[15]===p?I=t[16]:(I=(0,X.jsx)(`h3`,{className:`min-w-0 flex-1 truncate typography-ui-label font-medium text-foreground`,children:p}),t[15]=p,t[16]=I);let L=g?(0,X.jsx)(`span`,{className:H(`shrink-0 rounded-full px-1.5 py-0.5 typography-micro leading-none`,Ke(m)),children:r(g)}):null,R;t[17]!==I||t[18]!==L?(R=(0,X.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[I,L]}),t[17]=I,t[18]=L,t[19]=R):R=t[19];let z;t[20]===E?z=t[21]:(z=E.length>0?(0,X.jsx)(`p`,{className:`truncate typography-micro text-muted-foreground`,children:E.join(` · `)}):null,t[20]=E,t[21]=z);let B=T||w?.files!==void 0&&w.files>0?(0,X.jsxs)(`p`,{className:`flex flex-wrap items-baseline gap-x-1.5 typography-micro tabular-nums`,children:[w?.additions===void 0?null:(0,X.jsxs)(`span`,{className:`text-[var(--status-success)]`,children:[`+`,w.additions]}),w?.deletions===void 0?null:(0,X.jsxs)(`span`,{className:`text-[var(--status-error)]`,children:[`−`,w.deletions]}),w?.files===void 0?null:(0,X.jsx)(`span`,{className:`text-muted-foreground`,children:w.files===1?r(`assistants.contact.card.session.changes.filesSingle`,{count:w.files}):r(`assistants.contact.card.session.changes.filesPlural`,{count:w.files})})]}):null,V;t[22]!==R||t[23]!==z||t[24]!==B?(V=(0,X.jsxs)(`div`,{className:`min-w-0 flex-1 space-y-1`,children:[R,z,B]}),t[22]=R,t[23]=z,t[24]=B,t[25]=V):V=t[25];let U;t[26]!==F||t[27]!==V?(U=(0,X.jsxs)(`div`,{className:`flex items-start gap-2.5`,children:[F,V]}),t[26]=F,t[27]=V,t[28]=U):U=t[28];let G;return t[29]!==A||t[30]!==O||t[31]!==U||t[32]!==j||t[33]!==M?(G=(0,X.jsx)(`article`,{className:j,role:`button`,tabIndex:0,"aria-label":M,"data-assistant-contact-card":`session`,onClick:O,onKeyDown:A,children:U}),t[29]=A,t[30]=O,t[31]=U,t[32]=j,t[33]=M,t[34]=G):G=t[34],G};function Ye(e){return!!(e&&e.trim())}var Xe=2,Ze=1500,Qe=e=>({scrollTop:e.scrollTop,scrollHeight:e.scrollHeight,clientHeight:e.clientHeight}),$e=e=>e.scrollHeight-e.scrollTop-e.clientHeight,et=e=>$e(e)<=Xe,tt=(e,t)=>{let n=t instanceof Element?t:t instanceof Node?t.parentElement:null;for(;n&&n!==e;){if(n instanceof HTMLElement&&n.scrollHeight>n.clientHeight+1&&n.scrollTop+n.clientHeight<n.scrollHeight-1)return!0;n=n.parentElement}return!1},nt=e=>e.altKey||e.ctrlKey||e.metaKey?!1:e.key===`ArrowDown`||e.key===`PageDown`||e.key===`End`,rt=({active:e,assistantID:t,contentRevision:n})=>{let r=Y.useRef(null),[i,a]=Y.useState(null),[o,s]=Y.useState(null),c=Y.useRef(!0),l=Y.useRef(null),u=Y.useRef(null),d=Y.useRef(0),h=Y.useRef(null),g=Y.useRef(null),_=Y.useRef(e);_.current=e;let v=Y.useMemo(()=>e=>{r.current=e,a(t=>t===e?t:e)},[]),y=Y.useMemo(()=>e=>{s(t=>t===e?t:e)},[]),b=f(()=>{if(!_.current||!c.current)return!1;let e=r.current;if(!e)return!1;let t=Qe(e),n=Math.min(d.current,Math.max(0,t.scrollHeight-t.clientHeight));return t.scrollTop<n-.5?(c.current=!1,d.current=t.scrollTop,!1):et(t)?(d.current=t.scrollTop,!1):(e.scrollTop=e.scrollHeight,d.current=e.scrollTop,!0)}),x=f(e=>{g.current={direction:e,expiresAt:performance.now()+Ze}}),S=f(()=>{let e=g.current;return e?performance.now()>e.expiresAt?(g.current=null,null):e.direction:null}),C=f(()=>{c.current=!1}),w=f(()=>{if(!_.current)return;let e=r.current;if(!e)return;let t=Qe(e),n=t.scrollTop>d.current,i=Math.min(d.current,Math.max(0,t.scrollHeight-t.clientHeight));if(d.current=t.scrollTop,c.current){t.scrollTop<i-.5&&C();return}let a=S();n&&et(t)&&(a===`down`||a===`scrollbar`)&&(c.current=!0)}),T=f(e=>{let t=r.current;if(!(!t||e.deltaY===0)){if(e.deltaY<0){if(oe(t,e.target))return;x(`up`),C();return}tt(t,e.target)||x(`down`)}}),E=f(e=>{g.current=null;let t=e.touches.item(0);h.current=t?{x:t.clientX,y:t.clientY,deltaX:0,deltaY:0}:null}),D=f(e=>{let t=r.current,n=e.touches.item(0);if(!t||!n){h.current=null;return}let i=h.current;if(!i)return;let a=n.clientY-i.y,o=a*i.deltaY<0,s=(o?0:i.deltaX)+n.clientX-i.x,c=(o?0:i.deltaY)+a;if(h.current={x:n.clientX,y:n.clientY,deltaX:s,deltaY:c},!(Math.abs(s)>=Math.abs(c))){if(c>0&&oe(t,e.target)||c<0&&tt(t,e.target)){h.current.deltaX=0,h.current.deltaY=0;return}x(c>0?`up`:`down`),c>2&&C()}}),O=f(()=>{h.current=null}),k=f(e=>{if(ne(e)){x(`up`),C();return}nt(e)&&x(`down`)}),A=f(e=>{let t=r.current;e.pointerType===`mouse`&&e.target===t&&x(`scrollbar`)});Y.useLayoutEffect(()=>{let n=e?t:null,i=r.current;(l.current!==n||u.current!==i)&&(l.current=n,u.current=i,c.current=!0,g.current=null,h.current=null,d.current=i?.scrollTop??0),e&&b()},[e,t,n,i]);let j=Y.useMemo(()=>()=>void 0,[]),M=e&&i?i:j,N=Y.useMemo(()=>({passive:!0}),[]);p(`scroll`,w,M,N),p(`wheel`,T,M,N),p(`touchstart`,E,M,N),p(`touchmove`,D,M,N),p(`touchend`,O,M,N),p(`touchcancel`,O,M,N),p(`keydown`,k,M),p(`pointerdown`,A,M,N);let P=f(()=>{b()});return m(typeof ResizeObserver<`u`&&e?o:null,P),{scrollRef:v,contentRef:y}},it=64,at=e=>new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{t(typeof r.result==`string`?r.result:``)},r.onerror=()=>{n(r.error??Error(`read_failed`))},r.readAsDataURL(e)}),ot=e=>{if(!e)return[];let t=Array.from(e.files||[]);return t.length>0?t:Array.from(e.items||[]).filter(e=>e.kind===`file`).map(e=>e.getAsFile()).filter(e=>e instanceof File)},st=e=>Array.from(e?.files||[]),ct=async(e,t=a)=>{let n=[],r=0;for(let i of Array.from(e||[])){if(!i||i.size<=0)continue;if(i.size>52428800){r+=1;continue}let e=await at(i);e.startsWith(`data:`)&&n.push({id:t(),url:e,name:i.name.trim()||`attachment`,mime:i.type.trim()||`application/octet-stream`})}return{attachments:n,skippedTooLarge:r}},lt=(e,t)=>[...e,...t].slice(0,it),ut={"oc.settle.complete":`assistants.contact.settle.complete`,"oc.settle.error":`assistants.contact.settle.error`,"oc.settle.question":`assistants.contact.settle.question`},dt=({assistant:e,warning:t,active:n,overlayHeader:r=!1})=>{let{t:o}=h(),s=i(e=>e.isMobile),c=k(),l=C(),u=E(e.id,n),d=g(e.name),p=d.displayName||e.name,m=(e,t)=>{let n=e?l.data?.assistants.find(t=>t.id===e):void 0;return n?g(n.name).displayName||n.name:t||o(`assistants.contact.peer.unknown`)},[y,b]=Y.useState(``),[x,S]=Y.useState([]),[w,O]=Y.useState([]),[A,j]=Y.useState([]),[N,P]=Y.useState(null),F=Y.useMemo(()=>Ne(),[]),I=Y.useRef(new Set),L=Y.useRef(new Map),R=he(e=>e.setWorking),z=u.data?.messages??ve,B=Z(w,e.id),V=Q(A,e.id),U=Se(z,B,e.id,V),G=Fe(B),ee=Ie(V),ne=!!(e.working||e.activeContactTurn),ie=l.data?.revision??null,ae=V.reduce((e,t)=>e+t.bubbles.reduce((e,t)=>e+t.text.length,0),0),oe=new Map(V.map(e=>[e.turnID,e])),{scrollRef:se,contentRef:ce}=rt({active:n,assistantID:e.id,contentRevision:`${U.length}:${ae}`});Y.useEffect(()=>{P(null),I.current.clear(),L.current=new Map,O(t=>Z(t,e.id)),j(t=>Q(t,e.id))},[e.id]),Y.useEffect(()=>{O(e=>Ce(e,z))},[z]),Y.useEffect(()=>{let t=new Set(B.filter(e=>e.status===`sending`).map(e=>e.messageID));j(n=>De(n,{assistantID:e.id,activeContactTurn:e.activeContactTurn?{turnID:e.activeContactTurn.turnID,admittedAt:e.activeContactTurn.admittedAt}:null,serverWorking:ne,snapshotRevision:ie,admissionRevisionByTurnID:L.current,pendingSendTurnIDs:t,messages:z,settledTurnIDs:I.current}))},[e.activeContactTurn,e.id,z,B,ne,ie]),Y.useEffect(()=>{R(e.id,G||ee||ne)},[e.id,ee,G,ne,R]),Y.useEffect(()=>{let t=e.id;return()=>{R(t,!1)}},[e.id,R]);let le=f(t=>{if(!(!(`assistantID`in t)||t.assistantID!==e.id)){if(t.type===`contact-turn-start`){I.current.delete(t.turnID),j(e=>Te(e,t.assistantID,t.turnID,t.occurredAt));return}if(t.type===`contact-bubble-delta`){if(I.current.has(t.turnID))return;j(e=>Oe(e,t));return}t.type===`contact-turn-end`&&(I.current.add(t.turnID),j(e=>Ae(ke(e,t,{requireExisting:!0}),z)))}});Y.useEffect(()=>{if(n)return v(le)},[n,e.id]);let K=f(async e=>{let t=await ct(e);t.skippedTooLarge>0&&P(o(`assistants.contact.attachment.tooLarge`)),t.attachments.length!==0&&S(e=>lt(e,t.attachments))}),ue=f(e=>{let t=ot(e.clipboardData);t.length!==0&&(e.preventDefault(),K(t))}),de=f(e=>{Array.from(e.dataTransfer?.types??[]).includes(`Files`)&&(e.preventDefault(),e.dataTransfer.dropEffect=`copy`)}),q=f(e=>{let t=st(e.dataTransfer);t.length!==0&&(e.preventDefault(),K(t))}),fe=f(async()=>{let t=y,n=x,r=e.id,i=Pe({gate:F,sending:Fe(B),text:t,attachments:n,assistantID:r,createMessageID:()=>`oc_contact_${a()}`});if(i.ok){O(e=>[...e,i.turn]),b(``),S([]),P(null);try{let e=await T(r,i.messageID,{parts:i.parts});typeof e.revision==`number`&&L.current.set(i.messageID,e.revision),O(e=>we(e,i.messageID)),I.current.has(i.messageID)||j(e=>Te(e,r,i.messageID)),c.data?.serverInstanceID&&_({serverInstanceID:c.data.serverInstanceID,assistantID:r,name:p,avatarSeed:r,...d.avatarEmoji?{avatarEmoji:d.avatarEmoji}:{}}).catch(()=>void 0)}catch(e){if(e instanceof M&&e.code===`admission_timeout`)try{let e=await D(r,i.messageID);if(e){typeof e.revision==`number`&&L.current.set(i.messageID,e.revision),O(e=>we(e,i.messageID)),I.current.has(i.messageID)||j(e=>Te(e,r,i.messageID));return}}catch{}let t=Me(e,{noProvider:o(`assistants.contact.noProvider`),sendFailed:o(`assistants.contact.sendFailed`),timedOut:o(`assistants.contact.timedOut`)});O(e=>je(e,i.messageID,t))}finally{F.release()}}}),pe=u.isError&&U.length===0,J=u.isSuccess&&U.length===0,ge=new Map(B.map(e=>[e.messageID,e]));return(0,X.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col bg-background`,children:[(0,X.jsxs)(`div`,{ref:se,className:H(`min-h-0 flex-1 overflow-x-hidden overflow-y-auto px-4 pb-9 sm:px-8 sm:pb-12`,r?`pt-[calc(max(0.625rem,var(--oc-safe-area-top,0px))+var(--oc-mobile-detail-navigation-height)+1.25rem)]`:`pt-5 sm:pt-7`),style:{overflowAnchor:`none`,overscrollBehavior:`contain`,overscrollBehaviorY:`contain`},"data-assistant-contact-transcript":``,children:[t?(0,X.jsx)(`p`,{className:`mb-3 typography-micro text-[var(--status-warning)]`,children:t}):null,pe?(0,X.jsxs)(`div`,{ref:ce,className:`mx-auto flex h-full min-h-56 max-w-xs flex-col items-center justify-center pb-16 text-center`,"data-assistant-contact-error":``,children:[(0,X.jsxs)(`div`,{className:`relative`,children:[(0,X.jsx)(me,{name:e.id,emoji:d.avatarEmoji,size:44,label:p}),(0,X.jsx)(`span`,{"aria-hidden":!0,className:`absolute -right-1 -bottom-1 flex size-5 items-center justify-center rounded-full bg-[var(--surface-elevated)] text-[var(--status-error)] ring-2 ring-background`,children:(0,X.jsx)(W,{name:`error-warning`,className:`size-3`})})]}),(0,X.jsx)(`p`,{className:`mt-4 typography-ui-label font-medium text-foreground`,children:p}),(0,X.jsx)(`p`,{className:`mt-1.5 typography-ui leading-6 text-muted-foreground`,children:o(`assistants.contact.loadFailed`)})]}):J?(0,X.jsxs)(`div`,{ref:ce,className:`mx-auto flex h-full min-h-56 max-w-sm flex-col items-center justify-center pb-16 text-center`,"data-assistant-contact-empty":``,children:[(0,X.jsx)(me,{name:e.id,emoji:d.avatarEmoji,size:48,label:p,className:`opacity-90`}),(0,X.jsx)(`p`,{className:`mt-5 typography-ui-header font-medium tracking-[-0.01em] text-foreground`,children:o(`assistants.conversation.emptyTitle`,{name:p})}),(0,X.jsx)(`p`,{className:`mt-2 max-w-xs typography-ui leading-6 text-muted-foreground/80`,children:o(`assistants.contact.empty`)})]}):(0,X.jsx)(`div`,{ref:ce,className:`mx-auto flex w-full max-w-[42rem] flex-col`,children:U.map((t,n)=>{let r=n>0?U[n-1]:null,i=t.role===`user`,a=t.role===`peer`,s=t.role===`assistant`&&r?.role===`assistant`&&r.turnID===t.turnID,c=r?.turnID!==t.turnID,u=!i&&(a||!s),d=a?m(t.fromAssistantID,t.fromAssistantName):p,f=a&&t.fromAssistantID?l.data?.assistants.find(e=>e.id===t.fromAssistantID):e,h=f?g(f.name):null,_=ge.get(t.messageID),v=oe.get(t.turnID),y=t.status===`admitted`&&t.messageID.endsWith(`:preview:admitted`),b=t.status===`failed`&&t.messageID.endsWith(`:preview:failed`);return(0,X.jsxs)(`div`,{className:H(`flex w-full`,i?`justify-end`:`justify-start`,c?`mt-6 first:mt-0`:s?`mt-1.5`:`mt-3`),"data-assistant-contact-role":t.role,"data-assistant-contact-turn-status":_?.status??t.status,"data-assistant-contact-run-start":t.role===`assistant`&&!s?``:void 0,children:[i?null:(0,X.jsx)(`div`,{className:`mr-2.5 flex w-8 shrink-0 justify-center pt-0.5`,children:u?(0,X.jsx)(me,{name:f?.id||t.fromAssistantID||e.id,emoji:h?.avatarEmoji,size:28,label:d}):(0,X.jsx)(`span`,{className:`size-7`,"aria-hidden":!0})}),(0,X.jsxs)(`div`,{className:H(`flex min-w-0 flex-col gap-1.5`,i?`max-w-[min(82%,30rem)] items-end`:`max-w-[min(84%,34rem)] items-start`),children:[a?(0,X.jsx)(`span`,{className:`mb-0.5 px-1 typography-micro text-muted-foreground/75`,children:o(`assistants.contact.peer.from`,{name:d})}):null,y?(0,X.jsxs)(`div`,{role:`status`,"aria-label":o(`assistants.contact.processing`),className:`inline-flex w-fit items-center gap-1 rounded-full bg-[var(--surface-muted)] px-3.5 py-2.5 text-muted-foreground/70`,"data-assistant-contact-processing":``,children:[(0,X.jsx)(`span`,{"aria-hidden":!0,className:`size-1.5 animate-pulse rounded-full bg-current [animation-delay:-0.45s] [animation-duration:1.4s] motion-reduce:animate-none`}),(0,X.jsx)(`span`,{"aria-hidden":!0,className:`size-1.5 animate-pulse rounded-full bg-current [animation-delay:-0.22s] [animation-duration:1.4s] motion-reduce:animate-none`}),(0,X.jsx)(`span`,{"aria-hidden":!0,className:`size-1.5 animate-pulse rounded-full bg-current [animation-duration:1.4s] motion-reduce:animate-none`})]}):null,t.parts.map((e,n)=>{if(e.type===`card`&&e.cardType===`session`)return(0,X.jsx)(Je,{card:e},`${t.messageID}:card:${n}`);if(e.type===`card`&&e.cardType===`assistant`)return(0,X.jsx)(Ve,{card:e},`${t.messageID}:card:${n}`);if(e.type===`card`&&e.cardType===`schedule`)return(0,X.jsx)(He,{card:e},`${t.messageID}:card:${n}`);if(e.type===`file`&&e.mime.startsWith(`image/`)&&e.url)return(0,X.jsx)(`img`,{src:e.url,alt:e.filename||o(`assistants.contact.attachment.image`),className:`max-h-72 max-w-full rounded-[1.35rem] border border-border/40 object-contain`,"data-assistant-contact-image":``},`${t.messageID}:file:${n}`);if(e.type===`file`)return(0,X.jsxs)(`div`,{className:`flex max-w-full items-center gap-2.5 rounded-[1.25rem] bg-[var(--surface-muted)] px-3.5 py-2.5 ring-1 ring-inset ring-[var(--surface-subtle)]`,"data-assistant-contact-file":``,children:[(0,X.jsx)(W,{name:`file-text`,className:`size-4 shrink-0 text-muted-foreground`}),(0,X.jsx)(`span`,{className:`min-w-0 truncate typography-ui`,children:e.filename||o(`assistants.contact.attachment.file`)})]},`${t.messageID}:file:${n}`);if(e.type===`text`&&(e.text.trim()||t.status===`streaming`)){let r=ut[e.text],c=!i&&!r;return(0,X.jsxs)(`div`,{"aria-label":a?o(`assistants.contact.peer.aria`,{name:d}):void 0,"data-assistant-contact-text":``,className:H(`min-w-0 max-w-full [overflow-wrap:anywhere] rounded-[1.35rem] px-4 py-2.5 typography-markdown leading-6`,c?null:`whitespace-pre-wrap`,i?`rounded-[1.15rem] rounded-br-lg bg-[var(--primary-base)]/90 text-[var(--primary-foreground)]`:a?`rounded-[1.25rem] border border-dashed border-border/50 bg-[var(--surface-muted)]/70 text-foreground`:H(`bg-[var(--surface-muted)] text-foreground`,s&&`rounded-tl-lg`)),children:[c?(0,X.jsx)(te,{content:e.text,messageId:t.messageID,isAnimated:!1,isStreaming:t.status===`streaming`,variant:`assistant`,enableFileReferences:!1,className:`w-full min-w-0 [overflow-wrap:anywhere]`}):r?o(r):e.text,t.status===`streaming`&&n===t.parts.length-1?(0,X.jsx)(`span`,{"aria-hidden":!0,className:`ml-0.5 inline-block h-[1em] w-px translate-y-[0.12em] animate-pulse bg-current opacity-45 [animation-duration:1.1s] motion-reduce:animate-none`,"data-assistant-contact-streaming-caret":``}):null]},`${t.messageID}:text:${n}`)}return null}),_?.status===`sending`?(0,X.jsx)(`p`,{className:`px-1 typography-micro text-muted-foreground/75`,children:o(`assistants.contact.sending`)}):null,_?.status===`failed`?(0,X.jsx)(`p`,{className:`px-1 typography-micro text-[var(--status-error)]`,children:_.error||o(`assistants.contact.sendFailed`)}):null,b?(0,X.jsx)(`p`,{className:`px-1 typography-micro text-[var(--status-error)]`,children:v?.error||o(`assistants.contact.sendFailed`)}):null]})]},t.messageID)})})]}),(0,X.jsxs)(`footer`,{className:`relative z-10 shrink-0 bg-background pt-1`,"data-assistant-contact-composer":``,children:[N?(0,X.jsx)(`p`,{className:`chat-input-column mb-2 typography-micro text-[var(--status-error)]`,children:N}):null,(0,X.jsx)(`form`,{className:H(`relative w-full pt-1.5 pb-4`,s&&`bottom-safe-area oc-mobile-composer`),onSubmit:e=>{e.preventDefault(),fe()},onDragOver:de,onDrop:q,children:(0,X.jsx)(`div`,{className:`chat-input-column relative overflow-visible`,children:(0,X.jsx)(re,{layout:`inline`,value:y,attachments:x,pending:!1,isMobile:s,placeholder:o(`assistants.contact.placeholder`,{name:p}),sendLabel:o(`assistants.contact.send`),addFilesLabel:o(`assistants.contact.addFiles`),removeAttachmentLabel:o(`assistants.contact.removeAttachment`),fileAccept:`*/*`,onChange:e=>b(e),onSubmit:()=>{fe()},onAddFiles:e=>{K(e)},onRemoveAttachment:e=>{S(t=>t.filter(t=>t.id!==e))},onPaste:ue,onDragOver:de,onDrop:q,className:H(`relative z-10`,s&&`oc-mobile-composer-surface`),style:{borderRadius:`1.5rem`},"data-assistant-contact-composer-surface":``,textareaProps:{"aria-label":o(`assistants.contact.placeholder`,{name:p})}})})})]})]})};async function ft(e){await w(e);let t=N.getState().defaultShareAssistant;if(t?.assistantID!==e.id)return;let n=await O();n.serverInstanceID&&t.serverInstanceID===n.serverInstanceID&&N.getState().setDefaultShareAssistant(null)}function pt({assistant:e,open:t,onOpenChange:n,onDeleted:r}){let{t:i}=h(),[a,o]=Y.useState(!1),s=e?g(e.name).displayName||e.name:``,c=f(e=>{a&&!e||n(e)}),l=f(()=>{a||n(!1)}),u=f(async()=>{if(!(!e||a)){o(!0);try{await ft(e),r?.(e.id),n(!1),d.success(i(`assistants.settings.toast.deleted`))}catch{d.error(i(`assistants.settings.toast.deleteFailed`))}finally{o(!1)}}});return(0,X.jsx)(V,{open:t&&e!==null,onOpenChange:c,children:(0,X.jsxs)(R,{className:`max-w-md`,children:[(0,X.jsxs)(I,{children:[(0,X.jsx)(z,{children:i(`assistants.settings.delete`)}),(0,X.jsx)(B,{children:i(`assistants.settings.deleteConfirm`,{name:s})})]}),(0,X.jsxs)(L,{children:[(0,X.jsx)(G,{size:`sm`,variant:`ghost`,onClick:l,disabled:a,children:i(`settings.common.actions.cancel`)}),(0,X.jsx)(G,{size:`sm`,variant:`destructive`,onClick:u,disabled:a,children:i(`settings.common.actions.delete`)})]})]})})}var mt=e=>e.capabilityPending?`loading`:e.capabilityError||e.supported!==!0||e.capabilityEnabled!==!0?`unavailable`:e.snapshotPending?`loading`:e.snapshotEnabled===!0?e.assistantCount===0?`onboarding`:e.hasAssistant?`ready`:e.snapshotSettled?`unavailable`:`loading`:`unavailable`,ht=t({AssistantView:()=>vt}),gt=e=>{let t=(0,J.c)(21),{assistant:n,onBack:r}=e,{t:i}=h(),a=se(),o;t[0]===n?o=t[1]:(o=n?g(n.name):null,t[0]=n,t[1]=o);let s=o,c=n&&s?s.displayName||n.name:``,l;t[2]!==n||t[3]!==a?(l=()=>{n&&F(n.id,a?{openMobileSettings:a.openSettings}:void 0)},t[2]=n,t[3]=a,t[4]=l):l=t[4];let u=f(l),d;t[5]!==n||t[6]!==c||t[7]!==s?.avatarEmoji||t[8]!==i?(d=n?(0,X.jsxs)(`span`,{className:`inline-flex min-w-0 items-center gap-2.5`,children:[(0,X.jsx)(me,{name:n.id,emoji:s?.avatarEmoji,size:28,label:c}),(0,X.jsx)(`span`,{className:`truncate font-medium tracking-[-0.01em]`,children:c||i(`assistants.title`)})]}):c||i(`assistants.title`),t[5]=n,t[6]=c,t[7]=s?.avatarEmoji,t[8]=i,t[9]=d):d=t[9];let p;t[10]===i?p=t[11]:(p=i(`assistants.actions.backToChat`),t[10]=i,t[11]=p);let m;t[12]!==n||t[13]!==u||t[14]!==i?(m=n?[{icon:`settings-3`,ariaLabel:i(`assistants.conversation.openSettings`),onClick:u}]:[],t[12]=n,t[13]=u,t[14]=i,t[15]=m):m=t[15];let _;return t[16]!==r||t[17]!==d||t[18]!==p||t[19]!==m?(_=(0,X.jsx)(A,{title:d,backAriaLabel:p,onBack:r,actions:m,overlay:!0}),t[16]=r,t[17]=d,t[18]=p,t[19]=m,t[20]=_):_=t[20],_},_t=e=>{let t=(0,J.c)(41),{assistantID:n,displayName:r,avatarEmoji:i,selected:a,enabled:o,editLabel:s,deleteLabel:c,onSelect:l,onEdit:u,onDelete:d,serverWorking:p}=e,m=_e(n,p!==void 0&&p),[h,g]=Y.useState(!1),_;t[0]===l?_=t[1]:(_=()=>l(),t[0]=l,t[1]=_);let v=f(_),y;t[2]===u?y=t[3]:(y=()=>{g(!1),u()},t[2]=u,t[3]=y);let b=f(y),x;t[4]===d?x=t[5]:(x=()=>{g(!1),d()},t[4]=d,t[5]=x);let S=f(x),C;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(C=e=>{e.preventDefault(),g(!0)},t[6]=C):C=t[6];let w=a?`border-border/50 bg-[var(--surface-elevated)]`:`border-transparent hover:bg-interactive-hover`,T=!o&&`opacity-65`,E;t[7]!==w||t[8]!==T?(E=H(`flex w-full min-h-11 items-center gap-3 rounded-xl border px-3 py-3 text-left outline-none transition-[background-color,border-color,opacity] duration-150 ease-out active:bg-interactive-active focus-visible:ring-2 focus-visible:ring-[var(--interactive-focus-ring)] motion-reduce:transition-none`,w,T),t[7]=w,t[8]=T,t[9]=E):E=t[9];let D;t[10]!==v||t[11]!==a||t[12]!==E?(D=(0,X.jsx)(`button`,{type:`button`,role:`option`,"aria-selected":a,onClick:v,onContextMenu:C,className:E}),t[10]=v,t[11]=a,t[12]=E,t[13]=D):D=t[13];let O;t[14]!==n||t[15]!==i||t[16]!==r||t[17]!==m?(O=(0,X.jsx)(me,{name:n,emoji:i,size:24,label:r,working:m}),t[14]=n,t[15]=i,t[16]=r,t[17]=m,t[18]=O):O=t[18];let k;t[19]===r?k=t[20]:(k=(0,X.jsx)(`span`,{className:`min-w-0 flex-1`,children:(0,X.jsx)(`span`,{className:`block truncate typography-ui-label font-medium`,children:r})}),t[19]=r,t[20]=k);let A;t[21]!==O||t[22]!==k||t[23]!==D?(A=(0,X.jsxs)(K,{render:D,children:[O,k]}),t[21]=O,t[22]=k,t[23]=D,t[24]=A):A=t[24];let j;t[25]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,X.jsx)(W,{name:`edit`,className:`size-4`}),t[25]=j):j=t[25];let M;t[26]!==s||t[27]!==b?(M=(0,X.jsxs)(q,{onClick:b,children:[j,s]}),t[26]=s,t[27]=b,t[28]=M):M=t[28];let N;t[29]===Symbol.for(`react.memo_cache_sentinel`)?(N=(0,X.jsx)(ue,{}),t[29]=N):N=t[29];let P;t[30]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,X.jsx)(W,{name:`delete-bin`,className:`size-4`}),t[30]=P):P=t[30];let F;t[31]!==c||t[32]!==S?(F=(0,X.jsxs)(q,{onClick:S,className:`text-[var(--status-error)] focus:text-[var(--status-error)] data-[highlighted]:text-[var(--status-error)] [&_svg]:text-[var(--status-error)]`,children:[P,c]}),t[31]=c,t[32]=S,t[33]=F):F=t[33];let I;t[34]!==M||t[35]!==F?(I=(0,X.jsxs)(de,{className:`min-w-[10rem]`,children:[M,N,F]}),t[34]=M,t[35]=F,t[36]=I):I=t[36];let L;return t[37]!==h||t[38]!==A||t[39]!==I?(L=(0,X.jsxs)(fe,{open:h,onOpenChange:g,children:[A,I]}),t[37]=h,t[38]=A,t[39]=I,t[40]=L):L=t[40],L},vt=e=>{let t=(0,J.c)(109),{activeOverride:a,onMobileBack:o}=e,{t:s}=h(),{isMobile:c}=ee(),l=se(),u=Y.useSyncExternalStore(n,r,r),d=i(yt),p=a??d,m=i(bt),_=c||m,v=k(),y=C(),b=y.data,x;t[0]===u?x=t[1]:(x=e=>e.assistantByTransport[u]??null,t[0]=u,t[1]=x);let S=N(x),w=N(xt),T=N(St),E;t[2]!==S||t[3]!==b?.assistants?(E=b?.assistants.find(e=>e.id===S)??null,t[2]=S,t[3]=b?.assistants,t[4]=E):E=t[4];let D=E,O;t[5]!==w||t[6]!==S||t[7]!==b?(O=()=>{!S&&b?.assistants[0]&&w(b.assistants[0].id)},t[5]=w,t[6]=S,t[7]=b,t[8]=O):O=t[8];let A=b?.assistants,j;t[9]!==w||t[10]!==S||t[11]!==A?(j=[w,S,A],t[9]=w,t[10]=S,t[11]=A,t[12]=j):j=t[12],Y.useEffect(O,j);let M;t[13]!==D||t[14]!==w||t[15]!==S||t[16]!==b?.assistants||t[17]!==y.isSuccess?(M=()=>{y.isSuccess&&S&&!D&&w(b?.assistants[0]?.id??null)},t[13]=D,t[14]=w,t[15]=S,t[16]=b?.assistants,t[17]=y.isSuccess,t[18]=M):M=t[18];let P=b?.assistants,I;t[19]!==D||t[20]!==w||t[21]!==S||t[22]!==y.isSuccess||t[23]!==P?(I=[D,w,S,P,y.isSuccess],t[19]=D,t[20]=w,t[21]=S,t[22]=y.isSuccess,t[23]=P,t[24]=I):I=t[24],Y.useEffect(M,I);let L;t[25]!==l||t[26]!==T?(L=()=>{if(T(),l){l.openSettings();return}let e=i.getState();e.setSettingsPage(`assistants`),e.setSettingsDialogOpen(!0)},t[25]=l,t[26]=T,t[27]=L):L=t[27];let R=f(L),z;t[28]===l?z=t[29]:(z=e=>{F(e,l?{openMobileSettings:l.openSettings}:void 0)},t[28]=l,t[29]=z);let B=f(z),V;t[30]!==D||t[31]!==l?(V=()=>{D&&F(D.id,l?{openMobileSettings:l.openSettings}:void 0)},t[30]=D,t[31]=l,t[32]=V):V=t[32];let U=f(V),[te,ne]=Y.useState(null),re;t[33]===Symbol.for(`react.memo_cache_sentinel`)?(re=e=>{ne(e)},t[33]=re):re=t[33];let ie=f(re),ae;t[34]===Symbol.for(`react.memo_cache_sentinel`)?(ae=e=>{e||ne(null)},t[34]=ae):ae=t[34];let oe=f(ae),ce=f(Ct),le;t[35]!==o||t[36]!==ce?(le=()=>{if(o){o();return}ce()},t[35]=o,t[36]=ce,t[37]=le):le=t[37];let K=f(le),ue=mt({capabilityPending:v.isPending,snapshotPending:y.isPending,capabilityError:v.isError,supported:v.data?.supported,capabilityEnabled:v.data?.enabled,snapshotEnabled:b?.enabled,snapshotSettled:y.isSuccess,assistantCount:b?.assistants.length??0,hasAssistant:!!D}),de;t[38]!==D||t[39]!==K||t[40]!==_?(de=(e,t,n,r)=>(0,X.jsxs)(`div`,{className:`relative flex h-full min-h-0 flex-col`,children:[_?(0,X.jsx)(gt,{assistant:D,onBack:K}):null,(0,X.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col items-center justify-center px-6 pt-[calc(max(0.25rem,var(--oc-safe-area-top,0px))+var(--oc-mobile-detail-navigation-height,3.5rem))] text-center`,children:[(0,X.jsx)(W,{name:e,className:`size-6 text-muted-foreground`}),(0,X.jsx)(`h1`,{className:`mt-4 typography-ui-header font-semibold`,children:t}),n?(0,X.jsx)(`p`,{className:`mt-2 max-w-md typography-ui text-muted-foreground`,children:n}):null,r?(0,X.jsx)(`div`,{className:`mt-5`,children:r}):null]})]}),t[38]=D,t[39]=K,t[40]=_,t[41]=de):de=t[41];let q=de;if(ue===`loading`){let e;t[42]!==D||t[43]!==K||t[44]!==_?(e=_?(0,X.jsx)(gt,{assistant:D,onBack:K}):null,t[42]=D,t[43]=K,t[44]=_,t[45]=e):e=t[45];let n;t[46]===s?n=t[47]:(n=s(`common.loading`),t[46]=s,t[47]=n);let r;t[48]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,X.jsx)(W,{name:`loader-4`,className:`size-6 animate-spin text-muted-foreground`}),t[48]=r):r=t[48];let i;t[49]===n?i=t[50]:(i=(0,X.jsx)(`div`,{className:`flex min-h-0 flex-1 flex-col items-center justify-center px-6 pt-[calc(max(0.25rem,var(--oc-safe-area-top,0px))+var(--oc-mobile-detail-navigation-height,3.5rem))] text-center`,"aria-busy":`true`,"aria-label":n,"data-assistant-workspace-loading":``,children:r}),t[49]=n,t[50]=i);let a;return t[51]!==e||t[52]!==i?(a=(0,X.jsxs)(`div`,{className:`relative flex h-full min-h-0 flex-col`,children:[e,i]}),t[51]=e,t[52]=i,t[53]=a):a=t[53],a}if(ue===`unavailable`){let e;return t[54]!==q||t[55]!==s?(e=q(`cloud-off`,s(`assistants.state.unavailable`)),t[54]=q,t[55]=s,t[56]=e):e=t[56],e}if(ue===`onboarding`){let e;if(t[57]!==R||t[58]!==q||t[59]!==s){let n=s(`assistants.onboarding.title`),r=s(`assistants.onboarding.description`),i;t[61]===s?i=t[62]:(i=s(`assistants.onboarding.action`),t[61]=s,t[62]=i);let a;t[63]!==R||t[64]!==i?(a=(0,X.jsx)(G,{onClick:R,children:i}),t[63]=R,t[64]=i,t[65]=a):a=t[65],e=q(`ai-agent`,n,r,a),t[57]=R,t[58]=q,t[59]=s,t[60]=e}else e=t[60];return e}if(!b||!D){let e;return t[66]!==q||t[67]!==s?(e=q(`cloud-off`,s(`assistants.state.unavailable`)),t[66]=q,t[67]=s,t[68]=e):e=t[68],e}let fe;t[69]===D.name?fe=t[70]:(fe=g(D.name),t[69]=D.name,t[70]=fe);let pe=fe,he;t[71]!==D.enabled||t[72]!==y.isError||t[73]!==s?(he=D.enabled?y.isError?s(`assistants.state.staleSnapshot`):null:s(`assistants.state.assistantDisabled`),t[71]=D.enabled,t[72]=y.isError,t[73]=s,t[74]=he):he=t[74];let ge=he,_e;t[75]!==_||t[76]!==B||t[77]!==ie||t[78]!==w||t[79]!==S||t[80]!==b||t[81]!==s?(_e=_?null:(0,X.jsxs)(`section`,{className:`flex h-full min-h-0 w-[clamp(16rem,22vw,20rem)] shrink-0 flex-col overflow-hidden`,children:[(0,X.jsx)(`header`,{className:`shrink-0 px-4 pb-3 pt-4 sm:px-5`,children:(0,X.jsx)(`h1`,{className:`truncate typography-ui-label font-semibold text-foreground`,children:s(`assistants.title`)})}),(0,X.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-3 pb-4 sm:px-4`,role:`listbox`,"aria-label":s(`assistants.listAria`),children:(0,X.jsx)(`div`,{className:`flex flex-col gap-1 border-t border-border/40 pt-3`,children:b.assistants.map(e=>{let t=e.id===S,n=g(e.name);return(0,X.jsx)(_t,{assistantID:e.id,displayName:n.displayName,avatarEmoji:n.avatarEmoji??void 0,selected:t,enabled:e.enabled,editLabel:s(`assistants.menu.edit`),deleteLabel:s(`assistants.settings.delete`),assignedSessionIDs:e.assignedSessionIDs,serverWorking:e.working,onSelect:()=>w(e.id),onEdit:()=>B(e.id),onDelete:()=>ie(e)},e.id)})})})]}),t[75]=_,t[76]=B,t[77]=ie,t[78]=w,t[79]=S,t[80]=b,t[81]=s,t[82]=_e):_e=t[82];let ve=te!==null,ye;t[83]!==te||t[84]!==oe||t[85]!==ve?(ye=(0,X.jsx)(pt,{assistant:te,open:ve,onOpenChange:oe}),t[83]=te,t[84]=oe,t[85]=ve,t[86]=ye):ye=t[86];let be=!_&&`border-l border-[var(--surface-subtle)]`,Z;t[87]===be?Z=t[88]:(Z=H(`relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-background`,be),t[87]=be,t[88]=Z);let Q;t[89]!==D||t[90]!==K||t[91]!==_||t[92]!==U||t[93]!==pe||t[94]!==s?(Q=_?(0,X.jsx)(gt,{assistant:D,onBack:K}):(0,X.jsxs)(`header`,{className:`flex h-16 shrink-0 items-center gap-3.5 border-b border-[var(--surface-subtle)]/70 px-5 sm:px-7`,children:[(0,X.jsx)(me,{name:D.id,emoji:pe.avatarEmoji,size:30,label:pe.displayName||D.name}),(0,X.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,X.jsx)(`div`,{className:`truncate typography-ui-label font-semibold tracking-[-0.01em] text-foreground`,children:pe.displayName}),(0,X.jsx)(`div`,{className:`mt-1 truncate typography-micro leading-none text-muted-foreground/55`,children:s(`assistants.conversation.contactHint`)})]}),(0,X.jsx)(G,{type:`button`,variant:`ghost`,size:`icon`,className:`size-8 shrink-0 rounded-full text-muted-foreground/65 transition-colors hover:text-foreground`,onClick:U,"aria-label":s(`assistants.conversation.openSettings`),children:(0,X.jsx)(W,{name:`settings-3`,className:`size-4`})})]}),t[89]=D,t[90]=K,t[91]=_,t[92]=U,t[93]=pe,t[94]=s,t[95]=Q):Q=t[95];let $;t[96]!==p||t[97]!==D||t[98]!==_||t[99]!==ge?($=(0,X.jsx)(dt,{assistant:D,warning:ge,active:p,overlayHeader:_}),t[96]=p,t[97]=D,t[98]=_,t[99]=ge,t[100]=$):$=t[100];let xe;t[101]!==Z||t[102]!==Q||t[103]!==$?(xe=(0,X.jsxs)(`div`,{className:Z,children:[Q,$]}),t[101]=Z,t[102]=Q,t[103]=$,t[104]=xe):xe=t[104];let Se;return t[105]!==_e||t[106]!==ye||t[107]!==xe?(Se=(0,X.jsxs)(`div`,{className:`relative flex h-full min-h-0 overflow-hidden bg-background`,"data-presentation":`workspace`,children:[_e,ye,xe]}),t[105]=_e,t[106]=ye,t[107]=xe,t[108]=Se):Se=t[108],Se};function yt(e){return e.activeMainTab===`assistant`}function bt(e){return e.isMobile}function xt(e){return e.selectAssistant}function St(e){return e.requestCreate}function Ct(){i.getState().setActiveMainTab(`chat`)}export{me as a,_e as i,ht as n,pt as r,vt as t};
@@ -1 +0,0 @@
1
- import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{E as t,t as n}from"./useUIStore-DwpQTjLO.js";import{nt as r,rt as i}from"./vendor-base-ui-CnHfUmDN.js";import{l as a}from"./vendor-tanstack-react-query-D7pys_c9.js";import{r as o}from"./vendor-sonner-DVzioF3d.js";import{n as s}from"./clipboard-C05anBwU.js";import{n as c}from"./Icon-CeKHYyKZ.js";import{n as l}from"./useRuntimeAPIs-DSLXJybW.js";import{n as u}from"./useEffectiveDirectory-DJSYYkVp.js";import{n as d,r as f,t as p}from"./fileQueries-BcdWOPlD.js";import{t as m}from"./diagram-Csk5GpDC.js";var h=e(i(),1),g=r();function _(){let{t:e}=s(),{files:r}=l(),i=a(),_=u()??null,v=t(),[y,b]=h.useState(null),[x,S]=h.useState(``),C=h.useRef(null),w=h.useRef(!1),T=h.useRef(null),E=h.useRef(0),D=h.useRef(Promise.resolve()),O=n(e=>e.pendingDiagramFile),k=f({scopeDirectory:_,path:y},{enabled:!!y});if(y&&(T.current?.path!==y||T.current.transport!==v||T.current.scopeDirectory!==_)){let e=E.current+1;E.current=e,T.current={path:y,generation:e,transport:v,scopeDirectory:_}}h.useEffect(()=>(w.current=!0,()=>{w.current=!1,E.current+=1}),[]),h.useEffect(()=>{if(!O)return;let e=n.getState().consumePendingDiagramFile();if(e){let t=E.current+1;E.current=t,T.current={path:e,generation:t,transport:v,scopeDirectory:_},b(e)}},[_,O,v]),h.useEffect(()=>{let e=T.current;if(!(!e||e.path!==y||e.transport!==v||e.scopeDirectory!==_)){if(k.data!==void 0){S(k.data);return}k.isError&&S(``)}},[_,y,k.data,k.isError,v]);let A=h.useCallback(async()=>{let t=C.current?.getXml(),n=T.current,a=r?.writeFile;if(!y||!a||!t||t===x||n?.path!==y)return;let{path:s,generation:c,scopeDirectory:l,transport:u}=n,f=D.current.then(async()=>{try{if(!(await a(s,t))?.success)throw Error(e(`filesView.toast.writeFileFailed`));let n={scopeDirectory:l,path:s};await i.cancelQueries({queryKey:p(n,u),exact:!0}),d(i,n,u,t);let r=T.current;w.current&&r?.path===s&&r.generation===c&&r.transport===u&&r.scopeDirectory===l&&S(t)}catch(t){o.error(t instanceof Error?t.message:e(`filesView.toast.saveFailed`))}});D.current=f.catch(()=>void 0),await f},[y,r,i,e,x]),j=y?y.split(`/`).pop()||y:``;return y?k.isPending?(0,g.jsx)(`div`,{className:`flex h-full items-center justify-center p-3`,children:(0,g.jsx)(c,{name:`loader-4`,className:`size-4 animate-spin`})}):(0,g.jsxs)(`div`,{className:`flex h-full flex-col`,children:[(0,g.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/30 px-3 py-1.5`,children:[(0,g.jsx)(c,{name:`file`,className:`size-4 shrink-0 text-muted-foreground`}),(0,g.jsx)(`span`,{className:`typography-ui text-muted-foreground truncate flex-1`,children:j}),(0,g.jsx)(`button`,{type:`button`,onClick:()=>void A(),className:`size-6 flex items-center justify-center rounded-md text-foreground hover:bg-interactive-hover/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`,title:e(`filesView.diagram.saveDiagram`),children:(0,g.jsx)(c,{name:`save-3`,className:`size-4`})}),(0,g.jsx)(`button`,{type:`button`,onClick:()=>n.getState().setActiveMainTab(`chat`),className:`size-6 flex items-center justify-center rounded-md text-muted-foreground hover:bg-interactive-hover/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`,title:e(`filesView.diagram.closeDiagramView`),children:(0,g.jsx)(c,{name:`close`,className:`size-4`})})]}),(0,g.jsx)(`div`,{className:`flex-1 min-h-0`,children:(0,g.jsx)(m,{ref:C,xml:x,className:`h-full`})})]}):(0,g.jsx)(`div`,{className:`flex h-full items-center justify-center p-3`,children:(0,g.jsx)(`div`,{className:`typography-ui text-muted-foreground`,children:e(`filesView.editor.pickFileFromTree`)})})}export{_ as DiagramView};
@@ -1 +0,0 @@
1
- import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{sn as t}from"./persistence-CVTWFaqe.js";import{nt as n,rt as r}from"./vendor-base-ui-CnHfUmDN.js";import{n as i}from"./vendor-react-DLPElpoQ.js";import{t as a}from"./utils-81nUI_xp.js";import{h as o,m as s}from"./markdownTheme-DIB8WVmz.js";var c=500,l=12,u=(e={})=>{let t=e.delayMs??c,n=(e.moveThresholdPx??l)**2,r=e.schedule??((e,t)=>setTimeout(e,t)),i=e.clear??(e=>clearTimeout(e)),a=null,o=null,s=(t=!1)=>{a&&(i(a.timer),a=null,t||e.onPressedKeyChange?.(null))};return{start:({pointerId:n,key:i,clientX:c,clientY:l,onTrigger:u})=>{s(),e.onPressedKeyChange?.(i),a={pointerId:n,key:i,startX:c,startY:l,timer:r(()=>{!a||a.pointerId!==n||a.key!==i||(a.triggered=!0,o=i,u())},t),triggered:!1}},move:(e,t,r)=>{if(a?.pointerId!==e||a.triggered)return;let i=t-a.startX,o=r-a.startY;i*i+o*o>n&&s()},end:e=>{a?.pointerId===e&&s(a.triggered)},cancel:e=>{a?.pointerId===e&&s()},openFromContextMenu:(t,n)=>{s(),o=t,e.onPressedKeyChange?.(t),n()},consumeClick:e=>o===e&&(o=null,!0),reset:()=>{s(),o=null,e.onPressedKeyChange?.(null)}}},d=`#openchamber-skill:`,f=`#openchamber-agent:`,p=e=>`https://opencode.ai/docs/agents/#${encodeURIComponent(e)}`,m=e=>`${d}${encodeURIComponent(e)}`,h=e=>`${f}${encodeURIComponent(e)}`,g=e=>{if(!e?.startsWith(d))return null;try{return decodeURIComponent(e.slice(19))}catch{return null}},_=e=>{if(!e?.startsWith(f))return null;try{return decodeURIComponent(e.slice(19))}catch{return null}},v=6e5,y=new Map,b=e=>{let t=o(e);if(!t)return;let n=y.get(t);if(n){if(n.expiresAt<=Date.now()){y.delete(t);return}return n.outsideFileGrant}},x=(e,t,n)=>{let r=o(e);!r||!t||y.set(r,{outsideFileGrant:t,expiresAt:typeof n==`number`&&Number.isFinite(n)?n:Date.now()+v})},S=async(e,n)=>{let r=o(e);if(!r||!n||s(r,n))return;let i=b(r);if(i)return i;let a=await t(r);if(!(!a.success||!a.path||!a.outsideFileGrant))return x(a.path,a.outsideFileGrant),o(a.path)!==r&&x(r,a.outsideFileGrant),a.outsideFileGrant},C=[`user-blocking`,`visible`,`background`],w=e=>{let t={"user-blocking":{head:null,tail:null},visible:{head:null,tail:null},background:{head:null,tail:null}},n=Math.max(1,Math.floor(e.maxTasksPerFrame)),r=Math.max(1,e.frameBudgetMs),i=null,a=!1,o=0,s=e=>{let n=t[e.priority];e.previous=n.tail,n.tail?n.tail.next=e:n.head=e,n.tail=e},c=e=>{let n=t[e.priority];e.previous?e.previous.next=e.next:n.head=e.next,e.next?e.next.previous=e.previous:n.tail=e.previous,e.next=null,e.previous=null},l=()=>{for(let e of C){let n=t[e].head;if(n)return c(n),n.consumed=!0,--o,n}return null},u=()=>{i===null&&o!==0&&(i=e.requestFrame(d))},d=()=>{i=null;let t=e.now(),a=0;for(;o>0&&a<n;){let n=l();if(!n||(n.run(),a+=1,e.now()-t>=r))break}u()},f=()=>{i!==null||a||o===0||(a=!0,i=e.requestFrame(()=>{i=null,a=!1,u()}))};return{enqueue(t,n){let r={cancelled:!1,consumed:!1,next:null,previous:null,priority:n?.priority??`background`,run:t};return s(r),o+=1,!a&&i===null&&f(),()=>{r.cancelled||r.consumed||(r.cancelled=!0,c(r),--o,o===0&&(i!==null&&(e.cancelFrame(i),i=null),a=!1))}},clear(){for(let e of C){let n=t[e],r=n.head;for(;r;){let e=r.next;r.cancelled=!0,r.next=null,r.previous=null,r=e}n.head=null,n.tail=null}o=0,i!==null&&(e.cancelFrame(i),i=null),a=!1}}},T=null,E=()=>typeof window>`u`?null:T||(T=w({requestFrame:e=>window.requestAnimationFrame(e),cancelFrame:e=>window.cancelAnimationFrame(e),now:()=>performance.now(),maxTasksPerFrame:2,frameBudgetMs:6}),T),D=(e,t)=>{let n=E();return n?n.enqueue(e,t):(e(),()=>{})},O={textThrottleMs:20,markdownPaceMs:64},k={textThrottleMs:100,markdownPaceMs:128},A=e=>e===`android`?k:O,j=i(),M=e(r(),1),N=n(),P=M.createContext(!1),F=e=>{let t=(0,j.c)(3),{disabled:n,children:r}=e,i;return t[0]!==r||t[1]!==n?(i=(0,N.jsx)(P.Provider,{value:n,children:r}),t[0]=r,t[1]=n,t[2]=i):i=t[2],i},I=e=>{let t=(0,j.c)(8),{children:n,className:r}=e,[i,o]=M.useState(!1),s,c;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(s=()=>{let e=null,t=()=>o(!0);return typeof window<`u`&&typeof window.requestAnimationFrame==`function`?e=window.requestAnimationFrame(t):t(),()=>{e!==null&&typeof window<`u`&&typeof window.cancelAnimationFrame==`function`&&window.cancelAnimationFrame(e)}},c=[],t[0]=s,t[1]=c):(s=t[0],c=t[1]),M.useEffect(s,c);let l=i?`opacity-100 translate-y-0`:`opacity-0 translate-y-2`,u;t[2]!==r||t[3]!==l?(u=a(`w-full transition-all duration-300 ease-out`,l,r),t[2]=r,t[3]=l,t[4]=u):u=t[4];let d;return t[5]!==n||t[6]!==u?(d=(0,N.jsx)(`div`,{className:u,children:n}),t[5]=n,t[6]=u,t[7]=d):d=t[7],d},L=e=>{let t=(0,j.c)(5),{children:n,className:r,skipAnimation:i,forceAnimation:a,ignoreContextDisabled:o,respectReducedMotion:s}=e,c=a!==void 0&&a,l=o!==void 0&&o,u=s!==void 0&&s,d=M.useContext(P),f=u&&typeof window<`u`&&typeof window.matchMedia==`function`&&window.matchMedia(`(prefers-reduced-motion: reduce)`).matches;if(!c||i||!l&&d||f){let e;return t[0]===n?e=t[1]:(e=(0,N.jsx)(N.Fragment,{children:n}),t[0]=n,t[1]=e),e}let p;return t[2]!==n||t[3]!==r?(p=(0,N.jsx)(I,{className:r,children:n}),t[2]=n,t[3]=r,t[4]=p):p=t[4],p};export{S as a,p as c,g as d,u as f,D as i,m as l,L as n,b as o,A as r,h as s,F as t,_ as u};