@opengeni/react 6.1.0 → 7.0.0-canary.35585322809001

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 (206) hide show
  1. package/README.md +83 -2
  2. package/dist/artifacts-document.js +1 -1
  3. package/dist/artifacts-presentation.js +1 -1
  4. package/dist/artifacts-spreadsheet.js +1 -1
  5. package/dist/artifacts.js +3 -3
  6. package/dist/{browser-viewer-NTD6UIPR.js → browser-viewer-LGH4IP47.js} +3 -3
  7. package/dist/capability-catalog-row.d.ts +20 -0
  8. package/dist/capability-logo-fallback.d.ts +3 -0
  9. package/dist/{chunk-NOSYBS2D.js → chunk-2VHARTBL.js} +2 -1
  10. package/dist/{chunk-VRBL3F5E.js → chunk-5GAL23FF.js} +10 -2
  11. package/dist/{chunk-VRBL3F5E.js.map → chunk-5GAL23FF.js.map} +1 -1
  12. package/dist/chunk-5UONOQ3Y.js +635 -0
  13. package/dist/chunk-5UONOQ3Y.js.map +1 -0
  14. package/dist/{chunk-NYESIADM.js → chunk-AKOMC3VO.js} +3 -3
  15. package/dist/chunk-AKOMC3VO.js.map +1 -0
  16. package/dist/chunk-BRM6FVZA.js +302 -0
  17. package/dist/chunk-BRM6FVZA.js.map +1 -0
  18. package/dist/{chunk-TDSEILUH.js → chunk-D5NOQPLR.js} +1289 -1530
  19. package/dist/chunk-D5NOQPLR.js.map +1 -0
  20. package/dist/chunk-D7GAPJ3V.js +1558 -0
  21. package/dist/chunk-D7GAPJ3V.js.map +1 -0
  22. package/dist/{chunk-5M6VTFJ5.js → chunk-GBPCZH3Y.js} +2 -2
  23. package/dist/{chunk-YBM6CQRU.js → chunk-GEN54DU5.js} +4 -4
  24. package/dist/{chunk-UMECRJRV.js → chunk-HJ5VJ4JB.js} +21 -12
  25. package/dist/chunk-HJ5VJ4JB.js.map +1 -0
  26. package/dist/{chunk-R4KRSFGA.js → chunk-IP4BUBKH.js} +3 -3
  27. package/dist/{chunk-KANFU34B.js → chunk-IYUPN32E.js} +6 -2
  28. package/dist/chunk-IYUPN32E.js.map +1 -0
  29. package/dist/{chunk-L2NBTKRX.js → chunk-JKLGJQO5.js} +193 -12
  30. package/dist/chunk-JKLGJQO5.js.map +1 -0
  31. package/dist/{chunk-NLTS7JHI.js → chunk-JPHDWKZE.js} +5 -5
  32. package/dist/{chunk-ICA6R6TW.js → chunk-OWHPKAOV.js} +28 -20
  33. package/dist/chunk-OWHPKAOV.js.map +1 -0
  34. package/dist/{chunk-24M4YBCL.js → chunk-PNAVEK2U.js} +4 -4
  35. package/dist/{chunk-45XEUVTK.js → chunk-UXE5SSNN.js} +293 -463
  36. package/dist/chunk-UXE5SSNN.js.map +1 -0
  37. package/dist/{chunk-PEI6RANI.js → chunk-VR4Z73EU.js} +203 -9
  38. package/dist/chunk-VR4Z73EU.js.map +1 -0
  39. package/dist/{chunk-HUIKIDM5.js → chunk-XMXRI5JK.js} +65 -18
  40. package/dist/chunk-XMXRI5JK.js.map +1 -0
  41. package/dist/client.d.ts +1 -1
  42. package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
  43. package/dist/components/chat-composer.d.ts +13 -3
  44. package/dist/components/markdown.d.ts +4 -3
  45. package/dist/components/message-timeline.d.ts +7 -2
  46. package/dist/components/preview-loading.d.ts +2 -0
  47. package/dist/components/session-capability-frame.d.ts +24 -0
  48. package/dist/components/session-chrome.d.ts +1 -2
  49. package/dist/components/session-commands.d.ts +6 -0
  50. package/dist/components/session-connection-request.d.ts +8 -0
  51. package/dist/components/session-mcp-capability-card.d.ts +22 -0
  52. package/dist/components/timeline-search.d.ts +34 -0
  53. package/dist/composer.js +7 -7
  54. package/dist/connect-account-identity.d.ts +8 -0
  55. package/dist/connect-accounts.d.ts +5 -0
  56. package/dist/connect-chooser.d.ts +6 -0
  57. package/dist/connect-panel.d.ts +9 -2
  58. package/dist/connect-setup.d.ts +5 -1
  59. package/dist/connect.d.ts +5 -21
  60. package/dist/connect.js +1268 -584
  61. package/dist/connect.js.map +1 -1
  62. package/dist/connection-catalog.d.ts +6 -1
  63. package/dist/connection-discovery.d.ts +12 -0
  64. package/dist/connection-installed.d.ts +1 -0
  65. package/dist/connection-logo.d.ts +5 -2
  66. package/dist/connection-service-logo.d.ts +8 -0
  67. package/dist/connection-service-presentation.d.ts +13 -0
  68. package/dist/connection-skeleton.d.ts +5 -0
  69. package/dist/discovery-cache.d.ts +5 -0
  70. package/dist/hooks/use-codex-accounts.d.ts +11 -5
  71. package/dist/hooks/use-connect.d.ts +22 -0
  72. package/dist/hooks/use-session-events.d.ts +5 -0
  73. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  74. package/dist/hooks/use-workspace-sessions.d.ts +2 -0
  75. package/dist/index.d.ts +8 -2
  76. package/dist/index.js +771 -793
  77. package/dist/index.js.map +1 -1
  78. package/dist/interaction.js +2 -2
  79. package/dist/machines.js +2 -2
  80. package/dist/mcp-connection-status.d.ts +14 -0
  81. package/dist/{model-policy-picker-menu-3R336DP2.js → model-policy-picker-menu-RVOQ4ZZG.js} +4 -4
  82. package/dist/model-policy.d.ts +6 -2
  83. package/dist/model-policy.js +1 -1
  84. package/dist/{platform-activity-row-DUE2W74J.js → platform-activity-row-HLOH7FW4.js} +9 -4
  85. package/dist/platform-activity-row-HLOH7FW4.js.map +1 -0
  86. package/dist/plugin-discovery.d.ts +6 -0
  87. package/dist/{queue-surface-implementation-LK3S4BIW.js → queue-surface-implementation-3RIFK2BS.js} +4 -4
  88. package/dist/realtime.js +22 -22
  89. package/dist/realtime.js.map +1 -1
  90. package/dist/session-auth-recommendation.d.ts +5 -0
  91. package/dist/session-capability-policy.d.ts +12 -0
  92. package/dist/session-ui.d.ts +2 -0
  93. package/dist/session-ui.js +14 -12
  94. package/dist/session.d.ts +1 -0
  95. package/dist/session.js +6 -4
  96. package/dist/skill-discovery.d.ts +11 -0
  97. package/dist/timeline/compaction-copy.d.ts +10 -0
  98. package/dist/timeline/projection.d.ts +3 -1
  99. package/dist/timeline/turn-summary.d.ts +2 -2
  100. package/dist/timeline/types.d.ts +22 -1
  101. package/dist/{timeline-annotation-markers-DRSTLEM6.js → timeline-annotation-markers-OY6OWR3V.js} +2 -2
  102. package/dist/{timeline-annotation-selection-IHLUGVND.js → timeline-annotation-selection-BOXA2TIN.js} +2 -2
  103. package/dist/{timeline-annotations-dialog-6Q53N5FG.js → timeline-annotations-dialog-RGY3MHL3.js} +3 -3
  104. package/package.json +3 -3
  105. package/src/assets/README.md +8 -0
  106. package/src/assets/gmail.ico +0 -0
  107. package/src/assets/outlook.ico +0 -0
  108. package/src/capability-catalog-row.tsx +94 -0
  109. package/src/capability-logo-fallback.ts +22 -0
  110. package/src/client.ts +0 -9
  111. package/src/components/artifacts/document-editor.tsx +63 -6
  112. package/src/components/artifacts/presentation-editor.tsx +214 -9
  113. package/src/components/artifacts/spreadsheet-canvas.ts +3 -8
  114. package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
  115. package/src/components/artifacts/spreadsheet-grid.tsx +10 -11
  116. package/src/components/chat-composer.tsx +26 -3
  117. package/src/components/composer.tsx +10 -4
  118. package/src/components/human-input-form.tsx +12 -2
  119. package/src/components/markdown.tsx +86 -4
  120. package/src/components/message-timeline.tsx +104 -36
  121. package/src/components/model-picker.tsx +11 -3
  122. package/src/components/model-policy-picker-menu.tsx +10 -3
  123. package/src/components/preview-loading.tsx +154 -0
  124. package/src/components/sandbox-workspace.tsx +20 -36
  125. package/src/components/session-capability-frame.tsx +163 -0
  126. package/src/components/session-chrome.tsx +46 -77
  127. package/src/components/session-commands.tsx +18 -0
  128. package/src/components/session-connection-request.tsx +97 -0
  129. package/src/components/session-conversation.tsx +5 -4
  130. package/src/components/session-mcp-capability-card.tsx +532 -0
  131. package/src/components/session-status.tsx +2 -2
  132. package/src/components/timeline-anchor.tsx +9 -8
  133. package/src/components/timeline-search.tsx +168 -0
  134. package/src/components/user-message-body.tsx +7 -0
  135. package/src/connect-account-identity.tsx +34 -0
  136. package/src/connect-accounts.tsx +114 -52
  137. package/src/connect-chooser.tsx +171 -39
  138. package/src/connect-panel.tsx +73 -9
  139. package/src/connect-setup.tsx +72 -25
  140. package/src/connect.ts +13 -22
  141. package/src/connection-assets.d.ts +4 -0
  142. package/src/connection-catalog.tsx +61 -39
  143. package/src/connection-discovery.tsx +235 -0
  144. package/src/connection-installed.tsx +2 -0
  145. package/src/connection-logo.tsx +18 -10
  146. package/src/connection-service-logo.tsx +55 -0
  147. package/src/connection-service-presentation.ts +46 -0
  148. package/src/connection-skeleton.tsx +16 -0
  149. package/src/conversation-timeline.ts +1 -0
  150. package/src/discovery-cache.ts +42 -0
  151. package/src/hooks/use-codex-accounts.ts +33 -14
  152. package/src/hooks/use-composer.ts +4 -5
  153. package/src/hooks/use-connect.ts +23 -0
  154. package/src/hooks/use-session-events.ts +114 -1
  155. package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
  156. package/src/hooks/use-workspace-sessions.ts +10 -0
  157. package/src/index.ts +22 -2
  158. package/src/mcp-connection-status.ts +35 -0
  159. package/src/model-policy.ts +21 -0
  160. package/src/plugin-details.tsx +8 -5
  161. package/src/plugin-discovery.tsx +92 -72
  162. package/src/realtime/realtime-control.tsx +6 -8
  163. package/src/session-auth-recommendation.ts +48 -0
  164. package/src/session-capability-policy.ts +83 -0
  165. package/src/session-ui.ts +2 -0
  166. package/src/session.ts +1 -0
  167. package/src/skill-discovery.tsx +101 -56
  168. package/src/timeline/compaction-copy.ts +40 -0
  169. package/src/timeline/platform-activity-row.tsx +8 -3
  170. package/src/timeline/projection.ts +74 -2
  171. package/src/timeline/shared.tsx +6 -1
  172. package/src/timeline/startup-timings.tsx +1 -1
  173. package/src/timeline/turn-summary.tsx +17 -3
  174. package/src/timeline/types.ts +22 -2
  175. package/styles/compiled.css +325 -27
  176. package/styles/connect.css +90 -15
  177. package/styles/index.css +46 -0
  178. package/styles/session-capability.css +42 -0
  179. package/dist/chunk-45XEUVTK.js.map +0 -1
  180. package/dist/chunk-HQV2I5HV.js +0 -124
  181. package/dist/chunk-HQV2I5HV.js.map +0 -1
  182. package/dist/chunk-HUIKIDM5.js.map +0 -1
  183. package/dist/chunk-ICA6R6TW.js.map +0 -1
  184. package/dist/chunk-IHQII5WO.js +0 -886
  185. package/dist/chunk-IHQII5WO.js.map +0 -1
  186. package/dist/chunk-KANFU34B.js.map +0 -1
  187. package/dist/chunk-L2NBTKRX.js.map +0 -1
  188. package/dist/chunk-NYESIADM.js.map +0 -1
  189. package/dist/chunk-PEI6RANI.js.map +0 -1
  190. package/dist/chunk-TDSEILUH.js.map +0 -1
  191. package/dist/chunk-UMECRJRV.js.map +0 -1
  192. package/dist/hooks/use-packs.d.ts +0 -32
  193. package/dist/platform-activity-row-DUE2W74J.js.map +0 -1
  194. package/src/hooks/use-packs.ts +0 -157
  195. /package/dist/{browser-viewer-NTD6UIPR.js.map → browser-viewer-LGH4IP47.js.map} +0 -0
  196. /package/dist/{chunk-NOSYBS2D.js.map → chunk-2VHARTBL.js.map} +0 -0
  197. /package/dist/{chunk-5M6VTFJ5.js.map → chunk-GBPCZH3Y.js.map} +0 -0
  198. /package/dist/{chunk-YBM6CQRU.js.map → chunk-GEN54DU5.js.map} +0 -0
  199. /package/dist/{chunk-R4KRSFGA.js.map → chunk-IP4BUBKH.js.map} +0 -0
  200. /package/dist/{chunk-NLTS7JHI.js.map → chunk-JPHDWKZE.js.map} +0 -0
  201. /package/dist/{chunk-24M4YBCL.js.map → chunk-PNAVEK2U.js.map} +0 -0
  202. /package/dist/{model-policy-picker-menu-3R336DP2.js.map → model-policy-picker-menu-RVOQ4ZZG.js.map} +0 -0
  203. /package/dist/{queue-surface-implementation-LK3S4BIW.js.map → queue-surface-implementation-3RIFK2BS.js.map} +0 -0
  204. /package/dist/{timeline-annotation-markers-DRSTLEM6.js.map → timeline-annotation-markers-OY6OWR3V.js.map} +0 -0
  205. /package/dist/{timeline-annotation-selection-IHLUGVND.js.map → timeline-annotation-selection-BOXA2TIN.js.map} +0 -0
  206. /package/dist/{timeline-annotations-dialog-6Q53N5FG.js.map → timeline-annotations-dialog-RGY3MHL3.js.map} +0 -0
