@opengeni/react 3.3.2 → 3.5.1-canary.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.
Files changed (54) hide show
  1. package/dist/artifacts.js +12 -12
  2. package/dist/{browser-viewer-MKKFRRRV.js → browser-viewer-E5DDEFQ4.js} +3 -3
  3. package/dist/{chunk-2DZVYHVO.js → chunk-4JFUT22W.js} +39 -13
  4. package/dist/chunk-4JFUT22W.js.map +1 -0
  5. package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
  6. package/dist/{chunk-3AGBGTRT.js → chunk-IB27C53Y.js} +130 -16
  7. package/dist/chunk-IB27C53Y.js.map +1 -0
  8. package/dist/{chunk-JAQ5K7W6.js → chunk-TJFMMDQU.js} +56 -53
  9. package/dist/chunk-TJFMMDQU.js.map +1 -0
  10. package/dist/{chunk-XYRDDSXF.js → chunk-UNCXXIQR.js} +16 -12
  11. package/dist/{chunk-XYRDDSXF.js.map → chunk-UNCXXIQR.js.map} +1 -1
  12. package/dist/{chunk-ZPBTI3TE.js → chunk-W2RGYBCV.js} +4 -4
  13. package/dist/{chunk-WSK7G5LE.js → chunk-W3OUAH6M.js} +80 -80
  14. package/dist/chunk-W3OUAH6M.js.map +1 -0
  15. package/dist/{chunk-X4A66EWZ.js → chunk-WMGF4Z7X.js} +41 -43
  16. package/dist/chunk-WMGF4Z7X.js.map +1 -0
  17. package/dist/{chunk-YG4QEQF5.js → chunk-WY3MELVJ.js} +7769 -7753
  18. package/dist/chunk-WY3MELVJ.js.map +1 -0
  19. package/dist/composer.js +4 -4
  20. package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
  21. package/dist/hooks/use-file-attachments.d.ts +2 -0
  22. package/dist/hooks/use-session-events.d.ts +1 -1
  23. package/dist/index.js +85 -87
  24. package/dist/index.js.map +1 -1
  25. package/dist/interaction.js +3 -3
  26. package/dist/model-policy.d.ts +2 -1
  27. package/dist/model-policy.js +1 -1
  28. package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-XL2A6PBM.js} +2 -2
  29. package/dist/realtime.js +2 -2
  30. package/dist/session-ui.js +6 -6
  31. package/dist/session.js +4 -4
  32. package/dist/timeline/types.d.ts +2 -0
  33. package/package.json +2 -2
  34. package/src/components/composer.tsx +15 -5
  35. package/src/components/message-timeline.tsx +16 -2
  36. package/src/components/model-policy-picker.tsx +37 -10
  37. package/src/components/sandbox-workspace.tsx +7 -6
  38. package/src/hooks/use-file-attachments.ts +28 -2
  39. package/src/hooks/use-session-events.ts +168 -8
  40. package/src/model-policy.ts +48 -53
  41. package/src/timeline/projection.ts +6 -1
  42. package/src/timeline/types.ts +2 -0
  43. package/styles/compiled.css +5 -0
  44. package/dist/chunk-2DZVYHVO.js.map +0 -1
  45. package/dist/chunk-3AGBGTRT.js.map +0 -1
  46. package/dist/chunk-JAQ5K7W6.js.map +0 -1
  47. package/dist/chunk-WSK7G5LE.js.map +0 -1
  48. package/dist/chunk-X4A66EWZ.js.map +0 -1
  49. package/dist/chunk-YG4QEQF5.js.map +0 -1
  50. /package/dist/{browser-viewer-MKKFRRRV.js.map → browser-viewer-E5DDEFQ4.js.map} +0 -0
  51. /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
  52. /package/dist/{chunk-ZPBTI3TE.js.map → chunk-W2RGYBCV.js.map} +0 -0
  53. /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
  54. /package/dist/{queue-surface-implementation-BSKUWHRI.js.map → queue-surface-implementation-XL2A6PBM.js.map} +0 -0
package/dist/artifacts.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
- EditablePresentationArtifactSurface,
3
- PresentationArtifactSurface,
4
- PresentationEditor,
5
- PresentationProjectionArtifactSurface,
6
- PresentationProjectionEditor
7
- } from "./chunk-PEI6RANI.js";
2
+ DocumentArtifactSurface,
3
+ DocumentEditor,
4
+ DocumentProjectionArtifactSurface,
5
+ DocumentProjectionEditor,
6
+ EditableDocumentArtifactSurface
7
+ } from "./chunk-HUIKIDM5.js";
8
8
  import {
9
9
  EditableSpreadsheetArtifactSurface,
10
10
  EditableSpreadsheetGrid,
@@ -13,12 +13,12 @@ import {
13
13
  SpreadsheetProjectionGrid
14
14
  } from "./chunk-ICA6R6TW.js";
15
15
  import {
16
- DocumentArtifactSurface,
17
- DocumentEditor,
18
- DocumentProjectionArtifactSurface,
19
- DocumentProjectionEditor,
20
- EditableDocumentArtifactSurface
21
- } from "./chunk-HUIKIDM5.js";
16
+ EditablePresentationArtifactSurface,
17
+ PresentationArtifactSurface,
18
+ PresentationEditor,
19
+ PresentationProjectionArtifactSurface,
20
+ PresentationProjectionEditor
21
+ } from "./chunk-PEI6RANI.js";
22
22
  import {
23
23
  ArtifactSurface,
24
24
  EditableArtifactMessage
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  BrowserViewer
3
- } from "./chunk-ZPBTI3TE.js";
3
+ } from "./chunk-W2RGYBCV.js";
4
4
  import "./chunk-H5YGYNXS.js";
5
5
  import "./chunk-5YK2ZDIG.js";
6
+ import "./chunk-JVYQRRC4.js";
6
7
  import "./chunk-KZ73RL76.js";
7
8
  import "./chunk-NEXLLTP4.js";
8
- import "./chunk-JVYQRRC4.js";
9
9
  import "./chunk-VTOREEXF.js";
10
10
  import "./chunk-22KP6LR5.js";
11
11
  export {
12
12
  BrowserViewer
13
13
  };
