@opengeni/react 0.36.1 → 0.37.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 (42) hide show
  1. package/README.md +4 -0
  2. package/dist/{chunk-J2RVJ6JX.js → chunk-4IJCL7YO.js} +13 -2
  3. package/dist/chunk-4IJCL7YO.js.map +1 -0
  4. package/dist/{chunk-OKKMZDQF.js → chunk-FSPDND3P.js} +2 -2
  5. package/dist/{chunk-FT2TXNGZ.js → chunk-HJ4OQVGW.js} +174 -14
  6. package/dist/chunk-HJ4OQVGW.js.map +1 -0
  7. package/dist/{chunk-N4XEBE23.js → chunk-IP22SLO6.js} +432 -10
  8. package/dist/chunk-IP22SLO6.js.map +1 -0
  9. package/dist/{chunk-22RM4GMO.js → chunk-SJKT4TKW.js} +26 -2
  10. package/dist/chunk-SJKT4TKW.js.map +1 -0
  11. package/dist/{chunk-Z5FV355Z.js → chunk-UCZPNXV3.js} +91 -14
  12. package/dist/chunk-UCZPNXV3.js.map +1 -0
  13. package/dist/components/chat-composer.d.ts +3 -1
  14. package/dist/components/model-policy-picker.d.ts +78 -0
  15. package/dist/components/session-chrome.d.ts +1 -1
  16. package/dist/composer.d.ts +2 -0
  17. package/dist/composer.js +17 -3
  18. package/dist/hooks/use-composer.d.ts +9 -0
  19. package/dist/index.d.ts +3 -1
  20. package/dist/index.js +30 -9
  21. package/dist/index.js.map +1 -1
  22. package/dist/model-policy.d.ts +12 -4
  23. package/dist/model-policy.js +5 -1
  24. package/dist/session-ui.js +2 -2
  25. package/dist/session.js +3 -3
  26. package/dist/timeline/types.d.ts +5 -0
  27. package/package.json +2 -2
  28. package/src/components/chat-composer.tsx +8 -3
  29. package/src/components/model-policy-picker.tsx +582 -0
  30. package/src/components/session-chrome.tsx +138 -14
  31. package/src/composer.ts +13 -0
  32. package/src/hooks/use-composer.ts +213 -16
  33. package/src/index.ts +15 -0
  34. package/src/model-policy.ts +51 -7
  35. package/src/timeline/projection.ts +21 -1
  36. package/src/timeline/types.ts +5 -0
  37. package/dist/chunk-22RM4GMO.js.map +0 -1
  38. package/dist/chunk-FT2TXNGZ.js.map +0 -1
  39. package/dist/chunk-J2RVJ6JX.js.map +0 -1
  40. package/dist/chunk-N4XEBE23.js.map +0 -1
  41. package/dist/chunk-Z5FV355Z.js.map +0 -1
  42. /package/dist/{chunk-OKKMZDQF.js.map → chunk-FSPDND3P.js.map} +0 -0
@@ -53,7 +53,7 @@ function effortOptionsForModel(model) {
53
53
  if (!efforts || efforts.length === 0) {
54
54
  return ["low"];
55
55
  }
56
- const order = ["none", "minimal", "low", "medium", "high", "xhigh"];
56
+ const order = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
57
57
  return order.filter((effort) => efforts.includes(effort));
58
58
  }
59
59
  function defaultEffortForModel(model) {
@@ -84,6 +84,12 @@ function labelLatencyMode(mode) {
84
84
  }
85
85
  return "Standard";
86
86
  }
87
+ function labelReasoningEffort(effort) {
88
+ if (effort === "xhigh") {
89
+ return "Extra high";
90
+ }
91
+ return effort.slice(0, 1).toUpperCase() + effort.slice(1);
92
+ }
87
93
  function payerSummaryForModel(model) {
88
94
  const billing = model.billing;
89
95
  if (!billing) {
@@ -135,6 +141,22 @@ function projectPickerRows(models) {
135
141
  };
136
142
  });
137
143
  }
