@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.
- package/README.md +83 -2
- package/dist/artifacts-document.js +1 -1
- package/dist/artifacts-presentation.js +1 -1
- package/dist/artifacts-spreadsheet.js +1 -1
- package/dist/artifacts.js +3 -3
- package/dist/{browser-viewer-NTD6UIPR.js → browser-viewer-LGH4IP47.js} +3 -3
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/capability-logo-fallback.d.ts +3 -0
- package/dist/{chunk-NOSYBS2D.js → chunk-2VHARTBL.js} +2 -1
- package/dist/{chunk-VRBL3F5E.js → chunk-5GAL23FF.js} +10 -2
- package/dist/{chunk-VRBL3F5E.js.map → chunk-5GAL23FF.js.map} +1 -1
- package/dist/chunk-5UONOQ3Y.js +635 -0
- package/dist/chunk-5UONOQ3Y.js.map +1 -0
- package/dist/{chunk-NYESIADM.js → chunk-AKOMC3VO.js} +3 -3
- package/dist/chunk-AKOMC3VO.js.map +1 -0
- package/dist/chunk-BRM6FVZA.js +302 -0
- package/dist/chunk-BRM6FVZA.js.map +1 -0
- package/dist/{chunk-TDSEILUH.js → chunk-D5NOQPLR.js} +1289 -1530
- package/dist/chunk-D5NOQPLR.js.map +1 -0
- package/dist/chunk-D7GAPJ3V.js +1558 -0
- package/dist/chunk-D7GAPJ3V.js.map +1 -0
- package/dist/{chunk-5M6VTFJ5.js → chunk-GBPCZH3Y.js} +2 -2
- package/dist/{chunk-YBM6CQRU.js → chunk-GEN54DU5.js} +4 -4
- package/dist/{chunk-UMECRJRV.js → chunk-HJ5VJ4JB.js} +21 -12
- package/dist/chunk-HJ5VJ4JB.js.map +1 -0
- package/dist/{chunk-R4KRSFGA.js → chunk-IP4BUBKH.js} +3 -3
- package/dist/{chunk-KANFU34B.js → chunk-IYUPN32E.js} +6 -2
- package/dist/chunk-IYUPN32E.js.map +1 -0
- package/dist/{chunk-L2NBTKRX.js → chunk-JKLGJQO5.js} +193 -12
- package/dist/chunk-JKLGJQO5.js.map +1 -0
- package/dist/{chunk-NLTS7JHI.js → chunk-JPHDWKZE.js} +5 -5
- package/dist/{chunk-ICA6R6TW.js → chunk-OWHPKAOV.js} +28 -20
- package/dist/chunk-OWHPKAOV.js.map +1 -0
- package/dist/{chunk-24M4YBCL.js → chunk-PNAVEK2U.js} +4 -4
- package/dist/{chunk-45XEUVTK.js → chunk-UXE5SSNN.js} +293 -463
- package/dist/chunk-UXE5SSNN.js.map +1 -0
- package/dist/{chunk-PEI6RANI.js → chunk-VR4Z73EU.js} +203 -9
- package/dist/chunk-VR4Z73EU.js.map +1 -0
- package/dist/{chunk-HUIKIDM5.js → chunk-XMXRI5JK.js} +65 -18
- package/dist/chunk-XMXRI5JK.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
- package/dist/components/chat-composer.d.ts +13 -3
- package/dist/components/markdown.d.ts +4 -3
- package/dist/components/message-timeline.d.ts +7 -2
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/components/session-capability-frame.d.ts +24 -0
- package/dist/components/session-chrome.d.ts +1 -2
- package/dist/components/session-commands.d.ts +6 -0
- package/dist/components/session-connection-request.d.ts +8 -0
- package/dist/components/session-mcp-capability-card.d.ts +22 -0
- package/dist/components/timeline-search.d.ts +34 -0
- package/dist/composer.js +7 -7
- package/dist/connect-account-identity.d.ts +8 -0
- package/dist/connect-accounts.d.ts +5 -0
- package/dist/connect-chooser.d.ts +6 -0
- package/dist/connect-panel.d.ts +9 -2
- package/dist/connect-setup.d.ts +5 -1
- package/dist/connect.d.ts +5 -21
- package/dist/connect.js +1268 -584
- package/dist/connect.js.map +1 -1
- package/dist/connection-catalog.d.ts +6 -1
- package/dist/connection-discovery.d.ts +12 -0
- package/dist/connection-installed.d.ts +1 -0
- package/dist/connection-logo.d.ts +5 -2
- package/dist/connection-service-logo.d.ts +8 -0
- package/dist/connection-service-presentation.d.ts +13 -0
- package/dist/connection-skeleton.d.ts +5 -0
- package/dist/discovery-cache.d.ts +5 -0
- package/dist/hooks/use-codex-accounts.d.ts +11 -5
- package/dist/hooks/use-connect.d.ts +22 -0
- package/dist/hooks/use-session-events.d.ts +5 -0
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-workspace-sessions.d.ts +2 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +771 -793
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/machines.js +2 -2
- package/dist/mcp-connection-status.d.ts +14 -0
- package/dist/{model-policy-picker-menu-3R336DP2.js → model-policy-picker-menu-RVOQ4ZZG.js} +4 -4
- package/dist/model-policy.d.ts +6 -2
- package/dist/model-policy.js +1 -1
- package/dist/{platform-activity-row-DUE2W74J.js → platform-activity-row-HLOH7FW4.js} +9 -4
- package/dist/platform-activity-row-HLOH7FW4.js.map +1 -0
- package/dist/plugin-discovery.d.ts +6 -0
- package/dist/{queue-surface-implementation-LK3S4BIW.js → queue-surface-implementation-3RIFK2BS.js} +4 -4
- package/dist/realtime.js +22 -22
- package/dist/realtime.js.map +1 -1
- package/dist/session-auth-recommendation.d.ts +5 -0
- package/dist/session-capability-policy.d.ts +12 -0
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +14 -12
- package/dist/session.d.ts +1 -0
- package/dist/session.js +6 -4
- package/dist/skill-discovery.d.ts +11 -0
- package/dist/timeline/compaction-copy.d.ts +10 -0
- package/dist/timeline/projection.d.ts +3 -1
- package/dist/timeline/turn-summary.d.ts +2 -2
- package/dist/timeline/types.d.ts +22 -1
- package/dist/{timeline-annotation-markers-DRSTLEM6.js → timeline-annotation-markers-OY6OWR3V.js} +2 -2
- package/dist/{timeline-annotation-selection-IHLUGVND.js → timeline-annotation-selection-BOXA2TIN.js} +2 -2
- package/dist/{timeline-annotations-dialog-6Q53N5FG.js → timeline-annotations-dialog-RGY3MHL3.js} +3 -3
- package/package.json +3 -3
- package/src/assets/README.md +8 -0
- package/src/assets/gmail.ico +0 -0
- package/src/assets/outlook.ico +0 -0
- package/src/capability-catalog-row.tsx +94 -0
- package/src/capability-logo-fallback.ts +22 -0
- package/src/client.ts +0 -9
- package/src/components/artifacts/document-editor.tsx +63 -6
- package/src/components/artifacts/presentation-editor.tsx +214 -9
- package/src/components/artifacts/spreadsheet-canvas.ts +3 -8
- package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +10 -11
- package/src/components/chat-composer.tsx +26 -3
- package/src/components/composer.tsx +10 -4
- package/src/components/human-input-form.tsx +12 -2
- package/src/components/markdown.tsx +86 -4
- package/src/components/message-timeline.tsx +104 -36
- package/src/components/model-picker.tsx +11 -3
- package/src/components/model-policy-picker-menu.tsx +10 -3
- package/src/components/preview-loading.tsx +154 -0
- package/src/components/sandbox-workspace.tsx +20 -36
- package/src/components/session-capability-frame.tsx +163 -0
- package/src/components/session-chrome.tsx +46 -77
- package/src/components/session-commands.tsx +18 -0
- package/src/components/session-connection-request.tsx +97 -0
- package/src/components/session-conversation.tsx +5 -4
- package/src/components/session-mcp-capability-card.tsx +532 -0
- package/src/components/session-status.tsx +2 -2
- package/src/components/timeline-anchor.tsx +9 -8
- package/src/components/timeline-search.tsx +168 -0
- package/src/components/user-message-body.tsx +7 -0
- package/src/connect-account-identity.tsx +34 -0
- package/src/connect-accounts.tsx +114 -52
- package/src/connect-chooser.tsx +171 -39
- package/src/connect-panel.tsx +73 -9
- package/src/connect-setup.tsx +72 -25
- package/src/connect.ts +13 -22
- package/src/connection-assets.d.ts +4 -0
- package/src/connection-catalog.tsx +61 -39
- package/src/connection-discovery.tsx +235 -0
- package/src/connection-installed.tsx +2 -0
- package/src/connection-logo.tsx +18 -10
- package/src/connection-service-logo.tsx +55 -0
- package/src/connection-service-presentation.ts +46 -0
- package/src/connection-skeleton.tsx +16 -0
- package/src/conversation-timeline.ts +1 -0
- package/src/discovery-cache.ts +42 -0
- package/src/hooks/use-codex-accounts.ts +33 -14
- package/src/hooks/use-composer.ts +4 -5
- package/src/hooks/use-connect.ts +23 -0
- package/src/hooks/use-session-events.ts +114 -1
- package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
- package/src/hooks/use-workspace-sessions.ts +10 -0
- package/src/index.ts +22 -2
- package/src/mcp-connection-status.ts +35 -0
- package/src/model-policy.ts +21 -0
- package/src/plugin-details.tsx +8 -5
- package/src/plugin-discovery.tsx +92 -72
- package/src/realtime/realtime-control.tsx +6 -8
- package/src/session-auth-recommendation.ts +48 -0
- package/src/session-capability-policy.ts +83 -0
- package/src/session-ui.ts +2 -0
- package/src/session.ts +1 -0
- package/src/skill-discovery.tsx +101 -56
- package/src/timeline/compaction-copy.ts +40 -0
- package/src/timeline/platform-activity-row.tsx +8 -3
- package/src/timeline/projection.ts +74 -2
- package/src/timeline/shared.tsx +6 -1
- package/src/timeline/startup-timings.tsx +1 -1
- package/src/timeline/turn-summary.tsx +17 -3
- package/src/timeline/types.ts +22 -2
- package/styles/compiled.css +325 -27
- package/styles/connect.css +90 -15
- package/styles/index.css +46 -0
- package/styles/session-capability.css +42 -0
- package/dist/chunk-45XEUVTK.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-HUIKIDM5.js.map +0 -1
- package/dist/chunk-ICA6R6TW.js.map +0 -1
- package/dist/chunk-IHQII5WO.js +0 -886
- package/dist/chunk-IHQII5WO.js.map +0 -1
- package/dist/chunk-KANFU34B.js.map +0 -1
- package/dist/chunk-L2NBTKRX.js.map +0 -1
- package/dist/chunk-NYESIADM.js.map +0 -1
- package/dist/chunk-PEI6RANI.js.map +0 -1
- package/dist/chunk-TDSEILUH.js.map +0 -1
- package/dist/chunk-UMECRJRV.js.map +0 -1
- package/dist/hooks/use-packs.d.ts +0 -32
- package/dist/platform-activity-row-DUE2W74J.js.map +0 -1
- package/src/hooks/use-packs.ts +0 -157
- /package/dist/{browser-viewer-NTD6UIPR.js.map → browser-viewer-LGH4IP47.js.map} +0 -0
- /package/dist/{chunk-NOSYBS2D.js.map → chunk-2VHARTBL.js.map} +0 -0
- /package/dist/{chunk-5M6VTFJ5.js.map → chunk-GBPCZH3Y.js.map} +0 -0
- /package/dist/{chunk-YBM6CQRU.js.map → chunk-GEN54DU5.js.map} +0 -0
- /package/dist/{chunk-R4KRSFGA.js.map → chunk-IP4BUBKH.js.map} +0 -0
- /package/dist/{chunk-NLTS7JHI.js.map → chunk-JPHDWKZE.js.map} +0 -0
- /package/dist/{chunk-24M4YBCL.js.map → chunk-PNAVEK2U.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-3R336DP2.js.map → model-policy-picker-menu-RVOQ4ZZG.js.map} +0 -0
- /package/dist/{queue-surface-implementation-LK3S4BIW.js.map → queue-surface-implementation-3RIFK2BS.js.map} +0 -0
- /package/dist/{timeline-annotation-markers-DRSTLEM6.js.map → timeline-annotation-markers-OY6OWR3V.js.map} +0 -0
- /package/dist/{timeline-annotation-selection-IHLUGVND.js.map → timeline-annotation-selection-BOXA2TIN.js.map} +0 -0
- /package/dist/{timeline-annotations-dialog-6Q53N5FG.js.map → timeline-annotations-dialog-RGY3MHL3.js.map} +0 -0
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
CodexAccount,
|
|
4
4
|
CodexAccountsResponse,
|
|
5
5
|
CodexRotationSettings,
|
|
6
|
+
SessionCodexAccountsResponse,
|
|
6
7
|
SessionEvent,
|
|
7
8
|
} from "@opengeni/sdk";
|
|
8
9
|
import { useOpenGeni, type ClientOverride } from "../provider";
|
|
@@ -25,6 +26,8 @@ export function isCodexAccountEvent(event: Pick<SessionEvent, "type">): boolean
|
|
|
25
26
|
"turn.completed",
|
|
26
27
|
"turn.failed",
|
|
27
28
|
"turn.cancelled",
|
|
29
|
+
"turn.requires_action",
|
|
30
|
+
"session.status.changed",
|
|
28
31
|
].includes(event.type);
|
|
29
32
|
}
|
|
30
33
|
|
|
@@ -32,11 +35,17 @@ export function isCodexAccountEvent(event: Pick<SessionEvent, "type">): boolean
|
|
|
32
35
|
* The structural slice of the SDK client the Codex-accounts surface needs. Method
|
|
33
36
|
* NAMES + SIGNATURES match `OpenGeniClient` so the real client satisfies it
|
|
34
37
|
* directly; declared structurally (not a hard Pick) so a test/Geni client can
|
|
35
|
-
* stand in. `
|
|
38
|
+
* stand in. `listSessionCodexAccounts` reads the accepted selection and choices
|
|
39
|
+
* atomically; `pinSessionCodexAccount`
|
|
36
40
|
* is the optional mutation (absent ⇒ the indicator hides the switch affordance).
|
|
37
41
|
*/
|
|
38
42
|
export type CodexAccountsClientLike = {
|
|
39
43
|
listCodexAccounts: (workspaceId: string) => Promise<CodexAccountsResponse>;
|
|
44
|
+
/** Required for session-scoped use; never fall back to another pool. */
|
|
45
|
+
listSessionCodexAccounts?: (
|
|
46
|
+
workspaceId: string,
|
|
47
|
+
sessionId: string,
|
|
48
|
+
) => Promise<SessionCodexAccountsResponse>;
|
|
40
49
|
getSession?: (
|
|
41
50
|
workspaceId: string,
|
|
42
51
|
sessionId: string,
|
|
@@ -65,18 +74,21 @@ export type UseCodexAccountsOptions = ClientOverride &
|
|
|
65
74
|
|
|
66
75
|
export type UseCodexAccountsResult = {
|
|
67
76
|
accounts: CodexAccount[];
|
|
68
|
-
/** The
|
|
77
|
+
/** The ACTIVE account for the retry pool, or current workspace pool for new work. */
|
|
69
78
|
activeAccountId: string | null;
|
|
70
79
|
/** The session's PINNED account (null ⇒ following workspace active). */
|
|
71
80
|
pinnedAccountId: string | null;
|
|
72
81
|
/** Current preference only; automatic allocation can choose another account. */
|
|
73
82
|
effectiveAccountId: string | null;
|
|
74
83
|
currentSelection: { credentialId: string | null; waiting: boolean } | null;
|
|
84
|
+
/** The accepted account can differ from the choices for the next turn. */
|
|
85
|
+
currentAccount: CodexAccount | null;
|
|
75
86
|
switchAppliedTo: "waiting_turn" | "next_turn" | null;
|
|
76
87
|
/** The account the session's last turn ACTUALLY ran on (the "Running on:" source). */
|
|
77
88
|
lastAccountId: string | null;
|
|
78
89
|
settings: CodexRotationSettings;
|
|
79
90
|
loading: boolean;
|
|
91
|
+
error: Error | null;
|
|
80
92
|
refresh: () => Promise<void>;
|
|
81
93
|
/**
|
|
82
94
|
* Trigger a LIVE batched /wham/usage refresh across all accounts, then re-read
|
|
@@ -101,6 +113,7 @@ const EMPTY_SETTINGS: CodexRotationSettings = {
|
|
|
101
113
|
};
|
|
102
114
|
|
|
103
115
|
type CodexAccountsState = {
|
|
116
|
+
currentAccount: CodexAccount | null;
|
|
104
117
|
currentSelection: { credentialId: string | null; waiting: boolean } | null;
|
|
105
118
|
accounts: CodexAccount[];
|
|
106
119
|
activeAccountId: string | null;
|
|
@@ -110,6 +123,7 @@ type CodexAccountsState = {
|
|
|
110
123
|
};
|
|
111
124
|
|
|
112
125
|
const EMPTY_STATE: CodexAccountsState = {
|
|
126
|
+
currentAccount: null,
|
|
113
127
|
currentSelection: null,
|
|
114
128
|
accounts: [],
|
|
115
129
|
activeAccountId: null,
|
|
@@ -119,8 +133,8 @@ const EMPTY_STATE: CodexAccountsState = {
|
|
|
119
133
|
};
|
|
120
134
|
|
|
121
135
|
/**
|
|
122
|
-
* The workspace's Codex accounts
|
|
123
|
-
*
|
|
136
|
+
* The workspace's Codex accounts, or session-authorized retry/next-turn choices
|
|
137
|
+
* and accepted current account. Composed like
|
|
124
138
|
* `useMachines`: slow polling (the realtime work is done by the
|
|
125
139
|
* `codex.account.switched` event trigger) + a `pin` mutation.
|
|
126
140
|
* Dual-consumer safe via the structural `CodexAccountsClientLike` surface.
|
|
@@ -132,25 +146,27 @@ export function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCode
|
|
|
132
146
|
const sharedEvents = options.events;
|
|
133
147
|
|
|
134
148
|
const load = useCallback(async (): Promise<CodexAccountsState> => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
149
|
+
if (sessionId) {
|
|
150
|
+
if (!codexClient.listSessionCodexAccounts)
|
|
151
|
+
throw new Error("Session Codex account projection is unavailable");
|
|
152
|
+
return await codexClient.listSessionCodexAccounts(workspaceId, sessionId);
|
|
153
|
+
}
|
|
154
|
+
const acc = await codexClient.listCodexAccounts(workspaceId);
|
|
141
155
|
return {
|
|
142
|
-
currentSelection:
|
|
156
|
+
currentSelection: null,
|
|
157
|
+
currentAccount: null,
|
|
143
158
|
accounts: acc.accounts,
|
|
144
159
|
activeAccountId: acc.activeAccountId,
|
|
145
160
|
settings: acc.settings,
|
|
146
|
-
pinnedAccountId:
|
|
147
|
-
lastAccountId:
|
|
161
|
+
pinnedAccountId: null,
|
|
162
|
+
lastAccountId: null,
|
|
148
163
|
};
|
|
149
164
|
}, [codexClient, workspaceId, sessionId]);
|
|
150
165
|
|
|
151
166
|
const {
|
|
152
167
|
data: loadedData,
|
|
153
168
|
loading,
|
|
169
|
+
error,
|
|
154
170
|
refresh,
|
|
155
171
|
} = usePolledValue(load, {
|
|
156
172
|
pollIntervalMs: options.pollIntervalMs,
|
|
@@ -212,10 +228,12 @@ export function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCode
|
|
|
212
228
|
return result === true;
|
|
213
229
|
}, [codexClient, workspaceId, runUsageMutation, refresh]);
|
|
214
230
|
|
|
215
|
-
|
|
231
|
+
// Reauthorization failure must remove a previously visible session pool.
|
|
232
|
+
const data = (sessionId && error ? null : loadedData) ?? EMPTY_STATE;
|
|
216
233
|
const effectiveAccountId = data.pinnedAccountId ?? data.activeAccountId;
|
|
217
234
|
|
|
218
235
|
return {
|
|
236
|
+
currentAccount: data.currentAccount,
|
|
219
237
|
currentSelection: data.currentSelection,
|
|
220
238
|
switchAppliedTo:
|
|
221
239
|
switchReceipt?.sessionId === sessionId ? (switchReceipt?.appliedTo ?? null) : null,
|
|
@@ -226,6 +244,7 @@ export function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCode
|
|
|
226
244
|
lastAccountId: data.lastAccountId,
|
|
227
245
|
settings: data.settings,
|
|
228
246
|
loading,
|
|
247
|
+
error,
|
|
229
248
|
refresh,
|
|
230
249
|
refreshUsage,
|
|
231
250
|
refreshingUsage,
|
|
@@ -1475,9 +1475,10 @@ export function useComposer(
|
|
|
1475
1475
|
...(wireInput.mcpCredentialUpdates
|
|
1476
1476
|
? { mcpCredentialUpdates: wireInput.mcpCredentialUpdates }
|
|
1477
1477
|
: {}),
|
|
1478
|
-
...(wireInput.
|
|
1479
|
-
? {
|
|
1478
|
+
...(wireInput.connectionAccounts
|
|
1479
|
+
? { connectionAccounts: wireInput.connectionAccounts }
|
|
1480
1480
|
: {}),
|
|
1481
|
+
|
|
1481
1482
|
...(wireInput.personalResourceAttachment
|
|
1482
1483
|
? { personalResourceAttachment: wireInput.personalResourceAttachment }
|
|
1483
1484
|
: {}),
|
|
@@ -1751,9 +1752,7 @@ export function useComposer(
|
|
|
1751
1752
|
...(input.mcpCredentialUpdates
|
|
1752
1753
|
? { mcpCredentialUpdates: input.mcpCredentialUpdates }
|
|
1753
1754
|
: {}),
|
|
1754
|
-
...(input.
|
|
1755
|
-
? { connectionAuthorities: input.connectionAuthorities }
|
|
1756
|
-
: {}),
|
|
1755
|
+
...(input.connectionAccounts ? { connectionAccounts: input.connectionAccounts } : {}),
|
|
1757
1756
|
...(input.personalResourceAttachment
|
|
1758
1757
|
? { personalResourceAttachment: input.personalResourceAttachment }
|
|
1759
1758
|
: {}),
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ConnectController } from "@opengeni/connect";
|
|
2
|
+
import { useMemo, useSyncExternalStore } from "react";
|
|
3
|
+
|
|
4
|
+
/** Internal consumers import the hook directly so setup surfaces stay lazy. */
|
|
5
|
+
export function useConnect(controller: ConnectController) {
|
|
6
|
+
const snapshot = useSyncExternalStore(
|
|
7
|
+
controller.subscribe,
|
|
8
|
+
controller.getSnapshot,
|
|
9
|
+
controller.getSnapshot,
|
|
10
|
+
);
|
|
11
|
+
const actions = useMemo(
|
|
12
|
+
() => ({
|
|
13
|
+
begin: controller.begin.bind(controller),
|
|
14
|
+
recover: controller.recover.bind(controller),
|
|
15
|
+
refresh: controller.refresh.bind(controller),
|
|
16
|
+
waitForAction: controller.waitForAction.bind(controller),
|
|
17
|
+
advance: controller.advance.bind(controller),
|
|
18
|
+
cancel: controller.cancel.bind(controller),
|
|
19
|
+
}),
|
|
20
|
+
[controller],
|
|
21
|
+
);
|
|
22
|
+
return useMemo(() => ({ ...snapshot, ...actions }), [snapshot, actions]);
|
|
23
|
+
}
|
|
@@ -73,6 +73,9 @@ export type UseSessionEventsResult = {
|
|
|
73
73
|
* "Jump to latest" when the tip is not in memory (history view).
|
|
74
74
|
*/
|
|
75
75
|
jumpToLatest: () => Promise<void>;
|
|
76
|
+
/** Replace history with a bounded window containing this exact durable event. */
|
|
77
|
+
jumpToSequence: (sequence: number, options?: { signal?: AbortSignal }) => Promise<boolean>;
|
|
78
|
+
loadingTarget: boolean;
|
|
76
79
|
error: Error | null;
|
|
77
80
|
};
|
|
78
81
|
|
|
@@ -136,6 +139,8 @@ export function useSessionEvents(
|
|
|
136
139
|
const replay = options.replay ?? "windowed";
|
|
137
140
|
const fullReplay = replay === "full" || after !== 0;
|
|
138
141
|
const streamKey = `${workspaceId}\u0000${sessionId ?? ""}\u0000${after}\u0000${fullReplay ? "full" : "windowed"}`;
|
|
142
|
+
const navigationIdentityRef = useRef({ client, streamKey, enabled });
|
|
143
|
+
navigationIdentityRef.current = { client, streamKey, enabled };
|
|
139
144
|
|
|
140
145
|
const [eventWindow, setEventWindow] = useState<BrowserSessionEventWindow>(EMPTY_EVENT_WINDOW);
|
|
141
146
|
const [connectionState, setConnectionState] = useState<SessionEventsConnectionState>("idle");
|
|
@@ -151,6 +156,8 @@ export function useSessionEvents(
|
|
|
151
156
|
const [loadingNewer, setLoadingNewer] = useState(false);
|
|
152
157
|
const [loadingOldest, setLoadingOldest] = useState(false);
|
|
153
158
|
const [loadingLatest, setLoadingLatest] = useState(false);
|
|
159
|
+
const [loadingTarget, setLoadingTarget] = useState(false);
|
|
160
|
+
const loadingTargetRef = useRef(false);
|
|
154
161
|
const [streamEpoch, setStreamEpoch] = useState(0);
|
|
155
162
|
// "history" = reading a non-tip window; live SSE must not fill the gap.
|
|
156
163
|
const [viewMode, setViewMode] = useState<"live" | "history">("live");
|
|
@@ -191,6 +198,8 @@ export function useSessionEvents(
|
|
|
191
198
|
loadingOlderRef.current = false;
|
|
192
199
|
loadingNewerRef.current = false;
|
|
193
200
|
loadingOldestRef.current = false;
|
|
201
|
+
loadingTargetRef.current = false;
|
|
202
|
+
setLoadingTarget(false);
|
|
194
203
|
setLoadingOlder(false);
|
|
195
204
|
setLoadingNewer(false);
|
|
196
205
|
setLoadingOldest(false);
|
|
@@ -525,6 +534,7 @@ export function useSessionEvents(
|
|
|
525
534
|
]);
|
|
526
535
|
|
|
527
536
|
const navigationBusy = (): boolean =>
|
|
537
|
+
loadingTargetRef.current ||
|
|
528
538
|
loadingOlderRef.current ||
|
|
529
539
|
loadingNewerRef.current ||
|
|
530
540
|
loadingOldestRef.current ||
|
|
@@ -826,7 +836,102 @@ export function useSessionEvents(
|
|
|
826
836
|
}
|
|
827
837
|
}, [client, workspaceId, sessionId]);
|
|
828
838
|
|
|
839
|
+
const jumpToSequence = useCallback(
|
|
840
|
+
async (sequence: number, navigationOptions?: { signal?: AbortSignal }): Promise<boolean> => {
|
|
841
|
+
const signal = navigationOptions?.signal;
|
|
842
|
+
if (!sessionId || !Number.isSafeInteger(sequence) || sequence < 1) return false;
|
|
843
|
+
// A navigation that was already cancelled (for example Find closed before
|
|
844
|
+
// the caller could react) must not disturb any current view state.
|
|
845
|
+
if (signal?.aborted) return false;
|
|
846
|
+
// Explicit targets supersede both other targets and adjacent-page requests.
|
|
847
|
+
const generation = ++navigationGenerationRef.current;
|
|
848
|
+
const current = () =>
|
|
849
|
+
generation === navigationGenerationRef.current &&
|
|
850
|
+
navigationIdentityRef.current.client === client &&
|
|
851
|
+
navigationIdentityRef.current.streamKey === streamKey &&
|
|
852
|
+
navigationIdentityRef.current.enabled === enabled;
|
|
853
|
+
const previousMode = viewModeRef.current;
|
|
854
|
+
streamAbortRef.current?.abort();
|
|
855
|
+
generationRef.current += 1;
|
|
856
|
+
viewModeRef.current = "history";
|
|
857
|
+
setViewMode("history");
|
|
858
|
+
loadingOlderRef.current = loadingNewerRef.current = loadingOldestRef.current = false;
|
|
859
|
+
setLoadingOlder(false);
|
|
860
|
+
setLoadingNewer(false);
|
|
861
|
+
setLoadingOldest(false);
|
|
862
|
+
loadingTargetRef.current = true;
|
|
863
|
+
setLoadingTarget(true);
|
|
864
|
+
let published = false;
|
|
865
|
+
try {
|
|
866
|
+
const [before, following] = await Promise.all([
|
|
867
|
+
loadPreviousPage(client, workspaceId, sessionId, sequence + 1, { pageSize: 128 }),
|
|
868
|
+
loadNextPage(client, workspaceId, sessionId, sequence, { pageSize: 128 }),
|
|
869
|
+
]);
|
|
870
|
+
// Close-during-fetch fence: a pending jump must not replace the window
|
|
871
|
+
// after its owner (for example ConversationFind) has gone away. The
|
|
872
|
+
// finally block still settles loadingTarget for this generation.
|
|
873
|
+
if (signal?.aborted) return false;
|
|
874
|
+
if (!current()) return false;
|
|
875
|
+
if (!before.some((event) => event.sequence === sequence)) return false;
|
|
876
|
+
// Bound each side, keeping the target even if its message alone exceeds
|
|
877
|
+
// the ordinary byte budget. Never evict the very event being navigated to.
|
|
878
|
+
const prefix = boundBrowserSessionEventWindow(before, {
|
|
879
|
+
direction: "newest",
|
|
880
|
+
maxBytes: SESSION_EVENT_BROWSER_MAX_BYTES / 2,
|
|
881
|
+
});
|
|
882
|
+
const retained = boundBrowserSessionEventWindow([...prefix.events, ...following], {
|
|
883
|
+
direction: "oldest",
|
|
884
|
+
});
|
|
885
|
+
if (!retained.events.some((event) => event.sequence === sequence)) return false;
|
|
886
|
+
streamAbortRef.current?.abort();
|
|
887
|
+
generationRef.current += 1;
|
|
888
|
+
const status = observeSessionStatus(retained.events, sessionStatusRef);
|
|
889
|
+
eventWindowRef.current = retained;
|
|
890
|
+
oldestSequenceRef.current = retained.events[0]!.sequence;
|
|
891
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
892
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, newestSequenceRef.current ?? 0);
|
|
893
|
+
hasOlderRef.current = !isLogStart(retained.events[0]!);
|
|
894
|
+
// Compact/byte-bounded pages may be short despite more durable history.
|
|
895
|
+
// Only an empty forward page proves the end (same rule as loadNewer).
|
|
896
|
+
hasNewerRef.current =
|
|
897
|
+
following.length > 0 ||
|
|
898
|
+
retained.truncated ||
|
|
899
|
+
(newestSequenceRef.current ?? 0) < lastSequenceRef.current;
|
|
900
|
+
initialWindowLoadedRef.current = true;
|
|
901
|
+
viewModeRef.current = "history";
|
|
902
|
+
setEventWindow(retained);
|
|
903
|
+
setHasOlder(hasOlderRef.current);
|
|
904
|
+
setHasNewer(hasNewerRef.current);
|
|
905
|
+
setInitialLoading(false);
|
|
906
|
+
setViewMode("history");
|
|
907
|
+
setNewerError(null);
|
|
908
|
+
if (status !== undefined) setSessionStatusProjection(status);
|
|
909
|
+
published = true;
|
|
910
|
+
return true;
|
|
911
|
+
} catch (reason) {
|
|
912
|
+
if (!current() || signal?.aborted) return false;
|
|
913
|
+
throw reason;
|
|
914
|
+
} finally {
|
|
915
|
+
if (current()) {
|
|
916
|
+
loadingTargetRef.current = false;
|
|
917
|
+
setLoadingTarget(false);
|
|
918
|
+
if (!published && previousMode === "live") {
|
|
919
|
+
viewModeRef.current = "live";
|
|
920
|
+
setViewMode("live");
|
|
921
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
},
|
|
926
|
+
[client, workspaceId, sessionId, streamKey, enabled],
|
|
927
|
+
);
|
|
928
|
+
|
|
829
929
|
const jumpToLatest = useCallback(async (): Promise<void> => {
|
|
930
|
+
if (loadingTargetRef.current) {
|
|
931
|
+
navigationGenerationRef.current += 1;
|
|
932
|
+
loadingTargetRef.current = false;
|
|
933
|
+
setLoadingTarget(false);
|
|
934
|
+
}
|
|
830
935
|
if (!sessionId || navigationBusy()) {
|
|
831
936
|
return;
|
|
832
937
|
}
|
|
@@ -867,7 +972,13 @@ export function useSessionEvents(
|
|
|
867
972
|
|
|
868
973
|
const identityMatches = stateStreamKey === streamKey;
|
|
869
974
|
const visibleEvents = identityMatches ? eventWindow.events : EMPTY_EVENTS;
|
|
870
|
-
const timeline = useMemo(
|
|
975
|
+
const timeline = useMemo(
|
|
976
|
+
() =>
|
|
977
|
+
buildTimeline(visibleEvents, {
|
|
978
|
+
partialStart: hasOlder || eventWindow.truncated || after > 0,
|
|
979
|
+
}),
|
|
980
|
+
[visibleEvents, hasOlder, eventWindow.truncated, after],
|
|
981
|
+
);
|
|
871
982
|
|
|
872
983
|
return {
|
|
873
984
|
events: visibleEvents,
|
|
@@ -889,6 +1000,8 @@ export function useSessionEvents(
|
|
|
889
1000
|
loadOldest,
|
|
890
1001
|
loadingLatest: !identityMatches ? false : loadingLatest,
|
|
891
1002
|
jumpToLatest,
|
|
1003
|
+
jumpToSequence,
|
|
1004
|
+
loadingTarget: identityMatches && loadingTarget,
|
|
892
1005
|
error: identityMatches ? (newerError ?? error) : null,
|
|
893
1006
|
};
|
|
894
1007
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
SessionEvent,
|
|
3
3
|
SessionMcpApprovalPolicy,
|
|
4
|
-
|
|
4
|
+
SessionMcpApprovalPolicyTarget,
|
|
5
5
|
UpdateSessionMcpApprovalPolicyResponse,
|
|
6
6
|
} from "@opengeni/sdk";
|
|
7
7
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
@@ -38,7 +38,7 @@ export type UseSessionMcpApprovalPolicyOptions = EmbeddedSessionMcpApprovalPolic
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export type UseSessionMcpApprovalPolicyResult = {
|
|
41
|
-
server:
|
|
41
|
+
server: SessionMcpApprovalPolicyTarget | null;
|
|
42
42
|
policy: SessionMcpApprovalPolicy | null;
|
|
43
43
|
loading: boolean;
|
|
44
44
|
error: Error | null;
|
|
@@ -61,7 +61,7 @@ export function useSessionMcpApprovalPolicy(
|
|
|
61
61
|
): UseSessionMcpApprovalPolicyResult {
|
|
62
62
|
const { client, workspaceId } = useEmbeddedSessionMcpApprovalPolicy(options);
|
|
63
63
|
const enabled = (options.enabled ?? true) && Boolean(sessionId && serverId);
|
|
64
|
-
const [override, setOverride] = useState<
|
|
64
|
+
const [override, setOverride] = useState<SessionMcpApprovalPolicyTarget | null>(null);
|
|
65
65
|
const targetKey = `${workspaceId}\u0000${sessionId ?? ""}\u0000${serverId ?? ""}`;
|
|
66
66
|
const authoritativeGeneration = useRef(0);
|
|
67
67
|
const mutationIdentity = useMemo(() => ({ client, targetKey }), [client, targetKey]);
|
|
@@ -75,7 +75,7 @@ export function useSessionMcpApprovalPolicy(
|
|
|
75
75
|
type PolicyRead = {
|
|
76
76
|
targetKey: string;
|
|
77
77
|
generation: number;
|
|
78
|
-
server:
|
|
78
|
+
server: SessionMcpApprovalPolicyTarget | null;
|
|
79
79
|
};
|
|
80
80
|
const load = useCallback(async (): Promise<PolicyRead> => {
|
|
81
81
|
const generation = authoritativeGeneration.current;
|
|
@@ -86,7 +86,15 @@ export function useSessionMcpApprovalPolicy(
|
|
|
86
86
|
return {
|
|
87
87
|
targetKey,
|
|
88
88
|
generation,
|
|
89
|
-
server:
|
|
89
|
+
server:
|
|
90
|
+
session.mcpServers.find((server) => server.id === serverId) ??
|
|
91
|
+
(session.mcpApprovalPolicies && Object.hasOwn(session.mcpApprovalPolicies, serverId)
|
|
92
|
+
? {
|
|
93
|
+
id: serverId,
|
|
94
|
+
source: "workspace",
|
|
95
|
+
requireApproval: session.mcpApprovalPolicies[serverId]!,
|
|
96
|
+
}
|
|
97
|
+
: null),
|
|
90
98
|
};
|
|
91
99
|
}, [client, serverId, sessionId, targetKey, workspaceId]);
|
|
92
100
|
const {
|
|
@@ -11,6 +11,8 @@ export type UseWorkspaceSessionsOptions = ClientOverride & {
|
|
|
11
11
|
/** Return only the complete personal pinned projection. */
|
|
12
12
|
pinsOnly?: boolean | undefined;
|
|
13
13
|
archivedOnly?: boolean | undefined;
|
|
14
|
+
sortBy?: "updatedAt" | "createdAt" | "name" | undefined;
|
|
15
|
+
archiveStatus?: "active" | "archived" | "all" | undefined;
|
|
14
16
|
/** Refresh interval (ms) for fleet/manager views. Off by default. */
|
|
15
17
|
pollIntervalMs?: number | undefined;
|
|
16
18
|
enabled?: boolean | undefined;
|
|
@@ -49,6 +51,8 @@ export function useWorkspaceSessions(
|
|
|
49
51
|
const search = options.search;
|
|
50
52
|
const pinsOnly = options.pinsOnly;
|
|
51
53
|
const archivedOnly = options.archivedOnly;
|
|
54
|
+
const sortBy = options.sortBy;
|
|
55
|
+
const archiveStatus = options.archiveStatus;
|
|
52
56
|
const enabled = options.enabled ?? true;
|
|
53
57
|
const nextReadRevision = useRef(0);
|
|
54
58
|
const nextReadGeneration = useRef(0);
|
|
@@ -61,6 +65,8 @@ export function useWorkspaceSessions(
|
|
|
61
65
|
search ?? "",
|
|
62
66
|
pinsOnly ? "1" : "",
|
|
63
67
|
archivedOnly ? "archived" : "active",
|
|
68
|
+
sortBy ?? "",
|
|
69
|
+
archiveStatus ?? "",
|
|
64
70
|
].join("\u0000");
|
|
65
71
|
const previousQueryKey = useRef(queryKey);
|
|
66
72
|
const queryKeyTransition = previousQueryKey.current !== queryKey;
|
|
@@ -77,6 +83,8 @@ export function useWorkspaceSessions(
|
|
|
77
83
|
...(search !== undefined ? { search } : {}),
|
|
78
84
|
...(pinsOnly ? { pinsOnly: true } : {}),
|
|
79
85
|
...(archivedOnly ? { archivedOnly: true } : {}),
|
|
86
|
+
...(sortBy ? { sortBy } : {}),
|
|
87
|
+
...(archiveStatus ? { archiveStatus } : {}),
|
|
80
88
|
signal,
|
|
81
89
|
});
|
|
82
90
|
return {
|
|
@@ -96,6 +104,8 @@ export function useWorkspaceSessions(
|
|
|
96
104
|
search,
|
|
97
105
|
pinsOnly,
|
|
98
106
|
archivedOnly,
|
|
107
|
+
sortBy,
|
|
108
|
+
archiveStatus,
|
|
99
109
|
queryKey,
|
|
100
110
|
],
|
|
101
111
|
);
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
export { sessionAuthRecommendation } from "./session-auth-recommendation";
|
|
2
|
+
export {
|
|
3
|
+
SessionConnectionRequest,
|
|
4
|
+
type SessionConnectionRequestProps,
|
|
5
|
+
} from "./components/session-connection-request";
|
|
6
|
+
export {
|
|
7
|
+
SessionMcpCapabilityCard,
|
|
8
|
+
McpConnectionCard,
|
|
9
|
+
type McpConnectionCardProps,
|
|
10
|
+
type SessionMcpCapabilityCardProps,
|
|
11
|
+
} from "./components/session-mcp-capability-card";
|
|
12
|
+
export {
|
|
13
|
+
SessionCapabilityFrame,
|
|
14
|
+
type SessionCapabilityFrameProps,
|
|
15
|
+
} from "./components/session-capability-frame";
|
|
16
|
+
export {
|
|
17
|
+
attachSessionCapability,
|
|
18
|
+
completeSessionCapabilityOAuth,
|
|
19
|
+
} from "./session-capability-policy";
|
|
1
20
|
export { retainedImageId } from "./components/markdown";
|
|
2
21
|
// oxlint-disable-next-line typescript/triple-slash-reference -- package consumers must load the ambient type for this optional untyped peer without emitting a runtime import.
|
|
3
22
|
/// <reference path="./types/external.d.ts" />
|
|
@@ -217,8 +236,6 @@ export type {
|
|
|
217
236
|
UseRigChangesOptions,
|
|
218
237
|
UseRigChangesResult,
|
|
219
238
|
} from "./hooks/use-rigs";
|
|
220
|
-
export { usePacks } from "./hooks/use-packs";
|
|
221
|
-
export type { UsePacksOptions, UsePacksResult } from "./hooks/use-packs";
|
|
222
239
|
export { useWorkspaces } from "./hooks/use-workspaces";
|
|
223
240
|
export type { UseWorkspacesOptions, UseWorkspacesResult } from "./hooks/use-workspaces";
|
|
224
241
|
export { useBillingUsage } from "./hooks/use-billing-usage";
|
|
@@ -462,6 +479,7 @@ export type { CommandPaletteProps } from "./components/command-palette";
|
|
|
462
479
|
|
|
463
480
|
// Components
|
|
464
481
|
export { ChatComposer } from "./components/chat-composer";
|
|
482
|
+
export { conversationTimeline } from "./conversation-timeline";
|
|
465
483
|
export { SessionConversation } from "./components/session-conversation";
|
|
466
484
|
export type { SessionConversationProps } from "./components/session-conversation";
|
|
467
485
|
export type { ChatComposerProps } from "./components/chat-composer";
|
|
@@ -511,6 +529,7 @@ export { MessageTimeline, TimelineRow } from "./components/message-timeline";
|
|
|
511
529
|
export { GeneratedVideoPlayer } from "./components/generated-video-player";
|
|
512
530
|
export type { GeneratedVideoPlayerProps } from "./components/generated-video-player";
|
|
513
531
|
export type { MessageTimelineProps } from "./components/message-timeline";
|
|
532
|
+
export type { TimelineSearchTarget } from "./components/timeline-search";
|
|
514
533
|
export { UserMessageBody, userMessageLikelyNeedsDisclosure } from "./components/user-message-body";
|
|
515
534
|
export type {
|
|
516
535
|
UserMessageBodyProps,
|
|
@@ -617,6 +636,7 @@ export {
|
|
|
617
636
|
tryParseJson,
|
|
618
637
|
} from "./lib/format";
|
|
619
638
|
export { SessionCommandsPanel } from "./components/session-commands-panel";
|
|
639
|
+
export { SessionCommands } from "./components/session-commands";
|
|
620
640
|
|
|
621
641
|
export { KnowledgeActivityProvider } from "./timeline/knowledge-receipt";
|
|
622
642
|
export type { KnowledgeActivityActions } from "./timeline/knowledge-receipt";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CapabilityCatalogItem, ConnectionMetadata } from "@opengeni/sdk";
|
|
2
|
+
|
|
3
|
+
/** Status only; runtime use is authorized for the authenticated initiating user. */
|
|
4
|
+
export function matchingActiveMcpConnections(
|
|
5
|
+
item: CapabilityCatalogItem,
|
|
6
|
+
connections: ConnectionMetadata[],
|
|
7
|
+
) {
|
|
8
|
+
return matchingMcpConnections(item, connections).filter((entry) => entry.status === "active");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function matchingMcpConnections(item: CapabilityCatalogItem, connections: ConnectionMetadata[]) {
|
|
12
|
+
const ref = item.connectionRef;
|
|
13
|
+
if (!item.enabled || !ref) return [];
|
|
14
|
+
return connections.filter(
|
|
15
|
+
(entry) =>
|
|
16
|
+
entry.status !== "revoked" &&
|
|
17
|
+
entry.kind === "oauth2" &&
|
|
18
|
+
entry.metadata.mcpUrl === (item.mcpUrl ?? item.endpointUrl) &&
|
|
19
|
+
(ref.subjectScope !== "subject"
|
|
20
|
+
? entry.id === ref.connectionId && entry.subjectId === null
|
|
21
|
+
: entry.subjectId !== null),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Installation availability is not account readiness. Revoked history is not actionable. */
|
|
26
|
+
export function mcpConnectionDiscoveryState(
|
|
27
|
+
item: CapabilityCatalogItem,
|
|
28
|
+
connections: ConnectionMetadata[],
|
|
29
|
+
) {
|
|
30
|
+
const matching = matchingMcpConnections(item, connections);
|
|
31
|
+
const active = matching.filter((entry) => entry.status === "active");
|
|
32
|
+
if (active.length === 1) return { status: "added", label: "Connected" } as const;
|
|
33
|
+
if (matching.length > 0) return { status: "attention", label: "Review connection" } as const;
|
|
34
|
+
return { status: "available", label: "Connect" } as const;
|
|
35
|
+
}
|
package/src/model-policy.ts
CHANGED
|
@@ -274,9 +274,11 @@ export function findPickerRow<TCatalog extends ClientModel>(
|
|
|
274
274
|
|
|
275
275
|
export function groupPickerRowsByBillingClass(
|
|
276
276
|
rows: PickerModelRow[],
|
|
277
|
+
options?: { codexOnly?: boolean },
|
|
277
278
|
): Array<{ billingClass: PickerBillingClass; label: string; rows: PickerModelRow[] }>;
|
|
278
279
|
export function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(
|
|
279
280
|
rows: PickerModelRow<TCatalog>[],
|
|
281
|
+
options?: { codexOnly?: boolean },
|
|
280
282
|
): Array<{
|
|
281
283
|
billingClass: PickerBillingClass;
|
|
282
284
|
label: string;
|
|
@@ -284,6 +286,7 @@ export function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(
|
|
|
284
286
|
}>;
|
|
285
287
|
export function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(
|
|
286
288
|
rows: PickerModelRow<TCatalog>[],
|
|
289
|
+
options?: { codexOnly?: boolean },
|
|
287
290
|
): Array<{
|
|
288
291
|
billingClass: PickerBillingClass;
|
|
289
292
|
label: string;
|
|
@@ -307,5 +310,23 @@ export function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(
|
|
|
307
310
|
rows: [row],
|
|
308
311
|
});
|
|
309
312
|
}
|
|
313
|
+
const codexIndex = groups.findIndex((group) => group.billingClass === "codex_subscription");
|
|
314
|
+
const codex = groups[codexIndex];
|
|
315
|
+
const selectableOpenGeni = groups
|
|
316
|
+
.find((group) => group.billingClass === "opengeni_credits")
|
|
317
|
+
?.rows.filter((row) => row.selectable);
|
|
318
|
+
// Presentation only: never change the catalog/default model ordering or selection.
|
|
319
|
+
const onlyFreeOpenGeni =
|
|
320
|
+
selectableOpenGeni !== undefined &&
|
|
321
|
+
selectableOpenGeni.length > 0 &&
|
|
322
|
+
selectableOpenGeni.every((row) => row.catalog.cost === "free");
|
|
323
|
+
if (
|
|
324
|
+
codex &&
|
|
325
|
+
codex.rows.some((row) => row.selectable) &&
|
|
326
|
+
(options?.codexOnly || onlyFreeOpenGeni)
|
|
327
|
+
) {
|
|
328
|
+
groups.splice(codexIndex, 1);
|
|
329
|
+
groups.unshift(codex);
|
|
330
|
+
}
|
|
310
331
|
return groups;
|
|
311
332
|
}
|
package/src/plugin-details.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { pluginMcpUnavailableReason, type PluginDiscoveryItem } from "@opengeni/sdk";
|
|
3
|
+
import { BoxesIcon } from "lucide-react";
|
|
3
4
|
import { Markdown } from "./components/markdown";
|
|
5
|
+
import { ConnectionLogo } from "./connection-logo";
|
|
4
6
|
|
|
5
7
|
const componentLabels: Record<string, string> = {
|
|
6
8
|
skills: "Skills",
|
|
@@ -38,9 +40,8 @@ export function PluginDetails({
|
|
|
38
40
|
const installableCount =
|
|
39
41
|
(item.skills?.length ?? 0) + (item.mcpServers?.filter(supported).length ?? 0);
|
|
40
42
|
const exceedsInstallLimit = installableCount > 64;
|
|
41
|
-
const installable = installableCount > 0 && !exceedsInstallLimit;
|
|
43
|
+
const installable = !contentsUnknown && installableCount > 0 && !exceedsInstallLimit;
|
|
42
44
|
const [expanded, setExpanded] = useState(false);
|
|
43
|
-
const [logoFailed, setLogoFailed] = useState(false);
|
|
44
45
|
const description = item.longDescription || item.description;
|
|
45
46
|
const lengthy = description.length > 700;
|
|
46
47
|
const registryLabel =
|
|
@@ -52,9 +53,11 @@ export function PluginDetails({
|
|
|
52
53
|
return (
|
|
53
54
|
<article className="og-plugin-details">
|
|
54
55
|
<header className="og-plugin-details-header">
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
<ConnectionLogo
|
|
57
|
+
src={item.logoUrl}
|
|
58
|
+
name={item.displayName}
|
|
59
|
+
fallback={<BoxesIcon aria-hidden="true" />}
|
|
60
|
+
/>
|
|
58
61
|
<div>
|
|
59
62
|
<h2>{item.displayName.replace(/-/g, " ")}</h2>
|
|
60
63
|
{item.author?.name ? <p>By {item.author.name}</p> : null}
|