@opengeni/react 0.34.3 → 0.36.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.
- package/dist/{chunk-MEGI4FS6.js → chunk-J2RVJ6JX.js} +2 -1
- package/dist/{chunk-MEGI4FS6.js.map → chunk-J2RVJ6JX.js.map} +1 -1
- package/dist/{chunk-4NMH6UJ3.js → chunk-OKKMZDQF.js} +3 -2
- package/dist/chunk-OKKMZDQF.js.map +1 -0
- package/dist/{chunk-QRYJCWGD.js → chunk-XT7JF2EH.js} +125 -34
- package/dist/chunk-XT7JF2EH.js.map +1 -0
- package/dist/hooks/use-turn-queue.d.ts +3 -1
- package/dist/index.js +3 -3
- package/dist/session-ui.js +2 -2
- package/dist/session.js +2 -2
- package/package.json +2 -2
- package/src/components/markdown.tsx +5 -2
- package/src/components/message-timeline.tsx +149 -32
- package/src/hooks/use-turn-queue.ts +4 -0
- package/src/timeline/projection.ts +1 -0
- package/dist/chunk-4NMH6UJ3.js.map +0 -1
- package/dist/chunk-QRYJCWGD.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComposerDraft, EffectiveSessionControl, SessionEvent, SessionQueueSnapshot, SessionPendingInputPreview, SessionTurn } from "@opengeni/sdk";
|
|
1
|
+
import type { ComposerDraft, EffectiveSessionControl, McpPersonalConnectionSummary, SessionEvent, SessionQueueSnapshot, SessionPendingInputPreview, SessionTurn } from "@opengeni/sdk";
|
|
2
2
|
import { type EmbeddedSessionClientOverride } from "../session-context";
|
|
3
3
|
import { type SessionEventFeedOptions } from "./internal";
|
|
4
4
|
/** Events that can change the authoritative prompt queue or effective control. */
|
|
@@ -15,6 +15,8 @@ export type UseTurnQueueResult = {
|
|
|
15
15
|
pendingInputs: SessionPendingInputPreview[];
|
|
16
16
|
/** Exact pending members projected to join an already-waiting prompt. */
|
|
17
17
|
pendingInputAttachment: SessionQueueSnapshot["pendingInputAttachment"];
|
|
18
|
+
/** Secret-safe personal MCP summaries frozen on the exact active turn. */
|
|
19
|
+
activePersonalConnections: McpPersonalConnectionSummary[];
|
|
18
20
|
effectiveControl: EffectiveSessionControl | null;
|
|
19
21
|
/** The latest interrupted attempt has not yet durably proved physical quiescence. */
|
|
20
22
|
stoppingPreviousAttempt: boolean;
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
useSessionLineage,
|
|
53
53
|
useSessionMcpApprovalPolicy,
|
|
54
54
|
useTurnQueue
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-OKKMZDQF.js";
|
|
56
56
|
import {
|
|
57
57
|
ActivityDisclosure,
|
|
58
58
|
ActivityRail,
|
|
@@ -106,7 +106,7 @@ import {
|
|
|
106
106
|
useLightboxOptional,
|
|
107
107
|
useThemeType,
|
|
108
108
|
v4aToGitFileDiff
|
|
109
|
-
} from "./chunk-
|
|
109
|
+
} from "./chunk-XT7JF2EH.js";
|
|
110
110
|
import {
|
|
111
111
|
buildTimeline,
|
|
112
112
|
creditExhaustedFromEvents,
|
|
@@ -114,7 +114,7 @@ import {
|
|
|
114
114
|
groupTimeline,
|
|
115
115
|
sessionStatusFromEvents,
|
|
116
116
|
toolDisplayName
|
|
117
|
-
} from "./chunk-
|
|
117
|
+
} from "./chunk-J2RVJ6JX.js";
|
|
118
118
|
import {
|
|
119
119
|
Actions,
|
|
120
120
|
AttachButton,
|
package/dist/session-ui.js
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
SessionChrome,
|
|
7
7
|
TimelineRow,
|
|
8
8
|
sessionChromeGoalPillState
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-XT7JF2EH.js";
|
|
10
|
+
import "./chunk-J2RVJ6JX.js";
|
|
11
11
|
import "./chunk-ATSTR5P3.js";
|
|
12
12
|
import "./chunk-TK7G6XLT.js";
|
|
13
13
|
import "./chunk-LAKLF4PA.js";
|
package/dist/session.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
useSessionLineage,
|
|
19
19
|
useSessionMcpApprovalPolicy,
|
|
20
20
|
useTurnQueue
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-OKKMZDQF.js";
|
|
22
22
|
import {
|
|
23
23
|
buildTimeline,
|
|
24
24
|
creditExhaustedFromEvents,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
groupTimeline,
|
|
27
27
|
sessionStatusFromEvents,
|
|
28
28
|
toolDisplayName
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-J2RVJ6JX.js";
|
|
30
30
|
import {
|
|
31
31
|
FILE_ONLY_MESSAGE_TEXT,
|
|
32
32
|
composeSendInput,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.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": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@dnd-kit/utilities": "3.2.2",
|
|
71
71
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
72
72
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
73
|
-
"@opengeni/sdk": "^0.
|
|
73
|
+
"@opengeni/sdk": "^0.36.0",
|
|
74
74
|
"clsx": "^2.1.1",
|
|
75
75
|
"lucide-react": "^1.8.0",
|
|
76
76
|
"motion": "^12.0.0",
|
|
@@ -248,7 +248,10 @@ function MarkdownCodeBlock({ children }: { children?: ReactNode }) {
|
|
|
248
248
|
<CopyButton text={code} label="Copy code" reveal="group-hover" />
|
|
249
249
|
</div>
|
|
250
250
|
</div>
|
|
251
|
-
<pre
|
|
251
|
+
<pre
|
|
252
|
+
tabIndex={0}
|
|
253
|
+
className="overflow-x-auto overflow-y-hidden rounded-og-md bg-og-surface-1/70 px-3 py-2.5 font-og-mono text-og-sm leading-5 text-og-fg-muted [scrollbar-gutter:stable] [&>code]:block [&>code]:border-0 [&>code]:bg-transparent [&>code]:p-0 [&>code]:leading-5 [&>code]:text-inherit"
|
|
254
|
+
>
|
|
252
255
|
{children}
|
|
253
256
|
</pre>
|
|
254
257
|
</div>
|
|
@@ -268,7 +271,7 @@ function MarkdownTable({ children, className, ...props }: ComponentPropsWithoutR
|
|
|
268
271
|
/>
|
|
269
272
|
</div>
|
|
270
273
|
</div>
|
|
271
|
-
<div className="overflow-x-auto">
|
|
274
|
+
<div className="overflow-x-auto" tabIndex={0}>
|
|
272
275
|
<table
|
|
273
276
|
ref={tableRef}
|
|
274
277
|
className={cn("w-full min-w-0 border-collapse text-og-base", className)}
|
|
@@ -22,6 +22,7 @@ import type { ComponentType } from "react";
|
|
|
22
22
|
import { AnimatePresence, motion } from "motion/react";
|
|
23
23
|
import { Collapsible } from "radix-ui";
|
|
24
24
|
import {
|
|
25
|
+
Component,
|
|
25
26
|
memo,
|
|
26
27
|
useCallback,
|
|
27
28
|
useEffect,
|
|
@@ -1158,21 +1159,34 @@ export function MessageTimeline({
|
|
|
1158
1159
|
: 0;
|
|
1159
1160
|
return (
|
|
1160
1161
|
<div key={key} data-og-timeline-group-anchor="" data-og-group-key={key}>
|
|
1161
|
-
<
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1162
|
+
<TimelineGroupRenderBoundary
|
|
1163
|
+
resetKeys={[
|
|
1164
|
+
group,
|
|
1165
|
+
renderMessageText,
|
|
1166
|
+
onOpenSession,
|
|
1167
|
+
onMemoryClick,
|
|
1168
|
+
onReconnect,
|
|
1169
|
+
resolveProviderLogo,
|
|
1170
|
+
toolRegistry,
|
|
1171
|
+
turnSummary,
|
|
1172
|
+
]}
|
|
1173
|
+
>
|
|
1174
|
+
<TimelineGroupView
|
|
1175
|
+
group={group}
|
|
1176
|
+
renderMessageText={renderMessageText}
|
|
1177
|
+
onOpenSession={onOpenSession}
|
|
1178
|
+
onMemoryClick={onMemoryClick}
|
|
1179
|
+
onReconnect={onReconnect}
|
|
1180
|
+
resolveProviderLogo={resolveProviderLogo}
|
|
1181
|
+
toolRegistry={toolRegistry}
|
|
1182
|
+
turnSummary={turnSummary}
|
|
1183
|
+
foldLiveCluster={isAgentProgress(next)}
|
|
1184
|
+
trailingAgentText={trailingAgentTextAfterTurn(group, next)}
|
|
1185
|
+
contextCompactionCount={
|
|
1186
|
+
contextCompactionCount > 0 ? contextCompactionCount : undefined
|
|
1187
|
+
}
|
|
1188
|
+
/>
|
|
1189
|
+
</TimelineGroupRenderBoundary>
|
|
1176
1190
|
</div>
|
|
1177
1191
|
);
|
|
1178
1192
|
})}
|
|
@@ -1420,6 +1434,75 @@ function cancelFrame(id: number): void {
|
|
|
1420
1434
|
window.clearTimeout(id);
|
|
1421
1435
|
}
|
|
1422
1436
|
|
|
1437
|
+
type TimelineGroupRenderBoundaryProps = {
|
|
1438
|
+
children: ReactNode;
|
|
1439
|
+
resetKeys: readonly unknown[];
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
type TimelineGroupRenderBoundaryState = {
|
|
1443
|
+
failed: boolean;
|
|
1444
|
+
resetKeys: readonly unknown[];
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1447
|
+
function timelineRenderResetKeysChanged(
|
|
1448
|
+
previous: readonly unknown[],
|
|
1449
|
+
next: readonly unknown[],
|
|
1450
|
+
): boolean {
|
|
1451
|
+
return (
|
|
1452
|
+
previous.length !== next.length || previous.some((key, index) => !Object.is(key, next[index]))
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* A malformed historical payload or consumer renderer must not take down the
|
|
1458
|
+
* entire conversation. Keep the boundary outside the row component so React
|
|
1459
|
+
* can replace an invalid element type (error #130) with a bounded fallback.
|
|
1460
|
+
*/
|
|
1461
|
+
class TimelineGroupRenderBoundary extends Component<
|
|
1462
|
+
TimelineGroupRenderBoundaryProps,
|
|
1463
|
+
TimelineGroupRenderBoundaryState
|
|
1464
|
+
> {
|
|
1465
|
+
state: TimelineGroupRenderBoundaryState = {
|
|
1466
|
+
failed: false,
|
|
1467
|
+
resetKeys: this.props.resetKeys,
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1470
|
+
static getDerivedStateFromError(): Partial<TimelineGroupRenderBoundaryState> {
|
|
1471
|
+
return { failed: true };
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
static getDerivedStateFromProps(
|
|
1475
|
+
props: TimelineGroupRenderBoundaryProps,
|
|
1476
|
+
state: TimelineGroupRenderBoundaryState,
|
|
1477
|
+
): Partial<TimelineGroupRenderBoundaryState> | null {
|
|
1478
|
+
if (timelineRenderResetKeysChanged(state.resetKeys, props.resetKeys)) {
|
|
1479
|
+
return { failed: false, resetKeys: props.resetKeys };
|
|
1480
|
+
}
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
render(): ReactNode {
|
|
1485
|
+
if (this.state.failed) {
|
|
1486
|
+
return (
|
|
1487
|
+
<div
|
|
1488
|
+
data-testid="timeline-group-render-error"
|
|
1489
|
+
role="status"
|
|
1490
|
+
className="flex items-start gap-2 rounded-lg border border-og-border bg-og-surface-muted px-3 py-2 text-sm text-og-fg-muted"
|
|
1491
|
+
>
|
|
1492
|
+
<TriangleAlertIcon aria-hidden="true" className="mt-0.5 size-4 shrink-0" />
|
|
1493
|
+
<div>
|
|
1494
|
+
<p className="font-medium text-og-fg">Timeline item unavailable</p>
|
|
1495
|
+
<p>
|
|
1496
|
+
This item could not be displayed. The rest of the conversation is still available.
|
|
1497
|
+
</p>
|
|
1498
|
+
</div>
|
|
1499
|
+
</div>
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
return this.props.children;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1423
1506
|
// The full loaded window stays mounted, so settled history rows must be cheap
|
|
1424
1507
|
// on every commit: projection reuses group objects for unchanged groups, so
|
|
1425
1508
|
// memo skips them. Live projection creates a new group object, and
|
|
@@ -1486,7 +1569,10 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
1486
1569
|
// Latch live→folded so a top-level shell that was already mounted open can
|
|
1487
1570
|
// start the settle beat without remounting bare rail → wrapper.
|
|
1488
1571
|
const liveActivitySettle = useLiveSettleFold(activityShouldFold && !insideTurn);
|
|
1489
|
-
const turnDefaultOpen =
|
|
1572
|
+
const turnDefaultOpen =
|
|
1573
|
+
!insideTurn &&
|
|
1574
|
+
group.kind === "turn" &&
|
|
1575
|
+
(group.outcome === "failed" || timelineGroupContainsAuthNeeded(group));
|
|
1490
1576
|
// activity-* → turn-* remount: carry resting state so settleFold does not
|
|
1491
1577
|
// re-open a chip the reader already watched collapse.
|
|
1492
1578
|
if (group.kind === "turn" && foldMemory && !insideTurn) {
|
|
@@ -1578,21 +1664,37 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
1578
1664
|
// more "N steps" was the redundant double fold).
|
|
1579
1665
|
const nestClusters = foldableActivityClusterCount(group.groups) >= 2;
|
|
1580
1666
|
const turnCopyText = collectTurnCopyText(group.groups, trailingAgentText);
|
|
1581
|
-
const body = group.groups.map((child) =>
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1667
|
+
const body = group.groups.map((child) => {
|
|
1668
|
+
const key = timelineGroupKey(child);
|
|
1669
|
+
return (
|
|
1670
|
+
<TimelineGroupRenderBoundary
|
|
1671
|
+
key={key}
|
|
1672
|
+
resetKeys={[
|
|
1673
|
+
child,
|
|
1674
|
+
renderMessageText,
|
|
1675
|
+
onOpenSession,
|
|
1676
|
+
onMemoryClick,
|
|
1677
|
+
onReconnect,
|
|
1678
|
+
resolveProviderLogo,
|
|
1679
|
+
toolRegistry,
|
|
1680
|
+
turnSummary,
|
|
1681
|
+
]}
|
|
1682
|
+
>
|
|
1683
|
+
<TimelineGroupView
|
|
1684
|
+
group={child}
|
|
1685
|
+
renderMessageText={renderMessageText}
|
|
1686
|
+
onOpenSession={onOpenSession}
|
|
1687
|
+
onMemoryClick={onMemoryClick}
|
|
1688
|
+
onReconnect={onReconnect}
|
|
1689
|
+
resolveProviderLogo={resolveProviderLogo}
|
|
1690
|
+
toolRegistry={toolRegistry}
|
|
1691
|
+
turnSummary={turnSummary}
|
|
1692
|
+
insideTurn
|
|
1693
|
+
nestClusterChips={nestClusters}
|
|
1694
|
+
/>
|
|
1695
|
+
</TimelineGroupRenderBoundary>
|
|
1696
|
+
);
|
|
1697
|
+
});
|
|
1596
1698
|
return (
|
|
1597
1699
|
<TurnSummary
|
|
1598
1700
|
items={activityItems}
|
|
@@ -1679,6 +1781,17 @@ function timelineGroupKey(group: TimelineGroup): string {
|
|
|
1679
1781
|
}
|
|
1680
1782
|
}
|
|
1681
1783
|
|
|
1784
|
+
function timelineGroupContainsAuthNeeded(group: TimelineGroup): boolean {
|
|
1785
|
+
switch (group.kind) {
|
|
1786
|
+
case "item":
|
|
1787
|
+
return group.item.kind === "auth-needed";
|
|
1788
|
+
case "activity":
|
|
1789
|
+
return false;
|
|
1790
|
+
case "turn":
|
|
1791
|
+
return group.groups.some(timelineGroupContainsAuthNeeded);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1682
1795
|
function timelineGroupItemIds(group: TimelineGroup): string[] {
|
|
1683
1796
|
switch (group.kind) {
|
|
1684
1797
|
case "item":
|
|
@@ -2433,7 +2546,9 @@ function AuthNeededRow({
|
|
|
2433
2546
|
const [failed, setFailed] = useState(false);
|
|
2434
2547
|
const provider = providerLabel(item.providerDomain);
|
|
2435
2548
|
const unavailable =
|
|
2436
|
-
item.reason === "
|
|
2549
|
+
item.reason === "personal_authority_unavailable" ||
|
|
2550
|
+
item.reason === "unsupported_auth" ||
|
|
2551
|
+
item.reason === "resource_scope_unavailable";
|
|
2437
2552
|
const missing = item.reason === "missing_connection";
|
|
2438
2553
|
const actionLabel = missing ? "Connect" : "Reconnect";
|
|
2439
2554
|
|
|
@@ -2587,6 +2702,8 @@ function authReasonLine(reason: AuthNeededItem["reason"]): string {
|
|
|
2587
2702
|
case "expired":
|
|
2588
2703
|
case "refresh_failed":
|
|
2589
2704
|
return "Its access expired.";
|
|
2705
|
+
case "personal_authority_unavailable":
|
|
2706
|
+
return "This automation was not granted access to your personal connection.";
|
|
2590
2707
|
case "unsupported_auth":
|
|
2591
2708
|
return "This connection cannot authenticate the configured tool endpoint.";
|
|
2592
2709
|
case "resource_scope_unavailable":
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ComposerDraft,
|
|
3
3
|
EffectiveSessionControl,
|
|
4
|
+
McpPersonalConnectionSummary,
|
|
4
5
|
SessionEvent,
|
|
5
6
|
SessionQueueMutationResponse,
|
|
6
7
|
SessionQueueSnapshot,
|
|
@@ -53,6 +54,8 @@ export type UseTurnQueueResult = {
|
|
|
53
54
|
pendingInputs: SessionPendingInputPreview[];
|
|
54
55
|
/** Exact pending members projected to join an already-waiting prompt. */
|
|
55
56
|
pendingInputAttachment: SessionQueueSnapshot["pendingInputAttachment"];
|
|
57
|
+
/** Secret-safe personal MCP summaries frozen on the exact active turn. */
|
|
58
|
+
activePersonalConnections: McpPersonalConnectionSummary[];
|
|
56
59
|
effectiveControl: EffectiveSessionControl | null;
|
|
57
60
|
/** The latest interrupted attempt has not yet durably proved physical quiescence. */
|
|
58
61
|
stoppingPreviousAttempt: boolean;
|
|
@@ -332,6 +335,7 @@ export function useTurnQueue(
|
|
|
332
335
|
queue: visibleSnapshot?.items ?? [],
|
|
333
336
|
pendingInputs: visibleSnapshot?.pendingInputs ?? [],
|
|
334
337
|
pendingInputAttachment: visibleSnapshot?.pendingInputAttachment ?? null,
|
|
338
|
+
activePersonalConnections: visibleSnapshot?.activePersonalConnections ?? [],
|
|
335
339
|
effectiveControl: visibleSnapshot?.effectiveControl ?? null,
|
|
336
340
|
stoppingPreviousAttempt: visibleSnapshot?.stoppingPreviousAttempt ?? false,
|
|
337
341
|
loading: identityMatches ? loading : enabled,
|