@datalayer/agent-runtimes 1.3.84 → 1.3.86
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 +5 -4
- package/lib/chat/ChatFloating.js +8 -6
- package/lib/chat/ChatLanguage.d.ts +17 -0
- package/lib/chat/ChatLanguage.js +29 -0
- package/lib/chat/assistant/AssistantStage.d.ts +4 -4
- package/lib/chat/assistant/AssistantStage.js +4 -2
- package/lib/chat/assistant/BalloonParts.d.ts +2 -1
- package/lib/chat/assistant/BalloonParts.js +12 -5
- package/lib/chat/assistant/BalloonVisual.js +3 -1
- package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
- package/lib/chat/assistant/ConversationBalloon.js +3 -2
- package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
- package/lib/chat/assistant/SpeechBalloon.js +10 -7
- package/lib/chat/assistant/toolLine.d.ts +8 -4
- package/lib/chat/assistant/toolLine.js +13 -9
- package/lib/chat/base/ChatBase.js +14 -11
- package/lib/chat/display/EmptyState.js +3 -1
- package/lib/chat/index.d.ts +2 -0
- package/lib/chat/index.js +3 -0
- package/lib/chat/messages/ChatMessages.js +3 -1
- package/lib/chat/messages/TurnFooter.js +3 -1
- package/lib/chat/prompt/InputPrompt.js +3 -1
- package/lib/chat/prompt/InputPromptLexical.js +3 -1
- package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
- package/lib/chat/tools/ToolApprovalBanner.js +5 -3
- package/lib/chat/tools/ToolApprovalDialog.js +5 -3
- package/lib/chat/tools/ToolCallDisplay.js +29 -25
- package/lib/chat/words.d.ts +102 -0
- package/lib/chat/words.js +460 -0
- package/lib/components/a2ui/custom/index.d.ts +39 -0
- package/lib/components/a2ui/custom/index.js +95 -0
- package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
- package/lib/components/a2ui/custom/sandbox.js +188 -0
- package/lib/components/a2ui/datalayer/index.d.ts +1 -1
- package/lib/components/a2ui/datalayer/index.js +1 -1
- package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
- package/lib/components/a2ui/datalayer/schema.js +12 -1
- package/lib/components/a2ui/index.d.ts +1 -0
- package/lib/components/a2ui/index.js +1 -0
- package/lib/components/a2ui/visibleWhen.d.ts +12 -1
- package/lib/components/a2ui/visibleWhen.js +24 -3
- package/lib/components/inspector/AgentInspector.d.ts +25 -44
- package/lib/components/inspector/AgentInspector.js +27 -215
- package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
- package/lib/components/inspector/AgentInspectorDialog.js +2 -2
- package/lib/components/inspector/a2aSpans.d.ts +51 -0
- package/lib/components/inspector/a2aSpans.js +373 -0
- package/lib/components/inspector/agentSpans.d.ts +120 -0
- package/lib/components/inspector/agentSpans.js +253 -0
- package/lib/components/inspector/chatSpans.d.ts +36 -0
- package/lib/components/inspector/chatSpans.js +50 -0
- package/lib/components/inspector/classify.d.ts +11 -8
- package/lib/components/inspector/classify.js +23 -24
- package/lib/components/inspector/index.d.ts +7 -7
- package/lib/components/inspector/index.js +7 -7
- package/lib/components/teams/A2ATeamGraph.d.ts +117 -22
- package/lib/components/teams/A2ATeamGraph.js +217 -97
- package/lib/components/teams/SceneTranscript.d.ts +29 -0
- package/lib/components/teams/SceneTranscript.js +54 -0
- package/lib/components/teams/SceneView.d.ts +41 -0
- package/lib/components/teams/SceneView.js +28 -0
- package/lib/components/teams/index.d.ts +4 -0
- package/lib/components/teams/index.js +4 -0
- package/lib/components/teams/sceneStage.d.ts +120 -0
- package/lib/components/teams/sceneStage.js +170 -0
- package/lib/components/teams/sceneTranscript.d.ts +74 -0
- package/lib/components/teams/sceneTranscript.js +294 -0
- package/lib/components/teams/teamConnections.d.ts +2 -2
- package/lib/components/teams/teamConnections.js +3 -3
- package/lib/components/teams/useA2ATeam.d.ts +94 -22
- package/lib/components/teams/useA2ATeam.js +356 -140
- package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
- package/lib/examples/AgentA2ATeamExample.js +70 -60
- package/lib/examples/AssistantExample.js +2 -2
- package/lib/examples/ScenesExample.d.ts +26 -0
- package/lib/examples/ScenesExample.js +241 -0
- package/lib/examples/example-selector.js +1 -0
- package/lib/examples/exampleGroups.js +3 -0
- package/lib/examples/index.d.ts +1 -0
- package/lib/examples/index.js +1 -0
- package/lib/examples/main.js +4 -0
- package/lib/loop/apps/AppComposer.d.ts +2 -1
- package/lib/loop/apps/AppComposer.js +6 -3
- package/lib/loop/apps/AppRenderer.d.ts +2 -1
- package/lib/loop/apps/AppRenderer.js +34 -25
- package/lib/loop/apps/appspec.d.ts +11 -1
- package/lib/loop/apps/appspec.js +115 -1
- package/lib/loop/apps/checks.d.ts +8 -0
- package/lib/loop/apps/checks.js +106 -4
- package/lib/loop/apps/customComponents.d.ts +41 -0
- package/lib/loop/apps/customComponents.js +260 -0
- package/lib/loop/apps/hostTools.d.ts +8 -1
- package/lib/loop/apps/hostTools.js +9 -0
- package/lib/loop/apps/index.d.ts +1 -0
- package/lib/loop/apps/index.js +1 -0
- package/lib/loop/core/a2uiComponents.d.ts +10 -0
- package/lib/loop/core/a2uiComponents.js +15 -0
- package/lib/loop/core/index.d.ts +1 -0
- package/lib/loop/core/index.js +3 -0
- package/lib/loop/embed/AppEmbed.d.ts +11 -3
- package/lib/loop/embed/AppEmbed.js +42 -16
- package/lib/loop/embed/element.d.ts +5 -0
- package/lib/loop/embed/element.js +61 -5
- package/lib/loop/embed/embedConfig.d.ts +8 -0
- package/lib/loop/embed/embedConfig.js +18 -0
- package/lib/loop/embed/hostBridge.d.ts +28 -2
- package/lib/loop/embed/hostBridge.js +22 -2
- package/lib/loop/index.d.ts +4 -3
- package/lib/loop/index.js +4 -3
- package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
- package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
- package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
- package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
- package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
- package/lib/loop/plugins/app-components/index.d.ts +26 -0
- package/lib/loop/plugins/app-components/index.js +56 -0
- package/lib/loop/plugins/app-elements/AppElements.js +4 -1
- package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
- package/lib/loop/plugins/app-page/AppPage.js +106 -8
- package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
- package/lib/loop/plugins/app-page/appPageModel.js +120 -4
- package/lib/loop/plugins/app-page/index.d.ts +1 -0
- package/lib/loop/plugins/app-page/index.js +1 -0
- package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
- package/lib/loop/plugins/app-page/pageRun.js +111 -0
- package/lib/loop/plugins/assistant/index.js +8 -6
- package/lib/loop/plugins/chat/ChatView.js +6 -4
- package/lib/loop/plugins/chat/index.d.ts +1 -1
- package/lib/loop/plugins/chat/index.js +2 -1
- package/lib/models/PageTag.d.ts +1 -1
- package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
- package/lib/specs/actions.js +78 -1
- package/lib/specs/apps.d.ts +5 -0
- package/lib/specs/apps.js +1384 -231
- package/lib/specs/appspecSchema.js +191 -1
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/specs/mcpServers.js +8 -2
- package/lib/specs/scenes.d.ts +21 -0
- package/lib/specs/scenes.js +1191 -0
- package/lib/specs/teams/teams.d.ts +3 -0
- package/lib/specs/teams/teams.js +249 -0
- package/lib/types/agentspecs.d.ts +70 -0
- package/lib/types/chat.d.ts +6 -5
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/scenes.d.ts +207 -0
- package/lib/types/scenes.js +5 -0
- package/lib/types/teams.d.ts +13 -2
- package/package.json +4 -4
- package/scripts/codegen/generate_agents.py +1 -0
- package/scripts/codegen/generate_apps.py +4 -2
- package/scripts/codegen/generate_scenes.py +301 -0
- package/scripts/codegen/generate_teams.py +15 -2
- package/lib/components/inspector/JsonTree.d.ts +0 -24
- package/lib/components/inspector/JsonTree.js +0 -89
- package/lib/components/inspector/a2aInspect.d.ts +0 -56
- package/lib/components/inspector/a2aInspect.js +0 -478
- package/lib/components/inspector/agentInspector.d.ts +0 -179
- package/lib/components/inspector/agentInspector.js +0 -267
- package/lib/components/inspector/chatInspect.d.ts +0 -38
- package/lib/components/inspector/chatInspect.js +0 -65
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/agent-runtimes",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.86",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"clean:dist": "rimraf dist",
|
|
70
70
|
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
|
|
71
71
|
"create:patches": "bash scripts/create-patches.sh",
|
|
72
|
-
"examples": "run-p server:start server:start:codemode server:start:accounting server:
|
|
72
|
+
"examples": "run-p server:start server:start:codemode server:start:accounting server:scenes jupyter:start examples:vite",
|
|
73
73
|
"examples2": "run-p server:start server:start:codemode jupyter:start examples2:vite",
|
|
74
74
|
"example-local": "make example-local",
|
|
75
75
|
"examples:fresh": "npm run clean:cache && npm run examples",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"server:start:agent-node": "AGENT_NODE_JUPYTER_URL=${AGENT_NODE_JUPYTER_URL:-http://127.0.0.1:8888/api/jupyter-server} AGENT_NODE_JUPYTER_TOKEN=${AGENT_NODE_JUPYTER_TOKEN:-60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6} PYTHONIOENCODING=utf-8 AGENT_RUNTIMES_DEBUG=false AGENT_RUNTIMES_LOG_LEVEL=info DATALAYER_EVALS_MODE=interactive DATALAYER_EVALS_EMIT_LIVE_EVENTS=true AGENT_RUNTIMES_INFERENCE_PROVIDER_OVERRIDE=${AGENT_RUNTIMES_INFERENCE_PROVIDER_OVERRIDE:-datalayer} python -m agent_runtimes serve --node --port 8765 --log-level info",
|
|
100
100
|
"server:start:codemode": "PYTHONIOENCODING=utf-8 AGENT_RUNTIMES_DEBUG=false AGENT_RUNTIMES_LOG_LEVEL=info DATALAYER_EVALS_MODE=interactive DATALAYER_EVALS_EMIT_LIVE_EVENTS=true python -m agent_runtimes serve --port 8766 --log-level info",
|
|
101
101
|
"server:start:accounting": "PYTHONIOENCODING=utf-8 AGENT_RUNTIMES_DEBUG=false AGENT_RUNTIMES_LOG_LEVEL=info python -m agent_runtimes serve --port 8767 --log-level info",
|
|
102
|
-
"server:
|
|
102
|
+
"server:scenes": "python examples/sales-accounting-a2a/serve_scenes.py --url http://127.0.0.1:8767 --wait 300 || echo 'The scenes were not served: the Agent A2A Team and Scenes examples will say which member is not reachable.'",
|
|
103
103
|
"examples:codemode": "run-p server:start server:start:codemode examples:vite",
|
|
104
104
|
"start": "vite",
|
|
105
105
|
"start:acp": "run-p server:start start:acp:vite",
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"@ai-sdk/react": "4.0.93",
|
|
148
148
|
"@anthropic-ai/sdk": "^0.52.0",
|
|
149
149
|
"@datalayer/agent-teams": "^1.0.0",
|
|
150
|
-
"@datalayer/core": "^1.2.
|
|
150
|
+
"@datalayer/core": "^1.2.18",
|
|
151
151
|
"@datalayer/decks": "^1.0.4",
|
|
152
152
|
"@datalayer/icons-react": "^1.0.16",
|
|
153
153
|
"@datalayer/jupyter-lexical": "^2.0.18",
|
|
@@ -55,6 +55,7 @@ CAMEL = {
|
|
|
55
55
|
"backend_tools": "backendTools",
|
|
56
56
|
"character_alone": "characterAlone",
|
|
57
57
|
"settings_ui": "settingsUi",
|
|
58
|
+
"custom_components": "customComponents",
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
#: The keys whose value is carried as it is written: a component tree, weights by name.
|
|
@@ -126,6 +127,9 @@ def load_specs(specs_dir: Path) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
|
126
127
|
for case in spec["tests"]["cases"]:
|
|
127
128
|
if not case.get("code"):
|
|
128
129
|
case.pop("code", None)
|
|
130
|
+
# Components of its own: said only when it has some.
|
|
131
|
+
if not spec["interface"].get("custom_components"):
|
|
132
|
+
spec["interface"].pop("custom_components", None)
|
|
129
133
|
for key in ("description", "instructions", "goal"):
|
|
130
134
|
spec[key] = _flat(spec.get(key))
|
|
131
135
|
spec["interface"]["welcome"] = _flat(spec["interface"].get("welcome"))
|
|
@@ -492,8 +496,6 @@ def generate_schema_typescript_code() -> str:
|
|
|
492
496
|
" * @module specs/appspecSchema",
|
|
493
497
|
" */",
|
|
494
498
|
"",
|
|
495
|
-
"/* eslint-disable */",
|
|
496
|
-
"",
|
|
497
499
|
"export type JsonSchema = {",
|
|
498
500
|
" [key: string]: unknown;",
|
|
499
501
|
" type?: string;",
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
# Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Generate Python and TypeScript code from YAML scene specifications.
|
|
7
|
+
|
|
8
|
+
A scene (agentspecs >= 0.0.61, LOOP A-11) stages a team: the team says who
|
|
9
|
+
is on stage, the scene says what happens there — the setting, the script,
|
|
10
|
+
the stage directions, the audience, the rehearsal and where it plays. One
|
|
11
|
+
YAML per scene under ``agentspecs/scenes``.
|
|
12
|
+
|
|
13
|
+
Each scene is validated here, by ``agentspecs`` itself (``scene_problems``),
|
|
14
|
+
and arrives with its cast resolved — every member of the team, its persona
|
|
15
|
+
filled from its application (``cast_of``) — its ``entry`` said, what each
|
|
16
|
+
beat ``shows`` said, and what it names that is not enabled (``setup``).
|
|
17
|
+
Empty optional lists are dropped, as ``generate_apps.py`` drops them.
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
python generate_scenes.py \\
|
|
21
|
+
--specs-dir agentspecs/agentspecs/scenes \\
|
|
22
|
+
--python-output agent_runtimes/specs/scenes.py \\
|
|
23
|
+
--typescript-output src/specs/scenes.ts
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import argparse
|
|
27
|
+
import json
|
|
28
|
+
import sys
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
from agentspecs_clone import import_from_clone
|
|
33
|
+
from versioning import ensure_spec_version, version_suffix
|
|
34
|
+
|
|
35
|
+
#: The keys of a scene spec, as TypeScript spells them.
|
|
36
|
+
CAMEL = {
|
|
37
|
+
"runs_in": "runsIn",
|
|
38
|
+
"talks_to": "talksTo",
|
|
39
|
+
"opens_first": "opensFirst",
|
|
40
|
+
"rests_after": "restsAfter",
|
|
41
|
+
"ceiling_per_ask": "ceilingPerAsk",
|
|
42
|
+
"asks_a_day": "asksADay",
|
|
43
|
+
"must_say": "mustSay",
|
|
44
|
+
"must_not_say": "mustNotSay",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#: The lists written only when they hold something: TypeScript reads them as optional.
|
|
48
|
+
OPTIONAL_LISTS = (
|
|
49
|
+
"talks_to",
|
|
50
|
+
"frames",
|
|
51
|
+
"contents",
|
|
52
|
+
"branch",
|
|
53
|
+
"withhold",
|
|
54
|
+
"must_say",
|
|
55
|
+
"must_not_say",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _flat(text: Any) -> str:
|
|
60
|
+
"""A text on one line."""
|
|
61
|
+
return " ".join(str(text or "").split())
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _trim(value: Any, key: str = "") -> Any:
|
|
65
|
+
"""A value without the empty optional lists and the keys that hold nothing."""
|
|
66
|
+
if isinstance(value, dict):
|
|
67
|
+
trimmed = {}
|
|
68
|
+
for name, item in value.items():
|
|
69
|
+
if item is None or (name in OPTIONAL_LISTS and item == []):
|
|
70
|
+
continue
|
|
71
|
+
trimmed[name] = _trim(item, name)
|
|
72
|
+
return trimmed
|
|
73
|
+
if isinstance(value, list):
|
|
74
|
+
return [_trim(item) for item in value]
|
|
75
|
+
return value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def load_specs(specs_dir: Path) -> list[dict[str, Any]]:
|
|
79
|
+
"""Every scene, validated, as plain data: its cast resolved, its entry said."""
|
|
80
|
+
scenes_module = import_from_clone(specs_dir, "scenes")
|
|
81
|
+
scenes = scenes_module.load_scenes(specs_dir)
|
|
82
|
+
specs: list[dict[str, Any]] = []
|
|
83
|
+
for identity in sorted(scenes):
|
|
84
|
+
scene = scenes[identity]
|
|
85
|
+
spec = scene.model_dump(mode="json", by_alias=True)
|
|
86
|
+
spec["entry"] = scene.entry_of()
|
|
87
|
+
spec["cast"] = [
|
|
88
|
+
member.model_dump(mode="json", by_alias=True) for member in scene.cast_of()
|
|
89
|
+
]
|
|
90
|
+
for beat, data in zip(scene.script, spec["script"]):
|
|
91
|
+
data["shows"] = [kind.value for kind in beat.shown]
|
|
92
|
+
spec["setup"] = scenes_module.scene_setup(scene)
|
|
93
|
+
for key in ("description",):
|
|
94
|
+
spec[key] = _flat(spec.get(key))
|
|
95
|
+
spec["setting"]["assumes"] = _flat(spec["setting"].get("assumes"))
|
|
96
|
+
for member in spec["cast"]:
|
|
97
|
+
member["brief"] = _flat(member.get("brief"))
|
|
98
|
+
for beat in spec["script"]:
|
|
99
|
+
beat["expect"] = _flat(beat.get("expect"))
|
|
100
|
+
for branch in beat.get("branch") or []:
|
|
101
|
+
branch["expect"] = _flat(branch.get("expect"))
|
|
102
|
+
ensure_spec_version(spec)
|
|
103
|
+
specs.append(_trim(spec))
|
|
104
|
+
return specs
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _const_name(spec: dict[str, Any]) -> str:
|
|
108
|
+
"""`sales-and-accounting` 0.0.1 → `SALES_AND_ACCOUNTING_SCENE_0_0_1`."""
|
|
109
|
+
base = spec["id"].upper().replace("-", "_").replace(".", "_")
|
|
110
|
+
return f"{base}_SCENE" + version_suffix(spec["version"])
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _typescript_value(value: Any) -> Any:
|
|
114
|
+
"""A scene as the TypeScript interfaces read it."""
|
|
115
|
+
if isinstance(value, dict):
|
|
116
|
+
return {
|
|
117
|
+
CAMEL.get(key, key): _typescript_value(item) for key, item in value.items()
|
|
118
|
+
}
|
|
119
|
+
if isinstance(value, list):
|
|
120
|
+
return [_typescript_value(item) for item in value]
|
|
121
|
+
return value
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
HEADER = [
|
|
125
|
+
"This file is AUTO-GENERATED from YAML specifications.",
|
|
126
|
+
"DO NOT EDIT MANUALLY - run 'make specs' to regenerate.",
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
ABOUT = [
|
|
130
|
+
"Scene Catalog.",
|
|
131
|
+
"",
|
|
132
|
+
"A team, staged: the setting, the script, the stage directions, the",
|
|
133
|
+
"audience, the rehearsal and where it plays. The cast is resolved.",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
138
|
+
"""Generate the Python scene catalogue."""
|
|
139
|
+
lines = [
|
|
140
|
+
"# Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
141
|
+
"# Distributed under the terms of the Modified BSD License.",
|
|
142
|
+
'"""',
|
|
143
|
+
*ABOUT,
|
|
144
|
+
"",
|
|
145
|
+
*HEADER,
|
|
146
|
+
'"""',
|
|
147
|
+
"",
|
|
148
|
+
"from typing import Dict",
|
|
149
|
+
"",
|
|
150
|
+
"from agent_runtimes.types import SceneSpec",
|
|
151
|
+
"",
|
|
152
|
+
"# " + "=" * 76,
|
|
153
|
+
"# Scene Definitions",
|
|
154
|
+
"# " + "=" * 76,
|
|
155
|
+
"",
|
|
156
|
+
]
|
|
157
|
+
for spec in specs:
|
|
158
|
+
lines.extend(
|
|
159
|
+
[
|
|
160
|
+
f"{_const_name(spec)} = SceneSpec.model_validate(",
|
|
161
|
+
f" {spec!r}",
|
|
162
|
+
")",
|
|
163
|
+
"",
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
lines.extend(
|
|
167
|
+
[
|
|
168
|
+
"",
|
|
169
|
+
"# " + "=" * 76,
|
|
170
|
+
"# Scene Catalog",
|
|
171
|
+
"# " + "=" * 76,
|
|
172
|
+
"",
|
|
173
|
+
"SCENE_CATALOGUE: Dict[str, SceneSpec] = {",
|
|
174
|
+
]
|
|
175
|
+
)
|
|
176
|
+
for spec in specs:
|
|
177
|
+
lines.append(f" {spec['id']!r}: {_const_name(spec)},")
|
|
178
|
+
lines.extend(
|
|
179
|
+
[
|
|
180
|
+
"}",
|
|
181
|
+
"",
|
|
182
|
+
"",
|
|
183
|
+
"def get_scene_spec(scene_id: str) -> SceneSpec | None:",
|
|
184
|
+
' """A scene, by `id` or `id:version`, or None."""',
|
|
185
|
+
" found = SCENE_CATALOGUE.get(scene_id)",
|
|
186
|
+
" if found is not None:",
|
|
187
|
+
" return found",
|
|
188
|
+
' base, _, version = scene_id.rpartition(":")',
|
|
189
|
+
' return SCENE_CATALOGUE.get(base) if base and "." in version else None',
|
|
190
|
+
"",
|
|
191
|
+
"",
|
|
192
|
+
"def list_scene_specs(tag: str | None = None) -> list[SceneSpec]:",
|
|
193
|
+
' """Every scene of the catalogue, or those carrying a tag."""',
|
|
194
|
+
" return [",
|
|
195
|
+
" scene for scene in SCENE_CATALOGUE.values() if tag is None or tag in scene.tags",
|
|
196
|
+
" ]",
|
|
197
|
+
"",
|
|
198
|
+
"",
|
|
199
|
+
"def scenes_staging(team_id: str) -> list[SceneSpec]:",
|
|
200
|
+
' """Every scene that stages a team, by its id with or without a version."""',
|
|
201
|
+
' wanted = team_id.split(":")[0]',
|
|
202
|
+
" return [",
|
|
203
|
+
" scene",
|
|
204
|
+
" for scene in SCENE_CATALOGUE.values()",
|
|
205
|
+
' if scene.team and scene.team.split(":")[0] == wanted',
|
|
206
|
+
" ]",
|
|
207
|
+
"",
|
|
208
|
+
]
|
|
209
|
+
)
|
|
210
|
+
return "\n".join(lines)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
214
|
+
"""Generate the TypeScript scene catalogue."""
|
|
215
|
+
lines = [
|
|
216
|
+
"/*",
|
|
217
|
+
" * Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
218
|
+
" * Distributed under the terms of the Modified BSD License.",
|
|
219
|
+
" */",
|
|
220
|
+
"",
|
|
221
|
+
"/**",
|
|
222
|
+
*[f" * {line}".rstrip() for line in ABOUT],
|
|
223
|
+
" *",
|
|
224
|
+
*[f" * {line}" for line in HEADER],
|
|
225
|
+
" */",
|
|
226
|
+
"",
|
|
227
|
+
"import type { SceneSpec } from '../types/scenes';",
|
|
228
|
+
"",
|
|
229
|
+
]
|
|
230
|
+
for spec in specs:
|
|
231
|
+
body = json.dumps(_typescript_value(spec), indent=2, ensure_ascii=False)
|
|
232
|
+
lines.extend([f"export const {_const_name(spec)}: SceneSpec = {body};", ""])
|
|
233
|
+
lines.append("export const SCENE_CATALOGUE: Record<string, SceneSpec> = {")
|
|
234
|
+
for spec in specs:
|
|
235
|
+
lines.append(f" {json.dumps(spec['id'])}: {_const_name(spec)},")
|
|
236
|
+
lines.extend(
|
|
237
|
+
[
|
|
238
|
+
"};",
|
|
239
|
+
"",
|
|
240
|
+
"/** A scene, by `id` or `id:version`, or undefined. */",
|
|
241
|
+
"export function getSceneSpec(ref: string): SceneSpec | undefined {",
|
|
242
|
+
" // Own entries only: `constructor` and `toString` are not scenes.",
|
|
243
|
+
" const own = (id: string): SceneSpec | undefined =>",
|
|
244
|
+
" Object.prototype.hasOwnProperty.call(SCENE_CATALOGUE, id)",
|
|
245
|
+
" ? SCENE_CATALOGUE[id]",
|
|
246
|
+
" : undefined;",
|
|
247
|
+
" const at = ref.lastIndexOf(':');",
|
|
248
|
+
" return (",
|
|
249
|
+
" own(ref) ??",
|
|
250
|
+
" (at > 0 && ref.slice(at + 1).includes('.')",
|
|
251
|
+
" ? own(ref.slice(0, at))",
|
|
252
|
+
" : undefined)",
|
|
253
|
+
" );",
|
|
254
|
+
"}",
|
|
255
|
+
"",
|
|
256
|
+
"/** Every scene of the catalogue, in the catalogue's order, or those carrying a tag. */",
|
|
257
|
+
"export function listSceneSpecs(tag?: string): SceneSpec[] {",
|
|
258
|
+
" return Object.values(SCENE_CATALOGUE).filter(",
|
|
259
|
+
" scene => tag === undefined || scene.tags.includes(tag),",
|
|
260
|
+
" );",
|
|
261
|
+
"}",
|
|
262
|
+
"",
|
|
263
|
+
"/** Every scene that stages a team, by its id with or without a version. */",
|
|
264
|
+
"export function scenesStaging(teamId: string): SceneSpec[] {",
|
|
265
|
+
" const wanted = teamId.split(':')[0];",
|
|
266
|
+
" return Object.values(SCENE_CATALOGUE).filter(",
|
|
267
|
+
" scene => scene.team !== '' && scene.team.split(':')[0] === wanted,",
|
|
268
|
+
" );",
|
|
269
|
+
"}",
|
|
270
|
+
"",
|
|
271
|
+
]
|
|
272
|
+
)
|
|
273
|
+
return "\n".join(lines)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def main() -> None:
|
|
277
|
+
"""Generate the scene catalogue."""
|
|
278
|
+
parser = argparse.ArgumentParser(
|
|
279
|
+
description="Generate Python and TypeScript code from YAML scene specifications"
|
|
280
|
+
)
|
|
281
|
+
parser.add_argument("--specs-dir", type=Path, required=True)
|
|
282
|
+
parser.add_argument("--python-output", type=Path, required=True)
|
|
283
|
+
parser.add_argument("--typescript-output", type=Path, required=True)
|
|
284
|
+
args = parser.parse_args()
|
|
285
|
+
|
|
286
|
+
if not args.specs_dir.exists():
|
|
287
|
+
print(f"Error: Specs directory not found: {args.specs_dir}", file=sys.stderr)
|
|
288
|
+
sys.exit(1)
|
|
289
|
+
specs = load_specs(args.specs_dir)
|
|
290
|
+
for path, text in (
|
|
291
|
+
(args.python_output, generate_python_code(specs)),
|
|
292
|
+
(args.typescript_output, generate_typescript_code(specs)),
|
|
293
|
+
):
|
|
294
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
295
|
+
path.write_text(text)
|
|
296
|
+
print(f"Generated: {path}")
|
|
297
|
+
print(f"\n✓ Successfully generated code from {len(specs)} scene specs")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
if __name__ == "__main__":
|
|
301
|
+
main()
|
|
@@ -151,6 +151,8 @@ def _generate_team_agent_py(agent: Dict[str, Any]) -> str:
|
|
|
151
151
|
# emitted only when said, as a team of agents says none of them.
|
|
152
152
|
if agent.get("app"):
|
|
153
153
|
lines.append(f' app="{agent["app"]}",')
|
|
154
|
+
if agent.get("server"):
|
|
155
|
+
lines.append(f' server="{agent["server"]}",')
|
|
154
156
|
if agent.get("runs_in"):
|
|
155
157
|
lines.append(f' runs_in="{agent["runs_in"]}",')
|
|
156
158
|
links = agent.get("talks_to") or []
|
|
@@ -220,6 +222,7 @@ def _generate_team_agent_ts(agent: Dict[str, Any]) -> str:
|
|
|
220
222
|
links = agent.get("talks_to") or []
|
|
221
223
|
application = (
|
|
222
224
|
(f"\n app: '{agent['app']}'," if agent.get("app") else "")
|
|
225
|
+
+ (f"\n server: '{agent['server']}'," if agent.get("server") else "")
|
|
223
226
|
+ (f"\n runsIn: '{agent['runs_in']}'," if agent.get("runs_in") else "")
|
|
224
227
|
+ (
|
|
225
228
|
"\n talksTo: ["
|
|
@@ -484,10 +487,14 @@ from agent_runtimes.types import (
|
|
|
484
487
|
# `shared` by default: routing only makes sense if the member
|
|
485
488
|
# receiving the work can see what was already said.
|
|
486
489
|
context = spec.get("context") or {}
|
|
490
|
+
# The Frames of its shared context (LOOP A-04): written only when
|
|
491
|
+
# it names some, as the parser reads it.
|
|
492
|
+
frames = context.get("frames") or []
|
|
487
493
|
code += (
|
|
488
494
|
" context=TeamContextSpec("
|
|
489
495
|
f'sharing="{context.get("sharing", "shared")}"'
|
|
490
|
-
")
|
|
496
|
+
+ (f", frames={_fmt_list(frames)}" if frames else "")
|
|
497
|
+
+ "),\n"
|
|
491
498
|
)
|
|
492
499
|
code += f" validation={validation_code},\n"
|
|
493
500
|
code += f" agents={agents_code},\n"
|
|
@@ -778,7 +785,13 @@ import type {{ TeamSpec }} from '{types_import_path}';
|
|
|
778
785
|
" },\n"
|
|
779
786
|
)
|
|
780
787
|
context = spec.get("context") or {}
|
|
781
|
-
|
|
788
|
+
frames = context.get("frames") or []
|
|
789
|
+
frames_ts = (
|
|
790
|
+
", frames: [" + ", ".join(f"'{frame}'" for frame in frames) + "]"
|
|
791
|
+
if frames
|
|
792
|
+
else ""
|
|
793
|
+
)
|
|
794
|
+
code += f" context: {{ sharing: '{context.get('sharing', 'shared')}'{frames_ts} }},\n"
|
|
782
795
|
code += f" validation: {validation_ts},\n"
|
|
783
796
|
code += f" agents: {agents_ts},\n"
|
|
784
797
|
if rr_ts is not None:
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A JSON value as a tree: objects and arrays fold and unfold, each value
|
|
3
|
-
* coloured by its type (theme colours, light and dark), long strings cut
|
|
4
|
-
* until asked for whole, and the whole value copied as JSON.
|
|
5
|
-
*
|
|
6
|
-
* @module components/inspector/JsonTree
|
|
7
|
-
*/
|
|
8
|
-
import type { JSX } from 'react';
|
|
9
|
-
export type JsonTreeProps = {
|
|
10
|
-
value: unknown;
|
|
11
|
-
/** The root's name, shown as its key. */
|
|
12
|
-
name?: string;
|
|
13
|
-
/** How many levels open at first. */
|
|
14
|
-
expandDepth?: number;
|
|
15
|
-
/** Offer *Copy JSON* above the tree. */
|
|
16
|
-
copyable?: boolean;
|
|
17
|
-
/** Longer strings are cut until clicked. */
|
|
18
|
-
stringLimit?: number;
|
|
19
|
-
};
|
|
20
|
-
/** A value's type, as the tree colours it. */
|
|
21
|
-
export declare function jsonType(value: unknown): 'string' | 'number' | 'boolean' | 'null' | 'array' | 'object' | 'undefined';
|
|
22
|
-
/** A JSON value, as a tree. */
|
|
23
|
-
export declare function JsonTree({ value, name, expandDepth, copyable, stringLimit, }: JsonTreeProps): JSX.Element;
|
|
24
|
-
export default JsonTree;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { Button, Text } from '@primer/react';
|
|
4
|
-
import { Box } from '@datalayer/primer-addons';
|
|
5
|
-
import { CopyIcon, CheckIcon } from '@primer/octicons-react';
|
|
6
|
-
const MONO = 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace';
|
|
7
|
-
/** A value's type, as the tree colours it. */
|
|
8
|
-
export function jsonType(value) {
|
|
9
|
-
if (value === null) {
|
|
10
|
-
return 'null';
|
|
11
|
-
}
|
|
12
|
-
if (Array.isArray(value)) {
|
|
13
|
-
return 'array';
|
|
14
|
-
}
|
|
15
|
-
const type = typeof value;
|
|
16
|
-
if (type === 'string' || type === 'number' || type === 'boolean') {
|
|
17
|
-
return type;
|
|
18
|
-
}
|
|
19
|
-
if (type === 'bigint') {
|
|
20
|
-
return 'number';
|
|
21
|
-
}
|
|
22
|
-
return type === 'undefined' ? 'undefined' : 'object';
|
|
23
|
-
}
|
|
24
|
-
const COLOURS = {
|
|
25
|
-
string: 'success.fg',
|
|
26
|
-
number: 'accent.fg',
|
|
27
|
-
boolean: 'done.fg',
|
|
28
|
-
null: 'fg.muted',
|
|
29
|
-
undefined: 'fg.muted',
|
|
30
|
-
};
|
|
31
|
-
function Leaf({ value, stringLimit, }) {
|
|
32
|
-
const type = jsonType(value);
|
|
33
|
-
const [whole, setWhole] = useState(false);
|
|
34
|
-
if (type === 'string') {
|
|
35
|
-
const text = value;
|
|
36
|
-
const cut = !whole && text.length > stringLimit;
|
|
37
|
-
return (_jsxs(Text, { "data-json-type": "string", sx: {
|
|
38
|
-
color: COLOURS.string,
|
|
39
|
-
whiteSpace: 'pre-wrap',
|
|
40
|
-
wordBreak: 'break-word',
|
|
41
|
-
}, children: [JSON.stringify(cut ? text.slice(0, stringLimit) : text), cut && (_jsxs(Box, { as: "button", type: "button", onClick: () => setWhole(true), sx: {
|
|
42
|
-
ml: 1,
|
|
43
|
-
p: 0,
|
|
44
|
-
border: 0,
|
|
45
|
-
bg: 'transparent',
|
|
46
|
-
color: 'accent.fg',
|
|
47
|
-
cursor: 'pointer',
|
|
48
|
-
font: 'inherit',
|
|
49
|
-
}, children: ["\u2026 ", text.length - stringLimit, " more"] }))] }));
|
|
50
|
-
}
|
|
51
|
-
return (_jsx(Text, { "data-json-type": type, sx: { color: COLOURS[type] ?? 'fg.default' }, children: type === 'undefined' ? 'undefined' : String(value) }));
|
|
52
|
-
}
|
|
53
|
-
function Node({ name, value, depth, expandDepth, stringLimit, }) {
|
|
54
|
-
const type = jsonType(value);
|
|
55
|
-
const branch = type === 'array' || type === 'object';
|
|
56
|
-
const [open, setOpen] = useState(depth < expandDepth);
|
|
57
|
-
const key = name !== undefined ? (_jsxs(Text, { sx: { color: 'fg.default', fontWeight: 600 }, children: [name, ": "] })) : null;
|
|
58
|
-
if (!branch) {
|
|
59
|
-
return (_jsxs(Box, { role: "treeitem", "data-json-node": "", sx: { pl: 3 }, children: [key, _jsx(Leaf, { value: value, stringLimit: stringLimit })] }));
|
|
60
|
-
}
|
|
61
|
-
const items = type === 'array'
|
|
62
|
-
? value.map((item, index) => [String(index), item])
|
|
63
|
-
: Object.entries(value);
|
|
64
|
-
const [openMark, closeMark] = type === 'array' ? ['[', ']'] : ['{', '}'];
|
|
65
|
-
return (_jsxs(Box, { role: "treeitem", "aria-expanded": open, "data-json-node": "", sx: { pl: depth ? 3 : 0 }, children: [_jsxs(Box, { as: "button", type: "button", onClick: () => setOpen(!open), "aria-label": `${open ? 'Fold' : 'Unfold'} ${name ?? 'the value'}`, "data-json-toggle": "", sx: {
|
|
66
|
-
p: 0,
|
|
67
|
-
border: 0,
|
|
68
|
-
bg: 'transparent',
|
|
69
|
-
color: 'inherit',
|
|
70
|
-
font: 'inherit',
|
|
71
|
-
cursor: 'pointer',
|
|
72
|
-
textAlign: 'left',
|
|
73
|
-
}, children: [_jsx(Text, { sx: { color: 'fg.muted', display: 'inline-block', width: '1em' }, children: open ? '▾' : '▸' }), key, _jsx(Text, { sx: { color: 'fg.muted' }, children: open
|
|
74
|
-
? openMark
|
|
75
|
-
: `${openMark} ${items.length} ${type === 'array' ? (items.length === 1 ? 'item' : 'items') : items.length === 1 ? 'key' : 'keys'} ${closeMark}` })] }), open && (_jsxs(Box, { role: "group", children: [items.map(([itemName, item]) => (_jsx(Node, { name: itemName, value: item, depth: depth + 1, expandDepth: expandDepth, stringLimit: stringLimit }, itemName))), _jsx(Text, { sx: { color: 'fg.muted', pl: 3 }, children: closeMark })] }))] }));
|
|
76
|
-
}
|
|
77
|
-
/** A JSON value, as a tree. */
|
|
78
|
-
export function JsonTree({ value, name, expandDepth = 1, copyable = true, stringLimit = 240, }) {
|
|
79
|
-
const [copied, setCopied] = useState(false);
|
|
80
|
-
const copy = () => {
|
|
81
|
-
const text = JSON.stringify(value, null, 2) ?? 'undefined';
|
|
82
|
-
void navigator.clipboard?.writeText(text).then(() => {
|
|
83
|
-
setCopied(true);
|
|
84
|
-
setTimeout(() => setCopied(false), 1500);
|
|
85
|
-
}, () => undefined);
|
|
86
|
-
};
|
|
87
|
-
return (_jsxs(Box, { "data-json-tree": "", sx: { fontFamily: MONO, fontSize: 0, lineHeight: 1.6, minWidth: 0 }, children: [copyable && (_jsx(Button, { size: "small", variant: "invisible", leadingVisual: copied ? CheckIcon : CopyIcon, onClick: copy, "data-json-copy": "", sx: { float: 'right' }, children: copied ? 'Copied' : 'Copy JSON' })), _jsx(Box, { role: "tree", "aria-label": name ?? 'JSON', children: _jsx(Node, { name: name, value: value, depth: 0, expandDepth: expandDepth, stringLimit: stringLimit }) })] }));
|
|
88
|
-
}
|
|
89
|
-
export default JsonTree;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The A2A traffic between two agents, as the Agent Inspector records it: a
|
|
3
|
-
* fetch that hands every request on and records what went (the JSON-RPC
|
|
4
|
-
* request: its method, its message) and what came back — a JSON-RPC
|
|
5
|
-
* response, or each server-sent event of a stream (the task, its status
|
|
6
|
-
* updates, its artifact updates, a message) — with its task, context and
|
|
7
|
-
* state, whole.
|
|
8
|
-
*
|
|
9
|
-
* It sits under the A2A SDK (`ClientFactory`'s `JsonRpcTransportFactory`
|
|
10
|
-
* takes a `fetchImpl`): the SDK reads the response it is handed, and the
|
|
11
|
-
* inspector reads a copy of its body. Nothing it records changes what the
|
|
12
|
-
* SDK sees, and a key sent as a header is never recorded.
|
|
13
|
-
*
|
|
14
|
-
* A peer served by agent-runtimes tells each tool call of its agent on a
|
|
15
|
-
* `working` status (`{tool_call}`, then `{tool_result}`): each call is
|
|
16
|
-
* recorded besides as one entry of its own, the peer's, from its start to
|
|
17
|
-
* its end, linked to the two statuses — an MCP call, a skill, or a tool its
|
|
18
|
-
* runtime runs, by who claims its name. The text chunks of one artifact are
|
|
19
|
-
* one entry, their count kept.
|
|
20
|
-
*
|
|
21
|
-
* {@link linkA2APeerEvent} attaches what the asker made of an event (an
|
|
22
|
-
* `A2APeerEvent`) to the entry it came from.
|
|
23
|
-
*
|
|
24
|
-
* @module components/inspector/a2aInspect
|
|
25
|
-
*/
|
|
26
|
-
import type { A2APeerEvent } from '../../runtimes/browser/a2aPeer';
|
|
27
|
-
import { type AgentInspectorEntry, type AgentInspectorSink, type ToolClass } from './agentInspector';
|
|
28
|
-
export type A2AInspectOptions = {
|
|
29
|
-
sink: AgentInspectorSink;
|
|
30
|
-
/** The agent that asks: `Sales`. */
|
|
31
|
-
asker: string;
|
|
32
|
-
/** The agent asked: `Accounting`. */
|
|
33
|
-
peer: string;
|
|
34
|
-
/**
|
|
35
|
-
* Where a peer's tool call comes from — an MCP server's, a skill, … — and
|
|
36
|
-
* its mark (`classifyToolCall`, or a team's `teamToolClassifier`).
|
|
37
|
-
* Without it, a tool the peer's runtime runs (`backend-tool`).
|
|
38
|
-
*/
|
|
39
|
-
classifyTool?: (name: string, args?: Record<string, unknown>) => ToolClass;
|
|
40
|
-
};
|
|
41
|
-
/** The text of an A2A 1.0 message's or artifact's parts, as JSON carries them. */
|
|
42
|
-
export declare function partsText(parts: unknown): string;
|
|
43
|
-
/** `TASK_STATE_WORKING` is `working`. */
|
|
44
|
-
export declare function stateWord(state: unknown): string;
|
|
45
|
-
/**
|
|
46
|
-
* A fetch that records the A2A traffic it carries into `options.sink`, and
|
|
47
|
-
* hands the SDK what the network gave.
|
|
48
|
-
*/
|
|
49
|
-
export declare function inspectA2AFetch(base: typeof globalThis.fetch, options: A2AInspectOptions): typeof globalThis.fetch;
|
|
50
|
-
/**
|
|
51
|
-
* The linker the A2A capture adds to its sink: an `A2APeerEvent` noted
|
|
52
|
-
* (`sink.note`) is attached to the entry it came from.
|
|
53
|
-
*/
|
|
54
|
-
export declare function a2aEventLinker(event: unknown): ((entry: AgentInspectorEntry) => boolean) | undefined;
|
|
55
|
-
/** Attach what the asker made of an event to the A2A entry it came from. */
|
|
56
|
-
export declare function linkA2APeerEvent(sink: AgentInspectorSink, event: A2APeerEvent): void;
|