@datalayer/core 1.1.66 → 1.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +43 -20
  2. package/lib/api/constants.d.ts +1 -1
  3. package/lib/api/constants.js +1 -1
  4. package/lib/api/contents/attachments.js +3 -7
  5. package/lib/api/contents/datasets.d.ts +48 -0
  6. package/lib/api/contents/datasets.js +44 -7
  7. package/lib/api/contents/runtimeMounts.js +1 -1
  8. package/lib/api/contents/sandboxUid.d.ts +13 -45
  9. package/lib/api/contents/sandboxUid.js +14 -65
  10. package/lib/api/evals/client.d.ts +402 -0
  11. package/lib/api/evals/client.js +359 -0
  12. package/lib/api/evals/index.d.ts +10 -0
  13. package/lib/api/evals/index.js +18 -0
  14. package/lib/api/evals/request.d.ts +43 -0
  15. package/lib/api/evals/request.js +67 -0
  16. package/lib/api/evals/status.d.ts +27 -0
  17. package/lib/api/evals/status.js +122 -0
  18. package/lib/api/evals/types.d.ts +1335 -0
  19. package/lib/api/evals/types.js +5 -0
  20. package/lib/api/iam/authentication.js +6 -4
  21. package/lib/api/iam/connectedAgents.d.ts +29 -0
  22. package/lib/api/iam/connectedAgents.js +35 -0
  23. package/lib/api/iam/healthz.js +2 -2
  24. package/lib/api/iam/identityProviders.d.ts +76 -0
  25. package/lib/api/iam/identityProviders.js +187 -0
  26. package/lib/api/iam/index.d.ts +1 -0
  27. package/lib/api/iam/index.js +1 -0
  28. package/lib/api/iam/mcpPolicy.d.ts +102 -1
  29. package/lib/api/iam/mcpPolicy.js +38 -2
  30. package/lib/api/iam/oauth2.js +18 -12
  31. package/lib/api/iam/profile.d.ts +17 -1
  32. package/lib/api/iam/profile.js +27 -0
  33. package/lib/api/iam/scimTokens.d.ts +79 -0
  34. package/lib/api/iam/scimTokens.js +141 -0
  35. package/lib/api/iam/secrets.js +10 -23
  36. package/lib/api/iam/trials.d.ts +38 -0
  37. package/lib/api/iam/trials.js +66 -0
  38. package/lib/api/index.d.ts +4 -0
  39. package/lib/api/index.js +4 -0
  40. package/lib/api/mcp/observability.d.ts +34 -11
  41. package/lib/api/mcp/observability.js +97 -59
  42. package/lib/api/orchestration/client.d.ts +53 -0
  43. package/lib/api/orchestration/client.js +105 -0
  44. package/lib/api/orchestration/events.d.ts +88 -0
  45. package/lib/api/orchestration/events.js +245 -0
  46. package/lib/api/orchestration/generated.d.ts +693 -0
  47. package/lib/api/orchestration/generated.js +687 -0
  48. package/lib/api/orchestration/index.d.ts +30 -0
  49. package/lib/api/orchestration/index.js +34 -0
  50. package/lib/api/orchestration/lifecycle.d.ts +39 -0
  51. package/lib/api/orchestration/lifecycle.js +57 -0
  52. package/lib/api/orchestration/measures.d.ts +55 -0
  53. package/lib/api/orchestration/measures.js +99 -0
  54. package/lib/api/otel/dashboards.d.ts +56 -0
  55. package/lib/api/otel/dashboards.js +50 -0
  56. package/lib/api/otel/index.d.ts +1 -0
  57. package/lib/api/otel/index.js +1 -0
  58. package/lib/api/otel/metrics.d.ts +16 -0
  59. package/lib/api/otel/metrics.js +15 -0
  60. package/lib/api/scheduler/client.d.ts +32 -0
  61. package/lib/api/scheduler/client.js +42 -0
  62. package/lib/api/scheduler/index.d.ts +8 -0
  63. package/lib/api/scheduler/index.js +16 -0
  64. package/lib/api/scheduler/request.d.ts +28 -0
  65. package/lib/api/scheduler/request.js +50 -0
  66. package/lib/api/scheduler/types.d.ts +88 -0
  67. package/lib/api/scheduler/types.js +5 -0
  68. package/lib/api/spacer/comments.d.ts +97 -0
  69. package/lib/api/spacer/comments.js +45 -0
  70. package/lib/api/spacer/index.d.ts +9 -0
  71. package/lib/api/spacer/index.js +17 -0
  72. package/lib/api/spacer/notebooks.d.ts +33 -0
  73. package/lib/api/spacer/notebooks.js +39 -0
  74. package/lib/api/spacer/request.d.ts +25 -0
  75. package/lib/api/spacer/request.js +46 -0
  76. package/lib/api/spacer/spaces.d.ts +68 -0
  77. package/lib/api/spacer/spaces.js +45 -0
  78. package/lib/api/utils/validation.d.ts +4 -4
  79. package/lib/api/utils/validation.js +10 -7
  80. package/lib/client/auth/strategies.js +1 -1
  81. package/lib/client/constants.d.ts +1 -0
  82. package/lib/client/constants.js +1 -0
  83. package/lib/client/mixins/IAMMixin.js +2 -2
  84. package/lib/components/animation/AnimatedText.d.ts +1 -44
  85. package/lib/components/animation/AnimatedText.js +6 -122
  86. package/lib/components/anonymous/AnonymousKeyExpired.d.ts +56 -0
  87. package/lib/components/anonymous/AnonymousKeyExpired.js +96 -0
  88. package/lib/components/anonymous/AnonymousKeyTimer.d.ts +63 -0
  89. package/lib/components/anonymous/AnonymousKeyTimer.js +140 -0
  90. package/lib/components/anonymous/index.d.ts +7 -0
  91. package/lib/components/anonymous/index.js +15 -0
  92. package/lib/components/avatars/UserAvatar.d.ts +7 -1
  93. package/lib/components/avatars/UserAvatar.js +15 -1
  94. package/lib/components/billing/BillingEntitySelect.d.ts +6 -0
  95. package/lib/components/billing/BillingEntitySelect.js +49 -10
  96. package/lib/components/billing/eligibility.d.ts +22 -0
  97. package/lib/components/billing/eligibility.js +27 -0
  98. package/lib/components/billing/index.d.ts +1 -0
  99. package/lib/components/billing/index.js +1 -0
  100. package/lib/components/checkout/StripeCheckout.d.ts +1 -1
  101. package/lib/components/checkout/StripeCheckout.js +1 -1
  102. package/lib/components/collaboration/LiveEditorCollaborators.d.ts +5 -0
  103. package/lib/components/collaboration/LiveEditorCollaborators.js +3 -1
  104. package/lib/components/display/DatalayerBox.d.ts +2 -3
  105. package/lib/components/display/DatalayerBox.js +1 -1
  106. package/lib/components/display/NavLink.js +3 -2
  107. package/lib/components/display/VisuallyHidden.d.ts +1 -2
  108. package/lib/components/labels/StatusLabels.d.ts +2 -10
  109. package/lib/components/labels/StatusLabels.js +2 -26
  110. package/lib/components/principal/PrincipalAppearance.d.ts +21 -42
  111. package/lib/components/principal/PrincipalAppearance.js +232 -174
  112. package/lib/components/principal/PrincipalAvatar.d.ts +8 -1
  113. package/lib/components/principal/PrincipalAvatar.js +2 -2
  114. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +8 -1
  115. package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
  116. package/lib/components/screencapture/Screencapture.js +46 -3
  117. package/lib/components/sharing/ShareAccessComponent.d.ts +45 -4
  118. package/lib/components/sharing/ShareAccessComponent.js +110 -78
  119. package/lib/components/sharing/SharingEditor.d.ts +5 -1
  120. package/lib/components/sharing/SharingEditor.js +9 -4
  121. package/lib/components/sharing/index.d.ts +1 -0
  122. package/lib/components/sharing/index.js +1 -0
  123. package/lib/components/sharing/sandboxSharing.d.ts +26 -0
  124. package/lib/components/sharing/sandboxSharing.js +47 -0
  125. package/lib/components/spaces/SpaceDetailsOverlay.d.ts +39 -0
  126. package/lib/components/spaces/SpaceDetailsOverlay.js +80 -0
  127. package/lib/components/spaces/SpaceDisplay.d.ts +38 -0
  128. package/lib/components/spaces/SpaceDisplay.js +92 -0
  129. package/lib/components/spaces/index.d.ts +4 -0
  130. package/lib/components/spaces/index.js +7 -0
  131. package/lib/components/spaces/spaceDisplayModel.d.ts +93 -0
  132. package/lib/components/spaces/spaceDisplayModel.js +170 -0
  133. package/lib/config/Configuration.d.ts +4 -4
  134. package/lib/config/Configuration.js +7 -7
  135. package/lib/config/index.d.ts +1 -0
  136. package/lib/config/index.js +1 -0
  137. package/lib/config/planes.d.ts +45 -0
  138. package/lib/config/planes.js +53 -0
  139. package/lib/hooks/cacheConverters.d.ts +0 -7
  140. package/lib/hooks/cacheConverters.js +0 -25
  141. package/lib/hooks/index.d.ts +1 -0
  142. package/lib/hooks/index.js +1 -0
  143. package/lib/hooks/useBackdrop.d.ts +4 -5
  144. package/lib/hooks/useBillingEntityStore.js +3 -0
  145. package/lib/hooks/useCache.d.ts +197 -44
  146. package/lib/hooks/useCache.js +1586 -494
  147. package/lib/hooks/useContents.d.ts +22 -1
  148. package/lib/hooks/useContents.js +90 -21
  149. package/lib/hooks/useMcp.d.ts +87 -1
  150. package/lib/hooks/useMcp.js +248 -13
  151. package/lib/hooks/useNavigate.d.ts +41 -3
  152. package/lib/hooks/useNavigate.js +116 -52
  153. package/lib/hooks/usePrincipalCacheStore.js +3 -0
  154. package/lib/hooks/usePrincipalStore.js +3 -0
  155. package/lib/hooks/useSpaceCacheStore.d.ts +50 -0
  156. package/lib/hooks/useSpaceCacheStore.js +85 -0
  157. package/lib/hooks/useWindowSize.js +1 -1
  158. package/lib/models/CreditsDTO.d.ts +6 -5
  159. package/lib/models/CreditsDTO.js +7 -5
  160. package/lib/models/IAMProvidersSpecs.d.ts +10 -5
  161. package/lib/models/IAMProvidersSpecs.js +0 -23
  162. package/lib/models/McpBinding.d.ts +6 -0
  163. package/lib/models/Organization.d.ts +69 -0
  164. package/lib/models/Organization.js +96 -0
  165. package/lib/models/Secret.d.ts +1 -2
  166. package/lib/models/Secret.js +2 -18
  167. package/lib/models/StartedBy.d.ts +34 -0
  168. package/lib/models/StartedBy.js +36 -0
  169. package/lib/models/Team.d.ts +2 -0
  170. package/lib/models/Team.js +2 -0
  171. package/lib/models/User.d.ts +4 -0
  172. package/lib/models/User.js +3 -0
  173. package/lib/models/UserSettings.d.ts +13 -0
  174. package/lib/models/UserSettings.js +9 -0
  175. package/lib/models/contents/__fixtures__/v1-contracts.json +158 -30
  176. package/lib/navigation/adapters/react-router.d.ts +1 -1
  177. package/lib/navigation/adapters/react-router.js +3 -0
  178. package/lib/navigation/components.js +2 -3
  179. package/lib/routes/publicPaths.js +4 -0
  180. package/lib/state/index.d.ts +1 -0
  181. package/lib/state/index.js +1 -0
  182. package/lib/state/sessionEnd.d.ts +55 -0
  183. package/lib/state/sessionEnd.js +162 -0
  184. package/lib/state/substates/CoreState.js +3 -16
  185. package/lib/state/substates/IAMState.d.ts +3 -3
  186. package/lib/state/substates/IAMState.js +7 -0
  187. package/lib/state/substates/LayoutState.js +12 -0
  188. package/lib/state/substates/NavigationState.d.ts +82 -0
  189. package/lib/state/substates/NavigationState.js +255 -0
  190. package/lib/state/substates/ProfileState.d.ts +1 -0
  191. package/lib/state/substates/ProfileState.js +1 -0
  192. package/lib/state/substates/index.d.ts +1 -0
  193. package/lib/state/substates/index.js +1 -0
  194. package/lib/utils/Lazy.d.ts +20 -0
  195. package/lib/utils/Lazy.js +32 -6
  196. package/lib/utils/Screencapture.d.ts +15 -0
  197. package/lib/utils/Screencapture.js +26 -20
  198. package/lib/utils/WithSuspense.d.ts +11 -1
  199. package/lib/utils/WithSuspense.js +11 -1
  200. package/lib/views/mcp/AdmittedClients.d.ts +44 -0
  201. package/lib/views/mcp/AdmittedClients.js +59 -0
  202. package/lib/views/mcp/ApprovalQueue.d.ts +66 -0
  203. package/lib/views/mcp/ApprovalQueue.js +95 -0
  204. package/lib/views/mcp/ConnectedAgents.js +38 -3
  205. package/lib/views/mcp/EnterpriseConsole.d.ts +1 -1
  206. package/lib/views/mcp/EnterpriseConsole.js +23 -1
  207. package/lib/views/mcp/IdentityProviders.d.ts +33 -0
  208. package/lib/views/mcp/IdentityProviders.js +299 -0
  209. package/lib/views/mcp/McpDashboard.js +90 -7
  210. package/lib/views/mcp/McpHome.js +1 -1
  211. package/lib/views/mcp/McpObservability.d.ts +30 -1
  212. package/lib/views/mcp/McpObservability.js +105 -19
  213. package/lib/views/mcp/NotebookRuns.d.ts +39 -0
  214. package/lib/views/mcp/NotebookRuns.js +44 -0
  215. package/lib/views/mcp/OrganizationPolicy.js +2 -1
  216. package/lib/views/mcp/PersonalPolicy.js +2 -1
  217. package/lib/views/mcp/PolicyForm.d.ts +17 -1
  218. package/lib/views/mcp/PolicyForm.js +25 -3
  219. package/lib/views/mcp/PolicyHistory.d.ts +13 -0
  220. package/lib/views/mcp/PolicyHistory.js +14 -1
  221. package/lib/views/mcp/RunDetail.d.ts +87 -0
  222. package/lib/views/mcp/RunDetail.js +186 -0
  223. package/lib/views/mcp/ScimProvisioning.d.ts +54 -0
  224. package/lib/views/mcp/ScimProvisioning.js +173 -0
  225. package/lib/views/mcp/TeamPolicies.js +66 -5
  226. package/lib/views/mcp/ToolAccess.d.ts +60 -0
  227. package/lib/views/mcp/ToolAccess.js +101 -0
  228. package/lib/views/mcp/TraceTimeline.d.ts +81 -0
  229. package/lib/views/mcp/TraceTimeline.js +132 -0
  230. package/lib/views/mcp/index.d.ts +5 -0
  231. package/lib/views/mcp/index.js +5 -0
  232. package/lib/views/otel/simpleAuthStore.js +3 -0
  233. package/lib/views/secrets/SecretEdit.js +3 -2
  234. package/lib/views/secrets/SecretNew.js +5 -1
  235. package/package.json +271 -264
  236. package/scripts/generate-mcp-types.py +16 -10
  237. package/scripts/generate-orchestration-types.py +920 -0
