@datalayer/agent-runtimes 1.3.75 → 1.3.79
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 +12 -2
- package/lib/chat/ChatFloating.js +48 -11
- package/lib/chat/assistant/AgentDetailsDialog.d.ts +21 -0
- package/lib/chat/assistant/AgentDetailsDialog.js +19 -0
- package/lib/chat/assistant/AssistantStage.d.ts +51 -5
- package/lib/chat/assistant/AssistantStage.js +60 -10
- package/lib/chat/assistant/BalloonParts.d.ts +18 -4
- package/lib/chat/assistant/BalloonParts.js +46 -41
- package/lib/chat/assistant/BalloonVisual.js +2 -1
- package/lib/chat/assistant/SandboxDetailsDialog.d.ts +44 -0
- package/lib/chat/assistant/SandboxDetailsDialog.js +214 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +8 -6
- package/lib/chat/assistant/SpeechBalloon.js +29 -30
- 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 +20 -0
- package/lib/chat/base/loopMessage.d.ts +37 -0
- package/lib/chat/base/loopMessage.js +37 -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 +24 -18
- 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/teams/A2ATeamGraph.d.ts +30 -3
- package/lib/components/teams/A2ATeamGraph.js +264 -41
- package/lib/components/teams/TeamNotebookView.js +15 -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 +10 -6
- package/lib/components/teams/useA2ATeam.js +29 -8
- package/lib/examples/AgentA2ATeamExample.js +6 -0
- package/lib/examples/AssistantExample.js +3 -24
- 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 +21 -3
- 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/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 +53 -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/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 +5 -0
- package/package.json +2 -2
- package/scripts/codegen/generate_ui_plugins.py +112 -2
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
/** What a record entry is. */
|
|
12
12
|
export type RecordKind = 'session' | 'tool_call' | 'decision' | 'check' | 'approval' | 'output' | 'feedback'
|
|
13
13
|
/** A turn of a conversation: what was asked, what it answered. */
|
|
14
|
-
| 'turn'
|
|
14
|
+
| 'turn'
|
|
15
|
+
/** A run began: what Activity reads as in progress until it answers (R-15). */
|
|
16
|
+
| 'run'
|
|
17
|
+
/** What one of its channels was sent, or why not (R-37). */
|
|
18
|
+
| 'notification';
|
|
15
19
|
/** One entry of an application's record. */
|
|
16
20
|
export type RecordEntry = {
|
|
17
21
|
uid: string;
|
|
@@ -36,7 +40,11 @@ export declare const RECORD_KIND_WORDS: Record<RecordKind, string>;
|
|
|
36
40
|
export declare function entryOf(raw: Record<string, unknown>): RecordEntry;
|
|
37
41
|
/** A session in a sentence: how many calls, what stopped it, how it ended. */
|
|
38
42
|
export declare function sessionSentence(entries: readonly RecordEntry[]): string;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
/**
|
|
44
|
+
* The sessions of an application, newest first: their opening entries. Real
|
|
45
|
+
* use only — the sessions its tests ran are read apart (R-07) — unless
|
|
46
|
+
* `tests` says otherwise: `true` for the tests only, `null` for both.
|
|
47
|
+
*/
|
|
48
|
+
export declare function listSessions(context: RecordsContext, appUid: string, limit?: number, tests?: boolean | null): Promise<RecordEntry[]>;
|
|
49
|
+
/** What one session did, in order: a run's start is a mark, not a step (R-15). */
|
|
42
50
|
export declare function readSession(context: RecordsContext, sessionUid: string): Promise<RecordEntry[]>;
|
package/lib/loop/apps/records.js
CHANGED
|
@@ -12,6 +12,8 @@ export const RECORD_KIND_WORDS = {
|
|
|
12
12
|
output: 'Answer',
|
|
13
13
|
feedback: 'Feedback',
|
|
14
14
|
turn: 'Turn',
|
|
15
|
+
run: 'Working',
|
|
16
|
+
notification: 'Notification',
|
|
15
17
|
};
|
|
16
18
|
/** An entry as ai-agents answers with it. */
|
|
17
19
|
export function entryOf(raw) {
|
|
@@ -65,17 +67,23 @@ async function read(context, path, failure) {
|
|
|
65
67
|
}
|
|
66
68
|
return body;
|
|
67
69
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
/**
|
|
71
|
+
* The sessions of an application, newest first: their opening entries. Real
|
|
72
|
+
* use only — the sessions its tests ran are read apart (R-07) — unless
|
|
73
|
+
* `tests` says otherwise: `true` for the tests only, `null` for both.
|
|
74
|
+
*/
|
|
75
|
+
export async function listSessions(context, appUid, limit = 20, tests = false) {
|
|
76
|
+
const body = await read(context, `/sessions?app_uid=${encodeURIComponent(appUid)}&limit=${limit}${tests === null ? '' : `&tests=${tests}`}`, 'The sessions could not be read');
|
|
71
77
|
return Array.isArray(body.sessions)
|
|
72
78
|
? body.sessions.map(entryOf)
|
|
73
79
|
: [];
|
|
74
80
|
}
|
|
75
|
-
/** What one session did, in order. */
|
|
81
|
+
/** What one session did, in order: a run's start is a mark, not a step (R-15). */
|
|
76
82
|
export async function readSession(context, sessionUid) {
|
|
77
83
|
const body = await read(context, `/records?session_uid=${encodeURIComponent(sessionUid)}&limit=500`, 'The session could not be read');
|
|
78
84
|
return Array.isArray(body.entries)
|
|
79
|
-
? body.entries
|
|
85
|
+
? body.entries
|
|
86
|
+
.map(entryOf)
|
|
87
|
+
.filter(entry => entry.kind !== 'run')
|
|
80
88
|
: [];
|
|
81
89
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approve and save (LOOP R-24): what an application wants to keep, shown
|
|
3
|
+
* before it is kept. The runtime asks every `save_to_space` call on the
|
|
4
|
+
* approvals path with the title and the whole text; an approvals card shows
|
|
5
|
+
* them, with *Approve and save* and *Decline* in place of *Approve* and
|
|
6
|
+
* *Reject*.
|
|
7
|
+
*
|
|
8
|
+
* Pure: no React.
|
|
9
|
+
*
|
|
10
|
+
* @module loop/apps/saved
|
|
11
|
+
*/
|
|
12
|
+
/** The runtime's tool that saves a result as a page of a Space. */
|
|
13
|
+
export declare const SAVE_TOOL = "save_to_space";
|
|
14
|
+
/** What an approvals card says of a result to keep. */
|
|
15
|
+
export declare const SAVE_WORDS: {
|
|
16
|
+
readonly approve: "Approve and save";
|
|
17
|
+
readonly decline: "Decline";
|
|
18
|
+
readonly where: (space: string) => string;
|
|
19
|
+
};
|
|
20
|
+
/** The result an approval asks to save: its title, its text, its Space. */
|
|
21
|
+
export type Draft = {
|
|
22
|
+
title: string;
|
|
23
|
+
content: string;
|
|
24
|
+
space: string;
|
|
25
|
+
};
|
|
26
|
+
/** The draft an approval carries, or null for any other approval. */
|
|
27
|
+
export declare function draftOfApproval(approval: {
|
|
28
|
+
tool_name: string;
|
|
29
|
+
tool_args?: Record<string, unknown>;
|
|
30
|
+
}): Draft | null;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Approve and save (LOOP R-24): what an application wants to keep, shown
|
|
7
|
+
* before it is kept. The runtime asks every `save_to_space` call on the
|
|
8
|
+
* approvals path with the title and the whole text; an approvals card shows
|
|
9
|
+
* them, with *Approve and save* and *Decline* in place of *Approve* and
|
|
10
|
+
* *Reject*.
|
|
11
|
+
*
|
|
12
|
+
* Pure: no React.
|
|
13
|
+
*
|
|
14
|
+
* @module loop/apps/saved
|
|
15
|
+
*/
|
|
16
|
+
/** The runtime's tool that saves a result as a page of a Space. */
|
|
17
|
+
export const SAVE_TOOL = 'save_to_space';
|
|
18
|
+
/** What an approvals card says of a result to keep. */
|
|
19
|
+
export const SAVE_WORDS = {
|
|
20
|
+
approve: 'Approve and save',
|
|
21
|
+
decline: 'Decline',
|
|
22
|
+
where: (space) => space
|
|
23
|
+
? `To keep as a page of ${space}:`
|
|
24
|
+
: 'To keep as a page of its Space:',
|
|
25
|
+
};
|
|
26
|
+
/** The draft an approval carries, or null for any other approval. */
|
|
27
|
+
export function draftOfApproval(approval) {
|
|
28
|
+
if (approval.tool_name !== SAVE_TOOL) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const said = (key) => {
|
|
32
|
+
const value = approval.tool_args?.[key];
|
|
33
|
+
return typeof value === 'string' ? value : '';
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
title: said('title'),
|
|
37
|
+
content: said('content'),
|
|
38
|
+
space: said('space'),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -23,6 +23,13 @@ export type AppEmbedProps = {
|
|
|
23
23
|
serverUrl?: string;
|
|
24
24
|
/** What its record is kept under, on Datalayer. */
|
|
25
25
|
instance?: AppInstance;
|
|
26
|
+
/**
|
|
27
|
+
* The embed token the host's server was issued for this visit (LOOP R-20):
|
|
28
|
+
* what the chat speaks to the application's session with on the
|
|
29
|
+
* agent-runtimes server that runs its deployment (`serverUrl`). It runs a
|
|
30
|
+
* session of that deployment, and reaches nothing else.
|
|
31
|
+
*/
|
|
32
|
+
embedToken?: string;
|
|
26
33
|
/** Height of an inline application. */
|
|
27
34
|
height?: number | string;
|
|
28
35
|
/**
|
|
@@ -55,5 +62,5 @@ export declare function EmbedThemed({ accent, colorMode, font, children, style,
|
|
|
55
62
|
children: ReactNode;
|
|
56
63
|
style?: CSSProperties;
|
|
57
64
|
}): JSX.Element;
|
|
58
|
-
export declare function AppEmbed({ app, mode, accent, colorMode, font, serverUrl, instance, height, ownPortal, plugins, }: AppEmbedProps): JSX.Element;
|
|
65
|
+
export declare function AppEmbed({ app, mode, accent, colorMode, font, serverUrl, instance, embedToken, height, ownPortal, plugins, }: AppEmbedProps): JSX.Element;
|
|
59
66
|
export default AppEmbed;
|
|
@@ -72,7 +72,7 @@ function Face({ emoji }) {
|
|
|
72
72
|
* The workspace tells the chrome what the application is doing and what it
|
|
73
73
|
* last said (`onPresence`, `onSaying`).
|
|
74
74
|
*/
|
|
75
|
-
function FloatingApp({ app, view, colorMode, themeOverrides, character, serverUrl, instance, }) {
|
|
75
|
+
function FloatingApp({ app, view, colorMode, themeOverrides, character, serverUrl, instance, embedToken, }) {
|
|
76
76
|
const [presence, setPresence] = useState('idle');
|
|
77
77
|
const [said, setSaid] = useState({ answering: false });
|
|
78
78
|
const welcome = app.interface.welcome || app.description;
|
|
@@ -83,7 +83,7 @@ function FloatingApp({ app, view, colorMode, themeOverrides, character, serverUr
|
|
|
83
83
|
return (_jsx(ChatFloating, { defaultViewMode: view, ...(character ? { assistantCharacter: character } : {}), ...(app.interface.balloon
|
|
84
84
|
? { balloonDisplay: app.interface.balloon }
|
|
85
85
|
: {}), title: app.name, description: welcome, brandIcon: _jsx(Face, { emoji: app.emoji }), buttonIcon: _jsx(Face, { emoji: app.emoji }), buttonTooltip: `Talk to ${app.name}`, colorMode: colorMode, useStore: false, conversation: {
|
|
86
|
-
body: signedOut ? (_jsx(Text, { as: "p", role: "status", sx: { p: 3, m: 0, fontSize: 1 }, children: signedOutSentence(app) })) : (_jsx(AppRenderer, { app: app, target: serverUrl ? 'local' : 'datalayer', ...(serverUrl ? { serverUrl } : {}), instance: instance,
|
|
86
|
+
body: signedOut ? (_jsx(Text, { as: "p", role: "status", sx: { p: 3, m: 0, fontSize: 1 }, children: signedOutSentence(app) })) : (_jsx(AppRenderer, { app: app, target: serverUrl ? 'local' : 'datalayer', ...(serverUrl ? { serverUrl } : {}), ...(serverUrl && embedToken ? { embedToken } : {}), instance: instance,
|
|
87
87
|
// The host's accent and face over the application's own, inside
|
|
88
88
|
// its conversation too, in the embed's mode.
|
|
89
89
|
themeOverrides: themeOverrides, colorMode: colorMode,
|
|
@@ -139,7 +139,7 @@ export function EmbedThemed({ accent, colorMode, font, children, style, }) {
|
|
|
139
139
|
*/
|
|
140
140
|
_jsx("div", { "data-color-mode": colorMode, style: { display: 'contents' }, children: _jsx(DatalayerThemeProvider, { colorMode: colorMode, theme: loopTheme, themeStyles: themeStyles, baseStyles: style, children: children }) }));
|
|
141
141
|
}
|
|
142
|
-
export function AppEmbed({ app, mode, accent, colorMode = 'auto', font, serverUrl, instance, height = 640, ownPortal = false, plugins = NO_PLUGINS, }) {
|
|
142
|
+
export function AppEmbed({ app, mode, accent, colorMode = 'auto', font, serverUrl, instance, embedToken, height = 640, ownPortal = false, plugins = NO_PLUGINS, }) {
|
|
143
143
|
const system = useSystemMode();
|
|
144
144
|
const resolvedMode = colorMode === 'auto' ? system : colorMode;
|
|
145
145
|
const shownAs = mode ?? app.deployment.embedded?.mode ?? 'inline';
|
|
@@ -157,7 +157,7 @@ export function AppEmbed({ app, mode, accent, colorMode = 'auto', font, serverUr
|
|
|
157
157
|
return (_jsxs(EmbedThemed, { accent: worn, colorMode: resolvedMode, font: font, style: view
|
|
158
158
|
? // Floating: nothing in the page's flow, nothing painted behind.
|
|
159
159
|
{ backgroundColor: 'transparent' }
|
|
160
|
-
: { height, display: 'flex', flexDirection: 'column' }, children: [ownPortal ? _jsx(OwnPortalRoot, {}) : null, view ? (_jsx(FloatingApp, { app: app, view: view, serverUrl: serverUrl, instance: instance, colorMode: resolvedMode, themeOverrides: themeOverrides, character: character })) : (_jsx(AppRenderer, { app: app, target: serverUrl ? 'local' : 'datalayer', ...(serverUrl ? { serverUrl } : {}), instance: instance,
|
|
160
|
+
: { height, display: 'flex', flexDirection: 'column' }, children: [ownPortal ? _jsx(OwnPortalRoot, {}) : null, view ? (_jsx(FloatingApp, { app: app, view: view, serverUrl: serverUrl, instance: instance, embedToken: embedToken, colorMode: resolvedMode, themeOverrides: themeOverrides, character: character })) : (_jsx(AppRenderer, { app: app, target: serverUrl ? 'local' : 'datalayer', ...(serverUrl ? { serverUrl } : {}), ...(serverUrl && embedToken ? { embedToken } : {}), instance: instance,
|
|
161
161
|
// The host's accent and face over the application's own, inside
|
|
162
162
|
// its conversation too, in the embed's mode: the host's or its
|
|
163
163
|
// visitor's system's, not a Datalayer setting.
|
|
@@ -48,6 +48,11 @@ export type LoopEmbedProps = LoopPresetOptions & {
|
|
|
48
48
|
* which is what a full-page host wants.
|
|
49
49
|
*/
|
|
50
50
|
frameHeight?: number | string;
|
|
51
|
+
/**
|
|
52
|
+
* The sidebar as a rail of line icons, one panel shown at a time (LOOP
|
|
53
|
+
* T-07). See `LoopWorkspaceProps.sidebarRail`.
|
|
54
|
+
*/
|
|
55
|
+
sidebarRail?: boolean;
|
|
51
56
|
/**
|
|
52
57
|
* The host's own plugins, mounted alongside the preset's.
|
|
53
58
|
*
|
|
@@ -58,5 +63,5 @@ export type LoopEmbedProps = LoopPresetOptions & {
|
|
|
58
63
|
*/
|
|
59
64
|
plugins?: PluginRef[];
|
|
60
65
|
};
|
|
61
|
-
export declare function LoopEmbed({ agentId, showHeader, chatHeaderActions, headerActions, headerContainer, frameTitle, frameHeight, plugins, ...preset }: LoopEmbedProps): React.JSX.Element;
|
|
66
|
+
export declare function LoopEmbed({ agentId, showHeader, chatHeaderActions, headerActions, headerContainer, frameTitle, frameHeight, sidebarRail, plugins, ...preset }: LoopEmbedProps): React.JSX.Element;
|
|
62
67
|
export default LoopEmbed;
|
|
@@ -30,7 +30,7 @@ import { internalQueryClient } from '../../utils';
|
|
|
30
30
|
import { buildLoopReactor, LoopWorkspace } from '../shell';
|
|
31
31
|
import { loopPlugins } from '../presets';
|
|
32
32
|
import { WindowFrame } from '../plugins/window-frame';
|
|
33
|
-
export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions, headerActions, headerContainer, frameTitle, frameHeight, plugins = [], ...preset }) {
|
|
33
|
+
export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions, headerActions, headerContainer, frameTitle, frameHeight, sidebarRail = false, plugins = [], ...preset }) {
|
|
34
34
|
/*
|
|
35
35
|
* The latest `firstPromptHook`, kept in a ref rather than closed over
|
|
36
36
|
* directly.
|
|
@@ -77,6 +77,8 @@ export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions,
|
|
|
77
77
|
// By its content: an application's spec written inline is a new object each render.
|
|
78
78
|
JSON.stringify(preset.datalayerCreatePayload ?? null),
|
|
79
79
|
JSON.stringify(preset.datalayerVisitors ?? null),
|
|
80
|
+
preset.datalayerKept?.url,
|
|
81
|
+
preset.embedToken,
|
|
80
82
|
preset.defaultEditor,
|
|
81
83
|
preset.showViewSelector,
|
|
82
84
|
preset.hideChatHeader,
|
|
@@ -135,7 +137,7 @@ export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions,
|
|
|
135
137
|
* view and the workspace draws "No view is available yet".
|
|
136
138
|
*/
|
|
137
139
|
useReactor(reactor);
|
|
138
|
-
const shell = (_jsx(LoopWorkspace, { serverUrl: preset.serverUrl ?? '', agentId: agentId, reactor: reactor, manageReactor: false, showViewSelector: preset.showViewSelector, showHeader: showHeader, chatHeaderActions: chatHeaderActions, headerActions: headerActions, headerContainer: headerContainer }));
|
|
140
|
+
const shell = (_jsx(LoopWorkspace, { serverUrl: preset.serverUrl ?? '', agentId: agentId, reactor: reactor, manageReactor: false, showViewSelector: preset.showViewSelector, showHeader: showHeader, chatHeaderActions: chatHeaderActions, headerActions: headerActions, headerContainer: headerContainer, sidebarRail: sidebarRail }));
|
|
139
141
|
return (_jsx(QueryClientProvider, { client: internalQueryClient, children: frameTitle !== undefined ? (_jsx(WindowFrame, { title: frameTitle, height: frameHeight, children: shell })) : (
|
|
140
142
|
// Filling its host, whatever the host is: in a flex row it used to
|
|
141
143
|
// shrink to the chat's own width and leave the rest of the frame bare.
|
|
@@ -378,7 +378,7 @@ export function defineDatalayerAppElement(options = {}) {
|
|
|
378
378
|
shadow.appendChild(mount);
|
|
379
379
|
this.root = createRoot(mount);
|
|
380
380
|
const server = this.getAttribute('server') || undefined;
|
|
381
|
-
this.root.render(_jsx(StyleSheetManager, { target: shadow, children: _jsx(AppEmbed, { app: source.app, mode: look.mode, accent: look.accent, colorMode: look.colorMode, font: look.font, serverUrl: server, height: inlineHeightOf(this.getAttribute('height')), ownPortal: true }) }));
|
|
381
|
+
this.root.render(_jsx(StyleSheetManager, { target: shadow, children: _jsx(AppEmbed, { app: source.app, mode: look.mode, accent: look.accent, colorMode: look.colorMode, font: look.font, serverUrl: server, ...(server && this.token ? { embedToken: this.token } : {}), height: inlineHeightOf(this.getAttribute('height')), ownPortal: true }) }));
|
|
382
382
|
}
|
|
383
383
|
onMessage(event) {
|
|
384
384
|
if (!this.frame || event.source !== this.frame.contentWindow) {
|
|
@@ -25,6 +25,11 @@ export function DatalayerAgentBridge() {
|
|
|
25
25
|
// A conversation without an account (LOOP R-30): the agent is already on
|
|
26
26
|
// the visitors' runtime, kept warm there; nothing is allocated or made.
|
|
27
27
|
const visitors = agentsConfig?.datalayerVisitors;
|
|
28
|
+
// A deployment kept on a runtime of its own (LOOP R-33): its agent was made
|
|
29
|
+
// there from the deployment record as the runtime started; nothing is
|
|
30
|
+
// allocated or made either.
|
|
31
|
+
const kept = agentsConfig?.datalayerKept;
|
|
32
|
+
const already = visitors?.url ?? kept?.url;
|
|
28
33
|
const service = useOptionalSandboxService();
|
|
29
34
|
const target = useSignalValue(service?.target ?? IDLE_SANDBOX_TARGET_SIGNAL);
|
|
30
35
|
const onDatalayer = target === 'datalayer';
|
|
@@ -68,19 +73,20 @@ export function DatalayerAgentBridge() {
|
|
|
68
73
|
agentSpecId,
|
|
69
74
|
agentConfig,
|
|
70
75
|
variant: 'cloud-pydanticai',
|
|
71
|
-
autoStart: onDatalayer && !
|
|
72
|
-
autoCreateAgent: onDatalayer && !
|
|
76
|
+
autoStart: onDatalayer && !already,
|
|
77
|
+
autoCreateAgent: onDatalayer && !already,
|
|
73
78
|
});
|
|
74
79
|
useEffect(() => {
|
|
75
80
|
if (!service || !onDatalayer) {
|
|
76
81
|
return;
|
|
77
82
|
}
|
|
78
|
-
if (
|
|
83
|
+
if (already) {
|
|
79
84
|
service.report({
|
|
80
85
|
variant: 'datalayer',
|
|
81
|
-
// No sandbox:
|
|
86
|
+
// No sandbox reported: the agent is already on its runtime, and the
|
|
87
|
+
// chat is all the page talks to.
|
|
82
88
|
sandbox_running: false,
|
|
83
|
-
agent_base_url:
|
|
89
|
+
agent_base_url: already,
|
|
84
90
|
});
|
|
85
91
|
return;
|
|
86
92
|
}
|
|
@@ -118,7 +124,7 @@ export function DatalayerAgentBridge() {
|
|
|
118
124
|
// host's server for an agent that is not on it.
|
|
119
125
|
agent_base_url: runtime.agentBaseUrl,
|
|
120
126
|
});
|
|
121
|
-
}, [service, onDatalayer, runtime, status, error,
|
|
127
|
+
}, [service, onDatalayer, runtime, status, error, already]);
|
|
122
128
|
return null;
|
|
123
129
|
}
|
|
124
130
|
export default DatalayerAgentBridge;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ServiceManager } from '@jupyterlab/services';
|
|
1
|
+
import type { Kernel, ServiceManager } from '@jupyterlab/services';
|
|
2
2
|
import type { SandboxService } from './service';
|
|
3
3
|
/** What a browser sandbox additionally exposes. */
|
|
4
4
|
export type BrowserSandboxService = SandboxService & {
|
|
@@ -13,6 +13,12 @@ export type BrowserSandboxService = SandboxService & {
|
|
|
13
13
|
* point of a shared sandbox.
|
|
14
14
|
*/
|
|
15
15
|
getServiceManager: () => ServiceManager.IManager | null;
|
|
16
|
+
/**
|
|
17
|
+
* The kernel's connection, once it is up: what a view that follows the
|
|
18
|
+
* kernel itself listens to — its variables (jupyter-react's
|
|
19
|
+
* `KernelVariables`), refreshed after each execution.
|
|
20
|
+
*/
|
|
21
|
+
getKernelConnection: () => Kernel.IKernelConnection | null;
|
|
16
22
|
};
|
|
17
23
|
/**
|
|
18
24
|
* How a kernel-backed sandbox gets its services.
|
|
@@ -126,6 +126,7 @@ export function createBrowserSandboxService(source = browserSource()) {
|
|
|
126
126
|
const ready = computed(() => lifecycle.value === 'running');
|
|
127
127
|
let serviceManager = null;
|
|
128
128
|
let kernel = null;
|
|
129
|
+
let kernelConnection = null;
|
|
129
130
|
let starting = null;
|
|
130
131
|
async function start() {
|
|
131
132
|
if (kernel) {
|
|
@@ -139,6 +140,7 @@ export function createBrowserSandboxService(source = browserSource()) {
|
|
|
139
140
|
type: 'notebook',
|
|
140
141
|
kernel: { name: 'python' },
|
|
141
142
|
});
|
|
143
|
+
kernelConnection = connection.kernel;
|
|
142
144
|
kernel = connection.kernel;
|
|
143
145
|
status.value = {
|
|
144
146
|
variant: source.variant,
|
|
@@ -158,6 +160,7 @@ export function createBrowserSandboxService(source = browserSource()) {
|
|
|
158
160
|
// lives on the server — should ask `kind` rather than discover it here.
|
|
159
161
|
serverUrl: '',
|
|
160
162
|
getServiceManager: () => serviceManager,
|
|
163
|
+
getKernelConnection: () => kernelConnection,
|
|
161
164
|
report(next) {
|
|
162
165
|
status.value = next;
|
|
163
166
|
},
|
|
@@ -182,6 +185,7 @@ export function createBrowserSandboxService(source = browserSource()) {
|
|
|
182
185
|
// notebook that is open.
|
|
183
186
|
void kernel?.shutdown?.();
|
|
184
187
|
kernel = null;
|
|
188
|
+
kernelConnection = null;
|
|
185
189
|
serviceManager = source.owned ? null : serviceManager;
|
|
186
190
|
// And hand back whatever the source borrowed from the page.
|
|
187
191
|
source.release?.();
|
|
@@ -103,6 +103,22 @@ export type AgentsConfig = {
|
|
|
103
103
|
* and the chat speaks to it with a visitor's token for the application.
|
|
104
104
|
*/
|
|
105
105
|
datalayerVisitors?: DatalayerVisitors;
|
|
106
|
+
/**
|
|
107
|
+
* A deployment kept on a runtime of its own (LOOP R-33): its agent is
|
|
108
|
+
* already there, made from the deployment record when the runtime started,
|
|
109
|
+
* so nothing is allocated or created, and the chat speaks to it with the
|
|
110
|
+
* person's own token. `url` is the runtime's agent-runtimes address.
|
|
111
|
+
*/
|
|
112
|
+
datalayerKept?: {
|
|
113
|
+
url: string;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* An embed token for the application (LOOP R-20): what an embedded
|
|
117
|
+
* application's chat speaks to its session with, for a visitor the platform
|
|
118
|
+
* does not know — it runs a session of the application's deployment, and
|
|
119
|
+
* reaches nothing else.
|
|
120
|
+
*/
|
|
121
|
+
embedToken?: string;
|
|
106
122
|
/**
|
|
107
123
|
* The target is the host's decision, not the reader's: started there even
|
|
108
124
|
* with the control hidden. For an application that runs where it is
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* @module loop/plugins/agents/switchable
|
|
19
19
|
*/
|
|
20
20
|
import { type ReadonlySignal } from '@datalayer/reactor';
|
|
21
|
+
import type { Kernel } from '@jupyterlab/services';
|
|
21
22
|
import { type ServiceManagerSource } from './browserService';
|
|
22
23
|
import { type SandboxService } from './service';
|
|
23
24
|
/** Where the sandbox runs. */
|
|
@@ -72,6 +73,8 @@ export type SwitchableSandboxService = SandboxService & {
|
|
|
72
73
|
setTarget: (target: SandboxTarget) => Promise<void>;
|
|
73
74
|
/** The in-browser services, when the browser is the active target. */
|
|
74
75
|
getServiceManager: () => unknown | null;
|
|
76
|
+
/** The in-browser kernel's connection, when the browser is the active target. */
|
|
77
|
+
getKernelConnection: () => Kernel.IKernelConnection | null;
|
|
75
78
|
};
|
|
76
79
|
export type SwitchableConfig = {
|
|
77
80
|
serverUrl: string;
|
|
@@ -239,6 +239,7 @@ export function createSwitchableSandboxService({ serverUrl, initialTarget = 'loc
|
|
|
239
239
|
lastExecution,
|
|
240
240
|
serverUrl,
|
|
241
241
|
getServiceManager: () => target.peek() === 'browser' ? browser.getServiceManager() : null,
|
|
242
|
+
getKernelConnection: () => target.peek() === 'browser' ? browser.getKernelConnection() : null,
|
|
242
243
|
report(next) {
|
|
243
244
|
active.peek().report(next);
|
|
244
245
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { definePlugin } from '@datalayer/reactor';
|
|
3
|
+
import { PulseIcon } from '@primer/octicons-react';
|
|
3
4
|
import { LoopSlots } from '../../core';
|
|
4
5
|
import { AppActivity } from './AppActivity';
|
|
5
6
|
export const APP_ACTIVITY_PLUGIN_NAME = '@datalayer/loop-plugin-app-activity';
|
|
@@ -25,6 +26,8 @@ export function defineAppActivityPlugin(app, appUid) {
|
|
|
25
26
|
slot: LoopSlots.sidebar,
|
|
26
27
|
order: 20,
|
|
27
28
|
Component: Feed,
|
|
29
|
+
// Its line icon on the workspace's rail (T-07).
|
|
30
|
+
rail: { label: 'Activity', icon: PulseIcon },
|
|
28
31
|
},
|
|
29
32
|
],
|
|
30
33
|
}),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { definePlugin } from '@datalayer/reactor';
|
|
3
|
+
import { DeviceDesktopIcon } from '@primer/octicons-react';
|
|
3
4
|
import { LoopSlots } from '../../core';
|
|
4
5
|
import { AppComputer } from './AppComputer';
|
|
5
6
|
export const APP_COMPUTER_PLUGIN_NAME = '@datalayer/loop-plugin-app-computer';
|
|
@@ -21,6 +22,8 @@ export function defineAppComputerPlugin(app) {
|
|
|
21
22
|
slot: LoopSlots.sidebar,
|
|
22
23
|
order: 30,
|
|
23
24
|
Component: Computer,
|
|
25
|
+
// Its line icon on the workspace's rail (T-07).
|
|
26
|
+
rail: { label: 'Computer', icon: DeviceDesktopIcon },
|
|
24
27
|
},
|
|
25
28
|
],
|
|
26
29
|
}),
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { JSX } from 'react';
|
|
14
14
|
import type { AppRuleSpec, AppSpec } from '../../../types/agentspecs';
|
|
15
|
+
import { type Draft } from '../../apps/saved';
|
|
15
16
|
import { type ApprovalRecord } from '../../../hooks/useToolApprovals';
|
|
16
17
|
/** What the card says. */
|
|
17
18
|
export declare const APP_RULES_WORDS: {
|
|
@@ -45,6 +46,10 @@ export declare const ruleOfApproval: (approval: ApprovalRecord) => string;
|
|
|
45
46
|
* person's pages over the same `/ws`.
|
|
46
47
|
*/
|
|
47
48
|
export declare function approvalsOfApp(approvals: ApprovalRecord[], appId: string): ApprovalRecord[];
|
|
49
|
+
/** A result it wants to keep, shown whole before it is (LOOP R-24). */
|
|
50
|
+
export declare function DraftShown({ draft }: {
|
|
51
|
+
draft: Draft;
|
|
52
|
+
}): JSX.Element;
|
|
48
53
|
export declare function AppRulesCard({ app }: {
|
|
49
54
|
app: AppSpec;
|
|
50
55
|
}): JSX.Element;
|
|
@@ -4,6 +4,7 @@ import { Button, Heading, Label, Text } from '@primer/react';
|
|
|
4
4
|
import { Box } from '@datalayer/primer-addons';
|
|
5
5
|
import { useIAMStore } from '@datalayer/core/lib/state/substates/IAMState';
|
|
6
6
|
import { BEHAVIOUR_WORDS, coverOf } from '../../apps/rules';
|
|
7
|
+
import { SAVE_WORDS, draftOfApproval } from '../../apps/saved';
|
|
7
8
|
import { useApproveToolRequest, useRejectToolRequest, useToolApprovalsQuery, } from '../../../hooks/useToolApprovals';
|
|
8
9
|
/** What the card says. */
|
|
9
10
|
export const APP_RULES_WORDS = {
|
|
@@ -46,17 +47,35 @@ export function approvalsOfApp(approvals, appId) {
|
|
|
46
47
|
return approvals.filter(approval => approval.agent_id === appId || approval.tool_args?._app === appId);
|
|
47
48
|
}
|
|
48
49
|
const PENDING = { status: 'pending' };
|
|
50
|
+
/** A result it wants to keep, shown whole before it is (LOOP R-24). */
|
|
51
|
+
export function DraftShown({ draft }) {
|
|
52
|
+
return (_jsxs(Box, { "data-testid": "app-draft", sx: { mt: 1 }, children: [_jsx(Text, { as: "p", sx: { m: 0, fontSize: 0, color: 'fg.muted' }, children: SAVE_WORDS.where(draft.space) }), _jsx(Text, { as: "p", sx: { m: 0, fontWeight: 'semibold' }, children: draft.title }), _jsx(Box, { as: "pre", sx: {
|
|
53
|
+
m: 0,
|
|
54
|
+
mt: 1,
|
|
55
|
+
p: 2,
|
|
56
|
+
maxHeight: 240,
|
|
57
|
+
overflow: 'auto',
|
|
58
|
+
whiteSpace: 'pre-wrap',
|
|
59
|
+
fontFamily: 'inherit',
|
|
60
|
+
fontSize: 0,
|
|
61
|
+
bg: 'canvas.subtle',
|
|
62
|
+
borderRadius: 2,
|
|
63
|
+
}, children: draft.content })] }));
|
|
64
|
+
}
|
|
49
65
|
function Approvals({ app }) {
|
|
50
66
|
const query = useToolApprovalsQuery(PENDING);
|
|
51
67
|
const approve = useApproveToolRequest();
|
|
52
68
|
const reject = useRejectToolRequest();
|
|
53
69
|
const waiting = useMemo(() => approvalsOfApp(query.data?.approvals ?? [], app.id), [query.data, app.id]);
|
|
54
|
-
return (_jsxs(Box, { "data-testid": "app-approvals", children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 1 }, children: APP_RULES_WORDS.waiting }), approve.connectionState !== 'connected' ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0, mb: 1 }, children: APP_RULES_WORDS.offline })) : null, waiting.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: APP_RULES_WORDS.nothingWaiting })) : (_jsx(Box, { as: "ul", sx: { listStyle: 'none', p: 0, m: 0 }, children: waiting.map(approval =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
70
|
+
return (_jsxs(Box, { "data-testid": "app-approvals", children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 1 }, children: APP_RULES_WORDS.waiting }), approve.connectionState !== 'connected' ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0, mb: 1 }, children: APP_RULES_WORDS.offline })) : null, waiting.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: APP_RULES_WORDS.nothingWaiting })) : (_jsx(Box, { as: "ul", sx: { listStyle: 'none', p: 0, m: 0 }, children: waiting.map(approval => {
|
|
71
|
+
const draft = draftOfApproval(approval);
|
|
72
|
+
return (_jsxs(Box, { as: "li", sx: {
|
|
73
|
+
py: 2,
|
|
74
|
+
borderTop: '1px solid',
|
|
75
|
+
borderColor: 'border.muted',
|
|
76
|
+
fontSize: 1,
|
|
77
|
+
}, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: approval.tool_name }), ruleOfApproval(approval) ? (_jsx(Text, { as: "p", sx: { m: 0, color: 'fg.muted', fontSize: 0 }, children: ruleOfApproval(approval) })) : null, draft ? _jsx(DraftShown, { draft: draft }) : null, _jsxs(Box, { sx: { display: 'flex', gap: 2, mt: 1 }, children: [_jsx(Button, { size: "small", variant: "primary", disabled: approve.isPending, onClick: () => approve.mutate({ id: approval.id }), children: draft ? SAVE_WORDS.approve : APP_RULES_WORDS.approve }), _jsx(Button, { size: "small", disabled: reject.isPending, onClick: () => reject.mutate({ id: approval.id }), children: draft ? SAVE_WORDS.decline : APP_RULES_WORDS.reject })] })] }, approval.id));
|
|
78
|
+
}) }))] }));
|
|
60
79
|
}
|
|
61
80
|
export function AppRulesCard({ app }) {
|
|
62
81
|
const signedIn = Boolean(useIAMStore(state => state.token));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { definePlugin } from '@datalayer/reactor';
|
|
3
|
+
import { ShieldCheckIcon } from '@primer/octicons-react';
|
|
3
4
|
import { LoopSlots } from '../../core';
|
|
4
5
|
import { AppRulesCard } from './AppRulesCard';
|
|
5
6
|
export const APP_RULES_PLUGIN_NAME = '@datalayer/loop-plugin-app-rules';
|
|
@@ -21,6 +22,8 @@ export function defineAppRulesPlugin(app) {
|
|
|
21
22
|
slot: LoopSlots.sidebar,
|
|
22
23
|
order: 10,
|
|
23
24
|
Component: Card,
|
|
25
|
+
// Its line icon on the workspace's rail (T-07).
|
|
26
|
+
rail: { label: 'Rules', icon: ShieldCheckIcon },
|
|
24
27
|
},
|
|
25
28
|
],
|
|
26
29
|
}),
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
import type { JSX } from 'react';
|
|
29
29
|
import { type AssistantState } from '../../../chat/assistant/state';
|
|
30
30
|
import type { PresenceState } from '../../../chat/presence/presenceStatus';
|
|
31
|
-
import { type ChatTurnSnapshot } from '../../core';
|
|
31
|
+
import { type ChatTurnSnapshot, type LoopWorkspaceContext } from '../../core';
|
|
32
32
|
import type { BalloonDisplay } from '../../../chat/assistant/toolLine';
|
|
33
33
|
export declare const LOOP_ASSISTANT_PLUGIN_NAME = "@datalayer/loop-plugin-assistant";
|
|
34
34
|
export type LoopAssistantConfig = {
|
|
@@ -58,6 +58,11 @@ export type LoopAssistantConfig = {
|
|
|
58
58
|
* (`current`). The Appspec's `interface.balloon`.
|
|
59
59
|
*/
|
|
60
60
|
balloon?: BalloonDisplay;
|
|
61
|
+
/**
|
|
62
|
+
* The workspace it is drawn over (the root slot's): its agent and
|
|
63
|
+
* server, for *Agent Details…* and *Code Sandbox Details…*.
|
|
64
|
+
*/
|
|
65
|
+
workspace?: LoopWorkspaceContext;
|
|
61
66
|
};
|
|
62
67
|
/**
|
|
63
68
|
* What the character acts out (T-22), from what the application is doing as
|
|
@@ -68,6 +73,6 @@ export declare function assistantStateOfTurn(turn: ChatTurnSnapshot, presence: P
|
|
|
68
73
|
arriving?: boolean;
|
|
69
74
|
leaving?: boolean;
|
|
70
75
|
}): AssistantState;
|
|
71
|
-
export declare function LoopAssistant({ app, person, appId, decisions, balloon: displayGiven, }: LoopAssistantConfig): JSX.Element | null;
|
|
76
|
+
export declare function LoopAssistant({ app, person, appId, decisions, balloon: displayGiven, workspace, }: LoopAssistantConfig): JSX.Element | null;
|
|
72
77
|
export declare const LoopAssistantPlugin: import("@datalayer/reactor").ReactorPlugin<LoopAssistantConfig, unknown, unknown>;
|
|
73
78
|
export default LoopAssistantPlugin;
|