@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
package/src/plugin-discovery.tsx
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import { createDiscoveryCache } from "./discovery-cache";
|
|
1
2
|
import { useEffect, useRef, useState } from "react";
|
|
2
3
|
import type { PluginDiscoveryItem, PluginDiscoveryPage } from "@opengeni/sdk";
|
|
4
|
+
import { BoxesIcon } from "lucide-react";
|
|
5
|
+
import { CapabilityCatalogRow } from "./capability-catalog-row";
|
|
6
|
+
import { ConnectionLogo } from "./connection-logo";
|
|
7
|
+
|
|
8
|
+
const discoveryCache = createDiscoveryCache<PluginDiscoveryPage>();
|
|
9
|
+
const EMPTY_INSTALLED_IDS: ReadonlySet<string> = new Set();
|
|
10
|
+
|
|
3
11
|
export type PluginDiscoveryProps = {
|
|
4
12
|
client: {
|
|
5
13
|
discoverPlugins(
|
|
@@ -9,30 +17,43 @@ export type PluginDiscoveryProps = {
|
|
|
9
17
|
};
|
|
10
18
|
workspaceId: string;
|
|
11
19
|
query: string;
|
|
20
|
+
/** Initially selected registry. Users can still switch registries or browse all. */
|
|
21
|
+
defaultProvider?: "" | "openai" | "anthropic";
|
|
22
|
+
/** Discovery identities already installed in the current workspace. */
|
|
23
|
+
installedIds?: ReadonlySet<string>;
|
|
24
|
+
resultLimit?: number;
|
|
25
|
+
onShowMore?: () => void;
|
|
12
26
|
onOpen: (item: PluginDiscoveryItem) => void;
|
|
13
27
|
};
|
|
14
28
|
export function PluginDiscovery(props: PluginDiscoveryProps) {
|
|
15
|
-
const [provider, setProvider] = useState("");
|
|
29
|
+
const [provider, setProvider] = useState(props.defaultProvider ?? "");
|
|
16
30
|
return (
|
|
17
|
-
<section
|
|
31
|
+
<section
|
|
32
|
+
className={`og-plugin-discovery ${props.resultLimit ? "og-catalog-overview" : ""}`}
|
|
33
|
+
aria-label="Discover plugins"
|
|
34
|
+
>
|
|
18
35
|
<header>
|
|
19
|
-
<h3>Browse plugins</h3>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
<h3>{props.resultLimit ? "Plugins" : "Browse plugins"}</h3>
|
|
37
|
+
{!props.resultLimit ? (
|
|
38
|
+
<div className="og-plugin-filters" role="group" aria-label="Plugin registry">
|
|
39
|
+
{(
|
|
40
|
+
[
|
|
41
|
+
{ value: "", label: "All" },
|
|
42
|
+
{ value: "openai", label: "OpenAI plugin registry" },
|
|
43
|
+
{ value: "anthropic", label: "Anthropic plugin registry" },
|
|
44
|
+
] as const
|
|
45
|
+
).map((option) => (
|
|
46
|
+
<button
|
|
47
|
+
key={option.value}
|
|
48
|
+
type="button"
|
|
49
|
+
aria-pressed={provider === option.value}
|
|
50
|
+
onClick={() => setProvider(option.value)}
|
|
51
|
+
>
|
|
52
|
+
{option.label}
|
|
53
|
+
</button>
|
|
54
|
+
))}
|
|
55
|
+
</div>
|
|
56
|
+
) : null}
|
|
36
57
|
</header>
|
|
37
58
|
<Results
|
|
38
59
|
key={`${props.workspaceId}:${props.query}:${provider}`}
|
|
@@ -47,39 +68,49 @@ function Results({
|
|
|
47
68
|
workspaceId,
|
|
48
69
|
query,
|
|
49
70
|
provider,
|
|
71
|
+
installedIds = EMPTY_INSTALLED_IDS,
|
|
50
72
|
onOpen,
|
|
73
|
+
resultLimit,
|
|
74
|
+
onShowMore,
|
|
51
75
|
}: PluginDiscoveryProps & { provider: string }) {
|
|
52
|
-
const [
|
|
76
|
+
const initial = discoveryCache.peek(client, JSON.stringify([workspaceId, query, provider, 0]));
|
|
77
|
+
const [items, setItems] = useState<PluginDiscoveryItem[]>(initial?.items ?? []);
|
|
53
78
|
const [offset, setOffset] = useState(0);
|
|
54
|
-
const [next, setNext] = useState<number | null>(null);
|
|
55
|
-
const [total, setTotal] = useState(0);
|
|
56
|
-
const [loading, setLoading] = useState(
|
|
79
|
+
const [next, setNext] = useState<number | null>(initial?.nextOffset ?? null);
|
|
80
|
+
const [total, setTotal] = useState(initial?.total ?? 0);
|
|
81
|
+
const [loading, setLoading] = useState(!initial);
|
|
57
82
|
const [error, setError] = useState(false);
|
|
58
83
|
const [retry, setRetry] = useState(0);
|
|
59
84
|
const sentinel = useRef<HTMLDivElement>(null);
|
|
60
85
|
useEffect(() => {
|
|
61
86
|
let active = true;
|
|
62
|
-
|
|
87
|
+
const cacheKey = JSON.stringify([workspaceId, query, provider, offset]);
|
|
88
|
+
const cached = discoveryCache.peek(client, cacheKey);
|
|
89
|
+
setLoading(!cached);
|
|
63
90
|
setError(false);
|
|
64
91
|
const timer = setTimeout(
|
|
65
92
|
() => {
|
|
66
|
-
void
|
|
67
|
-
(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
setError(true);
|
|
93
|
+
void discoveryCache
|
|
94
|
+
.read(client, cacheKey, () =>
|
|
95
|
+
client.discoverPlugins(workspaceId, { query, provider, offset }),
|
|
96
|
+
)
|
|
97
|
+
.then(
|
|
98
|
+
(page) => {
|
|
99
|
+
if (!active) return;
|
|
100
|
+
setItems((previous) => (offset ? [...previous, ...page.items] : page.items));
|
|
101
|
+
setTotal(page.total);
|
|
102
|
+
setNext(page.nextOffset);
|
|
77
103
|
setLoading(false);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
104
|
+
},
|
|
105
|
+
() => {
|
|
106
|
+
if (active) {
|
|
107
|
+
setError(true);
|
|
108
|
+
setLoading(false);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
);
|
|
81
112
|
},
|
|
82
|
-
offset ? 0 : 200,
|
|
113
|
+
cached || offset ? 0 : 200,
|
|
83
114
|
);
|
|
84
115
|
return () => {
|
|
85
116
|
active = false;
|
|
@@ -87,7 +118,7 @@ function Results({
|
|
|
87
118
|
};
|
|
88
119
|
}, [client, workspaceId, query, provider, offset, retry]);
|
|
89
120
|
useEffect(() => {
|
|
90
|
-
if (loading || error || next === null || !sentinel.current) return;
|
|
121
|
+
if (resultLimit || loading || error || next === null || !sentinel.current) return;
|
|
91
122
|
const observer = new IntersectionObserver(
|
|
92
123
|
(entries) => {
|
|
93
124
|
if (entries.some((entry) => entry.isIntersecting)) setOffset(next);
|
|
@@ -96,7 +127,7 @@ function Results({
|
|
|
96
127
|
);
|
|
97
128
|
observer.observe(sentinel.current);
|
|
98
129
|
return () => observer.disconnect();
|
|
99
|
-
}, [loading, error, next]);
|
|
130
|
+
}, [loading, error, next, resultLimit]);
|
|
100
131
|
return (
|
|
101
132
|
<>
|
|
102
133
|
{!loading && !error ? (
|
|
@@ -105,39 +136,23 @@ function Results({
|
|
|
105
136
|
</p>
|
|
106
137
|
) : null}
|
|
107
138
|
<div className="og-plugin-discovery-grid">
|
|
108
|
-
{items.map((item) => (
|
|
109
|
-
<
|
|
139
|
+
{items.slice(0, resultLimit).map((item) => (
|
|
140
|
+
<CapabilityCatalogRow
|
|
110
141
|
key={item.id}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{item.
|
|
116
|
-
|
|
142
|
+
data-plugin-id={item.id}
|
|
143
|
+
name={item.displayName.replace(/-/g, " ")}
|
|
144
|
+
description={item.description}
|
|
145
|
+
status={installedIds.has(item.id) ? "added" : "available"}
|
|
146
|
+
statusLabel={installedIds.has(item.id) ? "Installed" : "Available to install"}
|
|
147
|
+
onOpen={() => onOpen(item)}
|
|
148
|
+
icon={
|
|
149
|
+
<ConnectionLogo
|
|
117
150
|
src={item.logoUrl}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
onError={(event) => {
|
|
121
|
-
event.currentTarget.style.display = "none";
|
|
122
|
-
}}
|
|
151
|
+
name={item.displayName}
|
|
152
|
+
fallback={<BoxesIcon aria-hidden="true" />}
|
|
123
153
|
/>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<span className="og-plugin-title">
|
|
127
|
-
<strong>{item.displayName.replace(/-/g, " ")}</strong>
|
|
128
|
-
<small>
|
|
129
|
-
{item.provider === "openai"
|
|
130
|
-
? "OpenAI plugin registry"
|
|
131
|
-
: "Anthropic plugin registry"}
|
|
132
|
-
</small>
|
|
133
|
-
</span>
|
|
134
|
-
<span className="og-plugin-description">{item.description}</span>
|
|
135
|
-
{item.category ? <span className="og-plugin-category">{item.category}</span> : null}
|
|
136
|
-
</span>
|
|
137
|
-
<span className="og-plugin-open" aria-hidden="true">
|
|
138
|
-
›
|
|
139
|
-
</span>
|
|
140
|
-
</button>
|
|
154
|
+
}
|
|
155
|
+
/>
|
|
141
156
|
))}
|
|
142
157
|
</div>
|
|
143
158
|
{loading ? <p role="status">Loading plugins…</p> : null}
|
|
@@ -149,8 +164,13 @@ function Results({
|
|
|
149
164
|
</button>
|
|
150
165
|
</p>
|
|
151
166
|
) : null}
|
|
167
|
+
{resultLimit && !loading && !error && total > resultLimit ? (
|
|
168
|
+
<button className="og-catalog-more" type="button" onClick={onShowMore}>
|
|
169
|
+
View all plugins
|
|
170
|
+
</button>
|
|
171
|
+
) : null}
|
|
152
172
|
<div ref={sentinel}>
|
|
153
|
-
{next !== null && !loading && !error ? (
|
|
173
|
+
{!resultLimit && next !== null && !loading && !error ? (
|
|
154
174
|
<button className="og-plugin-more" type="button" onClick={() => setOffset(next)}>
|
|
155
175
|
Show more plugins
|
|
156
176
|
</button>
|
|
@@ -1105,16 +1105,14 @@ export function RealtimeVoiceModelPanel(props: {
|
|
|
1105
1105
|
|
|
1106
1106
|
return (
|
|
1107
1107
|
<div className="flex min-h-0 flex-1 flex-col" data-testid="realtime-voice-model-panel">
|
|
1108
|
-
<div className="flex shrink-0 items-
|
|
1108
|
+
<div className="flex min-h-10 shrink-0 items-center gap-2 border-b border-og-border px-2 pb-2">
|
|
1109
1109
|
{props.leading}
|
|
1110
|
-
<div className="min-w-0">
|
|
1111
|
-
<div className="text-og-menu font-medium text-og-fg">Voice model</div>
|
|
1112
|
-
<p className="mt-0.5 text-og-control text-og-fg-subtle">
|
|
1113
|
-
Used when you start a voice conversation.
|
|
1114
|
-
</p>
|
|
1115
|
-
</div>
|
|
1110
|
+
<div className="min-w-0 flex-1 text-og-menu font-medium text-og-fg">Voice model</div>
|
|
1116
1111
|
</div>
|
|
1117
|
-
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain
|
|
1112
|
+
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain p-2">
|
|
1113
|
+
<p className="mb-3 text-og-control text-og-fg-muted">
|
|
1114
|
+
Used when you start a voice conversation.
|
|
1115
|
+
</p>
|
|
1118
1116
|
<RealtimeModelPickerMenu
|
|
1119
1117
|
models={props.models}
|
|
1120
1118
|
selectedModel={props.selectedModel}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { AuthNeededItem } from "./timeline/types";
|
|
2
|
+
import type { CapabilityCatalogItem } from "@opengeni/sdk";
|
|
3
|
+
|
|
4
|
+
/** Recovery events identify runtime servers/connections, while recommendation
|
|
5
|
+
* events already carry a catalog id. Never guess among same-domain accounts. */
|
|
6
|
+
export function sessionAuthRecommendation(
|
|
7
|
+
item: AuthNeededItem,
|
|
8
|
+
catalog: CapabilityCatalogItem[],
|
|
9
|
+
): AuthNeededItem | undefined {
|
|
10
|
+
if (
|
|
11
|
+
item.authoritySource === "host" ||
|
|
12
|
+
item.reason === "unsupported_auth" ||
|
|
13
|
+
item.reason === "resource_scope_unavailable"
|
|
14
|
+
)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (item.capability) return item;
|
|
17
|
+
const recoveryServerId = item.canonicalServerId ?? item.serverId;
|
|
18
|
+
const matches = catalog.filter(
|
|
19
|
+
(candidate) =>
|
|
20
|
+
(recoveryServerId !== null &&
|
|
21
|
+
candidate.runtime.mcpServerId === recoveryServerId &&
|
|
22
|
+
recoveryServerId !== "opengeni") ||
|
|
23
|
+
(item.connectionId !== null && candidate.connectionRef?.connectionId === item.connectionId),
|
|
24
|
+
);
|
|
25
|
+
if (matches.length !== 1) return undefined;
|
|
26
|
+
const entry = matches[0]!;
|
|
27
|
+
const personalAccess = item.reason === "personal_authority_unavailable";
|
|
28
|
+
if (
|
|
29
|
+
personalAccess &&
|
|
30
|
+
((item.connectionSubjectScope ?? entry.connectionRef?.subjectScope) !== "subject" ||
|
|
31
|
+
entry.connectionRef?.authoritySource === "host")
|
|
32
|
+
)
|
|
33
|
+
return undefined;
|
|
34
|
+
return {
|
|
35
|
+
...item,
|
|
36
|
+
capability: {
|
|
37
|
+
id: entry.id,
|
|
38
|
+
name: entry.name,
|
|
39
|
+
kind: entry.kind,
|
|
40
|
+
source: entry.source,
|
|
41
|
+
action: "connect",
|
|
42
|
+
rationale: personalAccess
|
|
43
|
+
? "Review permission to use your personal account in this conversation."
|
|
44
|
+
: "Reconnect this integration to use its tools in this conversation.",
|
|
45
|
+
requiredVariables: [],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { FirstPartyMcpToolName, CapabilityCatalogItem } from "@opengeni/sdk";
|
|
2
|
+
import type { OpenGeniBrowserClient as OpenGeniClient } from "@opengeni/sdk/browser";
|
|
3
|
+
|
|
4
|
+
/** Catalog tool discovery may be lazy; selecting the installed server does not
|
|
5
|
+
* require its individual tool inventory to have been fetched. */
|
|
6
|
+
export function sessionCapabilityTools(item: CapabilityCatalogItem) {
|
|
7
|
+
const serverId = item.kind === "mcp" ? item.runtime?.mcpServerId : undefined;
|
|
8
|
+
return serverId && !item.tools.some((tool) => tool.kind === "mcp" && tool.id === serverId)
|
|
9
|
+
? [...item.tools, { kind: "mcp" as const, id: serverId }]
|
|
10
|
+
: item.tools;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** A connection never resets the human's existing tool selection. Default-mode
|
|
14
|
+
* sessions discover newly enabled servers at admission; explicit/inherited
|
|
15
|
+
* policies add only this reviewed capability, with the existing CAS fence. */
|
|
16
|
+
export async function attachSessionCapability(
|
|
17
|
+
client: OpenGeniClient,
|
|
18
|
+
workspaceId: string,
|
|
19
|
+
sessionId: string,
|
|
20
|
+
item: CapabilityCatalogItem,
|
|
21
|
+
stillCurrent: () => boolean = () => true,
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
const tools = sessionCapabilityTools(item);
|
|
24
|
+
if (item.kind === "skill" || tools.length === 0) return;
|
|
25
|
+
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
26
|
+
const session = await client.getSession(workspaceId, sessionId);
|
|
27
|
+
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
28
|
+
// Session.tools includes mandatory runtime servers, which must never be
|
|
29
|
+
// echoed into the human's explicit selection.
|
|
30
|
+
let existing = session.tools.filter(
|
|
31
|
+
(tool) => !session.effectiveToolPolicy?.mandatoryIds?.includes(tool.id),
|
|
32
|
+
);
|
|
33
|
+
if (session.toolPolicy.mode === "workspace_default") {
|
|
34
|
+
const policy = session.effectiveToolPolicy;
|
|
35
|
+
if (!policy || policy.idsTruncated)
|
|
36
|
+
throw new Error(
|
|
37
|
+
"Connected, but the full session tool selection could not be read. Review the Tools picker before continuing.",
|
|
38
|
+
);
|
|
39
|
+
// A workspace may choose a restricted default instead of all enabled servers.
|
|
40
|
+
// Preserve that exact selection if this connection needs an explicit addition.
|
|
41
|
+
// selectedIds contains only explicit additions, and is normally empty for
|
|
42
|
+
// automatic selection. Compare against the resolved defaults instead.
|
|
43
|
+
existing = policy.effectiveIds
|
|
44
|
+
.filter((id) => !policy.mandatoryIds.includes(id))
|
|
45
|
+
.map((id) => ({ kind: "mcp" as const, id }));
|
|
46
|
+
}
|
|
47
|
+
const additions = tools.filter(
|
|
48
|
+
(tool) => !existing.some((current) => current.kind === tool.kind && current.id === tool.id),
|
|
49
|
+
);
|
|
50
|
+
const recommended = Array.isArray(item.metadata?.firstPartyMcpTools)
|
|
51
|
+
? (item.metadata.firstPartyMcpTools.filter(
|
|
52
|
+
(name): name is string => typeof name === "string",
|
|
53
|
+
) as FirstPartyMcpToolName[])
|
|
54
|
+
: [];
|
|
55
|
+
const firstParty = [...new Set([...session.firstPartyMcpTools, ...recommended])];
|
|
56
|
+
if (additions.length === 0 && firstParty.length === session.firstPartyMcpTools.length) return;
|
|
57
|
+
await client.updateSessionToolPolicy(workspaceId, sessionId, {
|
|
58
|
+
mode: "explicit",
|
|
59
|
+
tools: [...existing, ...additions],
|
|
60
|
+
firstPartyMcpTools: firstParty,
|
|
61
|
+
expectedVersion: session.toolPolicyVersion,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Native OAuth callbacks have already saved the provider connection. Finish
|
|
66
|
+
* the separate, version-fenced session tool selection before reporting success. */
|
|
67
|
+
export async function completeSessionCapabilityOAuth(
|
|
68
|
+
client: OpenGeniClient,
|
|
69
|
+
workspaceId: string,
|
|
70
|
+
sessionId: string,
|
|
71
|
+
capabilityId: string | null,
|
|
72
|
+
): Promise<void> {
|
|
73
|
+
if (!capabilityId)
|
|
74
|
+
throw new Error(
|
|
75
|
+
"The authorized integration could not be identified. Review its connection card to finish setup.",
|
|
76
|
+
);
|
|
77
|
+
const connected = (await client.listCapabilities(workspaceId)).items.find(
|
|
78
|
+
(item) => item.id === capabilityId,
|
|
79
|
+
);
|
|
80
|
+
if (!connected?.enabled)
|
|
81
|
+
throw new Error("The connection was authorized, but enabling its tools could not be verified.");
|
|
82
|
+
await attachSessionCapability(client, workspaceId, sessionId, connected);
|
|
83
|
+
}
|
package/src/session-ui.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type { ApprovalSurfaceProps, ApprovalSurfaceMessages } from "./components
|
|
|
13
13
|
export type { HumanInputSurfaceProps } from "./components/human-input-surface";
|
|
14
14
|
export { MessageTimeline, TimelineRow } from "./components/message-timeline";
|
|
15
15
|
export type { MessageTimelineProps } from "./components/message-timeline";
|
|
16
|
+
export type { TimelineSearchTarget } from "./components/timeline-search";
|
|
16
17
|
export { createOlderHistoryLoadReceipt } from "./older-history";
|
|
17
18
|
export type { OlderHistoryLoader, OlderHistoryLoadReceipt } from "./older-history";
|
|
18
19
|
export { UserMessageBody, userMessageLikelyNeedsDisclosure } from "./components/user-message-body";
|
|
@@ -46,6 +47,7 @@ export type {
|
|
|
46
47
|
SessionChromeSignalTone,
|
|
47
48
|
} from "./components/session-chrome";
|
|
48
49
|
export { SessionCommandsPanel } from "./components/session-commands-panel";
|
|
50
|
+
export { SessionCommands } from "./components/session-commands";
|
|
49
51
|
|
|
50
52
|
export { KnowledgeActivityProvider } from "./timeline/knowledge-receipt";
|
|
51
53
|
export type { KnowledgeActivityActions } from "./timeline/knowledge-receipt";
|
package/src/session.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type {
|
|
|
23
23
|
EmbeddedSessionClientOverride as ClientOverride,
|
|
24
24
|
} from "./session-context";
|
|
25
25
|
export { createEmbeddedSessionClient } from "./embedded-session-client";
|
|
26
|
+
export { conversationTimeline } from "./conversation-timeline";
|
|
26
27
|
export type {
|
|
27
28
|
ComposerDraftMapContext,
|
|
28
29
|
CreateEmbeddedSessionClientOptions,
|
package/src/skill-discovery.tsx
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
|
+
import { createDiscoveryCache } from "./discovery-cache";
|
|
3
|
+
import { BookOpenIcon } from "lucide-react";
|
|
4
|
+
import { CapabilityCatalogRow, type CapabilityCatalogStatus } from "./capability-catalog-row";
|
|
2
5
|
|
|
3
6
|
export type SkillDiscoveryItem = {
|
|
4
7
|
id: string;
|
|
@@ -8,6 +11,8 @@ export type SkillDiscoveryItem = {
|
|
|
8
11
|
url: string;
|
|
9
12
|
};
|
|
10
13
|
export type SkillDiscoveryPage = { items: SkillDiscoveryItem[]; nextCursor: null };
|
|
14
|
+
const discoveryCache = createDiscoveryCache<SkillDiscoveryPage>();
|
|
15
|
+
|
|
11
16
|
export type SkillDiscoveryClient = {
|
|
12
17
|
searchPublicSkills(workspaceId: string, query: string): Promise<SkillDiscoveryPage>;
|
|
13
18
|
};
|
|
@@ -18,35 +23,53 @@ export type SkillDiscoveryProps = {
|
|
|
18
23
|
query: string;
|
|
19
24
|
onImport: (url: string) => void;
|
|
20
25
|
canManage: boolean;
|
|
26
|
+
localSkills?: readonly {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
status?: CapabilityCatalogStatus;
|
|
31
|
+
statusLabel?: string;
|
|
32
|
+
onOpen: () => void;
|
|
33
|
+
}[];
|
|
34
|
+
resultLimit?: number;
|
|
35
|
+
onShowMore?: () => void;
|
|
21
36
|
onSearch?: (() => void) | undefined;
|
|
22
37
|
};
|
|
23
38
|
|
|
24
39
|
/** Host-owned query and import flow; browse presentation is reusable independently. */
|
|
25
40
|
export function SkillDiscovery(props: SkillDiscoveryProps) {
|
|
26
41
|
const query = props.query.trim();
|
|
42
|
+
const discoveryQuery = query || "agent";
|
|
27
43
|
return (
|
|
28
44
|
<section className="og-skill-discovery" aria-label="Discover skills">
|
|
29
45
|
<header>
|
|
30
|
-
<h3>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
<h3>{props.resultLimit ? "Skills" : query ? "Search skills" : "Browse skills"}</h3>
|
|
47
|
+
{!props.resultLimit ? (
|
|
48
|
+
<a
|
|
49
|
+
href="https://skills.sh/"
|
|
50
|
+
target="_blank"
|
|
51
|
+
rel="noopener noreferrer"
|
|
52
|
+
className="og-skill-discovery-browse"
|
|
53
|
+
>
|
|
54
|
+
Browse skills.sh ↗
|
|
55
|
+
</a>
|
|
56
|
+
) : null}
|
|
39
57
|
</header>
|
|
40
|
-
{!query
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
{!query ? (
|
|
59
|
+
<p>Agent skills from skills.sh. Search above for another topic.</p>
|
|
60
|
+
) : query.length < 2 ? (
|
|
61
|
+
<p>Enter at least two characters to search skills.sh.</p>
|
|
44
62
|
) : null}
|
|
45
|
-
<DiscoveryResults
|
|
63
|
+
<DiscoveryResults
|
|
64
|
+
key={`${props.workspaceId}:${discoveryQuery}`}
|
|
65
|
+
{...props}
|
|
66
|
+
query={discoveryQuery}
|
|
67
|
+
/>
|
|
46
68
|
</section>
|
|
47
69
|
);
|
|
48
70
|
}
|
|
49
71
|
|
|
72
|
+
const EMPTY_LOCAL_SKILLS: NonNullable<SkillDiscoveryProps["localSkills"]> = [];
|
|
50
73
|
const EMPTY_INSTALLED_SKILLS: NonNullable<SkillDiscoveryProps["installedSkills"]> = [];
|
|
51
74
|
|
|
52
75
|
function DiscoveryResults({
|
|
@@ -55,21 +78,29 @@ function DiscoveryResults({
|
|
|
55
78
|
query,
|
|
56
79
|
canManage,
|
|
57
80
|
onImport,
|
|
81
|
+
resultLimit,
|
|
82
|
+
onShowMore,
|
|
83
|
+
localSkills = EMPTY_LOCAL_SKILLS,
|
|
58
84
|
installedSkills = EMPTY_INSTALLED_SKILLS,
|
|
59
85
|
}: SkillDiscoveryProps) {
|
|
60
|
-
const [
|
|
61
|
-
const
|
|
86
|
+
const cacheKey = JSON.stringify([workspaceId, query]);
|
|
87
|
+
const cached = discoveryCache.peek(client, cacheKey);
|
|
88
|
+
const [result, setResult] = useState<SkillDiscoveryPage | null>(cached ?? null);
|
|
89
|
+
const [loading, setLoading] = useState(query.length >= 2 && !cached);
|
|
62
90
|
const [error, setError] = useState(false);
|
|
63
91
|
const [retry, setRetry] = useState(0);
|
|
64
92
|
useEffect(() => {
|
|
65
93
|
let active = true;
|
|
66
94
|
setError(false);
|
|
67
95
|
if (query.length < 2) return;
|
|
68
|
-
|
|
96
|
+
const existing = discoveryCache.peek(client, cacheKey);
|
|
97
|
+
setLoading(!existing);
|
|
69
98
|
let timer: ReturnType<typeof setTimeout>;
|
|
70
99
|
const search = async (attempt: number) => {
|
|
71
100
|
try {
|
|
72
|
-
const data = await
|
|
101
|
+
const data = await discoveryCache.read(client, cacheKey, () =>
|
|
102
|
+
client.searchPublicSkills(workspaceId, query),
|
|
103
|
+
);
|
|
73
104
|
if (!active) return;
|
|
74
105
|
setResult(data);
|
|
75
106
|
setLoading(false);
|
|
@@ -90,51 +121,65 @@ function DiscoveryResults({
|
|
|
90
121
|
setLoading(false);
|
|
91
122
|
}
|
|
92
123
|
};
|
|
93
|
-
timer = setTimeout(() => void search(0), 300);
|
|
124
|
+
timer = setTimeout(() => void search(0), existing ? 0 : 300);
|
|
94
125
|
return () => {
|
|
95
126
|
active = false;
|
|
96
127
|
clearTimeout(timer);
|
|
97
128
|
};
|
|
98
|
-
}, [client, workspaceId, query, retry]);
|
|
129
|
+
}, [client, workspaceId, query, cacheKey, retry]);
|
|
130
|
+
const remoteSkills =
|
|
131
|
+
result?.items.filter(
|
|
132
|
+
(skill) =>
|
|
133
|
+
!localSkills.some((local) => local.name.toLowerCase() === skill.name.toLowerCase()),
|
|
134
|
+
) ?? [];
|
|
99
135
|
return (
|
|
100
136
|
<div aria-busy={loading}>
|
|
101
137
|
<div className="og-skill-discovery-grid">
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
{localSkills.slice(0, resultLimit).map((skill) => (
|
|
139
|
+
<CapabilityCatalogRow
|
|
140
|
+
key={skill.id}
|
|
141
|
+
name={skill.name}
|
|
142
|
+
description={skill.description}
|
|
143
|
+
icon={<BookOpenIcon aria-hidden="true" />}
|
|
144
|
+
status={skill.status ?? "added"}
|
|
145
|
+
statusLabel={skill.statusLabel}
|
|
146
|
+
onOpen={skill.onOpen}
|
|
147
|
+
/>
|
|
148
|
+
))}
|
|
149
|
+
{remoteSkills
|
|
150
|
+
.slice(
|
|
151
|
+
0,
|
|
152
|
+
resultLimit === undefined ? undefined : Math.max(0, resultLimit - localSkills.length),
|
|
153
|
+
)
|
|
154
|
+
.map((skill) => {
|
|
155
|
+
const installed = installedSkills.some(
|
|
156
|
+
(entry) =>
|
|
157
|
+
entry.sourceUrl.replace(/\/$/, "").toLowerCase() === skill.url.toLowerCase() ||
|
|
158
|
+
(entry.repositoryUrl.replace(/\/$/, "").toLowerCase() ===
|
|
159
|
+
`https://github.com/${skill.source}`.toLowerCase() &&
|
|
160
|
+
entry.name.toLowerCase() === skill.name.toLowerCase()),
|
|
161
|
+
);
|
|
162
|
+
return (
|
|
163
|
+
<CapabilityCatalogRow
|
|
164
|
+
key={skill.id}
|
|
165
|
+
disabled={!canManage}
|
|
166
|
+
name={skill.name}
|
|
167
|
+
description={skill.source}
|
|
168
|
+
icon={<BookOpenIcon aria-hidden="true" />}
|
|
169
|
+
status={!canManage ? "unavailable" : installed ? "added" : "available"}
|
|
170
|
+
statusLabel={
|
|
171
|
+
!canManage ? "Admin required" : installed ? "Installed" : "Available to add"
|
|
172
|
+
}
|
|
173
|
+
onOpen={() => onImport(skill.url)}
|
|
174
|
+
/>
|
|
175
|
+
);
|
|
176
|
+
})}
|
|
137
177
|
</div>
|
|
178
|
+
{resultLimit && remoteSkills.length + localSkills.length > resultLimit && !loading ? (
|
|
179
|
+
<button className="og-catalog-more" type="button" onClick={onShowMore}>
|
|
180
|
+
View all skills
|
|
181
|
+
</button>
|
|
182
|
+
) : null}
|
|
138
183
|
{loading ? <p role="status">Loading skills…</p> : null}
|
|
139
184
|
{error ? (
|
|
140
185
|
<div role="alert" className="og-skill-discovery-error">
|
|
@@ -144,7 +189,7 @@ function DiscoveryResults({
|
|
|
144
189
|
</button>
|
|
145
190
|
</div>
|
|
146
191
|
) : null}
|
|
147
|
-
{result && !
|
|
192
|
+
{result && !remoteSkills.length && !localSkills.length && !loading && !error ? (
|
|
148
193
|
<p role="status">No skills found. Try a different search.</p>
|
|
149
194
|
) : null}
|
|
150
195
|
</div>
|