@openchambery/web 1.17.1-beta.11 → 1.17.1-beta.13

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 (133) hide show
  1. package/dist/assets/{AgentAvatar-C6Hxu5_o.js → AgentAvatar-Coq-zdWI.js} +1 -1
  2. package/dist/assets/AssistantView-BXLxZXL5.js +1 -0
  3. package/dist/assets/{ChatContainer-CCyV53_o.js → ChatContainer-BJGlptbG.js} +22 -22
  4. package/dist/assets/DiagramView-Czx_DVw0.js +1 -0
  5. package/dist/assets/{FadeInOnReveal-CvNA_aGD.js → FadeInOnReveal-1KpYuooP.js} +1 -1
  6. package/dist/assets/{Icon-BPJ_quEe.js → Icon-Vc-aDche.js} +1 -1
  7. package/dist/assets/{MarkdownRendererImpl-DswfuLC-.js → MarkdownRendererImpl-B9Bv95Vd.js} +2 -2
  8. package/dist/assets/MobileDetailNavigation-RupyXzPu.js +1 -0
  9. package/dist/assets/MobileShareBridge-LVT1_q5C.js +4 -0
  10. package/dist/assets/MobileSurface-BVwSEqn4.js +2 -0
  11. package/dist/assets/MultiRunWindow-C7hbEpv9.js +1 -0
  12. package/dist/assets/OnboardingScreen-CrekVj31.js +1 -0
  13. package/dist/assets/OverlayScrollbar-vZXoSFA9.js +1 -0
  14. package/dist/assets/SettingsView-C1sYcHr1.js +25 -0
  15. package/dist/assets/SettingsWindow-MtcqpoiN.js +1 -0
  16. package/dist/assets/TerminalView-DXQZlHNA.js +8 -0
  17. package/dist/assets/{ToolOutputDialog-IVZYEtTZ.js → ToolOutputDialog-_Qzdo46T.js} +1 -1
  18. package/dist/assets/appThemeRegistry-Chv6_pxq.js +2 -0
  19. package/dist/assets/{button-BmNJBT13.js → button-rDAIP5kY.js} +1 -1
  20. package/dist/assets/{collapsible-68IqWBM1.js → collapsible-nLeZm1kw.js} +1 -1
  21. package/dist/assets/{context-menu-m2Ep287d.js → context-menu-DeJdA9F6.js} +1 -1
  22. package/dist/assets/desktopHosts-RE7NIkeq.js +1 -0
  23. package/dist/assets/desktopRelayRestore-BzyaU7-i.js +1 -0
  24. package/dist/assets/{diagram-YN0GZ1Q0.js → diagram-C7qK82ZL.js} +1 -1
  25. package/dist/assets/dialog-DlUK6mj-.js +1 -0
  26. package/dist/assets/es-DMX-Uo1i.js +15 -0
  27. package/dist/assets/{fileQueries-i4oziMnC.js → fileQueries-D8m3PQr0.js} +1 -1
  28. package/dist/assets/{fr-jWNPO91J.js → fr-CSFYzSd8.js} +2 -2
  29. package/dist/assets/{gitApi-CwRDHIOl.js → gitApi-DV6F5KKI.js} +1 -1
  30. package/dist/assets/gitApiHttp-BswZXfrf.js +2 -0
  31. package/dist/assets/i18n-BAqcSln8.js +1 -0
  32. package/dist/assets/{input-BLVhPvhN.js → input-CxulMZ9j.js} +1 -1
  33. package/dist/assets/insertionBoundaries-7-TvMfgn.js +1 -0
  34. package/dist/assets/{ja-Dcb0VtDE.js → ja-C3eCimlB.js} +2 -2
  35. package/dist/assets/{ko-CX71xNDX.js → ko-gnciIU9a.js} +2 -2
  36. package/dist/assets/main-CADHVH9Y.js +172 -0
  37. package/dist/assets/main-q1V-_wWx.js +2 -0
  38. package/dist/assets/{markdown-worker-Cq3Twp1A.js → markdown-worker-DmBYud1m.js} +1 -1
  39. package/dist/assets/{markdownTheme-CCnvI6L0.js → markdownTheme-CNJf24L-.js} +1 -1
  40. package/dist/assets/miniChat-VXuyA8mS.js +2 -0
  41. package/dist/assets/mobile-CaKZM4y3.js +2 -0
  42. package/dist/assets/multirun-QqMnIbXa.js +4 -0
  43. package/dist/assets/persistence-CFtygeYy.js +3 -0
  44. package/dist/assets/{pl-mR_3Sqr8.js → pl-CNk6BfNS.js} +1 -1
  45. package/dist/assets/projectMeta-WJhNyk90.js +3 -0
  46. package/dist/assets/pt-BR-D4G-0rLj.js +15 -0
  47. package/dist/assets/radio-C1riLnF0.js +1 -0
  48. package/dist/assets/renderElectronMiniChatApp-CnmMV1o9.js +2 -0
  49. package/dist/assets/renderMobileApp-Bhe41b2q.js +23 -0
  50. package/dist/assets/runtimeConfig-D4ETkuTj.js +1 -0
  51. package/dist/assets/runtimeEndpointReset-GtJt8YhT.js +145 -0
  52. package/dist/assets/sortable-tabs-strip-Fiq5nrkF.js +1 -0
  53. package/dist/assets/src-DrgsP0XF.css +2 -0
  54. package/dist/assets/{store-oojlrF4d.js → store-CS6l6pH7.js} +3 -3
  55. package/dist/assets/{terminalApi-BYTA045F.js → terminalApi-MQ285A_s.js} +1 -1
  56. package/dist/assets/{tooltip-BYGBBTc2.js → tooltip-CbxKYhKv.js} +1 -1
  57. package/dist/assets/ui-D5j3JxkE.js +475 -0
  58. package/dist/assets/uk-BUry1gqt.js +15 -0
  59. package/dist/assets/{url-CGHw6D-E.js → url-BSalv2ov.js} +1 -1
  60. package/dist/assets/useAppFontEffects-CaGwli-K.js +7 -0
  61. package/dist/assets/{useAssistantUIStore-ESTV1mEY.js → useAssistantUIStore-DGtjM827.js} +1 -1
  62. package/dist/assets/useDesktopWindowControlsLayout-DCZXksMp.js +1 -0
  63. package/dist/assets/useEffectiveDirectory-CssNtlqg.js +1 -0
  64. package/dist/assets/{useFontPreferences-Bu4ME5hk.js → useFontPreferences-C7FEHFns.js} +1 -1
  65. package/dist/assets/useSessionAutoCleanup-B_mSwN7n.js +1 -0
  66. package/dist/assets/useUIStore-WQV8IuFw.js +1 -0
  67. package/dist/assets/{useWorkerHighlightedLines-BGHFpbvZ.js → useWorkerHighlightedLines-C74ZKDtw.js} +1 -1
  68. package/dist/assets/{utils-DyUyiLje.js → utils-Cwlw2GM0.js} +1 -1
  69. package/dist/assets/zh-CN-CJJZzHVW.js +15 -0
  70. package/dist/assets/zh-TW-DxhIXjND.js +15 -0
  71. package/dist/index.html +9 -9
  72. package/dist/mini-chat.html +9 -9
  73. package/dist/mobile.html +9 -9
  74. package/package.json +1 -1
  75. package/server/index.d.ts +2 -0
  76. package/server/index.js +8 -0
  77. package/server/lib/client-auth/pairing.js +7 -1
  78. package/server/lib/client-auth/pairing.test.js +45 -0
  79. package/server/lib/desktop-hosts/routes.js +152 -0
  80. package/server/lib/desktop-hosts/routes.test.js +194 -0
  81. package/server/lib/opencode/bootstrap-runtime.js +5 -0
  82. package/server/lib/opencode/core-routes.js +3 -0
  83. package/server/lib/opencode/core-routes.test.js +15 -0
  84. package/server/lib/opencode/openchamber-routes.js +10 -0
  85. package/server/lib/package-manager.js +3 -1
  86. package/server/lib/relay/DOCUMENTATION.md +43 -0
  87. package/server/lib/relay/host-client.js +4 -1
  88. package/server/lib/relay/service.js +4 -0
  89. package/server/lib/relay/tunnel-host.js +57 -4
  90. package/server/lib/relay/tunnel-host.test.js +250 -2
  91. package/dist/assets/AssistantView-CCYDEoHv.js +0 -1
  92. package/dist/assets/DiagramView-ClKA8FV3.js +0 -1
  93. package/dist/assets/MobileDetailNavigation-DLoODj_O.js +0 -1
  94. package/dist/assets/MobileShareBridge-CAc7AFue.js +0 -4
  95. package/dist/assets/MobileSurface-76JUp8KU.js +0 -2
  96. package/dist/assets/MultiRunWindow-DkKQaIfX.js +0 -1
  97. package/dist/assets/OnboardingScreen-CdvaEBtt.js +0 -1
  98. package/dist/assets/OverlayScrollbar-B-K9NJfL.js +0 -1
  99. package/dist/assets/SettingsView-zu_QFVkf.js +0 -25
  100. package/dist/assets/SettingsWindow-D2q5mZqr.js +0 -1
  101. package/dist/assets/TerminalView-BSkAxeUb.js +0 -8
  102. package/dist/assets/appThemeRegistry-b9Hk2IDb.js +0 -2
  103. package/dist/assets/clipboard-CXWbHCAk.js +0 -1
  104. package/dist/assets/desktopHosts-BpjUSDz4.js +0 -1
  105. package/dist/assets/desktopRelayRestore-BzUEk1i7.js +0 -1
  106. package/dist/assets/dialog-BV1ENlLf.js +0 -1
  107. package/dist/assets/es-EXZUX1sB.js +0 -15
  108. package/dist/assets/gitApiHttp-C1DaEMKR.js +0 -2
  109. package/dist/assets/insertionBoundaries-hP0Vo_9C.js +0 -1
  110. package/dist/assets/main-BLqbjxPt.js +0 -2
  111. package/dist/assets/main-C7LjTesY.js +0 -172
  112. package/dist/assets/miniChat-Cr9SuyZk.js +0 -2
  113. package/dist/assets/mobile-Bp-qiKtn.js +0 -2
  114. package/dist/assets/multirun-_rwgORX-.js +0 -4
  115. package/dist/assets/persistence-C_KCiMcY.js +0 -3
  116. package/dist/assets/projectMeta-7TXLLSNB.js +0 -3
  117. package/dist/assets/pt-BR-D1RpVOvC.js +0 -15
  118. package/dist/assets/radio-CDEqI-yy.js +0 -1
  119. package/dist/assets/renderElectronMiniChatApp-ChsEWTbg.js +0 -2
  120. package/dist/assets/renderMobileApp-B1pX9NK3.js +0 -23
  121. package/dist/assets/runtimeConfig-JSaYFA5x.js +0 -1
  122. package/dist/assets/runtimeEndpointReset-BS3Sd-xn.js +0 -145
  123. package/dist/assets/sortable-tabs-strip-DUk_Td2z.js +0 -1
  124. package/dist/assets/src-8zziWB78.css +0 -2
  125. package/dist/assets/uk-UvnVqWv-.js +0 -15
  126. package/dist/assets/useAppFontEffects-VKLeTcCY.js +0 -7
  127. package/dist/assets/useDesktopWindowControlsLayout-DLuCJzOs.js +0 -1
  128. package/dist/assets/useEffectiveDirectory-Bt1X5uGs.js +0 -1
  129. package/dist/assets/useMobileNavigationStore-DRbvzR8W.js +0 -475
  130. package/dist/assets/useSessionAutoCleanup-C8clHymf.js +0 -1
  131. package/dist/assets/useUIStore-D6BTB4va.js +0 -1
  132. package/dist/assets/zh-CN-9v-RC966.js +0 -15
  133. package/dist/assets/zh-TW-BZ_wZdC-.js +0 -15
@@ -1,5 +1,6 @@
1
1
  import { registerSessionIndexRoutes } from '../session-index/routes.js';
2
2
  import { registerTranscriptCacheRoutes } from '../transcript-cache/routes.js';
3
+ import { registerDesktopHostRoutes } from '../desktop-hosts/routes.js';
3
4
 