@@ -0,0 +1,299 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useMemo, useRef, useState } from 'react';
3
+ import { ActionList, ActionMenu, Button, Flash, FormControl, Heading, IconButton, Label, Select, Spinner, Text, TextInput, ToggleSwitch, } from '@primer/react';
4
+ import { Blankslate, DataTable, Dialog, Table, } from '@primer/react/experimental';
5
+ import { Box } from '@datalayer/primer-addons';
6
+ import { KebabHorizontalIcon, KeyIcon, TrashIcon, } from '@primer/octicons-react';
7
+ import { McpErrorBlankslate } from '../../components/mcp/index.js';
8
+ import { useCreateIdentityProvider, useDeleteIdentityProvider, useDomainVerification, useIdentityProviders, useSetIdentityProviderEnabled, useUpdateIdentityProvider, useVerifyDomain, } from '../../hooks/useMcp.js';
9
+ import { useToast } from '../../hooks/index.js';
10
+ import { IdentityProviderInvalid, ORGANIZATION_ROLES, TEAM_ROLES, } from '../../api/iam/identityProviders.js';
11
+ const ROLE_LABELS = {
12
+ organization_owner: 'Organization owner',
13
+ organization_security_auditor: 'Organization security auditor',
14
+ organization_user_reviewer: 'Organization usage reviewer',
15
+ organization_member: 'Organization member',
16
+ team_owner: 'Team owner',
17
+ team_member: 'Team member',
18
+ };
19
+ const roleLabel = (role) => ROLE_LABELS[role] ?? role;
20
+ const isTeamRole = (role) => TEAM_ROLES.includes(role);
21
+ const BLANK = {
22
+ name: '',
23
+ protocol: 'oidc',
24
+ issuer: '',
25
+ clientId: '',
26
+ clientSecretRef: '',
27
+ domains: [],
28
+ groupClaim: 'groups',
29
+ allowEmailLinking: false,
30
+ roleMappings: [],
31
+ enabled: true,
32
+ };
33
+ const domainsToText = (domains) => domains.join(', ');
34
+ const textToDomains = (text) => Array.from(new Set(text
35
+ .split(/[,\s]+/)
36
+ .map(domain => domain.trim().toLowerCase())
37
+ .filter(Boolean)));
38
+ export const IdentityProviders = ({ errorState, orgUid, readOnly = false, showTitle = true, }) => {
39
+ const { enqueueToast } = useToast();
40
+ const providers = useIdentityProviders(orgUid);
41
+ const create = useCreateIdentityProvider(orgUid);
42
+ const update = useUpdateIdentityProvider(orgUid);
43
+ const setEnabled = useSetIdentityProviderEnabled(orgUid);
44
+ const remove = useDeleteIdentityProvider(orgUid);
45
+ const domainVerification = useDomainVerification(orgUid);
46
+ const verifyDomain = useVerifyDomain(orgUid);
47
+ const [editing, setEditing] = useState(null);
48
+ const [creating, setCreating] = useState(false);
49
+ const [draft, setDraft] = useState(BLANK);
50
+ const [domainsText, setDomainsText] = useState('');
51
+ const [refusal, setRefusal] = useState('');
52
+ const [removing, setRemoving] = useState(null);
53
+ const [managingDomains, setManagingDomains] = useState(null);
54
+ // Primer 37 types Dialog's focus refs with React 18's non-nullable
55
+ // `RefObject<HTMLElement>`. React 19's `useRef(null)` is nullable, and this
56
+ // ref is only handed to Dialog, so narrow it once here.
57
+ const returnFocusRef = useRef(null);
58
+ const rows = useMemo(() => (providers.data ?? []).map(provider => ({
59
+ ...provider,
60
+ id: provider.uid,
61
+ })), [providers.data]);
62
+ const openCreate = () => {
63
+ setDraft(BLANK);
64
+ setDomainsText('');
65
+ setRefusal('');
66
+ setCreating(true);
67
+ };
68
+ const openEdit = (provider) => {
69
+ const { uid: _uid, orgUid: _org, verifiedDomains: _verified, createdBy: _by, createdAt: _c, updatedAt: _u, version: _v, ...rest } = provider;
70
+ setDraft(rest);
71
+ setDomainsText(domainsToText(provider.domains));
72
+ setRefusal('');
73
+ setEditing(provider);
74
+ };
75
+ const close = () => {
76
+ setCreating(false);
77
+ setEditing(null);
78
+ setRefusal('');
79
+ };
80
+ const onError = (error) => {
81
+ if (error instanceof IdentityProviderInvalid) {
82
+ // The registry's own words, beside the form: it names what is wrong,
83
+ // and the person can still fix it without guessing.
84
+ setRefusal(error.message);
85
+ return;
86
+ }
87
+ enqueueToast(`Could not save: ${error.message}`, { variant: 'error' });
88
+ };
89
+ const apply = () => {
90
+ setRefusal('');
91
+ const domains = textToDomains(domainsText);
92
+ if (domains.length === 0) {
93
+ setRefusal('At least one email domain is needed; a provider no domain routes ' +
94
+ 'to is one nobody can sign in through.');
95
+ return;
96
+ }
97
+ const payload = { ...draft, domains };
98
+ if (editing) {
99
+ update.mutate({ uid: editing.uid, provider: payload }, {
100
+ onSuccess: () => {
101
+ close();
102
+ enqueueToast('Identity provider saved.', { variant: 'success' });
103
+ },
104
+ onError,
105
+ });
106
+ return;
107
+ }
108
+ create.mutate(payload, {
109
+ onSuccess: () => {
110
+ close();
111
+ enqueueToast('Identity provider registered. Verify a domain before anybody ' +
112
+ 'signs in through it.', { variant: 'success' });
113
+ },
114
+ onError,
115
+ });
116
+ };
117
+ /** Switching a provider off leaves its configuration here; only sign-in
118
+ * through it stops. */
119
+ const toggle = (provider, enabled) => {
120
+ setEnabled.mutate({ uid: provider.uid, enabled }, {
121
+ onError: error => enqueueToast(`Could not change the provider: ${error.message}`, {
122
+ variant: 'error',
123
+ }),
124
+ });
125
+ };
126
+ const confirmRemove = () => {
127
+ const provider = removing;
128
+ if (!provider) {
129
+ return;
130
+ }
131
+ remove.mutate(provider.uid, {
132
+ onSuccess: () => {
133
+ setRemoving(null);
134
+ enqueueToast('Identity provider removed.', { variant: 'success' });
135
+ },
136
+ onError: error => {
137
+ setRemoving(null);
138
+ enqueueToast(`Could not remove: ${error.message}`, {
139
+ variant: 'error',
140
+ });
141
+ },
142
+ });
143
+ };
144
+ const set = (key, value) => setDraft(current => ({ ...current, [key]: value }));
145
+ const setMapping = (index, patch) => {
146
+ setDraft(current => ({
147
+ ...current,
148
+ roleMappings: current.roleMappings.map((mapping, i) => i === index ? { ...mapping, ...patch } : mapping),
149
+ }));
150
+ };
151
+ const addMapping = () => setDraft(current => ({
152
+ ...current,
153
+ roleMappings: [
154
+ ...current.roleMappings,
155
+ { group: '', role: 'organization_member', teamUid: '' },
156
+ ],
157
+ }));
158
+ const removeMapping = (index) => setDraft(current => ({
159
+ ...current,
160
+ roleMappings: current.roleMappings.filter((_, i) => i !== index),
161
+ }));
162
+ const columns = [
163
+ {
164
+ header: 'Provider',
165
+ id: 'name',
166
+ rowHeader: true,
167
+ width: 'growCollapse',
168
+ renderCell: row => (_jsxs(Box, { sx: { display: 'grid' }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: row.name }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: row.issuer })] })),
169
+ },
170
+ {
171
+ header: 'Domains',
172
+ id: 'domains',
173
+ width: 'growCollapse',
174
+ renderCell: row => (_jsxs(Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 1 }, children: [row.domains.length === 0 && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: "none" })), row.domains.map(domain => (_jsxs(Label, { size: "small", variant: row.verifiedDomains.includes(domain) ? 'success' : 'secondary', children: [domain, row.verifiedDomains.includes(domain)
175
+ ? ' · verified'
176
+ : ' · claimed'] }, domain)))] })),
177
+ },
178
+ {
179
+ header: 'On',
180
+ id: 'enabled',
181
+ width: '90px',
182
+ renderCell: row => (_jsxs(_Fragment, { children: [_jsx(Text, { id: `idp-toggle-${row.uid}`, sx: { display: 'none' }, children: `Sign-in through ${row.name}` }), _jsx(ToggleSwitch, { size: "small", checked: row.enabled, disabled: readOnly || setEnabled.isPending, "aria-labelledby": `idp-toggle-${row.uid}`, onClick: () => toggle(row, !row.enabled) })] })),
183
+ },
184
+ {
185
+ header: '',
186
+ id: 'actions',
187
+ width: '48px',
188
+ align: 'end',
189
+ renderCell: row => readOnly ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: "\u2014" })) : (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { variant: "invisible", size: "small", "aria-label": `Actions for ${row.name}`, icon: KebabHorizontalIcon }) }), _jsx(ActionMenu.Overlay, { align: "end", children: _jsxs(ActionList, { children: [_jsx(ActionList.Item, { onSelect: () => openEdit(row), children: "Edit" }), _jsx(ActionList.Item, { onSelect: () => setManagingDomains(row), disabled: row.domains.length === 0, children: "Manage domains" }), _jsx(ActionList.Item, { variant: "danger", onSelect: () => setRemoving(row), children: "Remove" })] }) })] })),
190
+ },
191
+ ];
192
+ if (providers.isError) {
193
+ return (_jsx(McpErrorBlankslate, { state: errorState(providers.error, 'Identity providers'), onRetry: () => providers.refetch() }));
194
+ }
195
+ return (_jsxs(Box, { sx: { display: 'grid', gap: 3, minWidth: 0 }, children: [showTitle && (_jsxs(Box, { sx: {
196
+ display: 'flex',
197
+ alignItems: 'start',
198
+ justifyContent: 'space-between',
199
+ gap: 3,
200
+ }, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Identity providers" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "The directory this organization signs in through. A domain is claimed the moment it is typed here and routes nothing until it is verified." })] }), !readOnly && (_jsx(Button, { variant: "primary", onClick: openCreate, children: "New provider" }))] })), providers.isPending && !providers.data ? (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 5 }, children: _jsx(Spinner, {}) })) : rows.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "identity-providers", children: "Providers" }), _jsx(Table.Subtitle, { as: "p", id: "identity-providers-subtitle", children: "A provider switched off keeps its configuration and its verified domains; only sign-in through it stops." }), _jsx(DataTable, { "aria-labelledby": "identity-providers", "aria-describedby": "identity-providers-subtitle", data: rows, columns: columns })] })) : (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(KeyIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "No directory registered" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "People here sign in the ordinary way. Register a provider to let them sign in through the directory this organization already has." }) }), !readOnly && (_jsx(Button, { size: "small", onClick: openCreate, children: "New provider" }))] })), (creating || editing) && (_jsx(Dialog, { title: editing ? 'Edit identity provider' : 'New identity provider', onClose: close, returnFocusRef: returnFocusRef, footerButtons: [
201
+ { buttonType: 'default', content: 'Cancel', onClick: close },
202
+ {
203
+ buttonType: 'primary',
204
+ content: editing ? 'Save' : 'Register',
205
+ onClick: apply,
206
+ disabled: create.isPending || update.isPending,
207
+ },
208
+ ], children: _jsxs(Box, { sx: { display: 'grid', gap: 3 }, children: [refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: draft.name, onChange: event => set('name', event.target.value), placeholder: "Acme Okta" }), _jsx(FormControl.Caption, { children: "What an administrator sees in this list." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Issuer" }), _jsx(TextInput, { block: true, value: draft.issuer, onChange: event => set('issuer', event.target.value), placeholder: "https://acme.okta.com" }), _jsx(FormControl.Caption, { children: "Must be https \u2014 discovery, the signing keys and the sign-in redirect all travel over it." })] }), _jsxs(Box, { sx: { display: 'grid', gap: 2, gridTemplateColumns: '1fr 1fr' }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Client ID" }), _jsx(TextInput, { block: true, value: draft.clientId, onChange: event => set('clientId', event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Client secret" }), _jsx(TextInput, { block: true, value: draft.clientSecretRef, onChange: event => set('clientSecretRef', event.target.value), placeholder: "the secret's name, not the secret" }), _jsx(FormControl.Caption, { children: "The name of a secret already in this organization\u2019s store \u2014 never the secret itself." })] })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Email domains" }), _jsx(TextInput, { block: true, value: domainsText, onChange: event => setDomainsText(event.target.value), placeholder: "acme.com, acme.io" }), _jsx(FormControl.Caption, { children: "Comma-separated. Claiming a domain is free and routes nothing \u2014 verify it below once it is registered before anybody can sign in through it." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Group claim" }), _jsx(TextInput, { block: true, value: draft.groupClaim, onChange: event => set('groupClaim', event.target.value), placeholder: "groups" }), _jsx(FormControl.Caption, { children: "Which claim in the token carries the groups the mapping below reads." })] }), _jsxs(FormControl, { children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(ToggleSwitch, { size: "small", checked: draft.allowEmailLinking, "aria-labelledby": "allow-email-linking-label", onClick: () => set('allowEmailLinking', !draft.allowEmailLinking) }), _jsx(FormControl.Label, { id: "allow-email-linking-label", children: "Adopt an existing account on a verified address" })] }), _jsx(FormControl.Caption, { children: "Off by default: a sign-in whose address already has a Datalayer account is refused rather than signed in as that account, and the person is told to connect the two from a session they already hold. Turn this on only if the smoother rollout is worth it \u2014 the address still has to be on a verified domain, and the provider still has to say it verified it itself." })] }), _jsxs(Box, { children: [_jsxs(Box, { sx: {
209
+ display: 'flex',
210
+ alignItems: 'center',
211
+ justifyContent: 'space-between',
212
+ mb: 2,
213
+ }, children: [_jsx(FormControl.Label, { children: "Group \u2192 role mapping" }), _jsx(Button, { size: "small", onClick: addMapping, children: "Add mapping" })] }), _jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 0, mb: 2 }, children: "Granted the next time somebody in the group signs in, and never taken back by a sign-in that omits it \u2014 a slow directory sync should not read as a demotion. Reaches no further than this organization and its own teams." }), draft.roleMappings.length === 0 && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: "No mapping: everybody who signs in through this provider becomes an organization member and nothing more." })), _jsx(Box, { sx: { display: 'grid', gap: 2 }, children: draft.roleMappings.map((mapping, index) => (_jsxs(Box, { sx: {
214
+ display: 'grid',
215
+ gap: 2,
216
+ gridTemplateColumns: isTeamRole(mapping.role)
217
+ ? '1fr 1fr 1fr auto'
218
+ : '1fr 1fr auto',
219
+ alignItems: 'end',
220
+ }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Group" }), _jsx(TextInput, { block: true, value: mapping.group, onChange: event => setMapping(index, { group: event.target.value }), placeholder: "admins" })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Grants" }), _jsxs(Select, { value: mapping.role, onChange: event => setMapping(index, {
221
+ role: event.target.value,
222
+ }), children: [ORGANIZATION_ROLES.map(role => (_jsx(Select.Option, { value: role, children: roleLabel(role) }, role))), TEAM_ROLES.map(role => (_jsx(Select.Option, { value: role, children: roleLabel(role) }, role)))] })] }), isTeamRole(mapping.role) && (_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Team" }), _jsx(TextInput, { block: true, value: mapping.teamUid, onChange: event => setMapping(index, { teamUid: event.target.value }), placeholder: "team uid" })] })), _jsx(IconButton, { icon: TrashIcon, variant: "danger", size: "small", "aria-label": "Remove this mapping", onClick: () => removeMapping(index) })] }, index))) })] })] }) })), managingDomains && (_jsx(DomainVerificationDialog, { provider: managingDomains, orgUid: orgUid, onClose: () => setManagingDomains(null), fetchVerification: domainVerification, verify: verifyDomain })), removing && (_jsx(Dialog, { title: "Remove this identity provider?", onClose: () => setRemoving(null), returnFocusRef: returnFocusRef, footerButtons: [
223
+ {
224
+ buttonType: 'default',
225
+ content: 'Keep it',
226
+ onClick: () => setRemoving(null),
227
+ },
228
+ {
229
+ buttonType: 'danger',
230
+ content: 'Remove',
231
+ onClick: confirmRemove,
232
+ disabled: remove.isPending,
233
+ },
234
+ ], children: _jsx(Text, { sx: { fontSize: 1 }, children: "Sign-in through it stops immediately and its domains stop being claimed. If people have used it before, consider turning it off instead \u2014 that keeps its uid resolvable for the sign-in rows that name it." }) }))] }));
235
+ };
236
+ /**
237
+ * Proving one domain: what to publish, and the button that checks it.
238
+ *
239
+ * Kept as its own dialog rather than inline in the edit form, because
240
+ * proving a domain is not part of editing the provider's configuration —
241
+ * it is a separate act, done once DNS has actually been changed, and
242
+ * conflating the two would make "Save" look like it also verifies.
243
+ */
244
+ const DomainVerificationDialog = ({ provider, onClose, fetchVerification, verify, }) => {
245
+ const { enqueueToast } = useToast();
246
+ const [selected, setSelected] = useState(provider.domains[0] ?? '');
247
+ const [record, setRecord] = useState(null);
248
+ const [verified, setVerified] = useState(provider.verifiedDomains.includes(selected));
249
+ const [refusal, setRefusal] = useState('');
250
+ const returnFocusRef = useRef(null);
251
+ const load = (domain) => {
252
+ setSelected(domain);
253
+ setRecord(null);
254
+ setRefusal('');
255
+ fetchVerification.mutate({ uid: provider.uid, domain }, {
256
+ onSuccess: result => {
257
+ setRecord(result.verification);
258
+ setVerified(result.verified);
259
+ },
260
+ onError: error => setRefusal(error.message),
261
+ });
262
+ };
263
+ const runVerify = () => {
264
+ setRefusal('');
265
+ verify.mutate({ uid: provider.uid, domain: selected }, {
266
+ onSuccess: updated => {
267
+ const done = updated.verifiedDomains.includes(selected);
268
+ setVerified(done);
269
+ if (done) {
270
+ enqueueToast(`${selected} is verified and now routes sign-in.`, {
271
+ variant: 'success',
272
+ });
273
+ }
274
+ },
275
+ onError: error => {
276
+ if (error instanceof IdentityProviderInvalid) {
277
+ // "No TXT record was found" and the like — the exact reason DNS
278
+ // did not check out, worth showing rather than a generic failure.
279
+ setRefusal(error.message);
280
+ return;
281
+ }
282
+ setRefusal(error.message);
283
+ },
284
+ });
285
+ };
286
+ return (_jsx(Dialog, { title: `Verify a domain for ${provider.name}`, onClose: onClose, returnFocusRef: returnFocusRef, footerButtons: [
287
+ { buttonType: 'default', content: 'Close', onClick: onClose },
288
+ ], children: _jsxs(Box, { sx: { display: 'grid', gap: 3 }, children: [_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: "Publish the record below at your DNS provider, then check it here. Until it checks out, this domain routes no sign-in \u2014 a claim is not proof." }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Domain" }), _jsx(Select, { value: selected, onChange: event => load(event.target.value), children: provider.domains.map(domain => (_jsxs(Select.Option, { value: domain, children: [domain, provider.verifiedDomains.includes(domain) ? ' — verified' : ''] }, domain))) })] }), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), fetchVerification.isPending ? (_jsx(Spinner, { size: "small" })) : record ? (_jsxs(Box, { sx: {
289
+ display: 'grid',
290
+ gap: 1,
291
+ p: 2,
292
+ border: '1px solid',
293
+ borderColor: 'border.default',
294
+ borderRadius: 2,
295
+ fontFamily: 'mono',
296
+ fontSize: 0,
297
+ }, children: [_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Type: ", record.type] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Host: ", record.host] }), _jsxs(Text, { sx: { fontSize: 0, wordBreak: 'break-all' }, children: ["Value: ", record.value] })] })) : (_jsx(Button, { size: "small", onClick: () => load(selected), children: "Show what to publish" })), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Label, { variant: verified ? 'success' : 'secondary', children: verified ? 'Verified — routes sign-in' : 'Not yet verified' }), !verified && (_jsx(Button, { variant: "primary", size: "small", onClick: runVerify, disabled: verify.isPending || !selected, children: verify.isPending ? 'Checking…' : 'Check DNS now' }))] })] }) }));
298
+ };
299
+ export default IdentityProviders;
@@ -1,12 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo, useRef, useState } from 'react';
3
- import { ActionList, ActionMenu, Button, Heading, Label, Link, Select, Spinner, Text, } from '@primer/react';
3
+ import { ActionList, ActionMenu, Button, Heading, Label, Link, RelativeTime, Select, Spinner, Text, } from '@primer/react';
4
4
  import { Blankslate, DataTable, Dialog, Table, } from '@primer/react/experimental';
