@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.
Files changed (57) hide show
  1. package/lib/chat/base/ChatBase.js +8 -3
  2. package/lib/chat/index.d.ts +1 -0
  3. package/lib/chat/index.js +3 -0
  4. package/lib/chat/marks/SpecMark.d.ts +46 -0
  5. package/lib/chat/marks/SpecMark.js +90 -0
  6. package/lib/chat/marks/iconRef.d.ts +29 -0
  7. package/lib/chat/marks/iconRef.js +40 -0
  8. package/lib/chat/marks/index.d.ts +8 -0
  9. package/lib/chat/marks/index.js +12 -0
  10. package/lib/chat/marks/toolMarks.d.ts +43 -0
  11. package/lib/chat/marks/toolMarks.js +110 -0
  12. package/lib/chat/messages/ChatMessageList.d.ts +8 -1
  13. package/lib/chat/messages/ChatMessageList.js +11 -18
  14. package/lib/chat/parts/ToolPart.js +6 -1
  15. package/lib/chat/prompt/InputPrompt.d.ts +4 -1
  16. package/lib/chat/prompt/InputPrompt.js +2 -2
  17. package/lib/chat/prompt/menus/SkillsMenu.js +2 -1
  18. package/lib/chat/prompt/menus/ToolsMenu.d.ts +3 -1
  19. package/lib/chat/prompt/menus/ToolsMenu.js +25 -9
  20. package/lib/chat/tools/ToolCallDisplay.d.ts +8 -1
  21. package/lib/chat/tools/ToolCallDisplay.js +3 -2
  22. package/lib/examples/AgentStrategyExample.d.ts +12 -0
  23. package/lib/examples/{AgentLoopExample.js → AgentStrategyExample.js} +62 -62
  24. package/lib/examples/example-selector.js +1 -1
  25. package/lib/examples/index.d.ts +1 -1
  26. package/lib/examples/index.js +1 -1
  27. package/lib/examples/main.js +3 -3
  28. package/lib/loop/plugins/chat/ChatView.js +7 -5
  29. package/lib/specs/actions.js +56 -0
  30. package/lib/specs/frontendTools.js +6 -6
  31. package/lib/specs/index.d.ts +1 -1
  32. package/lib/specs/index.js +1 -1
  33. package/lib/specs/mcpServers.d.ts +1 -0
  34. package/lib/specs/mcpServers.js +39 -16
  35. package/lib/specs/skills.js +7 -7
  36. package/lib/specs/strategies.d.ts +34 -0
  37. package/lib/specs/{loops.js → strategies.js} +30 -30
  38. package/lib/specs/tools.js +21 -21
  39. package/lib/types/chat.d.ts +4 -0
  40. package/lib/types/index.d.ts +1 -1
  41. package/lib/types/index.js +1 -1
  42. package/lib/types/mcp.d.ts +2 -2
  43. package/lib/types/skills.d.ts +2 -2
  44. package/lib/types/{loops.d.ts → strategies.d.ts} +17 -17
  45. package/lib/types/tools.d.ts +4 -4
  46. package/package.json +1 -1
  47. package/scripts/codegen/agentspecs_clone.py +5 -3
  48. package/scripts/codegen/generate_agents.py +1 -1
  49. package/scripts/codegen/generate_frontend_tools.py +2 -0
  50. package/scripts/codegen/generate_mcp_servers.py +2 -0
  51. package/scripts/codegen/generate_skills.py +2 -0
  52. package/scripts/codegen/{generate_loops.py → generate_strategies.py} +81 -81
  53. package/scripts/codegen/generate_tools.py +2 -0
  54. package/scripts/codegen/marks.py +33 -0
  55. package/lib/examples/AgentLoopExample.d.ts +0 -12
  56. package/lib/specs/loops.d.ts +0 -34
  57. /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 execution loop.
2
+ * How the human participates in (or around) an agent reasoning strategy.
3
3
  */
