@datalayer/agent-runtimes 1.3.55 → 1.3.57

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 (43) 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/loop/apps/appspec.js +17 -2
  23. package/lib/loop/plugins/chat/ChatView.js +7 -5
  24. package/lib/specs/actions.js +56 -0
  25. package/lib/specs/apps.js +239 -4
  26. package/lib/specs/appspecSchema.js +37 -1
  27. package/lib/specs/frontendTools.js +6 -6
  28. package/lib/specs/mcpServers.d.ts +1 -0
  29. package/lib/specs/mcpServers.js +39 -16
  30. package/lib/specs/skills.js +7 -7
  31. package/lib/specs/tools.js +21 -21
  32. package/lib/types/agentspecs.d.ts +11 -0
  33. package/lib/types/chat.d.ts +4 -0
  34. package/lib/types/mcp.d.ts +2 -2
  35. package/lib/types/skills.d.ts +2 -2
  36. package/lib/types/tools.d.ts +4 -4
  37. package/package.json +1 -1
  38. package/scripts/codegen/agentspecs_clone.py +5 -3
  39. package/scripts/codegen/generate_frontend_tools.py +2 -0
  40. package/scripts/codegen/generate_mcp_servers.py +2 -0
  41. package/scripts/codegen/generate_skills.py +2 -0
  42. package/scripts/codegen/generate_tools.py +2 -0
  43. package/scripts/codegen/marks.py +33 -0