5
5
  import { Box } from '@datalayer/primer-addons';
6
6
  import { ChecklistIcon, KebabHorizontalIcon, PlayIcon, PlugIcon, StackIcon, TelescopeIcon, } from '@primer/octicons-react';
7
+ import { ShareAccessDialog, sandboxSharingUrl } from '../../components/sharing/index.js';
7
8
  import { ClientBadge, McpErrorBlankslate } from '../../components/mcp/index.js';
8
9
  import { useDisconnectAgent, useMcpActivity, useTerminateBinding, } from '../../hooks/useMcp.js';
9
10
  import { useNavigate, useToast } from '../../hooks/index.js';
11
+ import { useCoreStore } from '../../state/index.js';
10
12
  import { clientStatusOf, durationLabel, plural, timeAgo } from './format.js';
11
13
  /** How a client's status is drawn. */
12
14
  const STATUS_LOOK = {
@@ -14,6 +16,29 @@ const STATUS_LOOK = {
14
16
  idle: { label: 'Idle', variant: 'attention' },
15
17
  disconnected: { label: 'Disconnected', variant: 'secondary' },
16
18
  };
19
+ /**
20
+ * How a task's status is drawn.
21
+ *
22
+ * `input_required` is `attention` rather than `secondary` because it is the
23
+ * one status that is waiting on a person: a run stopped for an approval is
24
+ * not idling, it is asking, and it stays asking until somebody answers.
25
+ */
26
+ const TASK_LOOK = {
27
+ working: { label: 'Working', variant: 'success' },
28
+ input_required: { label: 'Waiting on you', variant: 'attention' },
29
+ completed: { label: 'Completed', variant: 'secondary' },
30
+ failed: { label: 'Failed', variant: 'danger' },
31
+ cancelled: { label: 'Cancelled', variant: 'secondary' },
32
+ };
33
+ /** What a task is running, in one phrase. */
34
+ const taskContext = (task) => {
35
+ if (task.notebookUid) {
36
+ return task.cellId
37
+ ? `Notebook ${task.notebookUid} · cell ${task.cellId}`
38
+ : `Notebook ${task.notebookUid}`;
39
+ }
40
+ return task.sandboxUid ? `Sandbox ${task.sandboxUid}` : '';
41
+ };
17
42
  /** What a client is working on, in one phrase. */
18
43
  const boundTo = (client) => {
19
44
  const call = client.lastCall;
@@ -54,6 +79,10 @@ export const McpDashboard = ({ errorState, routes, org, showTitle = true, sectio
54
79
  const terminate = useTerminateBinding();
55
80
  const [disconnecting, setDisconnecting] = useState(null);
56
81
  const [terminating, setTerminating] = useState(null);
82
+ const [sharing, setSharing] = useState(null);
83
+ // Runtimes owns a sandbox's sharing, and addresses it by the *runtime's*
84
+ // name rather than by the handle the session holds.
85
+ const runtimesUrl = useCoreStore(state => state.configuration.runtimesUrl || state.configuration.managerUrl || '');
57
86
  // Primer 37 types Dialog's focus refs with React 18's non-nullable
58
87
  // `RefObject<HTMLElement>`. React 19's `useRef(null)` is nullable, and this
59
88
  // ref is only handed to Dialog, so narrow it once here.
@@ -71,6 +100,27 @@ export const McpDashboard = ({ errorState, routes, org, showTitle = true, sectio
71
100
  status: clientStatusOf(client.lastCall?.at),
72
101
  })), [data?.clients]);
73
102
  const sandboxes = useMemo(() => (data?.sandboxes ?? []).map(binding => ({ ...binding, id: binding.uid })), [data?.sandboxes]);
103
+ /**
104
+ * The runs, the ones asking for a person first.
105
+ *
106
+ * `input_required` is a run that has stopped and will not move until
107
+ * somebody answers it, so it goes to the top however long it has been
108
+ * there; `working` next; then whatever has just finished, newest first. A
109
+ * list ordered only by time buries the one row that needs a hand.
110
+ */
111
+ const tasks = useMemo(() => {
112
+ const rank = {
113
+ input_required: 0,
114
+ working: 1,
115
+ failed: 2,
116
+ completed: 3,
117
+ cancelled: 3,
118
+ };
119
+ return [...(data?.tasks ?? [])]
120
+ .map(task => ({ ...task, id: task.uid }))
121
+ .sort((left, right) => rank[left.status] - rank[right.status] ||
122
+ right.lastUpdatedAt.localeCompare(left.lastUpdatedAt));
123
+ }, [data?.tasks]);
74
124
  const calls = useMemo(() => (data?.calls ?? []).map(call => ({ ...call, id: call.uid })), [data?.calls]);
75
125
  const filteredCalls = useMemo(() => calls.filter(call => (!callClient || call.clientId === callClient) &&
76
126
  (!callTool || call.tool === callTool) &&
@@ -189,6 +239,41 @@ export const McpDashboard = ({ errorState, routes, org, showTitle = true, sectio
189
239
  renderCell: row => (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { variant: "invisible", size: "small", "aria-label": `Actions for ${row.clientName || row.clientId}`, icon: KebabHorizontalIcon }) }), _jsx(ActionMenu.Overlay, { align: "end", children: _jsxs(ActionList, { children: [_jsx(ActionList.Item, { onSelect: () => navigate(`${routes.audit}?agent=${encodeURIComponent(row.clientId)}`), children: "Audit" }), _jsx(ActionList.Divider, {}), _jsx(ActionList.Item, { variant: "danger", disabled: !row.grantUid, onSelect: () => setDisconnecting(row), children: "Disconnect" })] }) })] })),
