@opengeni/react 0.4.0 → 0.5.0

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/index.js CHANGED
@@ -29,7 +29,7 @@ function useOpenGeniClient(override = {}) {
29
29
  }
30
30
 
31
31
  // src/hooks/use-session.ts
32
- import { useCallback as useCallback2 } from "react";
32
+ import { useCallback as useCallback2, useState as useState2 } from "react";
33
33
 
34
34
  // src/hooks/internal.ts
35
35
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -203,23 +203,74 @@ function useDebouncedCallback(callback, delayMs = 150) {
203
203
  }
204
204
 
205
205
  // src/hooks/use-session.ts
206
+ function isTitleEvent(event) {
207
+ return event.type === "session.title_set";
208
+ }
206
209
  function useSession(sessionId, options = {}) {
207
210
  const { client, workspaceId } = useOpenGeni(options);
211
+ const enabled = (options.enabled ?? true) && Boolean(sessionId);
212
+ const [override, setOverride] = useState2(null);
213
+ const mutation = useMutationRunner();
208
214
  const load = useCallback2(async () => {
209
215
  if (!sessionId) {
210
216
  return null;
211
217
  }
212
- return await client.getSession(workspaceId, sessionId);
218
+ const fetched = await client.getSession(workspaceId, sessionId);
219
+ setOverride(null);
220
+ return fetched;
213
221
  }, [client, workspaceId, sessionId]);
214
222
  const state = usePolledValue(load, {
215
223
  pollIntervalMs: options.pollIntervalMs,
216
- enabled: (options.enabled ?? true) && Boolean(sessionId)
224
+ enabled
225
+ });
226
+ const base = state.data ?? null;
227
+ const session = base && override && override.id === base.id ? { ...base, title: override.title, titleSource: override.titleSource } : base;
228
+ const onTitleEvent = useCallback2((event) => {
229
+ const payload = event.payload ?? {};
230
+ const title = payload.title;
231
+ if (typeof title !== "string") {
232
+ return;
233
+ }
234
+ const source = payload.source === "user" || payload.source === "agent" ? payload.source : null;
235
+ setOverride((current) => {
236
+ const next = current ?? base;
237
+ if (!next) {
238
+ return current;
239
+ }
240
+ return { ...next, title, titleSource: source };
241
+ });
242
+ }, [base]);
243
+ useSessionEventTrigger(client, workspaceId, sessionId, isTitleEvent, onTitleEvent, {
244
+ enabled,
245
+ ...options.events !== void 0 ? { events: options.events } : {}
217
246
  });
218
- return { session: state.data ?? null, loading: state.loading, error: state.error, refresh: state.refresh };
247
+ const updateTitle = useCallback2(
248
+ async (title) => {
249
+ if (!sessionId) {
250
+ return null;
251
+ }
252
+ const result = await mutation.run(() => client.updateSession(workspaceId, sessionId, { title }));
253
+ if (result) {
254
+ setOverride(result);
255
+ }
256
+ return result;
257
+ },
258
+ [client, workspaceId, sessionId, mutation.run]
259
+ );
260
+ return {
261
+ session,
262
+ loading: state.loading,
263
+ error: state.error,
264
+ refresh: state.refresh,
265
+ updateTitle,
266
+ updating: mutation.mutating,
267
+ mutationError: mutation.mutationError,
268
+ clearMutationError: mutation.clearMutationError
269
+ };
219
270
  }
220
271
 
221
272
  // src/hooks/use-session-events.ts
222
- import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef2, useState as useState8 } from "react";
273
+ import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef2, useState as useState9 } from "react";
223
274
 
224
275
  // src/lib/format.ts
