@datalayer/agent-runtimes 1.3.59 → 1.3.61
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/chat/ChatFloating.d.ts +11 -1
- package/lib/chat/ChatFloating.js +142 -23
- package/lib/chat/assistant/AssistantStage.d.ts +13 -5
- package/lib/chat/assistant/AssistantStage.js +5 -3
- package/lib/chat/assistant/ConversationBalloon.d.ts +71 -0
- package/lib/chat/assistant/ConversationBalloon.js +102 -0
- package/lib/chat/assistant/DecisionAsk.d.ts +31 -0
- package/lib/chat/assistant/DecisionAsk.js +96 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +17 -4
- package/lib/chat/assistant/SpeechBalloon.js +20 -14
- package/lib/chat/assistant/decisions.d.ts +104 -0
- package/lib/chat/assistant/decisions.js +161 -0
- package/lib/chat/assistant/state.d.ts +1 -1
- package/lib/chat/assistant/state.js +1 -1
- package/lib/chat/base/ChatBase.js +22 -4
- package/lib/chat/marks/toolMarks.js +2 -2
- package/lib/config/AgentConfiguration.d.ts +2 -1
- package/lib/config/AgentConfiguration.js +2 -2
- package/lib/examples/AgentDecideExample.d.ts +24 -0
- package/lib/examples/AgentDecideExample.js +73 -0
- package/lib/examples/AgentNotificationsExample.js +1 -1
- package/lib/examples/AgentSkillsExample.js +1 -1
- package/lib/examples/AgentspecsExample.js +3 -3
- package/lib/examples/ChatAssistantExample.js +12 -1
- package/lib/examples/example-selector.js +1 -0
- package/lib/examples/index.d.ts +1 -0
- package/lib/examples/index.js +1 -0
- package/lib/examples/main.js +3 -0
- package/lib/loop/apps/appspec.js +4 -4
- package/lib/loop/apps/checks.js +6 -6
- package/lib/loop/apps/rules.js +2 -2
- package/lib/loop/plugins/assistant/index.d.ts +14 -2
- package/lib/loop/plugins/assistant/index.js +22 -5
- package/lib/specs/actions.d.ts +1 -1
- package/lib/specs/actions.js +1 -1
- package/lib/specs/agents/agents.js +197 -192
- package/lib/specs/apps.js +13 -13
- package/lib/specs/appspecSchema.js +3 -3
- package/lib/specs/backendTools.d.ts +31 -0
- package/lib/specs/{tools.js → backendTools.js} +48 -48
- package/lib/specs/cogs.js +8 -7
- package/lib/specs/frames.js +5 -5
- package/lib/specs/index.d.ts +1 -1
- package/lib/specs/index.js +1 -1
- package/lib/stores/agentRuntimeStore.js +2 -2
- package/lib/stories/loop/ReferenceScreens.d.ts +6 -2
- package/lib/stories/loop/ReferenceScreens.js +63 -6
- package/lib/types/agentspecs.d.ts +7 -5
- package/lib/types/chat.d.ts +12 -0
- package/lib/types/config.d.ts +2 -2
- package/lib/types/tools.d.ts +3 -3
- package/package.json +1 -1
- package/scripts/codegen/generate_agents.py +23 -12
- package/scripts/codegen/generate_apps.py +3 -2
- package/scripts/codegen/{generate_tools.py → generate_backend_tools.py} +29 -29
- package/scripts/codegen/generate_cogs.py +2 -2
- package/scripts/codegen/generate_frames.py +2 -2
- package/lib/specs/tools.d.ts +0 -31
|
@@ -28,6 +28,8 @@ import { InputPrompt } from '../../chat/prompt/InputPrompt';
|
|
|
28
28
|
import { PresenceFace, PresenceLine } from '../../chat/presence/Presence';
|
|
29
29
|
import { AssistantStage, } from '../../chat/assistant/AssistantStage';
|
|
30
30
|
import { ASSISTANT_WORDS, } from '../../chat/assistant/state';
|
|
31
|
+
import { CONVERSATION_BALLOON_WIDTH, ConversationBalloonClose, balloonTailAt, conversationBalloonHeight, conversationBalloonSx, peekLine, } from '../../chat/assistant/ConversationBalloon';
|
|
32
|
+
import { DecisionAsk } from '../../chat/assistant/DecisionAsk';
|
|
31
33
|
import { buildReactorFromPlugins } from '@datalayer/reactor';
|
|
32
34
|
import { AssistantCharactersPlugin, assistantCharacterNamed, } from '../../loop/plugins/assistant-characters';
|
|
33
35
|
import { OwlCharacterPlugin } from '../../examples/utils/owlCharacterPlugin';
|
|
@@ -39,7 +41,11 @@ export const REFERENCE_SCREENS = [
|
|
|
39
41
|
'approval',
|
|
40
42
|
'tool-marks',
|
|
41
43
|
];
|
|
42
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* The floating assistant's pictures (T-27): its states — closed, a peek
|
|
46
|
+
* where it has something to say — stepped aside, and open, the conversation
|
|
47
|
+
* as its balloon (T-23).
|
|
48
|
+
*/
|
|
43
49
|
export const ASSISTANT_PICTURES = [
|
|
44
50
|
'idle',
|
|
45
51
|
'thinking',
|
|
@@ -48,6 +54,7 @@ export const ASSISTANT_PICTURES = [
|
|
|
48
54
|
'paused',
|
|
49
55
|
'speaking',
|
|
50
56
|
'aside',
|
|
57
|
+
'open',
|
|
51
58
|
];
|
|
52
59
|
/**
|
|
53
60
|
* The characters pictured idle, each in light and dark (T-25, T-24):
|
|
@@ -232,9 +239,10 @@ export const REFERENCE_SCREEN_COMPONENTS = {
|
|
|
232
239
|
// The floating assistant (T-27)
|
|
233
240
|
// ---------------------------------------------------------------------------
|
|
234
241
|
const BALLOONS = {
|
|
242
|
+
// A new message, peeked by its first words (T-23).
|
|
235
243
|
speaking: {
|
|
236
|
-
|
|
237
|
-
|
|
244
|
+
...peekLine('Three customers wrote about late deliveries this week: Ada, Grace and Alan.'),
|
|
245
|
+
onDismiss: () => undefined,
|
|
238
246
|
},
|
|
239
247
|
// An approval it waits on, answered in the balloon (T-23).
|
|
240
248
|
waiting: {
|
|
@@ -257,9 +265,9 @@ const BALLOONS = {
|
|
|
257
265
|
*/
|
|
258
266
|
export function AssistantScreen({ picture, }) {
|
|
259
267
|
const stageRef = useRef(null);
|
|
260
|
-
const state = picture === 'aside' ? 'idle' : picture;
|
|
268
|
+
const state = picture === 'aside' || picture === 'open' ? 'idle' : picture;
|
|
261
269
|
const balloon = BALLOONS[picture];
|
|
262
|
-
return (_jsxs(Box, { sx: { flex: 1, position: 'relative' }, children: [picture === 'aside' && (_jsxs(Box, { role: "dialog", "aria-label": "A dialog over the assistant", sx: {
|
|
270
|
+
return (_jsxs(Box, { sx: { flex: 1, position: 'relative' }, children: [picture === 'open' && _jsx(OpenConversation, {}), picture === 'aside' && (_jsxs(Box, { role: "dialog", "aria-label": "A dialog over the assistant", sx: {
|
|
263
271
|
position: 'fixed',
|
|
264
272
|
right: 24,
|
|
265
273
|
bottom: 24,
|
|
@@ -272,7 +280,56 @@ export function AssistantScreen({ picture, }) {
|
|
|
272
280
|
borderColor: 'border.default',
|
|
273
281
|
borderRadius: 'var(--theme-radius-card, 6px)',
|
|
274
282
|
boxShadow: 'var(--theme-shadow, none)',
|
|
275
|
-
}, children: [_jsx(Text, { sx: { fontWeight: 600 }, children: "A dialog" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1 }, children: "The assistant steps aside while this is open." })] })), _jsx(AssistantStage, { character: "paperclip", state: state, place: { right: 48, bottom: 48 }, stageRef: stageRef, onDragStart: () => undefined, open:
|
|
283
|
+
}, children: [_jsx(Text, { sx: { fontWeight: 600 }, children: "A dialog" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1 }, children: "The assistant steps aside while this is open." })] })), _jsx(AssistantStage, { character: "paperclip", state: state, place: { right: 48, bottom: 48 }, stageRef: stageRef, onDragStart: () => undefined, open: picture === 'open', onToggle: () => undefined, balloon: balloon, insist: !!balloon, onDismiss: () => undefined })] }));
|
|
284
|
+
}
|
|
285
|
+
/** What the open picture's conversation holds. */
|
|
286
|
+
const OPEN_CONVERSATION = [
|
|
287
|
+
said('w', 'assistant', 'Hello! Ask me anything about this page.'),
|
|
288
|
+
said('o1', 'user', 'Which customers wrote about late deliveries this week?'),
|
|
289
|
+
said('o2', 'assistant', 'Three of them: **Ada**, **Grace** and **Alan**. Ada’s parcel is two days late; the other two are waiting for a reply.'),
|
|
290
|
+
];
|
|
291
|
+
/**
|
|
292
|
+
* The conversation open, as the assistant's balloon (T-23): over the
|
|
293
|
+
* character in the page's corner, its tail toward it, the history with the
|
|
294
|
+
* welcome first, *Ask a decision* beside the composer, the Lexical composer
|
|
295
|
+
* last, and its close in the corner — no header, no footer.
|
|
296
|
+
*/
|
|
297
|
+
function OpenConversation() {
|
|
298
|
+
const endRef = useRef(null);
|
|
299
|
+
const [input, setInput] = useState('');
|
|
300
|
+
const height = conversationBalloonHeight(window.innerHeight);
|
|
301
|
+
const left = window.innerWidth - 48 - CONVERSATION_BALLOON_WIDTH;
|
|
302
|
+
return (_jsxs(Box, { "data-conversation-balloon": "", sx: {
|
|
303
|
+
position: 'fixed',
|
|
304
|
+
right: '48px',
|
|
305
|
+
bottom: `${48 + 88 + 16}px`,
|
|
306
|
+
width: `${CONVERSATION_BALLOON_WIDTH}px`,
|
|
307
|
+
height: `${height}px`,
|
|
308
|
+
zIndex: 1001,
|
|
309
|
+
display: 'flex',
|
|
310
|
+
flexDirection: 'column',
|
|
311
|
+
bg: 'canvas.default',
|
|
312
|
+
border: '1px solid',
|
|
313
|
+
borderColor: 'border.default',
|
|
314
|
+
boxShadow: 'shadow.extra-large',
|
|
315
|
+
...conversationBalloonSx({
|
|
316
|
+
edge: 'bottom',
|
|
317
|
+
at: balloonTailAt(window.innerWidth - 48 - 44, left),
|
|
318
|
+
}, 'canvas.subtle'),
|
|
319
|
+
}, children: [_jsx(ConversationBalloonClose, { onClose: () => undefined }), _jsxs(Box, { sx: {
|
|
320
|
+
flex: 1,
|
|
321
|
+
minHeight: 0,
|
|
322
|
+
display: 'flex',
|
|
323
|
+
flexDirection: 'column',
|
|
324
|
+
borderRadius: 'var(--theme-radius-bubble, 16px)',
|
|
325
|
+
overflow: 'hidden',
|
|
326
|
+
}, children: [_jsx(Box, { sx: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(ChatMessageList, { displayItems: OPEN_CONVERSATION, isLoading: false, isStreaming: false, showLoadingIndicator: false, hideMessagesAfterToolUI: false, avatarConfig: { ...AVATARS, showAvatars: true }, padding: 2, emptyContent: null, messagesEndRef: endRef, onRespond: async () => undefined }) }), _jsx(Box, { sx: {
|
|
327
|
+
px: 3,
|
|
328
|
+
pb: 1,
|
|
329
|
+
display: 'flex',
|
|
330
|
+
justifyContent: 'flex-end',
|
|
331
|
+
fontSize: 0,
|
|
332
|
+
}, children: _jsx(DecisionAsk, { ask: async () => ({}) }) }), _jsx(InputPrompt, { input: input, setInput: setInput, isLoading: false, connectionConfirmed: true, placeholder: "Type a message...", autoFocus: false, padding: 2, promptVariant: "lexical", onSend: () => undefined, onStop: () => undefined, showTokenUsage: false, showModelSelector: false, showToolsMenu: false, showSkillsMenu: false, showAgentsMenu: false, codemodeEnabled: false, hasConfigData: false, hasSkillsData: false, isA2AProtocol: false })] })] }));
|
|
276
333
|
}
|
|
277
334
|
// ---------------------------------------------------------------------------
|
|
278
335
|
// The characters (T-25, T-24)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SkillSpec } from './skills';
|
|
2
2
|
import type { MCPServer, AgentMCPServerToolConfig } from './mcp';
|
|
3
|
-
import type {
|
|
3
|
+
import type { BackendToolSpec, FrontendToolSpec, FrontendRenderToolSpec } from './tools';
|
|
4
4
|
import type { AgentTriggerConfig } from './triggers';
|
|
5
5
|
import type { AgentModelConfig } from './models';
|
|
6
6
|
import type { AgentOutputConfig } from './outputs';
|
|
@@ -172,7 +172,8 @@ export interface FrameSpec {
|
|
|
172
172
|
architecture: string;
|
|
173
173
|
prompts: FramePromptSpec[];
|
|
174
174
|
skills: string[];
|
|
175
|
-
tools:
|
|
175
|
+
/** Backend tools (agentspecs/backend-tools), `id:version`. */
|
|
176
|
+
backendTools: string[];
|
|
176
177
|
mcpServers: string[];
|
|
177
178
|
guards: FrameGuardSpec[];
|
|
178
179
|
}
|
|
@@ -568,7 +569,8 @@ export interface AppSpec {
|
|
|
568
569
|
instructions: string;
|
|
569
570
|
model: string;
|
|
570
571
|
skills: string[];
|
|
571
|
-
tools
|
|
572
|
+
/** Backend tools (agentspecs/backend-tools) it adds to its agent's. */
|
|
573
|
+
backendTools: string[];
|
|
572
574
|
/** The Frames it works under. */
|
|
573
575
|
context: string[];
|
|
574
576
|
/** The documents and datasets it answers from. */
|
|
@@ -634,8 +636,8 @@ export interface Agentspec {
|
|
|
634
636
|
mcpServers: MCPServer[];
|
|
635
637
|
/** Skills available to this agent */
|
|
636
638
|
skills: SkillSpec[];
|
|
637
|
-
/**
|
|
638
|
-
|
|
639
|
+
/** Backend tools available to this agent: they run on the runtime */
|
|
640
|
+
backendTools?: BackendToolSpec[];
|
|
639
641
|
/** Disable tool approvals for this spec (default: false). */
|
|
640
642
|
disableToolApprovals?: boolean;
|
|
641
643
|
/** Frontend tool sets available to this agent */
|
package/lib/types/chat.d.ts
CHANGED
|
@@ -964,6 +964,18 @@ export interface ChatBaseProps {
|
|
|
964
964
|
notebookToolSurfacesId?: string;
|
|
965
965
|
/** Custom footer content (rendered above input) */
|
|
966
966
|
footerContent?: ReactNode;
|
|
967
|
+
/**
|
|
968
|
+
* The agent's welcome, said as the first message of the history: drawn as
|
|
969
|
+
* the chat draws the agent's messages, and never sent nor kept. The floating
|
|
970
|
+
* assistant's balloon opens on it (LOOP T-23).
|
|
971
|
+
*/
|
|
972
|
+
welcome?: string;
|
|
973
|
+
/**
|
|
974
|
+
* What follows the last message, inside the history and scrolled with it:
|
|
975
|
+
* the floating assistant's balloon holds an approval there, as a message
|
|
976
|
+
* with *Approve* and *Deny* (T-23).
|
|
977
|
+
*/
|
|
978
|
+
trailingContent?: ReactNode;
|
|
967
979
|
/**
|
|
968
980
|
* Show the information icon in the header.
|
|
969
981
|
* When clicked, fires onInformationClick.
|
package/lib/types/config.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export interface AgentConfig {
|
|
|
26
26
|
protocol?: Protocol;
|
|
27
27
|
/** Optional agent spec ID for server-side spec-based creation. */
|
|
28
28
|
agentSpecId?: string;
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/** Backend tool ids (agentspecs/backend-tools) to enable: they run on the runtime. */
|
|
30
|
+
backendTools?: unknown[];
|
|
31
31
|
/** Enable skills for this agent. */
|
|
32
32
|
enableSkills?: boolean;
|
|
33
33
|
/** Optional inference provider override (e.g. local, datalayer). */
|
package/lib/types/tools.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runtime binding metadata for a tool implementation.
|
|
3
3
|
*/
|
|
4
|
-
export interface
|
|
4
|
+
export interface BackendToolRuntimeSpec {
|
|
5
5
|
/** Implementation language */
|
|
6
6
|
language: 'python' | 'typescript';
|
|
7
7
|
/** Module/package containing the implementation */
|
|
@@ -12,7 +12,7 @@ export interface ToolRuntimeSpec {
|
|
|
12
12
|
/**
|
|
13
13
|
* Specification for a runtime tool.
|
|
14
14
|
*/
|
|
15
|
-
export interface
|
|
15
|
+
export interface BackendToolSpec {
|
|
16
16
|
/** Unique tool identifier */
|
|
17
17
|
id: string;
|
|
18
18
|
/** Version */
|
|
@@ -32,7 +32,7 @@ export interface ToolSpec {
|
|
|
32
32
|
/** Whether tool requires human approval before execution */
|
|
33
33
|
requiresApproval?: boolean;
|
|
34
34
|
/** Runtime binding metadata */
|
|
35
|
-
runtime:
|
|
35
|
+
runtime: BackendToolRuntimeSpec;
|
|
36
36
|
/** The icon, `<package>:<name>` — `@datalayer/icons-react:odoo` (agentspecs.marks; see `SpecMark`) */
|
|
37
37
|
icon?: string;
|
|
38
38
|
/** Drawn where there is no icon */
|
package/package.json
CHANGED
|
@@ -234,6 +234,15 @@ def load_fragments(specs_dir: Path) -> List[Dict[str, Any]]:
|
|
|
234
234
|
return fragments
|
|
235
235
|
|
|
236
236
|
|
|
237
|
+
def refuse_tools_field(spec: Dict[str, Any], path: Path) -> None:
|
|
238
|
+
"""Refuse an agent spec that still says `tools`: it is `backend_tools` now."""
|
|
239
|
+
if "tools" in spec:
|
|
240
|
+
raise SystemExit(
|
|
241
|
+
f"Error: {path} says `tools`: the field is `backend_tools` — the tools "
|
|
242
|
+
"that run on the runtime, as `frontend_tools` are those that run on the page"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
237
246
|
def load_yaml_specs(specs_dir: Path) -> List[tuple[str, Dict[str, Any]]]:
|
|
238
247
|
"""
|
|
239
248
|
Load all YAML agent specifications from directory and subdirectories.
|
|
@@ -254,6 +263,7 @@ def load_yaml_specs(specs_dir: Path) -> List[tuple[str, Dict[str, Any]]]:
|
|
|
254
263
|
spec = yaml.safe_load(f)
|
|
255
264
|
if spec: # Skip empty files
|
|
256
265
|
ensure_spec_version(spec)
|
|
266
|
+
refuse_tools_field(spec, yaml_file)
|
|
257
267
|
specs.append(("", spec))
|
|
258
268
|
|
|
259
269
|
# Then, load specs from subdirectories (one level deep)
|
|
@@ -264,6 +274,7 @@ def load_yaml_specs(specs_dir: Path) -> List[tuple[str, Dict[str, Any]]]:
|
|
|
264
274
|
spec = yaml.safe_load(f)
|
|
265
275
|
if spec: # Skip empty files
|
|
266
276
|
ensure_spec_version(spec)
|
|
277
|
+
refuse_tools_field(spec, yaml_file)
|
|
267
278
|
specs.append((subdir.name, spec))
|
|
268
279
|
|
|
269
280
|
fragments = load_fragments(specs_dir)
|
|
@@ -399,7 +410,7 @@ from agent_runtimes.types import (
|
|
|
399
410
|
for skill in spec.get("skills", [])
|
|
400
411
|
]
|
|
401
412
|
tool_refs = [
|
|
402
|
-
versioned_ref(*split_spec_ref(tool)) for tool in spec.get("
|
|
413
|
+
versioned_ref(*split_spec_ref(tool)) for tool in spec.get("backend_tools", [])
|
|
403
414
|
]
|
|
404
415
|
frontend_tool_refs = [
|
|
405
416
|
versioned_ref(*split_spec_ref(ft))
|
|
@@ -591,7 +602,7 @@ from agent_runtimes.types import (
|
|
|
591
602
|
{model_additionals_py_line} inference_provider={inference_provider_str},
|
|
592
603
|
mcp_servers=[{mcp_servers_str}],
|
|
593
604
|
skills={_fmt_list(skill_refs)},
|
|
594
|
-
|
|
605
|
+
backend_tools={_fmt_list(tool_refs)},
|
|
595
606
|
{disable_tool_approvals_line} frontend_tools={_fmt_list(frontend_tool_refs)},
|
|
596
607
|
{frontend_render_tools_py_line} environment_name="{spec.get("environment_name", "ai-agents-env")}",
|
|
597
608
|
icon={icon},
|
|
@@ -740,7 +751,7 @@ def generate_typescript_code(
|
|
|
740
751
|
used_mcp_servers.add(versioned_ref(*split_spec_ref(server)))
|
|
741
752
|
for skill in spec.get("skills", []):
|
|
742
753
|
used_skills.add(versioned_ref(*split_spec_ref(skill)))
|
|
743
|
-
for tool in spec.get("
|
|
754
|
+
for tool in spec.get("backend_tools", []):
|
|
744
755
|
used_tools.add(versioned_ref(*split_spec_ref(tool)))
|
|
745
756
|
for ft in spec.get("frontend_tools", []):
|
|
746
757
|
used_frontend_tools.add(versioned_ref(*split_spec_ref(ft)))
|
|
@@ -786,7 +797,7 @@ def generate_typescript_code(
|
|
|
786
797
|
if tref in used_tools:
|
|
787
798
|
const_name = (
|
|
788
799
|
tid.upper().replace("-", "_")
|
|
789
|
-
+ "
|
|
800
|
+
+ "_BACKEND_TOOL_SPEC"
|
|
790
801
|
+ version_suffix(spec["version"])
|
|
791
802
|
)
|
|
792
803
|
tool_imports.append(const_name)
|
|
@@ -830,7 +841,7 @@ def generate_typescript_code(
|
|
|
830
841
|
types_import_path = "../../types" if is_root_layout else "../../../types"
|
|
831
842
|
mcp_import_path = "../mcpServers" if is_root_layout else "../../mcpServers"
|
|
832
843
|
skills_import_path = "../skills" if is_root_layout else "../../skills"
|
|
833
|
-
tools_import_path = "../
|
|
844
|
+
tools_import_path = "../backendTools" if is_root_layout else "../../backendTools"
|
|
834
845
|
frontend_tools_import_path = (
|
|
835
846
|
"../frontendTools" if is_root_layout else "../../frontendTools"
|
|
836
847
|
)
|
|
@@ -929,7 +940,7 @@ function toAgentSkillSpec(skill: SkillSpec) {
|
|
|
929
940
|
if has_tools:
|
|
930
941
|
code += """
|
|
931
942
|
/**
|
|
932
|
-
* Map tool IDs to
|
|
943
|
+
* Map backend tool IDs to BackendToolSpec objects.
|
|
933
944
|
*/
|
|
934
945
|
const TOOL_MAP: Record<string, any> = {
|
|
935
946
|
"""
|
|
@@ -1019,9 +1030,9 @@ const FRONTEND_TOOL_MAP: Record<string, any> = {
|
|
|
1019
1030
|
else:
|
|
1020
1031
|
skills_str = ""
|
|
1021
1032
|
|
|
1022
|
-
# Get tools - resolve to
|
|
1033
|
+
# Get backend tools - resolve to BackendToolSpec via TOOL_MAP
|
|
1023
1034
|
tool_ids_list = [
|
|
1024
|
-
versioned_ref(*split_spec_ref(sid)) for sid in spec.get("
|
|
1035
|
+
versioned_ref(*split_spec_ref(sid)) for sid in spec.get("backend_tools", [])
|
|
1025
1036
|
]
|
|
1026
1037
|
if has_tools and tool_ids_list:
|
|
1027
1038
|
tools_str = ", ".join(f"TOOL_MAP['{tid}']" for tid in tool_ids_list)
|
|
@@ -1165,7 +1176,7 @@ const FRONTEND_TOOL_MAP: Record<string, any> = {
|
|
|
1165
1176
|
model: {model_ts},
|
|
1166
1177
|
{model_additionals_ts_line}{inference_provider_line} mcpServers: [{mcp_servers_str}],
|
|
1167
1178
|
skills: [{skills_str}].filter(Boolean) as SkillSpec[],
|
|
1168
|
-
|
|
1179
|
+
backendTools: [{tools_str}],
|
|
1169
1180
|
{disable_tool_approvals_line} frontendTools: [{frontend_tools_str}],
|
|
1170
1181
|
{frontend_render_tools_ts_line} environmentName: '{spec.get("environment_name", "ai-agents-env")}',
|
|
1171
1182
|
icon: {icon},
|
|
@@ -1338,7 +1349,7 @@ def generate_subfolder_structure(specs: List[tuple[str, Dict[str, Any]]], args):
|
|
|
1338
1349
|
# Get MCP and skills specs directories
|
|
1339
1350
|
mcp_specs_dir = args.specs_dir.parent / "mcp-servers"
|
|
1340
1351
|
skills_specs_dir = args.specs_dir.parent / "skills"
|
|
1341
|
-
tools_specs_dir = args.specs_dir.parent / "tools"
|
|
1352
|
+
tools_specs_dir = args.specs_dir.parent / "backend-tools"
|
|
1342
1353
|
|
|
1343
1354
|
# Determine base directories
|
|
1344
1355
|
python_base = args.python_output.parent / "agents"
|
|
@@ -1624,7 +1635,7 @@ export * from './modelProviders';
|
|
|
1624
1635
|
export * from './notifications';
|
|
1625
1636
|
export * from './outputs';
|
|
1626
1637
|
export * from './skills';
|
|
1627
|
-
export * from './
|
|
1638
|
+
export * from './backendTools';
|
|
1628
1639
|
export * from './triggers';
|
|
1629
1640
|
export * from './uiPlugins';
|
|
1630
1641
|
export * from './frames';
|
|
@@ -1698,7 +1709,7 @@ def main():
|
|
|
1698
1709
|
# Get MCP and skills specs directories (siblings to agents directory)
|
|
1699
1710
|
mcp_specs_dir = args.specs_dir.parent / "mcp-servers"
|
|
1700
1711
|
skills_specs_dir = args.specs_dir.parent / "skills"
|
|
1701
|
-
tools_specs_dir = args.specs_dir.parent / "tools"
|
|
1712
|
+
tools_specs_dir = args.specs_dir.parent / "backend-tools"
|
|
1702
1713
|
typescript_code = generate_typescript_code(
|
|
1703
1714
|
specs,
|
|
1704
1715
|
str(mcp_specs_dir),
|
|
@@ -52,6 +52,7 @@ CAMEL = {
|
|
|
52
52
|
"composed_at": "composedAt",
|
|
53
53
|
"min_confidence": "minConfidence",
|
|
54
54
|
"decision_model": "decisionModel",
|
|
55
|
+
"backend_tools": "backendTools",
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
#: The keys whose value is carried as it is written: a component tree, weights by name.
|
|
@@ -350,7 +351,7 @@ def generate_actions_python_code(actions: dict[str, Any]) -> str:
|
|
|
350
351
|
f"ACTION_CLASSES: List[str] = {actions['classes']!r}",
|
|
351
352
|
"",
|
|
352
353
|
"#: What each tool of the tools catalogue does, by id.",
|
|
353
|
-
f"
|
|
354
|
+
f"BACKEND_TOOL_ACTIONS: Dict[str, List[str]] = {actions['tools']!r}",
|
|
354
355
|
"",
|
|
355
356
|
"#: What each MCP server's tools do, by server id. `checked` is the day the",
|
|
356
357
|
"#: names were read off the running server, or None when nobody looked.",
|
|
@@ -401,7 +402,7 @@ def generate_actions_typescript_code(actions: dict[str, Any]) -> str:
|
|
|
401
402
|
f"export const ACTION_CLASSES: ActionClass[] = {json.dumps(actions['classes'])};",
|
|
402
403
|
"",
|
|
403
404
|
"/** What each tool of the tools catalogue does, by id. */",
|
|
404
|
-
"export const
|
|
405
|
+
"export const BACKEND_TOOL_ACTIONS: Record<string, ActionClass[]> = "
|
|
405
406
|
+ json.dumps(actions["tools"], indent=2)
|
|
406
407
|
+ ";",
|
|
407
408
|
"",
|
|
@@ -88,7 +88,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
88
88
|
"# Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
89
89
|
"# Distributed under the terms of the Modified BSD License.",
|
|
90
90
|
'"""',
|
|
91
|
-
"Tool Catalog.",
|
|
91
|
+
"Backend Tool Catalog.",
|
|
92
92
|
"",
|
|
93
93
|
"Predefined runtime tools that can be attached to agents.",
|
|
94
94
|
"",
|
|
@@ -98,11 +98,11 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
98
98
|
"",
|
|
99
99
|
"from typing import Dict, List",
|
|
100
100
|
"",
|
|
101
|
-
"from agent_runtimes.types import
|
|
101
|
+
"from agent_runtimes.types import BackendToolRuntimeSpec, BackendToolSpec",
|
|
102
102
|
"",
|
|
103
103
|
"",
|
|
104
104
|
"# " + "=" * 76,
|
|
105
|
-
"# Tool Definitions",
|
|
105
|
+
"# Backend Tool Definitions",
|
|
106
106
|
"# " + "=" * 76,
|
|
107
107
|
"",
|
|
108
108
|
]
|
|
@@ -111,7 +111,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
111
111
|
tool_id = spec["id"]
|
|
112
112
|
version = spec["version"]
|
|
113
113
|
const_name = (
|
|
114
|
-
f"{tool_id.upper().replace('-', '_')}
|
|
114
|
+
f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
|
|
115
115
|
)
|
|
116
116
|
runtime = _require_runtime(spec)
|
|
117
117
|
requires_approval = _requires_approval(spec)
|
|
@@ -122,7 +122,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
122
122
|
|
|
123
123
|
lines.extend(
|
|
124
124
|
[
|
|
125
|
-
f"{const_name} =
|
|
125
|
+
f"{const_name} = BackendToolSpec(",
|
|
126
126
|
f' id="{tool_id}",',
|
|
127
127
|
f' version="{version}",',
|
|
128
128
|
f" name={json.dumps(spec['name'], ensure_ascii=False)},",
|
|
@@ -132,7 +132,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
132
132
|
f' approval="{spec.get("approval", "auto")}",',
|
|
133
133
|
f" timeout={timeout_py},",
|
|
134
134
|
f" requires_approval={requires_approval},",
|
|
135
|
-
" runtime=
|
|
135
|
+
" runtime=BackendToolRuntimeSpec(",
|
|
136
136
|
f' language="{runtime["language"]}",',
|
|
137
137
|
f' package="{runtime["package"]}",',
|
|
138
138
|
f' method="{runtime["method"]}",',
|
|
@@ -147,10 +147,10 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
147
147
|
lines.extend(
|
|
148
148
|
[
|
|
149
149
|
"# " + "=" * 76,
|
|
150
|
-
"# Tool Catalog",
|
|
150
|
+
"# Backend Tool Catalog",
|
|
151
151
|
"# " + "=" * 76,
|
|
152
152
|
"",
|
|
153
|
-
"
|
|
153
|
+
"BACKEND_TOOL_CATALOG: Dict[str, BackendToolSpec] = {",
|
|
154
154
|
]
|
|
155
155
|
)
|
|
156
156
|
|
|
@@ -158,7 +158,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
158
158
|
tool_id = spec["id"]
|
|
159
159
|
version = spec["version"]
|
|
160
160
|
const_name = (
|
|
161
|
-
f"{tool_id.upper().replace('-', '_')}
|
|
161
|
+
f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
|
|
162
162
|
)
|
|
163
163
|
lines.append(f' "{tool_id}": {const_name},')
|
|
164
164
|
|
|
@@ -167,20 +167,20 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
167
167
|
"}",
|
|
168
168
|
"",
|
|
169
169
|
"",
|
|
170
|
-
"def
|
|
170
|
+
"def get_backend_tool_spec(tool_id: str) -> BackendToolSpec | None:",
|
|
171
171
|
' """Get a tool specification by ID (accepts both bare and versioned refs)."""',
|
|
172
|
-
" spec =
|
|
172
|
+
" spec = BACKEND_TOOL_CATALOG.get(tool_id)",
|
|
173
173
|
" if spec is not None:",
|
|
174
174
|
" return spec",
|
|
175
175
|
" base, _, ver = tool_id.rpartition(':')",
|
|
176
176
|
" if base and '.' in ver:",
|
|
177
|
-
" return
|
|
177
|
+
" return BACKEND_TOOL_CATALOG.get(base)",
|
|
178
178
|
" return None",
|
|
179
179
|
"",
|
|
180
180
|
"",
|
|
181
|
-
"def
|
|
181
|
+
"def list_backend_tool_specs() -> List[BackendToolSpec]:",
|
|
182
182
|
' """List all tool specifications."""',
|
|
183
|
-
" return list(
|
|
183
|
+
" return list(BACKEND_TOOL_CATALOG.values())",
|
|
184
184
|
"",
|
|
185
185
|
]
|
|
186
186
|
)
|
|
@@ -196,7 +196,7 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
196
196
|
" */",
|
|
197
197
|
"",
|
|
198
198
|
"/**",
|
|
199
|
-
" * Tool Catalog",
|
|
199
|
+
" * Backend Tool Catalog",
|
|
200
200
|
" *",
|
|
201
201
|
" * Predefined runtime tools that can be attached to agents.",
|
|
202
202
|
" *",
|
|
@@ -204,10 +204,10 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
204
204
|
" * DO NOT EDIT MANUALLY - run 'make specs' to regenerate.",
|
|
205
205
|
" */",
|
|
206
206
|
"",
|
|
207
|
-
"import type {
|
|
207
|
+
"import type { BackendToolSpec } from '../types';",
|
|
208
208
|
"",
|
|
209
209
|
"// " + "=" * 76,
|
|
210
|
-
"// Tool Definitions",
|
|
210
|
+
"// Backend Tool Definitions",
|
|
211
211
|
"// " + "=" * 76,
|
|
212
212
|
"",
|
|
213
213
|
]
|
|
@@ -216,7 +216,7 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
216
216
|
tool_id = spec["id"]
|
|
217
217
|
version = spec["version"]
|
|
218
218
|
const_name = (
|
|
219
|
-
f"{tool_id.upper().replace('-', '_')}
|
|
219
|
+
f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
|
|
220
220
|
)
|
|
221
221
|
runtime = _require_runtime(spec)
|
|
222
222
|
requires_approval = _requires_approval(spec)
|
|
@@ -228,7 +228,7 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
228
228
|
|
|
229
229
|
lines.extend(
|
|
230
230
|
[
|
|
231
|
-
f"export const {const_name}:
|
|
231
|
+
f"export const {const_name}: BackendToolSpec = {{",
|
|
232
232
|
f" id: '{tool_id}',",
|
|
233
233
|
f" version: '{version}',",
|
|
234
234
|
# JSON-escaped: an apostrophe in a description is not a syntax error.
|
|
@@ -254,10 +254,10 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
254
254
|
lines.extend(
|
|
255
255
|
[
|
|
256
256
|
"// " + "=" * 76,
|
|
257
|
-
"// Tool Catalog",
|
|
257
|
+
"// Backend Tool Catalog",
|
|
258
258
|
"// " + "=" * 76,
|
|
259
259
|
"",
|
|
260
|
-
"export const
|
|
260
|
+
"export const BACKEND_TOOL_CATALOG: Record<string, BackendToolSpec> = {",
|
|
261
261
|
]
|
|
262
262
|
)
|
|
263
263
|
|
|
@@ -265,7 +265,7 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
265
265
|
tool_id = spec["id"]
|
|
266
266
|
version = spec["version"]
|
|
267
267
|
const_name = (
|
|
268
|
-
f"{tool_id.upper().replace('-', '_')}
|
|
268
|
+
f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
|
|
269
269
|
)
|
|
270
270
|
lines.append(f" '{tool_id}': {const_name},")
|
|
271
271
|
|
|
@@ -273,22 +273,22 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
273
273
|
[
|
|
274
274
|
"};",
|
|
275
275
|
"",
|
|
276
|
-
"export function
|
|
277
|
-
" return Object.values(
|
|
276
|
+
"export function getBackendToolSpecs(): BackendToolSpec[] {",
|
|
277
|
+
" return Object.values(BACKEND_TOOL_CATALOG);",
|
|
278
278
|
"}",
|
|
279
279
|
"",
|
|
280
|
-
"function
|
|
281
|
-
" if (toolId in
|
|
280
|
+
"function resolveBackendToolId(toolId: string): string {",
|
|
281
|
+
" if (toolId in BACKEND_TOOL_CATALOG) return toolId;",
|
|
282
282
|
" const idx = toolId.lastIndexOf(':');",
|
|
283
283
|
" if (idx > 0) {",
|
|
284
284
|
" const base = toolId.slice(0, idx);",
|
|
285
|
-
" if (base in
|
|
285
|
+
" if (base in BACKEND_TOOL_CATALOG) return base;",
|
|
286
286
|
" }",
|
|
287
287
|
" return toolId;",
|
|
288
288
|
"}",
|
|
289
289
|
"",
|
|
290
|
-
"export function
|
|
291
|
-
" return
|
|
290
|
+
"export function getBackendToolSpec(toolId: string): BackendToolSpec | undefined {",
|
|
291
|
+
" return BACKEND_TOOL_CATALOG[resolveBackendToolId(toolId)];",
|
|
292
292
|
"}",
|
|
293
293
|
"",
|
|
294
294
|
]
|
|
@@ -168,7 +168,7 @@ def generate_typescript_code(specs: list[dict[str, Any]], specs_dir: Path) -> st
|
|
|
168
168
|
[("", spec) for spec in specs],
|
|
169
169
|
str(root / "mcp-servers"),
|
|
170
170
|
str(root / "skills"),
|
|
171
|
-
str(root / "tools"),
|
|
171
|
+
str(root / "backend-tools"),
|
|
172
172
|
)
|
|
173
173
|
marker = "// ============================================================================\n// Agent Specs Registry"
|
|
174
174
|
if marker not in code:
|
|
@@ -208,7 +208,7 @@ def generate_typescript_code(specs: list[dict[str, Any]], specs_dir: Path) -> st
|
|
|
208
208
|
# is one directory up, beside the catalogues it imports.
|
|
209
209
|
head = head.replace("from '../../types'", "from '../types'")
|
|
210
210
|
head = re.sub(
|
|
211
|
-
r"from '\.\./(mcpServers|skills|
|
|
211
|
+
r"from '\.\./(mcpServers|skills|backendTools|frontendTools)'", r"from './\1'", head
|
|
212
212
|
)
|
|
213
213
|
|
|
214
214
|
lines = [
|
|
@@ -53,13 +53,13 @@ FIELDS = (
|
|
|
53
53
|
"architecture",
|
|
54
54
|
"prompts",
|
|
55
55
|
"skills",
|
|
56
|
-
"
|
|
56
|
+
"backend_tools",
|
|
57
57
|
"mcp_servers",
|
|
58
58
|
"guards",
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
#: Python field → TypeScript property, where they differ.
|
|
62
|
-
CAMEL = {"mcp_servers": "mcpServers"}
|
|
62
|
+
CAMEL = {"mcp_servers": "mcpServers", "backend_tools": "backendTools"}
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
def load_frame_specs(specs_dir: Path) -> list[dict[str, Any]]:
|
package/lib/specs/tools.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool Catalog
|
|
3
|
-
*
|
|
4
|
-
* Predefined runtime tools that can be attached to agents.
|
|
5
|
-
*
|
|
6
|
-
* This file is AUTO-GENERATED from YAML specifications.
|
|
7
|
-
* DO NOT EDIT MANUALLY - run 'make specs' to regenerate.
|
|
8
|
-
*/
|
|
9
|
-
import type { ToolSpec } from '../types';
|
|
10
|
-
export declare const CREATE_PLAN_TOOL_SPEC_0_0_1: ToolSpec;
|
|
11
|
-
export declare const CURRENT_TIME_TOOL_SPEC_0_0_1: ToolSpec;
|
|
12
|
-
export declare const DECIDE_TOOL_SPEC_0_0_1: ToolSpec;
|
|
13
|
-
export declare const DISPLAY_RECIPE_TOOL_SPEC_0_0_1: ToolSpec;
|
|
14
|
-
export declare const EXAMPLE_CREATE_PLAN_TOOL_SPEC_0_0_1: ToolSpec;
|
|
15
|
-
export declare const EXAMPLE_CURRENT_TIME_TOOL_SPEC_0_0_1: ToolSpec;
|
|
16
|
-
export declare const EXAMPLE_DISPLAY_RECIPE_TOOL_SPEC_0_0_1: ToolSpec;
|
|
17
|
-
export declare const EXAMPLE_GENERATE_HAIKU_TOOL_SPEC_0_0_1: ToolSpec;
|
|
18
|
-
export declare const EXAMPLE_GENERATE_TASK_STEPS_TOOL_SPEC_0_0_1: ToolSpec;
|
|
19
|
-
export declare const EXAMPLE_GET_WEATHER_TOOL_SPEC_0_0_1: ToolSpec;
|
|
20
|
-
export declare const EXAMPLE_RENDER_A2UI_SURFACE_TOOL_SPEC_0_0_1: ToolSpec;
|
|
21
|
-
export declare const EXAMPLE_UPDATE_PLAN_STEP_TOOL_SPEC_0_0_1: ToolSpec;
|
|
22
|
-
export declare const GENERATE_HAIKU_TOOL_SPEC_0_0_1: ToolSpec;
|
|
23
|
-
export declare const GENERATE_TASK_STEPS_TOOL_SPEC_0_0_1: ToolSpec;
|
|
24
|
-
export declare const GET_WEATHER_TOOL_SPEC_0_0_1: ToolSpec;
|
|
25
|
-
export declare const RUNTIME_ECHO_TOOL_SPEC_0_0_1: ToolSpec;
|
|
26
|
-
export declare const RUNTIME_SEND_MAIL_TOOL_SPEC_0_0_1: ToolSpec;
|
|
27
|
-
export declare const RUNTIME_SENSITIVE_ECHO_TOOL_SPEC_0_0_1: ToolSpec;
|
|
28
|
-
export declare const UPDATE_PLAN_STEP_TOOL_SPEC_0_0_1: ToolSpec;
|
|
29
|
-
export declare const TOOL_CATALOG: Record<string, ToolSpec>;
|
|
30
|
-
export declare function getToolSpecs(): ToolSpec[];
|
|
31
|
-
export declare function getToolSpec(toolId: string): ToolSpec | undefined;
|