14
- //# sourceMappingURL=browser-viewer-MKKFRRRV.js.map
14
+ //# sourceMappingURL=browser-viewer-E5DDEFQ4.js.map
@@ -1,15 +1,16 @@
1
+ import {
2
+ usePortalTokenSource,
3
+ usePortalTokenStyle
4
+ } from "./chunk-VZTQ73XT.js";
1
5
  import {
2
6
  effortOptionsForModel,
3
7
  findPickerRow,
4
8
  groupPickerRowsByBillingClass,
5
9
  labelReasoningEffort,
10
+ payerSummaryForModel,
6
11
  projectClientModelRows,
7
12
  runnableLatencyModesForModel
8
- } from "./chunk-X4A66EWZ.js";
9
- import {
10
- usePortalTokenSource,
11
- usePortalTokenStyle
12
- } from "./chunk-VZTQ73XT.js";
13
+ } from "./chunk-WMGF4Z7X.js";
13
14
  import {
14
15
  cn
15
16
  } from "./chunk-22KP6LR5.js";
@@ -46,7 +47,8 @@ var defaultModelPolicyPickerMessages = {
46
47
  external: "Provider terms and limits apply",
47
48
  codex_subscription: "ChatGPT / Codex plan",
48
49
  supergrok_subscription: "SuperGrok / xAI plan",
49
- byok: "Billed to your AI Gateway"
50
+ byok: "Billed to the workspace provider account",
51
+ organization_byok: "Billed to the organization provider account"
50
52
  }
51
53
  };
52
54
  var SLIDE_EASE = [0.22, 1, 0.36, 1];
@@ -82,7 +84,8 @@ function BillingClassMark(props) {
82
84
  external: "External provider",
83
85
  codex_subscription: "Codex",
84
86
  supergrok_subscription: "SuperGrok",
85
- byok: "Bring your own key"
87
+ byok: "Workspace provider account",
88
+ organization_byok: "Organization provider account"
86
89
  };
87
90
  const label = props["aria-label"] ?? labels[props.billingClass];
88
91
  const accessibility = label.length === 0 ? { "aria-hidden": true } : { role: "img", "aria-label": label };