4
5
  export const registerOpenChamberRoutes = (app, dependencies) => {
5
6
  const {
@@ -15,10 +16,19 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
15
16
  sessionIndexService,
16
17
  sessionIndexSyncRuntime,
17
18
  transcriptCacheService,
19
+ getSshRoutingTable,
20
+ getPairingSession,
21
+ express,
18
22
  } = dependencies;
19
23
 
20
24
  registerSessionIndexRoutes(app, { sessionIndexService, sessionIndexSyncRuntime });
21
25
  registerTranscriptCacheRoutes(app, { transcriptCacheService });
26
+ registerDesktopHostRoutes(app, {
27
+ readSettingsFromDiskMigrated,
28
+ getSshRoutingTable,
29
+ getPairingSession,
30
+ express,
31
+ });
22
32
 
23
33
  app.get('/api/openchamber/update-check', async (req, res) => {
24
34
  try {
@@ -669,7 +669,9 @@ export function getUpdateCommand(pm = detectPackageManager()) {
669
669
  case 'bun':
670
670
  return `${pmCommand} add -g ${PACKAGE_NAME}@latest`;
671
671
  default:
672
- return `${pmCommand} install -g ${PACKAGE_NAME}@latest`;
672
+ // --force: npm refuses to overwrite an existing global bin (EEXIST) when
673
+ // a previous install left the bin file behind.
674
+ return `${pmCommand} install -g ${PACKAGE_NAME}@latest --force`;
673
675
  }
674
676
  }
675
677
 
@@ -104,6 +104,48 @@ Relay mode plugs into the existing client transport layer rather than a parallel
104
104
 
105
105
  Catalog loaders (`loadProviders` / `loadAgents`) and assistant Query keys gate writes/caches on the **transport fingerprint**, not `runtimeKey`. On endpoint reset and same-device transport switch, `runtimeEndpointReset.ts` must set `useConfigStore.catalogTransportIdentity` to `getRuntimeTransportIdentity()`. Writing `detail.runtimeKey` there silently discards provider/agent catalog refreshes under Relay, which then hides capability-gated surfaces such as Assistants.
106
106
 
107
+ ## SSH host routing
108
+
109
+ Mobile (and other relay clients) can reach a desktop's **SSH-forwarded remote OpenChamber instances** through the same private-relay tunnel that already reaches the desktop's local loopback origin. Routing is host-side only: the client selects a target local-forward port; the tunnel dispatcher dials that port on `127.0.0.1` when — and only when — it is present in the live SSH routing table.
110
+
111
+ ### Header contract
112
+
113
+ - Header name: `x-openchamber-target-port`
114
+ - Value: decimal port number string (e.g. `"41234"`)
115
+ - HTTP: carried on the tunneled request headers
116
+ - WebSocket: optional `headers` field on the `WsOpen` payload (same header name/value)
117
+ - The dispatcher **strips** this header before forwarding to loopback (it is routing metadata, not an upstream header)
118
+
119
+ ### Resolution rules (`resolveTargetPort` in `tunnel-host.js`)
120
+
121
+ | Condition | Result |
122
+ |---|---|
123
+ | Header absent | Dial `getLocalPort()` (desktop local origin) — unchanged default behavior |
124
+ | Header present and `localPort` is in `getSshRoutingTable()` | Dial that port |
125
+ | Header present but port missing/invalid/not in table | **Do not** fall back to the default port |
126
+
127
+ HTTP miss → synthetic **503** with body `{ error: 'ssh-host-unreachable', …, source: 'relay-tunnel-host' }`.
128
+ WS miss → stream abort with reason `ssh-host-unreachable`.
129
+
130
+ ### Routing table (authoritative in memory)
131
+
132
+ - Electron injects `getSshRoutingTable: () => sshManager.getRoutingTable()` into `startWebUiServer`.
133
+ - Table entries are `{ id, localPort }` for SSH sessions whose status phase is **`ready`** and whose `localPort` is finite.
134
+ - Degraded / connecting / disconnected sessions are absent. Settings.json is **not** the allowlist for dialing — only the live table is.
135
+ - Non-desktop runtimes default to `() => []`.
136
+
137
+ ### Companion HTTP APIs (authenticated `/api` gate)
138
+
139
+ - `GET /api/openchamber/desktop-hosts` → `{ hosts: [{ id, label, localPort, reachable }] }` for configured SSH instances. `reachable` is true iff the host id is in the routing table. **Never returns `clientToken`.**
140
+ - `POST /api/openchamber/ssh-host-token` body `{ hostId }` → `{ token }` from `settings.json` `desktopHosts[].clientToken` for SSH instance hosts only. Missing / non-SSH / no token → **404**. Response includes `Cache-Control: no-store`.
141
+
142
+ ### Security invariants (do not regress)
143
+
144
+ - Path allowlists (`isAllowedHttpPath` / `ALLOWED_WS_PATHS`) are **not** relaxed for SSH targets.
145
+ - The dispatcher **never injects credentials**; the client still authenticates with its own bearer / `oc_url_token` against the target instance.
146
+ - Port legitimacy is solely the in-memory routing table, not client-supplied trust and not settings alone.
147
+ - List endpoints must not leak `clientToken`; token mint is a separate authenticated POST.
148
+
107
149
  ## Design invariants (do not regress)
108
150
 
109
151
  - The relay never sees plaintext application traffic; it sees only routing metadata (routing id, connection identifiers, timestamps, coarse counts).
@@ -112,5 +154,6 @@ Catalog loaders (`loadProviders` / `loadAgents`) and assistant Query keys gate w
112
154
  - The host dispatcher never injects credentials; the server authenticates each tunneled request.
113
155
  - The tunnel is transparent to the app: adding relay support to a feature should not require the feature to know the relay exists — it goes through the shared runtime transport helpers.
114
156
  - The two implementations stay byte-compatible and the wire format is versioned/negotiated so mixed client/host app versions degrade gracefully rather than break.
157
+ - SSH target-port routing never falls back to the default local port on a routing-table miss (see "SSH host routing").
115
158
 
116
159
  For the operational rules that keep future changes (new WebSocket endpoints, transport refactors, terminal/voice porting) from breaking this, load the `relay-transport` skill.
@@ -54,12 +54,14 @@ const resolveBatchWindowMs = (option) => {
54
54
  * identity: { serverId: string, hostEncPrivateKey: CryptoKey, signRelayAuth: (role: string, connectionId?: string | null) => { ts: number, sig: string, pk: string } },
55
55
  * localPort?: number,
56
56
  * getLocalPort?: () => number,
57
+ * getSshRoutingTable?: () => { id: string, localPort: number }[],
57
58
  * onStatus?: (status: { state: string, lastError: string | null, connectedClients: number }) => void,
58
59
  * logger?: Pick<Console, 'warn'>,
59
60
  * }} options
60
61
  */
61
- export const startRelayHost = ({ relayUrl, identity, localPort, getLocalPort, onStatus, logger = console, batchWindowMs, batch }) => {
62
+ export const startRelayHost = ({ relayUrl, identity, localPort, getLocalPort, getSshRoutingTable = () => [], onStatus, logger = console, batchWindowMs, batch }) => {
62
63
  const resolveLocalPort = typeof getLocalPort === 'function' ? getLocalPort : () => localPort;
64
+ const resolveSshRoutingTable = typeof getSshRoutingTable === 'function' ? getSshRoutingTable : () => [];
63
65
  const localBatch = batch !== false;
64
66
  const resolvedBatchWindowMs = resolveBatchWindowMs(batchWindowMs);
65
67
 
@@ -188,6 +190,7 @@ export const startRelayHost = ({ relayUrl, identity, localPort, getLocalPort, on
188
190
  entry.tunnel = createTunnelHost({
189
191
  connectionId,
190
192
  getLocalPort: resolveLocalPort,
193
+ getSshRoutingTable: resolveSshRoutingTable,
191
194
  getBufferedAmount: () => socket.bufferedAmount,
192
195
  sendFrame: (plaintextFrame) => {
193
196
  if (dataSockets.get(connectionId) !== entry || socket.readyState !== WebSocket.OPEN) return;
@@ -68,6 +68,7 @@ const envRelayUrlOverride = () => {
68
68
  * readSettingsFromDiskMigrated: () => Promise<object>,
69
69
  * writeSettingsToDisk: (settings: object) => Promise<void>,
70
70
  * getLocalPort: () => number,
71
+ * getSshRoutingTable?: () => { id: string, localPort: number }[],
71
72
  * logger?: Pick<Console, 'warn'>,
72
73
  * canHostRelay?: () => boolean,
73
74
  * }} deps
@@ -80,6 +81,8 @@ export const createRelayService = ({
80
81
  // regeneration — see identity.js/signing-key.js.
81
82
  readSettingsStrict,
82
83
  getLocalPort,
84
+ // Live SSH local-forward ports (Electron). Empty outside desktop.
85
+ getSshRoutingTable = () => [],
83
86
  // Returns true when any paired device or pending pairing session uses the
84
87
  // relay transport. The relay lifecycle is driven purely by this demand.
85
88
  hasRelayDemand = async () => false,
@@ -207,6 +210,7 @@ export const createRelayService = ({
207
210
  relayUrl,
208
211
  identity,
209
212
  getLocalPort,
213
+ getSshRoutingTable,
210
214
  logger,
211
215
  onStatus: (next) => {
212
216
  status = next;
@@ -35,9 +35,14 @@ const ALLOWED_WS_PATHS = new Set([
35
35
  '/api/dictation/ws',
36
36
  ]);
37
37
 
38
+ // Client-selected SSH local-forward target (decimal port string). Valid only
39
+ // when present in the injected routing table; never falls back to default port.
40
+ const TARGET_PORT_HEADER = 'x-openchamber-target-port';
41
+
38
42
  // Hop-by-hop headers stripped from tunneled requests; `host` is set by fetch
39
43
  // to the loopback origin. content-length is dropped too because the body is
40
44
  // re-chunked through the tunnel and undici computes framing itself.
45
+ // Target-port is host-routing metadata and must not reach the loopback origin.
41
46
  const STRIPPED_REQUEST_HEADERS = new Set([
42
47
  'connection',
43
48
  'keep-alive',
@@ -45,6 +50,7 @@ const STRIPPED_REQUEST_HEADERS = new Set([
45
50
  'upgrade',
46
51
  'host',
47
52
  'content-length',
53
+ TARGET_PORT_HEADER,
48
54
  ]);
49
55
 
50
56
  // Response framing headers that no longer apply once the body crosses the
@@ -75,14 +81,45 @@ const isWsOpenPayload = (parsed) =>
75
81
  Boolean(parsed && typeof parsed === 'object'
76
82
  && typeof parsed.path === 'string'
77
83
  && typeof parsed.query === 'string'
78
- && (parsed.protocols === undefined || Array.isArray(parsed.protocols)));
84
+ && (parsed.protocols === undefined || Array.isArray(parsed.protocols))
85
+ && (parsed.headers === undefined || (parsed.headers && typeof parsed.headers === 'object')));
79
86
 
80
87
  const isWsClosePayload = (parsed) => Boolean(parsed && typeof parsed === 'object');
81
88
 
89
+ const readHeaderValue = (headers, name) => {
90
+ if (!headers || typeof headers !== 'object') return undefined;
91
+ const want = name.toLowerCase();
92
+ for (const [key, value] of Object.entries(headers)) {
93
+ if (typeof key === 'string' && key.toLowerCase() === want) return value;
94
+ }
95
+ return undefined;
96
+ };
97
+
98
+ /**
99
+ * Resolve loopback dial port from optional x-openchamber-target-port.
100
+ * No header → defaultPort. Header present → only a routingTable hit is accepted;
101
+ * miss/invalid returns null (caller must not fall back to defaultPort).
102
+ * @param {Record<string, string> | undefined} requestHeaders
103
+ * @param {number} defaultPort
104
+ * @param {() => { id?: string, localPort?: number }[]} getSshRoutingTable
105
+ * @returns {number | null}
106
+ */
107
+ export const resolveTargetPort = (requestHeaders, defaultPort, getSshRoutingTable) => {
108
+ const raw = readHeaderValue(requestHeaders, TARGET_PORT_HEADER);
109
+ if (raw === undefined || raw === null || raw === '') return defaultPort;
110
+ const parsed = typeof raw === 'number' ? raw : Number.parseInt(String(raw).trim(), 10);
111
+ if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 65535) return null;
112
+ const table = typeof getSshRoutingTable === 'function' ? getSshRoutingTable() : [];
113
+ if (!Array.isArray(table)) return null;
114
+ const hit = table.some((entry) => Number(entry?.localPort) === parsed);
115
+ return hit ? parsed : null;
116
+ };
117
+
82
118
  /**
83
119
  * @param {{
84
120
  * connectionId: string,
85
121
  * getLocalPort: () => number,
122
+ * getSshRoutingTable?: () => { id: string, localPort: number }[],
86
123
  * sendFrame: (plaintextFrame: Uint8Array) => void | Promise<void>,
87
124
  * getBufferedAmount: () => number,
88
125
  * createWebSocket?: (url: string, protocols: string[] | undefined, options: { headers: Record<string, string> }) => import('ws').WebSocket,
@@ -91,6 +128,7 @@ const isWsClosePayload = (parsed) => Boolean(parsed && typeof parsed === 'object
91
128
  export const createTunnelHost = ({
92
129
  connectionId,
93
130
  getLocalPort,
131
+ getSshRoutingTable = () => [],
94
132
  sendFrame,
95
133
  getBufferedAmount,
96
134
  createWebSocket = (url, protocols, options) => new WebSocket(url, protocols, options),
@@ -197,6 +235,13 @@ export const createTunnelHost = ({
197
235
  const stream = streams.get(streamId);
198
236
  if (!stream || stream.kind !== 'http') return;
199
237
 
238
+ const targetPort = resolveTargetPort(request.headers, getLocalPort(), getSshRoutingTable);
239
+ if (targetPort === null) {
240
+ dropStream(streamId);
241
+ await syntheticResponse(streamId, 503, 'ssh-host-unreachable');
242
+ return;
243
+ }
244
+
200
245
  const hasBody = method !== 'GET' && method !== 'HEAD';
201
246
  let requestBody;
202
247
  if (hasBody) {
@@ -210,7 +255,7 @@ export const createTunnelHost = ({
210
255
  stream.noBody = true;
211
256
  }
212
257
 
213
- const url = `http://127.0.0.1:${getLocalPort()}${request.path}${request.query ? `?${request.query}` : ''}`;
258
+ const url = `http://127.0.0.1:${targetPort}${request.path}${request.query ? `?${request.query}` : ''}`;
214
259
  let response;
215
260
  try {
216
261
  response = await fetch(url, {
@@ -323,7 +368,15 @@ export const createTunnelHost = ({
323
368
  return;
324
369
  }
325
370
 
326
- const url = `ws://127.0.0.1:${getLocalPort()}${open.path}${open.query ? `?${open.query}` : ''}`;
371
+ // Optional headers on WsOpen carry routing metadata (e.g. target-port).
372
+ // Same resolve rules as HTTP; miss refuses without falling back to default.
373
+ const targetPort = resolveTargetPort(open.headers, getLocalPort(), getSshRoutingTable);
374
+ if (targetPort === null) {
375
+ void sendAbort(streamId, 'ssh-host-unreachable');
376
+ return;
377
+ }
378
+
379
+ const url = `ws://127.0.0.1:${targetPort}${open.path}${open.query ? `?${open.query}` : ''}`;
327
380
  // Present the loopback origin we're actually dialing. The server derives this
328
381
  // as a trusted same-origin candidate from the Host header (127.0.0.1:<port>),
329
382
  // so the WS origin check passes reliably for every client platform. We do NOT
@@ -333,7 +386,7 @@ export const createTunnelHost = ({
333
386
  // authenticated by the tunneled oc_url_token, not by this origin.
334
387
  const dialHeaders = {
335
388
  'x-openchamber-relay-connection': connectionId,
336
- origin: `http://127.0.0.1:${getLocalPort()}`,
389
+ origin: `http://127.0.0.1:${targetPort}`,
337
390
  };
338
391
  let socket;
339
392
  try {
@@ -7,17 +7,18 @@
7
7
  // corrupted payload. This test must stay red until send serialization is fixed.
8
8
  // Production send path is intentionally not changed here.
9
9
 
10
- import { describe, expect, it } from 'bun:test';
10
+ import { afterEach, describe, expect, it } from 'bun:test';
11
11
 
12
12
  import {
13
13
  MAX_TUNNEL_PAYLOAD_BYTES,
14
14
  TunnelFrameType,
15
15
  createFragmentAssembler,
16
+ decodeJsonPayload,
16
17
  decodeTunnelFrame,
17
18
  encodeJsonPayload,
18
19
  encodeTunnelFrame,
19
20
  } from './tunnel-codec.js';
20
- import { createTunnelHost } from './tunnel-host.js';
21
+ import { createTunnelHost, resolveTargetPort } from './tunnel-host.js';
21
22
 
22
23
  const textEncoder = new TextEncoder();
23
24
  const textDecoder = new TextDecoder();
@@ -223,3 +224,250 @@ describe('createTunnelHost WS fragment ordering', () => {
223
224
  }
224
225
  });
225
226
  });
227
+
228
+ const collectHttpExchange = async (outbound, streamId) => {
229
+ await waitFor(() => outbound.some((frame) => {
230
+ const decoded = decodeTunnelFrame(frame);
231
+ return decoded.streamId === streamId
232
+ && (decoded.frameType === TunnelFrameType.StreamEnd
233
+ || decoded.frameType === TunnelFrameType.StreamAbort);
234
+ }));
235
+ let status = null;
236
+ /** @type {Uint8Array[]} */
237
+ const bodyChunks = [];
238
+ for (const raw of outbound) {
239
+ const frame = decodeTunnelFrame(raw);
240
+ if (frame.streamId !== streamId) continue;
241
+ if (frame.frameType === TunnelFrameType.HttpResponse) {
242
+ status = decodeJsonPayload(frame.payload, (v) => v && typeof v.status === 'number').status;
243
+ } else if (frame.frameType === TunnelFrameType.HttpBody) {
244
+ bodyChunks.push(frame.payload);
245
+ }
246
+ }
247
+ let body = null;
248
+ if (bodyChunks.length > 0) {
249
+ const total = bodyChunks.reduce((n, c) => n + c.length, 0);
250
+ const merged = new Uint8Array(total);
251
+ let off = 0;
252
+ for (const c of bodyChunks) {
253
+ merged.set(c, off);
254
+ off += c.length;
255
+ }
256
+ body = JSON.parse(textDecoder.decode(merged));
257
+ }
258
+ return { status, body };
259
+ };
260
+
261
+ describe('createTunnelHost SSH target-port routing', () => {
262
+ /** @type {typeof globalThis.fetch | undefined} */
263
+ let originalFetch;
264
+
265
+ afterEach(() => {
266
+ if (originalFetch) {
267
+ globalThis.fetch = originalFetch;
268
+ originalFetch = undefined;
269
+ }
270
+ });
271
+
272
+ const installFetchProbe = () => {
273
+ /** @type {{ url: string, headers: Record<string, string> }[]} */
274
+ const calls = [];
275
+ originalFetch = globalThis.fetch;
276
+ globalThis.fetch = async (url, init) => {
277
+ calls.push({
278
+ url: String(url),
279
+ headers: { ...(init?.headers || {}) },
280
+ });
281
+ return new Response(JSON.stringify({ ok: true }), {
282
+ status: 200,
283
+ headers: { 'content-type': 'application/json' },
284
+ });
285
+ };
286
+ return calls;
287
+ };
288
+
289
+ it('dials the routing-table port when x-openchamber-target-port is allowed', async () => {
290
+ const fetchCalls = installFetchProbe();
291
+ /** @type {Uint8Array[]} */
292
+ const outbound = [];
293
+ const host = createTunnelHost({
294
+ connectionId: 'test-conn',
295
+ getLocalPort: () => 3000,
296
+ getSshRoutingTable: () => [{ id: 'ssh-1', localPort: 41234 }],
297
+ sendFrame: (frame) => { outbound.push(frame); },
298
+ getBufferedAmount: () => 0,
299
+ });
300
+
301
+ const streamId = 11;
302
+ await host.handleFrame(encodeTunnelFrame(
303
+ TunnelFrameType.HttpRequest,
304
+ streamId,
305
+ encodeJsonPayload({
306
+ method: 'GET',
307
+ path: '/health',
308
+ query: '',
309
+ headers: { 'x-openchamber-target-port': '41234', accept: 'application/json' },
310
+ }),
311
+ ));
312
+
313
+ const exchange = await collectHttpExchange(outbound, streamId);
314
+ expect(exchange.status).toBe(200);
315
+ expect(fetchCalls).toHaveLength(1);
316
+ expect(fetchCalls[0].url).toBe('http://127.0.0.1:41234/health');
317
+ host.close();
318
+ });
319
+
320
+ it('returns 503 and does not fall back when target-port is unknown', async () => {
321
+ const fetchCalls = installFetchProbe();
322
+ /** @type {Uint8Array[]} */
323
+ const outbound = [];
324
+ const host = createTunnelHost({
325
+ connectionId: 'test-conn',
326
+ getLocalPort: () => 3000,
327
+ getSshRoutingTable: () => [{ id: 'ssh-1', localPort: 41234 }],
328
+ sendFrame: (frame) => { outbound.push(frame); },
329
+ getBufferedAmount: () => 0,
330
+ });
331
+
332
+ const streamId = 12;
333
+ await host.handleFrame(encodeTunnelFrame(
334
+ TunnelFrameType.HttpRequest,
335
+ streamId,
336
+ encodeJsonPayload({
337
+ method: 'GET',
338
+ path: '/health',
339
+ query: '',
340
+ headers: { 'x-openchamber-target-port': '9999' },
341
+ }),
342
+ ));
343
+
344
+ const exchange = await collectHttpExchange(outbound, streamId);
345
+ expect(exchange.status).toBe(503);
346
+ expect(exchange.body?.error).toBe('ssh-host-unreachable');
347
+ expect(fetchCalls).toHaveLength(0);
348
+ host.close();
349
+ });
350
+
351
+ it('strips x-openchamber-target-port before forwarding to loopback', async () => {
352
+ const fetchCalls = installFetchProbe();
353
+ /** @type {Uint8Array[]} */
354
+ const outbound = [];
355
+ const host = createTunnelHost({
356
+ connectionId: 'test-conn',
357
+ getLocalPort: () => 3000,
358
+ getSshRoutingTable: () => [{ id: 'ssh-1', localPort: 41234 }],
359
+ sendFrame: (frame) => { outbound.push(frame); },
360
+ getBufferedAmount: () => 0,
361
+ });
362
+
363
+ const streamId = 13;
364
+ await host.handleFrame(encodeTunnelFrame(
365
+ TunnelFrameType.HttpRequest,
366
+ streamId,
367
+ encodeJsonPayload({
368
+ method: 'GET',
369
+ path: '/api/version',
370
+ query: '',
371
+ headers: {
372
+ 'x-openchamber-target-port': '41234',
373
+ authorization: 'Bearer client-token',
374
+ },
375
+ }),
376
+ ));
377
+
378
+ await collectHttpExchange(outbound, streamId);
379
+ expect(fetchCalls).toHaveLength(1);
380
+ expect(fetchCalls[0].headers['x-openchamber-target-port']).toBeUndefined();
381
+ expect(fetchCalls[0].headers.authorization).toBe('Bearer client-token');
382
+ expect(fetchCalls[0].headers['x-openchamber-relay-connection']).toBe('test-conn');
383
+ host.close();
384
+ });
385
+
386
+ it('dials the default local port when target-port header is absent', async () => {
387
+ const fetchCalls = installFetchProbe();
388
+ /** @type {Uint8Array[]} */
389
+ const outbound = [];
390
+ const host = createTunnelHost({
391
+ connectionId: 'test-conn',
392
+ getLocalPort: () => 3000,
393
+ getSshRoutingTable: () => [{ id: 'ssh-1', localPort: 41234 }],
394
+ sendFrame: (frame) => { outbound.push(frame); },
395
+ getBufferedAmount: () => 0,
396
+ });
397
+
398
+ const streamId = 14;
399
+ await host.handleFrame(encodeTunnelFrame(
400
+ TunnelFrameType.HttpRequest,
401
+ streamId,
402
+ encodeJsonPayload({
403
+ method: 'GET',
404
+ path: '/health',
405
+ query: '',
406
+ headers: { accept: 'application/json' },
407
+ }),
408
+ ));
409
+
410
+ await collectHttpExchange(outbound, streamId);
411
+ expect(fetchCalls).toHaveLength(1);
412
+ expect(fetchCalls[0].url).toBe('http://127.0.0.1:3000/health');
413
+ host.close();
414
+ });
415
+
416
+ it('WS open dials routing-table port and aborts on miss without fallback', async () => {
417
+ FakeWebSocket.reset();
418
+ /** @type {Uint8Array[]} */
419
+ const outbound = [];
420
+ const host = createTunnelHost({
421
+ connectionId: 'test-conn',
422
+ getLocalPort: () => 3000,
423
+ getSshRoutingTable: () => [{ id: 'ssh-1', localPort: 41234 }],
424
+ sendFrame: (frame) => { outbound.push(frame); },
425
+ getBufferedAmount: () => 0,
426
+ createWebSocket: (url, protocols) => new FakeWebSocket(url, protocols),
427
+ });
428
+
429
+ await host.handleFrame(encodeTunnelFrame(
430
+ TunnelFrameType.WsOpen,
431
+ 21,
432
+ encodeJsonPayload({
433
+ path: '/api/event/ws',
434
+ query: '',
435
+ headers: { 'x-openchamber-target-port': '41234' },
436
+ }),
437
+ ));
438
+ expect(FakeWebSocket.instances).toHaveLength(1);
439
+ expect(FakeWebSocket.instances[0].url).toBe('ws://127.0.0.1:41234/api/event/ws');
440
+
441
+ await host.handleFrame(encodeTunnelFrame(
442
+ TunnelFrameType.WsOpen,
443
+ 22,
444
+ encodeJsonPayload({
445
+ path: '/api/event/ws',
446
+ query: '',
447
+ headers: { 'x-openchamber-target-port': '9999' },
448
+ }),
449
+ ));
450
+ // Miss must not open another socket (no fallback to 3000).
451
+ expect(FakeWebSocket.instances).toHaveLength(1);
452
+ await waitFor(() => outbound.some((frame) => {
453
+ const decoded = decodeTunnelFrame(frame);
454
+ return decoded.streamId === 22 && decoded.frameType === TunnelFrameType.StreamAbort;
455
+ }));
456
+ const abort = outbound
457
+ .map((frame) => decodeTunnelFrame(frame))
458
+ .find((frame) => frame.streamId === 22 && frame.frameType === TunnelFrameType.StreamAbort);
459
+ expect(decodeJsonPayload(abort.payload, () => true).reason).toBe('ssh-host-unreachable');
460
+ host.close();
461
+ });
462
+ });
463
+
464
+ describe('resolveTargetPort', () => {
465
+ it('returns default when header is absent and null on table miss', () => {
466
+ const table = () => [{ id: 'ssh-1', localPort: 41234 }];
467
+ expect(resolveTargetPort({}, 3000, table)).toBe(3000);
468
+ expect(resolveTargetPort({ 'x-openchamber-target-port': '41234' }, 3000, table)).toBe(41234);
469
+ expect(resolveTargetPort({ 'X-OpenChamber-Target-Port': '41234' }, 3000, table)).toBe(41234);
470
+ expect(resolveTargetPort({ 'x-openchamber-target-port': '9999' }, 3000, table)).toBeNull();
471
+ expect(resolveTargetPort({ 'x-openchamber-target-port': 'not-a-port' }, 3000, table)).toBeNull();
472
+ });
473
+ });
@@ -1 +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,w as a}from"./useUIStore-D6BTB4va.js";import{Ot as o,et as s,rt as c,yt as l}from"./persistence-C_KCiMcY.js";import{nt as u,rt as d}from"./vendor-base-ui-CnHfUmDN.js";import{n as f}from"./vendor-react-DLPElpoQ.js";import{r as p}from"./vendor-sonner-DVzioF3d.js";import{n as m}from"./vendor-reactuses-core-DDoDDyM9.js";import{n as h}from"./clipboard-CXWbHCAk.js";import{Aa as g,Ci as _,Ki as v,Na as y,Ur as b,_o as x,an as S,di as C,en as w,fn as T,mn as E,no as D,o as O,pn as k,qr as A,ro as j,wo as M}from"./useMobileNavigationStore-DRbvzR8W.js";import{a as N,n as P,s as F}from"./MobileShareBridge-CAc7AFue.js";import{_ as I,a as L,c as R,d as ee,f as te,g as ne,l as re,n as ie,o as ae,p as oe,r as se,t as z,v as B,x as V,y as ce}from"./MobileDetailNavigation-DLoODj_O.js";import{i as le,r as ue}from"./useAssistantUIStore-ESTV1mEY.js";import{t as de}from"./AgentAvatar-C6Hxu5_o.js";import{t as fe}from"./utils-DyUyiLje.js";import{n as pe}from"./Icon-BPJ_quEe.js";import{a as H,i as me,n as he,o as U,r as W,t as G}from"./dialog-BV1ENlLf.js";import{t as ge}from"./button-BmNJBT13.js";import{i as _e}from"./OverlayScrollbar-B-K9NJfL.js";import{I as ve,L as ye,M as be,N as K,q as xe,t as q}from"./ChatContainer-CCyV53_o.js";import{a as Se,i as Ce,n as we,r as Te,t as Ee}from"./context-menu-m2Ep287d.js";var De=f(),J=e(d(),1),Y=u(),Oe=e=>{let t=(0,De.c)(46),{assistant:n,sessionID:r,warning:i,surface:a,onRevertMessage:o,onEditMessage:s,pendingUserMessages:c,onPendingUserMessagesMaterialized:l}=e,u=n.effectiveWorkspacePath,d;t[0]!==n.sessionGeneration||t[1]!==r?(d={sessionID:r,sessionGeneration:n.sessionGeneration},t[0]=n.sessionGeneration,t[1]=r,t[2]=d):d=t[2];let f=B(n.id,d,a.active);f.data?.pages;let p;t[3]===f.data?.pages?p=t[4]:(p=f.data?.pages??[],t[3]=f.data?.pages,t[4]=p);let h;t[5]===p?h=t[6]:(h=C(p),t[5]=p,t[6]=h);let g=h,_;if(t[7]!==g){_=new Map;for(let e of g){let t=_.get(e.sessionID);_.set(e.sessionID,t===void 0||t===e.directory?e.directory:null)}t[7]=g,t[8]=_}else _=t[8];let v=_,y;t[9]===f?y=t[10]:(y=async()=>{(f.hasNextPage||f.isFetchNextPageError)&&await f.fetchNextPage()},t[9]=f,t[10]=y);let b=m(y),x=n.mode===`continuous`,S=be(),w;t[11]!==u||t[12]!==v||t[13]!==S||t[14]!==r?(w=(e,t)=>{let n=e===r?u:v.get(e);if(!n||n!==t){k(e,`missing-directory`);return}E(e,t,{phoneShell:!!(S&&!M()),switchToChat:!0})},t[11]=u,t[12]=v,t[13]=S,t[14]=r,t[15]=w):w=t[15];let T=m(w),D;t[16]===x?D=t[17]:(D={...K,forkSession:!1,navigateNestedSession:!1,mutateSession:x},t[16]=x,t[17]=D);let O;t[18]===s?O=t[19]:(O=s?{onEditMessage:s}:{},t[18]=s,t[19]=O);let A;t[20]!==u||t[21]!==o||t[22]!==T||t[23]!==r||t[24]!==a.active||t[25]!==a.surfaceID||t[26]!==D||t[27]!==O?(A={kind:`embedded`,surfaceId:a.surfaceID,sessionId:r,directory:u,active:a.active,capabilities:D,onRevertMessage:o,...O,openSourceSession:T},t[20]=u,t[21]=o,t[22]=T,t[23]=r,t[24]=a.active,t[25]=a.surfaceID,t[26]=D,t[27]=O,t[28]=A):A=t[28];let j=A,N=f.isError||f.isSuccess&&!f.hasNextPage,P=f.isLoading||f.isFetchingNextPage,F;t[29]!==b||t[30]!==N||t[31]!==g||t[32]!==P?(F={entries:g,complete:N,loading:P,fetchPrevious:b},t[29]=b,t[30]=N,t[31]=g,t[32]=P,t[33]=F):F=t[33];let I;t[34]!==u||t[35]!==l||t[36]!==o||t[37]!==c||t[38]!==r||t[39]!==j||t[40]!==a||t[41]!==F||t[42]!==i?(I={sessionId:r,directory:u,composerSurface:a,sessionSurface:j,warning:i,pendingUserMessages:c,onPendingUserMessagesMaterialized:l,assistantHistory:F,onRevertMessage:o},t[34]=u,t[35]=l,t[36]=o,t[37]=c,t[38]=r,t[39]=j,t[40]=a,t[41]=F,t[42]=i,t[43]=I):I=t[43];let L=I,R;return t[44]===L?R=t[45]:(R=(0,Y.jsx)(q,{autoOpenDraft:!1,host:L}),t[44]=L,t[45]=R),R},ke=class extends Error{constructor(){super(`assistant_selection_stale`)}},Ae=(e,t)=>e.assistantID===t.assistantID&&e.transportIdentity===t.transportIdentity&&e.runtimeGeneration===t.runtimeGeneration,je=class{onBusyChange;onError;flight=null;authoritativeIdentity=null;constructor(e,t){this.onBusyChange=e,this.onError=t}activate(e){this.authoritativeIdentity&&!Ae(this.authoritativeIdentity,e)&&this.flight&&this.retire(this.flight),this.authoritativeIdentity=e}enqueue(e,t,n){if(!this.isAuthoritative(e))return Promise.reject(new ke);let r=this.flight;r||(r={identity:e,controller:new AbortController,running:!1,desired:null,waiters:[],activeWaiters:[],idleWaiters:[],retired:!1},this.flight=r),r.desired=t;let i=new Promise((e,t)=>{r.waiters.push({resolve:e,reject:t})});return r.running||(r.running=!0,this.onBusyChange(!0),this.run(r,n)),i}async flush(e){if(!this.isAuthoritative(e))throw new ke;let t=this.flight;t?.running&&await new Promise((e,n)=>t.idleWaiters.push({resolve:e,reject:n}))}assertAuthoritative(e){if(!this.isAuthoritative(e))throw new ke}dispose(){this.flight&&this.retire(this.flight),this.authoritativeIdentity=null}async run(e,t){for(;!e.retired&&e.desired;){let n=e.desired;e.desired=null;let r=e.waiters.splice(0);e.activeWaiters=r;try{if(await t({identity:e.identity,selection:n,signal:e.controller.signal}),!e.retired)for(let e of r)e.resolve()}catch(t){if(!e.retired){this.onError(t);for(let e of r)e.reject(t)}}e.activeWaiters=[]}e.running=!1;for(let t of e.idleWaiters.splice(0))t.resolve();this.flight===e&&this.onBusyChange(!1)}retire(e){e.retired=!0,e.controller.abort();let t=new ke;for(let n of e.activeWaiters.splice(0))n.reject(t);for(let n of e.waiters.splice(0))n.reject(t);for(let n of e.idleWaiters.splice(0))n.reject(t);this.flight===e&&(this.flight=null,this.onBusyChange(!1))}isAuthoritative(e){return this.authoritativeIdentity!==null&&Ae(this.authoritativeIdentity,e)}},X=(e,t)=>{let n=t.providerID??e.providerID,r=t.modelID??e.modelID,i=n!==e.providerID||r!==e.modelID;return{enabled:e.enabled,name:e.name,defaultPrompt:e.defaultPrompt,workspacePath:e.workspacePath,providerID:n,modelID:r,agent:t.agent??null,variant:i?null:t.variant??null,mode:e.mode}},Me=async(e,t,n)=>{n.assertAuthoritative();let r=n.readSnapshot()?.assistants.find(t=>t.id===e.assistantID);if(!r)throw Error(`assistant_unavailable`);try{n.assertAuthoritative(),await n.updateAssistant(r,X(r,t)),n.assertAuthoritative()}catch(i){if(n.assertAuthoritative(),n.signal.aborted)throw new ke;if(!(i instanceof V)||i.code!==`revision_conflict`)throw i;if(n.assertAuthoritative(),r=(await n.ensureSnapshot(n.signal)).assistants.find(t=>t.id===e.assistantID),n.assertAuthoritative(),!r)throw new V(`revision_conflict`,409);n.assertAuthoritative(),await n.updateAssistant(r,X(r,t)),n.assertAuthoritative()}};async function Z(e){await L(e);let t=le.getState().defaultShareAssistant;if(t?.assistantID!==e.id)return;let n=await R();n.serverInstanceID&&t.serverInstanceID===n.serverInstanceID&&le.getState().setDefaultShareAssistant(null)}function Ne({assistant:e,open:t,onOpenChange:n,onDeleted:r}){let{t:i}=h(),[a,o]=J.useState(!1),s=e?N(e.name).displayName||e.name:``,c=m(e=>{a&&!e||n(e)}),l=m(()=>{a||n(!1)}),u=m(async()=>{if(!(!e||a)){o(!0);try{await Z(e),r?.(e.id),n(!1),p.success(i(`assistants.settings.toast.deleted`))}catch{p.error(i(`assistants.settings.toast.deleteFailed`))}finally{o(!1)}}});return(0,Y.jsx)(G,{open:t&&e!==null,onOpenChange:c,children:(0,Y.jsxs)(he,{className:`max-w-md`,children:[(0,Y.jsxs)(H,{children:[(0,Y.jsx)(U,{children:i(`assistants.settings.delete`)}),(0,Y.jsx)(W,{children:i(`assistants.settings.deleteConfirm`,{name:s})})]}),(0,Y.jsxs)(me,{children:[(0,Y.jsx)(ge,{size:`sm`,variant:`ghost`,onClick:l,disabled:a,children:i(`settings.common.actions.cancel`)}),(0,Y.jsx)(ge,{size:`sm`,variant:`destructive`,onClick:u,disabled:a,children:i(`settings.common.actions.delete`)})]})]})})}var Pe=[0,500,1500,4e3],Fe=(e,t,n)=>{let r=!1,i=e.map(e=>e.info.id!==t||e.info.sessionID===n?e:(r=!0,{...e,info:{...e.info,sessionID:n}}));return r?i:e},Ie=e=>{let t=e.refreshDelaysMs??Pe,n=e.sleep??(e=>new Promise(t=>setTimeout(t,e)));(async()=>{for(let r of t){if(r>0&&await n(r),!e.isCurrent())return;try{await e.refresh(e.binding);break}catch{}}})()},Le=e=>`${e.transport}\u0000${e.assistantID}`,Re=e=>({transport:e.transport,assistantID:e.assistantID}),ze=(e,t)=>e.assistantID===t.assistantID&&e.sessionID===t.sessionID&&e.directory===t.directory&&e.sessionGeneration===t.sessionGeneration&&e.transport===t.transport&&e.runtimeGeneration===t.runtimeGeneration,Be=(e,t)=>e.assistantID===t.assistantID&&e.sessionID===t.sessionID&&e.directory===t.directory&&e.sessionGeneration===t.sessionGeneration&&e.messageID===t.messageID&&e.transport===t.transport&&e.runtimeGeneration===t.runtimeGeneration,Ve=()=>{let e=new Map,t=new Map,n=1,r=e=>Le(e),i=async(e,n)=>{let i=r(e),a=t.get(i)??Promise.resolve(),o,s=new Promise(e=>{o=e}),c=a.catch(()=>void 0).then(()=>s);t.set(i,c),await a.catch(()=>void 0);try{return await n()}finally{o(),t.get(i)===c&&t.delete(i)}},a=(t,i)=>{let a=r(Re(t)),o=n++;return e.set(a,{identity:t,rollback:i,token:o}),o},o=t=>e.get(r(t))?.identity,s=t=>e.delete(r(t)),c=async e=>i(e,()=>s(e)),l=async t=>{let n=r(t),i=e.get(n);if(!i)return{kind:`none`};let a=i.token,o=i.identity,s;try{s=await i.rollback()}catch{return{kind:`blocked`,status:`failed`}}let c=e.get(n);return!c||c.token!==a||!Be(c.identity,o)?{kind:`superseded`}:s.status===`rolled-back`||s.status===`conflict`?(e.delete(n),{kind:`cleared`,status:s.status}):{kind:`blocked`,status:s.status}},u=async(t,n)=>{let i=r(t),a=e.get(i);if(!a)return{kind:`none`};if(ze(a.identity,n))return{kind:`match`};let o=a.token,s=a.identity,c=await a.rollback(),l=e.get(i);return!l||l.token!==o||!Be(l.identity,s)?{kind:`superseded`}:c.status===`rolled-back`||c.status===`conflict`?(e.delete(i),{kind:`cleared`,status:c.status}):{kind:`rollback_failed`,retained:!0,status:c.status}};return{register:a,read:o,clear:s,clearExclusive:c,retireCurrent:l,rollbackAndClearIfBindingMismatch:async(e,t)=>i(e,()=>u(e,t)),rollbackAllBestEffort:async t=>{let n=[...e.entries()];await Promise.all(n.map(async([,n])=>{if(t?.excludeTransport!==void 0&&n.identity.transport===t.excludeTransport)return;let a=Re(n.identity);await i(a,async()=>{let t=r(a),i=e.get(t);if(!i||i.token!==n.token)return;let o=i.token,s=i.identity;try{let n=await i.rollback(),r=e.get(t);if(!r||r.token!==o||!Be(r.identity,s))return;(n.status===`rolled-back`||n.status===`conflict`)&&e.delete(t)}catch{}})}))},stageExclusive:async(e,t)=>i(e,async()=>{let n=await l(e);if(n.kind===`blocked`)return{kind:`blocked`,status:n.status};let r=await t();if(!r)return{kind:`skipped`};let i=a(r.identity,r.rollback);return r.protectOnStale?{kind:`stale_protected`,token:i,identity:r.identity}:{kind:`registered`,token:i,identity:r.identity}})}},He=(e,t)=>!e?.attachments?.length||!t?[]:e.attachments.flatMap(e=>{let n=t[e.attachmentRefID];return n?[n]:[]}),Ue=(e,t)=>{let n=t??{};return e.map(e=>({partID:e.partID,text:e.text,...e.synthetic===!0?{synthetic:!0}:{},attachments:e.attachments.flatMap(e=>n[e.attachmentRefID]?[n[e.attachmentRefID]]:[])}))},We=(e,t)=>{if(t.length===0)return[...e];let n=new Map;for(let t of e)n.set(t.partID,t);for(let e of t)n.set(e.partID,e);let r=new Set,i=[];for(let t of e){let e=n.get(t.partID);e&&(i.push(e),r.add(t.partID))}for(let e of t)r.has(e.partID)||(i.push(e),r.add(e.partID));return i},Ge=t({AssistantView:()=>Qe}),Ke={},qe=[],Je=[],Ye=(e,t,n)=>[...e===void 0?[]:[{type:`text`,text:e}],...(t??[]).flatMap(e=>[{type:`text`,text:e.text,...e.synthetic===!0?{synthetic:!0}:{}},...(e.attachments??[]).map(e=>({type:`file`,mime:e.mimeType,url:e.dataUrl}))]),...(n??[]).map(e=>({type:`file`,mime:e.mimeType,url:e.dataUrl}))],Xe=e=>{let t=(0,De.c)(11),{assistant:n,onBack:r}=e,{t:i}=h(),a;t[0]===n?a=t[1]:(a=n?N(n.name):null,t[0]=n,t[1]=a);let o=a,s=n&&o?o.displayName||n.name:``,c;t[2]!==s||t[3]!==i?(c=s||i(`assistants.title`),t[2]=s,t[3]=i,t[4]=c):c=t[4];let l;t[5]===i?l=t[6]:(l=i(`assistants.actions.backToChat`),t[5]=i,t[6]=l);let u;return t[7]!==r||t[8]!==c||t[9]!==l?(u=(0,Y.jsx)(z,{title:c,backAriaLabel:l,onBack:r}),t[7]=r,t[8]=c,t[9]=l,t[10]=u):u=t[10],u},Ze=e=>{let t=(0,De.c)(45),{assistantID:n,displayName:r,avatarEmoji:i,modeLabel:a,selected:o,enabled:s,editLabel:c,deleteLabel:l,onSelect:u,onEdit:d,onDelete:f}=e,[p,h]=J.useState(!1),g;t[0]===u?g=t[1]:(g=()=>u(),t[0]=u,t[1]=g);let _=m(g),v;t[2]===d?v=t[3]:(v=()=>{h(!1),d()},t[2]=d,t[3]=v);let y=m(v),b;t[4]===f?b=t[5]:(b=()=>{h(!1),f()},t[4]=f,t[5]=b);let x=m(b),S;t[6]===Symbol.for(`react.memo_cache_sentinel`)?(S=e=>{e.preventDefault(),h(!0)},t[6]=S):S=t[6];let C=o?`border-border/50 bg-[var(--surface-elevated)]`:`border-transparent hover:bg-interactive-hover`,w=!s&&`opacity-65`,T;t[7]!==C||t[8]!==w?(T=fe(`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,transform,opacity] duration-150 ease-out active:scale-[0.995] focus-visible:ring-2 focus-visible:ring-[var(--interactive-focus-ring)] motion-reduce:transition-none`,C,w),t[7]=C,t[8]=w,t[9]=T):T=t[9];let E;t[10]!==_||t[11]!==o||t[12]!==T?(E=(0,Y.jsx)(`button`,{type:`button`,role:`option`,"aria-selected":o,onClick:_,onContextMenu:S,className:T}),t[10]=_,t[11]=o,t[12]=T,t[13]=E):E=t[13];let D;t[14]!==n||t[15]!==i||t[16]!==r?(D=(0,Y.jsx)(de,{name:n,emoji:i,size:24,label:r}),t[14]=n,t[15]=i,t[16]=r,t[17]=D):D=t[17];let O;t[18]===r?O=t[19]:(O=(0,Y.jsx)(`span`,{className:`block truncate typography-ui-label font-medium`,children:r}),t[18]=r,t[19]=O);let k;t[20]===a?k=t[21]:(k=(0,Y.jsx)(`span`,{className:`mt-0.5 block truncate typography-micro text-muted-foreground`,children:a}),t[20]=a,t[21]=k);let A;t[22]!==O||t[23]!==k?(A=(0,Y.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[O,k]}),t[22]=O,t[23]=k,t[24]=A):A=t[24];let j;t[25]!==A||t[26]!==E||t[27]!==D?(j=(0,Y.jsxs)(Se,{render:E,children:[D,A]}),t[25]=A,t[26]=E,t[27]=D,t[28]=j):j=t[28];let M;t[29]===Symbol.for(`react.memo_cache_sentinel`)?(M=(0,Y.jsx)(pe,{name:`edit`,className:`size-4`}),t[29]=M):M=t[29];let N;t[30]!==c||t[31]!==y?(N=(0,Y.jsxs)(Te,{onClick:y,children:[M,c]}),t[30]=c,t[31]=y,t[32]=N):N=t[32];let P;t[33]===Symbol.for(`react.memo_cache_sentinel`)?(P=(0,Y.jsx)(Ce,{}),t[33]=P):P=t[33];let F;t[34]===Symbol.for(`react.memo_cache_sentinel`)?(F=(0,Y.jsx)(pe,{name:`delete-bin`,className:`size-4`}),t[34]=F):F=t[34];let I;t[35]!==l||t[36]!==x?(I=(0,Y.jsxs)(Te,{onClick:x,className:`text-[var(--status-error)] focus:text-[var(--status-error)] data-[highlighted]:text-[var(--status-error)] [&_svg]:text-[var(--status-error)]`,children:[F,l]}),t[35]=l,t[36]=x,t[37]=I):I=t[37];let L;t[38]!==N||t[39]!==I?(L=(0,Y.jsxs)(we,{className:`min-w-[10rem]`,children:[N,P,I]}),t[38]=N,t[39]=I,t[40]=L):L=t[40];let R;return t[41]!==p||t[42]!==j||t[43]!==L?(R=(0,Y.jsxs)(Ee,{open:p,onOpenChange:h,children:[j,L]}),t[41]=p,t[42]=j,t[43]=L,t[44]=R):R=t[44],R},Qe=({activeOverride:e,onMobileBack:t})=>{let{t:u}=h(),{isMobile:d}=_e(),f=be(),C=J.useSyncExternalStore(n,r,r),E=a(),k=i(e=>e.activeMainTab===`assistant`),M=e??k,L=i(e=>e.isMobile),R=d||L,z=I(),B=ce(),V=B.data,H=le(e=>e.assistantByTransport[C]??null),me=le(e=>e.selectAssistant),he=le(e=>e.requestCreate),U=V?.assistants.find(e=>e.id===H)??null,W=U?.id??``,G=U?.sessionID??``,K=U?.effectiveWorkspacePath??``,q=J.useMemo(()=>o({transportIdentity:C},`assistant:${W}`),[W,C]),Se=l(q),Ce=x(e=>e.draftAttachmentViews[Se]??Ke),we=x(e=>e.drafts[Se]),Te=J.useMemo(()=>{let e=He(we,Ce);return e.length===0?qe:e},[Ce,we]),Ee=we?.attachments,De=we?.syntheticParts,Ae=J.useMemo(()=>!Ee||!De?``:`${Ee.map(e=>e.attachmentRefID).join(``)}\u0004${De.map(e=>`${e.partID}\u0002${e.attachments.map(e=>e.attachmentRefID).join(``)}`).join(``)}`,[Ee,De]);J.useEffect(()=>{W&&x.getState().hydrateDraftAttachments(q)},[W,q,Ae]);let X=j(K||null,{enabled:!!K&&M}),Z=D(K||null,{enabled:!!K&&M}),Pe=w(G,K||void 0),Le=S(G,K||void 0),ze=T(G),Be=xe(),[Ge,Qe]=J.useState(()=>new Map),$e=J.useRef(0),et=J.useRef(Ve()),tt=Ge.get(W)??Je,[,nt]=J.useState(!1),rt=J.useRef(()=>{});rt.current=()=>{p.error(u(`assistants.composer.selectionSaveFailed`))};let it=J.useRef(null);it.current||=new je(nt,e=>rt.current(e));let at=J.useMemo(()=>({assistantID:W,transportIdentity:C,runtimeGeneration:E}),[W,E,C]),Q=it.current,ot=J.useMemo(()=>({transport:C,assistantID:W}),[W,C]),st=m(async(e,t=C)=>{await et.current.clearExclusive({transport:t,assistantID:e})}),ct=m(e=>{et.current.rollbackAllBestEffort(e).catch(()=>void 0)});J.useEffect(()=>{!H&&V?.assistants[0]&&me(V.assistants[0].id)},[me,H,V?.assistants]),J.useEffect(()=>{B.isSuccess&&H&&!U&&me(V?.assistants[0]?.id??null)},[U,me,H,V?.assistants,B.isSuccess]),J.useEffect(()=>{M&&W&&!G&&ae(W)},[M,W,G]),J.useEffect(()=>{$e.current++,Qe(new Map),ct({excludeTransport:C})},[C,E,ct]),J.useEffect(()=>()=>{$e.current++,ct()},[ct]),J.useEffect(()=>{Q.activate(at)},[Q,at]),J.useEffect(()=>()=>{Q.dispose()},[Q]),J.useEffect(()=>{W&&et.current.rollbackAndClearIfBindingMismatch(ot,{assistantID:W,sessionID:G,directory:K,sessionGeneration:U?.sessionGeneration??-1,transport:C,runtimeGeneration:E}).catch(()=>void 0)},[U?.sessionGeneration,W,K,E,G,ot,C]);let lt=m(e=>at.assistantID?Q.enqueue(at,e,async({identity:e,selection:t,signal:n})=>{await Me(e,t,{readSnapshot:()=>te(void 0,e.transportIdentity),ensureSnapshot:e=>re(e),updateAssistant:ne,signal:n,assertAuthoritative:()=>{if(Q.assertAuthoritative(e),r()!==e.transportIdentity||a()!==e.runtimeGeneration)throw new ke}})}):Promise.reject(Error(`assistant_unavailable`))),ut=J.useMemo(()=>ye(Z.data),[Z.data]),dt=m(e=>{if(!U)return;let t=g(ut,U.agent??void 0,e);if(t){let e=y({providerID:U.providerID,modelID:U.modelID,agent:U.agent},t,ut,X.data??[]),n=e.providerID===U.providerID&&e.modelID===U.modelID;lt({...e,agent:e.agent??void 0,variant:n?U.variant??void 0:void 0})}}),$=m(async(e,t)=>{e.sessionID&&await Be.ensureSessionRenderable(e.sessionID,{directory:e.directory,...t?.force?{force:!0}:{}})}),ft=U?.sessionGeneration;J.useEffect(()=>{!M||!W||!G||!K||ft===void 0||$({sessionID:G,directory:K,sessionGeneration:ft},{force:!0})},[M,W,K,$,ft,G]);let pt=m((e,t)=>{if(t.length===0)return;let n=new Set(t);Qe(t=>{let r=t.get(e);if(!r?.some(e=>n.has(e.info.id)))return t;let i=new Map(t),a=r.filter(e=>!n.has(e.info.id));return a.length>0?i.set(e,a):i.delete(e),i})}),mt=m((e,t,n)=>{Qe(r=>{let i=r.get(e);if(!i)return r;let a=Fe(i,t,n);if(a===i)return r;let o=new Map(r);return o.set(e,a),o})}),ht=m(e=>{pt(W,e)}),gt=m(async e=>{if(!G||!K)throw Error(`assistant_unavailable`);v(G,K).some(t=>t?.id===e)&&(await b(G,e,{directory:K,draftKey:q,restorePrimaryInput:!1}),await st(W))}),_t=m(async(e,t)=>{if(!U||U.mode!==`continuous`)return;if(!G||!K)throw Error(`assistant_unavailable`);if(!v(G,K).some(t=>t?.id===e))return;if(t.info.id!==e||t.info.sessionID!==G||t.info.role!==`user`)throw Error(`The selected user message is unavailable`);let n={assistantID:U.id,sessionID:G,directory:K,sessionGeneration:U.sessionGeneration,messageID:e,transport:C,runtimeGeneration:E},i=Re(n),o=await et.current.stageExclusive(i,async()=>{let i=await A(G,e,t,{directory:K,draftKey:q}),o=r()===n.transport&&a()===n.runtimeGeneration,s=o?te(void 0,n.transport)?.assistants.find(e=>e.id===n.assistantID):void 0,c=!!(s&&s.mode===`continuous`&&s.sessionID===n.sessionID&&s.sessionGeneration===n.sessionGeneration&&(s.effectiveWorkspacePath??``)===n.directory);if(o&&c)return{identity:n,rollback:()=>i.rollback()};let l=await i.rollback();return l.status===`failed`||l.status===`skipped`?{identity:n,rollback:()=>i.rollback(),protectOnStale:!0}:null});if(o.kind===`blocked`)throw Error(`assistant_stage_message_edit_prior_blocked`);if(o.kind===`stale_protected`)throw Error(`assistant_stage_message_edit_rollback_failed`)}),vt=J.useMemo(()=>{let e=_(q,()=>x.getState());return{busy:!1,attachments:Te,addAttachment:t=>e.addLocal(t),removeAttachment:t=>e.removeByAttachmentID(t),clearAttachments:()=>e.clearRootAttachments(),restoreAttachments:t=>e.restoreRootAttachments(t),pendingInput:null,consumePendingInput:()=>null,pendingPreset:null,consumePendingPreset:()=>null,consumeSyntheticParts:()=>{let e=x.getState(),t=e.draftAttachmentViews[l(q)]??Ke,n=e.consumeDraftSyntheticParts(q,F);return n?Ue(n,t):null},restoreSyntheticParts:e=>{(async()=>{let t=x.getState(),n=e.map(e=>({partID:e.partID??s(),text:e.text,attachments:[],...e.synthetic===!0?{synthetic:!0}:{}})),r=We(t.getDraft(q)?.syntheticParts??[],n);t.setDraftSyntheticParts(q,r);for(let r=0;r<n.length;r++){let i=n[r].partID;for(let n of e[r]?.attachments??[])n.source===`server`&&n.dataUrl?t.addDraftDurableAttachment(q,{attachmentID:n.id,filename:n.filename,mimeType:n.mimeType,size:n.size,source:`server`,url:n.dataUrl,serverPath:n.serverPath,partID:i}):await t.addDraftLocalAttachment(q,n.file,{attachmentID:n.id,filename:n.filename,source:n.source===`vscode`?`vscode`:`local`,vscodePath:n.vscodePath,vscodeSource:n.vscodeSource===`selection`?`selection`:void 0,partID:i})}})()},inlineDrafts:[],removeInlineDraft:()=>{},restoreInlineDrafts:()=>{},history:ze,captureRuntime:()=>x.getState().captureDraftRuntime(),getDraft:e=>x.getState().getDraft(e),abortPrompt:{sessionID:null,clear:()=>{}}}},[Te,q,ze]),yt=J.useMemo(()=>{let e=(X.data??[]).find(e=>e.id===U?.providerID)?.models?.find(e=>e.id===U?.modelID);return ve(e)},[U?.modelID,U?.providerID,X.data]),bt=Pe.length>0,xt=J.useMemo(()=>{if(!U||!G||!K)return null;let e={sessionID:G,directory:K,sessionGeneration:U.sessionGeneration};return{kind:`secondary`,surfaceID:`assistant:${U.id}`,active:M,sessionID:G,directory:K,draftKey:q,transportIdentity:C,runtimeGeneration:E,deliveryTarget:{kind:`assistant`,assistantID:U.id},resources:vt,...U.mode===`stateless`?{queueSessionID:`assistant:${U.id}`}:{},selection:{value:{providerID:U.providerID,modelID:U.modelID,agent:U.agent??void 0,variant:U.variant??void 0},catalog:{providers:X.data??[],agents:ut,variants:yt,variantsReady:X.isSuccess,ready:X.isSuccess&&Z.isSuccess,loading:X.isPending||Z.isPending,error:X.isError||Z.isError},change:lt,flush:()=>Q.flush(at)},activity:{phase:Le?.type===`busy`?`busy`:Le?.type===`retry`?`retry`:`idle`,canAbort:Le?.type===`busy`||Le?.type===`retry`},commands:{sessionID:G,hasMessages:bt,hasNewDraft:!1},commandPolicy:e=>e.name!==`fork`&&e.name!==`thread`,backend:{send:async t=>{let n;for(let t of v(e.sessionID,e.directory)){let e=typeof t?.id==`string`?t.id:``;/^msg_[0-9a-f]{12}[0-9A-Za-z]{14}$/.test(e)&&(!n||e>n)&&(n=e)}let r=c(`msg`,n);t.options?.commitStagedMessageEdit&&et.current.clear({transport:C,assistantID:U.id});let i=O({messageID:r,sessionID:e.sessionID,providerID:t.providerID??U.providerID,modelID:t.modelID??U.modelID,agent:t.agent??U.agent??void 0,text:t.text,attachments:t.attachments,additionalParts:t.parts});Qe(e=>{let t=new Map(e);return t.set(U.id,[...e.get(U.id)??[],i]),t});let a;try{a=await oe(U.id,e,r,Ye(t.text,t.parts,t.attachments))}catch(e){throw pt(U.id,[r]),e}if(z.data?.serverInstanceID){let e=N(U.name);P({serverInstanceID:z.data.serverInstanceID,assistantID:U.id,name:e.displayName||U.name,avatarSeed:U.id,...e.avatarEmoji?{avatarEmoji:e.avatarEmoji}:{}}).catch(()=>void 0)}a.binding.sessionID&&mt(U.id,r,a.binding.sessionID);let o=$e.current;Ie({binding:a.binding,refresh:t=>$(t,{force:t.sessionID!==e.sessionID||t.sessionGeneration!==e.sessionGeneration}),isCurrent:()=>$e.current===o})},sendQueued:async()=>{throw Error(`assistant-server-queue-required`)},create:async()=>{let e=await ee(U.id);await st(U.id),await $(e,{force:!0})},compact:async()=>{let t=await se(U.id,e);await st(U.id),await $(t.binding,{force:!0})},abort:async()=>{await ie(U.id,e)}},shortcuts:{cycle:dt,new:async()=>{let e=await ee(U.id);await st(U.id),await $(e,{force:!0})},abort:async()=>{await ie(U.id,e)},submit:()=>{}}}},[M,U,z.data?.serverInstanceID,lt,st,dt,K,q,bt,X.data,X.isError,X.isPending,X.isSuccess,Z.isError,Z.isPending,Z.isSuccess,$,mt,pt,vt,E,Q,at,G,Le,C,yt,ut]),St=m(()=>{if(he(),f){f.openSettings();return}let e=i.getState();e.setSettingsPage(`assistants`),e.setSettingsDialogOpen(!0)}),Ct=m(e=>{ue(e,f?{openMobileSettings:f.openSettings}:void 0)}),[wt,Tt]=J.useState(null),Et=m(e=>{Tt(e)}),Dt=m(e=>{e||Tt(null)}),Ot=m(()=>{i.getState().setActiveMainTab(`chat`)}),kt=m(()=>{if(t){t();return}Ot()}),At=(e,t,n,r)=>(0,Y.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col`,children:[R?(0,Y.jsx)(Xe,{assistant:U,onBack:kt}):null,(0,Y.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col items-center justify-center px-6 text-center`,children:[(0,Y.jsx)(pe,{name:e,className:`size-6 text-muted-foreground`}),(0,Y.jsx)(`h1`,{className:`mt-4 typography-ui-header font-semibold`,children:t}),n?(0,Y.jsx)(`p`,{className:`mt-2 max-w-md typography-ui text-muted-foreground`,children:n}):null,r?(0,Y.jsx)(`div`,{className:`mt-5`,children:r}):null]})]});if(z.isPending||B.isPending)return At(`ai-agent`,u(`assistants.state.unavailable`));if(z.isError||!z.data?.supported||!z.data.enabled||!V?.enabled)return At(`cloud-off`,u(`assistants.state.unavailable`));if(!V.assistants.length)return At(`ai-agent`,u(`assistants.onboarding.title`),u(`assistants.onboarding.description`),(0,Y.jsx)(ge,{onClick:St,children:u(`assistants.onboarding.action`)}));if(!U)return At(`ai-agent`,u(`assistants.state.unavailable`));if(!xt)return(0,Y.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col bg-background`,children:[R?(0,Y.jsx)(Xe,{assistant:U,onBack:kt}):null,(0,Y.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col items-center justify-center gap-3 px-6`,"aria-busy":`true`,"aria-label":u(`assistants.state.unavailable`),children:[(0,Y.jsx)(`div`,{className:`size-10 animate-pulse rounded-xl bg-[var(--surface-muted)] motion-reduce:animate-none`}),(0,Y.jsx)(`div`,{className:`h-3.5 w-36 animate-pulse rounded-md bg-[var(--surface-muted)] motion-reduce:animate-none`})]})]});let jt=N(U.name),Mt=U.enabled?B.isError?u(`assistants.state.staleSnapshot`):null:u(`assistants.state.assistantDisabled`);return(0,Y.jsxs)(`div`,{className:`relative flex h-full min-h-0 overflow-hidden bg-background`,"data-presentation":`workspace`,children:[R?null:(0,Y.jsxs)(`section`,{className:`flex h-full min-h-0 w-[clamp(16rem,22vw,20rem)] shrink-0 flex-col overflow-hidden`,children:[(0,Y.jsx)(`header`,{className:`shrink-0 px-4 pb-3 pt-4 sm:px-5`,children:(0,Y.jsx)(`h1`,{className:`truncate typography-ui-label font-semibold text-foreground`,children:u(`assistants.title`)})}),(0,Y.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-3 pb-4 sm:px-4`,role:`listbox`,"aria-label":u(`assistants.listAria`),children:(0,Y.jsx)(`div`,{className:`flex flex-col gap-1 border-t border-border/40 pt-3`,children:V.assistants.map(e=>{let t=e.id===H,n=N(e.name);return(0,Y.jsx)(Ze,{assistantID:e.id,displayName:n.displayName,avatarEmoji:n.avatarEmoji??void 0,modeLabel:e.mode===`stateless`?u(`assistants.mode.stateless`):u(`assistants.mode.continuous`),selected:t,enabled:e.enabled,editLabel:u(`assistants.menu.edit`),deleteLabel:u(`assistants.settings.delete`),onSelect:()=>me(e.id),onEdit:()=>Ct(e.id),onDelete:()=>Et(e)},e.id)})})})]}),(0,Y.jsx)(Ne,{assistant:wt,open:wt!==null,onOpenChange:Dt}),(0,Y.jsxs)(`div`,{className:fe(`flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-background`,!R&&`border-l border-border/60`),children:[R?(0,Y.jsx)(Xe,{assistant:U,onBack:kt}):(0,Y.jsxs)(`header`,{className:`flex h-14 shrink-0 items-center gap-3 border-b border-border/40 px-4 sm:px-5`,children:[(0,Y.jsx)(de,{name:U.id,emoji:jt.avatarEmoji,size:24,label:jt.displayName||U.name}),(0,Y.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Y.jsx)(`div`,{className:`truncate typography-ui-label font-medium`,children:jt.displayName}),(0,Y.jsx)(`div`,{className:`mt-0.5 truncate typography-micro leading-none text-muted-foreground/70`,children:U.mode===`stateless`?u(`assistants.conversation.statelessHint`):u(`assistants.conversation.continuousHint`)})]})]}),(0,Y.jsx)(Oe,{onRevertMessage:gt,onEditMessage:U.mode===`continuous`?_t:void 0,assistant:U,sessionID:G,warning:Mt,surface:xt,pendingUserMessages:tt,onPendingUserMessagesMaterialized:ht})]})]})};export{Ge as n,Ne as r,Qe as t};
@@ -1 +0,0 @@
1
- import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{E as t,t as n}from"./useUIStore-D6BTB4va.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-CXWbHCAk.js";import{n as c}from"./Icon-BPJ_quEe.js";import{n as l}from"./useRuntimeAPIs-DSLXJybW.js";import{n as u}from"./useEffectiveDirectory-Bt1X5uGs.js";import{n as d,r as f,t as p}from"./fileQueries-i4oziMnC.js";import{t as m}from"./diagram-YN0GZ1Q0.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{E as t,w as n}from"./useUIStore-D6BTB4va.js";import{kn as r,u as i}from"./persistence-C_KCiMcY.js";import{nt as a,rt as o}from"./vendor-base-ui-CnHfUmDN.js";import{n as s}from"./vendor-react-DLPElpoQ.js";import{s as c,t as l}from"./vendor-tanstack-react-query-D7pys_c9.js";import{bo as u,xo as d}from"./useMobileNavigationStore-DRbvzR8W.js";import{t as f}from"./utils-DyUyiLje.js";import{n as p}from"./Icon-BPJ_quEe.js";import{t as m}from"./button-BmNJBT13.js";var h=e(o(),1),g=s(),_=class extends Error{code;status;resource;constructor(e,t,n){super(e),this.code=e,this.status=t,this.resource=n}},v=class extends _{operation;constructor(e,t,n){super(e,t,`share_operation`),this.operation=n}},y=(e,t=200)=>{throw new _(`invalid_${e}_response`,t,e)},b=(e,t)=>e&&typeof e==`object`&&!Array.isArray(e)?e:y(t),x=(e,t)=>typeof e==`string`?e:y(t),S=(e,t)=>e===null||typeof e==`string`?e:y(t),C=(e,t)=>typeof e==`number`&&Number.isFinite(e)?e:y(t),w=(e,t)=>e===null?null:C(e,t),T=(e,t)=>typeof e==`boolean`?e:y(t),E=(e,t,n)=>t.includes(e)?e:y(n),D=e=>{let t=b(e,`capability`);return{supported:T(t.supported,`capability`),enabled:T(t.enabled,`capability`),revision:C(t.revision,`capability`),serverInstanceID:S(t.serverInstanceID,`capability`)}},O=e=>{let t=b(e,`assistant`),n=Array.isArray(t.historySessionIDs)?t.historySessionIDs.map(e=>x(e,`assistant`)):[],r=t.historySessionCount===void 0?n.length:C(t.historySessionCount,`assistant`);return!Number.isSafeInteger(r)||r<n.length?y(`assistant`):{id:x(t.id,`assistant`),revision:C(t.revision,`assistant`),enabled:T(t.enabled,`assistant`),name:x(t.name,`assistant`),defaultPrompt:x(t.defaultPrompt,`assistant`),workspacePath:S(t.workspacePath,`assistant`),effectiveWorkspacePath:x(t.effectiveWorkspacePath,`assistant`),managedWorkspacePath:S(t.managedWorkspacePath??null,`assistant`),providerID:x(t.providerID,`assistant`),modelID:x(t.modelID,`assistant`),agent:S(t.agent,`assistant`),variant:S(t.variant??null,`assistant`),mode:E(t.mode,[`continuous`,`stateless`],`assistant`),sessionID:S(t.sessionID,`assistant`),sessionGeneration:C(t.sessionGeneration,`assistant`),historySessionIDs:n,historySessionCount:r,createdAt:w(t.createdAt,`assistant`),updatedAt:C(t.updatedAt,`assistant`),tombstoneAt:w(t.tombstoneAt,`assistant`)}},k=e=>{let t=b(e,`snapshot`);return{revision:C(t.revision,`snapshot`),enabled:T(t.enabled,`snapshot`),assistants:Array.isArray(t.assistants)?t.assistants.map(O):y(`snapshot`)}},A=e=>{let t=b(e,`binding`);return{sessionID:S(t.sessionID,`binding`),directory:x(t.directory,`binding`),sessionGeneration:C(t.sessionGeneration,`binding`)}},j=e=>{let t=b(e,`compact`);return t.summarized===!0?{binding:A(t.binding),summarized:!0}:y(`compact`)},M=e=>{let t=b(e,`message_admission`);return t.admitted===!0?{binding:A(t.binding),messageID:x(t.messageID,`message_admission`),admitted:!0}:y(`message_admission`)},N=e=>{let t=b(e,`share_operation`);return{operationID:x(t.operationID,`share_operation`),assistantID:x(t.assistantID,`share_operation`),sessionID:S(t.sessionID,`share_operation`),messageID:S(t.messageID,`share_operation`),state:E(t.state,[`submitting`,`running`,`completed`,`failed`,`unresolved`],`share_operation`),phase:x(t.phase,`share_operation`),attempt:C(t.attempt,`share_operation`),leaseExpiresAt:w(t.leaseExpiresAt,`share_operation`),errorCode:S(t.errorCode,`share_operation`)}},P=e=>{let t=b(e,`assistant_history`),n=S(t.nextCursor,`assistant_history`),r=T(t.complete,`assistant_history`);return!r&&!n||r&&n||!Array.isArray(t.entries)?y(`assistant_history`):{entries:t.entries.map(e=>{let t=b(e,`assistant_history_entry`),n=x(t.sessionID,`assistant_history_entry`),r=b(t.info,`assistant_history_info`),i=b(r.time,`assistant_history_info`);if(x(r.id,`assistant_history_info`),x(r.sessionID,`assistant_history_info`)!==n||(E(r.role,[`user`,`assistant`,`system`,`tool`],`assistant_history_info`),C(i.created,`assistant_history_info`),!Array.isArray(t.parts)))return y(`assistant_history_entry`);let a=t.parts.map(e=>{let t=b(e,`assistant_history_part`);return x(t.id,`assistant_history_part`),x(t.sessionID,`assistant_history_part`)!==n||x(t.messageID,`assistant_history_part`)!==r.id?y(`assistant_history_part`):(x(t.type,`assistant_history_part`),t)});return{sessionID:n,directory:S(t.directory,`assistant_history_entry`),info:r,parts:a}}),nextCursor:n,complete:r}},F=30,I={snapshot:(e=t())=>[e,`assistants`,`snapshot`],capability:(e=t())=>[e,`assistants`,`capability`],history:(e,r,i,a=t(),o=n())=>[a,o,`assistants`,`history`,e,r,i]},L=(e,t,n)=>{if(!e||!t)return;let r=t.queryKey;if(r[0]===n.transport&&r[1]===n.runtimeGeneration&&r[2]===`assistants`&&r[3]===`history`&&r[4]===n.assistantID)return e},R=async(e,t={})=>{let n=await r(e,t),i=await n.json().catch(()=>null);if(!n.ok)throw new _(i&&typeof i==`object`&&`error`in i&&typeof i.error==`string`?i.error:`request_failed`,n.status);return i},z=(e,t)=>({method:e,headers:{"Content-Type":`application/json`},body:t===void 0?void 0:JSON.stringify(t)}),B=(e,r)=>{if(t()!==e||n()!==r)throw new _(`runtime_stale`,409)},V=(e,t,r)=>{i.setQueryData(I.snapshot(r),n=>n&&{...n,assistants:n.assistants.map(n=>n.id!==e||n.sessionGeneration>t.sessionGeneration?n:{...n,sessionID:t.sessionID,sessionGeneration:t.sessionGeneration,effectiveWorkspacePath:t.directory})}),i.invalidateQueries({queryKey:I.snapshot(r)}),i.invalidateQueries({queryKey:[r,n(),`assistants`,`history`,e]})},H=(e,t)=>{i.setQueryData(I.snapshot(t),t=>t&&{...t,assistants:t.assistants.some(t=>t.id===e.id)?t.assistants.map(t=>t.id===e.id?e:t):[...t.assistants,e]}),i.invalidateQueries({queryKey:I.snapshot(t)})},U=(e=t())=>({queryKey:I.snapshot(e),queryFn:async({signal:e})=>k(await R(`/api/openchamber/assistants/snapshot`,{signal:e})),retry:2}),W=()=>{let e=(0,g.c)(5),n,r,a;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(a=t(),n=c,r=U(a),e[0]=n,e[1]=r,e[2]=a):(n=e[0],r=e[1],a=e[2]);let o=n(r),s,l;return e[3]===Symbol.for(`react.memo_cache_sentinel`)?(s=()=>d(e=>{if(t()!==a)return;if(e.type===`event-stream-ready`){i.invalidateQueries({queryKey:I.snapshot(a),exact:!0});return}if(e.type!==`assistants-changed`)return;let n=i.getQueryData(I.snapshot(a));(!n||e.revision>n.revision)&&i.invalidateQueries({queryKey:I.snapshot(a),exact:!0})}),l=[a],e[3]=s,e[4]=l):(s=e[3],l=e[4]),h.useEffect(s,l),o},G=(e,r,i,a=t(),o=n())=>({queryKey:I.history(e,r,i,a,o),queryFn:async({signal:t,pageParam:n})=>{B(a,o),await u(),B(a,o);let r=new URLSearchParams({limit:String(F)});n&&r.set(`before`,n);let i=P(await R(`/api/openchamber/assistants/${encodeURIComponent(e)}/messages?${r}`,{signal:t}));return B(a,o),i},initialPageParam:null,getNextPageParam:K,placeholderData:(t,n)=>L(t,n,{assistantID:e,transport:a,runtimeGeneration:o}),retry:2}),K=e=>e.complete?void 0:e.nextCursor??void 0,q=(e,t,n)=>{let r=(0,g.c)(7),i=n===void 0||n,a=t.sessionID??``,o;r[0]!==e||r[1]!==t.sessionGeneration||r[2]!==a?(o=G(e,a,t.sessionGeneration),r[0]=e,r[1]=t.sessionGeneration,r[2]=a,r[3]=o):o=r[3];let s=i&&!!(e&&t.sessionID),c;return r[4]!==o||r[5]!==s?(c={...o,enabled:s},r[4]=o,r[5]=s,r[6]=c):c=r[6],l(c)},J=async e=>k(await R(`/api/openchamber/assistants/snapshot`,{signal:e})),Y=(e=t())=>({queryKey:I.capability(e),queryFn:()=>Q(),retry:!1}),X=()=>{let e=(0,g.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=Y(),e[0]=t):t=e[0],c(t)},Z=(e=i,n=t())=>e.getQueryData(I.snapshot(n)),ee=(e=i,n=t())=>e.fetchQuery(U(n)),te=async(e=i)=>{let r=t(),a=n();await e.invalidateQueries({queryKey:I.snapshot(r),exact:!0}),B(r,a);let o=await e.fetchQuery(U(r));return B(r,a),o},ne=async e=>{let r=t(),i=n();await u(),B(r,i);let a=A(await R(`/api/openchamber/assistants/${encodeURIComponent(e)}/session/ensure`,z(`POST`)));return B(r,i),V(e,a,r),a},re=async e=>{let r=t(),i=n(),a=A(await R(`/api/openchamber/assistants/${encodeURIComponent(e)}/session/new`,z(`POST`)));return B(r,i),V(e,a,r),a},ie=async(e,r)=>{let i=t(),a=n(),o=j(await R(`/api/openchamber/assistants/${encodeURIComponent(e)}/session/compact`,z(`POST`,{sessionID:r.sessionID,sessionGeneration:r.sessionGeneration})));return B(i,a),V(e,o.binding,i),o},ae=async(e,r)=>{let i=t(),a=n();await R(`/api/openchamber/assistants/${encodeURIComponent(e)}/session/abort`,z(`POST`,{sessionID:r.sessionID,sessionGeneration:r.sessionGeneration})),B(i,a)},oe=async(e,r,i,a,o=`composer`)=>{let s=t(),c=n(),l=M(await R(`/api/openchamber/assistants/${encodeURIComponent(e)}/messages`,z(`POST`,{sessionID:r.sessionID,sessionGeneration:r.sessionGeneration,messageID:i,parts:a,source:o})));return B(s,c),V(e,l.binding,s),l},se=async(e,r,i,a,o)=>{let s=t(),c=n(),l=N(await R(`/api/openchamber/assistants/${encodeURIComponent(e)}/share`,z(`POST`,{operationID:r,payload:{messageID:i,parts:a,source:o}})));return B(s,c),l},ce=async(e,r=t(),i=n())=>{B(r,i);let a=N(await R(`/api/openchamber/assistants/share-operations/${encodeURIComponent(e)}`));return B(r,i),a},le=async(e,r=t(),i=n())=>{let a=e;for(let e=0;e<60&&(a.state===`running`||a.state===`submitting`);e+=1)B(r,i),await new Promise(e=>setTimeout(e,750)),a=await ce(a.operationID,r,i);if(B(r,i),a.state===`completed`)return a;throw a.state===`failed`?new v(a.errorCode??`share_failed`,400,a):new v(`share_unresolved`,408,a)},ue=async(e,n)=>{await R(`/api/openchamber/assistants/settings`,z(`PUT`,{enabled:e,expectedRevision:n}));let r=t();await Promise.all([i.invalidateQueries({queryKey:I.snapshot(r),exact:!0}),i.invalidateQueries({queryKey:I.capability(r),exact:!0})])},de=async e=>{let n=t(),r=O(await R(`/api/openchamber/assistants`,z(`POST`,e)));return H(r,n),r},fe=async(e,r)=>{let i=t(),a=n(),o=O(await R(`/api/openchamber/assistants/${encodeURIComponent(e.id)}`,z(`PATCH`,{...r,expectedRevision:e.revision})));return B(i,a),H(o,i),o},pe=async e=>{await R(`/api/openchamber/assistants/${encodeURIComponent(e.id)}`,z(`DELETE`,{expectedRevision:e.revision})),await i.invalidateQueries({queryKey:I.snapshot(t())})},Q=async()=>D(await R(`/api/openchamber/assistants/capability`)),$=a();function me(e){let t=(0,g.c)(32),{title:n,subtitle:r,backAriaLabel:i,onBack:a,backDisabled:o,actions:s,trailing:c,sticky:l,overlay:u,elevated:d}=e,h=o!==void 0&&o,_;t[0]===s?_=t[1]:(_=s===void 0?[]:s,t[0]=s,t[1]=_);let v=_,y=l!==void 0&&l,b=u!==void 0&&u,x=d!==void 0&&d,S=!!c||v.length>0,C=x||v.some(ge),w=y&&`oc-mobile-detail-navigation-sticky`,T=b&&`absolute inset-x-0 top-0`,E=C?`z-[60]`:b?`z-30`:void 0,D;t[2]!==w||t[3]!==T||t[4]!==E?(D=f(`oc-mobile-detail-navigation relative shrink-0 pt-[max(0.25rem,var(--oc-safe-area-top,0px))]`,w,T,E),t[2]=w,t[3]=T,t[4]=E,t[5]=D):D=t[5];let O=S&&v.length+ +!!c>1?`grid-cols-[2.75rem_minmax(0,1fr)_auto]`:`grid-cols-[2.75rem_minmax(0,1fr)_2.75rem]`,k;t[6]===O?k=t[7]:(k=f(`oc-mobile-detail-navigation-content grid items-center gap-1 px-4`,O),t[6]=O,t[7]=k);let A;t[8]!==i||t[9]!==h||t[10]!==a?(A=a?(0,$.jsx)(m,{type:`button`,variant:`mobileGlass`,size:`mobileIcon`,className:`justify-self-start`,onClick:a,disabled:h,"aria-label":i,children:(0,$.jsx)(p,{name:`arrow-left-s`,className:`size-5`})}):(0,$.jsx)(`span`,{"aria-hidden":`true`}),t[8]=i,t[9]=h,t[10]=a,t[11]=A):A=t[11];let j;t[12]===n?j=t[13]:(j=(0,$.jsx)(`div`,{className:`oc-mobile-detail-title w-full truncate typography-ui-label font-medium text-foreground`,children:n}),t[12]=n,t[13]=j);let M;t[14]===r?M=t[15]:(M=r?(0,$.jsx)(`div`,{className:`oc-mobile-detail-subtitle w-full truncate text-muted-foreground`,"aria-live":`polite`,children:r}):null,t[14]=r,t[15]=M);let N;t[16]!==j||t[17]!==M?(N=(0,$.jsxs)(`div`,{className:`flex w-full max-w-72 min-w-0 flex-col items-center justify-center justify-self-center px-1`,children:[j,M]}),t[16]=j,t[17]=M,t[18]=N):N=t[18];let P;t[19]===v?P=t[20]:(P=v.map(he),t[19]=v,t[20]=P);let F;t[21]!==P||t[22]!==c?(F=(0,$.jsxs)(`div`,{className:`flex min-w-0 items-center justify-end gap-2`,children:[c,P]}),t[21]=P,t[22]=c,t[23]=F):F=t[23];let I;t[24]!==k||t[25]!==A||t[26]!==N||t[27]!==F?(I=(0,$.jsxs)(`div`,{className:k,children:[A,N,F]}),t[24]=k,t[25]=A,t[26]=N,t[27]=F,t[28]=I):I=t[28];let L;return t[29]!==D||t[30]!==I?(L=(0,$.jsx)(`header`,{className:D,children:I}),t[29]=D,t[30]=I,t[31]=L):L=t[31],L}function he(e,t){return(0,$.jsx)(m,{type:`button`,variant:`mobileGlass`,size:`mobileIcon`,className:f(e.elevated&&`relative z-[60]`),onClick:e.onClick,disabled:e.disabled,"aria-label":e.ariaLabel,"aria-expanded":e.pressed,title:e.title,children:(0,$.jsx)(p,{name:e.icon,className:`size-5`})},`${e.icon}:${t}`)}function ge(e){return e.elevated}export{v as S,X as _,pe as a,le as b,Q as c,re as d,Z as f,fe as g,ue as h,de as i,J as l,se as m,ae as n,ne as o,oe as p,ie as r,ee as s,me as t,te as u,q as v,_ as x,W as y};