@opengeni/react 1.1.0 → 1.2.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 (45) hide show
  1. package/dist/{chunk-6TROCOEK.js → chunk-4OTO7KBK.js} +774 -731
  2. package/dist/chunk-4OTO7KBK.js.map +1 -0
  3. package/dist/{chunk-RLUSC2WA.js → chunk-5F4WR7X7.js} +2 -1
  4. package/dist/chunk-5F4WR7X7.js.map +1 -0
  5. package/dist/{chunk-EN3YTHKR.js → chunk-CY7WK5FD.js} +2 -2
  6. package/dist/{chunk-26ZNDDLZ.js → chunk-LNIRAOA3.js} +27 -8
  7. package/dist/chunk-LNIRAOA3.js.map +1 -0
  8. package/dist/{chunk-DO544OSV.js → chunk-RC6BZ44Q.js} +4 -3
  9. package/dist/chunk-RC6BZ44Q.js.map +1 -0
  10. package/dist/{chunk-WGXVEJO5.js → chunk-SGKD2OCN.js} +5 -5
  11. package/dist/chunk-SGKD2OCN.js.map +1 -0
  12. package/dist/{chunk-JIQYUGVL.js → chunk-X34BSY2R.js} +3 -3
  13. package/dist/components/model-mark.d.ts +3 -3
  14. package/dist/composer.js +4 -4
  15. package/dist/index.js +7 -7
  16. package/dist/{latest-question-HTRIS4KJ.js → latest-question-YTOBHCWR.js} +2 -2
  17. package/dist/{model-policy-picker-menu-M54ISBMN.js → model-policy-picker-menu-DXHUL73H.js} +4 -4
  18. package/dist/model-policy.d.ts +1 -0
  19. package/dist/model-policy.js +1 -1
  20. package/dist/realtime.js +2 -2
  21. package/dist/session-ui.js +6 -6
  22. package/dist/session.js +2 -2
  23. package/dist/timeline/genie-loading.d.ts +3 -1
  24. package/dist/timeline/startup-dispatch-details.d.ts +5 -0
  25. package/dist/timeline/types.d.ts +5 -1
  26. package/package.json +3 -3
  27. package/src/components/model-mark.tsx +16 -5
  28. package/src/components/model-policy-picker-menu.tsx +2 -2
  29. package/src/components/model-policy-picker.tsx +11 -3
  30. package/src/components/session-conversation.tsx +3 -0
  31. package/src/model-policy.ts +9 -0
  32. package/src/timeline/activity-rail.tsx +14 -4
  33. package/src/timeline/genie-loading.tsx +27 -16
  34. package/src/timeline/projection.ts +7 -3
  35. package/src/timeline/startup-dispatch-details.tsx +36 -0
  36. package/src/timeline/types.ts +5 -0
  37. package/dist/chunk-26ZNDDLZ.js.map +0 -1
  38. package/dist/chunk-6TROCOEK.js.map +0 -1
  39. package/dist/chunk-DO544OSV.js.map +0 -1
  40. package/dist/chunk-RLUSC2WA.js.map +0 -1
  41. package/dist/chunk-WGXVEJO5.js.map +0 -1
  42. /package/dist/{chunk-EN3YTHKR.js.map → chunk-CY7WK5FD.js.map} +0 -0
  43. /package/dist/{chunk-JIQYUGVL.js.map → chunk-X34BSY2R.js.map} +0 -0
  44. /package/dist/{latest-question-HTRIS4KJ.js.map → latest-question-YTOBHCWR.js.map} +0 -0
  45. /package/dist/{model-policy-picker-menu-M54ISBMN.js.map → model-policy-picker-menu-DXHUL73H.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/model-policy-picker-menu.tsx"],"sourcesContent":["import type { ClientModel, ReasoningEffort } from \"@opengeni/sdk\";\nimport { ArrowRightIcon, CheckIcon, SearchIcon, ZapIcon } from \"lucide-react\";\nimport { useRef, useState, type RefObject, type CSSProperties } from \"react\";\nimport { Popover, RadioGroup } from \"radix-ui\";\nimport { cn } from \"../lib/cn\";\nimport {\n MENU_CHECK_CLASS,\n MENU_LABEL_CLASS,\n MENU_NOTE_CLASS,\n MENU_SURFACE_CLASS,\n} from \"../lib/menu-styles\";\nimport {\n coerceReasoningEffortForModel,\n effortOptionsForModel,\n findPickerRow,\n groupPickerRowsByBillingClass,\n labelReasoningEffort,\n payerSummaryForModel,\n runnableLatencyModesForModel,\n type PickerModelRow,\n} from \"../model-policy\";\nimport {\n BillingClassMark,\n defaultModelPolicyPickerMessages,\n effectiveRows,\n groupPresentationFor,\n PickerNavRow,\n SCOPED_BILLING_HINTS,\n type ModelPolicyPickerProps,\n} from \"./model-policy-picker\";\nimport { ModelMark } from \"./model-mark\";\ntype ClientPickerModelRow = PickerModelRow<ClientModel>;\n\n/** Model selection stays flat; reasoning never becomes a navigation destination. */\nexport function ModelPolicyPickerMenu(props: ModelPolicyPickerProps) {\n const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };\n const [query, setQuery] = useState(\"\");\n const rows = effectiveRows(props);\n const selected = findPickerRow(rows, props.model);\n const words = query.toLowerCase().trim().split(/\\s+/).filter(Boolean);\n const filtered = rows.filter((row) => {\n const description = groupPresentationFor(props, row.billingClass)?.description;\n const text =\n `${row.label} ${row.id} ${row.providerLabel} ${row.billingClassLabel} ${description === undefined ? payerSummaryForModel(row.catalog) : (description ?? \"\")}`.toLowerCase();\n return words.every((word) => text.includes(word));\n });\n const matchingIds = new Set(filtered.map((row) => row.id));\n const groups = groupPickerRowsByBillingClass(rows, {\n codexOnly: props.codexOnly === true,\n selectedId: props.model,\n collapseScopes: props.collapseScopes !== false,\n })\n .map((group) => {\n const presentation = groupPresentationFor(props, group.billingClass);\n const override = presentation?.description;\n const defaultHint =\n props.collapseScopes === false &&\n messages.billingHints[group.billingClass] ===\n defaultModelPolicyPickerMessages.billingHints[group.billingClass]\n ? (SCOPED_BILLING_HINTS[group.billingClass] ?? messages.billingHints[group.billingClass])\n : messages.billingHints[group.billingClass];\n return {\n ...group,\n label: presentation?.label ?? group.label,\n rows: group.rows.filter((row) => matchingIds.has(row.id)),\n description:\n override === undefined\n ? group.billingClass === \"opengeni_credits\"\n ? null\n : defaultHint\n : override,\n };\n })\n .filter((group) => group.rows.length > 0);\n const choose = (row: ClientPickerModelRow) => {\n if (!row.selectable || props.disabled) return;\n if (row.id !== props.model) props.onModelChange(row.id);\n const effort = coerceReasoningEffortForModel(row.catalog, props.effort);\n // Hosts may commit the combined model/effort draft through this callback.\n props.onEffortChange(effort);\n if (\n props.latencyMode !== \"standard\" &&\n !runnableLatencyModesForModel(row.catalog).includes(props.latencyMode)\n ) {\n props.onLatencyModeChange(\"standard\");\n }\n };\n const modelRow = (row: ClientPickerModelRow) => (\n <PickerNavRow\n key={row.id}\n icon={\n props.collapseScopes === false ? undefined : (\n <ModelMark model={row.catalog} className=\"size-4 text-og-fg-muted\" />\n )\n }\n label={row.label}\n hint={row.unavailableReason ?? row.fundingHint}\n disabled={props.disabled || !row.selectable}\n title={[row.label, row.unavailableReason ?? row.fundingHint].filter(Boolean).join(\" · \")}\n active={row.selectable && row.id === props.model}\n showChevron={false}\n trailing={\n row.catalog.cost === \"free\" || row.id === props.model ? (\n <span className=\"flex items-center gap-2\">\n {row.catalog.cost === \"free\" ? (\n <span className=\"rounded-full bg-og-surface-2 px-1.5 py-0.5 text-og-control text-og-fg-muted\">\n {messages.free}\n </span>\n ) : null}\n {row.id === props.model ? (\n <CheckIcon className={MENU_CHECK_CLASS} aria-label={messages.selected} />\n ) : null}\n </span>\n ) : null\n }\n testId={`model-picker-choice-${row.id}`}\n onClick={() => choose(row)}\n />\n );\n return (\n <div\n data-testid=\"model-picker-menu\"\n className=\"flex min-h-0 flex-col\"\n onKeyDown={(event) => {\n if (\n !(event.target instanceof HTMLButtonElement) ||\n ![\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"].includes(event.key)\n )\n return;\n const buttons = Array.from(\n event.currentTarget.querySelectorAll<HTMLButtonElement>(\n '[data-testid^=\"model-picker-choice-\"]:not(:disabled)',\n ),\n );\n const index = buttons.indexOf(event.target);\n if (index < 0) return;\n event.preventDefault();\n const next =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? buttons.length - 1\n : (index + (event.key === \"ArrowDown\" ? 1 : -1) + buttons.length) % buttons.length;\n buttons[next]?.focus();\n }}\n >\n {rows.some((row) => row.selectable) ? (\n <div className=\"border-b border-og-border py-1\">\n <label className=\"og-model-policy-search flex items-center gap-2 px-2.5 py-0.5\">\n <SearchIcon className=\"size-4 shrink-0 text-og-fg-subtle\" aria-hidden />\n <input\n aria-label={messages.searchLabel}\n placeholder={messages.searchPlaceholder}\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== \"Escape\" && event.key !== \"Tab\") event.stopPropagation();\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n event.currentTarget\n .closest('[data-testid=\"model-picker-menu\"]')\n ?.querySelector<HTMLButtonElement>(\"button:not(:disabled)\")\n ?.focus();\n }\n }}\n className=\"og-model-policy-search-input h-8 min-w-0 flex-1 bg-transparent text-og-menu text-og-fg outline-hidden placeholder:text-og-fg-subtle\"\n />\n </label>\n </div>\n ) : null}\n {props.error ? (\n <p className={cn(MENU_NOTE_CLASS, \"text-og-sm text-og-status-failed\")} role=\"alert\">\n {props.error}\n </p>\n ) : null}\n <div\n className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain py-1\"\n data-testid=\"model-picker-models\"\n >\n {props.loading ? (\n // Rows at the menu's row height, never a sentence, so nothing jumps.\n <div role=\"status\" aria-label={messages.loading}>\n {[58, 44, 66, 50].map((width) => (\n <div key={width} aria-hidden=\"true\" className=\"flex h-8 items-center gap-3 px-2.5\">\n <span className=\"size-4 shrink-0 animate-pulse rounded bg-og-surface-2\" />\n <span\n className=\"h-2.5 animate-pulse rounded bg-og-surface-2\"\n style={{ width: `${width}%` }}\n />\n </div>\n ))}\n </div>\n ) : rows.some((row) => row.selectable) ? (\n <>\n {groups.map((group) => (\n <section\n key={group.billingClass}\n aria-label={group.label}\n className=\"[&+section]:mt-1.5 [&+section]:border-t [&+section]:border-og-border [&+section]:pt-1.5\"\n >\n <div className={cn(MENU_LABEL_CLASS, \"flex items-center gap-2\")}>\n <BillingClassMark\n billingClass={group.billingClass}\n presentation={groupPresentationFor(props, group.billingClass)}\n aria-label=\"\"\n />\n <span className=\"min-w-0 break-words\">{group.label}</span>\n </div>\n {group.description ? (\n <p className=\"break-words px-2.5 pb-1 text-og-sm text-og-fg-muted\">\n {group.description}\n </p>\n ) : null}\n {group.rows.map(modelRow)}\n </section>\n ))}\n {filtered.length === 0 ? (\n <p className={MENU_NOTE_CLASS}>\n {words.length ? messages.noMatches : messages.noModels}\n </p>\n ) : null}\n </>\n ) : props.connectModelsHref || rows.length > 0 ? (\n <ConnectModelsPanel href={props.connectModelsHref} messages={messages} />\n ) : (\n <p className={MENU_NOTE_CLASS}>{messages.noModels}</p>\n )}\n </div>\n {rows.some((row) => row.selectable) &&\n selected &&\n ((props.hasImageAttachments &&\n selected.catalog.capabilities?.inputModalities.includes(\"image\") === false) ||\n (effortOptionsForModel(selected.catalog).length > 1 &&\n selected.catalog.capabilities?.reasoning.runnable !== false) ||\n (props.allowLatencyMode !== false &&\n runnableLatencyModesForModel(selected.catalog).includes(\"fast\"))) ? (\n <div className=\"border-t border-og-border px-2.5 py-2.5\">\n {props.hasImageAttachments &&\n selected.catalog.capabilities?.inputModalities.includes(\"image\") === false ? (\n <p className=\"pb-1.5 text-og-control leading-relaxed text-og-fg-subtle\">\n {messages.unsupportedAttachments}\n </p>\n ) : null}\n <ModelThinkingControls {...props} />\n </div>\n ) : null}\n </div>\n );\n}\n\nconst CONNECT_MODEL_OPTIONS: Array<{\n billingClass: \"codex_subscription\" | \"supergrok_subscription\" | \"byok\" | \"external\";\n title: string;\n detail: string;\n}> = [\n { billingClass: \"codex_subscription\", title: \"Codex\", detail: \"ChatGPT\" },\n { billingClass: \"supergrok_subscription\", title: \"SuperGrok\", detail: \"xAI\" },\n { billingClass: \"byok\", title: \"AI Gateway\", detail: \"Vercel\" },\n { billingClass: \"external\", title: \"OpenRouter\", detail: \"API key\" },\n];\n\nfunction ConnectModelsPanel(props: {\n href: string | undefined;\n messages: typeof defaultModelPolicyPickerMessages;\n}) {\n return (\n <div className=\"px-1.5 pb-1.5 pt-2\" data-testid=\"model-picker-connect\">\n <div className=\"px-1.5 pb-3\">\n <p className=\"text-og-menu font-medium tracking-tight text-og-fg\">\n {props.messages.connectTitle}\n </p>\n <p className=\"mt-1 text-og-control leading-snug text-og-fg-subtle\">\n {props.messages.connectBody}\n </p>\n </div>\n <div className=\"grid grid-cols-2 gap-1\">\n {CONNECT_MODEL_OPTIONS.map((option) => {\n const content = (\n <>\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-og-md bg-og-surface-2 text-og-fg\">\n <BillingClassMark\n billingClass={option.billingClass}\n aria-label=\"\"\n className=\"size-4 text-og-fg\"\n />\n </span>\n <span className=\"min-w-0\">\n <span className=\"block truncate text-og-control font-medium text-og-fg\">\n {option.title}\n </span>\n <span className=\"block truncate text-og-control text-og-fg-subtle\">\n {option.detail}\n </span>\n </span>\n </>\n );\n const className =\n \"flex min-w-0 items-center gap-2 rounded-og-md px-1.5 py-1.5 outline-hidden transition-colors hover:bg-og-hover focus-visible:ring-2 focus-visible:ring-og-accent/40\";\n return props.href ? (\n <a key={option.title} href={props.href} className={className}>\n {content}\n </a>\n ) : (\n <div key={option.title} className={className}>\n {content}\n </div>\n );\n })}\n </div>\n {props.href ? (\n <a\n href={props.href}\n className=\"mt-2 flex h-9 w-full items-center justify-center gap-1.5 rounded-og-md border border-og-primary-border bg-og-primary text-og-primary-fg hover:bg-og-primary-hover text-og-control font-medium outline-hidden transition-colors focus-visible:ring-2 focus-visible:ring-og-accent/40\"\n >\n {props.messages.connectAction}\n <ArrowRightIcon className=\"size-3.5\" aria-hidden />\n </a>\n ) : (\n <p className=\"px-1.5 pt-3 text-og-control text-og-fg-subtle\">{props.messages.noModels}</p>\n )}\n </div>\n );\n}\n\nfunction ModelThinkingControls(props: ModelPolicyPickerProps) {\n const selected = findPickerRow(effectiveRows(props), props.model);\n if (!selected) return null;\n const efforts = effortOptionsForModel(selected.catalog);\n const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };\n const supportsFast = runnableLatencyModesForModel(selected.catalog).includes(\"fast\");\n const showThinking =\n efforts.length > 1 && selected.catalog.capabilities?.reasoning.runnable !== false;\n const showFast = supportsFast && props.allowLatencyMode !== false;\n if (!showThinking && !showFast) return null;\n return (\n <div className=\"space-y-2 text-og-control\" data-testid=\"model-picker-reasoning\">\n <div className=\"flex min-h-7 items-center justify-between gap-3\">\n {showThinking ? <span className=\"text-og-fg-subtle\">{messages.thinking}</span> : <span />}\n {showFast ? (\n <button\n type=\"button\"\n data-testid=\"model-picker-fast\"\n disabled={props.disabled || !selected.selectable}\n aria-pressed={props.latencyMode === \"fast\"}\n title={messages.fast + \" · \" + messages.fastRateHint}\n onClick={() =>\n props.onLatencyModeChange(props.latencyMode === \"fast\" ? \"standard\" : \"fast\")\n }\n className=\"flex min-h-8 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-og-sm px-2 text-og-fg-muted hover:bg-og-hover focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:opacity-50\"\n >\n <ZapIcon className={cn(\"size-3.5\", props.latencyMode === \"fast\" && \"fill-current\")} />\n {messages.fast}\n <span className=\"text-og-fg-subtle\">{messages.fastRateHint}</span>\n </button>\n ) : null}\n </div>\n {showThinking ? (\n <RadioGroup.Root\n aria-label={messages.thinkingEffort}\n value={coerceReasoningEffortForModel(selected.catalog, props.effort)}\n disabled={props.disabled || !selected.selectable}\n onValueChange={(value) => props.onEffortChange(value as ReasoningEffort)}\n orientation=\"horizontal\"\n className=\"flex flex-wrap gap-0.5 rounded-og-md bg-og-surface-2 p-0.5\"\n >\n {efforts.map((effort) => (\n <RadioGroup.Item\n key={effort}\n value={effort}\n aria-label={labelReasoningEffort(effort)}\n title={labelReasoningEffort(effort)}\n className=\"min-h-8 min-w-10 flex-1 cursor-pointer whitespace-nowrap rounded-og-sm px-1.5 text-og-control text-og-fg-muted outline-hidden transition-colors hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40 data-[state=checked]:bg-og-surface-1 data-[state=checked]:text-og-fg data-[state=checked]:shadow-sm disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {effort === \"xhigh\" ? \"X-high\" : labelReasoningEffort(effort)}\n </RadioGroup.Item>\n ))}\n </RadioGroup.Root>\n ) : null}\n </div>\n );\n}\n\nexport function ModelPolicyPickerPopover(\n props: ModelPolicyPickerProps & {\n anchor: RefObject<HTMLButtonElement | null>;\n contentId: string;\n portalStyle: CSSProperties;\n },\n) {\n const outside = useRef(false);\n const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };\n return (\n <Popover.Root open onOpenChange={(open) => props.onOpenChange?.(open)}>\n <Popover.Anchor virtualRef={props.anchor} />\n <Popover.Portal>\n <Popover.Content\n id={props.contentId}\n aria-label={messages.label}\n align=\"start\"\n side={props.menuSide ?? \"bottom\"}\n sideOffset={8}\n collisionPadding={12}\n onInteractOutside={(event) => {\n if (props.anchor.current?.contains(event.target as Node)) event.preventDefault();\n else outside.current = true;\n }}\n onCloseAutoFocus={(event) => {\n event.preventDefault();\n if (!outside.current) props.anchor.current?.focus();\n }}\n className={cn(\n MENU_SURFACE_CLASS,\n \"og-root og-model-policy-menu z-50 flex max-h-[min(32rem,var(--radix-popover-content-available-height))] w-[22rem] max-w-[calc(100vw-16px)] flex-col overflow-hidden p-[var(--og-model-picker-menu-padding)]\",\n props.contentClassName,\n )}\n style={{ ...props.portalStyle, ...props.contentStyle }}\n data-testid=\"model-picker-content\"\n >\n <ModelPolicyPickerMenu {...props} />\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  buildTimeline,
3
3
  groupTimeline,
4
4
  sessionStatusFromEvents
5
- } from "./chunk-DO544OSV.js";
5
+ } from "./chunk-RC6BZ44Q.js";
6
6
  import {
7
7
  useDebouncedCallback,
8
8
  useMutationRunner,
@@ -834,7 +834,7 @@ function useSessionEvents(sessionId, options = {}) {
834
834
  let generation = navigationGenerationRef.current;
835
835
  const current = () => ownsNavigation() && generation === navigationGenerationRef.current;
836
836
  try {
837
- const { resolveLatestQuestion } = await import("./latest-question-HTRIS4KJ.js");
837
+ const { resolveLatestQuestion } = await import("./latest-question-YTOBHCWR.js");
838
838
  if (!current()) return null;
839
839
  const destination = await resolveLatestQuestion({
840
840
  client,
@@ -2326,4 +2326,4 @@ export {
2326
2326
  projectPendingApprovals,
2327
2327
  conversationTimeline
2328
2328
  };
2329
- //# sourceMappingURL=chunk-JIQYUGVL.js.map
2329
+ //# sourceMappingURL=chunk-X34BSY2R.js.map
@@ -1,12 +1,12 @@
1
1
  import { type ModelDisplayInput } from "@opengeni/sdk/model-display";
2
- import type { ReactNode, SVGProps } from "react";
2
+ import { type ReactNode, type SVGProps } from "react";
3
3
  /** ChatGPT / OpenAI mark (Simple Icons path), currentColor like the other marks. */
4
4
  export declare function ChatGptMark(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
5
5
  /** True when `ModelMark` has a real maker logo for this model (not the neutral fallback). */
6
6
  export declare function modelHasMark(model: ModelDisplayInput): boolean;
7
7
  /**
8
- * The model maker's logo (OpenAI, Claude, Grok), never the connection that
9
- * serves it. Makers without a bundled logo get a neutral mark, so an org- and
8
+ * The catalog's maker logo, with bundled maker logos and a neutral fallback.
9
+ * Makers without a configured or bundled logo get a neutral mark, so an org- and
10
10
  * a workspace-connected copy of one model always look the same.
11
11
  */
12
12
  export declare function ModelMark(props: {
package/dist/composer.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ModelPolicyPickerMenu
3
- } from "./chunk-WGXVEJO5.js";
3
+ } from "./chunk-SGKD2OCN.js";
4
4
  import {
5
5
  Actions,
6
6
  AttachButton,
@@ -41,7 +41,7 @@ import {
41
41
  useChatComposer,
42
42
  useChatComposerController,
43
43
  useVoiceInput
44
- } from "./chunk-EN3YTHKR.js";
44
+ } from "./chunk-CY7WK5FD.js";
45
45
  import "./chunk-RSI7XYXL.js";
46
46
  import "./chunk-BRM6FVZA.js";
47
47
  import {
@@ -51,9 +51,9 @@ import {
51
51
  PickerBackHeader,
52
52
  PickerNavRow,
53
53
  defaultModelPolicyPickerMessages
54
- } from "./chunk-26ZNDDLZ.js";
54
+ } from "./chunk-LNIRAOA3.js";
55
55
  import "./chunk-K3HQAKQX.js";
56
- import "./chunk-RLUSC2WA.js";
56
+ import "./chunk-5F4WR7X7.js";
57
57
  import "./chunk-VZTQ73XT.js";
58
58
  import "./chunk-UTRTG6LH.js";
59
59
  import "./chunk-YQEAA6LT.js";
package/dist/index.js CHANGED
@@ -107,7 +107,7 @@ import {
107
107
  useWorkspaceSessions,
108
108
  userMessageLikelyNeedsDisclosure,
109
109
  v4aToGitFileDiff
110
- } from "./chunk-6TROCOEK.js";
110
+ } from "./chunk-4OTO7KBK.js";
111
111
  import {
112
112
  useThemeType
113
113
  } from "./chunk-UK4TINKT.js";
@@ -140,14 +140,14 @@ import {
140
140
  useSessionControl,
141
141
  useSessionEvents,
142
142
  useTurnQueue
143
- } from "./chunk-JIQYUGVL.js";
143
+ } from "./chunk-X34BSY2R.js";
144
144
  import {
145
145
  buildTimeline,
146
146
  creditExhaustedFromEvents,
147
147
  extractSessionRef,
148
148
  groupTimeline,
149
149
  sessionStatusFromEvents
150
- } from "./chunk-DO544OSV.js";
150
+ } from "./chunk-RC6BZ44Q.js";
151
151
  import {
152
152
  toolDisplayName
153
153
  } from "./chunk-ERGYJIBB.js";
@@ -202,7 +202,7 @@ import {
202
202
  import "./chunk-RHREZ7BO.js";
203
203
  import {
204
204
  ModelPolicyPickerMenu
205
- } from "./chunk-WGXVEJO5.js";
205
+ } from "./chunk-SGKD2OCN.js";
206
206
  import {
207
207
  CommandPalette,
208
208
  ComposerTranscriptionControl,
@@ -235,7 +235,7 @@ import {
235
235
  useSlashCommands,
236
236
  useTranscription,
237
237
  useVoiceInput
238
- } from "./chunk-EN3YTHKR.js";
238
+ } from "./chunk-CY7WK5FD.js";
239
239
  import {
240
240
  FILE_ONLY_MESSAGE_TEXT,
241
241
  composeSendInput,
@@ -265,7 +265,7 @@ import {
265
265
  PickerNavRow,
266
266
  defaultModelPolicyPickerMessages,
267
267
  modelHasMark
268
- } from "./chunk-26ZNDDLZ.js";
268
+ } from "./chunk-LNIRAOA3.js";
269
269
  import "./chunk-K3HQAKQX.js";
270
270
  import {
271
271
  advancedSourceSummary,
@@ -285,7 +285,7 @@ import {
285
285
  projectPickerRows,
286
286
  runnableLatencyModesForModel,
287
287
  sortPickerRows
288
- } from "./chunk-RLUSC2WA.js";
288
+ } from "./chunk-5F4WR7X7.js";
289
289
  import "./chunk-VZTQ73XT.js";
290
290
  import {
291
291
  COMPOSER_MODEL_UNAVAILABLE_MESSAGE,
@@ -2,7 +2,7 @@ import {
2
2
  isTimelineUserQuestion,
3
3
  isTurnExecutionEvidence,
4
4
  timelineQuestionPlacement
5
- } from "./chunk-DO544OSV.js";
5
+ } from "./chunk-RC6BZ44Q.js";
6
6
  import "./chunk-ERGYJIBB.js";
7
7
  import "./chunk-UTRTG6LH.js";
8
8
 
@@ -114,4 +114,4 @@ export {
114
114
  TIMELINE_TURN_ANCHOR_EVENT_TYPES,
115
115
  resolveLatestQuestion
116
116
  };
117
- //# sourceMappingURL=latest-question-HTRIS4KJ.js.map
117
+ //# sourceMappingURL=latest-question-YTOBHCWR.js.map
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  ModelPolicyPickerMenu,
3
3
  ModelPolicyPickerPopover
4
- } from "./chunk-WGXVEJO5.js";
5
- import "./chunk-26ZNDDLZ.js";
4
+ } from "./chunk-SGKD2OCN.js";
5
+ import "./chunk-LNIRAOA3.js";
6
6
  import "./chunk-K3HQAKQX.js";
7
- import "./chunk-RLUSC2WA.js";
7
+ import "./chunk-5F4WR7X7.js";
8
8
  import "./chunk-VZTQ73XT.js";
9
9
  import "./chunk-22KP6LR5.js";
10
10
  export {
11
11
  ModelPolicyPickerMenu,
12
12
  ModelPolicyPickerPopover
13
13
  };
14
- //# sourceMappingURL=model-policy-picker-menu-M54ISBMN.js.map
14
+ //# sourceMappingURL=model-policy-picker-menu-DXHUL73H.js.map
@@ -10,6 +10,7 @@ export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogM
10
10
  billingClassLabel: string;
11
11
  selectable: boolean;
12
12
  unavailableReason: string | null;
13
+ fundingHint?: string | undefined;
13
14
  provider: string;
14
15
  providerLabel: string;
15
16
  catalog: TCatalog;
@@ -19,7 +19,7 @@ import {
19
19
  runnableLatencyModesForModel,
20
20
  scopedBillingClassLabel,
21
21
  sortPickerRows
22
- } from "./chunk-RLUSC2WA.js";
22
+ } from "./chunk-5F4WR7X7.js";
23
23
  export {
24
24
  advancedSourceSummary,
25
25
  availabilityReasonLabel,
package/dist/realtime.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  PickerAnimatedPage,
4
4
  PickerBackHeader,
5
5
  PickerNavRow
6
- } from "./chunk-26ZNDDLZ.js";
6
+ } from "./chunk-LNIRAOA3.js";
7
7
  import {
8
8
  MENU_CHECK_CLASS,
9
9
  MENU_CHEVRON_CLASS,
@@ -11,7 +11,7 @@ import {
11
11
  MENU_SEPARATOR_CLASS,
12
12
  MENU_SURFACE_CLASS
13
13
  } from "./chunk-K3HQAKQX.js";
14
- import "./chunk-RLUSC2WA.js";
14
+ import "./chunk-5F4WR7X7.js";
15
15
  import {
16
16
  usePortalTokenSource,
17
17
  usePortalTokenStyle
@@ -24,7 +24,7 @@ import {
24
24
  setStartupDetails,
25
25
  useStartupDetails,
26
26
  userMessageLikelyNeedsDisclosure
27
- } from "./chunk-6TROCOEK.js";
27
+ } from "./chunk-4OTO7KBK.js";
28
28
  import "./chunk-UK4TINKT.js";
29
29
  import "./chunk-J2QWVSQY.js";
30
30
  import "./chunk-C5X5AJXK.js";
@@ -36,8 +36,8 @@ import {
36
36
  } from "./chunk-5CVXZA3Y.js";
37
37
  import {
38
38
  createOlderHistoryLoadReceipt
39
- } from "./chunk-JIQYUGVL.js";
40
- import "./chunk-DO544OSV.js";
39
+ } from "./chunk-X34BSY2R.js";
40
+ import "./chunk-RC6BZ44Q.js";
41
41
  import "./chunk-ERGYJIBB.js";