4
- export interface LoopHuman {
4
+ export interface StrategyHuman {
5
5
  /** Human interaction pattern: none, initiate, approve, feedback, or tool */
6
6
  mode: string;
7
- /** Whether the loop pauses for human approval before sensitive actions */
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 execution loop stops iterating.
15
+ * When and how an agent reasoning strategy stops iterating.
16
16
  */
17
- export interface LoopTermination {
18
- /** Maximum iterations before the loop is stopped */
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 loop as failed */
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 execution loop.
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 LoopSpec {
36
- /** Unique loop identifier (e.g., 'data-analysis') */
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 loop */
40
+ /** Display name for the strategy */
41
41
  name: string;
42
- /** Loop description */
42
+ /** Strategy description */
43
43
  description: string;
44
- /** Default goal/objective the loop works toward */
44
+ /** Default goal/objective the strategy works toward */
45
45
  objective: string;
46
- /** Loop strategy family */
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?: LoopTermination;
53
+ termination?: StrategyTermination;
54
54
  /** Human-in-the-loop participation settings */
55
- human?: LoopHuman;
56
- /** Where loop state lives between iterations */
55
+ human?: StrategyHuman;
56
+ /** Where strategy state lives between iterations */
57
57
  stateBackends: string[];
58
58
  /** Categorization tags */
59
59
  tags: string[];
@@ -33,9 +33,9 @@ export interface ToolSpec {
33
33
  requiresApproval?: boolean;
34
34
  /** Runtime binding metadata */
35
35
  runtime: ToolRuntimeSpec;
36
- /** Icon identifier */
36
+ /** The icon, `<package>:<name>` — `@datalayer/icons-react:odoo` (agentspecs.marks; see `SpecMark`) */
37
37
  icon?: string;
38
- /** Emoji identifier */
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
- /** Icon identifier */
81
+ /** The icon, `<package>:<name>` — `@datalayer/icons-react:odoo` (agentspecs.marks; see `SpecMark`) */
82
82
  icon?: string;
83
- /** Emoji identifier */
83
+ /** Drawn where there is no icon */
84
84
  emoji?: string;
85
85
  }
86
86
  import type { ReactNode } from 'react';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/agent-runtimes",
3
- "version": "1.3.54",
3
+ "version": "1.3.56",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",
@@ -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
- if not (root / "agentspecs" / module).exists():
32
+ package = root / "agentspecs"
33
+ if not (package / module).exists() and not (package / f"{module}.py").exists():
33
34
  raise SystemExit(
34
- f"Error: {root / 'agentspecs' / module} not found — Frames and Cogs "
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 './loops';
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 loop specifications.
6
+ Generate Python and TypeScript code from YAML strategy specifications.
7
7
 
8
- A *loop* describes how an agent progresses from one decision to the next: the
9
- control cycle (observe/think/act/evaluate), the objective it works toward, the
10
- constraints and success criteria that bound it, where state lives between
11
- iterations, how the human participates, and when the loop terminates.
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 generate_loops.py \\
15
- --specs-dir agentspecs/agentspecs/loops \\
16
- --python-output agent_runtimes/specs/loops.py \\
17
- --typescript-output src/specs/loops.ts
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(loop_id: str) -> str:
30
- """Convert a loop ID to a constant name (e.g., 'ooda' -> 'OODA_LOOP')."""
31
- return f"{loop_id.upper().replace('-', '_')}_LOOP"
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(loop_id: str) -> str:
35
- """Convert a loop ID to an enum member name (e.g., 'data-analysis' -> 'DATA_ANALYSIS')."""
36
- return loop_id.upper().replace("-", "_")
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 load_loop_specs(specs_dir: Path) -> list[dict[str, Any]]:
69
- """Load all loop YAML specifications from a directory."""
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 loop specifications."""
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
- "Loop Catalog.",
85
+ "Strategy Catalog.",
86
86
  "",
87
- "Predefined agent execution-loop specifications that can be used by agents.",
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 LoopHuman, LoopSpec, LoopTermination",
96
+ "from agent_runtimes.types import StrategyHuman, StrategySpec, StrategyTermination",
97
97
  "",
98
98
  "",
99
99
  "# " + "=" * 76,
100
- "# Loops Enum",
100
+ "# Strategies Enum",
101
101
  "# " + "=" * 76,
102
102
  "",
103
103
  "",
104
- "class Loops(str, Enum):",
105
- ' """Enumeration of available agent execution loops."""',
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
- "# Loop Definitions",
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} = LoopSpec(")
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=LoopTermination(")
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=LoopHuman(")
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
- "# Loop Catalog",
182
+ "# Strategy Catalog",
183
183
  "# " + "=" * 76,
184
184
  "",
185
- "LOOP_CATALOGUE: dict[str, LoopSpec] = {",
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
- 'DEFAULT_LOOP: str = "data-analysis"',
198
+ 'DEFAULT_STRATEGY: str = "data-analysis"',
199
199
  "",
200
200
  "",
201
- "def get_loop(loop_id: str) -> Optional[LoopSpec]:",
201
+ "def get_strategy(strategy_id: str) -> Optional[StrategySpec]:",
202
202
  ' """',
203
- " Get a loop specification by ID (accepts both bare and versioned refs).",
203
+ " Get a strategy specification by ID (accepts both bare and versioned refs).",
204
204
  "",
205
205
  " Args:",
206
- " loop_id: The unique identifier of the loop.",
206
+ " strategy_id: The unique identifier of the strategy.",
207
207
  "",
208
208
  " Returns:",
209
- " The LoopSpec, or None if not found.",
209
+ " The StrategySpec, or None if not found.",
210
210
  ' """',
211
- " loop = LOOP_CATALOGUE.get(loop_id)",
212
- " if loop is not None:",
213
- " return loop",
214
- " base, _, ver = loop_id.rpartition(':')",
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 LOOP_CATALOGUE.get(base)",
216
+ " return STRATEGY_CATALOGUE.get(base)",
217
217
  " return None",
218
218
  "",
219
219
  "",
220
- "def get_default_loop() -> Optional[LoopSpec]:",
220
+ "def get_default_strategy() -> Optional[StrategySpec]:",
221
221
  ' """',
222
- " Get the default loop.",
222
+ " Get the default strategy.",
223
223
  "",
224
224
  " Returns:",
225
- " The default LoopSpec, or None if no default is set.",
225
+ " The default StrategySpec, or None if no default is set.",
226
226
  ' """',
227
- " return LOOP_CATALOGUE.get(DEFAULT_LOOP)",
227
+ " return STRATEGY_CATALOGUE.get(DEFAULT_STRATEGY)",
228
228
  "",
229
229
  "",
230
- "def list_loops() -> list[LoopSpec]:",
230
+ "def list_strategies() -> list[StrategySpec]:",
231
231
  ' """',
232
- " List all available loops.",
232
+ " List all available strategies.",
233
233
  "",
234
234
  " Returns:",
235
- " List of all LoopSpec specifications.",
235
+ " List of all StrategySpec specifications.",
236
236
  ' """',
237
- " return list(LOOP_CATALOGUE.values())",
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 loop specifications."""
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
- " * Loop Catalog",
254
+ " * Strategy Catalog",
255
255
  " *",
256
- " * Predefined agent execution-loop specifications.",
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 { LoopSpec } from '../types';",
262
+ "import type { StrategySpec } from '../types';",
263
263
  "",
264
264
  "// " + "=" * 76,
265
- "// Loops Enum",
265
+ "// Strategies Enum",
266
266
  "// " + "=" * 76,
267
267
  "",
268
- "export const Loops = {",
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 LoopId = (typeof Loops)[keyof typeof Loops];",
278
+ "export type StrategyId = (typeof Strategies)[keyof typeof Strategies];",
279
279
  "",
280
280
  "// " + "=" * 76,
281
- "// Loop Definitions",
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}: LoopSpec = {{")
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
- "// Loop Catalog",
339
+ "// Strategy Catalog",
340
340
  "// " + "=" * 76,
341
341
  "",
342
- "export const LOOP_CATALOGUE: Record<string, LoopSpec> = {",
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 DEFAULT_LOOP: LoopId = Loops.DATA_ANALYSIS;",
354
+ "export const DEFAULT_STRATEGY: StrategyId = Strategies.DATA_ANALYSIS;",
355
355
  "",
356
- "function resolveLoopId(loopId: string): string {",
357
- " if (loopId in LOOP_CATALOGUE) return loopId;",
358
- " const idx = loopId.lastIndexOf(':');",
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 = loopId.slice(0, idx);",
361
- " if (base in LOOP_CATALOGUE) return base;",
360
+ " const base = strategyId.slice(0, idx);",
361
+ " if (base in STRATEGY_CATALOGUE) return base;",
362
362
  " }",
363
- " return loopId;",
363
+ " return strategyId;",
364
364
  "}",
365
365
  "",
366
366
  "/**",
367
- " * Get a loop specification by ID.",
367
+ " * Get a strategy specification by ID.",
368
368
  " */",
369
- "export function getLoop(loopId: string): LoopSpec | undefined {",
370
- " return LOOP_CATALOGUE[resolveLoopId(loopId)];",
369
+ "export function getStrategy(strategyId: string): StrategySpec | undefined {",
370
+ " return STRATEGY_CATALOGUE[resolveStrategyId(strategyId)];",
371
371
  "}",
372
372
  "",
373
373
  "/**",
374
- " * Get the default loop.",
374
+ " * Get the default strategy.",
375
375
  " */",
376
- "export function getDefaultLoop(): LoopSpec | undefined {",
377
- " return LOOP_CATALOGUE[DEFAULT_LOOP];",
376
+ "export function getDefaultStrategy(): StrategySpec | undefined {",
377
+ " return STRATEGY_CATALOGUE[DEFAULT_STRATEGY];",
378
378
  "}",
379
379
  "",
380
380
  "/**",
381
- " * List all available loops.",
381
+ " * List all available strategies.",
382
382
  " */",
383
- "export function listLoops(): LoopSpec[] {",
384
- " return Object.values(LOOP_CATALOGUE);",
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 loop specifications"
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 loop specification files",
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 loop specs from {args.specs_dir}...")
424
- specs = load_loop_specs(args.specs_dir)
425
- print(f"Loaded {len(specs)} loop specifications")
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)} loop 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;
@@ -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[];