@@ -10,7 +10,7 @@ export const ALPHAVANTAGE_MCP_SERVER_0_0_1 = {
10
10
  version: '0.0.1',
11
11
  name: 'Alpha Vantage',
12
12
  description: 'Financial market data and stock information',
13
- icon: 'graph',
13
+ icon: '@primer/octicons-react:graph',
14
14
  emoji: '💹',
15
15
  url: '',
16
16
  command: 'uvx',
@@ -26,7 +26,7 @@ export const CHART_MCP_SERVER_0_0_1 = {
26
26
  version: '0.0.1',
27
27
  name: 'Chart Generator',
28
28
  description: 'Generate charts and visualizations',
29
- icon: 'graph',
29
+ icon: '@datalayer/icons-react:chart-bar',
30
30
  emoji: '📊',
31
31
  url: '',
32
32
  command: 'npx',
@@ -42,7 +42,7 @@ export const DATALAYER_MCP_SERVER_0_0_1 = {
42
42
  version: '0.0.1',
43
43
  name: 'Datalayer',
44
44
  description: 'Read, edit and run Jupyter notebooks on Datalayer, with sandboxes and data',
45
- icon: 'notebook',
45
+ icon: '@datalayer/icons-react:datalayer',
46
46
  emoji: '🚀',
47
47
  url: '',
48
48
  command: 'npx',
@@ -64,7 +64,7 @@ export const EARTHDATA_MCP_SERVER_0_0_1 = {
64
64
  version: '0.0.1',
65
65
  name: 'Earthdata MCP',
66
66
  description: 'Access NASA Earthdata search and metadata capabilities',
67
- icon: 'globe',
67
+ icon: '@datalayer/icons-react:globe-americas',
68
68
  emoji: '🌍',
69
69
  url: '',
70
70
  command: 'npx',
@@ -80,7 +80,7 @@ export const EURUS_MCP_SERVER_0_0_1 = {
80
80
  version: '0.0.1',
81
81
  name: 'Eurus Climate MCP',
82
82
  description: 'Climate and reanalysis analysis tools for spatial workflows',
83
- icon: 'graph',
83
+ icon: '@primer/octicons-react:cloud',
84
84
  emoji: '🌦️',
85
85
  url: '',
86
86
  command: 'eurus-mcp',
@@ -96,7 +96,7 @@ export const FILESYSTEM_MCP_SERVER_0_0_1 = {
96
96
  version: '0.0.1',
97
97
  name: 'Filesystem',
98
98
  description: 'Local filesystem read/write operations',
99
- icon: 'file-directory',
99
+ icon: '@primer/octicons-react:file-directory',
100
100
  emoji: '📁',
101
101
  url: '',
102
102
  command: 'npx',
@@ -112,7 +112,7 @@ export const GITHUB_MCP_SERVER_0_0_1 = {
112
112
  version: '0.0.1',
113
113
  name: 'GitHub',
114
114
  description: 'GitHub repository operations (issues, PRs, code search)',
115
- icon: 'mark-github',
115
+ icon: '@datalayer/icons-react:github-mark',
116
116
  emoji: '🐙',
117
117
  url: '',
118
118
  command: 'docker',
@@ -135,7 +135,7 @@ export const GOOGLE_WORKSPACE_MCP_SERVER_0_0_1 = {
135
135
  version: '0.0.1',
136
136
  name: 'Google Workspace',
137
137
  description: 'Google Drive, Gmail, Calendar, and Docs integration',
138
- icon: 'mail',
138
+ icon: '@datalayer/icons-react:google',
139
139
  emoji: '📧',
140
140
  url: '',
141
141
  command: 'uvx',
@@ -154,7 +154,7 @@ export const HUGGINGFACE_MCP_SERVER_0_0_1 = {
154
154
  version: '0.0.1',
155
155
  name: 'Hugging Face',
156
156
  description: 'Hugging Face models, datasets, spaces, and papers access',
157
- icon: 'brain',
157
+ icon: '@primer/octicons-react:hubot',
158
158
  emoji: '🤗',
159
159
  url: '',
160
160
  command: 'npx',
@@ -176,8 +176,8 @@ export const KAGGLE_MCP_SERVER_0_0_1 = {
176
176
  version: '0.0.1',
177
177
  name: 'Kaggle',
178
178
  description: 'Kaggle datasets, models, competitions, and notebooks access',
179
- icon: 'database',
180
- emoji: '📊',
179
+ icon: '@datalayer/icons-react:kaggle',
180
+ emoji: '🏆',
181
181
  url: '',
182
182
  command: 'npx',
183
183
  args: [
@@ -193,13 +193,35 @@ export const KAGGLE_MCP_SERVER_0_0_1 = {
193
193
  tools: [],
194
194
  requiredEnvVars: ['KAGGLE_API_TOKEN:0.0.1'],
195
195
  };
196
+ export const ODOO_ACCOUNTING_MCP_SERVER_0_0_1 = {
197
+ id: 'odoo-accounting',
198
+ version: '0.0.1',
199
+ name: 'Odoo Accounting',
200
+ description: "An accountant's day in Odoo — invoices, bills, journal entries, reconciliations, bank lines and tax returns — through the Datalayer MCP server",
201
+ icon: '@datalayer/icons-react:odoo',
202
+ emoji: '🧮',
203
+ url: '',
204
+ command: 'npx',
205
+ args: [
206
+ '-y',
207
+ 'mcp-remote',
208
+ 'https://mcp.datalayer.run/mcp?only=odoo-accounting',
209
+ '--header',
210
+ 'Authorization: Bearer ${DATALAYER_API_KEY}',
211
+ ],
212
+ transport: 'stdio',
213
+ enabled: false,
214
+ isAvailable: false,
215
+ tools: [],
216
+ requiredEnvVars: ['DATALAYER_API_KEY:0.0.1'],
217
+ };
196
218
  export const ODOO_MCP_SERVER_0_0_1 = {
197
219
  id: 'odoo',
198
220
  version: '0.0.1',
199
221
  name: 'Odoo',
200
222
  description: 'Odoo ERP operations (search, invoices, journals, reconciliations)',
201
- icon: 'server',
202
- emoji: '📚',
223
+ icon: '@datalayer/icons-react:odoo',
224
+ emoji: '🧾',
203
225
  url: '',
204
226
  command: 'uvx',
205
227
  args: ['mcp-server-odoo'],
@@ -214,7 +236,7 @@ export const SALESFORCE_MCP_SERVER_0_0_1 = {
214
236
  version: '0.0.1',
215
237
  name: 'Salesforce',
216
238
  description: 'Salesforce CRM operations (queries, reports, objects, SOQL)',
217
- icon: 'briefcase',
239
+ icon: '@primer/octicons-react:briefcase',
218
240
  emoji: '☁️',
219
241
  url: '',
220
242
  command: 'npx',
@@ -233,7 +255,7 @@ export const SLACK_MCP_SERVER_0_0_1 = {
233
255
  version: '0.0.1',
234
256
  name: 'Slack',
235
257
  description: 'Slack messaging and channel operations',
236
- icon: 'comment-discussion',
258
+ icon: '@datalayer/icons-react:slack',
237
259
  emoji: '💬',
238
260
  url: '',
239
261
  command: 'npx',
@@ -253,7 +275,7 @@ export const TAVILY_MCP_SERVER_0_0_1 = {
253
275
  version: '0.0.1',
254
276
  name: 'Tavily Search',
255
277
  description: 'Web search and research capabilities via Tavily API',
256
- icon: 'search',
278
+ icon: '@primer/octicons-react:search',
257
279
  emoji: '🔍',
258
280
  url: '',
259
281
  command: 'npx',
@@ -278,6 +300,7 @@ export const MCP_SERVER_LIBRARY = {
278
300
  'google-workspace': GOOGLE_WORKSPACE_MCP_SERVER_0_0_1,
279
301
  huggingface: HUGGINGFACE_MCP_SERVER_0_0_1,
280
302
  kaggle: KAGGLE_MCP_SERVER_0_0_1,
303
+ 'odoo-accounting': ODOO_ACCOUNTING_MCP_SERVER_0_0_1,
281
304
  odoo: ODOO_MCP_SERVER_0_0_1,
282
305
  salesforce: SALESFORCE_MCP_SERVER_0_0_1,
283
306
  slack: SLACK_MCP_SERVER_0_0_1,
@@ -18,7 +18,7 @@ export const ACCOUNTING_SKILL_SPEC_0_0_1 = {
18
18
  optionalEnvVars: [],
19
19
  dependencies: ['pandas>=2.0.0'],
20
20
  tags: ['accounting', 'finance', 'reconciliation', 'close'],
21
- icon: 'book',
21
+ icon: '@primer/octicons-react:book',
22
22
  emoji: '🧮',
23
23
  enabled: false,
24
24
  };
@@ -35,7 +35,7 @@ export const CRAWL_SKILL_SPEC_0_0_1 = {
35
35
  optionalEnvVars: [],
36
36
  dependencies: ['requests>=2.31.0', 'beautifulsoup4>=4.12.0'],
37
37
  tags: ['web', 'crawl', 'scraping'],
38
- icon: 'globe',
38
+ icon: '@primer/octicons-react:globe',
39
39
  emoji: '🌐',
40
40
  enabled: true,
41
41
  };
@@ -52,7 +52,7 @@ export const EVENTS_SKILL_SPEC_0_0_1 = {
52
52
  optionalEnvVars: [],
53
53
  dependencies: ['httpx>=0.27.0'],
54
54
  tags: ['events', 'orchestration', 'automation'],
55
- icon: 'bell',
55
+ icon: '@primer/octicons-react:calendar',
56
56
  emoji: '📅',
57
57
  enabled: false,
58
58
  };
@@ -69,7 +69,7 @@ export const GITHUB_SKILL_SPEC_0_0_1 = {
69
69
  optionalEnvVars: [],
70
70
  dependencies: ['PyGithub>=2.1.0'],
71
71
  tags: ['github', 'git', 'code'],
72
- icon: 'mark-github',
72
+ icon: '@datalayer/icons-react:github-mark',
73
73
  emoji: '🐙',
74
74
  enabled: false,
75
75
  };
@@ -86,7 +86,7 @@ export const JOKES_SKILL_SPEC_0_0_1 = {
86
86
  optionalEnvVars: [],
87
87
  dependencies: [],
88
88
  tags: ['fun', 'humor', 'demo'],
89
- icon: 'smiley',
89
+ icon: '@primer/octicons-react:smiley',
90
90
  emoji: '😄',
91
91
  enabled: false,
92
92
  };
@@ -103,7 +103,7 @@ export const PDF_SKILL_SPEC_0_0_1 = {
103
103
  optionalEnvVars: [],
104
104
  dependencies: ['PyPDF2>=3.0.0', 'pdfplumber>=0.10.0'],
105
105
  tags: ['pdf', 'documents', 'extraction'],
106
- icon: 'file',
106
+ icon: '@primer/octicons-react:file',
107
107
  emoji: '📄',
108
108
  enabled: false,
109
109
  };
@@ -120,7 +120,7 @@ export const TEXT_SUMMARIZER_SKILL_SPEC_0_0_1 = {
120
120
  optionalEnvVars: [],
121
121
  dependencies: ['agent-skills>=0.0.1'],
122
122
  tags: ['nlp', 'summarization', 'text-processing'],
123
- icon: 'note',
123
+ icon: '@primer/octicons-react:note',
124
124
  emoji: '📝',
125
125
  enabled: false,
126
126
  };
@@ -20,7 +20,7 @@ export const CREATE_PLAN_TOOL_SPEC_0_0_1 = {
20
20
  package: 'agent_runtimes.examples.tools.ag_ui',
21
21
  method: 'create_plan',
22
22
  },
23
- icon: 'list-unordered',
23
+ icon: '@primer/octicons-react:list-unordered',
24
24
  emoji: '📋',
25
25
  };
26
26
  export const CURRENT_TIME_TOOL_SPEC_0_0_1 = {
@@ -38,7 +38,7 @@ export const CURRENT_TIME_TOOL_SPEC_0_0_1 = {
38
38
  package: 'agent_runtimes.examples.tools.ag_ui',
39
39
  method: 'current_time',
40
40
  },
41
- icon: 'clock',
41
+ icon: '@primer/octicons-react:clock',
42
42
  emoji: '🕒',
43
43
  };
44
44
  export const DECIDE_TOOL_SPEC_0_0_1 = {
@@ -56,7 +56,7 @@ export const DECIDE_TOOL_SPEC_0_0_1 = {
56
56
  package: 'agent_runtimes.tools.decisions',
57
57
  method: 'decide',
58
58
  },
59
- icon: 'law',
59
+ icon: '@primer/octicons-react:law',
60
60
  emoji: '⚖️',
61
61
  };
62
62
  export const DISPLAY_RECIPE_TOOL_SPEC_0_0_1 = {
@@ -74,7 +74,7 @@ export const DISPLAY_RECIPE_TOOL_SPEC_0_0_1 = {
74
74
  package: 'agent_runtimes.examples.tools.ag_ui',
75
75
  method: 'display_recipe',
76
76
  },
77
- icon: 'book',
77
+ icon: '@primer/octicons-react:book',
78
78
  emoji: '🍳',
79
79
  };
80
80
  export const EXAMPLE_CREATE_PLAN_TOOL_SPEC_0_0_1 = {
@@ -92,7 +92,7 @@ export const EXAMPLE_CREATE_PLAN_TOOL_SPEC_0_0_1 = {
92
92
  package: 'agent_runtimes.examples.tools.ag_ui',
93
93
  method: 'create_plan',
94
94
  },
95
- icon: 'list-unordered',
95
+ icon: '@primer/octicons-react:list-unordered',
96
96
  emoji: '📋',
97
97
  };
98
98
  export const EXAMPLE_CURRENT_TIME_TOOL_SPEC_0_0_1 = {
@@ -110,7 +110,7 @@ export const EXAMPLE_CURRENT_TIME_TOOL_SPEC_0_0_1 = {
110
110
  package: 'agent_runtimes.examples.tools.ag_ui',
111
111
  method: 'current_time',
112
112
  },
113
- icon: 'clock',
113
+ icon: '@primer/octicons-react:clock',
114
114
  emoji: '🕒',
115
115
  };
116
116
  export const EXAMPLE_DISPLAY_RECIPE_TOOL_SPEC_0_0_1 = {
@@ -128,7 +128,7 @@ export const EXAMPLE_DISPLAY_RECIPE_TOOL_SPEC_0_0_1 = {
128
128
  package: 'agent_runtimes.examples.tools.ag_ui',
129
129
  method: 'display_recipe',
130
130
  },
131
- icon: 'book',
131
+ icon: '@primer/octicons-react:book',
132
132
  emoji: '🍳',
133
133
  };
134
134
  export const EXAMPLE_GENERATE_HAIKU_TOOL_SPEC_0_0_1 = {
@@ -146,7 +146,7 @@ export const EXAMPLE_GENERATE_HAIKU_TOOL_SPEC_0_0_1 = {
146
146
  package: 'agent_runtimes.examples.tools.ag_ui',
147
147
  method: 'generate_haiku',
148
148
  },
149
- icon: 'pencil',
149
+ icon: '@primer/octicons-react:pencil',
150
150
  emoji: '🖋️',
151
151
  };
152
152
  export const EXAMPLE_GENERATE_TASK_STEPS_TOOL_SPEC_0_0_1 = {
@@ -164,8 +164,8 @@ export const EXAMPLE_GENERATE_TASK_STEPS_TOOL_SPEC_0_0_1 = {
164
164
  package: 'agent_runtimes.examples.tools.ag_ui',
165
165
  method: 'generate_task_steps',
166
166
  },
167
- icon: 'tasklist',
168
- emoji: '🧑‍⚖️',
167
+ icon: '@primer/octicons-react:tasklist',
168
+ emoji: '🙋',
169
169
  };
170
170
  export const EXAMPLE_GET_WEATHER_TOOL_SPEC_0_0_1 = {
171
171
  id: 'example-get-weather',
@@ -182,7 +182,7 @@ export const EXAMPLE_GET_WEATHER_TOOL_SPEC_0_0_1 = {
182
182
  package: 'agent_runtimes.examples.tools.ag_ui',
183
183
  method: 'get_weather',
184
184
  },
185
- icon: 'sun',
185
+ icon: '@primer/octicons-react:sun',
186
186
  emoji: '🌤️',
187
187
  };
188
188
  export const EXAMPLE_RENDER_A2UI_SURFACE_TOOL_SPEC_0_0_1 = {
@@ -200,7 +200,7 @@ export const EXAMPLE_RENDER_A2UI_SURFACE_TOOL_SPEC_0_0_1 = {
200
200
  package: 'agent_runtimes.examples.tools.a2ui',
201
201
  method: 'render_a2ui_surface',
202
202
  },
203
- icon: 'browser',
203
+ icon: '@primer/octicons-react:browser',
204
204
  emoji: '🎛️',
205
205
  };
206
206
  export const EXAMPLE_UPDATE_PLAN_STEP_TOOL_SPEC_0_0_1 = {
@@ -218,7 +218,7 @@ export const EXAMPLE_UPDATE_PLAN_STEP_TOOL_SPEC_0_0_1 = {
218
218
  package: 'agent_runtimes.examples.tools.ag_ui',
219
219
  method: 'update_plan_step',
220
220
  },
221
- icon: 'checklist',
221
+ icon: '@primer/octicons-react:checklist',
222
222
  emoji: '✅',
223
223
  };
224
224
  export const GENERATE_HAIKU_TOOL_SPEC_0_0_1 = {
@@ -236,7 +236,7 @@ export const GENERATE_HAIKU_TOOL_SPEC_0_0_1 = {
236
236
  package: 'agent_runtimes.examples.tools.ag_ui',
237
237
  method: 'generate_haiku',
238
238
  },
239
- icon: 'pencil',
239
+ icon: '@primer/octicons-react:pencil',
240
240
  emoji: '🖋️',
241
241
  };
242
242
  export const GENERATE_TASK_STEPS_TOOL_SPEC_0_0_1 = {
@@ -254,8 +254,8 @@ export const GENERATE_TASK_STEPS_TOOL_SPEC_0_0_1 = {
254
254
  package: 'agent_runtimes.examples.tools.ag_ui',
255
255
  method: 'generate_task_steps',
256
256
  },
257
- icon: 'tasklist',
258
- emoji: '🧑‍⚖️',
257
+ icon: '@primer/octicons-react:tasklist',
258
+ emoji: '🙋',
259
259
  };
260
260
  export const GET_WEATHER_TOOL_SPEC_0_0_1 = {
261
261
  id: 'get-weather',
@@ -272,7 +272,7 @@ export const GET_WEATHER_TOOL_SPEC_0_0_1 = {
272
272
  package: 'agent_runtimes.examples.tools.ag_ui',
273
273
  method: 'get_weather',
274
274
  },
275
- icon: 'sun',
275
+ icon: '@primer/octicons-react:sun',
276
276
  emoji: '🌤️',
277
277
  };
278
278
  export const RUNTIME_ECHO_TOOL_SPEC_0_0_1 = {
@@ -290,7 +290,7 @@ export const RUNTIME_ECHO_TOOL_SPEC_0_0_1 = {
290
290
  package: 'agent_runtimes.examples.tools',
291
291
  method: 'runtime_echo',
292
292
  },
293
- icon: 'comment',
293
+ icon: '@primer/octicons-react:comment',
294
294
  emoji: '💬',
295
295
  };
296
296
  export const RUNTIME_SEND_MAIL_TOOL_SPEC_0_0_1 = {
@@ -308,7 +308,7 @@ export const RUNTIME_SEND_MAIL_TOOL_SPEC_0_0_1 = {
308
308
  package: 'agent_runtimes.examples.tools',
309
309
  method: 'runtime_send_mail',
310
310
  },
311
- icon: 'mail',
311
+ icon: '@primer/octicons-react:mail',
312
312
  emoji: '📧',
313
313
  };
314
314
  export const RUNTIME_SENSITIVE_ECHO_TOOL_SPEC_0_0_1 = {
@@ -326,7 +326,7 @@ export const RUNTIME_SENSITIVE_ECHO_TOOL_SPEC_0_0_1 = {
326
326
  package: 'agent_runtimes.examples.tools',
327
327
  method: 'runtime_sensitive_echo',
328
328
  },
329
- icon: 'shield',
329
+ icon: '@primer/octicons-react:shield',
330
330
  emoji: '🛡️',
331
331
  };
332
332
  export const UPDATE_PLAN_STEP_TOOL_SPEC_0_0_1 = {
@@ -344,7 +344,7 @@ export const UPDATE_PLAN_STEP_TOOL_SPEC_0_0_1 = {
344
344
  package: 'agent_runtimes.examples.tools.ag_ui',
345
345
  method: 'update_plan_step',
346
346
  },
347
- icon: 'checklist',
347
+ icon: '@primer/octicons-react:checklist',
348
348
  emoji: '✅',
349
349
  };
350
350
  // ============================================================================
@@ -469,12 +469,23 @@ export interface AppTestCaseSpec {
469
469
  ask: string;
470
470
  expect: string;
471
471
  }
472
+ /**
473
+ * What was verified, and how, each in a sentence a person reads (LOOP E-14):
474
+ * what was tried live, what runs on recorded data, what is not verified yet.
475
+ */
476
+ export interface AppVerifiedSpec {
477
+ live: string[];
478
+ recorded: string[];
479
+ unverified: string[];
480
+ }
472
481
  /** How an application is verified. */
473
482
  export interface AppTestsSpec {
474
483
  /** The share of tests that has to pass for it to be ready. */
475
484
  readyAt: number;
476
485
  evalset: string;
477
486
  cases: AppTestCaseSpec[];
487
+ /** What was verified live, what runs on recorded data, what is not yet. */
488
+ verified: AppVerifiedSpec;
478
489
  }
479
490
  /** What is kept of what an application did, and for how long. */
480
491
  export interface AppRecordSpec {
@@ -276,6 +276,10 @@ export interface MCPServerConfig {
276
276
  transport?: string;
277
277
  isConfig?: boolean;
278
278
  isRunning?: boolean;
279
+ /** The icon, `<package>:<name>` (agentspecs.marks), as the runtime's catalogue says it. */
280
+ icon?: string;
281
+ /** Drawn where there is no icon. */
282
+ emoji?: string;
279
283
  }
280
284
  /**
281
285
  * Common props shared by all Chat wrapper components
@@ -39,9 +39,9 @@ export interface MCPServer {
39
39
  transport: 'stdio' | 'http';
40
40
  /** Environment variables required by this server (e.g., API keys) */
41
41
  requiredEnvVars?: string[];
42
- /** Icon identifier for the server */
42
+ /** The icon, `<package>:<name>` — `@datalayer/icons-react:odoo` (agentspecs.marks; see `SpecMark`) */
43
43
  icon?: string;
44
- /** Emoji identifier for the server */
44
+ /** Drawn where there is no icon */
45
45
  emoji?: string;
46
46
  }
47
47
  /**
@@ -107,9 +107,9 @@ export interface SkillSpec {
107
107
  dependencies?: string[];
108
108
  /** Tags for categorization */
109
109
  tags: string[];
110
- /** Icon identifier */
110
+ /** The icon, `<package>:<name>` — `@datalayer/icons-react:odoo` (agentspecs.marks; see `SpecMark`) */
111
111
  icon?: string;
112
- /** Emoji identifier */
112
+ /** Drawn where there is no icon */
113
113
  emoji?: string;
114
114
  /** Whether the skill is enabled */
115
115
  enabled: boolean;
@@ -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.55",
3
+ "version": "1.3.57",
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
@@ -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
@@ -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
+ )