190
240
  },
191
241
  ];
242
+ const taskColumns = [
243
+ {
244
+ header: 'Run',
245
+ field: 'uid',
246
+ rowHeader: true,
247
+ renderCell: row => (_jsx(Link, { href: `${routes.runs}/${encodeURIComponent(row.uid)}`, onClick: event => {
248
+ event.preventDefault();
249
+ navigate(`${routes.runs}/${encodeURIComponent(row.uid)}`);
250
+ }, sx: { fontFamily: 'mono', fontSize: 0 }, children: row.uid })),
251
+ },
252
+ {
253
+ header: 'Status',
254
+ id: 'status',
255
+ width: '150px',
256
+ renderCell: row => (_jsx(Label, { size: "small", variant: TASK_LOOK[row.status].variant, children: TASK_LOOK[row.status].label })),
257
+ },
258
+ {
259
+ header: 'Tool',
260
+ id: 'tool',
261
+ width: '160px',
262
+ renderCell: row => _jsx(Text, { sx: { fontSize: 0 }, children: row.tool || '—' }),
263
+ },
264
+ {
265
+ header: 'Running',
266
+ id: 'context',
267
+ width: 'growCollapse',
268
+ renderCell: row => (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: taskContext(row) || '—' })),
269
+ },
270
+ {
271
+ header: 'Updated',
272
+ id: 'updated',
273
+ width: '140px',
274
+ renderCell: row => (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: _jsx(RelativeTime, { datetime: row.lastUpdatedAt }) })),
275
+ },
276
+ ];
192
277
  const sandboxColumns = [
193
278
  {
194
279
  header: 'Handle',
@@ -226,9 +311,9 @@ export const McpDashboard = ({ errorState, routes, org, showTitle = true, sectio
226
311
  {
227
312
  header: '',
228
313
  id: 'actions',
229
- width: '110px',
314
+ width: '190px',
230
315
  align: 'end',
231
- renderCell: row => (_jsx(Button, { size: "small", variant: "danger", disabled: row.state === 'closed', onClick: () => setTerminating(row), children: "Terminate" })),
316
+ renderCell: row => (_jsxs(Box, { sx: { display: 'flex', gap: 2, justifyContent: 'flex-end' }, children: [sandboxSharingUrl(runtimesUrl, row.runtimeName) && (_jsx(Button, { size: "small", disabled: row.state === 'closed', onClick: () => setSharing(row), children: "Share" })), _jsx(Button, { size: "small", variant: "danger", disabled: row.state === 'closed', onClick: () => setTerminating(row), children: "Terminate" })] })),
232
317
  },
233
318
  ];
234
319
  const callColumns = [
@@ -299,9 +384,7 @@ export const McpDashboard = ({ errorState, routes, org, showTitle = true, sectio
299
384
  (clients.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-clients", children: "Connected clients" }), _jsxs(Table.Subtitle, { as: "p", id: "mcp-clients-subtitle", children: [plural(clients.length, 'client'), " with a live grant on your workspace."] }), _jsx(DataTable, { "aria-labelledby": "mcp-clients", "aria-describedby": "mcp-clients-subtitle", data: clients, columns: clientColumns })] })) : (_jsx(PanelEmpty, { icon: PlugIcon, heading: "No client connected", description: "Connect Claude Code, Codex, Cursor, VS Code or any MCP client to your workspace, and it appears here on its first call.", action: {
300
385
  label: 'Set up a client',
301
386
  onClick: () => navigate(routes.access),
302
- } }))), draws('runs') && !loading && (_jsxs(Box, { children: [_jsx(Heading, { as: "h3", sx: { fontSize: 1, mb: 2 }, children: "Running now" }), _jsx(PanelEmpty, { icon: PlayIcon, heading: "Nothing is running", description: (data?.tasks.length ?? 0) > 0
303
- ? 'Tasks are running, and the live pane that shows their progress, their cell and their approvals arrives with durable execution in milestone 2.'
304
- : 'A tool call that outlives its request becomes a task you can watch, cancel and answer. Durable execution arrives in milestone 2; until then every call finishes inside its own request and is listed under Recent calls.' })] })), draws('sandboxes') &&
387
+ } }))), draws('runs') && !loading && (_jsx(Box, { children: tasks.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-runs", children: "Running now" }), _jsx(Table.Subtitle, { as: "p", id: "mcp-runs-subtitle", children: "A tool call that outlives its request. Open one to watch its output, cancel it, or answer it when it is waiting on you." }), _jsx(DataTable, { "aria-labelledby": "mcp-runs", "aria-describedby": "mcp-runs-subtitle", data: tasks, columns: taskColumns })] })) : (_jsx(PanelEmpty, { icon: PlayIcon, heading: "Nothing is running", description: "A tool call that outlives its request becomes a task you can watch, cancel and answer. Ask a client for one \u2014 an agent that runs a long cell \u2014 and it appears here while it runs." })) })), draws('sandboxes') &&
305
388
  !loading &&
306
389
  (sandboxes.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-sandboxes", children: "Sandboxes bound" }), _jsx(Table.Subtitle, { as: "p", id: "mcp-sandboxes-subtitle", children: "The sessions your agents execute in. Terminating one ends its open work." }), _jsx(DataTable, { "aria-labelledby": "mcp-sandboxes", "aria-describedby": "mcp-sandboxes-subtitle", data: sandboxes, columns: sandboxColumns })] })) : (_jsx(PanelEmpty, { icon: StackIcon, heading: "No sandbox bound", description: "An agent that launches a sandbox keeps it for the session; it appears here with its provider, its capabilities and what it is doing." }))), draws('calls') &&
307
390
  !loading &&
@@ -342,6 +425,6 @@ export const McpDashboard = ({ errorState, routes, org, showTitle = true, sectio
342
425
  onClick: confirmTerminate,
343
426
  disabled: terminate.isPending,
344
427
  },
345
- ], children: _jsxs(Text, { sx: { fontSize: 1 }, children: ["The runtime behind ", terminating.uid, " is stopped and its session closed. Work the agent has open there ends as", ' ', _jsx("code", { children: "SANDBOX_LOST" }), "; anything already written to a notebook stays."] }) }))] }));
428
+ ], children: _jsxs(Text, { sx: { fontSize: 1 }, children: ["The runtime behind ", terminating.uid, " is stopped and its session closed. Work the agent has open there ends as", ' ', _jsx("code", { children: "SANDBOX_LOST" }), "; anything already written to a notebook stays."] }) })), sandboxSharingUrl(runtimesUrl, sharing?.runtimeName) && (_jsx(ShareAccessDialog, { isOpen: true, requestUrl: sandboxSharingUrl(runtimesUrl, sharing?.runtimeName), resourceLabel: "Sandbox", resourceName: sharing?.alias || sharing?.uid, resourceDescription: "Who else may look at this sandbox, change it, or run code on it. Sharing ends when the sandbox does.", onClose: () => setSharing(null) }))] }));
346
429
  };
