@datalayer/agent-runtimes 1.5.30 → 1.5.32
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/apps/apps/checks.js +9 -0
- package/lib/runtimes/variants.d.ts +42 -37
- package/lib/runtimes/variants.js +109 -9
- package/lib/specs/envvars.d.ts +4 -0
- package/lib/specs/envvars.js +44 -0
- package/lib/specs/harnesses.d.ts +22 -0
- package/lib/specs/harnesses.js +372 -0
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/types/agentspecs.d.ts +69 -0
- package/package.json +1 -1
- package/scripts/codegen/generate_agents.py +56 -0
- package/scripts/codegen/generate_envvars.py +3 -2
- package/scripts/codegen/generate_harnesses.py +329 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
export const CLAUDE_CODE_HARNESS_0_0_1 = {
|
|
6
|
+
id: 'claude-code',
|
|
7
|
+
version: '0.0.1',
|
|
8
|
+
name: 'Claude Code',
|
|
9
|
+
description: "Anthropic's coding agent, driven over ACP through the Agent Client Protocol project's adapter on the Claude Agent SDK.",
|
|
10
|
+
enabled: true,
|
|
11
|
+
tags: ['harness'],
|
|
12
|
+
docs: 'https://github.com/agentclientprotocol/claude-agent-acp',
|
|
13
|
+
acp: {
|
|
14
|
+
command: 'npx',
|
|
15
|
+
args: ['-y', '@agentclientprotocol/claude-agent-acp@0.91.0'],
|
|
16
|
+
protocolVersion: 1,
|
|
17
|
+
loadSession: true,
|
|
18
|
+
mcp: 'lend',
|
|
19
|
+
modes: ['default', 'plan', 'acceptEdits', 'bypassPermissions'],
|
|
20
|
+
env: {},
|
|
21
|
+
},
|
|
22
|
+
requires: {
|
|
23
|
+
secrets: ['ANTHROPIC_API_KEY'],
|
|
24
|
+
binaries: ['node', 'git'],
|
|
25
|
+
},
|
|
26
|
+
terms: {
|
|
27
|
+
servedToThirdParties: 'api_key_only',
|
|
28
|
+
licence: 'Apache-2.0 (adapter); Anthropic Commercial Terms of Service (the agent)',
|
|
29
|
+
},
|
|
30
|
+
skills: {
|
|
31
|
+
format: 'skill-md',
|
|
32
|
+
dir: '.claude/skills',
|
|
33
|
+
},
|
|
34
|
+
honours: {
|
|
35
|
+
model: true,
|
|
36
|
+
subagents: false,
|
|
37
|
+
},
|
|
38
|
+
instructionsFile: 'CLAUDE.md',
|
|
39
|
+
registered: {
|
|
40
|
+
adapter: '@agentclientprotocol/claude-agent-acp',
|
|
41
|
+
version: '0.91.0',
|
|
42
|
+
licenceSource: 'https://www.npmjs.com/package/@agentclientprotocol/claude-agent-acp',
|
|
43
|
+
termsSource: 'https://code.claude.com/docs/en/legal-and-compliance',
|
|
44
|
+
credential: "An Anthropic API key (ANTHROPIC_API_KEY), the person's own",
|
|
45
|
+
binaries: 'Node 22 (engines.node >=22 on npm), git',
|
|
46
|
+
note: "Anthropic's legal page (read 2026-10-15): running Claude Code in hosted sandboxes or agent infrastructure requires the Commercial Terms; each end user authenticates with their own API key or subscription; developers may not route requests through Free, Pro or Max plan credentials on behalf of their users, nor collect or intermediate Claude.ai credentials. So only API keys are taken; a subscription login is refused. The npm package moved from @zed-industries (0.23.1, March 2026) to @agentclientprotocol.",
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
export const CODEX_HARNESS_0_0_1 = {
|
|
50
|
+
id: 'codex',
|
|
51
|
+
version: '0.0.1',
|
|
52
|
+
name: 'Codex',
|
|
53
|
+
description: "OpenAI's coding agent, driven over ACP through the Agent Client Protocol project's adapter, which ships the Codex binary it drives.",
|
|
54
|
+
enabled: true,
|
|
55
|
+
tags: ['harness'],
|
|
56
|
+
docs: 'https://github.com/agentclientprotocol/codex-acp',
|
|
57
|
+
acp: {
|
|
58
|
+
command: 'npx',
|
|
59
|
+
args: ['-y', '@agentclientprotocol/codex-acp@2.2.2'],
|
|
60
|
+
protocolVersion: 1,
|
|
61
|
+
loadSession: true,
|
|
62
|
+
mcp: 'lend',
|
|
63
|
+
modes: ['read-only', 'workspace-write', 'agent', 'agent-full-access'],
|
|
64
|
+
env: { NO_BROWSER: '1' },
|
|
65
|
+
},
|
|
66
|
+
requires: {
|
|
67
|
+
secrets: ['OPENAI_API_KEY'],
|
|
68
|
+
binaries: ['node', 'git'],
|
|
69
|
+
},
|
|
70
|
+
terms: {
|
|
71
|
+
servedToThirdParties: 'api_key_only',
|
|
72
|
+
licence: 'Apache-2.0 (adapter); OpenAI Terms of Use (the agent)',
|
|
73
|
+
},
|
|
74
|
+
skills: {
|
|
75
|
+
format: 'skill-md',
|
|
76
|
+
dir: '.agents/skills',
|
|
77
|
+
},
|
|
78
|
+
honours: {
|
|
79
|
+
model: true,
|
|
80
|
+
subagents: false,
|
|
81
|
+
},
|
|
82
|
+
instructionsFile: 'AGENTS.md',
|
|
83
|
+
registered: {
|
|
84
|
+
adapter: '@agentclientprotocol/codex-acp',
|
|
85
|
+
version: '2.2.2',
|
|
86
|
+
licenceSource: 'https://www.npmjs.com/package/@agentclientprotocol/codex-acp',
|
|
87
|
+
termsSource: 'https://learn.chatgpt.com/docs/auth',
|
|
88
|
+
credential: "An OpenAI API key (OPENAI_API_KEY or CODEX_API_KEY), the person's own",
|
|
89
|
+
binaries: "Node 22 (as the Agent Client Protocol project's adapters; unstated on npm), git",
|
|
90
|
+
note: "Codex's authentication page (read 2026-10-15) recommends API keys for automation and device-code login for headless machines; a ChatGPT login needs a browser and is hidden with NO_BROWSER=1 in the adapter. Only API keys are taken; a ChatGPT login is refused. The Zed-scoped package was archived on 22 July 2026 and names this one.",
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
export const CUSTOM_HARNESS_0_0_1 = {
|
|
94
|
+
id: 'custom',
|
|
95
|
+
version: '0.0.1',
|
|
96
|
+
name: 'Custom harness',
|
|
97
|
+
description: "An organization's own ACP agent, its command named by the organization's own harness spec and never typed into a form.",
|
|
98
|
+
enabled: false,
|
|
99
|
+
tags: ['harness'],
|
|
100
|
+
docs: '',
|
|
101
|
+
requires: {
|
|
102
|
+
secrets: [],
|
|
103
|
+
binaries: [],
|
|
104
|
+
},
|
|
105
|
+
terms: {
|
|
106
|
+
servedToThirdParties: 'organizations_own',
|
|
107
|
+
licence: "The organization's own",
|
|
108
|
+
},
|
|
109
|
+
skills: {
|
|
110
|
+
format: 'none',
|
|
111
|
+
dir: '',
|
|
112
|
+
},
|
|
113
|
+
honours: {
|
|
114
|
+
model: false,
|
|
115
|
+
subagents: false,
|
|
116
|
+
},
|
|
117
|
+
instructionsFile: '',
|
|
118
|
+
registered: {
|
|
119
|
+
adapter: "The organization's own",
|
|
120
|
+
version: '0',
|
|
121
|
+
licenceSource: 'https://agentspecs.datalayer.tech/harnesses',
|
|
122
|
+
termsSource: 'https://agentspecs.datalayer.tech/harnesses',
|
|
123
|
+
credential: "The organization's own",
|
|
124
|
+
binaries: '',
|
|
125
|
+
note: "Not launchable from the shared catalogue; an organization's own harness spec names the command and answers for its terms.",
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
export const FAST_AGENT_HARNESS_0_0_1 = {
|
|
129
|
+
id: 'fast-agent',
|
|
130
|
+
version: '0.0.1',
|
|
131
|
+
name: 'fast-agent',
|
|
132
|
+
description: "llmindset's agent framework served over ACP; each agent it defines is a session mode, with any model provider it is configured with.",
|
|
133
|
+
enabled: true,
|
|
134
|
+
tags: ['harness', 'framework'],
|
|
135
|
+
docs: 'https://github.com/evalstate/fast-agent/blob/main/docs/docs/acp/index.md',
|
|
136
|
+
acp: {
|
|
137
|
+
command: 'uvx',
|
|
138
|
+
args: ['fast-agent-acp==0.10.45'],
|
|
139
|
+
protocolVersion: 1,
|
|
140
|
+
loadSession: true,
|
|
141
|
+
mcp: 'lend',
|
|
142
|
+
modes: [],
|
|
143
|
+
env: {},
|
|
144
|
+
},
|
|
145
|
+
requires: {
|
|
146
|
+
secrets: ['ANTHROPIC_API_KEY'],
|
|
147
|
+
binaries: ['uvx'],
|
|
148
|
+
},
|
|
149
|
+
terms: {
|
|
150
|
+
servedToThirdParties: 'permitted',
|
|
151
|
+
licence: 'Apache-2.0',
|
|
152
|
+
},
|
|
153
|
+
skills: {
|
|
154
|
+
format: 'skill-md',
|
|
155
|
+
dir: '.agents/skills',
|
|
156
|
+
},
|
|
157
|
+
honours: {
|
|
158
|
+
model: true,
|
|
159
|
+
subagents: false,
|
|
160
|
+
},
|
|
161
|
+
instructionsFile: 'AGENTS.md',
|
|
162
|
+
registered: {
|
|
163
|
+
adapter: 'fast-agent-acp',
|
|
164
|
+
version: '0.10.45',
|
|
165
|
+
licenceSource: 'https://pypi.org/project/fast-agent-acp/',
|
|
166
|
+
termsSource: 'https://github.com/evalstate/fast-agent/blob/main/LICENSE',
|
|
167
|
+
credential: "The provider's key fast-agent is given (ANTHROPIC_API_KEY, OPENAI_API_KEY or HF_TOKEN); ANTHROPIC_API_KEY declared",
|
|
168
|
+
binaries: 'uv (uvx), Python 3.12 to 3.15 (requires_python on PyPI)',
|
|
169
|
+
note: "A framework, not a coding agent (SHIELD §2.5): each agent it defines is an ACP mode; the client's MCP servers are attached; sessions are listed. Launched with uvx fast-agent-acp (docs/docs/acp/index.md); reads skills from .fast-agent/skills, .agents/skills and .claude/skills; its default prompt reads AGENTS.md.",
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
export const GEMINI_CLI_HARNESS_0_0_1 = {
|
|
173
|
+
id: 'gemini-cli',
|
|
174
|
+
version: '0.0.1',
|
|
175
|
+
name: 'Gemini CLI',
|
|
176
|
+
description: "Google's coding agent, which speaks ACP itself when started with --experimental-acp.",
|
|
177
|
+
enabled: true,
|
|
178
|
+
tags: ['harness'],
|
|
179
|
+
docs: 'https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/cli-reference.md',
|
|
180
|
+
acp: {
|
|
181
|
+
command: 'npx',
|
|
182
|
+
args: ['-y', '@google/gemini-cli@0.64.0', '--experimental-acp'],
|
|
183
|
+
protocolVersion: 1,
|
|
184
|
+
loadSession: false,
|
|
185
|
+
mcp: 'lend',
|
|
186
|
+
modes: ['default', 'auto_edit', 'yolo', 'plan'],
|
|
187
|
+
env: {},
|
|
188
|
+
},
|
|
189
|
+
requires: {
|
|
190
|
+
secrets: ['GEMINI_API_KEY'],
|
|
191
|
+
binaries: ['node', 'git'],
|
|
192
|
+
},
|
|
193
|
+
terms: {
|
|
194
|
+
servedToThirdParties: 'api_key_only',
|
|
195
|
+
licence: 'Apache-2.0 (the agent); Gemini API or Gemini Code Assist terms (the model)',
|
|
196
|
+
tierRestrictions: 'Gemini Code Assist Standard or Enterprise for a Google login (the individuals tier stopped being served on 18 June 2026); a Gemini API key otherwise',
|
|
197
|
+
},
|
|
198
|
+
skills: {
|
|
199
|
+
format: 'skill-md',
|
|
200
|
+
dir: '.gemini/skills',
|
|
201
|
+
},
|
|
202
|
+
honours: {
|
|
203
|
+
model: true,
|
|
204
|
+
subagents: false,
|
|
205
|
+
},
|
|
206
|
+
instructionsFile: 'GEMINI.md',
|
|
207
|
+
registered: {
|
|
208
|
+
adapter: '@google/gemini-cli',
|
|
209
|
+
version: '0.64.0',
|
|
210
|
+
licenceSource: 'https://www.npmjs.com/package/@google/gemini-cli',
|
|
211
|
+
termsSource: 'https://developers.google.com/gemini-code-assist/resources/privacy-notice-gemini-code-assist-individuals',
|
|
212
|
+
credential: "A Gemini API key (GEMINI_API_KEY), the person's own; or a Google login on a Code Assist Standard or Enterprise subscription",
|
|
213
|
+
binaries: 'Node 20 or later (engines.node >=20 on npm), git',
|
|
214
|
+
note: 'Google\'s notice (read 2026-10-15): "Starting June 18, 2026, Gemini Code Assist IDE extensions stopped serving requests for the Gemini Code Assist for individuals"; Standard and Enterprise follow a separate schedule. The ACP flag is --experimental-acp in the CLI reference (docs/cli/cli-reference.md).',
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
export const GOOSE_HARNESS_0_0_1 = {
|
|
218
|
+
id: 'goose',
|
|
219
|
+
version: '0.0.1',
|
|
220
|
+
name: 'goose',
|
|
221
|
+
description: "Block's open agent (now the Agentic AI Foundation's), which speaks ACP itself with goose acp; any model provider it is configured with.",
|
|
222
|
+
enabled: true,
|
|
223
|
+
tags: ['harness'],
|
|
224
|
+
docs: 'https://block.github.io/goose/docs/gdk/acp/',
|
|
225
|
+
acp: {
|
|
226
|
+
command: 'goose',
|
|
227
|
+
args: ['acp'],
|
|
228
|
+
protocolVersion: 1,
|
|
229
|
+
loadSession: false,
|
|
230
|
+
mcp: 'lend',
|
|
231
|
+
modes: [],
|
|
232
|
+
env: { GOOSE_PROVIDER: 'anthropic' },
|
|
233
|
+
},
|
|
234
|
+
requires: {
|
|
235
|
+
secrets: ['ANTHROPIC_API_KEY'],
|
|
236
|
+
binaries: ['goose@1.54.0', 'git'],
|
|
237
|
+
},
|
|
238
|
+
terms: {
|
|
239
|
+
servedToThirdParties: 'permitted',
|
|
240
|
+
licence: 'Apache-2.0',
|
|
241
|
+
},
|
|
242
|
+
skills: {
|
|
243
|
+
format: 'skill-md',
|
|
244
|
+
dir: '.agents/skills',
|
|
245
|
+
},
|
|
246
|
+
honours: {
|
|
247
|
+
model: true,
|
|
248
|
+
subagents: false,
|
|
249
|
+
},
|
|
250
|
+
instructionsFile: '.goosehints',
|
|
251
|
+
registered: {
|
|
252
|
+
adapter: 'goose',
|
|
253
|
+
version: '1.54.0',
|
|
254
|
+
licenceSource: 'https://github.com/aaif-goose/goose',
|
|
255
|
+
termsSource: 'https://github.com/aaif-goose/goose/blob/main/LICENSE',
|
|
256
|
+
credential: "The provider's key goose is configured with (GOOSE_PROVIDER); ANTHROPIC_API_KEY declared",
|
|
257
|
+
binaries: 'The goose CLI (release v1.54.0 of 8 October 2026), git',
|
|
258
|
+
note: "Open source under Apache-2.0, no vendor terms of its own; the model provider's terms apply to the key. Launched as goose acp (documentation/docs/gdk/acp/index.md).",
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
export const OPENCODE_HARNESS_0_0_1 = {
|
|
262
|
+
id: 'opencode',
|
|
263
|
+
version: '0.0.1',
|
|
264
|
+
name: 'OpenCode',
|
|
265
|
+
description: "Anomaly's open coding agent, which speaks ACP itself with opencode acp; any model provider it is configured with.",
|
|
266
|
+
enabled: true,
|
|
267
|
+
tags: ['harness'],
|
|
268
|
+
docs: 'https://opencode.ai/docs/acp/',
|
|
269
|
+
acp: {
|
|
270
|
+
command: 'npx',
|
|
271
|
+
args: ['-y', 'opencode-ai@1.19.0', 'acp'],
|
|
272
|
+
protocolVersion: 1,
|
|
273
|
+
loadSession: false,
|
|
274
|
+
mcp: 'lend',
|
|
275
|
+
modes: [],
|
|
276
|
+
env: {},
|
|
277
|
+
},
|
|
278
|
+
requires: {
|
|
279
|
+
secrets: ['ANTHROPIC_API_KEY'],
|
|
280
|
+
binaries: ['node', 'git'],
|
|
281
|
+
},
|
|
282
|
+
terms: {
|
|
283
|
+
servedToThirdParties: 'permitted',
|
|
284
|
+
licence: 'MIT',
|
|
285
|
+
},
|
|
286
|
+
skills: {
|
|
287
|
+
format: 'skill-md',
|
|
288
|
+
dir: '.opencode/skills',
|
|
289
|
+
},
|
|
290
|
+
honours: {
|
|
291
|
+
model: true,
|
|
292
|
+
subagents: false,
|
|
293
|
+
},
|
|
294
|
+
instructionsFile: 'AGENTS.md',
|
|
295
|
+
registered: {
|
|
296
|
+
adapter: 'opencode-ai',
|
|
297
|
+
version: '1.19.0',
|
|
298
|
+
licenceSource: 'https://www.npmjs.com/package/opencode-ai',
|
|
299
|
+
termsSource: 'https://github.com/anomalyco/opencode/blob/dev/LICENSE',
|
|
300
|
+
credential: "The provider's key OpenCode is configured with; ANTHROPIC_API_KEY declared",
|
|
301
|
+
binaries: 'Node (the opencode-ai package), git',
|
|
302
|
+
note: "Open source under MIT, no vendor terms of its own; the model provider's terms apply to the key. Launched as opencode acp (opencode.ai/docs/acp); reads skills from .opencode/skills, .claude/skills and .agents/skills.",
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
export const OPENHANDS_HARNESS_0_0_1 = {
|
|
306
|
+
id: 'openhands',
|
|
307
|
+
version: '0.0.1',
|
|
308
|
+
name: 'OpenHands',
|
|
309
|
+
description: 'The OpenHands CLI, which speaks ACP itself with openhands acp; any model provider it is configured with.',
|
|
310
|
+
enabled: true,
|
|
311
|
+
tags: ['harness'],
|
|
312
|
+
docs: 'https://docs.openhands.dev/openhands/usage/run-openhands/acp',
|
|
313
|
+
acp: {
|
|
314
|
+
command: 'uvx',
|
|
315
|
+
args: [
|
|
316
|
+
'--from',
|
|
317
|
+
'openhands==1.16.0',
|
|
318
|
+
'openhands',
|
|
319
|
+
'acp',
|
|
320
|
+
'--override-with-envs',
|
|
321
|
+
],
|
|
322
|
+
protocolVersion: 1,
|
|
323
|
+
loadSession: true,
|
|
324
|
+
mcp: 'lend',
|
|
325
|
+
modes: ['always-ask', 'always-approve', 'llm-approve'],
|
|
326
|
+
env: {},
|
|
327
|
+
},
|
|
328
|
+
requires: {
|
|
329
|
+
secrets: ['LLM_API_KEY'],
|
|
330
|
+
binaries: ['uvx', 'git'],
|
|
331
|
+
},
|
|
332
|
+
terms: {
|
|
333
|
+
servedToThirdParties: 'permitted',
|
|
334
|
+
licence: 'MIT',
|
|
335
|
+
},
|
|
336
|
+
skills: {
|
|
337
|
+
format: 'skill-md',
|
|
338
|
+
dir: '.agents/skills',
|
|
339
|
+
},
|
|
340
|
+
honours: {
|
|
341
|
+
model: true,
|
|
342
|
+
subagents: false,
|
|
343
|
+
},
|
|
344
|
+
instructionsFile: 'AGENTS.md',
|
|
345
|
+
registered: {
|
|
346
|
+
adapter: 'openhands',
|
|
347
|
+
version: '1.16.0',
|
|
348
|
+
licenceSource: 'https://pypi.org/project/openhands/',
|
|
349
|
+
termsSource: 'https://github.com/OpenHands/OpenHands-CLI/blob/main/LICENSE',
|
|
350
|
+
credential: "The provider's key OpenHands reads as LLM_API_KEY, with LLM_MODEL, under --override-with-envs",
|
|
351
|
+
binaries: 'uv (uvx), Python 3.12 or later, git',
|
|
352
|
+
note: "Open source under MIT, no vendor terms of its own; the model provider's terms apply to the key. Launched as openhands acp (OpenHands-CLI README; docs.openhands.dev's ACP page); resumes with --resume; reads skills from .agents/skills (.openhands/skills is legacy).",
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
export const HARNESS_CATALOGUE = {
|
|
356
|
+
'claude-code': CLAUDE_CODE_HARNESS_0_0_1,
|
|
357
|
+
codex: CODEX_HARNESS_0_0_1,
|
|
358
|
+
custom: CUSTOM_HARNESS_0_0_1,
|
|
359
|
+
'fast-agent': FAST_AGENT_HARNESS_0_0_1,
|
|
360
|
+
'gemini-cli': GEMINI_CLI_HARNESS_0_0_1,
|
|
361
|
+
goose: GOOSE_HARNESS_0_0_1,
|
|
362
|
+
opencode: OPENCODE_HARNESS_0_0_1,
|
|
363
|
+
openhands: OPENHANDS_HARNESS_0_0_1,
|
|
364
|
+
};
|
|
365
|
+
/** A harness by id. */
|
|
366
|
+
export function getHarness(harnessId) {
|
|
367
|
+
return HARNESS_CATALOGUE[harnessId];
|
|
368
|
+
}
|
|
369
|
+
/** Every harness of the catalogue. */
|
|
370
|
+
export function listHarnesses() {
|
|
371
|
+
return Object.values(HARNESS_CATALOGUE);
|
|
372
|
+
}
|
package/lib/specs/index.d.ts
CHANGED
package/lib/specs/index.js
CHANGED
|
@@ -204,6 +204,75 @@ export interface ProtocolSpec {
|
|
|
204
204
|
/** The Appspec block it configures, if any: refused unless `protocols` lists it. */
|
|
205
205
|
appspecBlock?: 'pap' | 'pact' | 'a2a' | 'signed_fetch';
|
|
206
206
|
}
|
|
207
|
+
/** How a harness is launched and driven over ACP (SHIELD.md §3.1). */
|
|
208
|
+
export interface HarnessAcpSpec {
|
|
209
|
+
/** The executable, as the runtime image has it. */
|
|
210
|
+
command: string;
|
|
211
|
+
/** Its arguments, the adapter's version pinned among them. */
|
|
212
|
+
args: string[];
|
|
213
|
+
protocolVersion: number;
|
|
214
|
+
/** Whether it answers `session/load`. */
|
|
215
|
+
loadSession: boolean;
|
|
216
|
+
/** `lend`: the Shield lends MCP servers through the gateway; `own`; or `none`. */
|
|
217
|
+
mcp: 'lend' | 'own' | 'none';
|
|
218
|
+
/** The session modes it advertises, as it names them. */
|
|
219
|
+
modes: string[];
|
|
220
|
+
/** Plain variables the launch needs, never a secret. */
|
|
221
|
+
env: Record<string, string>;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* An agent program driven over the Agent Client Protocol
|
|
225
|
+
* (`agentspecs/harnesses`, SHIELD.md SH-01). An agent spec's `harness:` names
|
|
226
|
+
* one beside `pydantic-ai` and `vercel-ai`; the Datalayer runtime serves it
|
|
227
|
+
* through its ACP adapter, `ACPAgent` (SHIELD SH-10), never the browser.
|
|
228
|
+
*/
|
|
229
|
+
export interface HarnessSpec {
|
|
230
|
+
/** What `harness:` names (e.g. 'claude-code'). */
|
|
231
|
+
id: string;
|
|
232
|
+
version: string;
|
|
233
|
+
name: string;
|
|
234
|
+
description: string;
|
|
235
|
+
/** Whether the shared catalogue offers it: `custom` is not. */
|
|
236
|
+
enabled: boolean;
|
|
237
|
+
tags: string[];
|
|
238
|
+
/** Where its ACP use is documented. */
|
|
239
|
+
docs: string;
|
|
240
|
+
/** Absent only on `custom`, whose command an organization's own spec names. */
|
|
241
|
+
acp?: HarnessAcpSpec;
|
|
242
|
+
/** What it needs in the pod: secrets as ids of the env-vars catalogue, executables. */
|
|
243
|
+
requires: {
|
|
244
|
+
secrets: string[];
|
|
245
|
+
binaries: string[];
|
|
246
|
+
};
|
|
247
|
+
/** The terms it is served under, as the register says them (§3.3). */
|
|
248
|
+
terms: {
|
|
249
|
+
servedToThirdParties: 'api_key_only' | 'permitted' | 'refused' | 'organizations_own';
|
|
250
|
+
licence: string;
|
|
251
|
+
tierRestrictions?: string;
|
|
252
|
+
};
|
|
253
|
+
/** Where the Shield writes an application's skills for it (§4.10). */
|
|
254
|
+
skills: {
|
|
255
|
+
format: 'skill-md' | 'none';
|
|
256
|
+
dir: string;
|
|
257
|
+
};
|
|
258
|
+
/** What of an agent spec it can honour; the rest is refused at load. */
|
|
259
|
+
honours: {
|
|
260
|
+
model: boolean;
|
|
261
|
+
subagents: boolean;
|
|
262
|
+
};
|
|
263
|
+
/** The workspace file it reads its instructions from. */
|
|
264
|
+
instructionsFile: string;
|
|
265
|
+
/** The register's line: what was checked, and where. */
|
|
266
|
+
registered: {
|
|
267
|
+
adapter: string;
|
|
268
|
+
version: string;
|
|
269
|
+
licenceSource: string;
|
|
270
|
+
termsSource: string;
|
|
271
|
+
credential: string;
|
|
272
|
+
binaries: string;
|
|
273
|
+
note: string;
|
|
274
|
+
};
|
|
275
|
+
}
|
|
207
276
|
/** A check a Frame requires of the output of work done under it. */
|
|
208
277
|
export interface FrameGuardSpec {
|
|
209
278
|
id: string;
|
package/package.json
CHANGED
|
@@ -304,6 +304,7 @@ def load_yaml_specs(specs_dir: Path) -> List[tuple[str, Dict[str, Any]]]:
|
|
|
304
304
|
|
|
305
305
|
check_model_additionals(resolved, load_model_ids(specs_dir))
|
|
306
306
|
check_protocols(resolved, load_protocol_ids(specs_dir))
|
|
307
|
+
check_harnesses(resolved, load_harnesses(specs_dir))
|
|
307
308
|
check_voices(resolved)
|
|
308
309
|
return resolved
|
|
309
310
|
|
|
@@ -364,6 +365,60 @@ def check_protocols(
|
|
|
364
365
|
raise SystemExit("\n".join(problems))
|
|
365
366
|
|
|
366
367
|
|
|
368
|
+
BUILTIN_HARNESSES = ("pydantic-ai", "vercel-ai")
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def load_harnesses(specs_dir: Path) -> dict[str, dict[str, Any]]:
|
|
372
|
+
"""Every harness of the catalogue (`agentspecs/harnesses`, SHIELD.md SH-01), by id."""
|
|
373
|
+
harnesses: dict[str, dict[str, Any]] = {}
|
|
374
|
+
for yaml_file in sorted((specs_dir.parent / "harnesses").glob("*.yaml")):
|
|
375
|
+
if yaml_file.name == "register.yaml":
|
|
376
|
+
continue
|
|
377
|
+
harness = yaml.safe_load(yaml_file.read_text()) or {}
|
|
378
|
+
if harness.get("id"):
|
|
379
|
+
harnesses[str(harness["id"])] = harness
|
|
380
|
+
return harnesses
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def check_harnesses(
|
|
384
|
+
specs: List[tuple[str, Dict[str, Any]]], harnesses: dict[str, dict[str, Any]]
|
|
385
|
+
) -> None:
|
|
386
|
+
"""Refuse an agent naming a harness the catalogue does not know, or asking what it cannot honour (SH-01)."""
|
|
387
|
+
problems = []
|
|
388
|
+
for _, spec in specs:
|
|
389
|
+
harness_id = str(spec.get("harness") or BUILTIN_HARNESSES[0])
|
|
390
|
+
if harness_id in BUILTIN_HARNESSES:
|
|
391
|
+
continue
|
|
392
|
+
harness = harnesses.get(harness_id)
|
|
393
|
+
if harness is None:
|
|
394
|
+
problems.append(
|
|
395
|
+
f"{spec.get('id')}: there is no harness named {harness_id!r}: one of "
|
|
396
|
+
f"{', '.join([*BUILTIN_HARNESSES, *sorted(harnesses)])}"
|
|
397
|
+
)
|
|
398
|
+
continue
|
|
399
|
+
if harness_id == "custom":
|
|
400
|
+
problems.append(
|
|
401
|
+
f"{spec.get('id')}: the harness 'custom' names no command: an organization's "
|
|
402
|
+
"own harness spec names it"
|
|
403
|
+
)
|
|
404
|
+
continue
|
|
405
|
+
honours = harness.get("honours") or {}
|
|
406
|
+
if spec.get("model") and not honours.get("model"):
|
|
407
|
+
problems.append(
|
|
408
|
+
f"{spec.get('id')}: the harness {harness_id!r} does not take a model"
|
|
409
|
+
)
|
|
410
|
+
if spec.get("subagents") and not honours.get("subagents"):
|
|
411
|
+
problems.append(
|
|
412
|
+
f"{spec.get('id')}: the harness {harness_id!r} has its own subagents"
|
|
413
|
+
)
|
|
414
|
+
if spec.get("skills") and (harness.get("skills") or {}).get("format") == "none":
|
|
415
|
+
problems.append(
|
|
416
|
+
f"{spec.get('id')}: the harness {harness_id!r} reads no skills"
|
|
417
|
+
)
|
|
418
|
+
if problems:
|
|
419
|
+
raise SystemExit("\n".join(problems))
|
|
420
|
+
|
|
421
|
+
|
|
367
422
|
def load_model_ids(specs_dir: Path) -> set[str]:
|
|
368
423
|
"""Every id the models catalogue answers to: its ids and their aliases."""
|
|
369
424
|
ids: set[str] = set()
|
|
@@ -1726,6 +1781,7 @@ export * from './uiRenderers';
|
|
|
1726
1781
|
export * from './protocols';
|
|
1727
1782
|
export * from './frames';
|
|
1728
1783
|
export * from './cogs';
|
|
1784
|
+
export * from './harnesses';
|
|
1729
1785
|
export * from './guards';
|
|
1730
1786
|
export * from './gates';
|
|
1731
1787
|
export * from './tracks';
|
|
@@ -13,6 +13,7 @@ Usage:
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
import argparse
|
|
16
|
+
import json
|
|
16
17
|
import sys
|
|
17
18
|
from pathlib import Path
|
|
18
19
|
from typing import Any
|
|
@@ -85,7 +86,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
85
86
|
f' id="{envvar_id}",',
|
|
86
87
|
f' version="{version}",',
|
|
87
88
|
f' name="{spec["name"]}",',
|
|
88
|
-
f
|
|
89
|
+
f" description={json.dumps(spec['description'], ensure_ascii=False)},",
|
|
89
90
|
f" registrationUrl={registration_url_value},",
|
|
90
91
|
f" tags={_fmt_list(spec.get('tags', []))},",
|
|
91
92
|
f" icon={icon},",
|
|
@@ -185,7 +186,7 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
185
186
|
f" id: '{envvar_id}',",
|
|
186
187
|
f" version: '{version}',",
|
|
187
188
|
f" name: '{spec['name']}',",
|
|
188
|
-
f" description:
|
|
189
|
+
f" description: {json.dumps(spec['description'], ensure_ascii=False)},",
|
|
189
190
|
]
|
|
190
191
|
)
|
|
191
192
|
|