42
42
  import {
43
43
  OpenGeniProvider
@@ -54,12 +54,12 @@ import {
54
54
  } from "./chunk-XCHWDJ22.js";
55
55
  import "./chunk-A2PI6UMY.js";
56
56
  import "./chunk-JVYQRRC4.js";
57
- import "./chunk-EN3YTHKR.js";
57
+ import "./chunk-CY7WK5FD.js";
58
58
  import "./chunk-RSI7XYXL.js";
59
59
  import "./chunk-BRM6FVZA.js";
60
- import "./chunk-26ZNDDLZ.js";
60
+ import "./chunk-LNIRAOA3.js";
61
61
  import "./chunk-K3HQAKQX.js";
62
- import "./chunk-RLUSC2WA.js";
62
+ import "./chunk-5F4WR7X7.js";
63
63
  import "./chunk-VZTQ73XT.js";
64
64
  import "./chunk-UTRTG6LH.js";
65
65
  import "./chunk-B3V6TG77.js";
package/dist/session.js CHANGED
@@ -23,14 +23,14 @@ import {
23
23
  useSessionControl,
24
24
  useSessionEvents,
25
25
  useTurnQueue
26
- } from "./chunk-JIQYUGVL.js";
26
+ } from "./chunk-X34BSY2R.js";
27
27
  import {
28
28
  buildTimeline,
29
29
  creditExhaustedFromEvents,
30
30
  extractSessionRef,
31
31
  groupTimeline,
32
32
  sessionStatusFromEvents
33
- } from "./chunk-DO544OSV.js";
33
+ } from "./chunk-RC6BZ44Q.js";
34
34
  import {
35
35
  toolDisplayName
36
36
  } from "./chunk-ERGYJIBB.js";