347
430
  export default McpDashboard;
@@ -75,7 +75,7 @@ const Summary = ({ icon: Icon, title, children, action, }) => (_jsxs(Box, { sx:
75
75
  }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Icon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: title })] }), _jsx(Box, { sx: { fontSize: 0, color: 'fg.muted', display: 'grid', gap: 1 }, children: children }), action && (_jsx(Box, { children: _jsx(Link, { as: "button", sx: { fontSize: 0, cursor: 'pointer' }, onClick: action.onClick, children: action.label }) }))] }));
76
76
  export const McpHome = ({ routes, clientDocsBase = '/docs/mcp-clients', showTitle = true, showSummaries = true, showSetup = true, onOpenDocs, }) => {
77
77
  const navigate = useNavigate();
78
- const endpoint = useCoreStore(state => state.configuration.jupyterMcpServerUrl);
78
+ const endpoint = useCoreStore(state => state.configuration.datalayerMcpServerUrl);
79
79
  // Whether there is a token, never what it is: this page shows an address
80
80
  // and a status, and a credential belongs on neither.
81
81
  const signedIn = Boolean(useIAMStore(state => state.token));
@@ -21,8 +21,9 @@
21
21
  * @module views/mcp/McpObservability
22
22
  */
23
23
  import type { JSX } from 'react';
24
+ import type { McpWorkflowsHealth } from '../../api/mcp/operations';
24
25
  import { type McpErrorStateFn, type McpRoutes } from './types';
25
- export type McpObservabilityPane = 'runs' | 'metrics';
26
+ export type McpObservabilityPane = 'runs' | 'live' | 'metrics';
26
27
  export interface McpObservabilityProps {
27
28
  /** The application's words for a failed request. */
28
29
  errorState: McpErrorStateFn;
@@ -46,5 +47,33 @@ export interface McpObservabilityProps {
46
47
  org?: string;
47
48
  showTitle?: boolean;
48
49
  }
50
+ /**
51
+ * What part of the platform a span belongs to, from its name.
52
+ *
53
+ * By prefix rather than by service, because one service emits spans for more
54
+ * than one stage — the gateway does its own policy check — and because a
55
+ * durable run's spans arrive from a process the gateway never talks to. A
56
+ * name nothing recognises gets no label rather than a wrong one: the span is
57
+ * still drawn, still timed, and still named.
58
+ */
59
+ export declare const stageOf: (name: string) => string;
60
+ /**
61
+ * What the durable engine's own report amounts to, in one line.
62
+ *
63
+ * Three states, not two. "Unreachable" is deliberately not "unhealthy": the
64
+ * gateway answers `/operations/workflows` even when it cannot reach the
65
+ * engine, precisely so an operator can tell "the engine says it is unwell"
66
+ * from "nobody could ask it" — the first is a problem with the engine, the
67
+ * second is usually a problem with the network between them, and sending
68
+ * somebody to the wrong one costs an hour.
69
+ *
70
+ * A healthy engine with a backlog is still healthy, and says so with the
71
+ * backlog beside it: a queue is a queue, not a fault.
72
+ */
73
+ export declare const engineLook: (health: McpWorkflowsHealth | undefined, failed: boolean) => {
74
+ label: string;
75
+ variant: "success" | "attention" | "danger" | "secondary";
76
+ note: string;
77
+ };
49
78
  export declare const McpObservability: ({ errorState, routes, pane, onPaneChange, taskUid, traceId, onSelect, agent, org, showTitle, }: McpObservabilityProps) => JSX.Element;
50
79
  export default McpObservability;