@opengeni/react 0.36.0 → 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 (45) 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-PVW56EVR.js → chunk-IP22SLO6.js} +447 -13
  8. package/dist/chunk-IP22SLO6.js.map +1 -0
  9. package/dist/{chunk-U255M5EZ.js → chunk-SJKT4TKW.js} +39 -6
  10. package/dist/chunk-SJKT4TKW.js.map +1 -0
  11. package/dist/{chunk-XT7JF2EH.js → chunk-UCZPNXV3.js} +96 -17
  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/composer.tsx +17 -1
  30. package/src/components/copy-button.tsx +13 -6
  31. package/src/components/model-picker.tsx +2 -2
  32. package/src/components/model-policy-picker.tsx +582 -0
  33. package/src/components/session-chrome.tsx +138 -14
  34. package/src/composer.ts +13 -0
  35. package/src/hooks/use-composer.ts +213 -16
  36. package/src/index.ts +15 -0
  37. package/src/model-policy.ts +69 -14
  38. package/src/timeline/projection.ts +21 -1
  39. package/src/timeline/types.ts +5 -0
  40. package/dist/chunk-FT2TXNGZ.js.map +0 -1
  41. package/dist/chunk-J2RVJ6JX.js.map +0 -1
  42. package/dist/chunk-PVW56EVR.js.map +0 -1
  43. package/dist/chunk-U255M5EZ.js.map +0 -1
  44. package/dist/chunk-XT7JF2EH.js.map +0 -1
  45. /package/dist/{chunk-OKKMZDQF.js.map → chunk-FSPDND3P.js.map} +0 -0
@@ -7,7 +7,7 @@ var BILLING_CLASS_ORDER = [
7
7
  var BILLING_CLASS_LABELS = {
8
8
  opengeni_credits: "OpenGeni",
9
9
  codex_subscription: "Codex",
10
- byok: "Bring your own key"
10
+ byok: "Your Gateway"
11
11
  };
12
12
  var AVAILABILITY_REASON_LABELS = {
13
13
  missing_credential: "Credentials required",
@@ -19,6 +19,12 @@ var AVAILABILITY_REASON_LABELS = {
19
19
  unsupported: "Unsupported"
20
20
  };
21
21
  function billingClassForModel(model) {
22
+ if (model.source === "codex") {
23
+ return "codex_subscription";
24
+ }
25
+ if (model.source === "workspace_gateway") {
26
+ return "byok";
27
+ }
22
28
  if (model.credentialSource?.kind === "connected_subscription") {
23
29
  return "codex_subscription";
24
30
  }
@@ -47,7 +53,7 @@ function effortOptionsForModel(model) {
47
53
  if (!efforts || efforts.length === 0) {
48
54
  return ["low"];
49
55
  }
50
- const order = ["none", "minimal", "low", "medium", "high", "xhigh"];
56
+ const order = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
51
57
  return order.filter((effort) => efforts.includes(effort));
52
58
  }
53
59
  function defaultEffortForModel(model) {
@@ -78,6 +84,12 @@ function labelLatencyMode(mode) {
78
84
  }
79
85
  return "Standard";
80
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
+ }
81
93
  function payerSummaryForModel(model) {
82
94
  const billing = model.billing;
83
95
  if (!billing) {
@@ -90,7 +102,7 @@ function payerSummaryForModel(model) {
90
102
  return "Codex subscription \xB7 external billing";
91
103
  }
92
104
  if (billing.upstreamPayer === "workspace") {
93
- return "Workspace credentials \xB7 external billing";
105
+ return "Billed to your AI Gateway";
94
106
  }
95
107
  return "Deployment route \xB7 external billing";
96
108
  }
@@ -103,7 +115,7 @@ function advancedSourceSummary(model) {
103
115
  return "Connected Codex subscription";
104
116
  }
105
117
  if (source.kind === "workspace_connection") {
106
- return "Workspace API key connection";
118
+ return "Workspace AI Gateway";
107
119
  }
108
120
  if (source.kind === "deployment") {
109
121
  return source.mechanism === "azure_ad_bearer" ? "Deployment Azure identity" : "Deployment API key";
@@ -111,7 +123,10 @@ function advancedSourceSummary(model) {
111
123
  return null;
112
124
  }
113
125
  function projectPickerRows(models) {
114
- return models.map((catalog) => {
126
+ return models.filter((catalog) => {
127
+ const billingClass = billingClassForModel(catalog);
128
+ return billingClass === "opengeni_credits" || catalog.credentialReadiness.status === "ready";
129
+ }).map((catalog) => {
115
130
  const billingClass = billingClassForModel(catalog);
116
131
  return {
117
132
  id: catalog.id,
@@ -126,6 +141,22 @@ function projectPickerRows(models) {
126
141
  };
127
142
  });
128
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
+ }
129
160
  function sortPickerRows(rows) {
130
161
  return [...rows].sort((left, right) => {
131
162
  const classDelta = BILLING_CLASS_ORDER.indexOf(left.billingClass) - BILLING_CLASS_ORDER.indexOf(right.billingClass);
@@ -168,11 +199,13 @@ export {
168
199
  coerceReasoningEffortForModel,
169
200
  runnableLatencyModesForModel,
170
201
  labelLatencyMode,
202
+ labelReasoningEffort,
171
203
  payerSummaryForModel,
172
204
  advancedSourceSummary,
173
205
  projectPickerRows,
206
+ projectClientModelRows,
174
207
  sortPickerRows,
175
208
  findPickerRow,
176
209
  groupPickerRowsByBillingClass
177
210
  };
178
- //# sourceMappingURL=chunk-U255M5EZ.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(
@@ -3183,6 +3260,8 @@ function CopyButton({
3183
3260
  const onClick = async (event) => {
3184
3261
  event.preventDefault();
3185
3262
  event.stopPropagation();
3263
+ const button = event.currentTarget;
3264
+ const pointerActivated = event.detail > 0;
3186
3265
  const value = typeof text === "function" ? text() : text;
3187
3266
  const ok = await copyTextToClipboard(value);
3188
3267
  if (!ok) {
@@ -3190,9 +3269,6 @@ function CopyButton({
3190
3269
  }
3191
3270
  setCopied(true);
3192
3271
  setTipOpen(true);
3193
- if (event.detail > 0) {
3194
- event.currentTarget.blur();
3195
- }
3196
3272
  if (timerRef.current !== null) {
3197
3273
  clearTimeout(timerRef.current);
3198
3274
  }
@@ -3201,6 +3277,9 @@ function CopyButton({
3201
3277
  setCopied(false);
3202
3278
  setTipOpen(false);
3203
3279
  }, COPIED_MS);
3280
+ if (pointerActivated) {
3281
+ button.blur();
3282
+ }
3204
3283
  };
3205
3284
  const tip = copied ? "Copied" : label;
3206
3285
  return /* @__PURE__ */ jsxs8(
@@ -6804,4 +6883,4 @@ export {
6804
6883
  MessageTimeline,
6805
6884
  TimelineRow
6806
6885
  };
6807
- //# sourceMappingURL=chunk-XT7JF2EH.js.map
6886
+ //# sourceMappingURL=chunk-UCZPNXV3.js.map