@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.
- package/dist/assets/{AgentAvatar-C6Hxu5_o.js → AgentAvatar-Coq-zdWI.js} +1 -1
- package/dist/assets/AssistantView-BXLxZXL5.js +1 -0
- package/dist/assets/{ChatContainer-CCyV53_o.js → ChatContainer-BJGlptbG.js} +22 -22
- package/dist/assets/DiagramView-Czx_DVw0.js +1 -0
- package/dist/assets/{FadeInOnReveal-CvNA_aGD.js → FadeInOnReveal-1KpYuooP.js} +1 -1
- package/dist/assets/{Icon-BPJ_quEe.js → Icon-Vc-aDche.js} +1 -1
- package/dist/assets/{MarkdownRendererImpl-DswfuLC-.js → MarkdownRendererImpl-B9Bv95Vd.js} +2 -2
- package/dist/assets/MobileDetailNavigation-RupyXzPu.js +1 -0
- package/dist/assets/MobileShareBridge-LVT1_q5C.js +4 -0
- package/dist/assets/MobileSurface-BVwSEqn4.js +2 -0
- package/dist/assets/MultiRunWindow-C7hbEpv9.js +1 -0
- package/dist/assets/OnboardingScreen-CrekVj31.js +1 -0
- package/dist/assets/OverlayScrollbar-vZXoSFA9.js +1 -0
- package/dist/assets/SettingsView-C1sYcHr1.js +25 -0
- package/dist/assets/SettingsWindow-MtcqpoiN.js +1 -0
- package/dist/assets/TerminalView-DXQZlHNA.js +8 -0
- package/dist/assets/{ToolOutputDialog-IVZYEtTZ.js → ToolOutputDialog-_Qzdo46T.js} +1 -1
- package/dist/assets/appThemeRegistry-Chv6_pxq.js +2 -0
- package/dist/assets/{button-BmNJBT13.js → button-rDAIP5kY.js} +1 -1
- package/dist/assets/{collapsible-68IqWBM1.js → collapsible-nLeZm1kw.js} +1 -1
- package/dist/assets/{context-menu-m2Ep287d.js → context-menu-DeJdA9F6.js} +1 -1
- package/dist/assets/desktopHosts-RE7NIkeq.js +1 -0
- package/dist/assets/desktopRelayRestore-BzyaU7-i.js +1 -0
- package/dist/assets/{diagram-YN0GZ1Q0.js → diagram-C7qK82ZL.js} +1 -1
- package/dist/assets/dialog-DlUK6mj-.js +1 -0
- package/dist/assets/es-DMX-Uo1i.js +15 -0
- package/dist/assets/{fileQueries-i4oziMnC.js → fileQueries-D8m3PQr0.js} +1 -1
- package/dist/assets/{fr-jWNPO91J.js → fr-CSFYzSd8.js} +2 -2
- package/dist/assets/{gitApi-CwRDHIOl.js → gitApi-DV6F5KKI.js} +1 -1
- package/dist/assets/gitApiHttp-BswZXfrf.js +2 -0
- package/dist/assets/i18n-BAqcSln8.js +1 -0
- package/dist/assets/{input-BLVhPvhN.js → input-CxulMZ9j.js} +1 -1
- package/dist/assets/insertionBoundaries-7-TvMfgn.js +1 -0
- package/dist/assets/{ja-Dcb0VtDE.js → ja-C3eCimlB.js} +2 -2
- package/dist/assets/{ko-CX71xNDX.js → ko-gnciIU9a.js} +2 -2
- package/dist/assets/main-CADHVH9Y.js +172 -0
- package/dist/assets/main-q1V-_wWx.js +2 -0
- package/dist/assets/{markdown-worker-Cq3Twp1A.js → markdown-worker-DmBYud1m.js} +1 -1
- package/dist/assets/{markdownTheme-CCnvI6L0.js → markdownTheme-CNJf24L-.js} +1 -1
- package/dist/assets/miniChat-VXuyA8mS.js +2 -0
- package/dist/assets/mobile-CaKZM4y3.js +2 -0
- package/dist/assets/multirun-QqMnIbXa.js +4 -0
- package/dist/assets/persistence-CFtygeYy.js +3 -0
- package/dist/assets/{pl-mR_3Sqr8.js → pl-CNk6BfNS.js} +1 -1
- package/dist/assets/projectMeta-WJhNyk90.js +3 -0
- package/dist/assets/pt-BR-D4G-0rLj.js +15 -0
- package/dist/assets/radio-C1riLnF0.js +1 -0
- package/dist/assets/renderElectronMiniChatApp-CnmMV1o9.js +2 -0
- package/dist/assets/renderMobileApp-Bhe41b2q.js +23 -0
- package/dist/assets/runtimeConfig-D4ETkuTj.js +1 -0
- package/dist/assets/runtimeEndpointReset-GtJt8YhT.js +145 -0
- package/dist/assets/sortable-tabs-strip-Fiq5nrkF.js +1 -0
- package/dist/assets/src-DrgsP0XF.css +2 -0
- package/dist/assets/{store-oojlrF4d.js → store-CS6l6pH7.js} +3 -3
- package/dist/assets/{terminalApi-BYTA045F.js → terminalApi-MQ285A_s.js} +1 -1
- package/dist/assets/{tooltip-BYGBBTc2.js → tooltip-CbxKYhKv.js} +1 -1
- package/dist/assets/ui-D5j3JxkE.js +475 -0
- package/dist/assets/uk-BUry1gqt.js +15 -0
- package/dist/assets/{url-CGHw6D-E.js → url-BSalv2ov.js} +1 -1
- package/dist/assets/useAppFontEffects-CaGwli-K.js +7 -0
- package/dist/assets/{useAssistantUIStore-ESTV1mEY.js → useAssistantUIStore-DGtjM827.js} +1 -1
- package/dist/assets/useDesktopWindowControlsLayout-DCZXksMp.js +1 -0
- package/dist/assets/useEffectiveDirectory-CssNtlqg.js +1 -0
- package/dist/assets/{useFontPreferences-Bu4ME5hk.js → useFontPreferences-C7FEHFns.js} +1 -1
- package/dist/assets/useSessionAutoCleanup-B_mSwN7n.js +1 -0
- package/dist/assets/useUIStore-WQV8IuFw.js +1 -0
- package/dist/assets/{useWorkerHighlightedLines-BGHFpbvZ.js → useWorkerHighlightedLines-C74ZKDtw.js} +1 -1
- package/dist/assets/{utils-DyUyiLje.js → utils-Cwlw2GM0.js} +1 -1
- package/dist/assets/zh-CN-CJJZzHVW.js +15 -0
- package/dist/assets/zh-TW-DxhIXjND.js +15 -0
- package/dist/index.html +9 -9
- package/dist/mini-chat.html +9 -9
- package/dist/mobile.html +9 -9
- package/package.json +1 -1
- package/server/index.d.ts +2 -0
- package/server/index.js +8 -0
- package/server/lib/client-auth/pairing.js +7 -1
- package/server/lib/client-auth/pairing.test.js +45 -0
- package/server/lib/desktop-hosts/routes.js +152 -0
- package/server/lib/desktop-hosts/routes.test.js +194 -0
- package/server/lib/opencode/bootstrap-runtime.js +5 -0
- package/server/lib/opencode/core-routes.js +3 -0
- package/server/lib/opencode/core-routes.test.js +15 -0
- package/server/lib/opencode/openchamber-routes.js +10 -0
- package/server/lib/package-manager.js +3 -1
- package/server/lib/relay/DOCUMENTATION.md +43 -0
- package/server/lib/relay/host-client.js +4 -1
- package/server/lib/relay/service.js +4 -0
- package/server/lib/relay/tunnel-host.js +57 -4
- package/server/lib/relay/tunnel-host.test.js +250 -2
- package/dist/assets/AssistantView-CCYDEoHv.js +0 -1
- package/dist/assets/DiagramView-ClKA8FV3.js +0 -1
- package/dist/assets/MobileDetailNavigation-DLoODj_O.js +0 -1
- package/dist/assets/MobileShareBridge-CAc7AFue.js +0 -4
- package/dist/assets/MobileSurface-76JUp8KU.js +0 -2
- package/dist/assets/MultiRunWindow-DkKQaIfX.js +0 -1
- package/dist/assets/OnboardingScreen-CdvaEBtt.js +0 -1
- package/dist/assets/OverlayScrollbar-B-K9NJfL.js +0 -1
- package/dist/assets/SettingsView-zu_QFVkf.js +0 -25
- package/dist/assets/SettingsWindow-D2q5mZqr.js +0 -1
- package/dist/assets/TerminalView-BSkAxeUb.js +0 -8
- package/dist/assets/appThemeRegistry-b9Hk2IDb.js +0 -2
- package/dist/assets/clipboard-CXWbHCAk.js +0 -1
- package/dist/assets/desktopHosts-BpjUSDz4.js +0 -1
- package/dist/assets/desktopRelayRestore-BzUEk1i7.js +0 -1
- package/dist/assets/dialog-BV1ENlLf.js +0 -1
- package/dist/assets/es-EXZUX1sB.js +0 -15
- package/dist/assets/gitApiHttp-C1DaEMKR.js +0 -2
- package/dist/assets/insertionBoundaries-hP0Vo_9C.js +0 -1
- package/dist/assets/main-BLqbjxPt.js +0 -2
- package/dist/assets/main-C7LjTesY.js +0 -172
- package/dist/assets/miniChat-Cr9SuyZk.js +0 -2
- package/dist/assets/mobile-Bp-qiKtn.js +0 -2
- package/dist/assets/multirun-_rwgORX-.js +0 -4
- package/dist/assets/persistence-C_KCiMcY.js +0 -3
- package/dist/assets/projectMeta-7TXLLSNB.js +0 -3
- package/dist/assets/pt-BR-D1RpVOvC.js +0 -15
- package/dist/assets/radio-CDEqI-yy.js +0 -1
- package/dist/assets/renderElectronMiniChatApp-ChsEWTbg.js +0 -2
- package/dist/assets/renderMobileApp-B1pX9NK3.js +0 -23
- package/dist/assets/runtimeConfig-JSaYFA5x.js +0 -1
- package/dist/assets/runtimeEndpointReset-BS3Sd-xn.js +0 -145
- package/dist/assets/sortable-tabs-strip-DUk_Td2z.js +0 -1
- package/dist/assets/src-8zziWB78.css +0 -2
- package/dist/assets/uk-UvnVqWv-.js +0 -15
- package/dist/assets/useAppFontEffects-VKLeTcCY.js +0 -7
- package/dist/assets/useDesktopWindowControlsLayout-DLuCJzOs.js +0 -1
- package/dist/assets/useEffectiveDirectory-Bt1X5uGs.js +0 -1
- package/dist/assets/useMobileNavigationStore-DRbvzR8W.js +0 -475
- package/dist/assets/useSessionAutoCleanup-C8clHymf.js +0 -1
- package/dist/assets/useUIStore-D6BTB4va.js +0 -1
- package/dist/assets/zh-CN-9v-RC966.js +0 -15
- 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
|
-
|
|
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:${
|
|
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
|
-
|
|
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:${
|
|
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};
|