@@ -26,9 +26,11 @@ export type GenieLoadingOptions = {
26
26
  };
27
27
  export declare const GenieLoadingOptionsContext: import("react").Context<GenieLoadingOptions | undefined>;
28
28
  /** Decorative copy never substitutes for a failure or claims measurable progress. */
29
- export declare function GenieLoading({ startedAt, phase, onShowDetails, detailsOpen, }: {
29
+ export declare function GenieLoading({ startedAt, phase, onShowDetails, detailsOpen, notice, }: {
30
30
  startedAt: string;
31
31
  phase?: GenieLoadingRenderProps["phase"];
32
32
  onShowDetails: () => void;
33
33
  detailsOpen?: boolean;
34
+ /** An actionable startup problem takes precedence over decorative copy. */
35
+ notice?: string | undefined;
34
36
  }): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
@@ -0,0 +1,5 @@
1
+ import type { Session } from "@opengeni/sdk";
2
+ /** Live dispatch diagnostics belong to the current loading area, never history. */
3
+ export declare function StartupDispatchDetails({ wait }: {
4
+ wait: Session["dispatchWait"];
5
+ }): import("react").JSX.Element;
@@ -1,4 +1,4 @@
1
- import type { AllowanceExhaustedRefusal, HumanInputQuestion, HumanInputResponse, MediaGenerationResult, ResourceRef, SessionStatus, TimelineAnnotation, TimelineAnnotationSource, ToolAuthNeededPayload, ToolRef, ToolDisplayMetadata } from "@opengeni/sdk";
1
+ import type { AllowanceExhaustedRefusal, HumanInputQuestion, HumanInputResponse, MediaGenerationResult, ResourceRef, SessionStatus, Session, TimelineAnnotation, TimelineAnnotationSource, ToolAuthNeededPayload, ToolRef, ToolDisplayMetadata } from "@opengeni/sdk";
2
2
  export type TimelineAnnotationSourceDescriptor = Omit<TimelineAnnotationSource, "startOffset" | "endOffset" | "contextBefore" | "contextAfter"> & {
3
3
  text: string;
4
4
  };
@@ -179,6 +179,10 @@ export type StartupPhaseItem = {
179
179
  id: string;
180
180
  turnId: string | null;
181
181
  phase: StartupPhase;
182
+ /** Live presentation only: accepted work waiting for its first worker claim. */
183
+ dispatchWait?: Session["dispatchWait"];
184
+ /** Renderer-only elapsed anchor when acceptance preceded recorded startup spans. */
185
+ loadingStartedAt?: string | undefined;
182
186
  status: "running" | "complete" | "failed" | "cancelled";
183
187
  startedAt: string;
184
188
  completedAt: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/react",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -153,8 +153,8 @@
153
153
  "@dnd-kit/utilities": "3.2.2",
154
154
  "@fontsource-variable/noto-sans-arabic": "5.2.10",
155
155
  "@fontsource-variable/noto-sans-jp": "5.2.10",
156
- "@opengeni/connect": "1.1.0",
157
- "@opengeni/sdk": "1.1.0",
156
+ "@opengeni/connect": "1.2.0",
157
+ "@opengeni/sdk": "1.2.0",
158
158
  "clsx": "^2.1.1",
159
159
  "dequal": "2.0.3",
160
160
  "lucide-react": "^1.8.0",
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  modelDisplayName,
3
+ modelLogoUrl,
3
4
  modelVendor,
4
5
  type ModelDisplayInput,
5
6
  type ModelVendor,
6
7
  } from "@opengeni/sdk/model-display";
7
8
  import { SparklesIcon } from "lucide-react";
8
- import type { ReactNode, SVGProps } from "react";
9
+ import { useState, type ReactNode, type SVGProps } from "react";
9
10
  import { cn } from "../lib/cn";
10
11
  import { ClaudeMark } from "./claude-mark";
11
12
  import { GrokMark } from "./grok-mark";
@@ -24,12 +25,12 @@ const MARKED_VENDORS: ReadonlySet<ModelVendor> = new Set(["openai", "anthropic",
24
25
  /** True when `ModelMark` has a real maker logo for this model (not the neutral fallback). */
25
26
  export function modelHasMark(model: ModelDisplayInput): boolean {
26
27
  const vendor = modelVendor(model);
27
- return vendor !== null && MARKED_VENDORS.has(vendor);
28
+ return modelLogoUrl(model) !== null || (vendor !== null && MARKED_VENDORS.has(vendor));
28
29
  }
29
30
 
30
31
  /**
31
- * The model maker's logo (OpenAI, Claude, Grok), never the connection that
32
- * serves it. Makers without a bundled logo get a neutral mark, so an org- and
32
+ * The catalog's maker logo, with bundled maker logos and a neutral fallback.
33
+ * Makers without a configured or bundled logo get a neutral mark, so an org- and
33
34
  * a workspace-connected copy of one model always look the same.
34
35
  */
35
36
  export function ModelMark(props: {
@@ -39,12 +40,22 @@ export function ModelMark(props: {
39
40
  fallback?: ReactNode;
40
41
  "aria-label"?: string | undefined;
41
42
  }) {
43
+ const logoUrl = modelLogoUrl(props.model);
44
+ const [failedUrl, setFailedUrl] = useState<string | null>(null);
42
45
  const vendor = modelVendor(props.model);
43
46
  const className = cn("size-3.5 shrink-0", props.className);
44
47
  const label = props["aria-label"];
45
48
  const a11y = label ? { role: "img" as const, "aria-label": label } : { "aria-hidden": true };
46
49
  const mark =
47
- vendor === "openai" ? (
50
+ logoUrl && logoUrl !== failedUrl ? (
51
+ <img
52
+ src={logoUrl}
53
+ alt=""
54
+ className="size-full object-contain"
55
+ referrerPolicy="no-referrer"
56
+ onError={() => setFailedUrl(logoUrl)}
57
+ />
58
+ ) : vendor === "openai" ? (
48
59
  <ChatGptMark className="size-full" />
49
60
  ) : vendor === "anthropic" ? (
50
61
  <ClaudeMark className="size-full" />
@@ -94,9 +94,9 @@ export function ModelPolicyPickerMenu(props: ModelPolicyPickerProps) {
94
94
  )
95
95
  }
96
96
  label={row.label}
97
- hint={row.unavailableReason ?? undefined}
97
+ hint={row.unavailableReason ?? row.fundingHint}
98
98
  disabled={props.disabled || !row.selectable}
99
- title={[row.label, row.unavailableReason].filter(Boolean).join(" · ")}
99
+ title={[row.label, row.unavailableReason ?? row.fundingHint].filter(Boolean).join(" · ")}
100
100
  active={row.selectable && row.id === props.model}
101
101
  showChevron={false}
102
102
  trailing={
@@ -1,7 +1,7 @@
1
1
  import { ClaudeMark } from "./claude-mark";
2
2
  import { GrokMark } from "./grok-mark";
3
3
  import { ChatGptMark, ModelMark, modelHasMark } from "./model-mark";
4
- import { modelDisplayName } from "@opengeni/sdk/model-display";
4
+ import { modelDisplayName, modelLogoUrl } from "@opengeni/sdk/model-display";
5
5
  import type { ClientModel, LatencyMode, ReasoningEffort } from "@opengeni/sdk";
6
6
  import {
7
7
  ChevronDownIcon,
@@ -260,8 +260,8 @@ function applyCodexOnly(
260
260
 
261
261
  /**
262
262
  * The trigger shows who makes the chosen model. Subscription rails already
263
- * carry their maker's mark (ChatGPT, Claude, Grok), and explicit host branding
264
- * for a payment group always wins; otherwise API-key and deployment models show
263
+ * carry their maker's mark (ChatGPT, Claude, Grok). Configured catalog logos win
264
+ * in collapsed model presentation; otherwise explicit host branding wins and models show
265
265
  * the maker's logo instead of a generic key, falling back to the group mark.
266
266
  */
267
267
  function SelectedModelMark(input: {
@@ -282,6 +282,9 @@ function SelectedModelMark(input: {
282
282
  />
283
283
  );
284
284
  }
285
+ if (modelLogoUrl(model)) {
286
+ return <ModelMark model={model} className="text-og-fg" />;
287
+ }
285
288
  if (
286
289
  presentation?.icon !== undefined ||
287
290
  BRANDED_BILLING_CLASSES.has(billingClass) ||
@@ -463,6 +466,9 @@ export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
463
466
  const selected = findPickerRow(rows, props.model);
464
467
  const needsModel =
465
468
  !rows.some((row) => row.selectable) && (rows.length > 0 || Boolean(props.connectModelsHref));
469
+ const selectedDescription = needsModel
470
+ ? messages.connectTitle
471
+ : [selected?.label ?? fallbackName(props), labelReasoningEffort(props.effort)].join(" · ");
466
472
 
467
473
  if (props.loading) {
468
474
  return (
@@ -491,6 +497,7 @@ export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
491
497
  onClick={() => setOpen(!open)}
492
498
  disabled={props.disabled}
493
499
  aria-label={messages.label}
500
+ aria-description={selectedDescription}
494
501
  className={cn(
495
502
  "og-root inline-flex h-8 min-w-[180px] max-w-full items-center gap-2 rounded-og-md border border-og-border bg-og-surface px-2.5 text-sm 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",
496
503
  props.className,
@@ -537,6 +544,7 @@ export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
537
544
  onClick={() => setOpen(!open)}
538
545
  disabled={props.disabled}
539
546
  aria-label={messages.label}
547
+ aria-description={selectedDescription}
540
548
  className={cn(
541
549
  "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 px-2.5 text-og-control outline-hidden transition-colors 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",
542
550
  // With no usable model the pill is the one thing that unblocks the
@@ -421,6 +421,9 @@ function Conversation({
421
421
  error={catalog.error?.message}
422
422
  disabled={terminal}
423
423
  sessionKey={sessionId}
424
+ onOpenChange={(open) => {
425
+ if (open) void catalog.refresh();
426
+ }}
424
427
  onModelChange={(model) => composer.setModel?.(model)}
425
428
  onEffortChange={(effort) => composer.setReasoningEffort?.(effort)}
426
429
  onLatencyModeChange={(mode) => composer.setLatencyMode?.(mode)}
@@ -17,6 +17,7 @@ export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogM
17
17
  billingClassLabel: string;
18
18
  selectable: boolean;
19
19
  unavailableReason: string | null;
20
+ fundingHint?: string | undefined;
20
21
  provider: string;
21
22
  providerLabel: string;
22
23
  catalog: TCatalog;
@@ -274,6 +275,14 @@ export function projectPickerRows(models: WorkspaceModelCatalogModel[]): PickerM
274
275
  billingClass,
275
276
  billingClassLabel: billingClassLabel(billingClass),
276
277
  selectable: catalog.availability.selectable,
278
+ fundingHint:
279
+ catalog.creditFunding === "promotional"
280
+ ? "Free credits"
281
+ : catalog.creditFunding === "general"
282
+ ? "Uses credits"
283
+ : catalog.creditFunding === "unavailable"
284
+ ? "Needs credits"
285
+ : undefined,
277
286
  unavailableReason: catalog.availability.selectable
278
287
  ? null
279
288
  : availabilityReasonLabel(catalog.availability.reason),
@@ -1,6 +1,7 @@
1
1
  import { KnowledgeReceiptRow } from "./knowledge-receipt";
2
2
  import { AnimatePresence, motion, useReducedMotion } from "motion/react";
3
3
  import { GenieLoading } from "./genie-loading";
4
+ import { StartupDispatchDetails } from "./startup-dispatch-details";
4
5
  import { useStartupDetails } from "./startup-preference";
5
6
  import { ArrowRightIcon, BotIcon, BrainCircuitIcon, MessageSquareTextIcon } from "lucide-react";
6
7
  import {
@@ -94,6 +95,7 @@ export function ActivityRail({
94
95
  const reducedMotion = useReducedMotion();
95
96
  const [detailsOpen, setDetailsOpen] = useState(false);
96
97
  const phases = items.filter((item) => item.kind === "startup-phase");
98
+ const dispatch = phases.find((item) => item.dispatchWait !== undefined);
97
99
  // Empty reasoning envelopes can arrive before any visible model output.
98
100
  const hasWork = items.some(
99
101
  (item) =>
@@ -118,10 +120,10 @@ export function ActivityRail({
118
120
  ? item.status === "failed" || item.status === "cancelled"
119
121
  : item.kind !== "reasoning" || item.text.trim().length > 0,
120
122
  );
121
- const startedAt = phases.reduce(
122
- (first, item) => (item.startedAt < first ? item.startedAt : first),
123
- phases[0]?.startedAt ?? "",
124
- );
123
+ const startedAt = phases.reduce((first, item) => {
124
+ const start = item.loadingStartedAt ?? item.startedAt;
125
+ return start < first ? start : first;
126
+ }, phases[0]?.startedAt ?? "");
125
127
  const enterMounted = useEntranceAnimation();
126
128
  // Live gate: rails born during bulk capture enter=false forever; with a
127
129
  // seen-id map we still want later live appends to fade (ids gate remounts).
@@ -183,6 +185,11 @@ export function ActivityRail({
183
185
  >
184
186
  <GenieLoading
185
187
  startedAt={startedAt}
188
+ notice={
189
+ dispatch?.dispatchWait?.lastError
190
+ ? "Unable to start yet. Your messages are saved."
191
+ : undefined
192
+ }
186
193
  phase={responsePending ? "waiting" : "preparing"}
187
194
  detailsOpen={detailsOpen}
188
195
  onShowDetails={() => setDetailsOpen((open) => !open)}
@@ -199,6 +206,9 @@ export function ActivityRail({
199
206
  Hide startup details
200
207
  </button>
201
208
  ) : null}
209
+ {dispatch && (detailsOpen || debug) ? (
210
+ <StartupDispatchDetails wait={dispatch.dispatchWait ?? null} />
211
+ ) : null}
202
212
  {visibleItems.map((item, index) => {
203
213
  const newFamily = index > 0 && familyOf(item) !== familyOf(visibleItems[index - 1]!);
204
214
  const row = renderActivity(