@agentproto/driver-agent-cli 0.1.0-alpha.0
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/LICENSE +21 -0
- package/dist/chunk-EEIYDRIU.mjs +552 -0
- package/dist/chunk-EEIYDRIU.mjs.map +1 -0
- package/dist/index.d.ts +234 -0
- package/dist/index.mjs +226 -0
- package/dist/index.mjs.map +1 -0
- package/dist/manifest/index.d.ts +22 -0
- package/dist/manifest/index.mjs +28 -0
- package/dist/manifest/index.mjs.map +1 -0
- package/dist/schema-CdCb4gW1.d.ts +548 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agentproto contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Writable, Readable } from 'stream';
|
|
3
|
+
import { createAcpClient } from '@agentproto/acp/client';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import { randomUUID } from 'crypto';
|
|
6
|
+
import { createDoctype } from '@agentproto/define-doctype';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @agentproto/driver-agent-cli v0.1.0-alpha
|
|
10
|
+
* AIP-45 AGENT-CLI.md `defineAgentCli` reference implementation.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
var ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
|
|
14
|
+
var SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/;
|
|
15
|
+
var TAG_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
16
|
+
var SEMVER_RANGE_PATTERN = /^[><=^~ \d.\-+\w*xX|, ]+$/;
|
|
17
|
+
var ADAPTER_PATTERN = /^@?[a-z0-9][a-z0-9-./]*$/;
|
|
18
|
+
var BIN_PATTERN = /^[A-Za-z0-9._\-/]+$/;
|
|
19
|
+
var MODE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
20
|
+
var OPTION_ID_PATTERN = /^[a-z0-9][a-z0-9_]{0,63}$/;
|
|
21
|
+
var CONTINUATION_STRATEGY_IDS = [
|
|
22
|
+
"none",
|
|
23
|
+
"pinned-session",
|
|
24
|
+
"transcript",
|
|
25
|
+
"native-resume"
|
|
26
|
+
];
|
|
27
|
+
var CONTINUATION_KEY_SCOPES = [
|
|
28
|
+
"conversation",
|
|
29
|
+
"operator",
|
|
30
|
+
"user",
|
|
31
|
+
"guild",
|
|
32
|
+
"workspace"
|
|
33
|
+
];
|
|
34
|
+
var installMethodSchema = z.discriminatedUnion("method", [
|
|
35
|
+
z.object({ method: z.literal("brew"), package: z.string(), experimental: z.boolean().optional() }).strict(),
|
|
36
|
+
z.object({ method: z.literal("apt"), package: z.string(), experimental: z.boolean().optional() }).strict(),
|
|
37
|
+
z.object({ method: z.literal("dnf"), package: z.string(), experimental: z.boolean().optional() }).strict(),
|
|
38
|
+
z.object({ method: z.literal("pacman"), package: z.string(), experimental: z.boolean().optional() }).strict(),
|
|
39
|
+
z.object({ method: z.literal("choco"), package: z.string(), experimental: z.boolean().optional() }).strict(),
|
|
40
|
+
z.object({ method: z.literal("scoop"), package: z.string(), experimental: z.boolean().optional() }).strict(),
|
|
41
|
+
z.object({ method: z.literal("npm"), package: z.string(), global: z.boolean().optional(), experimental: z.boolean().optional() }).strict(),
|
|
42
|
+
z.object({ method: z.literal("pip"), package: z.string(), user: z.boolean().optional(), experimental: z.boolean().optional() }).strict(),
|
|
43
|
+
z.object({ method: z.literal("cargo"), package: z.string(), experimental: z.boolean().optional() }).strict(),
|
|
44
|
+
z.object({ method: z.literal("go"), package: z.string(), experimental: z.boolean().optional() }).strict(),
|
|
45
|
+
z.object({
|
|
46
|
+
method: z.literal("curl"),
|
|
47
|
+
url: z.string().url(),
|
|
48
|
+
verify_sha256: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
49
|
+
experimental: z.boolean().optional()
|
|
50
|
+
}).strict(),
|
|
51
|
+
z.object({
|
|
52
|
+
method: z.literal("download"),
|
|
53
|
+
url: z.string().url(),
|
|
54
|
+
extract_bin: z.string(),
|
|
55
|
+
verify_sha256: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
56
|
+
experimental: z.boolean().optional()
|
|
57
|
+
}).strict(),
|
|
58
|
+
z.object({ method: z.literal("vendored"), path: z.string(), experimental: z.boolean().optional() }).strict()
|
|
59
|
+
]);
|
|
60
|
+
var versionCheckSchema = z.object({
|
|
61
|
+
cmd: z.string(),
|
|
62
|
+
parse: z.string().describe("ECMAScript regex with at least one capture group."),
|
|
63
|
+
range: z.string().regex(SEMVER_RANGE_PATTERN).describe("npm-style semver range."),
|
|
64
|
+
timeout_ms: z.number().int().min(100).default(5e3)
|
|
65
|
+
}).strict();
|
|
66
|
+
var authSchema = z.object({
|
|
67
|
+
ref: z.string().optional(),
|
|
68
|
+
state: z.object({
|
|
69
|
+
paths: z.array(z.string()).optional(),
|
|
70
|
+
env: z.array(z.string()).optional()
|
|
71
|
+
}).strict().optional(),
|
|
72
|
+
login: z.object({
|
|
73
|
+
cmd: z.string(),
|
|
74
|
+
interactive: z.boolean().optional(),
|
|
75
|
+
requires_callback_url: z.boolean().optional()
|
|
76
|
+
}).strict().optional(),
|
|
77
|
+
refresh: z.object({
|
|
78
|
+
cmd: z.string(),
|
|
79
|
+
interval_s: z.number().int().positive().optional()
|
|
80
|
+
}).strict().optional(),
|
|
81
|
+
expiry: z.object({
|
|
82
|
+
parse: z.string().optional(),
|
|
83
|
+
grace_s: z.number().int().nonnegative().optional()
|
|
84
|
+
}).strict().optional()
|
|
85
|
+
}).strict();
|
|
86
|
+
var sessionSchema = z.object({
|
|
87
|
+
mode: z.enum(["ephemeral", "persistent", "resumable"]).default("ephemeral"),
|
|
88
|
+
idle_timeout_ms: z.number().int().min(1e3).default(6e5),
|
|
89
|
+
max_turns: z.number().int().positive().optional(),
|
|
90
|
+
context_carryover: z.boolean().default(true)
|
|
91
|
+
}).strict();
|
|
92
|
+
var modelsSchema = z.object({
|
|
93
|
+
default: z.string().optional(),
|
|
94
|
+
allowed: z.array(z.string()).optional(),
|
|
95
|
+
env: z.record(z.string(), z.string()).optional()
|
|
96
|
+
}).strict();
|
|
97
|
+
var capabilitiesSchema = z.object({
|
|
98
|
+
streaming: z.boolean().optional(),
|
|
99
|
+
tool_calls: z.boolean().optional(),
|
|
100
|
+
sub_agents: z.boolean().optional(),
|
|
101
|
+
file_io: z.boolean().optional(),
|
|
102
|
+
multimodal: z.boolean().optional(),
|
|
103
|
+
resumable: z.boolean().optional(),
|
|
104
|
+
bidirectional: z.boolean().optional()
|
|
105
|
+
}).strict();
|
|
106
|
+
var modeSchema = z.object({
|
|
107
|
+
id: z.string().regex(MODE_ID_PATTERN),
|
|
108
|
+
description: z.string().optional(),
|
|
109
|
+
bin_args_append: z.array(z.string()).optional(),
|
|
110
|
+
env: z.record(z.string(), z.string()).optional()
|
|
111
|
+
}).strict();
|
|
112
|
+
var optionSchema = z.object({
|
|
113
|
+
id: z.string().regex(OPTION_ID_PATTERN),
|
|
114
|
+
type: z.enum(["boolean", "integer", "string", "enum"]),
|
|
115
|
+
description: z.string().optional(),
|
|
116
|
+
enum: z.array(z.string()).min(1).optional(),
|
|
117
|
+
default: z.union([z.boolean(), z.number(), z.string()]).optional(),
|
|
118
|
+
min: z.number().int().optional(),
|
|
119
|
+
max: z.number().int().optional(),
|
|
120
|
+
bin_args_template: z.array(z.string()).optional(),
|
|
121
|
+
bin_args_append_when_true: z.array(z.string()).optional(),
|
|
122
|
+
env: z.record(z.string(), z.string()).optional()
|
|
123
|
+
}).strict().refine(
|
|
124
|
+
// type === "enum" REQUIRES an `enum` array. The JSON Schema enforces
|
|
125
|
+
// this via `if/then`; the zod side mirrors with a refine so both
|
|
126
|
+
// validation paths reject the same shapes.
|
|
127
|
+
(o) => o.type !== "enum" || Array.isArray(o.enum) && o.enum.length > 0,
|
|
128
|
+
{ message: "option.type === 'enum' requires a non-empty `enum` array" }
|
|
129
|
+
);
|
|
130
|
+
var continuationStrategyIdSchema = z.enum(CONTINUATION_STRATEGY_IDS);
|
|
131
|
+
var continuationSchema = z.object({
|
|
132
|
+
default: continuationStrategyIdSchema,
|
|
133
|
+
supported: z.array(continuationStrategyIdSchema).min(1),
|
|
134
|
+
pinned_session: z.object({
|
|
135
|
+
idle_timeout_ms: z.number().int().min(1e3).default(18e5),
|
|
136
|
+
key_scope: z.array(z.enum(CONTINUATION_KEY_SCOPES)).min(1).default(["conversation", "operator"])
|
|
137
|
+
}).strict().optional()
|
|
138
|
+
}).strict().refine(
|
|
139
|
+
// The chosen `default` must appear in `supported` — otherwise the
|
|
140
|
+
// host has no valid strategy to fall back to.
|
|
141
|
+
(c) => c.supported.includes(c.default),
|
|
142
|
+
{
|
|
143
|
+
message: "continuation.default must be a member of continuation.supported"
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
var mcpBlockSchema = z.object({
|
|
147
|
+
command: z.string().optional(),
|
|
148
|
+
args: z.array(z.string()).optional(),
|
|
149
|
+
transport: z.enum(["stdio", "http", "sse"]),
|
|
150
|
+
url: z.string().url().optional()
|
|
151
|
+
}).strict();
|
|
152
|
+
var agentCliFrontmatterSchema = z.object({
|
|
153
|
+
name: z.string().min(1).max(80),
|
|
154
|
+
id: z.string().regex(ID_PATTERN),
|
|
155
|
+
description: z.string().min(1).max(2e3),
|
|
156
|
+
version: z.string().regex(SEMVER_PATTERN),
|
|
157
|
+
bin: z.string().regex(BIN_PATTERN),
|
|
158
|
+
bin_args: z.array(z.string()).optional(),
|
|
159
|
+
install: z.array(installMethodSchema).min(1),
|
|
160
|
+
version_check: versionCheckSchema,
|
|
161
|
+
auth: authSchema.optional(),
|
|
162
|
+
sandbox: z.union([z.string(), z.record(z.string(), z.unknown())]),
|
|
163
|
+
runner: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
|
|
164
|
+
protocol: z.enum(["acp", "mcp", "proprietary"]),
|
|
165
|
+
acp: z.string().optional(),
|
|
166
|
+
mcp: mcpBlockSchema.optional(),
|
|
167
|
+
adapter: z.string().regex(ADAPTER_PATTERN).optional(),
|
|
168
|
+
session: sessionSchema.optional(),
|
|
169
|
+
models: modelsSchema.optional(),
|
|
170
|
+
capabilities: capabilitiesSchema.optional(),
|
|
171
|
+
modes: z.array(modeSchema).optional(),
|
|
172
|
+
options: z.array(optionSchema).optional(),
|
|
173
|
+
continuation: continuationSchema.optional(),
|
|
174
|
+
requires: z.object({
|
|
175
|
+
os: z.array(z.enum(["darwin", "linux", "windows"])).optional(),
|
|
176
|
+
arch: z.array(z.enum(["x64", "arm64", "x86", "arm"])).optional(),
|
|
177
|
+
min_disk_mb: z.number().int().nonnegative().optional(),
|
|
178
|
+
min_memory_mb: z.number().int().nonnegative().optional()
|
|
179
|
+
}).strict().optional(),
|
|
180
|
+
examples: z.array(z.object({
|
|
181
|
+
goal: z.string(),
|
|
182
|
+
prompt: z.string(),
|
|
183
|
+
note: z.string().optional()
|
|
184
|
+
}).strict()).optional(),
|
|
185
|
+
tags: z.array(z.string().regex(TAG_PATTERN)).optional(),
|
|
186
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
187
|
+
}).strict().superRefine((m, ctx) => {
|
|
188
|
+
if (m.modes) {
|
|
189
|
+
const seen = /* @__PURE__ */ new Set();
|
|
190
|
+
for (const mode of m.modes) {
|
|
191
|
+
if (seen.has(mode.id)) {
|
|
192
|
+
ctx.addIssue({
|
|
193
|
+
code: z.ZodIssueCode.custom,
|
|
194
|
+
path: ["modes"],
|
|
195
|
+
message: `Duplicate mode id '${mode.id}' \u2014 mode ids must be unique within a manifest.`
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
seen.add(mode.id);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (m.options) {
|
|
202
|
+
const seen = /* @__PURE__ */ new Set();
|
|
203
|
+
for (const opt of m.options) {
|
|
204
|
+
if (seen.has(opt.id)) {
|
|
205
|
+
ctx.addIssue({
|
|
206
|
+
code: z.ZodIssueCode.custom,
|
|
207
|
+
path: ["options"],
|
|
208
|
+
message: `Duplicate option id '${opt.id}' \u2014 option ids must be unique within a manifest.`
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
seen.add(opt.id);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (m.continuation?.default === "native-resume" && !m.capabilities?.resumable) {
|
|
215
|
+
ctx.addIssue({
|
|
216
|
+
code: z.ZodIssueCode.custom,
|
|
217
|
+
path: ["continuation", "default"],
|
|
218
|
+
message: "continuation.default === 'native-resume' requires capabilities.resumable: true."
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}).describe(
|
|
222
|
+
"Validates the YAML frontmatter portion of an AIP-45 AGENT-CLI.md manifest."
|
|
223
|
+
);
|
|
224
|
+
var runtimeConfigSchema = z.object({
|
|
225
|
+
mode: z.string().optional(),
|
|
226
|
+
options: z.record(z.string(), z.union([z.boolean(), z.number(), z.string()])).optional(),
|
|
227
|
+
continuation: continuationStrategyIdSchema.optional()
|
|
228
|
+
}).strict();
|
|
229
|
+
function createAcpProtocolArm(options) {
|
|
230
|
+
const { child, cwd } = options;
|
|
231
|
+
if (!child.stdin || !child.stdout) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
"AcpProtocolArm: subprocess must be spawned with piped stdin + stdout"
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const output = Writable.toWeb(child.stdin);
|
|
237
|
+
const input = Readable.toWeb(child.stdout);
|
|
238
|
+
let client = null;
|
|
239
|
+
let session = null;
|
|
240
|
+
const pendingByTurn = /* @__PURE__ */ new Map();
|
|
241
|
+
return {
|
|
242
|
+
get sessionId() {
|
|
243
|
+
return session?.sessionId;
|
|
244
|
+
},
|
|
245
|
+
async connect(opts) {
|
|
246
|
+
client = await createAcpClient({
|
|
247
|
+
output,
|
|
248
|
+
input,
|
|
249
|
+
clientInfo: options.clientInfo,
|
|
250
|
+
capabilities: {
|
|
251
|
+
fs: { readTextFile: true, writeTextFile: true }
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
if (opts.resumeSessionId) {
|
|
255
|
+
session = await client.loadSession({
|
|
256
|
+
sessionId: opts.resumeSessionId,
|
|
257
|
+
cwd
|
|
258
|
+
});
|
|
259
|
+
} else {
|
|
260
|
+
session = await client.newSession({ cwd });
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
async send(turnId, message) {
|
|
264
|
+
if (!session) throw new Error("AcpProtocolArm.send: not connected");
|
|
265
|
+
const messages = Array.isArray(message) ? message : [message];
|
|
266
|
+
const stream = session.prompt({ messages });
|
|
267
|
+
pendingByTurn.set(turnId, stream);
|
|
268
|
+
},
|
|
269
|
+
async *events() {
|
|
270
|
+
const last = Array.from(pendingByTurn.values()).at(-1);
|
|
271
|
+
if (!last) return;
|
|
272
|
+
for await (const evt of last) yield evt;
|
|
273
|
+
},
|
|
274
|
+
async cancel(_turnId) {
|
|
275
|
+
if (!session) return;
|
|
276
|
+
await session.cancel();
|
|
277
|
+
},
|
|
278
|
+
async close() {
|
|
279
|
+
if (session) await session.close();
|
|
280
|
+
if (client) await client.close();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/manifest/compose.ts
|
|
286
|
+
var RuntimeConfigError = class extends Error {
|
|
287
|
+
code;
|
|
288
|
+
path;
|
|
289
|
+
constructor(code, path, message) {
|
|
290
|
+
super(`[${code} at ${path}] ${message}`);
|
|
291
|
+
this.code = code;
|
|
292
|
+
this.path = path;
|
|
293
|
+
this.name = "RuntimeConfigError";
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
function composeSpawn(handle, config) {
|
|
297
|
+
const binArgs = [...handle.bin_args ?? []];
|
|
298
|
+
const env = {};
|
|
299
|
+
if (!config) return { binArgs, env };
|
|
300
|
+
if (config.mode !== void 0) {
|
|
301
|
+
const mode = (handle.modes ?? []).find((m) => m.id === config.mode);
|
|
302
|
+
if (!mode) {
|
|
303
|
+
const known = (handle.modes ?? []).map((m) => m.id).join(", ") || "(none)";
|
|
304
|
+
throw new RuntimeConfigError(
|
|
305
|
+
"unknown_mode",
|
|
306
|
+
"config.mode",
|
|
307
|
+
`Mode '${config.mode}' is not declared by manifest '${handle.id}'. Known modes: ${known}`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
if (mode.bin_args_append) binArgs.push(...mode.bin_args_append);
|
|
311
|
+
if (mode.env) Object.assign(env, mode.env);
|
|
312
|
+
}
|
|
313
|
+
if (config.options && Object.keys(config.options).length > 0) {
|
|
314
|
+
const declared = handle.options ?? [];
|
|
315
|
+
const declaredById = new Map(declared.map((o) => [o.id, o]));
|
|
316
|
+
for (const id of Object.keys(config.options)) {
|
|
317
|
+
if (!declaredById.has(id)) {
|
|
318
|
+
const known = declared.map((o) => o.id).join(", ") || "(none)";
|
|
319
|
+
throw new RuntimeConfigError(
|
|
320
|
+
"unknown_option",
|
|
321
|
+
`config.options.${id}`,
|
|
322
|
+
`Option '${id}' is not declared by manifest '${handle.id}'. Known options: ${known}`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const option of declared) {
|
|
327
|
+
if (!(option.id in config.options)) continue;
|
|
328
|
+
const value = config.options[option.id];
|
|
329
|
+
validateOptionValue(option, value);
|
|
330
|
+
const patch = renderOptionPatch(option, value);
|
|
331
|
+
binArgs.push(...patch.binArgs);
|
|
332
|
+
Object.assign(env, patch.env);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (config.continuation !== void 0 && handle.continuation) {
|
|
336
|
+
if (!handle.continuation.supported.includes(config.continuation)) {
|
|
337
|
+
throw new RuntimeConfigError(
|
|
338
|
+
"unsupported_continuation",
|
|
339
|
+
"config.continuation",
|
|
340
|
+
`Continuation strategy '${config.continuation}' is not in manifest '${handle.id}'.continuation.supported (${handle.continuation.supported.join(", ")}).`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return { binArgs, env };
|
|
345
|
+
}
|
|
346
|
+
function resolveContinuationStrategy(handle, config) {
|
|
347
|
+
if (config?.continuation) return config.continuation;
|
|
348
|
+
return handle.continuation?.default ?? "none";
|
|
349
|
+
}
|
|
350
|
+
function validateOptionValue(option, value) {
|
|
351
|
+
const path = `config.options.${option.id}`;
|
|
352
|
+
switch (option.type) {
|
|
353
|
+
case "boolean":
|
|
354
|
+
if (typeof value !== "boolean") {
|
|
355
|
+
throw new RuntimeConfigError(
|
|
356
|
+
"option_type_mismatch",
|
|
357
|
+
path,
|
|
358
|
+
`Expected boolean, got ${typeof value} (${JSON.stringify(value)}).`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
return;
|
|
362
|
+
case "integer":
|
|
363
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
364
|
+
throw new RuntimeConfigError(
|
|
365
|
+
"option_type_mismatch",
|
|
366
|
+
path,
|
|
367
|
+
`Expected integer, got ${typeof value === "number" ? "non-integer number" : typeof value} (${JSON.stringify(value)}).`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
if (option.min !== void 0 && value < option.min) {
|
|
371
|
+
throw new RuntimeConfigError(
|
|
372
|
+
"option_bounds_violation",
|
|
373
|
+
path,
|
|
374
|
+
`Value ${value} is below the declared minimum ${option.min}.`
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
if (option.max !== void 0 && value > option.max) {
|
|
378
|
+
throw new RuntimeConfigError(
|
|
379
|
+
"option_bounds_violation",
|
|
380
|
+
path,
|
|
381
|
+
`Value ${value} is above the declared maximum ${option.max}.`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
case "string":
|
|
386
|
+
if (typeof value !== "string") {
|
|
387
|
+
throw new RuntimeConfigError(
|
|
388
|
+
"option_type_mismatch",
|
|
389
|
+
path,
|
|
390
|
+
`Expected string, got ${typeof value} (${JSON.stringify(value)}).`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
return;
|
|
394
|
+
case "enum":
|
|
395
|
+
if (typeof value !== "string") {
|
|
396
|
+
throw new RuntimeConfigError(
|
|
397
|
+
"option_type_mismatch",
|
|
398
|
+
path,
|
|
399
|
+
`Expected string (enum value), got ${typeof value} (${JSON.stringify(value)}).`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
if (!(option.enum ?? []).includes(value)) {
|
|
403
|
+
throw new RuntimeConfigError(
|
|
404
|
+
"option_enum_violation",
|
|
405
|
+
path,
|
|
406
|
+
`Value '${value}' is not in the declared enum (${(option.enum ?? []).join(", ")}).`
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function renderOptionPatch(option, value) {
|
|
413
|
+
const stringValue = String(value);
|
|
414
|
+
if (option.type === "boolean") {
|
|
415
|
+
if (value !== true) return { binArgs: [], env: {} };
|
|
416
|
+
return {
|
|
417
|
+
binArgs: option.bin_args_append_when_true ? [...option.bin_args_append_when_true] : [],
|
|
418
|
+
env: option.env ? interpolateEnv(option.env, stringValue) : {}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
binArgs: option.bin_args_template ? option.bin_args_template.map(
|
|
423
|
+
(token) => token.replace(/\{value\}/g, stringValue)
|
|
424
|
+
) : [],
|
|
425
|
+
env: option.env ? interpolateEnv(option.env, stringValue) : {}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function interpolateEnv(env, value) {
|
|
429
|
+
const out = {};
|
|
430
|
+
for (const [k, v] of Object.entries(env)) {
|
|
431
|
+
out[k] = v.replace(/\{value\}/g, value);
|
|
432
|
+
}
|
|
433
|
+
return out;
|
|
434
|
+
}
|
|
435
|
+
var defineAgentCli = createDoctype(
|
|
436
|
+
{
|
|
437
|
+
aip: 45,
|
|
438
|
+
name: "agent-cli",
|
|
439
|
+
readIdentity: (def) => def.id,
|
|
440
|
+
validate(def) {
|
|
441
|
+
const result = agentCliFrontmatterSchema.safeParse(def);
|
|
442
|
+
if (!result.success) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
`defineAgentCli (AIP-45): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
const data = result.data;
|
|
448
|
+
if (data.protocol === "acp" && !data.acp) {
|
|
449
|
+
throw new Error(
|
|
450
|
+
"defineAgentCli (AIP-45): `acp` ref is required when protocol=acp"
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
if (data.protocol === "mcp" && !data.mcp) {
|
|
454
|
+
throw new Error(
|
|
455
|
+
"defineAgentCli (AIP-45): `mcp` block is required when protocol=mcp"
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
if (data.protocol === "proprietary" && !data.adapter) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
"defineAgentCli (AIP-45): `adapter` package is required when protocol=proprietary"
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
if (data.session?.mode === "resumable" && data.capabilities?.resumable !== true) {
|
|
464
|
+
throw new Error(
|
|
465
|
+
"defineAgentCli (AIP-45): session.mode=resumable requires capabilities.resumable: true"
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
build(def) {
|
|
470
|
+
return { ...def };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
);
|
|
474
|
+
function createAgentCliRuntime(definition) {
|
|
475
|
+
return {
|
|
476
|
+
definition,
|
|
477
|
+
async start(opts) {
|
|
478
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
479
|
+
const composed = composeSpawn(definition, opts?.config);
|
|
480
|
+
const env = {
|
|
481
|
+
...filterStringEnv(process.env),
|
|
482
|
+
...composed.env,
|
|
483
|
+
...opts?.env ?? {}
|
|
484
|
+
};
|
|
485
|
+
const child = spawn(definition.bin, composed.binArgs, {
|
|
486
|
+
cwd,
|
|
487
|
+
env,
|
|
488
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
489
|
+
signal: opts?.signal
|
|
490
|
+
});
|
|
491
|
+
const arm = buildProtocolArm(definition, child, cwd);
|
|
492
|
+
const abortController = new AbortController();
|
|
493
|
+
if (opts?.signal) {
|
|
494
|
+
opts.signal.addEventListener("abort", () => abortController.abort(), {
|
|
495
|
+
once: true
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
await arm.connect({
|
|
499
|
+
cwd,
|
|
500
|
+
env,
|
|
501
|
+
abortSignal: abortController.signal,
|
|
502
|
+
...opts?.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {}
|
|
503
|
+
});
|
|
504
|
+
const sessionId = arm.sessionId ?? randomUUID();
|
|
505
|
+
return {
|
|
506
|
+
sessionId,
|
|
507
|
+
send(message) {
|
|
508
|
+
const turnId = randomUUID();
|
|
509
|
+
return promptTurn(arm, turnId, message);
|
|
510
|
+
},
|
|
511
|
+
async cancel() {
|
|
512
|
+
abortController.abort();
|
|
513
|
+
},
|
|
514
|
+
async close() {
|
|
515
|
+
await arm.close();
|
|
516
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
async function* promptTurn(arm, turnId, message) {
|
|
523
|
+
await arm.send(turnId, message);
|
|
524
|
+
for await (const evt of arm.events()) yield evt;
|
|
525
|
+
}
|
|
526
|
+
function buildProtocolArm(def, child, cwd) {
|
|
527
|
+
switch (def.protocol) {
|
|
528
|
+
case "acp":
|
|
529
|
+
return createAcpProtocolArm({
|
|
530
|
+
child,
|
|
531
|
+
cwd,
|
|
532
|
+
clientInfo: { name: def.id, version: def.version }
|
|
533
|
+
});
|
|
534
|
+
case "mcp":
|
|
535
|
+
throw new Error("createAgentCliRuntime: mcp protocol arm not yet implemented");
|
|
536
|
+
case "proprietary":
|
|
537
|
+
throw new Error(
|
|
538
|
+
"createAgentCliRuntime: proprietary protocol arm not yet implemented"
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function filterStringEnv(env) {
|
|
543
|
+
const out = {};
|
|
544
|
+
for (const [k, v] of Object.entries(env)) {
|
|
545
|
+
if (typeof v === "string") out[k] = v;
|
|
546
|
+
}
|
|
547
|
+
return out;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export { RuntimeConfigError, agentCliFrontmatterSchema, composeSpawn, createAcpProtocolArm, createAgentCliRuntime, defineAgentCli, resolveContinuationStrategy, runtimeConfigSchema };
|
|
551
|
+
//# sourceMappingURL=chunk-EEIYDRIU.mjs.map
|
|
552
|
+
//# sourceMappingURL=chunk-EEIYDRIU.mjs.map
|