@datalayer/agent-runtimes 1.3.54 → 1.3.56
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/base/ChatBase.js +8 -3
- package/lib/chat/index.d.ts +1 -0
- package/lib/chat/index.js +3 -0
- package/lib/chat/marks/SpecMark.d.ts +46 -0
- package/lib/chat/marks/SpecMark.js +90 -0
- package/lib/chat/marks/iconRef.d.ts +29 -0
- package/lib/chat/marks/iconRef.js +40 -0
- package/lib/chat/marks/index.d.ts +8 -0
- package/lib/chat/marks/index.js +12 -0
- package/lib/chat/marks/toolMarks.d.ts +43 -0
- package/lib/chat/marks/toolMarks.js +110 -0
- package/lib/chat/messages/ChatMessageList.d.ts +8 -1
- package/lib/chat/messages/ChatMessageList.js +11 -18
- package/lib/chat/parts/ToolPart.js +6 -1
- package/lib/chat/prompt/InputPrompt.d.ts +4 -1
- package/lib/chat/prompt/InputPrompt.js +2 -2
- package/lib/chat/prompt/menus/SkillsMenu.js +2 -1
- package/lib/chat/prompt/menus/ToolsMenu.d.ts +3 -1
- package/lib/chat/prompt/menus/ToolsMenu.js +25 -9
- package/lib/chat/tools/ToolCallDisplay.d.ts +8 -1
- package/lib/chat/tools/ToolCallDisplay.js +3 -2
- package/lib/examples/AgentStrategyExample.d.ts +12 -0
- package/lib/examples/{AgentLoopExample.js → AgentStrategyExample.js} +62 -62
- package/lib/examples/example-selector.js +1 -1
- package/lib/examples/index.d.ts +1 -1
- package/lib/examples/index.js +1 -1
- package/lib/examples/main.js +3 -3
- package/lib/loop/plugins/chat/ChatView.js +7 -5
- package/lib/specs/actions.js +56 -0
- package/lib/specs/frontendTools.js +6 -6
- package/lib/specs/index.d.ts +1 -1
- package/lib/specs/index.js +1 -1
- package/lib/specs/mcpServers.d.ts +1 -0
- package/lib/specs/mcpServers.js +39 -16
- package/lib/specs/skills.js +7 -7
- package/lib/specs/strategies.d.ts +34 -0
- package/lib/specs/{loops.js → strategies.js} +30 -30
- package/lib/specs/tools.js +21 -21
- package/lib/types/chat.d.ts +4 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.js +1 -1
- package/lib/types/mcp.d.ts +2 -2
- package/lib/types/skills.d.ts +2 -2
- package/lib/types/{loops.d.ts → strategies.d.ts} +17 -17
- package/lib/types/tools.d.ts +4 -4
- package/package.json +1 -1
- package/scripts/codegen/agentspecs_clone.py +5 -3
- package/scripts/codegen/generate_agents.py +1 -1
- package/scripts/codegen/generate_frontend_tools.py +2 -0
- package/scripts/codegen/generate_mcp_servers.py +2 -0
- package/scripts/codegen/generate_skills.py +2 -0
- package/scripts/codegen/{generate_loops.py → generate_strategies.py} +81 -81
- package/scripts/codegen/generate_tools.py +2 -0
- package/scripts/codegen/marks.py +33 -0
- package/lib/examples/AgentLoopExample.d.ts +0 -12
- package/lib/specs/loops.d.ts +0 -34
- /package/lib/types/{loops.js → strategies.js} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* How the human participates in (or around) an agent
|
|
2
|
+
* How the human participates in (or around) an agent reasoning strategy.
|
|
3
3
|
*/
|
|
4
|
-
export interface
|
|
4
|
+
export interface StrategyHuman {
|
|
5
5
|
/** Human interaction pattern: none, initiate, approve, feedback, or tool */
|
|
6
6
|
mode: string;
|
|
7
|
-
/** Whether the
|
|
7
|
+
/** Whether the strategy pauses for human approval before sensitive actions */
|
|
8
8
|
approvalRequired: boolean;
|
|
9
9
|
/** Actions that require explicit human approval */
|
|
10
10
|
approvalFor: string[];
|
|
@@ -12,48 +12,48 @@ export interface LoopHuman {
|
|
|
12
12
|
description: string;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* When and how an agent
|
|
15
|
+
* When and how an agent reasoning strategy stops iterating.
|
|
16
16
|
*/
|
|
17
|
-
export interface
|
|
18
|
-
/** Maximum iterations before the
|
|
17
|
+
export interface StrategyTermination {
|
|
18
|
+
/** Maximum iterations before the strategy is stopped */
|
|
19
19
|
maxIterations: number;
|
|
20
20
|
/** Conditions that mark the goal as reached */
|
|
21
21
|
successCriteria: string[];
|
|
22
|
-
/** Conditions that mark the
|
|
22
|
+
/** Conditions that mark the strategy as failed */
|
|
23
23
|
failureCriteria: string[];
|
|
24
24
|
/** What to do when blocked: ask-human, retry, or abort */
|
|
25
25
|
onBlocked: string;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Specification for an agent
|
|
28
|
+
* Specification for an agent reasoning strategy (a control loop).
|
|
29
29
|
*
|
|
30
30
|
* A framework-agnostic description of how an agent progresses from one
|
|
31
31
|
* decision to the next: the control cycle (observe/think/act/evaluate), the
|
|
32
32
|
* objective, constraints, where state lives, human participation, and the
|
|
33
33
|
* termination policy.
|
|
34
34
|
*/
|
|
35
|
-
export interface
|
|
36
|
-
/** Unique
|
|
35
|
+
export interface StrategySpec {
|
|
36
|
+
/** Unique strategy identifier (e.g., 'data-analysis') */
|
|
37
37
|
id: string;
|
|
38
38
|
/** Version */
|
|
39
39
|
version: string;
|
|
40
|
-
/** Display name for the
|
|
40
|
+
/** Display name for the strategy */
|
|
41
41
|
name: string;
|
|
42
|
-
/**
|
|
42
|
+
/** Strategy description */
|
|
43
43
|
description: string;
|
|
44
|
-
/** Default goal/objective the
|
|
44
|
+
/** Default goal/objective the strategy works toward */
|
|
45
45
|
objective: string;
|
|
46
|
-
/**
|
|
46
|
+
/** Strategy family */
|
|
47
47
|
strategy: string;
|
|
48
48
|
/** Ordered phase names that make up one iteration */
|
|
49
49
|
phases: string[];
|
|
50
50
|
/** Boundaries the agent must respect */
|
|
51
51
|
constraints: string[];
|
|
52
52
|
/** Termination policy */
|
|
53
|
-
termination?:
|
|
53
|
+
termination?: StrategyTermination;
|
|
54
54
|
/** Human-in-the-loop participation settings */
|
|
55
|
-
human?:
|
|
56
|
-
/** Where
|
|
55
|
+
human?: StrategyHuman;
|
|
56
|
+
/** Where strategy state lives between iterations */
|
|
57
57
|
stateBackends: string[];
|
|
58
58
|
/** Categorization tags */
|
|
59
59
|
tags: string[];
|
package/lib/types/tools.d.ts
CHANGED
|
@@ -33,9 +33,9 @@ export interface ToolSpec {
|
|
|
33
33
|
requiresApproval?: boolean;
|
|
34
34
|
/** Runtime binding metadata */
|
|
35
35
|
runtime: ToolRuntimeSpec;
|
|
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 */
|
|
39
39
|
emoji?: string;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
@@ -78,9 +78,9 @@ export interface FrontendToolSpec {
|
|
|
78
78
|
* read and edit a notebook but must never delete from it.
|
|
79
79
|
*/
|
|
80
80
|
toolset: string | string[];
|
|
81
|
-
/**
|
|
81
|
+
/** The icon, `<package>:<name>` — `@datalayer/icons-react:odoo` (agentspecs.marks; see `SpecMark`) */
|
|
82
82
|
icon?: string;
|
|
83
|
-
/**
|
|
83
|
+
/** Drawn where there is no icon */
|
|
84
84
|
emoji?: string;
|
|
85
85
|
}
|
|
86
86
|
import type { ReactNode } from 'react';
|
package/package.json
CHANGED
|
@@ -29,10 +29,12 @@ def package_root(specs_dir: Path) -> Path:
|
|
|
29
29
|
def import_from_clone(specs_dir: Path, module: str) -> ModuleType:
|
|
30
30
|
"""Import `agentspecs.<module>` from the clone `specs_dir` belongs to."""
|
|
31
31
|
root = package_root(specs_dir)
|
|
32
|
-
|
|
32
|
+
package = root / "agentspecs"
|
|
33
|
+
if not (package / module).exists() and not (package / f"{module}.py").exists():
|
|
33
34
|
raise SystemExit(
|
|
34
|
-
f"Error: {
|
|
35
|
-
"need agentspecs >= 0.0.12"
|
|
35
|
+
f"Error: agentspecs.{module} not found in {package} — the clone is "
|
|
36
|
+
"older than the generators (Frames and Cogs need agentspecs >= 0.0.12, "
|
|
37
|
+
"the marks of the catalogues >= 0.0.31)"
|
|
36
38
|
)
|
|
37
39
|
for name in [
|
|
38
40
|
name
|
|
@@ -1616,7 +1616,7 @@ export * from './teams';
|
|
|
1616
1616
|
export * from './envvars';
|
|
1617
1617
|
export * from './evals';
|
|
1618
1618
|
export * from './guardrails';
|
|
1619
|
-
export * from './
|
|
1619
|
+
export * from './strategies';
|
|
1620
1620
|
export * from './mcpServers';
|
|
1621
1621
|
export * from './memory';
|
|
1622
1622
|
export * from './models';
|
|
@@ -13,6 +13,7 @@ from pathlib import Path
|
|
|
13
13
|
from typing import Any
|
|
14
14
|
|
|
15
15
|
import yaml
|
|
16
|
+
from marks import check_marks
|
|
16
17
|
from versioning import ensure_spec_version, version_suffix
|
|
17
18
|
|
|
18
19
|
|
|
@@ -274,6 +275,7 @@ def main() -> None:
|
|
|
274
275
|
|
|
275
276
|
print(f"Loading frontend tool specs from {args.specs_dir}...")
|
|
276
277
|
specs = load_frontend_tool_specs(args.specs_dir)
|
|
278
|
+
check_marks(specs, args.specs_dir)
|
|
277
279
|
print(f"Loaded {len(specs)} frontend tool specifications")
|
|
278
280
|
|
|
279
281
|
# Generate Python code
|
|
@@ -18,6 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
20
|
import yaml
|
|
21
|
+
from marks import check_marks
|
|
21
22
|
from versioning import ensure_spec_version, version_suffix
|
|
22
23
|
|
|
23
24
|
|
|
@@ -434,6 +435,7 @@ def main():
|
|
|
434
435
|
# Load specifications
|
|
435
436
|
print(f"Loading MCP server specs from {args.specs_dir}...")
|
|
436
437
|
specs = load_mcp_specs(args.specs_dir)
|
|
438
|
+
check_marks(specs, args.specs_dir)
|
|
437
439
|
print(f"Loaded {len(specs)} MCP server specifications")
|
|
438
440
|
|
|
439
441
|
# Generate Python code
|
|
@@ -18,6 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
20
|
import yaml
|
|
21
|
+
from marks import check_marks
|
|
21
22
|
from versioning import ensure_spec_version, version_suffix
|
|
22
23
|
|
|
23
24
|
|
|
@@ -368,6 +369,7 @@ def main():
|
|
|
368
369
|
# Load specifications
|
|
369
370
|
print(f"Loading skill specs from {args.specs_dir}...")
|
|
370
371
|
specs = load_skill_specs(args.specs_dir)
|
|
372
|
+
check_marks(specs, args.specs_dir)
|
|
371
373
|
print(f"Loaded {len(specs)} skill specifications")
|
|
372
374
|
|
|
373
375
|
# Generate Python code
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
# Distributed under the terms of the Modified BSD License.
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
|
-
Generate Python and TypeScript code from YAML
|
|
6
|
+
Generate Python and TypeScript code from YAML strategy specifications.
|
|
7
7
|
|
|
8
|
-
A *
|
|
9
|
-
|
|
10
|
-
constraints and success criteria that bound it, where state lives
|
|
11
|
-
iterations, how the human participates, and when
|
|
8
|
+
A *strategy* is the control loop an agent reasons with: how it progresses from
|
|
9
|
+
one decision to the next (observe/think/act/evaluate), the objective it works
|
|
10
|
+
toward, the constraints and success criteria that bound it, where state lives
|
|
11
|
+
between iterations, how the human participates, and when it terminates.
|
|
12
12
|
|
|
13
13
|
Usage:
|
|
14
|
-
python
|
|
15
|
-
--specs-dir agentspecs/agentspecs/
|
|
16
|
-
--python-output agent_runtimes/specs/
|
|
17
|
-
--typescript-output src/specs/
|
|
14
|
+
python generate_strategies.py \\
|
|
15
|
+
--specs-dir agentspecs/agentspecs/strategies \\
|
|
16
|
+
--python-output agent_runtimes/specs/strategies.py \\
|
|
17
|
+
--typescript-output src/specs/strategies.ts
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
import argparse
|
|
@@ -26,14 +26,14 @@ import yaml
|
|
|
26
26
|
from versioning import ensure_spec_version, version_suffix
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def _make_const_name(
|
|
30
|
-
"""Convert a
|
|
31
|
-
return f"{
|
|
29
|
+
def _make_const_name(strategy_id: str) -> str:
|
|
30
|
+
"""Convert a strategy ID to a constant name (e.g., 'ooda' -> 'OODA_STRATEGY')."""
|
|
31
|
+
return f"{strategy_id.upper().replace('-', '_')}_STRATEGY"
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
def _make_enum_name(
|
|
35
|
-
"""Convert a
|
|
36
|
-
return
|
|
34
|
+
def _make_enum_name(strategy_id: str) -> str:
|
|
35
|
+
"""Convert a strategy ID to an enum member name (e.g., 'data-analysis' -> 'DATA_ANALYSIS')."""
|
|
36
|
+
return strategy_id.upper().replace("-", "_")
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def _clean(text: str) -> str:
|
|
@@ -65,8 +65,8 @@ def _ts_list(items: list) -> str:
|
|
|
65
65
|
return "[" + ", ".join(_ts_str(item) for item in items) + "]"
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def
|
|
69
|
-
"""Load all
|
|
68
|
+
def load_strategy_specs(specs_dir: Path) -> list[dict[str, Any]]:
|
|
69
|
+
"""Load all strategy YAML specifications from a directory."""
|
|
70
70
|
specs = []
|
|
71
71
|
for yaml_file in sorted(specs_dir.glob("*.yaml")):
|
|
72
72
|
with open(yaml_file) as f:
|
|
@@ -77,14 +77,14 @@ def load_loop_specs(specs_dir: Path) -> list[dict[str, Any]]:
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
80
|
-
"""Generate Python code from
|
|
80
|
+
"""Generate Python code from strategy specifications."""
|
|
81
81
|
lines = [
|
|
82
82
|
"# Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
83
83
|
"# Distributed under the terms of the Modified BSD License.",
|
|
84
84
|
'"""',
|
|
85
|
-
"
|
|
85
|
+
"Strategy Catalog.",
|
|
86
86
|
"",
|
|
87
|
-
"Predefined agent
|
|
87
|
+
"Predefined agent reasoning strategies (control loops) that agents can use.",
|
|
88
88
|
"",
|
|
89
89
|
"This file is AUTO-GENERATED from YAML specifications.",
|
|
90
90
|
"DO NOT EDIT MANUALLY - run 'make specs' to regenerate.",
|
|
@@ -93,16 +93,16 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
93
93
|
"from enum import Enum",
|
|
94
94
|
"from typing import Optional",
|
|
95
95
|
"",
|
|
96
|
-
"from agent_runtimes.types import
|
|
96
|
+
"from agent_runtimes.types import StrategyHuman, StrategySpec, StrategyTermination",
|
|
97
97
|
"",
|
|
98
98
|
"",
|
|
99
99
|
"# " + "=" * 76,
|
|
100
|
-
"#
|
|
100
|
+
"# Strategies Enum",
|
|
101
101
|
"# " + "=" * 76,
|
|
102
102
|
"",
|
|
103
103
|
"",
|
|
104
|
-
"class
|
|
105
|
-
' """Enumeration of available agent
|
|
104
|
+
"class Strategies(str, Enum):",
|
|
105
|
+
' """Enumeration of available agent reasoning strategies."""',
|
|
106
106
|
"",
|
|
107
107
|
]
|
|
108
108
|
|
|
@@ -114,7 +114,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
114
114
|
"",
|
|
115
115
|
"",
|
|
116
116
|
"# " + "=" * 76,
|
|
117
|
-
"#
|
|
117
|
+
"# Strategy Definitions",
|
|
118
118
|
"# " + "=" * 76,
|
|
119
119
|
"",
|
|
120
120
|
]
|
|
@@ -124,7 +124,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
124
124
|
version = spec["version"]
|
|
125
125
|
const_name = _make_const_name(spec["id"]) + version_suffix(version)
|
|
126
126
|
|
|
127
|
-
lines.append(f"{const_name} =
|
|
127
|
+
lines.append(f"{const_name} = StrategySpec(")
|
|
128
128
|
lines.append(f' id="{spec["id"]}",')
|
|
129
129
|
lines.append(f' version="{version}",')
|
|
130
130
|
lines.append(f' name="{spec["name"]}",')
|
|
@@ -138,7 +138,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
138
138
|
|
|
139
139
|
term = spec.get("termination")
|
|
140
140
|
if term:
|
|
141
|
-
lines.append(" termination=
|
|
141
|
+
lines.append(" termination=StrategyTermination(")
|
|
142
142
|
lines.append(
|
|
143
143
|
f" max_iterations={int(term.get('max_iterations', 10))},"
|
|
144
144
|
)
|
|
@@ -155,7 +155,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
155
155
|
|
|
156
156
|
human = spec.get("human")
|
|
157
157
|
if human:
|
|
158
|
-
lines.append(" human=
|
|
158
|
+
lines.append(" human=StrategyHuman(")
|
|
159
159
|
lines.append(f" mode={_py_str(human.get('mode', 'initiate'))},")
|
|
160
160
|
lines.append(
|
|
161
161
|
f" approval_required={bool(human.get('approval_required', False))},"
|
|
@@ -179,10 +179,10 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
179
179
|
[
|
|
180
180
|
"",
|
|
181
181
|
"# " + "=" * 76,
|
|
182
|
-
"#
|
|
182
|
+
"# Strategy Catalog",
|
|
183
183
|
"# " + "=" * 76,
|
|
184
184
|
"",
|
|
185
|
-
"
|
|
185
|
+
"STRATEGY_CATALOGUE: dict[str, StrategySpec] = {",
|
|
186
186
|
]
|
|
187
187
|
)
|
|
188
188
|
|
|
@@ -195,46 +195,46 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
195
195
|
"}",
|
|
196
196
|
"",
|
|
197
197
|
"",
|
|
198
|
-
'
|
|
198
|
+
'DEFAULT_STRATEGY: str = "data-analysis"',
|
|
199
199
|
"",
|
|
200
200
|
"",
|
|
201
|
-
"def
|
|
201
|
+
"def get_strategy(strategy_id: str) -> Optional[StrategySpec]:",
|
|
202
202
|
' """',
|
|
203
|
-
" Get a
|
|
203
|
+
" Get a strategy specification by ID (accepts both bare and versioned refs).",
|
|
204
204
|
"",
|
|
205
205
|
" Args:",
|
|
206
|
-
"
|
|
206
|
+
" strategy_id: The unique identifier of the strategy.",
|
|
207
207
|
"",
|
|
208
208
|
" Returns:",
|
|
209
|
-
" The
|
|
209
|
+
" The StrategySpec, or None if not found.",
|
|
210
210
|
' """',
|
|
211
|
-
"
|
|
212
|
-
" if
|
|
213
|
-
" return
|
|
214
|
-
" base, _, ver =
|
|
211
|
+
" strategy = STRATEGY_CATALOGUE.get(strategy_id)",
|
|
212
|
+
" if strategy is not None:",
|
|
213
|
+
" return strategy",
|
|
214
|
+
" base, _, ver = strategy_id.rpartition(':')",
|
|
215
215
|
" if base and '.' in ver:",
|
|
216
|
-
" return
|
|
216
|
+
" return STRATEGY_CATALOGUE.get(base)",
|
|
217
217
|
" return None",
|
|
218
218
|
"",
|
|
219
219
|
"",
|
|
220
|
-
"def
|
|
220
|
+
"def get_default_strategy() -> Optional[StrategySpec]:",
|
|
221
221
|
' """',
|
|
222
|
-
" Get the default
|
|
222
|
+
" Get the default strategy.",
|
|
223
223
|
"",
|
|
224
224
|
" Returns:",
|
|
225
|
-
" The default
|
|
225
|
+
" The default StrategySpec, or None if no default is set.",
|
|
226
226
|
' """',
|
|
227
|
-
" return
|
|
227
|
+
" return STRATEGY_CATALOGUE.get(DEFAULT_STRATEGY)",
|
|
228
228
|
"",
|
|
229
229
|
"",
|
|
230
|
-
"def
|
|
230
|
+
"def list_strategies() -> list[StrategySpec]:",
|
|
231
231
|
' """',
|
|
232
|
-
" List all available
|
|
232
|
+
" List all available strategies.",
|
|
233
233
|
"",
|
|
234
234
|
" Returns:",
|
|
235
|
-
" List of all
|
|
235
|
+
" List of all StrategySpec specifications.",
|
|
236
236
|
' """',
|
|
237
|
-
" return list(
|
|
237
|
+
" return list(STRATEGY_CATALOGUE.values())",
|
|
238
238
|
"",
|
|
239
239
|
]
|
|
240
240
|
)
|
|
@@ -243,7 +243,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
243
243
|
|
|
244
244
|
|
|
245
245
|
def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
246
|
-
"""Generate TypeScript code from
|
|
246
|
+
"""Generate TypeScript code from strategy specifications."""
|
|
247
247
|
lines = [
|
|
248
248
|
"/*",
|
|
249
249
|
" * Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
@@ -251,21 +251,21 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
251
251
|
" */",
|
|
252
252
|
"",
|
|
253
253
|
"/**",
|
|
254
|
-
" *
|
|
254
|
+
" * Strategy Catalog",
|
|
255
255
|
" *",
|
|
256
|
-
" * Predefined agent
|
|
256
|
+
" * Predefined agent reasoning strategies (control loops).",
|
|
257
257
|
" *",
|
|
258
258
|
" * This file is AUTO-GENERATED from YAML specifications.",
|
|
259
259
|
" * DO NOT EDIT MANUALLY - run 'make specs' to regenerate.",
|
|
260
260
|
" */",
|
|
261
261
|
"",
|
|
262
|
-
"import type {
|
|
262
|
+
"import type { StrategySpec } from '../types';",
|
|
263
263
|
"",
|
|
264
264
|
"// " + "=" * 76,
|
|
265
|
-
"//
|
|
265
|
+
"// Strategies Enum",
|
|
266
266
|
"// " + "=" * 76,
|
|
267
267
|
"",
|
|
268
|
-
"export const
|
|
268
|
+
"export const Strategies = {",
|
|
269
269
|
]
|
|
270
270
|
|
|
271
271
|
for spec in specs:
|
|
@@ -275,10 +275,10 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
275
275
|
[
|
|
276
276
|
"} as const;",
|
|
277
277
|
"",
|
|
278
|
-
"export type
|
|
278
|
+
"export type StrategyId = (typeof Strategies)[keyof typeof Strategies];",
|
|
279
279
|
"",
|
|
280
280
|
"// " + "=" * 76,
|
|
281
|
-
"//
|
|
281
|
+
"// Strategy Definitions",
|
|
282
282
|
"// " + "=" * 76,
|
|
283
283
|
"",
|
|
284
284
|
]
|
|
@@ -288,7 +288,7 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
288
288
|
version = spec["version"]
|
|
289
289
|
const_name = _make_const_name(spec["id"]) + version_suffix(version)
|
|
290
290
|
|
|
291
|
-
lines.append(f"export const {const_name}:
|
|
291
|
+
lines.append(f"export const {const_name}: StrategySpec = {{")
|
|
292
292
|
lines.append(f" id: '{spec['id']}',")
|
|
293
293
|
lines.append(f" version: '{version}',")
|
|
294
294
|
lines.append(f" name: {_ts_str(spec['name'])},")
|
|
@@ -336,10 +336,10 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
336
336
|
lines.extend(
|
|
337
337
|
[
|
|
338
338
|
"// " + "=" * 76,
|
|
339
|
-
"//
|
|
339
|
+
"// Strategy Catalog",
|
|
340
340
|
"// " + "=" * 76,
|
|
341
341
|
"",
|
|
342
|
-
"export const
|
|
342
|
+
"export const STRATEGY_CATALOGUE: Record<string, StrategySpec> = {",
|
|
343
343
|
]
|
|
344
344
|
)
|
|
345
345
|
|
|
@@ -351,37 +351,37 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
351
351
|
[
|
|
352
352
|
"};",
|
|
353
353
|
"",
|
|
354
|
-
"export const
|
|
354
|
+
"export const DEFAULT_STRATEGY: StrategyId = Strategies.DATA_ANALYSIS;",
|
|
355
355
|
"",
|
|
356
|
-
"function
|
|
357
|
-
" if (
|
|
358
|
-
" const idx =
|
|
356
|
+
"function resolveStrategyId(strategyId: string): string {",
|
|
357
|
+
" if (strategyId in STRATEGY_CATALOGUE) return strategyId;",
|
|
358
|
+
" const idx = strategyId.lastIndexOf(':');",
|
|
359
359
|
" if (idx > 0) {",
|
|
360
|
-
" const base =
|
|
361
|
-
" if (base in
|
|
360
|
+
" const base = strategyId.slice(0, idx);",
|
|
361
|
+
" if (base in STRATEGY_CATALOGUE) return base;",
|
|
362
362
|
" }",
|
|
363
|
-
" return
|
|
363
|
+
" return strategyId;",
|
|
364
364
|
"}",
|
|
365
365
|
"",
|
|
366
366
|
"/**",
|
|
367
|
-
" * Get a
|
|
367
|
+
" * Get a strategy specification by ID.",
|
|
368
368
|
" */",
|
|
369
|
-
"export function
|
|
370
|
-
" return
|
|
369
|
+
"export function getStrategy(strategyId: string): StrategySpec | undefined {",
|
|
370
|
+
" return STRATEGY_CATALOGUE[resolveStrategyId(strategyId)];",
|
|
371
371
|
"}",
|
|
372
372
|
"",
|
|
373
373
|
"/**",
|
|
374
|
-
" * Get the default
|
|
374
|
+
" * Get the default strategy.",
|
|
375
375
|
" */",
|
|
376
|
-
"export function
|
|
377
|
-
" return
|
|
376
|
+
"export function getDefaultStrategy(): StrategySpec | undefined {",
|
|
377
|
+
" return STRATEGY_CATALOGUE[DEFAULT_STRATEGY];",
|
|
378
378
|
"}",
|
|
379
379
|
"",
|
|
380
380
|
"/**",
|
|
381
|
-
" * List all available
|
|
381
|
+
" * List all available strategies.",
|
|
382
382
|
" */",
|
|
383
|
-
"export function
|
|
384
|
-
" return Object.values(
|
|
383
|
+
"export function listStrategies(): StrategySpec[] {",
|
|
384
|
+
" return Object.values(STRATEGY_CATALOGUE);",
|
|
385
385
|
"}",
|
|
386
386
|
"",
|
|
387
387
|
]
|
|
@@ -393,13 +393,13 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
393
393
|
def main():
|
|
394
394
|
"""Main entry point."""
|
|
395
395
|
parser = argparse.ArgumentParser(
|
|
396
|
-
description="Generate Python and TypeScript code from YAML
|
|
396
|
+
description="Generate Python and TypeScript code from YAML strategy specifications"
|
|
397
397
|
)
|
|
398
398
|
parser.add_argument(
|
|
399
399
|
"--specs-dir",
|
|
400
400
|
type=Path,
|
|
401
401
|
required=True,
|
|
402
|
-
help="Directory containing YAML
|
|
402
|
+
help="Directory containing YAML strategy specification files",
|
|
403
403
|
)
|
|
404
404
|
parser.add_argument(
|
|
405
405
|
"--python-output",
|
|
@@ -420,9 +420,9 @@ def main():
|
|
|
420
420
|
print(f"Error: Specs directory does not exist: {args.specs_dir}")
|
|
421
421
|
sys.exit(1)
|
|
422
422
|
|
|
423
|
-
print(f"Loading
|
|
424
|
-
specs =
|
|
425
|
-
print(f"Loaded {len(specs)}
|
|
423
|
+
print(f"Loading strategy specs from {args.specs_dir}...")
|
|
424
|
+
specs = load_strategy_specs(args.specs_dir)
|
|
425
|
+
print(f"Loaded {len(specs)} strategy specifications")
|
|
426
426
|
|
|
427
427
|
print("Generating Python code...")
|
|
428
428
|
python_code = generate_python_code(specs)
|
|
@@ -436,7 +436,7 @@ def main():
|
|
|
436
436
|
args.typescript_output.write_text(typescript_code)
|
|
437
437
|
print(f"✓ Generated {args.typescript_output}")
|
|
438
438
|
|
|
439
|
-
print(f"\n✓ Successfully generated code from {len(specs)}
|
|
439
|
+
print(f"\n✓ Successfully generated code from {len(specs)} strategy specs")
|
|
440
440
|
|
|
441
441
|
|
|
442
442
|
if __name__ == "__main__":
|
|
@@ -14,6 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
from typing import Any
|
|
15
15
|
|
|
16
16
|
import yaml
|
|
17
|
+
from marks import check_marks
|
|
17
18
|
from versioning import ensure_spec_version, version_suffix
|
|
18
19
|
|
|
19
20
|
|
|
@@ -327,6 +328,7 @@ def main() -> None:
|
|
|
327
328
|
|
|
328
329
|
print(f"Loading tool specs from {args.specs_dir}...")
|
|
329
330
|
specs = load_tool_specs(args.specs_dir)
|
|
331
|
+
check_marks(specs, args.specs_dir)
|
|
330
332
|
print(f"Loaded {len(specs)} tool specifications")
|
|
331
333
|
|
|
332
334
|
print("Generating Python code...")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
2
|
+
# Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
"""The marks of a catalogue entry, checked before anything is generated from it.
|
|
5
|
+
|
|
6
|
+
An MCP server, a skill, a tool and a frontend tool set each have an icon and an
|
|
7
|
+
emoji. The icon says its package (`@datalayer/icons-react:odoo`,
|
|
8
|
+
`@primer/octicons-react:mark-github`), so the chat loads it from the right one.
|
|
9
|
+
The rule is `agentspecs.marks`, imported from the clone the specs are read
|
|
10
|
+
from; an entry that breaks it stops the generator, naming the entry.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from agentspecs_clone import import_from_clone
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def check_marks(specs: list[dict[str, Any]], specs_dir: Path) -> None:
|
|
22
|
+
"""Refuse a catalogue with an entry whose marks are missing or malformed."""
|
|
23
|
+
marks = import_from_clone(specs_dir, "marks")
|
|
24
|
+
problems = [
|
|
25
|
+
f" {spec.get('id', '?')}: {problem}"
|
|
26
|
+
for spec in specs
|
|
27
|
+
for problem in marks.marks_problems(spec)
|
|
28
|
+
]
|
|
29
|
+
if problems:
|
|
30
|
+
raise SystemExit(
|
|
31
|
+
f"Error: the marks of {specs_dir.name} are not what agentspecs.marks says:\n"
|
|
32
|
+
+ "\n".join(problems)
|
|
33
|
+
)
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ServiceManager } from '@jupyterlab/services';
|
|
2
|
-
interface AgentLoopExampleInnerProps {
|
|
3
|
-
serviceManager?: ServiceManager.IManager;
|
|
4
|
-
}
|
|
5
|
-
export declare function AgentLoopExampleInner({ serviceManager, }: AgentLoopExampleInnerProps): import("react").JSX.Element;
|
|
6
|
-
/**
|
|
7
|
-
* Main example component with Jupyter provider wrapper.
|
|
8
|
-
*/
|
|
9
|
-
export declare function AgentLoopExample({ serviceManager, }: {
|
|
10
|
-
serviceManager?: ServiceManager.IManager;
|
|
11
|
-
}): import("react").JSX.Element;
|
|
12
|
-
export default AgentLoopExample;
|
package/lib/specs/loops.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Loop Catalog
|
|
3
|
-
*
|
|
4
|
-
* Predefined agent execution-loop specifications.
|
|
5
|
-
*
|
|
6
|
-
* This file is AUTO-GENERATED from YAML specifications.
|
|
7
|
-
* DO NOT EDIT MANUALLY - run 'make specs' to regenerate.
|
|
8
|
-
*/
|
|
9
|
-
import type { LoopSpec } from '../types';
|
|
10
|
-
export declare const Loops: {
|
|
11
|
-
readonly DATA_ANALYSIS: "data-analysis";
|
|
12
|
-
readonly HUMAN_IN_THE_LOOP: "human-in-the-loop";
|
|
13
|
-
readonly OODA: "ooda";
|
|
14
|
-
readonly PLAN_EXECUTE_CRITIC: "plan-execute-critic";
|
|
15
|
-
};
|
|
16
|
-
export type LoopId = (typeof Loops)[keyof typeof Loops];
|
|
17
|
-
export declare const DATA_ANALYSIS_LOOP_0_0_1: LoopSpec;
|
|
18
|
-
export declare const HUMAN_IN_THE_LOOP_LOOP_0_0_1: LoopSpec;
|
|
19
|
-
export declare const OODA_LOOP_0_0_1: LoopSpec;
|
|
20
|
-
export declare const PLAN_EXECUTE_CRITIC_LOOP_0_0_1: LoopSpec;
|
|
21
|
-
export declare const LOOP_CATALOGUE: Record<string, LoopSpec>;
|
|
22
|
-
export declare const DEFAULT_LOOP: LoopId;
|
|
23
|
-
/**
|
|
24
|
-
* Get a loop specification by ID.
|
|
25
|
-
*/
|
|
26
|
-
export declare function getLoop(loopId: string): LoopSpec | undefined;
|
|
27
|
-
/**
|
|
28
|
-
* Get the default loop.
|
|
29
|
-
*/
|
|
30
|
-
export declare function getDefaultLoop(): LoopSpec | undefined;
|
|
31
|
-
/**
|
|
32
|
-
* List all available loops.
|
|
33
|
-
*/
|
|
34
|
-
export declare function listLoops(): LoopSpec[];
|