144
+ function projectClientModelRows(models) {
145
+ return models.map((catalog) => {
146
+ const billingClass = billingClassForModel(catalog);
147
+ return {
148
+ id: catalog.id,
149
+ label: catalog.label,
150
+ billingClass,
151
+ billingClassLabel: billingClassLabel(billingClass),
152
+ selectable: true,
153
+ unavailableReason: null,
154
+ provider: catalog.provider,
155
+ providerLabel: catalog.providerLabel,
156
+ catalog
157
+ };
158
+ });
159
+ }
138
160
  function sortPickerRows(rows) {
139
161
  return [...rows].sort((left, right) => {
140
162
  const classDelta = BILLING_CLASS_ORDER.indexOf(left.billingClass) - BILLING_CLASS_ORDER.indexOf(right.billingClass);
@@ -177,11 +199,13 @@ export {
177
199
  coerceReasoningEffortForModel,
178
200
  runnableLatencyModesForModel,
179
201
  labelLatencyMode,
202
+ labelReasoningEffort,
180
203
  payerSummaryForModel,
181
204
  advancedSourceSummary,
182
205
  projectPickerRows,
206
+ projectClientModelRows,
183
207
  sortPickerRows,
184
208
  findPickerRow,
185
209
  groupPickerRowsByBillingClass
186
210
  };
187
- //# sourceMappingURL=chunk-22RM4GMO.js.map
211
+ //# sourceMappingURL=chunk-SJKT4TKW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/model-policy.ts"],"sourcesContent":["import type { ClientModel, ReasoningEffort, WorkspaceModelCatalogModel } from \"@opengeni/sdk\";\n\nexport type PickerBillingClass = \"opengeni_credits\" | \"codex_subscription\" | \"byok\";\n\nexport type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {\n id: string;\n label: string;\n billingClass: PickerBillingClass;\n billingClassLabel: string;\n selectable: boolean;\n unavailableReason: string | null;\n provider: string;\n providerLabel: string;\n catalog: TCatalog;\n};\n\nexport type LatencyModeId = \"standard\" | \"priority\" | \"fast\";\n\nconst BILLING_CLASS_ORDER: PickerBillingClass[] = [\n \"opengeni_credits\",\n \"codex_subscription\",\n \"byok\",\n];\n\nconst BILLING_CLASS_LABELS: Record<PickerBillingClass, string> = {\n opengeni_credits: \"OpenGeni\",\n codex_subscription: \"Codex\",\n byok: \"Your Gateway\",\n};\n\nconst AVAILABILITY_REASON_LABELS: Record<string, string> = {\n missing_credential: \"Credentials required\",\n needs_reauth: \"Reconnect required\",\n credential_not_ready: \"Credential not ready\",\n not_entitled: \"Not entitled\",\n provider_unhealthy: \"Provider unavailable\",\n policy_blocked: \"Blocked by workspace policy\",\n unsupported: \"Unsupported\",\n};\n\nexport function billingClassForModel(model: ClientModel): PickerBillingClass {\n if (model.source === \"codex\") {\n return \"codex_subscription\";\n }\n if (model.source === \"workspace_gateway\") {\n return \"byok\";\n }\n if (model.credentialSource?.kind === \"connected_subscription\") {\n return \"codex_subscription\";\n }\n if (model.credentialSource?.kind === \"workspace_connection\") {\n return \"byok\";\n }\n if (model.billing?.upstreamPayer === \"connected_subscription\") {\n return \"codex_subscription\";\n }\n if (model.billing?.upstreamPayer === \"workspace\") {\n return \"byok\";\n }\n return \"opengeni_credits\";\n}\n\nexport function billingClassLabel(billingClass: PickerBillingClass): string {\n return BILLING_CLASS_LABELS[billingClass];\n}\n\nexport function availabilityReasonLabel(\n reason: WorkspaceModelCatalogModel[\"availability\"][\"reason\"],\n): string | null {\n if (!reason) {\n return null;\n }\n return AVAILABILITY_REASON_LABELS[reason] ?? \"Unavailable\";\n}\n\nexport function effortOptionsForModel(model: ClientModel): ReasoningEffort[] {\n const efforts = model.capabilities?.reasoning.efforts;\n if (!efforts || efforts.length === 0) {\n return [\"low\"];\n }\n const order: ReasoningEffort[] = [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n return order.filter((effort) => efforts.includes(effort));\n}\n\nexport function defaultEffortForModel(model: ClientModel): ReasoningEffort {\n const options = effortOptionsForModel(model);\n const configured = model.capabilities?.reasoning.defaultEffort;\n if (configured && options.includes(configured)) {\n return configured;\n }\n return options[0] ?? \"low\";\n}\n\nexport function coerceReasoningEffortForModel(\n model: ClientModel,\n effort: ReasoningEffort,\n): ReasoningEffort {\n const options = effortOptionsForModel(model);\n if (options.includes(effort)) {\n return effort;\n }\n return defaultEffortForModel(model);\n}\n\nexport function runnableLatencyModesForModel(model: ClientModel): LatencyModeId[] {\n const modes = model.capabilities?.latencyModes ?? [];\n return modes.filter((mode) => mode.runnable).map((mode) => mode.id);\n}\n\nexport function labelLatencyMode(mode: LatencyModeId): string {\n if (mode === \"fast\") {\n return \"Fast\";\n }\n if (mode === \"priority\") {\n return \"Priority\";\n }\n return \"Standard\";\n}\n\nexport function labelReasoningEffort(effort: ReasoningEffort): string {\n if (effort === \"xhigh\") {\n return \"Extra high\";\n }\n return effort.slice(0, 1).toUpperCase() + effort.slice(1);\n}\n\nexport function payerSummaryForModel(model: ClientModel): string {\n const billing = model.billing;\n if (!billing) {\n return \"Route unknown\";\n }\n if (billing.metering === \"opengeni_credits\") {\n return \"OpenGeni credits · automatic managed route\";\n }\n if (billing.upstreamPayer === \"connected_subscription\") {\n return \"Codex subscription · external billing\";\n }\n if (billing.upstreamPayer === \"workspace\") {\n return \"Billed to your AI Gateway\";\n }\n return \"Deployment route · external billing\";\n}\n\nexport function advancedSourceSummary(model: ClientModel): string | null {\n const source = model.credentialSource;\n if (!source) {\n return null;\n }\n if (source.kind === \"connected_subscription\") {\n return \"Connected Codex subscription\";\n }\n if (source.kind === \"workspace_connection\") {\n return \"Workspace AI Gateway\";\n }\n if (source.kind === \"deployment\") {\n return source.mechanism === \"azure_ad_bearer\"\n ? \"Deployment Azure identity\"\n : \"Deployment API key\";\n }\n return null;\n}\n\nexport function projectPickerRows(models: WorkspaceModelCatalogModel[]): PickerModelRow[] {\n return models\n .filter((catalog) => {\n const billingClass = billingClassForModel(catalog);\n return billingClass === \"opengeni_credits\" || catalog.credentialReadiness.status === \"ready\";\n })\n .map((catalog) => {\n const billingClass = billingClassForModel(catalog);\n return {\n id: catalog.id,\n label: catalog.label,\n billingClass,\n billingClassLabel: billingClassLabel(billingClass),\n selectable: catalog.availability.selectable,\n unavailableReason: catalog.availability.selectable\n ? null\n : availabilityReasonLabel(catalog.availability.reason),\n provider: catalog.provider,\n providerLabel: catalog.providerLabel,\n catalog,\n };\n });\n}\n\n/** Project the lightweight client-config model list into the same picker contract. */\nexport function projectClientModelRows(models: ClientModel[]): PickerModelRow<ClientModel>[] {\n return models.map((catalog) => {\n const billingClass = billingClassForModel(catalog);\n return {\n id: catalog.id,\n label: catalog.label,\n billingClass,\n billingClassLabel: billingClassLabel(billingClass),\n selectable: true,\n unavailableReason: null,\n provider: catalog.provider,\n providerLabel: catalog.providerLabel,\n catalog,\n };\n });\n}\n\nexport function sortPickerRows<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n): PickerModelRow<TCatalog>[] {\n return [...rows].sort((left, right) => {\n const classDelta =\n BILLING_CLASS_ORDER.indexOf(left.billingClass) -\n BILLING_CLASS_ORDER.indexOf(right.billingClass);\n if (classDelta !== 0) {\n return classDelta;\n }\n if (left.selectable !== right.selectable) {\n return left.selectable ? -1 : 1;\n }\n return left.label.localeCompare(right.label);\n });\n}\n\nexport function findPickerRow<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n modelId: string,\n): PickerModelRow<TCatalog> | null {\n return rows.find((row) => row.id === modelId) ?? null;\n}\n\nexport function groupPickerRowsByBillingClass(\n rows: PickerModelRow[],\n): Array<{ billingClass: PickerBillingClass; label: string; rows: PickerModelRow[] }>;\nexport function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n): Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n}>;\nexport function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n): Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n}> {\n const sorted = sortPickerRows(rows);\n const groups: Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n }> = [];\n for (const row of sorted) {\n const existing = groups.find((group) => group.billingClass === row.billingClass);\n if (existing) {\n existing.rows.push(row);\n continue;\n }\n groups.push({\n billingClass: row.billingClass,\n label: row.billingClassLabel,\n rows: [row],\n });\n }\n return groups;\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  groupTimeline,
4
4
  mcpToolLeaf,
5
5
  toolDisplayName
6
- } from "./chunk-J2RVJ6JX.js";
6
+ } from "./chunk-4IJCL7YO.js";
7
7
  import {
8
8
  Tooltip,
9
9
  TooltipContent,
@@ -68,6 +68,7 @@ function QueueSurfaceFallback() {
68
68
 
69
69
  // src/components/session-chrome.tsx
70
70
  import {
71
+ AudioLinesIcon,
71
72
  ArrowDownIcon,
72
73
  ArrowUpIcon,
73
74
  BotIcon,
@@ -101,6 +102,23 @@ var GOAL_LABEL = {
101
102
  invariant_broken: "Needs attention",
102
103
  completed: "Done"
103
104
  };
105
+ function queuedTurnPresentation(turn) {
106
+ const realtimeDelegation = objectValue(turn.metadata.realtimeDelegation);
107
+ const inputTranscript = realtimeDelegation?.inputTranscript;
108
+ if (typeof inputTranscript === "string" && inputTranscript.trim()) {
109
+ return { kind: "realtime_voice", text: inputTranscript.trim() };
110
+ }
111
+ if (objectValue(turn.metadata.realtimeTailFlush)) {
112
+ return { kind: "realtime_voice_handoff", text: "Remaining voice context" };
113
+ }
114
+ return { kind: "prompt", text: turn.prompt };
115
+ }
116
+ function isSteeringTurn(turn) {
117
+ return turn.metadata.delivery === "steer";
118
+ }
119
+ function objectValue(value) {
120
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
121
+ }
104
122
  function sessionChromeGoalPillState(goalStatus, continuation) {
105
123
  if (goalStatus === "completed") return "completed";
106
124
  if (goalStatus === "paused") return "paused";
@@ -184,6 +202,25 @@ function SessionChrome({
184
202
  const record = goal?.goal ?? null;
185
203
  const incoming = queue.pendingInputs;
186
204
  const turns = queue.queue;
205
+ const composerSteering = composer?.steering ?? null;
206
+ const { steering, queuedTurns } = useMemo(() => {
207
+ const pendingQueueSteer = turns.find((turn) => queue.pendingByTurn[turn.id] === "steer") ?? null;
208
+ const durableQueueSteer = !pendingQueueSteer && turns[0] && isSteeringTurn(turns[0]) ? turns[0] : null;
209
+ const currentSteering = composerSteering?.phase === "submitting" ? composerSteering : pendingQueueSteer ? {
210
+ phase: "submitting",
211
+ text: queuedTurnPresentation(pendingQueueSteer).text,
212
+ turnId: pendingQueueSteer.id
213
+ } : durableQueueSteer ? {
214
+ phase: "accepted",
215
+ text: queuedTurnPresentation(durableQueueSteer).text,
216
+ turnId: durableQueueSteer.id
217
+ } : composerSteering;
218
+ const currentTurnId = currentSteering?.turnId ?? null;
219
+ return {
220
+ steering: currentSteering,
221
+ queuedTurns: currentTurnId ? turns.filter((turn) => turn.id !== currentTurnId) : turns
222
+ };
223
+ }, [composerSteering, queue.pendingByTurn, turns]);
187
224
  const canMutateQueue = !readOnly && composer !== void 0;
188
225
  const liveGoal = record?.status === "active" && record.continuation?.state === "running" && record.continuation.reason === "goal_turn_running";
189
226
  const elapsed = useLiveElapsed(
@@ -206,14 +243,28 @@ function SessionChrome({
206
243
  icon: /* @__PURE__ */ jsx2(InboxIcon, { className: "size-3" })
207
244
  });
208
245
  }
209
- if (turns.length > 0) {
210
- const detail = turns[0]?.prompt;
246
+ if (steering) {
247
+ rows.push({
248
+ id: "steering",
249
+ label: "Changing direction\u2026",
250
+ detail: steering.text,
251
+ tone: "accent",
252
+ icon: steering.phase === "submitting" ? /* @__PURE__ */ jsx2(Loader2Icon2, { className: "size-3 animate-og-spin" }) : /* @__PURE__ */ jsx2(ZapIcon, { className: "size-3" })
253
+ });
254
+ }
255
+ if (queuedTurns.length > 0) {
256
+ const presentations = queuedTurns.map(queuedTurnPresentation);
257
+ const first = presentations[0];
258
+ const allVoiceRequests = presentations.every(({ kind }) => kind === "realtime_voice");
259
+ const onlyVoiceHandoff = presentations.length === 1 && first?.kind === "realtime_voice_handoff";
260
+ const voiceOnly = allVoiceRequests || onlyVoiceHandoff;
261
+ const detail = first?.text;
211
262
  rows.push({
212
263
  id: "queue",
213
- label: `${turns.length} queued prompt${turns.length === 1 ? "" : "s"}`,
264
+ label: allVoiceRequests ? queuedTurns.length === 1 ? "Voice request queued" : `${queuedTurns.length} voice requests queued` : onlyVoiceHandoff ? "Voice handoff queued" : `${queuedTurns.length} queued prompt${queuedTurns.length === 1 ? "" : "s"}`,
214
265
  ...detail ? { detail } : {},
215
266
  tone: "neutral",
216
- icon: /* @__PURE__ */ jsx2(ListOrderedIcon, { className: "size-3" })
267
+ icon: voiceOnly ? /* @__PURE__ */ jsx2(AudioLinesIcon, { className: "size-3" }) : /* @__PURE__ */ jsx2(ListOrderedIcon, { className: "size-3" })
217
268
  });
218
269
  }
219
270
  if (record && goalState) {
@@ -237,7 +288,7 @@ function SessionChrome({
237
288
  });
238
289
  }
239
290
  return rows;
240
- }, [agentsSignal, elapsed, goalState, incoming, record, turns]);
291
+ }, [agentsSignal, elapsed, goalState, incoming, queuedTurns, record, steering]);
241
292
  const [activeUncontrolled, setActiveUncontrolled] = useState(
242
293
  defaultActive
243
294
  );
@@ -261,10 +312,10 @@ function SessionChrome({
261
312
  }, [active, activeControlled, onActiveChange, signalIds]);
262
313
  useEffect(() => {
263
314
  if (active !== "queue" || !replaceDraftFor) return;
264
- if (!turns.some((turn) => turn.id === replaceDraftFor)) {
315
+ if (!queuedTurns.some((turn) => turn.id === replaceDraftFor)) {
265
316
  setReplaceDraftFor(null);
266
317
  }
267
- }, [active, replaceDraftFor, turns]);
318
+ }, [active, queuedTurns, replaceDraftFor]);
268
319
  useEffect(() => {
269
320
  const rail = railRef.current;
270
321
  const measure = () => {
@@ -301,7 +352,7 @@ function SessionChrome({
301
352
  const node = panelBodyRef.current;
302
353
  if (!node) return;
303
354
  setPanelHeight(node.offsetHeight);
304
- }, [open, active, incoming, turns, record, goalState, agentsPanel, agentsSignal]);
355
+ }, [open, active, incoming, queuedTurns, record, goalState, agentsPanel, agentsSignal, steering]);
305
356
  useEffect(() => {
306
357
  if (!open) return;
307
358
  const node = panelBodyRef.current;
@@ -316,10 +367,10 @@ function SessionChrome({
316
367
  const shellDuration = reduceMotion ? 0 : 0.22;
317
368
  const crossfadeDuration = reduceMotion ? 0 : 0.18;
318
369
  const ease = [0.22, 1, 0.36, 1];
319
- const panelBody = active === "incoming" ? /* @__PURE__ */ jsx2(IncomingPanel, { inputs: incoming, onDismiss: onDismissIncoming }) : active === "queue" ? /* @__PURE__ */ jsx2(
370
+ const panelBody = active === "incoming" ? /* @__PURE__ */ jsx2(IncomingPanel, { inputs: incoming, onDismiss: onDismissIncoming }) : active === "steering" && steering ? /* @__PURE__ */ jsx2(SteeringPanel, { phase: steering.phase, text: steering.text }) : active === "queue" ? /* @__PURE__ */ jsx2(
320
371
  QueuePanel,
321
372
  {
322
- turns,
373
+ turns: queuedTurns,
323
374
  readOnly: !canMutateQueue,
324
375
  mutationFor: queue.mutationFor,
325
376
  replaceDraftFor,
@@ -553,6 +604,24 @@ function IncomingPanel({
553
604
  }
554
605
  );
555
606
  }
607
+ function SteeringPanel({ phase, text }) {
608
+ return /* @__PURE__ */ jsxs2(
609
+ "div",
610
+ {
611
+ className: "flex items-start gap-2 rounded-og-sm px-1.5 py-1",
612
+ role: "status",
613
+ "aria-live": "polite",
614
+ "data-og-session-chrome-panel": "steering",
615
+ children: [
616
+ /* @__PURE__ */ jsx2("span", { className: "mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full bg-og-accent-soft text-og-accent", children: phase === "submitting" ? /* @__PURE__ */ jsx2(Loader2Icon2, { className: "size-3 animate-og-spin", "aria-hidden": "true" }) : /* @__PURE__ */ jsx2(ZapIcon, { className: "size-3", "aria-hidden": "true" }) }),
617
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
618
+ /* @__PURE__ */ jsx2("p", { className: "truncate text-og-xs font-medium leading-4 text-og-fg", children: text }),
619
+ /* @__PURE__ */ jsx2("p", { className: "mt-0.5 text-[10px] leading-4 text-og-fg-muted", children: phase === "submitting" ? "Sending your latest direction\u2026" : "Direction accepted. The agent will continue from it." })
620
+ ] })
621
+ ]
622
+ }
623
+ );
624
+ }
556
625
  function QueuePanel({
557
626
  turns,
558
627
  readOnly,
@@ -572,6 +641,8 @@ function QueuePanel({
572
641
  "aria-label": "Queued prompts",
573
642
  "data-og-session-chrome-panel": "queue",
574
643
  children: turns.map((turn, index) => {
644
+ const presentation = queuedTurnPresentation(turn);
645
+ const voice = presentation.kind !== "prompt";
575
646
  const pending = mutationFor(turn.id);
576
647
  const beforeUp = index > 0 ? turns[index - 1]?.id ?? null : null;
577
648
  const beforeDown = index < turns.length - 1 ? turns[index + 2]?.id ?? null : null;
@@ -584,8 +655,14 @@ function QueuePanel({
584
655
  className: "group flex flex-col gap-1 rounded-og-sm px-1.5 py-1 transition-colors hover:bg-[var(--og-session-chrome-row-hover)]",
585
656
  children: [
586
657
  /* @__PURE__ */ jsxs2("div", { className: "flex items-start gap-1.5", children: [
587
- /* @__PURE__ */ jsx2("span", { className: "mt-px shrink-0 font-og-mono text-[10px] leading-4 text-og-fg-subtle", children: index + 1 }),
588
- /* @__PURE__ */ jsx2("p", { className: "min-w-0 flex-1 truncate text-og-xs leading-4 text-og-fg", children: turn.prompt }),
658
+ voice ? /* @__PURE__ */ jsx2(
659
+ AudioLinesIcon,
660
+ {
661
+ "aria-hidden": "true",
662
+ className: "mt-0.5 size-3 shrink-0 text-og-accent"
663
+ }
664
+ ) : /* @__PURE__ */ jsx2("span", { className: "mt-px shrink-0 font-og-mono text-[10px] leading-4 text-og-fg-subtle", children: index + 1 }),
665
+ /* @__PURE__ */ jsx2("p", { className: "min-w-0 flex-1 truncate text-og-xs leading-4 text-og-fg", children: presentation.text }),
589
666
  showActions ? /* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100 max-sm:opacity-100", children: [
590
667
  onMove && turns.length > 1 ? /* @__PURE__ */ jsxs2(Fragment, { children: [
591
668
  /* @__PURE__ */ jsx2(
@@ -6806,4 +6883,4 @@ export {
6806
6883
  MessageTimeline,
6807
6884
  TimelineRow
6808
6885
  };
6809
- //# sourceMappingURL=chunk-Z5FV355Z.js.map
6886
+ //# sourceMappingURL=chunk-UCZPNXV3.js.map