@@ -119,19 +119,6 @@ function sourceDrivenDefaultTab(
119
119
  return null;
120
120
  }
121
121
 
122
- function captureDegradedMessage(reason: string): string {
123
- switch (reason) {
124
- case "repository_discovery_timed_out":
125
- return "Workspace capture is incomplete because repository discovery timed out. Live files remain authoritative.";
126
- case "repository_discovery_result_limit_exceeded":
127
- return "Workspace capture is incomplete because the repository limit was exceeded. Live files remain authoritative.";
128
- case "repository_read_unavailable":
129
- return "Workspace capture is incomplete because repository changes could not be read. Live files remain authoritative.";
130
- default:
131
- return "Workspace capture is incomplete because repository discovery failed. Live files remain authoritative.";
132
- }
133
- }
134
-
135
122
  function WorkbenchSurfaceLoading({ name }: { name: "Browser" | "Desktop" }) {
136
123
  return (
137
124
  <CenteredState
@@ -555,23 +542,6 @@ export function useSandboxWorkspaceTabs(
555
542
  if (advertised.length > 0) return advertised;
556
543
  return captureState.capture?.repos.map((repo) => repo.root) ?? [];
557
544
  }, [capabilities?.Git.repos, captureState.capture]);
558
- const notifiedCaptureDegradedReason = useRef<{
559
- sessionId: string;
560
- reason: string | null;
561
- }>({
562
- sessionId,
563
- reason: null,
564
- });
565
- if (notifiedCaptureDegradedReason.current.sessionId !== sessionId) {
566
- notifiedCaptureDegradedReason.current = { sessionId, reason: null };
567
- }
568
- useEffect(() => {
569
- const reason = captureState.degradedReason;
570
- if (!reason || notifiedCaptureDegradedReason.current.reason === reason) return;
571
- notifiedCaptureDegradedReason.current = { sessionId, reason };
572
- onNotify?.({ kind: "error", message: captureDegradedMessage(reason) });
573
- }, [sessionId, captureState.degradedReason, onNotify]);
574
-
575
545
  const files = useSandboxFiles(sessionId, {
576
546
  events,
577
547
  // No passive Channel-A reads while cold, even after a conclusive capture
@@ -743,6 +713,7 @@ export function useSandboxWorkspaceTabs(
743
713
  comparison={changesComparison}
744
714
  onComparisonChange={setChangesComparison}
745
715
  captureAvailable={captureAvailable}
716
+ captureDegraded={captureState.degradedReason !== null}
746
717
  captureRevision={captureState.revision}
747
718
  capturePending={capturePending}
748
719
  liveWorkspaceExpected={liveWorkspaceExpected}
@@ -928,6 +899,7 @@ export function useSandboxWorkspaceTabs(
928
899
  browserEnabled,
929
900
  desktopEnabled,
930
901
  captureAvailable,
902
+ captureState.degradedReason,
931
903
  dirtyCount,
932
904
  warmTerminal,
933
905
  requestedFilePath,
@@ -1354,6 +1326,7 @@ function ChangesTabContent({
1354
1326
  git,
1355
1327
  comparison,
1356
1328
  captureAvailable,
1329
+ captureDegraded,
1357
1330
  captureRevision,
1358
1331
  capturePending,
1359
1332
  liveWorkspaceExpected,
@@ -1367,6 +1340,7 @@ function ChangesTabContent({
1367
1340
  git: UseSandboxGitResult;
1368
1341
  comparison: SandboxGitComparison;
1369
1342
  captureAvailable: boolean;
1343
+ captureDegraded: boolean;
1370
1344
  captureRevision: number | null;
1371
1345
  capturePending: boolean;
1372
1346
  liveWorkspaceExpected: boolean;
@@ -1460,6 +1434,8 @@ function ChangesTabContent({
1460
1434
  }
1461
1435
 
1462
1436
  if (!liveWorkspaceExpected && (comparison !== "working" || !captureAvailable)) {
1437
+ // Staged changes are live-only; a failed saved preview is not their blocker.
1438
+ const previewUnavailable = captureDegraded && !captureAvailable && comparison !== "staged";
1463
1439
  return (
1464
1440
  <CenteredState
1465
1441
  icon={
@@ -1468,22 +1444,30 @@ function ChangesTabContent({
1468
1444
  className="size-5 animate-spin motion-reduce:animate-none"
1469
1445
  aria-hidden
1470
1446
  />
1447
+ ) : previewUnavailable ? (
1448
+ <TriangleAlertIcon className="size-5" aria-hidden />
1471
1449
  ) : (
1472
1450
  <CpuIcon className="size-5" aria-hidden />
1473
1451
  )
1474
1452
  }
1475
1453
  >
1476
1454
  <p className="text-og-sm font-medium text-og-fg">
1477
- {workspaceWaking ? "Waking workspace" : "Workspace is resting"}
1455
+ {workspaceWaking
1456
+ ? "Waking workspace"
1457
+ : previewUnavailable
1458
+ ? "Saved changes preview is unavailable"
1459
+ : "Workspace is resting"}
1478
1460
  </p>
1479
1461
  <p className="text-og-sm leading-5 text-og-fg-subtle">
1480
1462
  {workspaceWaking
1481
1463
  ? "Connecting to the live working tree…"
1482
- : comparison === "branch"
1483
- ? "Wake the sandbox to compare this branch with the remote default branch."
1484
- : comparison === "staged"
1485
- ? "Wake the sandbox to inspect staged changes."
1486
- : "No captured revision is available yet. Wake the sandbox to inspect uncommitted changes."}
1464
+ : previewUnavailable
1465
+ ? "Open the live workspace to view current changes."
1466
+ : comparison === "branch"
1467
+ ? "Wake the sandbox to compare this branch with the remote default branch."
1468
+ : comparison === "staged"
1469
+ ? "Wake the sandbox to inspect staged changes."
1470
+ : "No captured revision is available yet. Wake the sandbox to inspect uncommitted changes."}
1487
1471
  </p>
1488
1472
  {!workspaceWaking ? (
1489
1473
  <DockActionButton onClick={onWake}>
@@ -0,0 +1,163 @@
1
+ import { CheckIcon, ShieldCheckIcon, XIcon } from "lucide-react";
2
+ import { Dialog } from "radix-ui";
3
+ import { useRef, useState, type ReactNode, type RefObject } from "react";
4
+ import { usePortalTokenStyle } from "../lib/use-portal-token-style";
5
+
6
+ export type SessionCapabilityFrameProps = {
7
+ name: string;
8
+ subtitle: string;
9
+ logo: string | null;
10
+ typeLabel: string;
11
+ description: string;
12
+ skill: boolean;
13
+ expanded: boolean;
14
+ complete: boolean;
15
+ actionLabel: string;
16
+ note: string;
17
+ onOpen(): void;
18
+ onClose(): void;
19
+ busy?: boolean;
20
+ opener?: RefObject<HTMLButtonElement | null>;
21
+ cardRef?: RefObject<HTMLElement | null>;
22
+ children?: ReactNode;
23
+ /** A catalogue row can own the opener while reusing the same details dialog. */
24
+ dialogOnly?: boolean;
25
+ };
26
+
27
+ function Mark({ src, name }: { src: string | null; name: string }) {
28
+ const [failed, setFailed] = useState<string | null>(null);
29
+ return (
30
+ <span className="og-session-capability-logo" aria-hidden>
31
+ {src && failed !== src ? (
32
+ <img src={src} alt="" loading="lazy" decoding="async" onError={() => setFailed(src)} />
33
+ ) : (
34
+ name.trim().slice(0, 2).toUpperCase()
35
+ )}
36
+ </span>
37
+ );
38
+ }
39
+
40
+ /** The same conversation card in the console and embedded clients. Setup
41
+ * content owns the provider flow; this frame owns identity and protected focus. */
42
+ export function SessionCapabilityFrame({
43
+ name,
44
+ subtitle,
45
+ logo,
46
+ typeLabel,
47
+ description,
48
+ skill,
49
+ expanded,
50
+ complete,
51
+ actionLabel,
52
+ note,
53
+ onOpen,
54
+ onClose,
55
+ busy = false,
56
+ opener,
57
+ cardRef,
58
+ children,
59
+ dialogOnly = false,
60
+ }: SessionCapabilityFrameProps) {
61
+ const localOpener = useRef<HTMLButtonElement>(null);
62
+ const localCard = useRef<HTMLElement>(null);
63
+ const buttonRef = opener ?? localOpener;
64
+ const sectionRef = cardRef ?? localCard;
65
+ const portalStyle = usePortalTokenStyle(sectionRef.current);
66
+ const identity = (
67
+ <>
68
+ <Mark src={logo} name={name} />
69
+ <div className="og-session-capability-identity">
70
+ <h3>{name}</h3>
71
+ <span>{subtitle}</span>
72
+ </div>
73
+ <span className="og-session-capability-type">{typeLabel}</span>
74
+ </>
75
+ );
76
+ return (
77
+ <div className="og-session-capability">
78
+ <section
79
+ hidden={dialogOnly}
80
+ style={dialogOnly ? { display: "none" } : undefined}
81
+ ref={sectionRef}
82
+ tabIndex={-1}
83
+ aria-label={`${name} setup`}
84
+ data-state={complete ? "complete" : expanded ? "setup" : "suggested"}
85
+ className="og-session-capability-shell"
86
+ >
87
+ <div className="og-session-capability-header">
88
+ {identity}
89
+ {complete ? <CheckIcon size={16} aria-hidden /> : null}
90
+ </div>
91
+ {complete ? (
92
+ <p role="status" className="og-session-capability-copy">
93
+ {skill ? "Installed · Workspace" : "Connected · Available in this conversation"}
94
+ </p>
95
+ ) : (
96
+ <>
97
+ <p className="og-session-capability-copy">{description}</p>
98
+ <div className="og-session-capability-actions">
99
+ <span>
100
+ <ShieldCheckIcon size={13} aria-hidden />
101
+ {skill ? "Guidance only · no account access" : "You choose what to authorize"}
102
+ </span>
103
+ <button
104
+ ref={buttonRef}
105
+ type="button"
106
+ onClick={onOpen}
107
+ aria-haspopup="dialog"
108
+ aria-expanded={expanded}
109
+ >
110
+ {actionLabel}
111
+ </button>
112
+ </div>
113
+ </>
114
+ )}
115
+ </section>
116
+ {!complete && !dialogOnly ? <p className="og-session-capability-note">{note}</p> : null}
117
+ <Dialog.Root
118
+ open={expanded && !complete}
119
+ onOpenChange={(open) => {
120
+ if (!open && !busy) onClose();
121
+ }}
122
+ >
123
+ <Dialog.Portal>
124
+ <Dialog.Overlay className="og-root og-session-capability-overlay" style={portalStyle} />
125
+ <Dialog.Content
126
+ className="og-root og-session-capability-dialog"
127
+ style={portalStyle}
128
+ onEscapeKeyDown={(event) => {
129
+ if (busy) event.preventDefault();
130
+ }}
131
+ onInteractOutside={(event) => {
132
+ if (busy) event.preventDefault();
133
+ }}
134
+ onCloseAutoFocus={(event) => {
135
+ event.preventDefault();
136
+ const target = buttonRef.current?.isConnected
137
+ ? buttonRef.current
138
+ : sectionRef.current;
139
+ if (target?.isConnected) target.focus();
140
+ }}
141
+ >
142
+ <div className="og-session-capability-dialog-header">
143
+ <Mark src={logo} name={name} />
144
+ <div>
145
+ <Dialog.Title>{name}</Dialog.Title>
146
+ <Dialog.Description>
147
+ {typeLabel}
148
+ {subtitle ? ` · ${subtitle}` : ""}
149
+ </Dialog.Description>
150
+ </div>
151
+ {!busy ? (
152
+ <Dialog.Close aria-label="Close connection setup">
153
+ <XIcon size={18} />
154
+ </Dialog.Close>
155
+ ) : null}
156
+ </div>
157
+ <div className="og-session-capability-dialog-body">{children}</div>
158
+ </Dialog.Content>
159
+ </Dialog.Portal>
160
+ </Dialog.Root>
161
+ </div>
162
+ );
163
+ }
@@ -94,7 +94,6 @@ export type SessionChromeAgentsSignal = {
94
94
  };
95
95
 
96
96
  export type SessionChromeProps = {
97
- compact?: boolean;
98
97
  /** Authoritative execution status; omitted by older embedding hosts. */
99
98
  sessionStatus?: SessionStatus | undefined;
100
99
  queue: UseTurnQueueResult;
@@ -432,7 +431,6 @@ function toneClass(tone: SessionChromeSignalTone, selected: boolean): string {
432
431
  }
433
432
 
434
433
  export function SessionChrome({
435
- compact = false,
436
434
  sessionStatus,
437
435
  queue,
438
436
  composer,
@@ -451,7 +449,7 @@ export function SessionChrome({
451
449
  onActiveChange,
452
450
  }: SessionChromeProps) {
453
451
  const [activityRequested, setActivityOpen] = useState(
454
- Boolean(compact && defaultActive && ["incoming", "agents", "commands"].includes(defaultActive)),
452
+ Boolean(defaultActive && ["incoming", "agents", "commands"].includes(defaultActive)),
455
453
  );
456
454
  const reactId = useId();
457
455
  const panelId = `og-session-chrome-panel-${reactId}`;
@@ -499,8 +497,7 @@ export function SessionChrome({
499
497
  );
500
498
  const active = activeControlled !== undefined ? activeControlled : activeUncontrolled;
501
499
  const activityOpen =
502
- activityRequested ||
503
- Boolean(compact && active && ["incoming", "agents", "commands"].includes(active));
500
+ activityRequested || Boolean(active && ["incoming", "agents", "commands"].includes(active));
504
501
  const activityVisibleRef = useRef(activityOpen);
505
502
  activityVisibleRef.current = activityOpen;
506
503
  // Two independent suppressions, both occupancy-scoped:
@@ -711,12 +708,9 @@ export function SessionChrome({
711
708
 
712
709
  const signalIds = signals.map((signal) => signal.id).join(",");
713
710
  useEffect(() => {
714
- if (
715
- compact &&
716
- !signals.some((signal) => ["incoming", "agents", "commands"].includes(signal.id))
717
- )
711
+ if (!signals.some((signal) => ["incoming", "agents", "commands"].includes(signal.id)))
718
712
  setActivityOpen(false);
719
- }, [compact, signals]);
713
+ }, [signals]);
720
714
  useEffect(() => {
721
715
  if (active && !signalIds.split(",").includes(active)) {
722
716
  if (activeControlled === undefined) setActiveUncontrolled(null);
@@ -885,21 +879,9 @@ export function SessionChrome({
885
879
  )}
886
880
  style={{
887
881
  borderRadius: "var(--_og-session-chrome-radius)",
888
- background: compact
889
- ? "transparent"
890
- : open
891
- ? "var(--_og-session-chrome-surface-open)"
892
- : "var(--_og-session-chrome-surface)",
893
- borderColor: compact
894
- ? "transparent"
895
- : open
896
- ? "var(--_og-session-chrome-border-open)"
897
- : "var(--_og-session-chrome-border)",
898
- boxShadow: compact
899
- ? "none"
900
- : open
901
- ? "var(--og-session-chrome-shadow-open)"
902
- : "var(--og-session-chrome-shadow)",
882
+ background: "transparent",
883
+ borderColor: "transparent",
884
+ boxShadow: "none",
903
885
  transitionDuration: "var(--og-session-chrome-duration)",
904
886
  transitionTimingFunction: "var(--_og-session-chrome-ease)",
905
887
  }}
@@ -915,9 +897,9 @@ export function SessionChrome({
915
897
  >
916
898
  <div
917
899
  ref={railRef}
918
- className={cn("relative flex items-center", compact ? "flex-nowrap" : "flex-wrap")}
900
+ className="relative flex flex-nowrap items-center"
919
901
  style={{
920
- gap: compact ? "10px" : "var(--og-session-chrome-chip-gap)",
902
+ gap: "10px",
921
903
  }}
922
904
  >
923
905
  <motion.div
@@ -938,9 +920,7 @@ export function SessionChrome({
938
920
  transition={{ duration: shellDuration, ease }}
939
921
  />
940
922
  {signals
941
- .filter(
942
- (signal) => !compact || !["incoming", "agents", "commands"].includes(signal.id),
943
- )
923
+ .filter((signal) => !["incoming", "agents", "commands"].includes(signal.id))
944
924
  .map((signal) => {
945
925
  const selected = active === signal.id;
946
926
  return (
@@ -956,8 +936,8 @@ export function SessionChrome({
956
936
  }
957
937
  className={cn(
958
938
  "group/signal relative z-[1] inline-flex min-w-0 max-w-full items-center rounded-og-md",
959
- compact && "bg-og-surface-2/60 px-0.5",
960
- compact && selected && "bg-og-surface-3",
939
+ "bg-og-surface-2/60 px-0.5",
940
+ selected && "bg-og-surface-3",
961
941
  )}
962
942
  >
963
943
  <button
@@ -968,7 +948,7 @@ export function SessionChrome({
968
948
  aria-expanded={selected}
969
949
  aria-controls={panelId}
970
950
  aria-label={
971
- compact && signal.id === "goal"
951
+ signal.id === "goal"
972
952
  ? `Goal · ${goalState === "pursuing" ? "Running" : signal.label}`
973
953
  : selected
974
954
  ? `Close ${signal.label}`
@@ -976,9 +956,9 @@ export function SessionChrome({
976
956
  }
977
957
  data-testid={`session-chrome-${signal.id}`}
978
958
  data-og-session-chrome-signal={signal.id}
979
- title={signal.title ?? (compact ? signal.label : undefined)}
959
+ title={signal.title ?? signal.label}
980
960
  onClick={() => {
981
- if (compact) setActivityOpen(false);
961
+ setActivityOpen(false);
982
962
  setActive(selected ? null : signal.id);
983
963
  }}
984
964
  className={cn(
@@ -1007,43 +987,21 @@ export function SessionChrome({
1007
987
  <span className={cn("shrink-0", toneClass(signal.tone, selected))}>
1008
988
  {signal.icon}
1009
989
  </span>
1010
- <span
1011
- className={cn(
1012
- "font-medium text-og-fg",
1013
- compact ? "min-w-0 truncate" : "shrink-0",
1014
- )}
1015
- >
1016
- {compact && signal.id === "goal" ? "Goal · " : null}
1017
- {compact && signal.id === "goal"
990
+ <span className={cn("font-medium text-og-fg", "min-w-0 truncate")}>
991
+ {signal.id === "goal" ? "Goal · " : null}
992
+ {signal.id === "goal"
1018
993
  ? goalState === "pursuing"
1019
994
  ? "Running"
1020
995
  : signal.label
1021
- : compact && signal.id === "queue"
1022
- ? `${queuedTurns.length + optimisticQueued.length} queued`
996
+ : signal.id === "queue"
997
+ ? queue.error || queue.mutationError
998
+ ? signal.label
999
+ : `${queuedTurns.length + optimisticQueued.length} queued`
1023
1000
  : signal.label}
1024
1001
  </span>
1025
- {signal.detail && !compact ? (
1026
- <>
1027
- <span aria-hidden className="shrink-0 text-og-fg-subtle/60">
1028
- ·
1029
- </span>
1030
- <span className="min-w-0 max-w-[8.5rem] truncate text-og-fg sm:max-w-[12rem]">
1031
- {signal.detail}
1032
- </span>
1033
- </>
1034
- ) : null}
1035
- {selected && !compact ? (
1036
- <span
1037
- data-testid="session-chrome-close"
1038
- className="ml-0.5 inline-flex size-4 shrink-0 items-center justify-center rounded-og-sm text-og-fg-subtle transition-colors group-hover:text-og-fg pointer-coarse:size-5"
1039
- aria-hidden
1040
- >
1041
- <XIcon className="size-3" />
1042
- </span>
1043
- ) : null}
1044
1002
  </button>
1045
1003
  {signal.id === "goal" && goal && record && !readOnly ? (
1046
- <div className={cn("flex shrink-0 items-center pr-1", compact && "pl-0.5")}>
1004
+ <div className="flex shrink-0 items-center pr-1 pl-0.5">
1047
1005
  {record.status === "active" || record.status === "paused" ? (
1048
1006
  <IconAction
1049
1007
  label={record.status === "paused" ? "Resume goal" : "Pause goal"}
@@ -1075,7 +1033,7 @@ export function SessionChrome({
1075
1033
  </IconAction>
1076
1034
  </div>
1077
1035
  ) : signal.id === "queue" && canMutateQueue && queuedTurns[0] ? (
1078
- <div className={cn("flex shrink-0 items-center pr-1", compact && "pl-0.5")}>
1036
+ <div className="flex shrink-0 items-center pr-1 pl-0.5">
1079
1037
  <IconAction
1080
1038
  label="Steer first queued message"
1081
1039
  text="Steer"
@@ -1092,11 +1050,11 @@ export function SessionChrome({
1092
1050
  </div>
1093
1051
  );
1094
1052
  })}
1095
- {compact &&
1096
- signals.some((signal) => ["incoming", "agents", "commands"].includes(signal.id)) ? (
1053
+ {signals.some((signal) => ["incoming", "agents", "commands"].includes(signal.id)) ? (
1097
1054
  <button
1098
1055
  type="button"
1099
1056
  aria-label="Session activity"
1057
+ aria-describedby={commandsCount > 0 ? `${panelId}-commands-count` : undefined}
1100
1058
  title={activityOpen ? "Close activity" : "Inbox, agents and commands"}
1101
1059
  aria-controls={panelId}
1102
1060
  aria-expanded={activityOpen}
@@ -1105,9 +1063,11 @@ export function SessionChrome({
1105
1063
  setActive(
1106
1064
  activityOpen
1107
1065
  ? null
1108
- : (signals.find((signal) =>
1109
- ["incoming", "agents", "commands"].includes(signal.id),
1110
- )?.id ?? null),
1066
+ : commandsCount > 0 && hasCommandsPanel
1067
+ ? "commands"
1068
+ : (signals.find((signal) =>
1069
+ ["incoming", "agents", "commands"].includes(signal.id),
1070
+ )?.id ?? null),
1111
1071
  );
1112
1072
  }}
1113
1073
  className={cn(
@@ -1119,6 +1079,17 @@ export function SessionChrome({
1119
1079
  >
1120
1080
  <ActivityIcon className="size-3.5" />
1121
1081
  <span className="sr-only">Activity</span>
1082
+ {commandsCount > 0 ? (
1083
+ <>
1084
+ <span
1085
+ aria-hidden="true"
1086
+ className="size-1.5 shrink-0 animate-og-pulse rounded-full bg-og-fg-muted motion-reduce:animate-none"
1087
+ />
1088
+ <span id={`${panelId}-commands-count`}>
1089
+ {commandsCount} command{commandsCount === 1 ? "" : "s"}
1090
+ </span>
1091
+ </>
1092
+ ) : null}
1122
1093
  {incoming.length > 0 ? (
1123
1094
  <span className="size-1.5 rounded-full bg-og-accent" />
1124
1095
  ) : null}
@@ -1142,7 +1113,7 @@ export function SessionChrome({
1142
1113
  </div>
1143
1114
 
1144
1115
  <AnimatePresence initial={false}>
1145
- {compact && activityOpen ? (
1116
+ {activityOpen ? (
1146
1117
  <motion.div
1147
1118
  key="activity-tabs"
1148
1119
  initial={{ height: 0, opacity: 0 }}
@@ -1201,11 +1172,9 @@ export function SessionChrome({
1201
1172
  <div
1202
1173
  className={cn(
1203
1174
  "relative overflow-y-auto overscroll-contain",
1204
- compact
1205
- ? activityOpen
1206
- ? "rounded-b-og-lg bg-og-surface-2/50"
1207
- : "mt-1 rounded-og-lg bg-og-surface-2/50"
1208
- : "border-t border-og-border/50",
1175
+ activityOpen
1176
+ ? "rounded-b-og-lg bg-og-surface-2/50"
1177
+ : "mt-1 rounded-og-lg bg-og-surface-2/50",
1209
1178
  )}
1210
1179
  style={{
1211
1180
  maxHeight: "var(--og-session-chrome-panel-max-height)",
@@ -0,0 +1,18 @@
1
+ import {
2
+ useSessionBackgroundCommands,
3
+ type UseSessionBackgroundCommandsOptions,
4
+ } from "../hooks/use-session-background-commands";
5
+ import { SessionCommandsPanel } from "./session-commands-panel";
6
+
7
+ /** Mount in the open activity panel so closed chrome never polls commands. */
8
+ export function SessionCommands({
9
+ sessionId,
10
+ readOnly = false,
11
+ ...options
12
+ }: UseSessionBackgroundCommandsOptions & { sessionId: string; readOnly?: boolean }) {
13
+ const commands = useSessionBackgroundCommands(sessionId, {
14
+ pollIntervalMs: 3_000,
15
+ ...options,
16
+ });
17
+ return <SessionCommandsPanel commands={commands} readOnly={readOnly} />;
18
+ }
@@ -0,0 +1,97 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import type { AuthNeededItem } from "../timeline/types";
3
+ import { sessionAuthRecommendation } from "../session-auth-recommendation";
4
+ import {
5
+ SessionMcpCapabilityCard,
6
+ type SessionMcpCapabilityCardProps,
7
+ } from "./session-mcp-capability-card";
8
+
9
+ export type SessionConnectionRequestProps = Omit<
10
+ SessionMcpCapabilityCardProps,
11
+ "capabilityId" | "name" | "rationale"
12
+ > & {
13
+ request: AuthNeededItem;
14
+ };
15
+
16
+ /** Resolve native reauthorization notices with the same exact-identity rules
17
+ * as the console. Recommendations never supply endpoints or account authority. */
18
+ export function SessionConnectionRequest(props: SessionConnectionRequestProps) {
19
+ const [client, setClient] = useState(props.client);
20
+ const [generation, setGeneration] = useState(0);
21
+ if (client !== props.client) {
22
+ setClient(props.client);
23
+ setGeneration(generation + 1);
24
+ }
25
+ return (
26
+ <ScopedRequest
27
+ key={`${generation}:${props.workspaceId}:${props.sessionId}:${props.request.id}`}
28
+ {...props}
29
+ />
30
+ );
31
+ }
32
+
33
+ function ScopedRequest({ request, ...props }: SessionConnectionRequestProps) {
34
+ const [resolved, setResolved] = useState<AuthNeededItem | null>(null);
35
+ const [error, setError] = useState<string | null>(null);
36
+ const [retry, setRetry] = useState(0);
37
+ const requestRef = useRef(request);
38
+ requestRef.current = request;
39
+ useEffect(() => {
40
+ let current = true;
41
+ const event = requestRef.current;
42
+ setResolved(null);
43
+ setError(null);
44
+ void props.client
45
+ .listCapabilities(props.workspaceId)
46
+ .then((catalog) => {
47
+ if (!current) return;
48
+ const recommendation = sessionAuthRecommendation(event, catalog.items);
49
+ const candidate = catalog.items.find(
50
+ (entry) => entry.id === recommendation?.capability?.id,
51
+ );
52
+ if (!recommendation || candidate?.kind !== "mcp" || candidate.authKind !== "oauth2") {
53
+ setError(
54
+ "This request could not be matched to one available OAuth integration. Review its connection settings before continuing.",
55
+ );
56
+ return;
57
+ }
58
+ setResolved(recommendation);
59
+ })
60
+ .catch(() => {
61
+ if (current)
62
+ setError("Couldn't load connection details. Retry to check the current integration.");
63
+ });
64
+ return () => {
65
+ current = false;
66
+ };
67
+ }, [
68
+ props.client,
69
+ props.workspaceId,
70
+ request.id,
71
+ request.serverId,
72
+ request.connectionId,
73
+ request.authoritySource,
74
+ request.reason,
75
+ request.capability?.id,
76
+ retry,
77
+ ]);
78
+ if (!resolved?.capability)
79
+ return (
80
+ <div className="og-session-capability-setup">
81
+ <p role={error ? "alert" : "status"}>{error ?? "Loading connection details…"}</p>
82
+ {error ? (
83
+ <button type="button" onClick={() => setRetry((value) => value + 1)}>
84
+ Retry
85
+ </button>
86
+ ) : null}
87
+ </div>
88
+ );
89
+ return (
90
+ <SessionMcpCapabilityCard
91
+ {...props}
92
+ capabilityId={resolved.capability.id}
93
+ name={resolved.capability.name}
94
+ rationale={resolved.capability.rationale}
95
+ />
96
+ );
97
+ }