225
276
  function formatRelativeTime(iso, now = /* @__PURE__ */ new Date()) {
@@ -870,7 +921,7 @@ function v4aToGitFileDiff(op) {
870
921
  hunks.push(cur);
871
922
  } else if (cur || op.type === "create_file") {
872
923
  if (!cur) {
873
- cur = { oldStart: 0, oldLines: 0, newStart: 1, newLines: 0, header: "@@ +1 @@", lines: [] };
924
+ cur = { oldStart: 0, oldLines: 0, newStart: 1, newLines: 0, header: "", lines: [] };
874
925
  hunks.push(cur);
875
926
  oldNo = 0;
876
927
  newNo = 1;
@@ -956,7 +1007,7 @@ function unwrapMcpOutput(output) {
956
1007
 
957
1008
  // src/timeline/shared.tsx
958
1009
  import { CameraIcon, CameraOffIcon, ChevronRightIcon } from "lucide-react";
959
- import { useState as useState3 } from "react";
1010
+ import { useState as useState4 } from "react";
960
1011
  import { Collapsible } from "radix-ui";
961
1012
 
962
1013
  // src/lib/cn.ts
@@ -987,7 +1038,7 @@ function useForcedDefaultOpen() {
987
1038
  // src/timeline/screenshot-lightbox.tsx
988
1039
  import { XIcon } from "lucide-react";
989
1040
  import { AnimatePresence, motion } from "motion/react";
990
- import { createContext as createContext3, useCallback as useCallback3, useContext as useContext3, useMemo as useMemo2, useState as useState2 } from "react";
1041
+ import { createContext as createContext3, useCallback as useCallback3, useContext as useContext3, useMemo as useMemo2, useState as useState3 } from "react";
991
1042
  import { Dialog } from "radix-ui";
992
1043
  import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
993
1044
  var LightboxContext = createContext3(null);
@@ -1007,7 +1058,7 @@ function LightboxProvider({ children }) {
1007
1058
  return /* @__PURE__ */ jsx3(LightboxRoot, { children });
1008
1059
  }
1009
1060
  function LightboxRoot({ children }) {
1010
- const [state, setState] = useState2(null);
1061
+ const [state, setState] = useState3(null);
1011
1062
  const open = useCallback3((src, caption) => {
1012
1063
  setState(caption ? { src, caption } : { src });
1013
1064
  }, []);
@@ -1106,7 +1157,7 @@ function ActivityDisclosure({
1106
1157
  const iconTone = failed && iconToneProp === "muted" ? "failed" : iconToneProp;
1107
1158
  const chip = chipProp ?? (failed ? { tone: "bad", text: "failed" } : cancelled ? { tone: "interrupted", text: "interrupted" } : void 0);
1108
1159
  const forcedDefaultOpen = useForcedDefaultOpen();
1109
- const [open, setOpen] = useState3(forcedDefaultOpen ?? false);
1160
+ const [open, setOpen] = useState4(forcedDefaultOpen ?? false);
1110
1161
  const hasBody = expandable && children != null;
1111
1162
  const previewVisible = preview != null && !open;
1112
1163
  const rowClass = cn(
@@ -1179,7 +1230,7 @@ function TermBlock({
1179
1230
  live,
1180
1231
  tailLines = 12
1181
1232
  }) {
1182
- const [full, setFull] = useState3(false);
1233
+ const [full, setFull] = useState4(false);
1183
1234
  const empty = output.trim() === "";
1184
1235
  const lines = output.split("\n");
1185
1236
  const big = lines.length > tailLines + 4;
@@ -1248,7 +1299,7 @@ function MediaEmpty() {
1248
1299
  }
1249
1300
  function Thumbnail({ src, caption, alt = "screenshot" }) {
1250
1301
  const lightbox = useLightboxOptional();
1251
- const [failed, setFailed] = useState3(false);
1302
+ const [failed, setFailed] = useState4(false);
1252
1303
  if (failed) {
1253
1304
  return /* @__PURE__ */ jsx4(MediaEmpty, {});
1254
1305
  }
@@ -1289,7 +1340,7 @@ function Thumbnail({ src, caption, alt = "screenshot" }) {
1289
1340
  }
1290
1341
  function ScreenshotFigure({ src, caption, alt = "screenshot" }) {
1291
1342
  const lightbox = useLightboxOptional();
1292
- const [failed, setFailed] = useState3(false);
1343
+ const [failed, setFailed] = useState4(false);
1293
1344
  const surface = "block w-full overflow-hidden rounded-og-md border border-og-border bg-og-bg";
1294
1345
  const img = /* @__PURE__ */ jsx4("img", { src, alt, onError: () => setFailed(true), className: "max-h-80 w-full object-contain" });
1295
1346
  return /* @__PURE__ */ jsxs2("figure", { className: "m-0 min-w-0", children: [
@@ -1299,12 +1350,12 @@ function ScreenshotFigure({ src, caption, alt = "screenshot" }) {
1299
1350
  }
1300
1351
 
1301
1352
  // src/timeline/tool-diff.tsx
1302
- import { useState as useState6 } from "react";
1353
+ import { useState as useState7 } from "react";
1303
1354
 
1304
1355
  // src/lib/use-theme-type.ts
1305
- import { useEffect as useEffect2, useState as useState4 } from "react";
1356
+ import { useEffect as useEffect2, useState as useState5 } from "react";
1306
1357
  function useThemeType(forced) {
1307
- const [detected, setDetected] = useState4("dark");
1358
+ const [detected, setDetected] = useState5("dark");
1308
1359
  useEffect2(() => {
1309
1360
  if (forced || typeof document === "undefined") return;
1310
1361
  const read = () => {
@@ -1489,7 +1540,7 @@ import {
1489
1540
  lazy,
1490
1541
  Suspense,
1491
1542
  useEffect as useEffect3,
1492
- useState as useState5
1543
+ useState as useState6
1493
1544
  } from "react";
1494
1545
 
1495
1546
  // src/lib/git-patch.ts
@@ -1509,7 +1560,8 @@ function gitFileDiffToPatch(file) {
1509
1560
  lines.push(`+++ b/${newPath}`);
1510
1561
  }
1511
1562
  for (const hunk of file.hunks) {
1512
- const header = hunk.header && hunk.header.startsWith("@@") ? hunk.header : `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`;
1563
+ const headerIsValid = /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/.test(hunk.header ?? "");
1564
+ const header = headerIsValid ? hunk.header : `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`;
1513
1565
  lines.push(header);
1514
1566
  for (const line of hunk.lines) {
1515
1567
  if (line.type === "meta") continue;
@@ -1536,7 +1588,7 @@ function PierreDiff({
1536
1588
  fallback,
1537
1589
  className
1538
1590
  }) {
1539
- const [failed, setFailed] = useState5(false);
1591
+ const [failed, setFailed] = useState6(false);
1540
1592
  useEffect3(() => {
1541
1593
  let cancelled = false;
1542
1594
  void import("@pierre/diffs/react").catch(() => {
@@ -1588,7 +1640,7 @@ function DiffSkeleton() {
1588
1640
  // src/timeline/tool-diff.tsx
1589
1641
  import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1590
1642
  function ToolDiff({ files }) {
1591
- const [layout, setLayout] = useState6("unified");
1643
+ const [layout, setLayout] = useState7("unified");
1592
1644
  const themeType = useThemeType(void 0);
1593
1645
  const plain = /* @__PURE__ */ jsx7(DiffView, { diff: files, layout });
1594
1646
  return /* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
@@ -2445,12 +2497,12 @@ function WorkerRow({ item, onOpenSession }) {
2445
2497
 
2446
2498
  // src/timeline/turn-summary.tsx
2447
2499
  import { CheckIcon, ChevronRightIcon as ChevronRightIcon2, CircleSlashIcon, TriangleAlertIcon } from "lucide-react";
2448
- import { useState as useState7 } from "react";
2500
+ import { useState as useState8 } from "react";
2449
2501
  import { Collapsible as Collapsible2 } from "radix-ui";
2450
2502
  import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2451
2503
  function TurnSummary({ items, outcome, failureText, defaultOpen, children }) {
2452
2504
  const forcedDefaultOpen = useForcedDefaultOpen();
2453
- const [open, setOpen] = useState7(defaultOpen ?? forcedDefaultOpen ?? false);
2505
+ const [open, setOpen] = useState8(defaultOpen ?? forcedDefaultOpen ?? false);
2454
2506
  const facets = summarizeTurn(items);
2455
2507
  return /* @__PURE__ */ jsxs7(Collapsible2.Root, { open, onOpenChange: setOpen, className: "animate-og-enter", children: [
2456
2508
  /* @__PURE__ */ jsxs7(
@@ -2524,9 +2576,9 @@ function useSessionEvents(sessionId, options = {}) {
2524
2576
  const { client, workspaceId } = useOpenGeni(options);
2525
2577
  const enabled = options.enabled ?? true;
2526
2578
  const after = options.after ?? 0;
2527
- const [events, setEvents] = useState8([]);
2528
- const [connectionState, setConnectionState] = useState8("idle");
2529
- const [error, setError] = useState8(null);
2579
+ const [events, setEvents] = useState9([]);
2580
+ const [connectionState, setConnectionState] = useState9("idle");
2581
+ const [error, setError] = useState9(null);
2530
2582
  const lastSequenceRef = useRef2(after);
2531
2583
  const streamKeyRef = useRef2(null);
2532
2584
  useEffect4(() => {
@@ -2607,15 +2659,15 @@ function useSessionEvents(sessionId, options = {}) {
2607
2659
  }
2608
2660
 
2609
2661
  // src/hooks/use-composer.ts
2610
- import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef3, useState as useState9 } from "react";
2662
+ import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef3, useState as useState10 } from "react";
2611
2663
  function useComposer(sessionId, options = {}) {
2612
2664
  const { client, workspaceId } = useOpenGeni(options);
2613
2665
  const defaultMode = options.defaultMode ?? "queue";
2614
- const [value, setValue] = useState9("");
2615
- const [mode, setMode] = useState9(defaultMode);
2616
- const [sending, setSending] = useState9(false);
2617
- const [interrupting, setInterrupting] = useState9(false);
2618
- const [error, setError] = useState9(null);
2666
+ const [value, setValue] = useState10("");
2667
+ const [mode, setMode] = useState10(defaultMode);
2668
+ const [sending, setSending] = useState10(false);
2669
+ const [interrupting, setInterrupting] = useState10(false);
2670
+ const [error, setError] = useState10(null);
2619
2671
  const pendingClientEventId = useRef3(null);
2620
2672
  const onSent = options.onSent;
2621
2673
  const modeRef = useRef3(mode);
@@ -2726,12 +2778,12 @@ function generateClientEventId() {
2726
2778
  }
2727
2779
 
2728
2780
  // src/hooks/use-file-attachments.ts
2729
- import { useCallback as useCallback5, useState as useState10 } from "react";
2781
+ import { useCallback as useCallback5, useState as useState11 } from "react";
2730
2782
  var isImage = (file) => file.type.startsWith("image/");
2731
2783
  function useFileAttachments(options = {}) {
2732
2784
  const { client, workspaceId } = useOpenGeni(options);
2733
2785
  const pasteFilter = options.pasteFilter ?? isImage;
2734
- const [attachments, setAttachments] = useState10([]);
2786
+ const [attachments, setAttachments] = useState11([]);
2735
2787
  const addFiles = useCallback5((files) => {
2736
2788
  for (const file of files) {
2737
2789
  const id = crypto.randomUUID();
@@ -2796,7 +2848,7 @@ function useFileAttachments(options = {}) {
2796
2848
  }
2797
2849
 
2798
2850
  // src/hooks/use-turn-queue.ts
2799
- import { useCallback as useCallback6, useEffect as useEffect6, useRef as useRef4, useState as useState11 } from "react";
2851
+ import { useCallback as useCallback6, useEffect as useEffect6, useRef as useRef4, useState as useState12 } from "react";
2800
2852
  function isTurnQueueEvent(event) {
2801
2853
  return event.type.startsWith("turn.");
2802
2854
  }
@@ -2836,9 +2888,9 @@ function applyTurnRemoval(turns, turnId) {
2836
2888
  function useTurnQueue(sessionId, options = {}) {
2837
2889
  const { client, workspaceId } = useOpenGeni(options);
2838
2890
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
2839
- const [turns, setTurns] = useState11([]);
2840
- const [loading, setLoading] = useState11(enabled);
2841
- const [error, setError] = useState11(null);
2891
+ const [turns, setTurns] = useState12([]);
2892
+ const [loading, setLoading] = useState12(enabled);
2893
+ const [error, setError] = useState12(null);
2842
2894
  const mutation = useMutationRunner();
2843
2895
  const generation = useRef4(0);
2844
2896
  const targetKeyRef = useRef4(null);
@@ -2960,16 +3012,16 @@ function useTurnQueue(sessionId, options = {}) {
2960
3012
 
2961
3013
  // src/hooks/use-goal.ts
2962
3014
  import { OpenGeniApiError } from "@opengeni/sdk";
2963
- import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef5, useState as useState12 } from "react";
3015
+ import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef5, useState as useState13 } from "react";
2964
3016
  function isGoalEvent(event) {
2965
3017
  return event.type.startsWith("goal.");
2966
3018
  }
2967
3019
  function useGoal(sessionId, options = {}) {
2968
3020
  const { client, workspaceId } = useOpenGeni(options);
2969
3021
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
2970
- const [goal, setGoal] = useState12(null);
2971
- const [loading, setLoading] = useState12(enabled);
2972
- const [error, setError] = useState12(null);
3022
+ const [goal, setGoal] = useState13(null);
3023
+ const [loading, setLoading] = useState13(enabled);
3024
+ const [error, setError] = useState13(null);
2973
3025
  const mutation = useMutationRunner();
2974
3026
  const generation = useRef5(0);
2975
3027
  const targetKeyRef = useRef5(null);
@@ -3371,7 +3423,7 @@ import {
3371
3423
  OpenGeniApiError as OpenGeniApiError2,
3372
3424
  applyUrlRotation
3373
3425
  } from "@opengeni/sdk";
3374
- import { useCallback as useCallback16, useEffect as useEffect8, useRef as useRef6, useState as useState13 } from "react";
3426
+ import { useCallback as useCallback16, useEffect as useEffect8, useRef as useRef6, useState as useState14 } from "react";
3375
3427
  function desktopAttachable(cell) {
3376
3428
  if (cell.transport !== null) return true;
3377
3429
  return cell.reason === "lease_cold" || cell.reason === "not_provisioned" || cell.reason === null;
@@ -3399,13 +3451,13 @@ function useSessionCapabilities(sessionId, options = {}) {
3399
3451
  const attachFiles = options.attachFiles ?? false;
3400
3452
  const warmingPollMs = options.warmingPollMs ?? 1500;
3401
3453
  const warmingDeadlineMs = options.warmingDeadlineMs ?? 3e4;
3402
- const [capabilities, setCapabilities] = useState13(null);
3403
- const [state, setState] = useState13("idle");
3404
- const [error, setError] = useState13(null);
3405
- const [acknowledgmentRequired, setAcknowledgmentRequired] = useState13(null);
3406
- const [viewerCapReached, setViewerCapReached] = useState13(false);
3407
- const [viewerId, setViewerId] = useState13(null);
3408
- const [nonce, setNonce] = useState13(0);
3454
+ const [capabilities, setCapabilities] = useState14(null);
3455
+ const [state, setState] = useState14("idle");
3456
+ const [error, setError] = useState14(null);
3457
+ const [acknowledgmentRequired, setAcknowledgmentRequired] = useState14(null);
3458
+ const [viewerCapReached, setViewerCapReached] = useState14(false);
3459
+ const [viewerId, setViewerId] = useState14(null);
3460
+ const [nonce, setNonce] = useState14(0);
3409
3461
  const epochRef = useRef6(0);
3410
3462
  const viewerIdRef = useRef6(null);
3411
3463
  const renegotiate = useCallback16(() => {
@@ -3606,7 +3658,7 @@ import {
3606
3658
  desktopSocketUrl,
3607
3659
  nextDesktopState
3608
3660
  } from "@opengeni/sdk";
3609
- import { useEffect as useEffect9, useRef as useRef7, useState as useState14 } from "react";
3661
+ import { useEffect as useEffect9, useRef as useRef7, useState as useState15 } from "react";
3610
3662
  async function defaultRfbFactory() {
3611
3663
  const mod = await import("@novnc/novnc");
3612
3664
  const RFB = mod.default;
@@ -3614,9 +3666,9 @@ async function defaultRfbFactory() {
3614
3666
  }
3615
3667
  function useDesktopStream(options) {
3616
3668
  const { capability, containerRef, interactive, scaleViewport, rfbFactory } = options;
3617
- const [state, setState] = useState14("idle");
3618
- const [error, setError] = useState14(null);
3619
- const [nonce, setNonce] = useState14(0);
3669
+ const [state, setState] = useState15("idle");
3670
+ const [error, setError] = useState15(null);
3671
+ const [nonce, setNonce] = useState15(0);
3620
3672
  const stateRef = useRef7("idle");
3621
3673
  const setBoth = (next) => {
3622
3674
  stateRef.current = next;
@@ -3723,7 +3775,7 @@ import {
3723
3775
  ttydResizeFrame,
3724
3776
  TtydServerCommand
3725
3777
  } from "@opengeni/sdk";
3726
- import { useEffect as useEffect10, useMemo as useMemo4, useRef as useRef8, useState as useState15 } from "react";
3778
+ import { useEffect as useEffect10, useMemo as useMemo4, useRef as useRef8, useState as useState16 } from "react";
3727
3779
  function decodeFrame(data) {
3728
3780
  if (typeof data === "string") {
3729
3781
  return { command: data.charAt(0), payload: data.slice(1) };
@@ -3735,7 +3787,7 @@ function decodeFrame(data) {
3735
3787
  }
3736
3788
  function useTerminalStream(options) {
3737
3789
  const { capability, onOutput, onTitle, initialCols, initialRows } = options;
3738
- const [status, setStatus] = useState15("closed");
3790
+ const [status, setStatus] = useState16("closed");
3739
3791
  const wsRef = useRef8(null);
3740
3792
  const sizeRef = useRef8({
3741
3793
  cols: initialCols ?? 80,
@@ -3845,16 +3897,16 @@ function useTerminalStream(options) {
3845
3897
 
3846
3898
  // src/hooks/use-sandbox-terminal.ts
3847
3899
  import { OpenGeniApiError as OpenGeniApiError3 } from "@opengeni/sdk";
3848
- import { useCallback as useCallback17, useEffect as useEffect11, useMemo as useMemo5, useRef as useRef9, useState as useState16 } from "react";
3900
+ import { useCallback as useCallback17, useEffect as useEffect11, useMemo as useMemo5, useRef as useRef9, useState as useState17 } from "react";
3849
3901
  function useSandboxTerminal(sessionId, options) {
3850
3902
  const { client, workspaceId } = useOpenGeni(options);
3851
3903
  const includeAgentFirehose = options.includeAgentFirehose ?? true;
3852
3904
  const interactive = options.interactive ?? false;
3853
3905
  const ptyFilter = options.ptyId;
3854
3906
  const liveness = options.liveness;
3855
- const [openedPtyId, setOpenedPtyId] = useState16(null);
3856
- const [openError, setOpenError] = useState16(null);
3857
- const [reopenNonce, setReopenNonce] = useState16(0);
3907
+ const [openedPtyId, setOpenedPtyId] = useState17(null);
3908
+ const [openError, setOpenError] = useState17(null);
3909
+ const [reopenNonce, setReopenNonce] = useState17(0);
3858
3910
  const openInFlight = useRef9(false);
3859
3911
  const boxWarm = liveness === void 0 || liveness === "warm" || liveness === "draining";
3860
3912
  useEffect11(() => {
@@ -3961,7 +4013,7 @@ function useSandboxTerminal(sessionId, options) {
3961
4013
  }
3962
4014
 
3963
4015
  // src/hooks/use-sandbox-files.ts
3964
- import { useCallback as useCallback18, useEffect as useEffect12, useRef as useRef10, useState as useState17 } from "react";
4016
+ import { useCallback as useCallback18, useEffect as useEffect12, useRef as useRef10, useState as useState18 } from "react";
3965
4017
  function parentOf(path) {
3966
4018
  const i = path.lastIndexOf("/");
3967
4019
  return i <= 0 ? "" : path.slice(0, i);
@@ -4080,11 +4132,11 @@ function useSandboxFiles(sessionId, options = {}) {
4080
4132
  const { client, workspaceId } = useOpenGeni(options);
4081
4133
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
4082
4134
  const rootPath = options.rootPath ?? "";
4083
- const [tree, setTree] = useState17([]);
4135
+ const [tree, setTree] = useState18([]);
4084
4136
  const treeRef = useRef10([]);
4085
- const [loading, setLoading] = useState17(false);
4086
- const [expandingPaths, setExpandingPaths] = useState17(/* @__PURE__ */ new Set());
4087
- const [error, setError] = useState17(null);
4137
+ const [loading, setLoading] = useState18(false);
4138
+ const [expandingPaths, setExpandingPaths] = useState18(/* @__PURE__ */ new Set());
4139
+ const [error, setError] = useState18(null);
4088
4140
  const statusRef = useRef10(/* @__PURE__ */ new Map());
4089
4141
  const ownRevisionsRef = useRef10(/* @__PURE__ */ new Set());
4090
4142
  const onMutationError = options.onMutationError;
@@ -4376,19 +4428,19 @@ function useSandboxFiles(sessionId, options = {}) {
4376
4428
  }
4377
4429
 
4378
4430
  // src/hooks/use-sandbox-git.ts
4379
- import { useCallback as useCallback19, useEffect as useEffect13, useRef as useRef11, useState as useState18 } from "react";
4431
+ import { useCallback as useCallback19, useEffect as useEffect13, useRef as useRef11, useState as useState19 } from "react";
4380
4432
  function useSandboxGit(sessionId, options = {}) {
4381
4433
  const { client, workspaceId } = useOpenGeni(options);
4382
4434
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
4383
4435
  const repoPath = options.repoPath ?? "";
4384
4436
  const staged = options.staged ?? false;
4385
- const [diff, setDiff] = useState18([]);
4386
- const [branch, setBranch] = useState18(null);
4387
- const [isRepo, setIsRepo] = useState18(false);
4388
- const [ahead, setAhead] = useState18(0);
4389
- const [behind, setBehind] = useState18(0);
4390
- const [loading, setLoading] = useState18(false);
4391
- const [error, setError] = useState18(null);
4437
+ const [diff, setDiff] = useState19([]);
4438
+ const [branch, setBranch] = useState19(null);
4439
+ const [isRepo, setIsRepo] = useState19(false);
4440
+ const [ahead, setAhead] = useState19(0);
4441
+ const [behind, setBehind] = useState19(0);
4442
+ const [loading, setLoading] = useState19(false);
4443
+ const [error, setError] = useState19(null);
4392
4444
  const refresh = useCallback19(async () => {
4393
4445
  if (!sessionId) return;
4394
4446
  setLoading(true);
@@ -4670,11 +4722,11 @@ function clearErrorMessage(cause) {
4670
4722
  }
4671
4723
 
4672
4724
  // src/hooks/use-slash-commands.ts
4673
- import { useCallback as useCallback20, useMemo as useMemo6, useRef as useRef12, useState as useState19 } from "react";
4725
+ import { useCallback as useCallback20, useMemo as useMemo6, useRef as useRef12, useState as useState20 } from "react";
4674
4726
  function useSlashCommands(options) {
4675
4727
  const { commands, context, handlers, value, setValue } = options;
4676
- const [highlight, setHighlight] = useState19(0);
4677
- const [dismissedValue, setDismissedValue] = useState19(null);
4728
+ const [highlight, setHighlight] = useState20(0);
4729
+ const [dismissedValue, setDismissedValue] = useState20(null);
4678
4730
  const dismissed = dismissedValue !== null && dismissedValue === value;
4679
4731
  const navigatedRef = useRef12(false);
4680
4732
  const navTokenRef = useRef12(value);
@@ -4947,7 +4999,7 @@ function CommandPalette({ open, items, highlight, onHighlight, onRun, argHintTex
4947
4999
  // src/components/chat-composer.tsx
4948
5000
  import { ArrowUpIcon, FileIcon, ImageIcon as ImageIcon2, LoaderCircleIcon, PaperclipIcon, SquareIcon, XIcon as XIcon2 } from "lucide-react";
4949
5001
  import { AnimatePresence as AnimatePresence3, motion as motion3 } from "motion/react";
4950
- import { useCallback as useCallback21, useEffect as useEffect14, useId as useId2, useMemo as useMemo8, useRef as useRef13, useState as useState20 } from "react";
5002
+ import { useCallback as useCallback21, useEffect as useEffect14, useId as useId2, useMemo as useMemo8, useRef as useRef13, useState as useState21 } from "react";
4951
5003
 
4952
5004
  // src/components/model-picker.tsx
4953
5005
  import { ChevronDownIcon } from "lucide-react";
@@ -5030,7 +5082,7 @@ function ChatComposer({
5030
5082
  const blockedByUpload = attachments?.uploading === true;
5031
5083
  const hasReadyAttachment = (attachments?.readyResources.length ?? 0) > 0;
5032
5084
  const canSend = (composer.canSend || hasReadyAttachment) && !blockedByUpload && !composer.sending;
5033
- const [dragging, setDragging] = useState20(false);
5085
+ const [dragging, setDragging] = useState21(false);
5034
5086
  const dragCarriesFiles = (event) => event.dataTransfer != null && [...event.dataTransfer.types].includes("Files");
5035
5087
  const handleDragOver = useCallback21(
5036
5088
  (event) => {
@@ -5067,9 +5119,9 @@ function ChatComposer({
5067
5119
  },
5068
5120
  [attachments]
5069
5121
  );
5070
- const [notice, setNotice] = useState20(null);
5071
- const [helpOpen, setHelpOpen] = useState20(false);
5072
- const [confirmState, setConfirmState] = useState20(null);
5122
+ const [notice, setNotice] = useState21(null);
5123
+ const [helpOpen, setHelpOpen] = useState21(false);
5124
+ const [confirmState, setConfirmState] = useState21(null);
5073
5125
  const listboxId = useId2();
5074
5126
  const resize = useCallback21(() => {
5075
5127
  const textarea = textareaRef.current;
@@ -5492,7 +5544,7 @@ import {
5492
5544
  TriangleAlertIcon as TriangleAlertIcon2
5493
5545
  } from "lucide-react";
5494
5546
  import { AnimatePresence as AnimatePresence4, motion as motion4 } from "motion/react";
5495
- import { useEffect as useEffect15, useMemo as useMemo9, useRef as useRef14, useState as useState21 } from "react";
5547
+ import { useEffect as useEffect15, useMemo as useMemo9, useRef as useRef14, useState as useState22 } from "react";
5496
5548
 
5497
5549
  // src/components/markdown.tsx
5498
5550
  import { memo } from "react";
@@ -5657,7 +5709,7 @@ function MessageTimeline({
5657
5709
  const resolvedItems = useMemo9(() => items ?? buildTimeline(events ?? []), [items, events]);
5658
5710
  const groups = useMemo9(() => groupTimeline(resolvedItems), [resolvedItems]);
5659
5711
  const scrollRef = useRef14(null);
5660
- const [pinned, setPinned] = useState21(true);
5712
+ const [pinned, setPinned] = useState22(true);
5661
5713
  const lastItem = resolvedItems[resolvedItems.length - 1];
5662
5714
  const streaming = lastItem !== void 0 && (lastItem.kind === "agent-message" || lastItem.kind === "reasoning") && lastItem.streaming;
5663
5715
  const working = status === "running" && !streaming;
@@ -5797,6 +5849,9 @@ function NoticeRow({ item }) {
5797
5849
  // src/components/fleet-tile.tsx
5798
5850
  import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
5799
5851
  function sessionDisplayTitle(session) {
5852
+ if (typeof session.title === "string" && session.title.trim().length > 0) {
5853
+ return session.title;
5854
+ }
5800
5855
  for (const key of ["title", "name"]) {
5801
5856
  const value = session.metadata[key];
5802
5857
  if (typeof value === "string" && value.trim().length > 0) {
@@ -5851,7 +5906,7 @@ function FleetTile({ session, title, subtitle, onOpen, className }) {
5851
5906
  }
5852
5907
 
5853
5908
  // src/components/sandbox-terminal.tsx
5854
- import { useEffect as useEffect16, useRef as useRef15, useState as useState22 } from "react";
5909
+ import { useEffect as useEffect16, useRef as useRef15, useState as useState23 } from "react";
5855
5910
  import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
5856
5911
  function SandboxTerminal({
5857
5912
  result,
@@ -5870,7 +5925,7 @@ function SandboxTerminal({
5870
5925
  const termRef = useRef15(null);
5871
5926
  const fitRef = useRef15(null);
5872
5927
  const writtenRef = useRef15(/* @__PURE__ */ new Set());
5873
- const [ready, setReady] = useState22(false);
5928
+ const [ready, setReady] = useState23(false);
5874
5929
  const ptyWs = terminalCapability?.transport === "pty-ws" && Boolean(terminalCapability?.url);
5875
5930
  const ptyOutputQueueRef = useRef15([]);
5876
5931
  const ptyStream = useTerminalStream({
@@ -6131,7 +6186,7 @@ import {
6131
6186
  useEffect as useEffect17,
6132
6187
  useMemo as useMemo10,
6133
6188
  useRef as useRef16,
6134
- useState as useState23
6189
+ useState as useState24
6135
6190
  } from "react";
6136
6191
  import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
6137
6192
  var STATUS_TINT = {
@@ -6169,14 +6224,14 @@ function FileBrowser({
6169
6224
  confirmDelete,
6170
6225
  className
6171
6226
  }) {
6172
- const [expanded, setExpanded] = useState23(/* @__PURE__ */ new Set());
6173
- const [loadingPaths, setLoadingPaths] = useState23(/* @__PURE__ */ new Set());
6174
- const [active, setActive] = useState23(null);
6175
- const [draftCreate, setDraftCreate] = useState23(null);
6176
- const [draftRename, setDraftRename] = useState23(null);
6177
- const [dragOver, setDragOver] = useState23(null);
6178
- const [menu, setMenu] = useState23(null);
6179
- const [busy, setBusy] = useState23(false);
6227
+ const [expanded, setExpanded] = useState24(/* @__PURE__ */ new Set());
6228
+ const [loadingPaths, setLoadingPaths] = useState24(/* @__PURE__ */ new Set());
6229
+ const [active, setActive] = useState24(null);
6230
+ const [draftCreate, setDraftCreate] = useState24(null);
6231
+ const [draftRename, setDraftRename] = useState24(null);
6232
+ const [dragOver, setDragOver] = useState24(null);
6233
+ const [menu, setMenu] = useState24(null);
6234
+ const [busy, setBusy] = useState24(false);
6180
6235
  const containerRef = useRef16(null);
6181
6236
  const cursor = active ?? selectedPath ?? null;
6182
6237
  const expand = useCallback22(
@@ -6676,7 +6731,7 @@ function InlineInput({
6676
6731
  onCommit,
6677
6732
  onCancel
6678
6733
  }) {
6679
- const [value, setValue] = useState23(initialValue);
6734
+ const [value, setValue] = useState24(initialValue);
6680
6735
  const ref = useRef16(null);
6681
6736
  const doneRef = useRef16(false);
6682
6737
  useEffect17(() => {
@@ -6787,7 +6842,7 @@ import {
6787
6842
  lazy as lazy2,
6788
6843
  Suspense as Suspense2,
6789
6844
  useEffect as useEffect18,
6790
- useState as useState24
6845
+ useState as useState25
6791
6846
  } from "react";
6792
6847
  import { jsx as jsx20 } from "react/jsx-runtime";
6793
6848
  var LazyFile = lazy2(async () => {
@@ -6804,7 +6859,7 @@ function PierreFile({
6804
6859
  fallback,
6805
6860
  className
6806
6861
  }) {
6807
- const [failed, setFailed] = useState24(false);
6862
+ const [failed, setFailed] = useState25(false);
6808
6863
  useEffect18(() => {
6809
6864
  let cancelled = false;
6810
6865
  void import("@pierre/diffs/react").catch(() => {
@@ -6865,7 +6920,7 @@ import {
6865
6920
  useEffect as useEffect19,
6866
6921
  useMemo as useMemo11,
6867
6922
  useRef as useRef17,
6868
- useState as useState25
6923
+ useState as useState26
6869
6924
  } from "react";
6870
6925
  import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
6871
6926
  var LANGUAGE_LOADERS = {
@@ -6925,13 +6980,13 @@ function CodeEditor({
6925
6980
  fallback,
6926
6981
  className
6927
6982
  }) {
6928
- const [failed, setFailed] = useState25(false);
6929
- const [bundle, setBundle] = useState25(null);
6930
- const [value, setValue] = useState25(initialContents);
6931
- const [baseline, setBaseline] = useState25(initialContents);
6932
- const [saving, setSaving] = useState25(false);
6933
- const [saveError, setSaveError] = useState25(null);
6934
- const [savedTick, setSavedTick] = useState25(false);
6983
+ const [failed, setFailed] = useState26(false);
6984
+ const [bundle, setBundle] = useState26(null);
6985
+ const [value, setValue] = useState26(initialContents);
6986
+ const [baseline, setBaseline] = useState26(initialContents);
6987
+ const [saving, setSaving] = useState26(false);
6988
+ const [saveError, setSaveError] = useState26(null);
6989
+ const [savedTick, setSavedTick] = useState26(false);
6935
6990
  const dirty = value !== baseline;
6936
6991
  const lastSeed = useRef17({ path, contents: initialContents });
6937
6992
  useEffect19(() => {
@@ -7122,7 +7177,7 @@ function EditorSkeleton() {
7122
7177
 
7123
7178
  // src/components/sandbox-files.tsx
7124
7179
  import { FileIcon as FileIcon3 } from "lucide-react";
7125
- import { useCallback as useCallback24, useEffect as useEffect20, useMemo as useMemo12, useRef as useRef18, useState as useState26 } from "react";
7180
+ import { useCallback as useCallback24, useEffect as useEffect20, useMemo as useMemo12, useRef as useRef18, useState as useState27 } from "react";
7126
7181
  import { Fragment as Fragment6, jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
7127
7182
  var STATUS_TINT2 = {
7128
7183
  added: "text-[color:var(--og-color-status-idle,var(--color-success,#3fb950))]",
@@ -7156,12 +7211,12 @@ function SandboxFiles({
7156
7211
  themeType,
7157
7212
  className
7158
7213
  }) {
7159
- const [selected, setSelected] = useState26(null);
7160
- const [staged, setStaged] = useState26(false);
7161
- const [layout, setLayout] = useState26("unified");
7162
- const [editMode, setEditMode] = useState26(false);
7214
+ const [selected, setSelected] = useState27(null);
7215
+ const [staged, setStaged] = useState27(false);
7216
+ const [layout, setLayout] = useState27("unified");
7217
+ const [editMode, setEditMode] = useState27(false);
7163
7218
  const rootRef = useRef18(null);
7164
- const [wide, setWide] = useState26(false);
7219
+ const [wide, setWide] = useState27(false);
7165
7220
  useEffect20(() => {
7166
7221
  const el = rootRef.current;
7167
7222
  if (!el || typeof ResizeObserver === "undefined") return;
@@ -7423,7 +7478,7 @@ function Segmented({
7423
7478
  )) });
7424
7479
  }
7425
7480
  function useFileView(path, readFile) {
7426
- const [state, setState] = useState26({
7481
+ const [state, setState] = useState27({
7427
7482
  content: null,
7428
7483
  isBinary: false,
7429
7484
  truncated: false,
@@ -7493,7 +7548,7 @@ function Notice({ children, className }) {
7493
7548
 
7494
7549
  // src/components/desktop-viewer.tsx
7495
7550
  import { LoaderCircleIcon as LoaderCircleIcon2, MonitorIcon, MousePointerClickIcon, WifiOffIcon } from "lucide-react";
7496
- import { useEffect as useEffect21, useRef as useRef19, useState as useState27 } from "react";
7551
+ import { useEffect as useEffect21, useRef as useRef19, useState as useState28 } from "react";
7497
7552
  import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
7498
7553
  function isHardUnavailable(reason) {
7499
7554
  switch (reason) {
@@ -7525,8 +7580,8 @@ function DesktopViewer({
7525
7580
  className
7526
7581
  }) {
7527
7582
  const containerRef = useRef19(null);
7528
- const [consented, setConsented] = useState27(false);
7529
- const [takeControl, setTakeControl] = useState27(interactive ?? false);
7583
+ const [consented, setConsented] = useState28(false);
7584
+ const [takeControl, setTakeControl] = useState28(interactive ?? false);
7530
7585
  const externallyControlled = interactive !== void 0;
7531
7586
  const serverAllowsControl = capability?.mode !== "read-only";
7532
7587
  const wantControl = externallyControlled ? interactive : takeControl;
@@ -7589,8 +7644,8 @@ function DesktopViewer({
7589
7644
  document.addEventListener("visibilitychange", maybeReattach);
7590
7645
  return () => document.removeEventListener("visibilitychange", maybeReattach);
7591
7646
  }, [hasLiveUrl, connectCapability?.url]);
7592
- const [connectTimedOut, setConnectTimedOut] = useState27(false);
7593
- const [reconnectNonce, setReconnectNonce] = useState27(0);
7647
+ const [connectTimedOut, setConnectTimedOut] = useState28(false);
7648
+ const [reconnectNonce, setReconnectNonce] = useState28(0);
7594
7649
  useEffect21(() => {
7595
7650
  setConnectTimedOut(false);
7596
7651
  if (!hasLiveUrl || connected || stream.error || connectTimeoutMs <= 0) return;
@@ -7859,7 +7914,7 @@ function defaultNotice(title, body, onRetry) {
7859
7914
  }
7860
7915
 
7861
7916
  // src/components/workspace-dock.tsx
7862
- import { useCallback as useCallback25, useEffect as useEffect22, useState as useState28 } from "react";
7917
+ import { useCallback as useCallback25, useEffect as useEffect22, useState as useState29 } from "react";
7863
7918
  import {
7864
7919
  ChevronsLeftRightIcon,
7865
7920
  Maximize2Icon,
@@ -7886,9 +7941,9 @@ function WorkspaceDock({
7886
7941
  className
7887
7942
  }) {
7888
7943
  const dockPanelRef = usePanelRef();
7889
- const [collapsed, setCollapsed] = useState28(false);
7890
- const [maximized, setMaximized] = useState28(false);
7891
- const [internalTab, setInternalTab] = useState28(tabs[0]?.id ?? "");
7944
+ const [collapsed, setCollapsed] = useState29(false);
7945
+ const [maximized, setMaximized] = useState29(false);
7946
+ const [internalTab, setInternalTab] = useState29(tabs[0]?.id ?? "");
7892
7947
  const { defaultLayout, onLayoutChanged } = useDefaultLayout({
7893
7948
  panelIds: ["primary", "dock"],
7894
7949
  storage: typeof window !== "undefined" ? window.localStorage : void 0,
@@ -8120,6 +8175,7 @@ export {
8120
8175
  isApplyPatch,
8121
8176
  isExecSessionLostBanner,
8122
8177
  isGoalEvent,
8178
+ isTitleEvent,
8123
8179
  isTurnQueueEvent,
8124
8180
  languageForPath,
8125
8181
  looksBinary,