@@ -104,6 +107,14 @@ function BillingClassMark(props) {
104
107
  function isCodexModel(model) {
105
108
  return model.id.startsWith("codex/") || model.source === "codex";
106
109
  }
110
+ function billingClassForMissingSelection(modelId) {
111
+ if (modelId.startsWith("workspace-gateway/")) return "byok";
112
+ if (modelId.startsWith("workspace-openrouter/")) return "byok";
113
+ if (modelId.startsWith("openrouter/")) return "opengeni_credits";
114
+ if (modelId.startsWith("codex/")) return "codex_subscription";
115
+ if (modelId.startsWith("supergrok/")) return "supergrok_subscription";
116
+ return "opengeni_credits";
117
+ }
107
118
  function applyCodexOnly(rows, codexOnly, unavailableReason) {
108
119
  if (!codexOnly) return rows;
109
120
  return rows.map(
@@ -126,7 +137,11 @@ function defaultNavState(rows, modelId) {
126
137
  const onlyRail = rails.size === 1 ? rows[0]?.billingClass : null;
127
138
  return onlyRail ? { level: "models", rail: onlyRail, modelId: null } : { level: "providers", rail: null, modelId: null };
128
139
  }
129
- return { level: "thinking", rail: selected.billingClass, modelId: selected.id };
140
+ return {
141
+ level: "thinking",
142
+ rail: selected.billingClass,
143
+ modelId: selected.id
144
+ };
130
145
  }
131
146
  function usePickerNavState(sessionKey, rows, model) {
132
147
  const [state, setState] = useState(() => defaultNavState(rows, model));
@@ -208,7 +223,11 @@ function ModelPolicyPickerMenu(props) {
208
223
  const nav = props.nav ?? localNav;
209
224
  const setNav = props.onNavChange ?? setLocalNav;
210
225
  const [direction, setDirection] = useState(1);
211
- const effectiveNav = nav.level === "providers" && groups.length === 1 ? { level: "models", rail: groups[0].billingClass, modelId: null } : nav;
226
+ const effectiveNav = nav.level === "providers" && groups.length === 1 ? {
227
+ level: "models",
228
+ rail: groups[0].billingClass,
229
+ modelId: null
230
+ } : nav;
212
231
  const activeGroup = effectiveNav.rail === null ? void 0 : groups.find((group) => group.billingClass === effectiveNav.rail);
213
232
  const focusModel = effectiveNav.modelId === null ? void 0 : findPickerRow(rows, effectiveNav.modelId);
214
233
  const selectedRow = findPickerRow(rows, props.model);
@@ -254,14 +273,21 @@ function ModelPolicyPickerMenu(props) {
254
273
  PickerNavRow,
255
274
  {
256
275
  label: row.label,
257
- hint: row.unavailableReason ?? void 0,
276
+ hint: row.unavailableReason ?? payerSummaryForModel(row.catalog),
258
277
  disabled: !row.selectable,
259
278
  title: row.unavailableReason ?? void 0,
260
279
  active: row.id === props.model,
261
280
  testId: `model-picker-choice-${row.id}`,
262
281
  onClick: () => {
263
282
  if (row.selectable) {
264
- go({ level: "thinking", rail: row.billingClass, modelId: row.id }, 1);
283
+ go(
284
+ {
285
+ level: "thinking",
286
+ rail: row.billingClass,
287
+ modelId: row.id
288
+ },
289
+ 1
290
+ );
265
291
  }
266
292
  }
267
293
  },
@@ -419,7 +445,7 @@ function ModelPolicyPicker(props) {
419
445
  /* @__PURE__ */ jsx(
420
446
  BillingClassMark,
421
447
  {
422
- billingClass: selected?.billingClass ?? (props.model.startsWith("codex/") ? "codex_subscription" : "opengeni_credits"),
448
+ billingClass: selected?.billingClass ?? billingClassForMissingSelection(props.model),
423
449
  className: "text-og-fg"
424
450
  }
425
451
  ),
@@ -467,4 +493,4 @@ export {
467
493
  ModelPolicyPickerMenu,
468
494
  ModelPolicyPicker
469
495
  };
470
- //# sourceMappingURL=chunk-2DZVYHVO.js.map
496
+ //# sourceMappingURL=chunk-4JFUT22W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/model-policy-picker.tsx"],"sourcesContent":["import type { ClientModel, LatencyMode, ReasoningEffort } from \"@opengeni/sdk\";\nimport {\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n Globe2Icon,\n KeyRoundIcon,\n ZapIcon,\n} from \"lucide-react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { DropdownMenu } from \"radix-ui\";\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type SVGProps,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { usePortalTokenSource, usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport {\n effortOptionsForModel,\n findPickerRow,\n groupPickerRowsByBillingClass,\n labelReasoningEffort,\n payerSummaryForModel,\n projectClientModelRows,\n runnableLatencyModesForModel,\n type PickerBillingClass,\n type PickerModelRow,\n} from \"../model-policy\";\n\ntype ClientPickerModelRow = PickerModelRow<ClientModel>;\n\ntype NavLevel = \"providers\" | \"models\" | \"thinking\";\n\ntype PickerNavState = {\n level: NavLevel;\n rail: PickerBillingClass | null;\n modelId: string | null;\n};\n\ntype NavUpdater = PickerNavState | ((previous: PickerNavState) => PickerNavState);\n\nexport type ModelPolicyPickerMessages = {\n label: string;\n loading: string;\n noModels: string;\n thinking: string;\n fast: string;\n fastRateHint: string;\n codexOnly: string;\n billingHints: Record<PickerBillingClass, string>;\n};\n\nexport const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {\n label: \"Model and effort\",\n loading: \"Loading model catalog…\",\n noModels: \"No models available.\",\n thinking: \"Thinking\",\n fast: \"Fast\",\n fastRateHint: \"2× rate\",\n codexOnly: \"Codex-only session\",\n billingHints: {\n opengeni_credits: \"Will use credits\",\n external: \"Provider terms and limits apply\",\n codex_subscription: \"ChatGPT / Codex plan\",\n supergrok_subscription: \"SuperGrok / xAI plan\",\n byok: \"Billed to the workspace provider account\",\n organization_byok: \"Billed to the organization provider account\",\n },\n};\n\nexport type ModelPolicyPickerProps = {\n /** Lightweight deployment models. Catalog rows take precedence when supplied. */\n models?: ClientModel[] | undefined;\n /** Catalog-backed rows with availability and billing-class truth. */\n rows?: PickerModelRow[] | undefined;\n model: string;\n effort: ReasoningEffort;\n latencyMode: LatencyMode;\n disabled?: boolean | undefined;\n loading?: boolean | undefined;\n error?: string | null | undefined;\n /** Controlled menu state. Omit for the built-in uncontrolled behavior. */\n open?: boolean | undefined;\n /** Initial menu state when `open` is uncontrolled. */\n defaultOpen?: boolean | undefined;\n onOpenChange?: ((open: boolean) => void) | undefined;\n /** Non-Codex models stay visible but cannot be selected. */\n codexOnly?: boolean | undefined;\n /** Resets drill-down navigation when the session scope changes. */\n sessionKey?: string | undefined;\n /** Prefer bottom on new-chat surfaces and top for bottom-docked composers. */\n menuSide?: \"top\" | \"bottom\" | undefined;\n /** Hide latency controls on surfaces whose saved policy does not include latency. */\n allowLatencyMode?: boolean | undefined;\n /** Classes for the portalled menu surface. Prefer --og-* tokens for theming. */\n contentClassName?: string | undefined;\n /** Inline styles for the portalled menu, applied after inherited --og-* tokens. */\n contentStyle?: CSSProperties | undefined;\n className?: string | undefined;\n messages?: Partial<ModelPolicyPickerMessages> | undefined;\n onModelChange: (modelId: string) => void;\n onEffortChange: (effort: ReasoningEffort) => void;\n onLatencyModeChange: (latencyMode: LatencyMode) => void;\n};\n\nconst SLIDE_EASE = [0.22, 1, 0.36, 1] as const;\n\nfunction OpenGeniMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg\n viewBox=\"0 0 140 133\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.2}\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <g transform=\"translate(-17.5,-20.999893188476562) scale(1.75)\">\n <g transform=\"translate(0,-952.36218)\">\n <path d=\"m 60.7828,964.36215 27.1809,0.8834 -27.1809,25.9958 z m -1.9745,1.4513 0,26.7845 -25.2681,0 c 8.6166,-8.7334 16.8796,-17.8103 25.2681,-26.7845 z m 27.7053,3.628 3.4864,1.1989 -12.5877,7.4768 z m -68.1835,2.9656 5.5226,0 12.8654,14.0705 -5.9854,6.1204 -12.4026,0 c 9e-4,-6.7347 0,-13.4597 0,-20.1909 z m -1.9746,1.2304 0,5.8364 -6.3555,0 z m 3.363,20.9796 38.627,0 -10.7675,29.43465 z m 39.0898,4.54286 0,41.20229 -12.5878,-6.8775 c 4.1972,-11.443 8.3886,-22.879 12.5878,-34.32479 z\" />\n </g>\n </g>\n </svg>\n );\n}\n\nfunction ChatGptMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n <path d=\"M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3653-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8414 3.3698-2.02 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.783-2.7622a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z\" />\n </svg>\n );\n}\n\nfunction XaiMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg viewBox=\"0 0 466.04 516.93\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n <polygon points=\"0.12 182.71 234.14 516.92 338.15 516.92 104.13 182.71 0.12 182.71\" />\n <polygon points=\"0 516.92 104.08 516.92 156.08 442.67 104.04 368.34 0 516.92\" />\n <polygon points=\"466.04 0 361.96 0 182.1 256.86 234.15 331.18 466.04 0\" />\n <polygon points=\"380.78 516.92 466.04 516.92 466.04 37.16 380.78 158.92 380.78 516.92\" />\n </svg>\n );\n}\n\nexport function BillingClassMark(props: {\n billingClass: PickerBillingClass;\n className?: string | undefined;\n \"aria-label\"?: string | undefined;\n}) {\n const labels: Record<PickerBillingClass, string> = {\n opengeni_credits: \"OpenGeni\",\n external: \"External provider\",\n codex_subscription: \"Codex\",\n supergrok_subscription: \"SuperGrok\",\n byok: \"Workspace provider account\",\n organization_byok: \"Organization provider account\",\n };\n const label = props[\"aria-label\"] ?? labels[props.billingClass];\n const accessibility =\n label.length === 0\n ? { \"aria-hidden\": true as const }\n : { role: \"img\" as const, \"aria-label\": label };\n const shell = cn(\n \"inline-flex size-3.5 shrink-0 items-center justify-center overflow-hidden text-og-fg-subtle\",\n props.className,\n );\n const mark = \"size-3.5\";\n return (\n <span\n className={shell}\n data-testid={`billing-class-icon-${props.billingClass}`}\n {...accessibility}\n >\n {props.billingClass === \"opengeni_credits\" ? (\n <OpenGeniMark className={mark} />\n ) : props.billingClass === \"external\" ? (\n <Globe2Icon className={mark} aria-hidden />\n ) : props.billingClass === \"codex_subscription\" ? (\n <ChatGptMark className={mark} />\n ) : props.billingClass === \"supergrok_subscription\" ? (\n <XaiMark className={mark} />\n ) : (\n <KeyRoundIcon className={mark} aria-hidden />\n )}\n </span>\n );\n}\n\nfunction isCodexModel(model: ClientModel): boolean {\n return model.id.startsWith(\"codex/\") || model.source === \"codex\";\n}\n\nfunction billingClassForMissingSelection(modelId: string): PickerBillingClass {\n if (modelId.startsWith(\"workspace-gateway/\")) return \"byok\";\n if (modelId.startsWith(\"workspace-openrouter/\")) return \"byok\";\n // A deployment OpenRouter ID does not encode its workspace-facing cost.\n // Missing rows therefore use the credits-safe rail instead of falsely\n // claiming that an unknown former selection was externally funded.\n if (modelId.startsWith(\"openrouter/\")) return \"opengeni_credits\";\n if (modelId.startsWith(\"codex/\")) return \"codex_subscription\";\n if (modelId.startsWith(\"supergrok/\")) return \"supergrok_subscription\";\n return \"opengeni_credits\";\n}\n\nfunction applyCodexOnly(\n rows: ClientPickerModelRow[],\n codexOnly: boolean,\n unavailableReason: string,\n): ClientPickerModelRow[] {\n if (!codexOnly) return rows;\n return rows.map((row) =>\n isCodexModel(row.catalog)\n ? row\n : {\n ...row,\n selectable: false,\n unavailableReason: row.unavailableReason ?? unavailableReason,\n },\n );\n}\n\nfunction effectiveRows(props: ModelPolicyPickerProps): ClientPickerModelRow[] {\n const rows = props.rows !== undefined ? props.rows : projectClientModelRows(props.models ?? []);\n const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };\n return applyCodexOnly(rows, props.codexOnly === true, messages.codexOnly);\n}\n\nfunction defaultNavState(rows: ClientPickerModelRow[], modelId: string): PickerNavState {\n const selected = findPickerRow(rows, modelId);\n if (!selected) {\n const rails = new Set(rows.map((row) => row.billingClass));\n const onlyRail = rails.size === 1 ? rows[0]?.billingClass : null;\n return onlyRail\n ? { level: \"models\", rail: onlyRail, modelId: null }\n : { level: \"providers\", rail: null, modelId: null };\n }\n return {\n level: \"thinking\",\n rail: selected.billingClass,\n modelId: selected.id,\n };\n}\n\nfunction usePickerNavState(\n sessionKey: string | undefined,\n rows: ClientPickerModelRow[],\n model: string,\n): [PickerNavState, (next: NavUpdater) => void] {\n const [state, setState] = useState<PickerNavState>(() => defaultNavState(rows, model));\n useEffect(() => {\n setState(defaultNavState(rows, model));\n // The menu deliberately preserves its drill-down while the same session is mounted.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sessionKey]);\n return [state, setState];\n}\n\nexport function PickerNavRow(props: {\n label: string;\n hint?: string | undefined;\n icon?: ReactNode;\n trailing?: ReactNode;\n showChevron?: boolean | undefined;\n disabled?: boolean | undefined;\n title?: string | undefined;\n active?: boolean | undefined;\n testId?: string | undefined;\n onClick: () => void;\n}) {\n return (\n <button\n type=\"button\"\n disabled={props.disabled}\n title={props.title}\n onClick={props.onClick}\n data-testid={props.testId}\n className={cn(\n \"flex w-full cursor-pointer items-center gap-2 rounded-og-sm px-[var(--og-model-picker-row-padding-x)] py-[var(--og-model-picker-row-padding-y)] text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40\",\n props.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n {props.icon}\n <span className=\"min-w-0 flex-1\">\n <span className={cn(\"block truncate text-og-menu\", props.active && \"font-medium\")}>\n {props.label}\n </span>\n {props.hint ? (\n <span className=\"mt-0.5 block truncate text-og-control text-og-fg-subtle\">\n {props.hint}\n </span>\n ) : null}\n </span>\n {props.trailing ? <span className=\"ml-auto shrink-0\">{props.trailing}</span> : null}\n {props.showChevron === false ? null : (\n <ChevronRightIcon className=\"size-3.5 shrink-0 text-og-fg-subtle\" />\n )}\n </button>\n );\n}\n\nexport function PickerBackHeader(props: {\n label: string;\n icon?: ReactNode;\n onBack: () => void;\n trailing?: ReactNode;\n}) {\n return (\n <div className=\"mb-1 flex items-center gap-0.5 border-b border-og-border/70 px-0.5 pb-1.5\">\n <button\n type=\"button\"\n onClick={props.onBack}\n data-testid=\"model-picker-back\"\n className=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-og-sm px-1.5 py-1.5 text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40\"\n >\n <ChevronLeftIcon className=\"size-3.5 shrink-0 text-og-fg-subtle\" />\n {props.icon}\n <span className=\"min-w-0 flex-1 truncate text-og-menu font-medium\">{props.label}</span>\n </button>\n {props.trailing ? <div className=\"relative z-10 shrink-0\">{props.trailing}</div> : null}\n </div>\n );\n}\n\nexport function PickerAnimatedPage(props: {\n pageKey: string;\n direction: 1 | -1;\n children: ReactNode;\n}) {\n const reduceMotion = useReducedMotion();\n return (\n <motion.div\n key={props.pageKey}\n initial={reduceMotion ? false : { x: props.direction * 14, opacity: 0 }}\n animate={{ x: 0, opacity: 1 }}\n transition={{ duration: reduceMotion ? 0 : 0.18, ease: SLIDE_EASE }}\n className=\"w-full\"\n >\n {props.children}\n </motion.div>\n );\n}\n\nexport function ModelPolicyPickerMenu(\n props: ModelPolicyPickerProps & {\n nav?: PickerNavState | undefined;\n onNavChange?: ((next: NavUpdater) => void) | undefined;\n },\n) {\n const messages = useMemo(\n () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),\n [props.messages],\n );\n const rows = effectiveRows(props);\n const groups = useMemo(() => groupPickerRowsByBillingClass(rows), [rows]);\n const [localNav, setLocalNav] = usePickerNavState(\n props.nav === undefined ? props.sessionKey : undefined,\n rows,\n props.model,\n );\n const nav = props.nav ?? localNav;\n const setNav = props.onNavChange ?? setLocalNav;\n const [direction, setDirection] = useState<1 | -1>(1);\n\n const effectiveNav =\n nav.level === \"providers\" && groups.length === 1\n ? {\n level: \"models\" as const,\n rail: groups[0]!.billingClass,\n modelId: null,\n }\n : nav;\n const activeGroup =\n effectiveNav.rail === null\n ? undefined\n : groups.find((group) => group.billingClass === effectiveNav.rail);\n const focusModel =\n effectiveNav.modelId === null ? undefined : findPickerRow(rows, effectiveNav.modelId);\n const selectedRow = findPickerRow(rows, props.model);\n\n const go = (next: PickerNavState, nextDirection: 1 | -1) => {\n setDirection(nextDirection);\n setNav(next);\n };\n\n const selectEffort = (row: ClientPickerModelRow, effort: ReasoningEffort) => {\n if (!row.selectable) return;\n if (row.id !== props.model) props.onModelChange(row.id);\n props.onEffortChange(effort);\n };\n\n const pageKey =\n effectiveNav.level === \"providers\"\n ? \"providers\"\n : effectiveNav.level === \"models\"\n ? `models:${effectiveNav.rail ?? \"\"}`\n : `thinking:${effectiveNav.modelId ?? \"\"}`;\n\n let body: ReactNode;\n if (props.loading) {\n body = <p className=\"px-2 py-3 text-og-control text-og-fg-subtle\">{messages.loading}</p>;\n } else if (groups.length === 0) {\n body = <p className=\"px-2 py-3 text-og-control text-og-fg-subtle\">{messages.noModels}</p>;\n } else if (effectiveNav.level === \"providers\") {\n body = (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"model-picker-providers\">\n {groups.map((group) => (\n <PickerNavRow\n key={group.billingClass}\n label={group.label}\n hint={messages.billingHints[group.billingClass]}\n icon={<BillingClassMark billingClass={group.billingClass} />}\n active={selectedRow?.billingClass === group.billingClass}\n testId={`model-picker-rail-${group.billingClass}`}\n onClick={() => go({ level: \"models\", rail: group.billingClass, modelId: null }, 1)}\n />\n ))}\n </div>\n );\n } else if (effectiveNav.level === \"models\" && activeGroup) {\n body = (\n <div data-testid=\"model-picker-models\">\n {groups.length > 1 ? (\n <PickerBackHeader\n label={activeGroup.label}\n icon={<BillingClassMark billingClass={activeGroup.billingClass} />}\n onBack={() => go({ level: \"providers\", rail: null, modelId: null }, -1)}\n />\n ) : null}\n <div className=\"flex flex-col gap-0.5\">\n {activeGroup.rows.map((row) => (\n <PickerNavRow\n key={`${row.billingClass}:${row.id}`}\n label={row.label}\n hint={row.unavailableReason ?? payerSummaryForModel(row.catalog)}\n disabled={!row.selectable}\n title={row.unavailableReason ?? undefined}\n active={row.id === props.model}\n testId={`model-picker-choice-${row.id}`}\n onClick={() => {\n if (row.selectable) {\n go(\n {\n level: \"thinking\",\n rail: row.billingClass,\n modelId: row.id,\n },\n 1,\n );\n }\n }}\n />\n ))}\n </div>\n </div>\n );\n } else if (effectiveNav.level === \"thinking\" && focusModel) {\n const activeModel = focusModel.id === props.model;\n const supportsFast = runnableLatencyModesForModel(focusModel.catalog).includes(\"fast\");\n body = (\n <div data-testid=\"model-picker-reasoning\">\n <PickerBackHeader\n label={focusModel.label}\n icon={<BillingClassMark billingClass={focusModel.billingClass} />}\n onBack={() => go({ level: \"models\", rail: focusModel.billingClass, modelId: null }, -1)}\n trailing={\n supportsFast && props.allowLatencyMode !== false ? (\n <button\n type=\"button\"\n disabled={!focusModel.selectable}\n aria-pressed={activeModel && props.latencyMode === \"fast\"}\n aria-label={\n activeModel && props.latencyMode === \"fast\"\n ? `Disable ${messages.fast}`\n : `Enable ${messages.fast}`\n }\n title={`${messages.fast} · ${messages.fastRateHint}`}\n data-testid=\"model-picker-fast\"\n onClick={() => {\n if (!activeModel) {\n props.onModelChange(focusModel.id);\n }\n props.onLatencyModeChange(\n activeModel && props.latencyMode === \"fast\" ? \"standard\" : \"fast\",\n );\n }}\n className={cn(\n \"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-og-sm text-og-fg-subtle outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50\",\n activeModel && props.latencyMode === \"fast\" && \"text-og-fg\",\n )}\n >\n <ZapIcon\n className={cn(\n \"size-3.5\",\n activeModel && props.latencyMode === \"fast\" && \"fill-current\",\n )}\n />\n </button>\n ) : null\n }\n />\n <p className=\"px-2.5 pt-1 pb-1 text-og-control font-medium tracking-wide text-og-fg-subtle uppercase\">\n {messages.thinking}\n </p>\n {focusModel.catalog.capabilities?.inputModalities.includes(\"image\") === false ? (\n <p className=\"px-2.5 pb-1.5 text-og-control leading-relaxed text-og-fg-subtle\">\n Unsupported attachments stay in the session but are hidden from this model.\n </p>\n ) : null}\n <div className=\"flex flex-col gap-0.5\">\n {effortOptionsForModel(focusModel.catalog).map((effort) => {\n const selected = activeModel && effort === props.effort;\n return (\n <button\n key={effort}\n type=\"button\"\n disabled={!focusModel.selectable}\n onClick={() => selectEffort(focusModel, effort)}\n className={cn(\n \"flex h-[var(--og-model-picker-effort-height)] w-full cursor-pointer items-center rounded-og-sm px-[var(--og-model-picker-row-padding-x)] text-left text-og-menu text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50\",\n selected && \"bg-og-surface-2\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{labelReasoningEffort(effort)}</span>\n {selected ? (\n <CheckIcon\n className=\"ml-auto size-3.5 shrink-0\"\n data-testid=\"model-picker-effort-check\"\n />\n ) : null}\n </button>\n );\n })}\n </div>\n </div>\n );\n } else {\n body = (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"model-picker-providers\">\n {groups.map((group) => (\n <PickerNavRow\n key={group.billingClass}\n label={group.label}\n hint={messages.billingHints[group.billingClass]}\n icon={<BillingClassMark billingClass={group.billingClass} />}\n testId={`model-picker-rail-${group.billingClass}`}\n onClick={() => go({ level: \"models\", rail: group.billingClass, modelId: null }, 1)}\n />\n ))}\n </div>\n );\n }\n\n return (\n <div data-testid=\"model-picker-menu\" className=\"relative overflow-hidden\">\n {props.error ? (\n <p className=\"px-2 py-1 text-og-control text-og-status-failed\" role=\"alert\">\n {props.error}\n </p>\n ) : null}\n <PickerAnimatedPage pageKey={pageKey} direction={direction}>\n {body}\n </PickerAnimatedPage>\n </div>\n );\n}\n\n/** @internal Shared state seam kept outside the Radix portal for deterministic testing. */\nexport function useModelPolicyPickerState(props: ModelPolicyPickerProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(props.defaultOpen ?? false);\n const open = props.open ?? uncontrolledOpen;\n const rows = effectiveRows(props);\n const [nav, setNav] = usePickerNavState(props.sessionKey, rows, props.model);\n const previousControlledOpen = useRef(props.open);\n useEffect(() => {\n const wasOpen = previousControlledOpen.current;\n previousControlledOpen.current = props.open;\n if (props.open === true && wasOpen !== true) {\n setNav(defaultNavState(rows, props.model));\n }\n // Reset only on the controlled closed-to-open edge; ordinary rerenders\n // deliberately preserve the current drill-down page.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.open]);\n const setOpen = (next: boolean) => {\n if (next) {\n setNav(defaultNavState(rows, props.model));\n }\n if (props.open === undefined) setUncontrolledOpen(next);\n props.onOpenChange?.(next);\n };\n return { open, setOpen, rows, nav, setNav };\n}\n\nexport function ModelPolicyPicker(props: ModelPolicyPickerProps) {\n const trigger = usePortalTokenSource<HTMLButtonElement>();\n const portalStyle = usePortalTokenStyle(trigger.source);\n const messages = useMemo(\n () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),\n [props.messages],\n );\n const { open, setOpen, rows, nav, setNav } = useModelPolicyPickerState(props);\n const selected = findPickerRow(rows, props.model);\n\n if (props.loading) {\n return (\n <span\n className={cn(\n \"og-root inline-flex h-8 w-40 shrink-0 animate-pulse rounded-full bg-og-surface-2\",\n props.className,\n )}\n aria-label={messages.loading}\n data-testid=\"model-picker-loading\"\n />\n );\n }\n return (\n <DropdownMenu.Root open={open} onOpenChange={setOpen}>\n <DropdownMenu.Trigger asChild>\n <button\n ref={trigger.ref}\n type=\"button\"\n disabled={props.disabled}\n aria-label={messages.label}\n className={cn(\n \"og-root og-model-policy-trigger inline-flex h-[var(--og-model-picker-trigger-height)] min-w-0 max-w-64 items-center gap-1 rounded-full border border-transparent px-2.5 text-og-control text-og-fg-muted outline-hidden transition-colors hover:border-og-border hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50 max-sm:h-11 max-sm:max-w-[7.5rem] max-sm:px-2\",\n props.className,\n )}\n >\n <BillingClassMark\n billingClass={selected?.billingClass ?? billingClassForMissingSelection(props.model)}\n className=\"text-og-fg\"\n />\n <span className=\"og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden\">\n {selected?.label ?? props.model}\n </span>\n <span className=\"og-model-policy-label-short min-w-0 truncate font-medium text-og-fg sm:hidden\">\n {selected?.shortLabel ?? selected?.label ?? props.model}\n </span>\n <span className=\"og-model-policy-effort max-sm:hidden\">\n {labelReasoningEffort(props.effort)}\n </span>\n {props.latencyMode === \"fast\" ? (\n <ZapIcon\n className=\"size-3.5 shrink-0 fill-current stroke-current text-og-fg\"\n aria-label={messages.fast}\n data-testid=\"model-picker-fast-icon\"\n />\n ) : null}\n <ChevronDownIcon className=\"size-3 shrink-0\" />\n </button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Portal>\n <DropdownMenu.Content\n align=\"start\"\n side={props.menuSide ?? \"bottom\"}\n sideOffset={8}\n collisionPadding={12}\n onCloseAutoFocus={(event) => event.preventDefault()}\n className={cn(\n \"og-root og-model-policy-menu z-50 flex max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] w-[var(--og-model-picker-menu-width)] max-w-[calc(100vw-16px)] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 p-[var(--og-model-picker-menu-padding)] text-og-fg shadow-og-lg\",\n props.contentClassName,\n )}\n style={{ ...portalStyle, ...props.contentStyle }}\n data-testid=\"model-picker-content\"\n >\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain\">\n <ModelPolicyPickerMenu {...props} nav={nav} onNavChange={setNav} />\n </div>\n </DropdownMenu.Content>\n </DropdownMenu.Portal>\n </DropdownMenu.Root>\n );\n}\n"],"mappings":"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","names":[]}
@@ -17,15 +17,15 @@ import {
17
17
  encodeStreamClose,
18
18
  encodeStreamOpen
19
19
  } from "./chunk-5YK2ZDIG.js";
20
+ import {
21
+ copyTextToClipboard
22
+ } from "./chunk-JVYQRRC4.js";
20
23
  import {
21
24
  usePageLiveActivity
22
25
  } from "./chunk-KZ73RL76.js";
23
26
  import {
24
27
  useEmbeddedComputerInteraction
25
28
  } from "./chunk-NEXLLTP4.js";
26
- import {
27
- copyTextToClipboard
28
- } from "./chunk-JVYQRRC4.js";
29
29
  import {
30
30
  cn
31
31
  } from "./chunk-22KP6LR5.js";
@@ -2297,4 +2297,4 @@ export {
2297
2297
  ComputerViewer,
2298
2298
  computerKey
2299
2299
  };
2300
- //# sourceMappingURL=chunk-ELIYWBZR.js.map
2300
+ //# sourceMappingURL=chunk-BV5A4OTE.js.map
@@ -1,10 +1,3 @@
1
- import {
2
- buildTimeline,
3
- createOlderHistoryLoadReceipt,
4
- groupTimeline,
5
- isActionableHumanInputRequest,
6
- sessionStatusFromEvents
7
- } from "./chunk-JAQ5K7W6.js";
8
1
  import {
9
2
  useDebouncedCallback,
10
3
  useMutationRunner,
@@ -21,6 +14,13 @@ import {
21
14
  useEmbeddedSessionMcpApprovalPolicy,
22
15
  useEmbeddedSessionRead
23
16
  } from "./chunk-NEXLLTP4.js";
17
+ import {
18
+ buildTimeline,
19
+ createOlderHistoryLoadReceipt,
20
+ groupTimeline,
21
+ isActionableHumanInputRequest,
22
+ sessionStatusFromEvents
23
+ } from "./chunk-TJFMMDQU.js";
24
24
 
25
25
  // src/embedded-session-client.ts
26
26
  var requiredMethods = [
@@ -192,6 +192,9 @@ function useSession(sessionId, options = {}) {
192
192
  }
193
193
 
194
194
  // src/hooks/use-session-events.ts
195
+ import {
196
+ sessionEventStreamCoveredThrough
197
+ } from "@opengeni/sdk";
195
198
  import { startTransition, useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
196
199
  var INITIAL_TAIL_PAGE_SIZE = 1e3;
197
200
  var OLDER_PAGE_SIZE = 5e3;
@@ -205,6 +208,11 @@ var NEWER_FETCH_CAP = 2;
205
208
  var OLDEST_GROUP_TARGET = 32;
206
209
  var OLDEST_FETCH_CAP = 2;
207
210
  var BOUNDARY_PAGE_CAP = 4;
211
+ var FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES = 16;
212
+ var FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES = 5e3;
213
+ var FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS = 128;
214
+ var FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS = 16;
215
+ var FOREGROUND_COMPACT_CATCHUP_MAX_BYTES = 512 * 1024;
208
216
  var EMPTY_EVENTS = [];
209
217
  var encoder = new TextEncoder();
210
218
  var decoder = new TextDecoder();
@@ -276,6 +284,7 @@ function useSessionEvents(sessionId, options = {}) {
276
284
  const loadingLatestRef = useRef2(false);
277
285
  const viewModeRef = useRef2("live");
278
286
  const initialWindowLoadedRef = useRef2(false);
287
+ const reconcileAfterPageResumeRef = useRef2(false);
279
288
  const streamAbortRef = useRef2(null);
280
289
  const streamKeyRef = useRef2(null);
281
290
  const generationRef = useRef2(0);
@@ -314,6 +323,7 @@ function useSessionEvents(sessionId, options = {}) {
314
323
  viewModeRef.current = "live";
315
324
  setViewMode("live");
316
325
  initialWindowLoadedRef.current = false;
326
+ reconcileAfterPageResumeRef.current = false;
317
327
  }
318
328
  const generation = generationRef.current + 1;
319
329
  generationRef.current = generation;
@@ -322,14 +332,20 @@ function useSessionEvents(sessionId, options = {}) {
322
332
  setLoadingOlder(false);
323
333
  }
324
334
  if (!sessionId || !streamEnabled) {
335
+ if (sessionId && enabled && !pageLive && !fullReplay && viewModeRef.current === "live" && initialWindowLoadedRef.current) {
336
+ reconcileAfterPageResumeRef.current = true;
337
+ }
325
338
  setConnectionState("idle");
326
339
  return;
327
340
  }
328
341
  if (viewMode === "history") {
342
+ reconcileAfterPageResumeRef.current = false;
329
343
  setConnectionState("idle");
330
344
  setInitialLoading(false);
331
345
  return;
332
346
  }
347
+ const reconcileForegroundResume = reconcileAfterPageResumeRef.current && !fullReplay;
348
+ reconcileAfterPageResumeRef.current = false;
333
349
  const controller = new AbortController();
334
350
  const isCurrent = () => generationRef.current === generation && !controller.signal.aborted;
335
351
  streamAbortRef.current = controller;
@@ -386,6 +402,59 @@ function useSessionEvents(sessionId, options = {}) {
386
402
  };
387
403
  void (async () => {
388
404
  try {
405
+ if (reconcileForegroundResume) {
406
+ setConnectionState("connecting");
407
+ const cursor = streamResumeSequenceRef.current;
408
+ let plan = { kind: "resume" };
409
+ try {
410
+ plan = await planForegroundCatchup(client, workspaceId, sessionId, cursor, {
411
+ signal: controller.signal
412
+ });
413
+ } catch {
414
+ if (controller.signal.aborted) return;
415
+ plan = { kind: "resume" };
416
+ }
417
+ if (!isCurrent()) return;
418
+ if (plan.kind === "append") {
419
+ const current = eventWindowRef.current;
420
+ assertAppendOrder(current.events, plan.events);
421
+ const status = observeSessionStatus(plan.events, sessionStatusRef);
422
+ const next = boundBrowserSessionEventWindow([...current.events, ...plan.events]);
423
+ const retained = {
424
+ ...next,
425
+ truncated: current.truncated || next.truncated
426
+ };
427
+ eventWindowRef.current = retained;
428
+ oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
429
+ newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
430
+ lastSequenceRef.current = Math.max(lastSequenceRef.current, plan.resumeSequence);
431
+ streamResumeSequenceRef.current = Math.max(
432
+ streamResumeSequenceRef.current,
433
+ plan.resumeSequence
434
+ );
435
+ if (status !== void 0) {
436
+ setSessionStatusProjection(status);
437
+ }
438
+ setEventWindow(retained);
439
+ if (retained.truncated) {
440
+ hasOlderRef.current = true;
441
+ setHasOlder(true);
442
+ }
443
+ } else if (plan.kind === "reload") {
444
+ eventWindowRef.current = EMPTY_EVENT_WINDOW;
445
+ oldestSequenceRef.current = null;
446
+ newestSequenceRef.current = null;
447
+ hasOlderRef.current = false;
448
+ hasNewerRef.current = false;
449
+ initialWindowLoadedRef.current = false;
450
+ streamResumeSequenceRef.current = after;
451
+ setEventWindow(EMPTY_EVENT_WINDOW);
452
+ setHasOlder(false);
453
+ setHasNewer(false);
454
+ setInitialLoading(true);
455
+ setError(null);
456
+ }
457
+ }
389
458
  if (!fullReplay && !initialWindowLoadedRef.current) {
390
459
  setConnectionState("connecting");
391
460
  const window = await loadEventWindow(client, workspaceId, sessionId, {
@@ -481,6 +550,8 @@ function useSessionEvents(sessionId, options = {}) {
481
550
  workspaceId,
482
551
  sessionId,
483
552
  after,
553
+ enabled,
554
+ pageLive,
484
555
  streamEnabled,
485
556
  fullReplay,
486
557
  streamKey,
@@ -1010,6 +1081,39 @@ function observeSessionStatus(events, ref) {
1010
1081
  ref.current = latest;
1011
1082
  return latest.status;
1012
1083
  }
1084
+ async function planForegroundCatchup(client, workspaceId, sessionId, cursor, options = {}) {
1085
+ if (options.signal?.aborted) throw abortError();
1086
+ const session = await client.getSession(workspaceId, sessionId, {
1087
+ fresh: true,
1088
+ ...options.signal ? { signal: options.signal } : {}
1089
+ });
1090
+ if (options.signal?.aborted) throw abortError();
1091
+ const durableHead = Math.max(cursor, session.lastSequence);
1092
+ const rawGap = durableHead - cursor;
1093
+ if (rawGap <= FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES) {
1094
+ return { kind: "resume" };
1095
+ }
1096
+ if (rawGap > FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES) {
1097
+ return { kind: "reload" };
1098
+ }
1099
+ const page = await loadNextPage(client, workspaceId, sessionId, cursor, {
1100
+ pageSize: rawGap,
1101
+ ...options.signal ? { signal: options.signal } : {}
1102
+ });
1103
+ const events = page.filter((event) => eventResumeSequence(event) > cursor).map(boundBrowserLegacyEvent);
1104
+ if (events.length === 0) {
1105
+ return { kind: "reload" };
1106
+ }
1107
+ assertAscending(events);
1108
+ const resumeSequence = maxResumeSequence(events);
1109
+ if (resumeSequence < durableHead) {
1110
+ return { kind: "reload" };
1111
+ }
1112
+ if (events.length > FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS || groupCount(events) > FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS || browserJsonBytes(events) > FOREGROUND_COMPACT_CATCHUP_MAX_BYTES) {
1113
+ return { kind: "reload" };
1114
+ }
1115
+ return { kind: "append", events, resumeSequence };
1116
+ }
1013
1117
  async function loadEventWindow(client, workspaceId, sessionId, options) {
1014
1118
  let cursor = options.before;
1015
1119
  let buffer = [];
@@ -1202,12 +1306,9 @@ function maxResumeSequenceOrNull(events) {
1202
1306
  return events.length > 0 ? maxResumeSequence(events) : null;
1203
1307
  }
1204
1308
  function eventResumeSequence(event) {
1205
- const payload = asRecord(event.payload);
1206
- const coalescedUntil = Number(payload.coalescedUntil);
1207
- return Math.max(event.sequence, Number.isFinite(coalescedUntil) ? Math.floor(coalescedUntil) : 0);
1208
- }
1209
- function asRecord(value) {
1210
- return value && typeof value === "object" ? value : {};
1309
+ const streamedCoverage = sessionEventStreamCoveredThrough(event);
1310
+ if (streamedCoverage !== null) return streamedCoverage;
1311
+ return typeof event.coveredThrough === "number" && Number.isSafeInteger(event.coveredThrough) && event.coveredThrough >= event.sequence ? event.coveredThrough : event.sequence;
1211
1312
  }
1212
1313
  function assertAscending(events) {
1213
1314
  for (let index = 1; index < events.length; index += 1) {
@@ -1245,6 +1346,16 @@ function abortError() {
1245
1346
  // src/hooks/use-file-attachments.ts
1246
1347
  import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
1247
1348
  var isImage = (file) => file.type.startsWith("image/");
1349
+ var fallbackAttachmentId = 0;
1350
+ function createAttachmentId() {
1351
+ const cryptoSource = globalThis.crypto;
1352
+ if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
1353
+ fallbackAttachmentId += 1;
1354
+ return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
1355
+ }
1356
+ function secureContextRequiredErrorCode(error) {
1357
+ return typeof error === "object" && error !== null && "code" in error && error.code === "secure_context_required" ? error.code : void 0;
1358
+ }
1248
1359
  function useFileAttachments(options = {}) {
1249
1360
  const { client, workspaceId } = useEmbeddedFileAttachments(options);
1250
1361
  const pasteFilter = options.pasteFilter ?? isImage;
@@ -1332,6 +1443,7 @@ function useFileAttachments(options = {}) {
1332
1443
  name: asset.filename,
1333
1444
  contentType: asset.contentType,
1334
1445
  sizeBytes: asset.sizeBytes,
1446
+ errorCode: void 0,
1335
1447
  error: void 0
1336
1448
  } : attachment
1337
1449
  )
@@ -1343,6 +1455,7 @@ function useFileAttachments(options = {}) {
1343
1455
  (attachment) => attachment.id === id ? {
1344
1456
  ...attachment,
1345
1457
  status: "failed",
1458
+ errorCode: secureContextRequiredErrorCode(error),
1346
1459
  error: error instanceof Error ? error.message : String(error)
1347
1460
  } : attachment
1348
1461
  )
@@ -1354,7 +1467,7 @@ function useFileAttachments(options = {}) {
1354
1467
  const addFiles = useCallback3(
1355
1468
  (files) => {
1356
1469
  for (const file of files) {
1357
- const id = crypto.randomUUID();
1470
+ const id = createAttachmentId();
1358
1471
  sources.current.set(id, file);
1359
1472
  const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
1360
1473
  if (previewUrl) previewUrls.current.set(id, previewUrl);
@@ -1382,7 +1495,7 @@ function useFileAttachments(options = {}) {
1382
1495
  }
1383
1496
  setAttachments(
1384
1497
  (current) => current.map(
1385
- (attachment) => attachment.id === id ? { ...attachment, status: "uploading", error: void 0 } : attachment
1498
+ (attachment) => attachment.id === id ? { ...attachment, status: "uploading", errorCode: void 0, error: void 0 } : attachment
1386
1499
  )
1387
1500
  );
1388
1501
  startUpload(id, file);
@@ -1426,6 +1539,7 @@ function useFileAttachments(options = {}) {
1426
1539
  sizeBytes: file.sizeBytes,
1427
1540
  status: "ready",
1428
1541
  file,
1542
+ errorCode: void 0,
1429
1543
  error: void 0
1430
1544
  } : {
1431
1545
  id: `restored:${file.id}`,
@@ -2465,4 +2579,4 @@ export {
2465
2579
  isHumanInputEvent,
2466
2580
  useHumanInputRequests
2467
2581
  };
2468
- //# sourceMappingURL=chunk-3AGBGTRT.js.map
2582
+ //# sourceMappingURL=chunk-IB27C53Y.js.map