@datalayer/core 1.2.27 → 1.2.29
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/lib/api/evals/__tests__/external-definition.golden.json +110 -0
- package/lib/api/evals/client.d.ts +13 -1
- package/lib/api/evals/client.js +13 -0
- package/lib/api/evals/types.d.ts +168 -1
- package/lib/api/mcp/agentAccess.d.ts +10 -0
- package/lib/api/mcp/agentAccess.js +28 -0
- package/lib/api/mcp/generated.js +20 -0
- package/lib/api/mcp/index.d.ts +1 -0
- package/lib/api/mcp/index.js +1 -0
- package/lib/api/mcp/observability.d.ts +1 -1
- package/lib/api/mcp/observability.js +2 -0
- package/lib/api/mcp/operations.d.ts +28 -10
- package/lib/api/mcp/tasks.d.ts +9 -1
- package/lib/api/otel/firewall.d.ts +74 -0
- package/lib/api/otel/firewall.js +42 -0
- package/lib/api/otel/index.d.ts +1 -0
- package/lib/api/otel/index.js +1 -0
- package/lib/cli-login.js +2 -2
- package/lib/components/anonymous/AnonymousKeyExpired.js +15 -3
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/mcp/ScopeList.js +10 -1
- package/lib/components/principal/PrincipalSwitcherMenu.js +4 -4
- package/lib/components/sharing/ShareAccessComponent.js +33 -8
- package/lib/components/toast/ToastPanel.d.ts +11 -0
- package/lib/components/toast/ToastPanel.js +42 -0
- package/lib/components/toast/index.d.ts +2 -0
- package/lib/components/toast/index.js +6 -0
- package/lib/components/toast/toastQueue.d.ts +35 -0
- package/lib/components/toast/toastQueue.js +47 -0
- package/lib/hooks/index.d.ts +1 -0
- package/lib/hooks/index.js +1 -0
- package/lib/hooks/useCache.d.ts +4 -2
- package/lib/hooks/useCache.js +13 -4
- package/lib/hooks/useDatalayer.js +12 -16
- package/lib/hooks/useKeptScroll.d.ts +6 -0
- package/lib/hooks/useKeptScroll.js +54 -0
- package/lib/hooks/useMcp.d.ts +2 -2
- package/lib/hooks/useMcp.js +5 -2
- package/lib/hooks/useNavigate.d.ts +16 -2
- package/lib/hooks/useNavigate.js +65 -5
- package/lib/hooks/useToast.d.ts +6 -0
- package/lib/hooks/useToast.js +40 -1
- package/lib/models/McpAgentAccess.d.ts +95 -0
- package/lib/models/McpAgentAccess.js +5 -0
- package/lib/models/Usage.d.ts +13 -0
- package/lib/models/Usage.js +5 -0
- package/lib/models/index.d.ts +1 -0
- package/lib/models/index.js +1 -0
- package/lib/otel/client/OtelClient.d.ts +56 -0
- package/lib/otel/client/OtelClient.js +25 -0
- package/lib/otel/client/index.d.ts +1 -1
- package/lib/otel/index.d.ts +1 -1
- package/lib/otel/views/OtelLive.js +3 -3
- package/lib/otel/views/OtelLiveSpans.js +2 -2
- package/lib/otel/views/OtelLogsList.js +3 -3
- package/lib/otel/views/OtelMetricsChart.d.ts +13 -0
- package/lib/otel/views/OtelMetricsChart.js +109 -26
- package/lib/otel/views/OtelSpanDetail.js +3 -3
- package/lib/otel/views/OtelSqlView.js +4 -4
- package/lib/otel/views/OtelSystemView.js +2 -2
- package/lib/otel/views/OtelTimelineRangeSlider.js +2 -2
- package/lib/otel/views/OtelTracesList.js +2 -2
- package/lib/state/substates/LayoutState.js +24 -3
- package/lib/state/substates/NavigationState.d.ts +11 -3
- package/lib/state/substates/NavigationState.js +48 -5
- package/lib/views/mcp/AuditLog.js +1 -1
- package/lib/views/mcp/ConnectedAgents.d.ts +1 -1
- package/lib/views/mcp/ConnectedAgents.js +13 -10
- package/lib/views/mcp/McpDashboard.d.ts +1 -1
- package/lib/views/mcp/McpDashboard.js +6 -6
- package/lib/views/mcp/McpHome.d.ts +3 -1
- package/lib/views/mcp/McpHome.js +19 -9
- package/lib/views/mcp/McpObservability.d.ts +22 -3
- package/lib/views/mcp/McpObservability.js +65 -17
- package/lib/views/mcp/Policies.js +1 -1
- package/lib/views/mcp/RunDetail.js +2 -2
- package/lib/views/mcp/types.d.ts +8 -4
- package/lib/views/otel/OtelHeader.js +2 -2
- package/package.json +2 -2
|
@@ -67,23 +67,19 @@ export function useDatalayer(props = {}) {
|
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
69
|
if (notifyOnError_ && !isForbidden) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
enqueueToast(`${responseError.message}`, { variant: 'error' });
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
enqueueToast(`API Error`, { variant: 'error' });
|
|
81
|
-
}
|
|
82
|
-
if (responseError.exceptionMessage) {
|
|
83
|
-
enqueueToast(`${responseError.exceptionMessage}`, {
|
|
84
|
-
variant: 'error',
|
|
85
|
-
});
|
|
70
|
+
// One toast per failed request, its lines together and each once
|
|
71
|
+
// (2 November 2026: its message and its exception were a toast
|
|
72
|
+
// each, so a page that asked twice showed four).
|
|
73
|
+
const once = (lines) => Array.from(new Set(lines.map(line => `${line ?? ''}`.trim()).filter(Boolean))).join('\n');
|
|
74
|
+
const warnings = once(responseError.warnings ?? []);
|
|
75
|
+
if (warnings) {
|
|
76
|
+
enqueueToast(warnings, { variant: 'warning' });
|
|
86
77
|
}
|
|
78
|
+
enqueueToast(once([
|
|
79
|
+
...(responseError.errors ?? []),
|
|
80
|
+
responseError.message || 'API Error',
|
|
81
|
+
responseError.exceptionMessage,
|
|
82
|
+
]), { variant: 'error' });
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
85
|
const response = {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The nearest ancestor of `marker` that scrolls vertically, if any. */
|
|
2
|
+
export declare function scrollingAncestor(marker: HTMLElement | null): HTMLElement | null;
|
|
3
|
+
/** Where the sidebar kept under `key` was last scrolled to, if it was. */
|
|
4
|
+
export declare const keptScrollOf: (key: string) => number | undefined;
|
|
5
|
+
export declare function useKeptScroll<T extends HTMLElement = HTMLElement>(key: string): import("react").RefObject<T | null>;
|
|
6
|
+
export default useKeptScroll;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A sidebar keeps its scroll across pages: an entry pressed far down the list
|
|
7
|
+
* leaves the list where it was, so the next one is still under the pointer.
|
|
8
|
+
*
|
|
9
|
+
* `useKeptScroll(key)` returns a ref for a small element placed inside the
|
|
10
|
+
* sidebar; the nearest ancestor that scrolls is the one kept. Its position is
|
|
11
|
+
* remembered under `key` while it scrolls, and given back when a sidebar with
|
|
12
|
+
* the same key is mounted again (a layout rendered anew by the next route).
|
|
13
|
+
* The panel must not be one `useNavigate` brings to the top: it is not marked
|
|
14
|
+
* `data-scroll-root`.
|
|
15
|
+
*
|
|
16
|
+
* @module hooks/useKeptScroll
|
|
17
|
+
*/
|
|
18
|
+
import { useLayoutEffect, useRef } from 'react';
|
|
19
|
+
const kept = new Map();
|
|
20
|
+
const scrollsY = (element) => {
|
|
21
|
+
const overflow = window.getComputedStyle(element).overflowY;
|
|
22
|
+
return overflow === 'auto' || overflow === 'scroll';
|
|
23
|
+
};
|
|
24
|
+
/** The nearest ancestor of `marker` that scrolls vertically, if any. */
|
|
25
|
+
export function scrollingAncestor(marker) {
|
|
26
|
+
let element = marker?.parentElement ?? null;
|
|
27
|
+
while (element && !scrollsY(element)) {
|
|
28
|
+
element = element.parentElement;
|
|
29
|
+
}
|
|
30
|
+
return element;
|
|
31
|
+
}
|
|
32
|
+
/** Where the sidebar kept under `key` was last scrolled to, if it was. */
|
|
33
|
+
export const keptScrollOf = (key) => kept.get(key);
|
|
34
|
+
export function useKeptScroll(key) {
|
|
35
|
+
const ref = useRef(null);
|
|
36
|
+
useLayoutEffect(() => {
|
|
37
|
+
const panel = scrollingAncestor(ref.current);
|
|
38
|
+
if (!panel) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const saved = kept.get(key);
|
|
42
|
+
if (saved !== undefined) {
|
|
43
|
+
panel.scrollTop = saved;
|
|
44
|
+
}
|
|
45
|
+
const remember = () => kept.set(key, panel.scrollTop);
|
|
46
|
+
panel.addEventListener('scroll', remember, { passive: true });
|
|
47
|
+
return () => {
|
|
48
|
+
remember();
|
|
49
|
+
panel.removeEventListener('scroll', remember);
|
|
50
|
+
};
|
|
51
|
+
}, [key]);
|
|
52
|
+
return ref;
|
|
53
|
+
}
|
|
54
|
+
export default useKeptScroll;
|
package/lib/hooks/useMcp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type McpTaskAnswered, type McpActivity, type McpActivityFilters, type McpAuditFilters, type McpBindingListFilters, type McpGatewayVersion, type McpMetricsFilters, type McpMetricsSnapshot, type McpOrganizationOverview, type McpOrganizationUsage, type McpOrganizationOverviewFilters, type McpPolicyFilters, type McpRunLogs, type McpRunTrace, type McpTaskEvent, type McpTaskListFilters, type McpWorkerList, type McpWorkflowsHealth } from '../api/mcp';
|
|
1
|
+
import { type McpTaskAnswered, type McpTaskCancelled, type McpActivity, type McpActivityFilters, type McpAuditFilters, type McpBindingListFilters, type McpGatewayVersion, type McpMetricsFilters, type McpMetricsSnapshot, type McpOrganizationOverview, type McpOrganizationUsage, type McpOrganizationOverviewFilters, type McpPolicyFilters, type McpRunLogs, type McpRunTrace, type McpTaskEvent, type McpTaskListFilters, type McpWorkerList, type McpWorkflowsHealth } from '../api/mcp';
|
|
2
2
|
import { type ConnectedAgent } from '../api/iam/connectedAgents';
|
|
3
3
|
import { type OrganizationTeam } from '../api/iam/organizationTeams';
|
|
4
4
|
import { setAuditSettings, type McpAuditSettings } from '../api/iam/mcpAuditSettings';
|
|
@@ -28,7 +28,7 @@ export declare const useTaskEvents: (taskUid?: string, options?: {
|
|
|
28
28
|
enabled?: boolean;
|
|
29
29
|
onEvent?: (event: McpTaskEvent) => void;
|
|
30
30
|
}) => void;
|
|
31
|
-
export declare const useCancelTask: () => import("@tanstack/react-query").UseMutationResult<
|
|
31
|
+
export declare const useCancelTask: () => import("@tanstack/react-query").UseMutationResult<McpTaskCancelled, Error, string, unknown>;
|
|
32
32
|
export declare const useAnswerTask: () => import("@tanstack/react-query").UseMutationResult<McpTaskAnswered, Error, {
|
|
33
33
|
taskUid: string;
|
|
34
34
|
input: Record<string, unknown>;
|
package/lib/hooks/useMcp.js
CHANGED
|
@@ -124,8 +124,11 @@ export const useCancelTask = () => {
|
|
|
124
124
|
const mcpUrl = useMcpServerUrl();
|
|
125
125
|
return useMutation({
|
|
126
126
|
mutationFn: taskUid => cancelTask(token ?? '', taskUid, mcpUrl),
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
// The gateway answers `{uid, cancelled, status}`, not the task: read the
|
|
128
|
+
// task again rather than caching that reply in its place (the run page
|
|
129
|
+
// lost its tool and output on a cancel, as it did on an answer).
|
|
130
|
+
onSuccess: (_cancelled, taskUid) => {
|
|
131
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.mcp.task(taskUid) });
|
|
129
132
|
queryClient.invalidateQueries({ queryKey: queryKeys.mcp.tasks() });
|
|
130
133
|
queryClient.invalidateQueries({ queryKey: queryKeys.mcp.activity() });
|
|
131
134
|
},
|
|
@@ -5,8 +5,15 @@ export interface NavigateOptions {
|
|
|
5
5
|
state?: unknown;
|
|
6
6
|
/** Open the destination in a new tab and leave this one where it is. */
|
|
7
7
|
newTab?: boolean;
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Where the reader lands: the top of the page when unsaid or `true`; an
|
|
10
|
+
* element, by its id (with or without its `#`), scrolled into view once
|
|
11
|
+
* the page has it; `false` to leave the scroll where it is. The page and
|
|
12
|
+
* every scroll panel marked `data-scroll-root` are scrolled, so a layout
|
|
13
|
+
* that scrolls inside its own panel lands at the top too (the user,
|
|
14
|
+
* 6 November 2026: the Trust page opened scrolled down).
|
|
15
|
+
*/
|
|
16
|
+
scroll?: boolean | string;
|
|
10
17
|
/**
|
|
11
18
|
* Close the layout's side portals on the way; on by default. Only the
|
|
12
19
|
* history API host has them.
|
|
@@ -40,5 +47,12 @@ export type Navigate = (to: NavigateTarget, optionsOrEvent?: NavigateOptions | N
|
|
|
40
47
|
export declare function isNewTabClick(event: NavigateEvent): boolean;
|
|
41
48
|
/** Open `to` in a new tab, without handing it this window. */
|
|
42
49
|
export declare function openInNewTab(to: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Scroll on arrival: to the top (`undefined` or `true`), to an element by
|
|
52
|
+
* its id (a string, `#` or not), or nowhere (`false`). Done now and again
|
|
53
|
+
* on the next frames, since the page navigated to renders after the call
|
|
54
|
+
* and a layout's panel keeps its scroll across pages.
|
|
55
|
+
*/
|
|
56
|
+
export declare function scrollOnArrival(target: boolean | string | undefined): void;
|
|
43
57
|
export declare const useNavigate: () => Navigate;
|
|
44
58
|
export default useNavigate;
|
package/lib/hooks/useNavigate.js
CHANGED
|
@@ -62,6 +62,68 @@ function isLinkTo(event, to) {
|
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The scroll panels a layout marks, beside the page itself: `data-scroll-root`,
|
|
67
|
+
* or the `dla-scroll-root` class for a component that passes a class name but
|
|
68
|
+
* no data attribute (Primer's `PageLayout.Content`, whose own element is the
|
|
69
|
+
* one that scrolls; 9 November 2026, a card on /agentspecs/identities opened
|
|
70
|
+
* its page scrolled down).
|
|
71
|
+
*/
|
|
72
|
+
const scrollRoots = () => Array.from(document.querySelectorAll('[data-scroll-root], .dla-scroll-root'));
|
|
73
|
+
const nextFrame = (run) => {
|
|
74
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
75
|
+
requestAnimationFrame(run);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
setTimeout(run, 16);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
/** How many frames an element is waited for: a page loaded lazily takes a few. */
|
|
82
|
+
const ARRIVAL_FRAMES = 60;
|
|
83
|
+
/**
|
|
84
|
+
* Scroll on arrival: to the top (`undefined` or `true`), to an element by
|
|
85
|
+
* its id (a string, `#` or not), or nowhere (`false`). Done now and again
|
|
86
|
+
* on the next frames, since the page navigated to renders after the call
|
|
87
|
+
* and a layout's panel keeps its scroll across pages.
|
|
88
|
+
*/
|
|
89
|
+
export function scrollOnArrival(target) {
|
|
90
|
+
if (target === false || typeof document === 'undefined') {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const id = typeof target === 'string' ? target.replace(/^#/, '') : '';
|
|
94
|
+
if (!id) {
|
|
95
|
+
const toTop = () => {
|
|
96
|
+
window.scrollTo(0, 0);
|
|
97
|
+
document.body.scrollTop = 0;
|
|
98
|
+
document.documentElement.scrollTop = 0;
|
|
99
|
+
for (const root of scrollRoots()) {
|
|
100
|
+
root.scrollTop = 0;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
toTop();
|
|
104
|
+
let frames = 0;
|
|
105
|
+
const again = () => {
|
|
106
|
+
toTop();
|
|
107
|
+
if (++frames < 2) {
|
|
108
|
+
nextFrame(again);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
nextFrame(again);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
let tries = 0;
|
|
115
|
+
const attempt = () => {
|
|
116
|
+
const element = document.getElementById(id);
|
|
117
|
+
if (element) {
|
|
118
|
+
element.scrollIntoView({ block: 'start' });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (++tries < ARRIVAL_FRAMES) {
|
|
122
|
+
nextFrame(attempt);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
attempt();
|
|
126
|
+
}
|
|
65
127
|
const isEvent = (value) => !!value && typeof value.preventDefault === 'function';
|
|
66
128
|
function parseCall(optionsOrEvent, third, fourth) {
|
|
67
129
|
if (isEvent(optionsOrEvent)) {
|
|
@@ -129,10 +191,7 @@ export const useNavigate = () => {
|
|
|
129
191
|
return;
|
|
130
192
|
}
|
|
131
193
|
event?.preventDefault();
|
|
132
|
-
|
|
133
|
-
window.scrollTo(0, 0);
|
|
134
|
-
document.body.scrollTop = 0;
|
|
135
|
-
}
|
|
194
|
+
scrollOnArrival(options.scroll);
|
|
136
195
|
// Where the reader is now, for the shell they are in. A new tab and a
|
|
137
196
|
// step back through history are both already past by here. Only for
|
|
138
197
|
// somebody signed in: the shells are theirs, and a page a visitor read
|
|
@@ -152,7 +211,8 @@ export const useNavigate = () => {
|
|
|
152
211
|
nativeNavigate(to, {
|
|
153
212
|
replace: options.replace,
|
|
154
213
|
state: options.state,
|
|
155
|
-
|
|
214
|
+
// Scrolled above, after the page has rendered.
|
|
215
|
+
scroll: false,
|
|
156
216
|
});
|
|
157
217
|
}, [rrNavigate, nativeNavigate, layoutStore]);
|
|
158
218
|
};
|
package/lib/hooks/useToast.d.ts
CHANGED
|
@@ -14,6 +14,12 @@ export type ToastProps = {
|
|
|
14
14
|
*/
|
|
15
15
|
variant: 'info' | 'success' | 'warning' | 'error';
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Toasts in JupyterLab, as they always were: its notifications, or
|
|
19
|
+
* react-toastify until they are loaded. Everywhere else (the SaaS, the
|
|
20
|
+
* desktop…) they go to `ToastPanel`, primer-addons' sliding panel, which the
|
|
21
|
+
* application mounts once (2 November 2026).
|
|
22
|
+
*/
|
|
17
23
|
export declare const useToast: () => Readonly<{
|
|
18
24
|
/**
|
|
19
25
|
* Emit a message as a toast.
|
package/lib/hooks/useToast.js
CHANGED
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { toast } from 'react-toastify';
|
|
4
4
|
import { Button } from '@primer/react';
|
|
5
5
|
import { isInsideJupyterLab } from '../utils/index.js';
|
|
6
|
+
import { defaultAutoClose, toastQueue } from '../components/toast/toastQueue.js';
|
|
6
7
|
// `@datalayer/jupyter-react` and `@jupyterlab/apputils` are heavy (they pull
|
|
7
8
|
// JupyterLab / CodeMirror / lumino) and are only needed when running INSIDE
|
|
8
9
|
// JupyterLab. They are lazily loaded here so the standalone bundle (landing app)
|
|
@@ -76,11 +77,25 @@ function createContent(message, closeHandler, actions) {
|
|
|
76
77
|
return (_jsx(React.Suspense, { fallback: null, children: _jsx(JupyterReactTheme, { children: _jsx(ToastButton, { action: action, closeToast: closeHandler }) }) }, 'button-' + idx));
|
|
77
78
|
})] }))] }));
|
|
78
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Toasts in JupyterLab, as they always were: its notifications, or
|
|
82
|
+
* react-toastify until they are loaded. Everywhere else (the SaaS, the
|
|
83
|
+
* desktop…) they go to `ToastPanel`, primer-addons' sliding panel, which the
|
|
84
|
+
* application mounts once (2 November 2026).
|
|
85
|
+
*/
|
|
79
86
|
export const useToast = () => {
|
|
80
87
|
// This CANNOT use Jupyter React hooks as it may be used outside Jupyter React context.
|
|
81
88
|
const insideJupyterLab = isInsideJupyterLab();
|
|
82
89
|
const enqueueToast = (message, options = { variant: 'info' }) => {
|
|
83
90
|
const { actions, autoClose } = options;
|
|
91
|
+
if (!insideJupyterLab) {
|
|
92
|
+
return toastQueue.push({
|
|
93
|
+
variant: options.variant,
|
|
94
|
+
message,
|
|
95
|
+
actions,
|
|
96
|
+
autoClose: autoClose ?? defaultAutoClose(options.variant),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
84
99
|
const theme = resolveToastTheme();
|
|
85
100
|
switch (options.variant) {
|
|
86
101
|
case 'info': {
|
|
@@ -138,6 +153,27 @@ export const useToast = () => {
|
|
|
138
153
|
}
|
|
139
154
|
};
|
|
140
155
|
const trackAsyncTask = (promise, options) => {
|
|
156
|
+
if (!insideJupyterLab) {
|
|
157
|
+
// One toast for the task: pending, then what it came to.
|
|
158
|
+
const message = (value, result) => typeof value === 'function'
|
|
159
|
+
? String(value(result))
|
|
160
|
+
: String(value);
|
|
161
|
+
const id = toastQueue.push({
|
|
162
|
+
variant: 'info',
|
|
163
|
+
message: message(options.pending.message, undefined),
|
|
164
|
+
autoClose: false,
|
|
165
|
+
});
|
|
166
|
+
promise.then(result => toastQueue.update(id, {
|
|
167
|
+
variant: 'success',
|
|
168
|
+
message: message(options.success.message, result),
|
|
169
|
+
autoClose: defaultAutoClose('success'),
|
|
170
|
+
}), reason => toastQueue.update(id, {
|
|
171
|
+
variant: 'error',
|
|
172
|
+
message: message(options.error.message, reason),
|
|
173
|
+
autoClose: defaultAutoClose('error'),
|
|
174
|
+
}));
|
|
175
|
+
return id;
|
|
176
|
+
}
|
|
141
177
|
const theme = resolveToastTheme();
|
|
142
178
|
return insideJupyterLab && NotificationApi
|
|
143
179
|
? NotificationApi.promise(promise, options)
|
|
@@ -169,7 +205,10 @@ export const useToast = () => {
|
|
|
169
205
|
* @param id Toast id
|
|
170
206
|
*/
|
|
171
207
|
dismiss: (id) => {
|
|
172
|
-
if (insideJupyterLab
|
|
208
|
+
if (!insideJupyterLab) {
|
|
209
|
+
toastQueue.dismiss(id === undefined ? undefined : String(id));
|
|
210
|
+
}
|
|
211
|
+
else if (insideJupyterLab && NotificationApi) {
|
|
173
212
|
NotificationApi.dismiss(id);
|
|
174
213
|
}
|
|
175
214
|
else {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What one agent may execute and reach, as the Jupyter MCP Server decides it.
|
|
3
|
+
*
|
|
4
|
+
* Every answer here comes from the steps a call goes through — the token's
|
|
5
|
+
* scopes, the policy layers, then Spacer or Contents for the object — so a
|
|
6
|
+
* tool or a notebook listed as refused is one the agent would be refused.
|
|
7
|
+
*
|
|
8
|
+
* @module models/McpAgentAccess
|
|
9
|
+
*/
|
|
10
|
+
/** The agent an answer is about. `grant` is null for the token that asked. */
|
|
11
|
+
export interface McpAgentNamed {
|
|
12
|
+
grant: string | null;
|
|
13
|
+
clientId: string | null;
|
|
14
|
+
clientName: string | null;
|
|
15
|
+
scopes: string[];
|
|
16
|
+
orgUid: string | null;
|
|
17
|
+
teamUid: string | null;
|
|
18
|
+
}
|
|
19
|
+
export type McpToolsetHealthState = 'ready' | 'degraded' | 'unknown';
|
|
20
|
+
export interface McpToolsetHealth {
|
|
21
|
+
/** `unknown` is a toolset nothing here measures, never one reported well. */
|
|
22
|
+
state: McpToolsetHealthState;
|
|
23
|
+
detail: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* How a client comes to hold a toolset: the plain endpoint serves `base`, a
|
|
27
|
+
* URL names an `optional` one, a `source` is enabled in a session, and an
|
|
28
|
+
* `application` one is reached through an application's connection.
|
|
29
|
+
*/
|
|
30
|
+
export type McpToolsetSelected = 'base' | 'optional' | 'source' | 'application';
|
|
31
|
+
/** One tool, decided for this agent. */
|
|
32
|
+
export interface McpAgentTool {
|
|
33
|
+
name: string;
|
|
34
|
+
/** The scopes it requires, space-separated. */
|
|
35
|
+
scope: string;
|
|
36
|
+
allowed: boolean;
|
|
37
|
+
/** Why not: `scope`, a policy rule, `connection` or `item`. */
|
|
38
|
+
rule: string | null;
|
|
39
|
+
/** Which layer decided. */
|
|
40
|
+
decidedBy: string | null;
|
|
41
|
+
detail: string | null;
|
|
42
|
+
/** The layer that makes a person approve a call first, when one does. */
|
|
43
|
+
approval: string | null;
|
|
44
|
+
}
|
|
45
|
+
/** A source's own rule for its calls, as Contents keeps it. */
|
|
46
|
+
export type McpSourceApprovalPolicy = 'explicit' | 'auto-allowlisted' | 'never';
|
|
47
|
+
export interface McpAgentToolset {
|
|
48
|
+
kind: 'toolset' | 'source';
|
|
49
|
+
/** The source, when it is one. */
|
|
50
|
+
uid?: string;
|
|
51
|
+
name: string;
|
|
52
|
+
title: string;
|
|
53
|
+
description: string | null;
|
|
54
|
+
selected: McpToolsetSelected;
|
|
55
|
+
health: McpToolsetHealth;
|
|
56
|
+
/** A source's; a toolset's tools each say their own `approval`. */
|
|
57
|
+
approvalPolicy: McpSourceApprovalPolicy | null;
|
|
58
|
+
tools: McpAgentTool[];
|
|
59
|
+
}
|
|
60
|
+
export interface McpAgentToolsets {
|
|
61
|
+
agent: McpAgentNamed;
|
|
62
|
+
toolsets: McpAgentToolset[];
|
|
63
|
+
/** Why the sources are missing, when Contents could not be read. */
|
|
64
|
+
sourcesError: string | null;
|
|
65
|
+
}
|
|
66
|
+
/** One notebook or source, and how far this agent reaches it. */
|
|
67
|
+
export interface McpReachRow {
|
|
68
|
+
kind: 'notebook' | 'source';
|
|
69
|
+
uid: string;
|
|
70
|
+
name: string;
|
|
71
|
+
/** A notebook's Space, a source's kind. */
|
|
72
|
+
where: string | null;
|
|
73
|
+
read: boolean;
|
|
74
|
+
write: boolean;
|
|
75
|
+
execute: boolean;
|
|
76
|
+
/** Why it stops short of `execute`, in sentences. */
|
|
77
|
+
refused: string[];
|
|
78
|
+
/** Whether a source is the caller's own. */
|
|
79
|
+
yours?: boolean;
|
|
80
|
+
/** Set when the check could not run: an answer about the check, not the agent. */
|
|
81
|
+
unchecked: string | null;
|
|
82
|
+
}
|
|
83
|
+
export interface McpAgentReach {
|
|
84
|
+
agent: McpAgentNamed;
|
|
85
|
+
notebooks: McpReachRow[];
|
|
86
|
+
sources: McpReachRow[];
|
|
87
|
+
/** How many there are of each, of which `limit` were asked about. */
|
|
88
|
+
notebooksListed: number;
|
|
89
|
+
sourcesListed: number;
|
|
90
|
+
limit: number;
|
|
91
|
+
/** Contents has more than were read. */
|
|
92
|
+
sourcesTruncated: boolean;
|
|
93
|
+
notebooksError: string | null;
|
|
94
|
+
sourcesError: string | null;
|
|
95
|
+
}
|
package/lib/models/Usage.d.ts
CHANGED
|
@@ -8,6 +8,15 @@ interface IResources {
|
|
|
8
8
|
memory: string;
|
|
9
9
|
'nvidia.com/gpu'?: string;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* What ran and hosted the agent a usage record is for (`runtime_engine`):
|
|
13
|
+
* a Datalayer runtime, kagent, or AWS AgentCore in an organization's AWS
|
|
14
|
+
* account. IAM records it from the launch; a record from before, or of a
|
|
15
|
+
* call with no agent runtime behind it, has none.
|
|
16
|
+
*/
|
|
17
|
+
export type UsageRuntimeEngine = 'datalayer' | 'kagent' | 'agentcore';
|
|
18
|
+
/** The engine a raw record says, or `undefined` when it says none (or one not known). */
|
|
19
|
+
export declare const usageRuntimeEngineOf: (value: unknown) => UsageRuntimeEngine | undefined;
|
|
11
20
|
export interface IRawUsage {
|
|
12
21
|
account_uid: string;
|
|
13
22
|
resource_uid: string;
|
|
@@ -26,6 +35,7 @@ export interface IRawUsage {
|
|
|
26
35
|
usage_period_label?: string;
|
|
27
36
|
usage_period_start?: string;
|
|
28
37
|
usage_period_end?: string;
|
|
38
|
+
runtime_engine?: UsageRuntimeEngine | null;
|
|
29
39
|
metadata: Map<string, string>;
|
|
30
40
|
}
|
|
31
41
|
export declare const asUsage: (u: any) => {
|
|
@@ -46,6 +56,7 @@ export declare const asUsage: (u: any) => {
|
|
|
46
56
|
usagePeriodLabel: any;
|
|
47
57
|
usagePeriodStart: Date | undefined;
|
|
48
58
|
usagePeriodEnd: Date | undefined;
|
|
59
|
+
runtimeEngine: UsageRuntimeEngine | undefined;
|
|
49
60
|
metadata: Map<string, string>;
|
|
50
61
|
};
|
|
51
62
|
export interface IUsage {
|
|
@@ -66,6 +77,8 @@ export interface IUsage {
|
|
|
66
77
|
usagePeriodLabel?: string;
|
|
67
78
|
usagePeriodStart?: Date;
|
|
68
79
|
usagePeriodEnd?: Date;
|
|
80
|
+
/** What ran and hosted its agent; absent on a record that says none. */
|
|
81
|
+
runtimeEngine?: UsageRuntimeEngine;
|
|
69
82
|
metadata: Map<string, string>;
|
|
70
83
|
}
|
|
71
84
|
export {};
|
package/lib/models/Usage.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
+
/** The engine a raw record says, or `undefined` when it says none (or one not known). */
|
|
6
|
+
export const usageRuntimeEngineOf = (value) => value === 'datalayer' || value === 'kagent' || value === 'agentcore'
|
|
7
|
+
? value
|
|
8
|
+
: undefined;
|
|
5
9
|
export const asUsage = (u) => {
|
|
6
10
|
const usagePeriodKey = u.usage_period_key;
|
|
7
11
|
const usagePeriodLabel = u.usage_period_label;
|
|
@@ -25,6 +29,7 @@ export const asUsage = (u) => {
|
|
|
25
29
|
usagePeriodLabel,
|
|
26
30
|
usagePeriodStart: usagePeriodStart ? new Date(usagePeriodStart) : undefined,
|
|
27
31
|
usagePeriodEnd: usagePeriodEnd ? new Date(usagePeriodEnd) : undefined,
|
|
32
|
+
runtimeEngine: usageRuntimeEngineOf(u.runtime_engine),
|
|
28
33
|
metadata: new Map(Object.entries(u.metadata ?? {})),
|
|
29
34
|
};
|
|
30
35
|
};
|
package/lib/models/index.d.ts
CHANGED
package/lib/models/index.js
CHANGED
|
@@ -55,6 +55,7 @@ export * from './Invite.js';
|
|
|
55
55
|
export * from './LandingRoles.js';
|
|
56
56
|
export * from './McpAuditEvent.js';
|
|
57
57
|
export * from './McpBinding.js';
|
|
58
|
+
export * from './McpAgentAccess.js';
|
|
58
59
|
export * from './McpPolicy.js';
|
|
59
60
|
export * from './McpTask.js';
|
|
60
61
|
export * from './Member.js';
|
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
import type { OtelSpan, OtelLog, OtelMetric, OtelQueryResult } from '../types';
|
|
2
2
|
import type { OtelSystemData, OtelSystemProcess, OtelSystemDisk, OtelSystemTable } from '../hooks';
|
|
3
|
+
export interface FetchTokenUsageOptions {
|
|
4
|
+
/** The window, in hours back from now (24 by default). */
|
|
5
|
+
hours?: number;
|
|
6
|
+
/** Only these services (runtimes); every one by default. */
|
|
7
|
+
serviceNames?: string[];
|
|
8
|
+
accountUid?: string;
|
|
9
|
+
}
|
|
10
|
+
/** What `GET /api/otel/v1/metrics/tokens` answers. */
|
|
11
|
+
export interface OtelTokenUsage {
|
|
12
|
+
total: number;
|
|
13
|
+
prompt: number;
|
|
14
|
+
completion: number;
|
|
15
|
+
by_service: Array<{
|
|
16
|
+
service_name: string;
|
|
17
|
+
total: number;
|
|
18
|
+
prompt: number;
|
|
19
|
+
completion: number;
|
|
20
|
+
}>;
|
|
21
|
+
window?: {
|
|
22
|
+
start: number;
|
|
23
|
+
end: number;
|
|
24
|
+
hours: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* When telemetry last reached the store, whatever the account: `at` in
|
|
28
|
+
* unix nanoseconds, from rows ingested since the service started
|
|
29
|
+
* (`ingest`) or from its newest stored file (`files`); `null` with the
|
|
30
|
+
* `reason` when nothing was ever stored or the time could not be read.
|
|
31
|
+
* Absent from a service that does not say it.
|
|
32
|
+
*/
|
|
33
|
+
last_received?: OtelLastReceived;
|
|
34
|
+
/**
|
|
35
|
+
* The same for the asked account, known only from what arrived since the
|
|
36
|
+
* service started (`since`, unix nanoseconds): `at` is `null`, with a
|
|
37
|
+
* `reason`, until rows of the account arrive.
|
|
38
|
+
*/
|
|
39
|
+
account_last_received?: OtelAccountLastReceived;
|
|
40
|
+
}
|
|
41
|
+
export interface OtelLastReceived {
|
|
42
|
+
at: number | null;
|
|
43
|
+
source: 'ingest' | 'files' | null;
|
|
44
|
+
reason: string | null;
|
|
45
|
+
}
|
|
46
|
+
export interface OtelAccountLastReceived {
|
|
47
|
+
at: number | null;
|
|
48
|
+
since: number;
|
|
49
|
+
reason: string | null;
|
|
50
|
+
}
|
|
3
51
|
export interface OtelClientOptions {
|
|
4
52
|
/**
|
|
5
53
|
* Base URL of the OTEL service.
|
|
@@ -65,6 +113,14 @@ export declare class OtelClient {
|
|
|
65
113
|
data: OtelMetric[];
|
|
66
114
|
count: number;
|
|
67
115
|
}>;
|
|
116
|
+
/**
|
|
117
|
+
* The tokens agents counted over the last `hours` (24 by default): total,
|
|
118
|
+
* prompt and completion, overall and per service, in one request — the
|
|
119
|
+
* service reads each counter's increase over the window in one scan. The
|
|
120
|
+
* answer also says when telemetry last reached the store (`last_received`),
|
|
121
|
+
* so a total of 0 can be told from a store that stopped receiving.
|
|
122
|
+
*/
|
|
123
|
+
fetchTokenUsage(options?: FetchTokenUsageOptions): Promise<OtelTokenUsage>;
|
|
68
124
|
/** Sum all values for one metric name, with optional service filter. */
|
|
69
125
|
fetchMetricTotal(metricName: string, options?: FetchMetricTotalOptions): Promise<number>;
|
|
70
126
|
/** List known service names observed in traces. */
|
|
@@ -177,6 +177,31 @@ export class OtelClient {
|
|
|
177
177
|
: [];
|
|
178
178
|
return { data: normalized, count: normalized.length };
|
|
179
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* The tokens agents counted over the last `hours` (24 by default): total,
|
|
182
|
+
* prompt and completion, overall and per service, in one request — the
|
|
183
|
+
* service reads each counter's increase over the window in one scan. The
|
|
184
|
+
* answer also says when telemetry last reached the store (`last_received`),
|
|
185
|
+
* so a total of 0 can be told from a store that stopped receiving.
|
|
186
|
+
*/
|
|
187
|
+
async fetchTokenUsage(options = {}) {
|
|
188
|
+
const params = new URLSearchParams({ hours: String(options.hours ?? 24) });
|
|
189
|
+
for (const name of options.serviceNames ?? []) {
|
|
190
|
+
params.append('service_name', name);
|
|
191
|
+
}
|
|
192
|
+
if (options.accountUid)
|
|
193
|
+
params.set('account_uid', options.accountUid);
|
|
194
|
+
const resp = await otelFetch(`${this.baseUrl}/api/otel/v1/metrics/tokens?${params}`, this.token, this.userUid);
|
|
195
|
+
return {
|
|
196
|
+
total: Number(resp.total ?? 0),
|
|
197
|
+
prompt: Number(resp.prompt ?? 0),
|
|
198
|
+
completion: Number(resp.completion ?? 0),
|
|
199
|
+
by_service: Array.isArray(resp.by_service) ? resp.by_service : [],
|
|
200
|
+
window: resp.window,
|
|
201
|
+
last_received: resp.last_received,
|
|
202
|
+
account_last_received: resp.account_last_received,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
180
205
|
/** Sum all values for one metric name, with optional service filter. */
|
|
181
206
|
async fetchMetricTotal(metricName, options = {}) {
|
|
182
207
|
const { serviceName, limit = 500, fallbackWithoutService = true, accountUid, } = options;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { OtelClient, createOtelClient } from './OtelClient';
|
|
2
|
-
export type { OtelClientOptions, FetchTracesOptions, FetchLogsOptions, FetchMetricsOptions, FetchMetricTotalOptions, OtelSystemData, OtelSystemProcess, OtelSystemDisk, OtelSystemTable, } from './OtelClient';
|
|
2
|
+
export type { OtelClientOptions, FetchTracesOptions, FetchLogsOptions, FetchMetricsOptions, FetchMetricTotalOptions, FetchTokenUsageOptions, OtelTokenUsage, OtelLastReceived, OtelAccountLastReceived, OtelSystemData, OtelSystemProcess, OtelSystemDisk, OtelSystemTable, } from './OtelClient';
|
package/lib/otel/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type { ResolvedOtelAuth } from './auth';
|
|
|
20
20
|
export { useOtelTraces, useOtelTrace, useOtelLogs, useOtelMetrics, useOtelServices, useOtelStats, useOtelQuery, useOtelSystem, useOtelWebSocket, setOtelOnUnauthorized, } from './hooks';
|
|
21
21
|
export type { OtelWsMessage, OtelWsCallbacks, OtelSystemData, OtelSystemProcess, OtelSystemDisk, OtelSystemTable, } from './hooks';
|
|
22
22
|
export { OtelClient, createOtelClient } from './client';
|
|
23
|
-
export type { OtelClientOptions, FetchTracesOptions, FetchLogsOptions, FetchMetricsOptions, FetchMetricTotalOptions, } from './client';
|
|
23
|
+
export type { OtelClientOptions, FetchTracesOptions, FetchLogsOptions, FetchMetricsOptions, FetchMetricTotalOptions, FetchTokenUsageOptions, OtelTokenUsage, OtelLastReceived, OtelAccountLastReceived, } from './client';
|
|
24
24
|
export { OtelLive } from './views';
|
|
25
25
|
export { OtelTracesList } from './views';
|
|
26
26
|
export { OtelSpanDetail } from './views';
|