@datalayer/agent-runtimes 1.3.74 → 1.3.78
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/agents/AgentDetails.d.ts +16 -2
- package/lib/agents/AgentDetails.js +381 -272
- package/lib/chat/ChatFloating.d.ts +27 -1
- package/lib/chat/ChatFloating.js +97 -13
- package/lib/chat/assistant/AgentDetailsDialog.d.ts +21 -0
- package/lib/chat/assistant/AgentDetailsDialog.js +19 -0
- package/lib/chat/assistant/AssistantContextMenu.d.ts +67 -0
- package/lib/chat/assistant/AssistantContextMenu.js +68 -0
- package/lib/chat/assistant/AssistantStage.d.ts +144 -4
- package/lib/chat/assistant/AssistantStage.js +280 -10
- package/lib/chat/assistant/BalloonParts.d.ts +33 -5
- package/lib/chat/assistant/BalloonParts.js +66 -44
- package/lib/chat/assistant/BalloonVisual.d.ts +10 -2
- package/lib/chat/assistant/BalloonVisual.js +4 -3
- package/lib/chat/assistant/SandboxDetailsDialog.d.ts +44 -0
- package/lib/chat/assistant/SandboxDetailsDialog.js +214 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +54 -6
- package/lib/chat/assistant/SpeechBalloon.js +73 -22
- package/lib/chat/assistant/assistantDetails.d.ts +84 -0
- package/lib/chat/assistant/assistantDetails.js +140 -0
- package/lib/chat/assistant/formats/files.d.ts +23 -0
- package/lib/chat/assistant/formats/files.js +48 -0
- package/lib/chat/assistant/formats/index.d.ts +2 -0
- package/lib/chat/assistant/formats/index.js +1 -0
- package/lib/chat/assistant/state.d.ts +7 -14
- package/lib/chat/assistant/state.js +21 -20
- package/lib/chat/assistant/toolLine.d.ts +16 -0
- package/lib/chat/assistant/toolLine.js +46 -0
- package/lib/chat/base/ChatBase.js +71 -1
- package/lib/chat/base/loopMessage.d.ts +37 -0
- package/lib/chat/base/loopMessage.js +37 -0
- package/lib/chat/indicators/TypingDots.d.ts +13 -0
- package/lib/chat/indicators/TypingDots.js +21 -0
- package/lib/chat/messages/ChatMarkdown.d.ts +16 -0
- package/lib/chat/messages/ChatMarkdown.js +18 -0
- package/lib/chat/messages/ChatMessageList.d.ts +8 -1
- package/lib/chat/messages/ChatMessageList.js +25 -33
- package/lib/chat/styles/streamdownStyles.js +3 -1
- package/lib/chat/tools/ToolCallDisplay.d.ts +8 -1
- package/lib/chat/tools/ToolCallDisplay.js +8 -7
- package/lib/components/code-sandboxes/CodeSandboxCellVariablesDialog.js +5 -11
- package/lib/components/code-sandboxes/CodeSandboxVariables.d.ts +8 -0
- package/lib/components/code-sandboxes/CodeSandboxVariables.js +17 -11
- package/lib/components/inspector/AgentInspector.d.ts +59 -0
- package/lib/components/inspector/AgentInspector.js +220 -0
- package/lib/components/inspector/AgentInspectorDialog.d.ts +19 -0
- package/lib/components/inspector/AgentInspectorDialog.js +9 -0
- package/lib/components/inspector/JsonTree.d.ts +24 -0
- package/lib/components/inspector/JsonTree.js +89 -0
- package/lib/components/inspector/a2aInspect.d.ts +56 -0
- package/lib/components/inspector/a2aInspect.js +478 -0
- package/lib/components/inspector/agentInspector.d.ts +179 -0
- package/lib/components/inspector/agentInspector.js +267 -0
- package/lib/components/inspector/chatInspect.d.ts +38 -0
- package/lib/components/inspector/chatInspect.js +65 -0
- package/lib/components/inspector/classify.d.ts +19 -0
- package/lib/components/inspector/classify.js +42 -0
- package/lib/components/inspector/index.d.ts +14 -0
- package/lib/components/inspector/index.js +18 -0
- package/lib/components/teams/A2ATeamGraph.d.ts +69 -1
- package/lib/components/teams/A2ATeamGraph.js +277 -37
- package/lib/components/teams/NotebookPreviewView.js +9 -1
- package/lib/components/teams/TeamNotebook.d.ts +3 -1
- package/lib/components/teams/TeamNotebook.js +3 -3
- package/lib/components/teams/TeamNotebookView.js +15 -0
- package/lib/components/teams/jupyterThemeVariables.d.ts +9 -0
- package/lib/components/teams/jupyterThemeVariables.js +441 -0
- package/lib/components/teams/teamConnections.d.ts +9 -1
- package/lib/components/teams/teamConnections.js +20 -0
- package/lib/components/teams/teamNotebookKernel.d.ts +3 -0
- package/lib/components/teams/teamNotebookKernel.js +17 -0
- package/lib/components/teams/useA2ATeam.d.ts +20 -3
- package/lib/components/teams/useA2ATeam.js +108 -23
- package/lib/examples/AgentA2ATeamExample.d.ts +13 -0
- package/lib/examples/AgentA2ATeamExample.js +67 -13
- package/lib/examples/AssistantExample.js +65 -26
- package/lib/examples/AssistantGalleryExample.js +2 -1
- package/lib/examples/exampleGroups.d.ts +1 -1
- package/lib/examples/exampleGroups.js +1 -1
- package/lib/examples/utils/assistantGallery.d.ts +7 -6
- package/lib/examples/utils/assistantGallery.js +23 -8
- package/lib/jupyter/exec/Python.d.ts +0 -1
- package/lib/jupyter/exec/Python.js +0 -32
- package/lib/jupyter/exec/Snippets.d.ts +4 -12
- package/lib/jupyter/exec/Snippets.js +0 -8
- package/lib/loop/apps/AppFeedback.js +3 -1
- package/lib/loop/apps/AppKept.d.ts +22 -0
- package/lib/loop/apps/AppKept.js +36 -0
- package/lib/loop/apps/AppRenderer.d.ts +11 -1
- package/lib/loop/apps/AppRenderer.js +22 -4
- package/lib/loop/apps/checks.d.ts +7 -0
- package/lib/loop/apps/checks.js +39 -0
- package/lib/loop/apps/index.d.ts +3 -0
- package/lib/loop/apps/index.js +3 -0
- package/lib/loop/apps/kept.d.ts +30 -0
- package/lib/loop/apps/kept.js +52 -0
- package/lib/loop/apps/records.d.ts +12 -4
- package/lib/loop/apps/records.js +13 -5
- package/lib/loop/apps/saved.d.ts +30 -0
- package/lib/loop/apps/saved.js +40 -0
- package/lib/loop/core/index.d.ts +17 -0
- package/lib/loop/core/index.js +1 -0
- package/lib/loop/embed/AppEmbed.d.ts +8 -1
- package/lib/loop/embed/AppEmbed.js +4 -4
- package/lib/loop/embed/LoopEmbed.d.ts +6 -1
- package/lib/loop/embed/LoopEmbed.js +4 -2
- package/lib/loop/embed/element.js +1 -1
- package/lib/loop/plugins/agents/DatalayerAgentBridge.js +12 -6
- package/lib/loop/plugins/agents/browserService.d.ts +7 -1
- package/lib/loop/plugins/agents/browserService.js +4 -0
- package/lib/loop/plugins/agents/plugin.d.ts +16 -0
- package/lib/loop/plugins/agents/switchable.d.ts +3 -0
- package/lib/loop/plugins/agents/switchable.js +1 -0
- package/lib/loop/plugins/app-activity/index.js +3 -0
- package/lib/loop/plugins/app-computer/index.js +3 -0
- package/lib/loop/plugins/app-rules/AppRulesCard.d.ts +5 -0
- package/lib/loop/plugins/app-rules/AppRulesCard.js +25 -6
- package/lib/loop/plugins/app-rules/index.js +3 -0
- package/lib/loop/plugins/assistant/index.d.ts +7 -2
- package/lib/loop/plugins/assistant/index.js +58 -4
- package/lib/loop/plugins/assistant-characters/index.d.ts +7 -0
- package/lib/loop/plugins/assistant-characters/index.js +17 -0
- package/lib/loop/plugins/chat/ChatView.js +4 -1
- package/lib/loop/plugins/page-layout/SplitLayout.d.ts +2 -1
- package/lib/loop/plugins/page-layout/SplitLayout.js +4 -0
- package/lib/loop/presets.d.ts +4 -0
- package/lib/loop/presets.js +3 -1
- package/lib/loop/shell/LoopWorkspace.d.ts +7 -0
- package/lib/loop/shell/LoopWorkspace.js +5 -4
- package/lib/loop/shell/SidebarRail.d.ts +53 -0
- package/lib/loop/shell/SidebarRail.js +74 -0
- package/lib/loop/shell/index.d.ts +2 -0
- package/lib/loop/shell/index.js +2 -0
- package/lib/loop/shell/paneMotion.d.ts +21 -0
- package/lib/loop/shell/paneMotion.js +37 -0
- package/lib/runtimes/browser/a2aPeer.d.ts +4 -1
- package/lib/specs/apps.js +8 -5
- package/lib/specs/uiPlugins.js +529 -0
- package/lib/types/agentspecs.d.ts +5 -3
- package/lib/types/chat.d.ts +12 -0
- package/package.json +2 -2
- package/scripts/codegen/generate_jupyter_theme_variables.mjs +57 -0
- package/scripts/codegen/generate_ui_plugins.py +112 -2
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { Button, Dialog, Heading, IconButton, Label, Link, Text, } from '@primer/react';
|
|
4
|
+
import { CheckIcon, CopyIcon } from '@primer/octicons-react';
|
|
5
|
+
import { Box } from '@datalayer/primer-addons';
|
|
6
|
+
import { KernelAPI, KernelConnection, ServerConnection, } from '@jupyterlab/services';
|
|
7
|
+
import { KernelVariables, executeSilently, } from '@datalayer/jupyter-react';
|
|
8
|
+
import { NO_SANDBOX_YET, SANDBOX_KIND_LABELS, displayUrl, sandboxIsLive, sandboxExecuteOverHttp, timeLeft, } from './assistantDetails';
|
|
9
|
+
/** A status, as a Primer label. */
|
|
10
|
+
function StatusLabel({ status }) {
|
|
11
|
+
const variant = status === 'running' || status === 'idle'
|
|
12
|
+
? 'success'
|
|
13
|
+
: status === 'busy' || status === 'starting'
|
|
14
|
+
? 'attention'
|
|
15
|
+
: status === 'error' || status === 'dead'
|
|
16
|
+
? 'danger'
|
|
17
|
+
: 'secondary';
|
|
18
|
+
return (_jsx(Label, { variant: variant, size: "small", "data-sandbox-status": status, children: status }));
|
|
19
|
+
}
|
|
20
|
+
/** A URL, linked, with a copy button. */
|
|
21
|
+
function UrlValue({ url }) {
|
|
22
|
+
const [copied, setCopied] = useState(false);
|
|
23
|
+
return (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, minWidth: 0 }, children: [_jsx(Link, { href: url, target: "_blank", rel: "noreferrer", sx: { fontFamily: 'mono', fontSize: 0, wordBreak: 'break-all' }, children: url }), _jsx(IconButton, { icon: copied ? CheckIcon : CopyIcon, "aria-label": "Copy the URL", size: "small", variant: "invisible", onClick: () => {
|
|
24
|
+
void navigator.clipboard?.writeText(url).then(() => {
|
|
25
|
+
setCopied(true);
|
|
26
|
+
setTimeout(() => setCopied(false), 1500);
|
|
27
|
+
});
|
|
28
|
+
} })] }));
|
|
29
|
+
}
|
|
30
|
+
/** A moment, for a reader. */
|
|
31
|
+
function moment(value) {
|
|
32
|
+
if (value === undefined || value === '') {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const at = typeof value === 'number'
|
|
36
|
+
? new Date(value < 1e12 ? value * 1000 : value)
|
|
37
|
+
: new Date(value);
|
|
38
|
+
return Number.isNaN(at.getTime()) ? undefined : at.toLocaleString();
|
|
39
|
+
}
|
|
40
|
+
const PYODIDE_MARKER = '__DL_PYODIDE__';
|
|
41
|
+
const PYODIDE_SNIPPET = `def __dl_pyodide():
|
|
42
|
+
import json, sys
|
|
43
|
+
facts = {'python': sys.version.split()[0], 'packages': []}
|
|
44
|
+
try:
|
|
45
|
+
import pyodide
|
|
46
|
+
facts['version'] = pyodide.__version__
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
try:
|
|
50
|
+
import pyodide_js
|
|
51
|
+
facts['packages'] = sorted(str(k) for k in pyodide_js.loadedPackages.object_keys())
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
54
|
+
print('${PYODIDE_MARKER}' + json.dumps(facts) + '${PYODIDE_MARKER}')
|
|
55
|
+
try:
|
|
56
|
+
__dl_pyodide()
|
|
57
|
+
finally:
|
|
58
|
+
del __dl_pyodide
|
|
59
|
+
`;
|
|
60
|
+
/** Pyodide's facts out of what the snippet printed. */
|
|
61
|
+
export function parsePyodideFacts(stdout) {
|
|
62
|
+
const end = stdout.lastIndexOf(PYODIDE_MARKER);
|
|
63
|
+
const start = end > 0 ? stdout.lastIndexOf(PYODIDE_MARKER, end - 1) : -1;
|
|
64
|
+
if (start < 0) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const facts = JSON.parse(stdout.slice(start + PYODIDE_MARKER.length, end));
|
|
69
|
+
return {
|
|
70
|
+
version: facts.version,
|
|
71
|
+
python: facts.python,
|
|
72
|
+
packages: Array.isArray(facts.packages) ? facts.packages : [],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** The kernel connection the dialog lists from: the host's, or one opened here. */
|
|
80
|
+
function useKernelConnection(sandbox) {
|
|
81
|
+
const [opened, setOpened] = useState(null);
|
|
82
|
+
const { connection, url, token, kernelId, kind } = sandbox;
|
|
83
|
+
const live = sandboxIsLive(sandbox);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (connection || !url || !kernelId || kind === 'browser' || !live) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const base = url.replace(/\/+$/, '') + '/';
|
|
89
|
+
const serverSettings = ServerConnection.makeSettings({
|
|
90
|
+
baseUrl: base,
|
|
91
|
+
wsUrl: base.replace(/^http/, 'ws'),
|
|
92
|
+
token: token ?? '',
|
|
93
|
+
appendToken: true,
|
|
94
|
+
});
|
|
95
|
+
const opening = new KernelConnection({
|
|
96
|
+
model: { id: kernelId, name: '' },
|
|
97
|
+
serverSettings,
|
|
98
|
+
handleComms: true,
|
|
99
|
+
});
|
|
100
|
+
setOpened(opening);
|
|
101
|
+
return () => {
|
|
102
|
+
setOpened(null);
|
|
103
|
+
opening.dispose();
|
|
104
|
+
};
|
|
105
|
+
}, [connection, url, token, kernelId, kind, live]);
|
|
106
|
+
return live ? (connection ?? opened) : null;
|
|
107
|
+
}
|
|
108
|
+
export function SandboxDetailsDialog({ sandbox, name, onClose, }) {
|
|
109
|
+
const connection = useKernelConnection(sandbox);
|
|
110
|
+
const [kernel, setKernel] = useState({});
|
|
111
|
+
const [pyodide, setPyodide] = useState();
|
|
112
|
+
const [showPackages, setShowPackages] = useState(false);
|
|
113
|
+
// The kernel, as it says it is: its name, language, status, last activity.
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!connection) {
|
|
116
|
+
setKernel({});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
let cancelled = false;
|
|
120
|
+
const update = (next) => !cancelled && setKernel(current => ({ ...current, ...next }));
|
|
121
|
+
update({
|
|
122
|
+
id: connection.id,
|
|
123
|
+
name: connection.name || undefined,
|
|
124
|
+
status: connection.status,
|
|
125
|
+
});
|
|
126
|
+
connection.info
|
|
127
|
+
.then(info => update({
|
|
128
|
+
language: [info.language_info?.name, info.language_info?.version]
|
|
129
|
+
.filter(Boolean)
|
|
130
|
+
.join(' '),
|
|
131
|
+
}))
|
|
132
|
+
.catch(() => undefined);
|
|
133
|
+
if (sandbox.kind !== 'browser') {
|
|
134
|
+
KernelAPI.getKernelModel(connection.id, connection.serverSettings)
|
|
135
|
+
.then(model => update({
|
|
136
|
+
name: model?.name || undefined,
|
|
137
|
+
lastActivity: model?.last_activity,
|
|
138
|
+
}))
|
|
139
|
+
.catch(() => undefined);
|
|
140
|
+
}
|
|
141
|
+
const onStatus = (_, status) => update({ status });
|
|
142
|
+
connection.statusChanged.connect(onStatus);
|
|
143
|
+
return () => {
|
|
144
|
+
cancelled = true;
|
|
145
|
+
connection.statusChanged.disconnect(onStatus);
|
|
146
|
+
};
|
|
147
|
+
}, [connection, sandbox.kind]);
|
|
148
|
+
// Pyodide's version and packages, asked once of the browser kernel.
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (sandbox.kind !== 'browser' || !connection) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
let cancelled = false;
|
|
154
|
+
executeSilently(connection, PYODIDE_SNIPPET)
|
|
155
|
+
.then(({ stdout }) => {
|
|
156
|
+
if (!cancelled) {
|
|
157
|
+
setPyodide(parsePyodideFacts(stdout));
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
.catch(() => undefined);
|
|
161
|
+
return () => {
|
|
162
|
+
cancelled = true;
|
|
163
|
+
};
|
|
164
|
+
}, [connection, sandbox.kind]);
|
|
165
|
+
// Without a connection, the agent's server runs the snippet in its sandbox.
|
|
166
|
+
const execute = useMemo(() =>
|
|
167
|
+
// Not before the sandbox is up: the server would start one to answer.
|
|
168
|
+
!connection && sandbox.serverUrl && sandboxIsLive(sandbox)
|
|
169
|
+
? sandboxExecuteOverHttp(sandbox.serverUrl, sandbox.agentId)
|
|
170
|
+
: undefined, [connection, sandbox.serverUrl, sandbox.agentId]);
|
|
171
|
+
const url = displayUrl(sandbox.url ?? sandbox.serverUrl);
|
|
172
|
+
const left = timeLeft(sandbox.expiresAt);
|
|
173
|
+
const facts = [];
|
|
174
|
+
const add = (key, value) => {
|
|
175
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
176
|
+
facts.push([key, value]);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
add('Environment', sandbox.environment);
|
|
180
|
+
add('URL', url ? _jsx(UrlValue, { url: url }) : undefined);
|
|
181
|
+
add('Kernel', kernel.name || kernel.id ? (_jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: [kernel.name, kernel.id?.slice(0, 8)].filter(Boolean).join(' · ') })) : undefined);
|
|
182
|
+
add('Language', kernel.language);
|
|
183
|
+
add('Kernel status', kernel.status ? _jsx(StatusLabel, { status: kernel.status }) : undefined);
|
|
184
|
+
add('Agent', sandbox.kind === 'local' || sandbox.kind === 'runtime'
|
|
185
|
+
? sandbox.agentId
|
|
186
|
+
: undefined);
|
|
187
|
+
add('Runtime', sandbox.runtimeUid);
|
|
188
|
+
add('Time left', left);
|
|
189
|
+
add('Burning rate', sandbox.burningRate !== undefined
|
|
190
|
+
? `${sandbox.burningRate} credits/h`
|
|
191
|
+
: undefined);
|
|
192
|
+
add('Pyodide', pyodide?.version);
|
|
193
|
+
add('Packages', pyodide && pyodide.packages.length > 0 ? (_jsxs(Box, { children: [_jsx(Button, { size: "small", variant: "invisible", onClick: () => setShowPackages(!showPackages), "aria-expanded": showPackages, sx: { px: 1 }, children: `${pyodide.packages.length} loaded` }), showPackages && (_jsx(Text, { as: "p", sx: { m: 0, mt: 1, fontFamily: 'mono', fontSize: 0 }, "data-sandbox-packages": "", children: pyodide.packages.join(', ') }))] })) : undefined);
|
|
194
|
+
add('Started', moment(sandbox.startedAt));
|
|
195
|
+
add('Last activity', moment(sandbox.lastActivity ?? kernel.lastActivity));
|
|
196
|
+
return (_jsx(Dialog, { title: `${name} · Code Sandbox`, onClose: onClose, width: "xlarge", height: "large", children: _jsxs(Box, { "data-assistant-sandbox-details": "", sx: { display: 'flex', flexDirection: 'column', gap: 3 }, children: [_jsxs(Box, { "data-sandbox-summary": "", sx: {
|
|
197
|
+
p: 3,
|
|
198
|
+
bg: 'canvas.subtle',
|
|
199
|
+
border: '1px solid',
|
|
200
|
+
borderColor: 'border.default',
|
|
201
|
+
borderRadius: 2,
|
|
202
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 2 }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold' }, "data-sandbox-kind": sandbox.kind, children: [SANDBOX_KIND_LABELS[sandbox.kind], sandbox.kind === 'runtime' && sandbox.variant
|
|
203
|
+
? ` (${sandbox.variant})`
|
|
204
|
+
: ''] }), sandbox.status && _jsx(StatusLabel, { status: sandbox.status })] }), _jsx(Box, { as: "dl", sx: {
|
|
205
|
+
display: 'grid',
|
|
206
|
+
gridTemplateColumns: 'max-content minmax(0, 1fr)',
|
|
207
|
+
columnGap: 3,
|
|
208
|
+
rowGap: 1,
|
|
209
|
+
m: 0,
|
|
210
|
+
fontSize: 1,
|
|
211
|
+
alignItems: 'center',
|
|
212
|
+
}, children: facts.map(([key, value]) => (_jsxs(Box, { sx: { display: 'contents' }, children: [_jsx(Box, { as: "dt", sx: { color: 'fg.muted' }, children: key }), _jsx(Box, { as: "dd", sx: { m: 0, minWidth: 0 }, "data-sandbox-fact": key, children: value })] }, key))) })] }), _jsxs(Box, { children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 2 }, children: "Variables" }), sandboxIsLive(sandbox) ? (_jsx(KernelVariables, { connection: connection, execute: execute, maxHeight: "calc(80vh - 360px)" })) : (_jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, "data-sandbox-not-running": "", children: NO_SANDBOX_YET }))] })] }) }));
|
|
213
|
+
}
|
|
214
|
+
export default SandboxDetailsDialog;
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
* them all — the person's and the agent's, under a header that counts them,
|
|
14
14
|
* scrolled to the newest — rather than the newest line alone.
|
|
15
15
|
*
|
|
16
|
+
* Given suggestions while the agent waits for a question, it offers them as
|
|
17
|
+
* chips under its words: one clicked is sent as the person's prompt.
|
|
18
|
+
*
|
|
16
19
|
* Shown `current` (LOOP T-23), it is the one thing being said or done now:
|
|
17
20
|
* *Now*, then the words as they are written, or the tool being called
|
|
18
21
|
* ("Using **list_invoices**…"), cut to a few lines, and what goes with them
|
|
@@ -23,9 +26,20 @@
|
|
|
23
26
|
* @module chat/assistant/SpeechBalloon
|
|
24
27
|
*/
|
|
25
28
|
import type { JSX, ReactNode } from 'react';
|
|
26
|
-
import { type BalloonApproval
|
|
29
|
+
import { type BalloonApproval } from './state';
|
|
30
|
+
import type { DisplayItem } from '../../types/chat';
|
|
27
31
|
import type { DecisionAsker } from './decisions';
|
|
28
|
-
import type
|
|
32
|
+
import { type BalloonDisplay, type BalloonToolLine } from './toolLine';
|
|
33
|
+
/** A suggestion the balloon offers: its label, and the prompt it sends. */
|
|
34
|
+
export type BalloonSuggestion = {
|
|
35
|
+
label: string;
|
|
36
|
+
prompt: string;
|
|
37
|
+
};
|
|
38
|
+
/** The suggestions, as chips: each sends its prompt. */
|
|
39
|
+
export declare function BalloonSuggestions({ suggestions, onSuggestion, }: {
|
|
40
|
+
suggestions: readonly BalloonSuggestion[];
|
|
41
|
+
onSuggestion: (suggestion: BalloonSuggestion) => void;
|
|
42
|
+
}): JSX.Element;
|
|
29
43
|
export interface SpeechBalloonProps {
|
|
30
44
|
text: string;
|
|
31
45
|
/** There is more than the balloon holds. */
|
|
@@ -64,10 +78,11 @@ export interface SpeechBalloonProps {
|
|
|
64
78
|
/** What goes with the words, under them: a notebook given, read-only. */
|
|
65
79
|
attachment?: ReactNode;
|
|
66
80
|
/**
|
|
67
|
-
* The conversation
|
|
68
|
-
*
|
|
81
|
+
* The conversation (`history`), in the chat's own model — its messages
|
|
82
|
+
* and tool calls, as the chat holds them — listed in the balloon by the
|
|
83
|
+
* chat's own components, in place of the newest line alone.
|
|
69
84
|
*/
|
|
70
|
-
history?: readonly
|
|
85
|
+
history?: readonly DisplayItem[];
|
|
71
86
|
/**
|
|
72
87
|
* What goes with the words is a large visual (a notebook): an *Expand*
|
|
73
88
|
* button under it draws it large, named by `expandTitle`.
|
|
@@ -75,10 +90,43 @@ export interface SpeechBalloonProps {
|
|
|
75
90
|
onExpand?: () => void;
|
|
76
91
|
/** What the large visual is, for the *Expand* button: `Accounting's notebook`. */
|
|
77
92
|
expandTitle?: string;
|
|
93
|
+
/** Draws the listed history large (`history` display): its *Expand*. */
|
|
94
|
+
onExpandHistory?: () => void;
|
|
95
|
+
/**
|
|
96
|
+
* What the person may ask, as chips under the words: one clicked is sent
|
|
97
|
+
* as the prompt (`onSuggestion`), as if typed and asked.
|
|
98
|
+
*/
|
|
99
|
+
suggestions?: readonly BalloonSuggestion[];
|
|
100
|
+
onSuggestion?: (suggestion: BalloonSuggestion) => void;
|
|
101
|
+
/**
|
|
102
|
+
* At work with nothing written yet: the chat's three dots — after the
|
|
103
|
+
* current line, or last in the history — until words arrive.
|
|
104
|
+
*/
|
|
105
|
+
waiting?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* The words uncut, when `text` was cut to fit: *more* shows them whole,
|
|
108
|
+
* in the balloon, *less* folds them back.
|
|
109
|
+
*/
|
|
110
|
+
fullText?: string;
|
|
78
111
|
}
|
|
79
112
|
/** How tall the listed conversation grows before it scrolls, in pixels. */
|
|
80
113
|
export declare const BALLOON_HISTORY_MAX_HEIGHT = 220;
|
|
81
114
|
/** Shorter, when something goes with the words under it (a notebook). */
|
|
82
115
|
export declare const BALLOON_HISTORY_WITH_ATTACHMENT_MAX_HEIGHT = 120;
|
|
83
|
-
|
|
116
|
+
/**
|
|
117
|
+
* The conversation in the `history` balloon: a header that counts it and
|
|
118
|
+
* opens it, then every message, the person's to the right, scrolled to the
|
|
119
|
+
* newest.
|
|
120
|
+
*/
|
|
121
|
+
/**
|
|
122
|
+
* The history balloon, large (*Expand*): every message, then the tool being
|
|
123
|
+
* called and what goes with the words, in the overlay or the page's area,
|
|
124
|
+
* scrolled there.
|
|
125
|
+
*/
|
|
126
|
+
export declare function BalloonHistoryLarge({ history, tool, attachment, }: {
|
|
127
|
+
history: readonly DisplayItem[];
|
|
128
|
+
tool?: BalloonToolLine;
|
|
129
|
+
attachment?: ReactNode;
|
|
130
|
+
}): JSX.Element;
|
|
131
|
+
export declare function SpeechBalloon({ text, more, onOpen, above, side, align, tailAt, approval, decide, onDecisionActive, wide, onDismissPeek, display, tool, speaking, busy, attachment, history, onExpand, expandTitle, suggestions, onSuggestion, waiting, fullText, onExpandHistory, }: SpeechBalloonProps): JSX.Element;
|
|
84
132
|
export default SpeechBalloon;
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { Button, IconButton, Text } from '@primer/react';
|
|
4
4
|
import { Box } from '@datalayer/primer-addons';
|
|
5
5
|
import { XIcon } from '@primer/octicons-react';
|
|
6
|
-
import { ASSISTANT_WORDS
|
|
6
|
+
import { ASSISTANT_WORDS } from './state';
|
|
7
7
|
import { moreWaiting } from './ConversationBalloon';
|
|
8
8
|
import { DecisionAsk } from './DecisionAsk';
|
|
9
|
-
import {
|
|
9
|
+
import { BalloonChatItems, BalloonToolCall, CurrentBalloonBody, ToolLineAnnouncer, conversationHeaderText, } from './BalloonParts';
|
|
10
|
+
import { conversationCount, withToolCall, } from './toolLine';
|
|
10
11
|
import { BalloonExpandButton } from './BalloonVisual';
|
|
12
|
+
import { ScreenFullIcon } from '@primer/octicons-react';
|
|
13
|
+
/** The suggestions, as chips: each sends its prompt. */
|
|
14
|
+
export function BalloonSuggestions({ suggestions, onSuggestion, }) {
|
|
15
|
+
return (_jsx(Box, { role: "group", "aria-label": "Suggestions", "data-balloon-suggestions": "", sx: { display: 'flex', flexWrap: 'wrap', gap: 1, mt: 2 }, children: suggestions.map(suggestion => (_jsx(Button, { size: "small", "data-balloon-suggestion": suggestion.label, "aria-label": `Ask: ${suggestion.prompt}`, title: suggestion.prompt, onClick: () => onSuggestion(suggestion), sx: { borderRadius: 999 }, children: suggestion.label }, suggestion.label))) }));
|
|
16
|
+
}
|
|
11
17
|
/** How tall the listed conversation grows before it scrolls, in pixels. */
|
|
12
18
|
export const BALLOON_HISTORY_MAX_HEIGHT = 220;
|
|
13
19
|
/** Shorter, when something goes with the words under it (a notebook). */
|
|
@@ -17,7 +23,15 @@ export const BALLOON_HISTORY_WITH_ATTACHMENT_MAX_HEIGHT = 120;
|
|
|
17
23
|
* opens it, then every message, the person's to the right, scrolled to the
|
|
18
24
|
* newest.
|
|
19
25
|
*/
|
|
20
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The history balloon, large (*Expand*): every message, then the tool being
|
|
28
|
+
* called and what goes with the words, in the overlay or the page's area,
|
|
29
|
+
* scrolled there.
|
|
30
|
+
*/
|
|
31
|
+
export function BalloonHistoryLarge({ history, tool, attachment, }) {
|
|
32
|
+
return (_jsxs(Box, { role: "log", "aria-label": "Conversation", "data-balloon-history-large": "", sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx(BalloonChatItems, { items: withToolCall(history, tool), density: "comfortable" }), attachment ? _jsx(Box, { children: attachment }) : null] }));
|
|
33
|
+
}
|
|
34
|
+
function BalloonHistory({ history, onOpen, maxHeight = BALLOON_HISTORY_MAX_HEIGHT, onExpand, tool, waiting = false, }) {
|
|
21
35
|
const listRef = useRef(null);
|
|
22
36
|
const newest = history[history.length - 1]?.id;
|
|
23
37
|
useEffect(() => {
|
|
@@ -25,7 +39,21 @@ function BalloonHistory({ history, onOpen, maxHeight = BALLOON_HISTORY_MAX_HEIGH
|
|
|
25
39
|
if (list) {
|
|
26
40
|
list.scrollTop = list.scrollHeight;
|
|
27
41
|
}
|
|
28
|
-
}, [history.length, newest]);
|
|
42
|
+
}, [history.length, newest, waiting, tool?.id, tool?.phase]);
|
|
43
|
+
// The chat's markdown draws after the list does: kept at the newest as
|
|
44
|
+
// what is drawn grows.
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const list = listRef.current;
|
|
47
|
+
const content = list?.firstElementChild;
|
|
48
|
+
if (!list || !content || typeof ResizeObserver === 'undefined') {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const observer = new ResizeObserver(() => {
|
|
52
|
+
list.scrollTop = list.scrollHeight;
|
|
53
|
+
});
|
|
54
|
+
observer.observe(content);
|
|
55
|
+
return () => observer.disconnect();
|
|
56
|
+
}, []);
|
|
29
57
|
return (_jsxs(Box, { "data-balloon-history": "", sx: { flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column' }, children: [_jsx(Box, { as: "button", type: "button", "data-balloon-peek": "", "data-balloon-header": "", onClick: onOpen, sx: {
|
|
30
58
|
m: 0,
|
|
31
59
|
p: 0,
|
|
@@ -39,24 +67,27 @@ function BalloonHistory({ history, onOpen, maxHeight = BALLOON_HISTORY_MAX_HEIGH
|
|
|
39
67
|
fontWeight: 'semibold',
|
|
40
68
|
color: 'fg.muted',
|
|
41
69
|
'&:hover': { textDecoration: 'underline' },
|
|
42
|
-
}, children: conversationHeaderText(history
|
|
70
|
+
}, children: conversationHeaderText(conversationCount(history)) }), onExpand ? (_jsx(IconButton, { icon: ScreenFullIcon, size: "small", variant: "invisible", "aria-label": "Expand the conversation", "data-balloon-history-expand": "", onClick: onExpand, sx: { position: 'absolute', top: '4px', right: '28px' } })) : null, _jsx(Box, { ref: listRef, role: "log", "aria-label": "Conversation", "data-balloon-history-list": "", sx: {
|
|
43
71
|
maxHeight,
|
|
44
72
|
overflowY: 'auto',
|
|
45
73
|
display: 'flex',
|
|
46
74
|
flexDirection: 'column',
|
|
47
|
-
gap: 1,
|
|
48
75
|
pr: 1,
|
|
49
|
-
}, children:
|
|
50
|
-
alignSelf: message.role === 'user' ? 'flex-end' : 'flex-start',
|
|
51
|
-
maxWidth: '88%',
|
|
52
|
-
px: 2,
|
|
53
|
-
py: 1,
|
|
54
|
-
borderRadius: 2,
|
|
55
|
-
bg: message.role === 'user' ? 'accent.subtle' : 'canvas.subtle',
|
|
56
|
-
overflowWrap: 'anywhere',
|
|
57
|
-
}, children: message.text }, message.id))) })] }));
|
|
76
|
+
}, children: _jsx(BalloonChatItems, { items: withToolCall(history, tool), waiting: waiting }) })] }));
|
|
58
77
|
}
|
|
59
|
-
export function SpeechBalloon({ text, more = false, onOpen, above, side = 'above', align, tailAt, approval, decide, onDecisionActive, wide = false, onDismissPeek, display = 'history', tool, speaking = false, busy = false, attachment, history, onExpand, expandTitle, }) {
|
|
78
|
+
export function SpeechBalloon({ text, more = false, onOpen, above, side = 'above', align, tailAt, approval, decide, onDecisionActive, wide = false, onDismissPeek, display = 'history', tool, speaking = false, busy = false, attachment, history, onExpand, expandTitle, suggestions, onSuggestion, waiting = false, fullText, onExpandHistory, }) {
|
|
79
|
+
// *more*: the words whole, when they were cut or overflow their lines.
|
|
80
|
+
const [whole, setWhole] = useState(false);
|
|
81
|
+
const [overflowing, setOverflowing] = useState(false);
|
|
82
|
+
const cut = (!!fullText && fullText !== text) || (!!more && text.endsWith('…'));
|
|
83
|
+
const wholeText = fullText ?? text;
|
|
84
|
+
// Other words, folded again.
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
setWhole(false);
|
|
87
|
+
}, [wholeText.slice(0, 40)]);
|
|
88
|
+
const offered = suggestions && suggestions.length > 0 && onSuggestion && !approval
|
|
89
|
+
? suggestions
|
|
90
|
+
: null;
|
|
60
91
|
const current = display === 'current';
|
|
61
92
|
// History with messages to list: all of them, not the newest line alone.
|
|
62
93
|
const listed = !current && history && history.length > 0 ? history : null;
|
|
@@ -68,8 +99,14 @@ export function SpeechBalloon({ text, more = false, onOpen, above, side = 'above
|
|
|
68
99
|
? { bottom: `${above}px` }
|
|
69
100
|
: { top: `${above}px` }),
|
|
70
101
|
[align]: 0,
|
|
71
|
-
maxWidth: wide || listed || (current && attachment)
|
|
72
|
-
|
|
102
|
+
maxWidth: wide || listed || offered || whole || (current && attachment)
|
|
103
|
+
? 300
|
|
104
|
+
: current
|
|
105
|
+
? 260
|
|
106
|
+
: 280,
|
|
107
|
+
width: wide || listed || offered || whole || (current && attachment)
|
|
108
|
+
? 300
|
|
109
|
+
: 'max-content',
|
|
73
110
|
px: 3,
|
|
74
111
|
py: 2,
|
|
75
112
|
bg: 'canvas.default',
|
|
@@ -106,9 +143,12 @@ export function SpeechBalloon({ text, more = false, onOpen, above, side = 'above
|
|
|
106
143
|
font: 'inherit',
|
|
107
144
|
textAlign: 'left',
|
|
108
145
|
cursor: 'pointer',
|
|
109
|
-
}, children: _jsx(CurrentBalloonBody, { text: text, tool: tool, busy: busy, speaking: speaking }) })) : listed ? (_jsx(BalloonHistory, { history: listed, onOpen: onOpen, maxHeight: attachment
|
|
146
|
+
}, children: _jsx(CurrentBalloonBody, { text: text, tool: tool, busy: busy, speaking: speaking, waiting: waiting, whole: whole, fullText: wholeText, onOverflow: setOverflowing }) })) : listed ? (_jsx(BalloonHistory, { history: listed, onOpen: onOpen, onExpand: onExpandHistory, tool: tool, waiting: waiting, maxHeight: attachment
|
|
110
147
|
? BALLOON_HISTORY_WITH_ATTACHMENT_MAX_HEIGHT
|
|
111
|
-
: BALLOON_HISTORY_MAX_HEIGHT })) :
|
|
148
|
+
: BALLOON_HISTORY_MAX_HEIGHT })) : tool ? (
|
|
149
|
+
// The tool being called: the chat's card, not inside the peek's
|
|
150
|
+
// button (the card is a button of its own).
|
|
151
|
+
_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(BalloonToolCall, { line: tool }) })) : (_jsx(Box, { as: "button", type: "button", "data-balloon-peek": "", "data-balloon-more": more ? '' : undefined, onClick: onOpen, sx: {
|
|
112
152
|
flex: 1,
|
|
113
153
|
minWidth: 0,
|
|
114
154
|
m: 0,
|
|
@@ -120,6 +160,17 @@ export function SpeechBalloon({ text, more = false, onOpen, above, side = 'above
|
|
|
120
160
|
textAlign: 'left',
|
|
121
161
|
cursor: 'pointer',
|
|
122
162
|
'&:hover': { textDecoration: 'underline' },
|
|
123
|
-
}, children:
|
|
163
|
+
}, children: _jsx(Box, { as: "span", role: "status", "aria-live": "polite", children: text }) })), onDismissPeek && !approval ? (_jsx(IconButton, { icon: XIcon, size: "small", variant: "invisible", "aria-label": "Dismiss", "data-balloon-dismiss": "", onClick: onDismissPeek, sx: { mt: '-4px', mr: '-8px', flexShrink: 0 } })) : null] }), current && !approval && !tool && (cut || overflowing || whole) ? (_jsx(Box, { as: "button", type: "button", "aria-expanded": whole, "data-balloon-whole": whole ? 'less' : 'more', onClick: () => setWhole(!whole), sx: {
|
|
164
|
+
m: 0,
|
|
165
|
+
mt: 1,
|
|
166
|
+
p: 0,
|
|
167
|
+
border: 0,
|
|
168
|
+
bg: 'transparent',
|
|
169
|
+
color: 'accent.fg',
|
|
170
|
+
font: 'inherit',
|
|
171
|
+
fontSize: 0,
|
|
172
|
+
cursor: 'pointer',
|
|
173
|
+
'&:hover': { textDecoration: 'underline' },
|
|
174
|
+
}, children: whole ? 'less' : 'more' })) : null, !current && !listed && waiting ? (_jsx(Box, { "data-balloon-waiting": "", sx: { mt: 1 }, children: _jsx(BalloonChatItems, { items: [], waiting: true }) })) : null, approval && (_jsxs(Box, { "data-balloon-approval": approval.id, sx: { mt: 1 }, children: [_jsx(Text, { as: "p", sx: { m: 0, fontWeight: 'semibold' }, children: approval.asks }), approval.why ? (_jsx(Text, { as: "p", sx: { m: 0, color: 'fg.muted', fontSize: 0 }, children: approval.why })) : null, _jsxs(Box, { sx: { display: 'flex', gap: 2, mt: 2 }, children: [_jsx(Button, { size: "small", variant: "primary", disabled: approval.deciding, onClick: approval.onApprove, children: ASSISTANT_WORDS.approve }), _jsx(Button, { size: "small", disabled: approval.deciding, onClick: approval.onDeny, children: ASSISTANT_WORDS.deny })] }), approval.others > 0 ? (_jsx(Text, { as: "p", sx: { m: 0, mt: 1, color: 'fg.muted', fontSize: 0 }, children: moreWaiting(approval.others) })) : null] })), offered && onSuggestion ? (_jsx(BalloonSuggestions, { suggestions: offered, onSuggestion: onSuggestion })) : null, attachment ? (_jsxs(Box, { "data-balloon-attachment": "", sx: { mt: 2, display: 'flex', flexDirection: 'column', gap: 1 }, children: [attachment, onExpand ? (_jsx(BalloonExpandButton, { title: expandTitle ?? 'the visual', onExpand: onExpand })) : null] })) : null, !current ? _jsx(ToolLineAnnouncer, { line: tool }) : null, decide && _jsx(DecisionAsk, { ask: decide, onActiveChange: onDecisionActive })] }));
|
|
124
175
|
}
|
|
125
176
|
export default SpeechBalloon;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the assistant's menu knows of its agent and of its code sandbox, for
|
|
3
|
+
* *Agent Details…* and *Code Sandbox Details…*.
|
|
4
|
+
*
|
|
5
|
+
* Light on purpose: types and pure helpers only. The dialogs that draw them
|
|
6
|
+
* — `AgentDetails`, jupyter-react's `KernelVariables` — are loaded when one
|
|
7
|
+
* is opened, never with the character.
|
|
8
|
+
*
|
|
9
|
+
* @module chat/assistant/assistantDetails
|
|
10
|
+
*/
|
|
11
|
+
import type { Kernel } from '@jupyterlab/services';
|
|
12
|
+
import type { KernelVariablesExecution } from '@datalayer/jupyter-react';
|
|
13
|
+
/** Where a sandbox runs. */
|
|
14
|
+
export type AssistantSandboxKind =
|
|
15
|
+
/** Pyodide, in this page. */
|
|
16
|
+
'browser'
|
|
17
|
+
/** A Jupyter server on this machine, the agent's own. */
|
|
18
|
+
| 'local'
|
|
19
|
+
/** A Jupyter server elsewhere. */
|
|
20
|
+
| 'jupyter'
|
|
21
|
+
/** A Datalayer cloud runtime. */
|
|
22
|
+
| 'cloud'
|
|
23
|
+
/** In the agent runtime's process, or at a provider (`eval`, `e2b`, …). */
|
|
24
|
+
| 'runtime';
|
|
25
|
+
/** A code sandbox, as much of it as its host knows. */
|
|
26
|
+
export type AssistantSandbox = {
|
|
27
|
+
kind: AssistantSandboxKind;
|
|
28
|
+
/** `running`, `starting`, `idle`, `stopped`, `error`. */
|
|
29
|
+
status?: string;
|
|
30
|
+
/** The sandbox manager's variant: `jupyter-server`, `eval`, `datalayer`, a provider. */
|
|
31
|
+
variant?: string;
|
|
32
|
+
/** The Jupyter server, or the base the sandbox is reached at. Shown without its tokens. */
|
|
33
|
+
url?: string;
|
|
34
|
+
/** The token the Jupyter server wants: used to connect, never shown. */
|
|
35
|
+
token?: string;
|
|
36
|
+
/** The kernel the sandbox runs, on that server. */
|
|
37
|
+
kernelId?: string;
|
|
38
|
+
/** The agent whose sandbox it is (a local server's per-agent sandbox). */
|
|
39
|
+
agentId?: string;
|
|
40
|
+
/** The environment it runs in. */
|
|
41
|
+
environment?: string;
|
|
42
|
+
/** A cloud runtime's identity: its uid, never its pod's name. */
|
|
43
|
+
runtimeUid?: string;
|
|
44
|
+
/** When a cloud runtime ends; `null` when it is not metered. */
|
|
45
|
+
expiresAt?: string | number | null;
|
|
46
|
+
/** What a cloud runtime burns, in credits per hour. */
|
|
47
|
+
burningRate?: number;
|
|
48
|
+
startedAt?: string | number;
|
|
49
|
+
lastActivity?: string | number;
|
|
50
|
+
/** The kernel, when the page talks to it: listed live, widgets drawn. */
|
|
51
|
+
connection?: Kernel.IKernelConnection | null;
|
|
52
|
+
/**
|
|
53
|
+
* The agent-runtimes server whose `/api/v1/sandbox/execute` runs code in
|
|
54
|
+
* the agent's sandbox, when the page has no kernel connection to it.
|
|
55
|
+
*/
|
|
56
|
+
serverUrl?: string;
|
|
57
|
+
};
|
|
58
|
+
/** Whether a sandbox is up: one with no status is taken to be. */
|
|
59
|
+
export declare function sandboxIsLive(sandbox: AssistantSandbox): boolean;
|
|
60
|
+
/** What the dialog says of a sandbox that is not running yet. */
|
|
61
|
+
export declare const NO_SANDBOX_YET = "No sandbox is running yet: run a cell or ask the agent to execute code.";
|
|
62
|
+
/** What each kind is called. */
|
|
63
|
+
export declare const SANDBOX_KIND_LABELS: Record<AssistantSandboxKind, string>;
|
|
64
|
+
/** Whether a URL is on this machine. */
|
|
65
|
+
export declare function isLocalUrl(url: string | undefined): boolean;
|
|
66
|
+
/** A URL fit to show: no credentials, no token in its query. */
|
|
67
|
+
export declare function displayUrl(url: string | undefined): string | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* A sandbox from the status its server reports — the Loop's sandbox
|
|
70
|
+
* service, or the agent's codemode status — and what the host adds.
|
|
71
|
+
*/
|
|
72
|
+
export declare function assistantSandboxOf(status: Record<string, unknown> | null | undefined, options?: {
|
|
73
|
+
agentId?: string;
|
|
74
|
+
serverUrl?: string;
|
|
75
|
+
state?: string;
|
|
76
|
+
}): AssistantSandbox | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Run code in an agent's sandbox through its agent-runtimes server
|
|
79
|
+
* (`POST /api/v1/sandbox/execute`), whatever the variant: what a page with
|
|
80
|
+
* no kernel connection to the sandbox lists its variables with.
|
|
81
|
+
*/
|
|
82
|
+
export declare function sandboxExecuteOverHttp(serverUrl: string, agentId?: string): (code: string) => Promise<KernelVariablesExecution>;
|
|
83
|
+
/** How long until a moment, for a reader: `1 h 20 min`, `never`. */
|
|
84
|
+
export declare function timeLeft(expiresAt: string | number | null | undefined, now?: number): string | undefined;
|