@rivet-dev/agentos-runtime-core 0.2.20-rc.1 → 0.2.21
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/commands/bash +0 -0
- package/commands/rg +0 -0
- package/commands/sh +0 -0
- package/commands/ssh +0 -0
- package/commands/unzip +0 -0
- package/commands/zip +0 -0
- package/dist/generated/HostFunctionLimitsConfig.d.ts +10 -0
- package/dist/generated/PermissionsPolicy.d.ts +1 -1
- package/dist/generated/VmLimitsConfig.d.ts +2 -2
- package/dist/generated-protocol.d.ts +2 -2
- package/dist/generated-protocol.js +4 -4
- package/dist/host-functions-zod.d.ts +9 -0
- package/dist/host-functions-zod.js +274 -0
- package/dist/host-functions.d.ts +95 -0
- package/dist/host-functions.js +54 -0
- package/dist/node-runtime-options-schema.d.ts +1 -1
- package/dist/node-runtime-options-schema.js +14 -13
- package/dist/node-runtime.d.ts +40 -39
- package/dist/node-runtime.js +34 -62
- package/dist/permissions.d.ts +1 -1
- package/dist/permissions.js +2 -2
- package/dist/process.d.ts +13 -1
- package/dist/process.js +65 -3
- package/dist/request-payloads.d.ts +1 -1
- package/dist/request-payloads.js +1 -1
- package/dist/sidecar-process.d.ts +2 -2
- package/dist/sidecar-process.js +2 -2
- package/dist/test-runtime.d.ts +12 -37
- package/dist/test-runtime.js +62 -53
- package/dist/vm-config.d.ts +1 -1
- package/package.json +14 -3
- package/dist/generated/BindingLimitsConfig.d.ts +0 -10
- /package/dist/generated/{BindingLimitsConfig.js → HostFunctionLimitsConfig.js} +0 -0
package/commands/bash
CHANGED
|
Binary file
|
package/commands/rg
CHANGED
|
Binary file
|
package/commands/sh
CHANGED
|
Binary file
|
package/commands/ssh
CHANGED
|
Binary file
|
package/commands/unzip
CHANGED
|
Binary file
|
package/commands/zip
CHANGED
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type HostFunctionLimitsConfig = {
|
|
2
|
+
defaultTimeoutMs?: number;
|
|
3
|
+
maxTimeoutMs?: number;
|
|
4
|
+
maxRegisteredCollections?: number;
|
|
5
|
+
maxRegisteredFunctionsPerVm?: number;
|
|
6
|
+
maxFunctionsPerCollection?: number;
|
|
7
|
+
maxSchemaBytes?: number;
|
|
8
|
+
maxExamplesPerFunction?: number;
|
|
9
|
+
maxExampleInputBytes?: number;
|
|
10
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AcpLimitsConfig } from "./AcpLimitsConfig.js";
|
|
2
|
-
import type { BindingLimitsConfig } from "./BindingLimitsConfig.js";
|
|
3
2
|
import type { ExecutionLimitsConfig } from "./ExecutionLimitsConfig.js";
|
|
3
|
+
import type { HostFunctionLimitsConfig } from "./HostFunctionLimitsConfig.js";
|
|
4
4
|
import type { Http2LimitsConfig } from "./Http2LimitsConfig.js";
|
|
5
5
|
import type { HttpLimitsConfig } from "./HttpLimitsConfig.js";
|
|
6
6
|
import type { JsRuntimeLimitsConfig } from "./JsRuntimeLimitsConfig.js";
|
|
@@ -20,7 +20,7 @@ export type VmLimitsConfig = {
|
|
|
20
20
|
udp?: UdpLimitsConfig;
|
|
21
21
|
tls?: TlsLimitsConfig;
|
|
22
22
|
http2?: Http2LimitsConfig;
|
|
23
|
-
|
|
23
|
+
hostFunctions?: HostFunctionLimitsConfig;
|
|
24
24
|
plugins?: PluginLimitsConfig;
|
|
25
25
|
acp?: AcpLimitsConfig;
|
|
26
26
|
sqlite?: SqliteLimitsConfig;
|
|
@@ -206,7 +206,7 @@ export type PermissionsPolicy = {
|
|
|
206
206
|
readonly childProcess: PatternPermissionScope | null;
|
|
207
207
|
readonly process: PatternPermissionScope | null;
|
|
208
208
|
readonly env: PatternPermissionScope | null;
|
|
209
|
-
readonly
|
|
209
|
+
readonly hostFunction: PatternPermissionScope | null;
|
|
210
210
|
};
|
|
211
211
|
export declare function readPermissionsPolicy(bc: bare.ByteCursor): PermissionsPolicy;
|
|
212
212
|
export declare function writePermissionsPolicy(bc: bare.ByteCursor, x: PermissionsPolicy): void;
|
|
@@ -339,7 +339,7 @@ export type ConfigureVmRequest = {
|
|
|
339
339
|
readonly packages: readonly PackageDescriptor[];
|
|
340
340
|
readonly packagesMountAt: string;
|
|
341
341
|
readonly bootstrapCommands: readonly string[];
|
|
342
|
-
readonly
|
|
342
|
+
readonly hostFunctionShimCommands: readonly string[];
|
|
343
343
|
};
|
|
344
344
|
export declare function readConfigureVmRequest(bc: bare.ByteCursor): ConfigureVmRequest;
|
|
345
345
|
export declare function writeConfigureVmRequest(bc: bare.ByteCursor, x: ConfigureVmRequest): void;
|
|
@@ -696,7 +696,7 @@ export function readPermissionsPolicy(bc) {
|
|
|
696
696
|
childProcess: read11(bc),
|
|
697
697
|
process: read11(bc),
|
|
698
698
|
env: read11(bc),
|
|
699
|
-
|
|
699
|
+
hostFunction: read11(bc),
|
|
700
700
|
};
|
|
701
701
|
}
|
|
702
702
|
export function writePermissionsPolicy(bc, x) {
|
|
@@ -705,7 +705,7 @@ export function writePermissionsPolicy(bc, x) {
|
|
|
705
705
|
write11(bc, x.childProcess);
|
|
706
706
|
write11(bc, x.process);
|
|
707
707
|
write11(bc, x.env);
|
|
708
|
-
write11(bc, x.
|
|
708
|
+
write11(bc, x.hostFunction);
|
|
709
709
|
}
|
|
710
710
|
export function readCreateVmRequest(bc) {
|
|
711
711
|
return {
|
|
@@ -1102,7 +1102,7 @@ export function readConfigureVmRequest(bc) {
|
|
|
1102
1102
|
packages: read20(bc),
|
|
1103
1103
|
packagesMountAt: bare.readString(bc),
|
|
1104
1104
|
bootstrapCommands: read6(bc),
|
|
1105
|
-
|
|
1105
|
+
hostFunctionShimCommands: read6(bc),
|
|
1106
1106
|
};
|
|
1107
1107
|
}
|
|
1108
1108
|
export function writeConfigureVmRequest(bc, x) {
|
|
@@ -1117,7 +1117,7 @@ export function writeConfigureVmRequest(bc, x) {
|
|
|
1117
1117
|
write20(bc, x.packages);
|
|
1118
1118
|
bare.writeString(bc, x.packagesMountAt);
|
|
1119
1119
|
write6(bc, x.bootstrapCommands);
|
|
1120
|
-
write6(bc, x.
|
|
1120
|
+
write6(bc, x.hostFunctionShimCommands);
|
|
1121
1121
|
}
|
|
1122
1122
|
export function readRegisteredHostCallbackExample(bc) {
|
|
1123
1123
|
return {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ZodType } from "zod";
|
|
2
|
+
export declare class HostFunctionSchemaConversionError extends Error {
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly zodType: string;
|
|
5
|
+
constructor(path: string, zodType: string, details?: string);
|
|
6
|
+
}
|
|
7
|
+
/** The `.describe()` text on a schema, if it has one. */
|
|
8
|
+
export declare function schemaDescription(schema: ZodType): string | undefined;
|
|
9
|
+
export declare function zodToJsonSchema(schema: ZodType): unknown;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { zodToJsonSchema as zodV3ToJsonSchema } from "zod-to-json-schema";
|
|
2
|
+
const OPTIONAL_WRAPPER_TYPES = new Set(["default", "optional"]);
|
|
3
|
+
const TRANSPARENT_WRAPPER_TYPES = new Set([
|
|
4
|
+
...OPTIONAL_WRAPPER_TYPES,
|
|
5
|
+
"branded",
|
|
6
|
+
"catch",
|
|
7
|
+
"readonly",
|
|
8
|
+
]);
|
|
9
|
+
const UNSUPPORTED_TYPES = new Set([
|
|
10
|
+
"bigint",
|
|
11
|
+
"date",
|
|
12
|
+
"effects",
|
|
13
|
+
"intersection",
|
|
14
|
+
"pipeline",
|
|
15
|
+
"pipe",
|
|
16
|
+
"tuple",
|
|
17
|
+
]);
|
|
18
|
+
export class HostFunctionSchemaConversionError extends Error {
|
|
19
|
+
path;
|
|
20
|
+
zodType;
|
|
21
|
+
constructor(path, zodType, details) {
|
|
22
|
+
super([
|
|
23
|
+
`Unsupported Zod schema at ${path}: ${zodType}`,
|
|
24
|
+
details ? `(${details})` : undefined,
|
|
25
|
+
]
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.join(" "));
|
|
28
|
+
this.name = "HostFunctionSchemaConversionError";
|
|
29
|
+
this.path = path;
|
|
30
|
+
this.zodType = zodType;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function getSchemaDef(schema) {
|
|
34
|
+
if (!schema || typeof schema !== "object") {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
return (schema._def ??
|
|
38
|
+
schema.def ??
|
|
39
|
+
{});
|
|
40
|
+
}
|
|
41
|
+
function getInnerSchema(schema) {
|
|
42
|
+
const def = getSchemaDef(schema);
|
|
43
|
+
return (def.innerType ?? def.schema ?? def.type ?? def.in);
|
|
44
|
+
}
|
|
45
|
+
function normalizeTypeName(schema) {
|
|
46
|
+
const def = getSchemaDef(schema);
|
|
47
|
+
const constructorName = String(schema.constructor?.name ?? "")
|
|
48
|
+
.replace(/^Zod/, "")
|
|
49
|
+
.toLowerCase();
|
|
50
|
+
if (constructorName === "discriminatedunion" || "discriminator" in def) {
|
|
51
|
+
return "discriminatedunion";
|
|
52
|
+
}
|
|
53
|
+
const rawTypeName = String(def.typeName ??
|
|
54
|
+
def.type ??
|
|
55
|
+
schema.type ??
|
|
56
|
+
constructorName);
|
|
57
|
+
return rawTypeName.replace(/^Zod/, "").toLowerCase();
|
|
58
|
+
}
|
|
59
|
+
function displayTypeName(typeName) {
|
|
60
|
+
switch (typeName) {
|
|
61
|
+
case "discriminatedunion":
|
|
62
|
+
return "discriminatedUnion";
|
|
63
|
+
case "nativeenum":
|
|
64
|
+
return "nativeEnum";
|
|
65
|
+
default:
|
|
66
|
+
return typeName;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** The `.describe()` text on a schema, if it has one. */
|
|
70
|
+
export function schemaDescription(schema) {
|
|
71
|
+
return getDescription(schema);
|
|
72
|
+
}
|
|
73
|
+
function getDescription(schema) {
|
|
74
|
+
const def = getSchemaDef(schema);
|
|
75
|
+
if (typeof def.description === "string") {
|
|
76
|
+
return def.description;
|
|
77
|
+
}
|
|
78
|
+
const instanceDescription = schema.description;
|
|
79
|
+
return typeof instanceDescription === "string"
|
|
80
|
+
? instanceDescription
|
|
81
|
+
: undefined;
|
|
82
|
+
}
|
|
83
|
+
function joinPath(parent, segment) {
|
|
84
|
+
return parent === "$" ? `$.${segment}` : `${parent}.${segment}`;
|
|
85
|
+
}
|
|
86
|
+
function metadataProducesRefs(schema) {
|
|
87
|
+
const meta = schema.meta?.();
|
|
88
|
+
return (typeof meta === "object" &&
|
|
89
|
+
meta !== null &&
|
|
90
|
+
("id" in meta || "$ref" in meta || "$defs" in meta));
|
|
91
|
+
}
|
|
92
|
+
function getChecks(schema) {
|
|
93
|
+
const checks = getSchemaDef(schema).checks;
|
|
94
|
+
return Array.isArray(checks) ? checks : [];
|
|
95
|
+
}
|
|
96
|
+
function isCustomRefinement(check) {
|
|
97
|
+
const checkDef = getSchemaDef(check);
|
|
98
|
+
const nestedDef = getSchemaDef(check._zod);
|
|
99
|
+
return (String(checkDef.check ?? nestedDef.check ?? "").toLowerCase() === "custom");
|
|
100
|
+
}
|
|
101
|
+
function validateChecks(schema, path, typeName) {
|
|
102
|
+
if (getChecks(schema).some(isCustomRefinement)) {
|
|
103
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "custom refinements cannot be represented faithfully in JSON Schema");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function validateSchema(schema, path) {
|
|
107
|
+
const typeName = normalizeTypeName(schema);
|
|
108
|
+
if (metadataProducesRefs(schema)) {
|
|
109
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "metadata that emits $ref/$defs is not supported");
|
|
110
|
+
}
|
|
111
|
+
if (UNSUPPORTED_TYPES.has(typeName)) {
|
|
112
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName));
|
|
113
|
+
}
|
|
114
|
+
if (typeName === "discriminatedunion") {
|
|
115
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName));
|
|
116
|
+
}
|
|
117
|
+
if (TRANSPARENT_WRAPPER_TYPES.has(typeName)) {
|
|
118
|
+
const inner = getInnerSchema(schema);
|
|
119
|
+
if (!inner) {
|
|
120
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "wrapper schema is missing its inner schema");
|
|
121
|
+
}
|
|
122
|
+
validateSchema(inner, path);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (typeName === "nullable") {
|
|
126
|
+
const inner = getInnerSchema(schema);
|
|
127
|
+
if (!inner) {
|
|
128
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "nullable schema is missing its inner schema");
|
|
129
|
+
}
|
|
130
|
+
validateSchema(inner, path);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
validateChecks(schema, path, typeName);
|
|
134
|
+
if (typeName === "object") {
|
|
135
|
+
const rawShape = getSchemaDef(schema).shape;
|
|
136
|
+
const shape = typeof rawShape === "function"
|
|
137
|
+
? rawShape()
|
|
138
|
+
: (rawShape ?? {});
|
|
139
|
+
for (const [fieldName, fieldSchema] of Object.entries(shape)) {
|
|
140
|
+
validateSchema(fieldSchema, joinPath(path, fieldName));
|
|
141
|
+
}
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (typeName === "array") {
|
|
145
|
+
const def = getSchemaDef(schema);
|
|
146
|
+
const itemSchema = (def.element ?? def.type);
|
|
147
|
+
if (!itemSchema) {
|
|
148
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "array schema is missing its item schema");
|
|
149
|
+
}
|
|
150
|
+
validateSchema(itemSchema, `${path}[]`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (typeName === "record") {
|
|
154
|
+
const def = getSchemaDef(schema);
|
|
155
|
+
const keySchema = def.keyType;
|
|
156
|
+
const valueSchema = def.valueType;
|
|
157
|
+
if (!keySchema || !valueSchema) {
|
|
158
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "record schema is missing its key or value schema");
|
|
159
|
+
}
|
|
160
|
+
const keyTypeName = normalizeTypeName(keySchema);
|
|
161
|
+
if (keyTypeName !== "string" || getChecks(keySchema).length > 0) {
|
|
162
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "record keys must be unconstrained strings");
|
|
163
|
+
}
|
|
164
|
+
validateSchema(valueSchema, `${path}<record-value>`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (typeName === "union") {
|
|
168
|
+
const options = getSchemaDef(schema).options;
|
|
169
|
+
if (!Array.isArray(options) || options.length === 0) {
|
|
170
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "union schema is missing its options");
|
|
171
|
+
}
|
|
172
|
+
for (let index = 0; index < options.length; index += 1) {
|
|
173
|
+
validateSchema(options[index], `${path}<union:${index}>`);
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (typeName === "literal") {
|
|
178
|
+
const def = getSchemaDef(schema);
|
|
179
|
+
const literalValues = Array.isArray(def.values)
|
|
180
|
+
? def.values
|
|
181
|
+
: "value" in def
|
|
182
|
+
? [def.value]
|
|
183
|
+
: [];
|
|
184
|
+
const [literalValue] = literalValues;
|
|
185
|
+
if (literalValues.length !== 1 ||
|
|
186
|
+
!("string number boolean".split(" ").includes(typeof literalValue) ||
|
|
187
|
+
literalValue === null)) {
|
|
188
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName), "literal values must be JSON primitives");
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (typeName === "string" ||
|
|
193
|
+
typeName === "number" ||
|
|
194
|
+
typeName === "boolean" ||
|
|
195
|
+
typeName === "enum" ||
|
|
196
|
+
typeName === "nativeenum") {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
throw new HostFunctionSchemaConversionError(path, displayTypeName(typeName));
|
|
200
|
+
}
|
|
201
|
+
function sanitizeJsonSchema(value) {
|
|
202
|
+
if (Array.isArray(value)) {
|
|
203
|
+
return value.map((item) => sanitizeJsonSchema(item));
|
|
204
|
+
}
|
|
205
|
+
if (!value || typeof value !== "object") {
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
const record = value;
|
|
209
|
+
const sanitized = {};
|
|
210
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
211
|
+
if (key === "$schema") {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (key === "additionalProperties" &&
|
|
215
|
+
entry === false &&
|
|
216
|
+
record.type === "object") {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
sanitized[key] = sanitizeJsonSchema(entry);
|
|
220
|
+
}
|
|
221
|
+
return sanitized;
|
|
222
|
+
}
|
|
223
|
+
function findUnsupportedGeneratedKeyword(value, path) {
|
|
224
|
+
if (Array.isArray(value)) {
|
|
225
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
226
|
+
const match = findUnsupportedGeneratedKeyword(value[index], `${path}[${index}]`);
|
|
227
|
+
if (match) {
|
|
228
|
+
return match;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
if (!value || typeof value !== "object") {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
237
|
+
if (key === "$ref" || key === "$defs") {
|
|
238
|
+
return { keyword: key, path };
|
|
239
|
+
}
|
|
240
|
+
const match = findUnsupportedGeneratedKeyword(entry, joinPath(path, key));
|
|
241
|
+
if (match) {
|
|
242
|
+
return match;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
function generateJsonSchema(schema) {
|
|
248
|
+
const nativeJsonSchema = schema.toJSONSchema?.();
|
|
249
|
+
if (nativeJsonSchema) {
|
|
250
|
+
return nativeJsonSchema;
|
|
251
|
+
}
|
|
252
|
+
const generated = zodV3ToJsonSchema(schema, {
|
|
253
|
+
$refStrategy: "none",
|
|
254
|
+
target: "jsonSchema7",
|
|
255
|
+
});
|
|
256
|
+
if (!generated ||
|
|
257
|
+
(typeof generated === "object" && Object.keys(generated).length === 0)) {
|
|
258
|
+
throw new HostFunctionSchemaConversionError("$", displayTypeName(normalizeTypeName(schema)), "schema cannot be converted to JSON Schema");
|
|
259
|
+
}
|
|
260
|
+
return generated;
|
|
261
|
+
}
|
|
262
|
+
export function zodToJsonSchema(schema) {
|
|
263
|
+
validateSchema(schema, "$");
|
|
264
|
+
const jsonSchema = generateJsonSchema(schema);
|
|
265
|
+
const unsupportedKeyword = findUnsupportedGeneratedKeyword(jsonSchema, "$");
|
|
266
|
+
if (unsupportedKeyword) {
|
|
267
|
+
throw new HostFunctionSchemaConversionError("$", displayTypeName(normalizeTypeName(schema)), `${unsupportedKeyword.keyword} emitted at ${unsupportedKeyword.path} is not supported`);
|
|
268
|
+
}
|
|
269
|
+
const sanitized = sanitizeJsonSchema(jsonSchema);
|
|
270
|
+
const description = getDescription(schema);
|
|
271
|
+
return description && typeof sanitized === "object" && sanitized !== null
|
|
272
|
+
? { ...sanitized, description }
|
|
273
|
+
: sanitized;
|
|
274
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { ZodType, z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* A single function that executes on the host.
|
|
4
|
+
*
|
|
5
|
+
* The function carries no name or description of its own: the key it is
|
|
6
|
+
* registered under names it, and `inputSchema.describe()` documents it for the
|
|
7
|
+
* agent.
|
|
8
|
+
*/
|
|
9
|
+
export interface HostFunction<SCHEMA extends ZodType = ZodType, OUTPUT = any> {
|
|
10
|
+
/**
|
|
11
|
+
* Zod schema for the input. Drives CLI flag generation and validation.
|
|
12
|
+
* `.describe()` on the schema becomes the function's description in `--help`
|
|
13
|
+
* and in the agent's system prompt; `.describe()` on a field documents that
|
|
14
|
+
* field's flag.
|
|
15
|
+
*/
|
|
16
|
+
inputSchema: SCHEMA;
|
|
17
|
+
/**
|
|
18
|
+
* Runs on the host when the agent invokes the function. Its input is the
|
|
19
|
+
* type `inputSchema` describes, inferred when the collection is written
|
|
20
|
+
* inline in the call that takes it.
|
|
21
|
+
*/
|
|
22
|
+
execute: (input: HostFunctionInput<SCHEMA>) => Promise<OUTPUT> | OUTPUT;
|
|
23
|
+
/** Examples included in auto-generated prompt docs. */
|
|
24
|
+
examples?: HostFunctionExample<HostFunctionInput<SCHEMA>>[];
|
|
25
|
+
/** Timeout in ms. Default: 30000. */
|
|
26
|
+
timeout?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The input type a function's `execute` receives. A concrete schema gives the
|
|
30
|
+
* type it describes; a collection built outside the call that takes it has no
|
|
31
|
+
* schema to infer from, so it falls back to `any` rather than making every
|
|
32
|
+
* handler annotate its parameter.
|
|
33
|
+
*/
|
|
34
|
+
export type HostFunctionInput<SCHEMA extends ZodType> = ZodType extends SCHEMA ? any : z.infer<SCHEMA>;
|
|
35
|
+
export interface HostFunctionExample<INPUT = any> {
|
|
36
|
+
/** Human description of what this example does. */
|
|
37
|
+
description: string;
|
|
38
|
+
/** The input args for the example. */
|
|
39
|
+
input: INPUT;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* One collection of host functions, keyed by function name. Each key becomes a
|
|
43
|
+
* subcommand of the collection's CLI binary and a method on the collection's
|
|
44
|
+
* guest global.
|
|
45
|
+
*/
|
|
46
|
+
export type HostFunctionCollection<T extends Record<string, ZodType> = Record<string, ZodType>> = {
|
|
47
|
+
[name in keyof T]: HostFunction<T[name]>;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* The input schemas behind a set of collections. Only used to give each
|
|
51
|
+
* `execute` its input type: `AgentOs.create()` infers this from the literal, so
|
|
52
|
+
* `execute` sees the type its own `inputSchema` describes without a wrapper
|
|
53
|
+
* call to hang the inference on.
|
|
54
|
+
*/
|
|
55
|
+
export type HostFunctionSchemas = Record<string, Record<string, ZodType>>;
|
|
56
|
+
/**
|
|
57
|
+
* Host function collections, keyed by collection name. Each key becomes the CLI
|
|
58
|
+
* binary `agentos-{name}` and a frozen guest global.
|
|
59
|
+
*
|
|
60
|
+
* ```ts
|
|
61
|
+
* hostFunctions: {
|
|
62
|
+
* store: {
|
|
63
|
+
* listOrders: { inputSchema, execute },
|
|
64
|
+
* },
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export type HostFunctionCollections<T extends HostFunctionSchemas = HostFunctionSchemas> = {
|
|
69
|
+
[collection in keyof T]: HostFunctionCollection<T[collection]>;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* A collection resolved to the names the VM uses. Keys arrive as JavaScript
|
|
73
|
+
* identifiers and are converted once here, so the rest of the client and the
|
|
74
|
+
* sidecar only ever see kebab-case command names.
|
|
75
|
+
*/
|
|
76
|
+
export interface ResolvedHostFunctions {
|
|
77
|
+
/** Kebab-case collection name. Becomes the CLI suffix: `agentos-{name}`. */
|
|
78
|
+
name: string;
|
|
79
|
+
/** Functions keyed by kebab-case command name. */
|
|
80
|
+
functions: Record<string, HostFunction>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Convert a registration key to its command name. `listOrders` and
|
|
84
|
+
* `list-orders` both become `list-orders`, so the guest sees one spelling
|
|
85
|
+
* whichever the caller wrote.
|
|
86
|
+
*/
|
|
87
|
+
export declare function hostFunctionCommandName(key: string): string;
|
|
88
|
+
/** The description the agent sees, taken from the input schema's `.describe()`. */
|
|
89
|
+
export declare function hostFunctionDescription(definition: HostFunction): string;
|
|
90
|
+
/**
|
|
91
|
+
* Resolve the caller's collections into the shape the client and sidecar use.
|
|
92
|
+
* Throws on a key that cannot become a command name, and on two keys that
|
|
93
|
+
* collide once converted.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolveHostFunctions(collections: HostFunctionCollections): ResolvedHostFunctions[];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { schemaDescription } from "./host-functions-zod.js";
|
|
2
|
+
const HOST_FUNCTION_COMMAND_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
3
|
+
/**
|
|
4
|
+
* Convert a registration key to its command name. `listOrders` and
|
|
5
|
+
* `list-orders` both become `list-orders`, so the guest sees one spelling
|
|
6
|
+
* whichever the caller wrote.
|
|
7
|
+
*/
|
|
8
|
+
export function hostFunctionCommandName(key) {
|
|
9
|
+
return key
|
|
10
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
11
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
|
|
12
|
+
.toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
function toCommandName(kind, key) {
|
|
15
|
+
const name = hostFunctionCommandName(key);
|
|
16
|
+
if (!HOST_FUNCTION_COMMAND_NAME_RE.test(name)) {
|
|
17
|
+
throw new Error(`${kind} name "${key}" must be alphanumeric, written in camelCase or with single hyphen separators`);
|
|
18
|
+
}
|
|
19
|
+
return name;
|
|
20
|
+
}
|
|
21
|
+
/** The description the agent sees, taken from the input schema's `.describe()`. */
|
|
22
|
+
export function hostFunctionDescription(definition) {
|
|
23
|
+
return schemaDescription(definition.inputSchema) ?? "";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the caller's collections into the shape the client and sidecar use.
|
|
27
|
+
* Throws on a key that cannot become a command name, and on two keys that
|
|
28
|
+
* collide once converted.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveHostFunctions(collections) {
|
|
31
|
+
const resolved = [];
|
|
32
|
+
const seenCollections = new Map();
|
|
33
|
+
for (const [collectionKey, collection] of Object.entries(collections)) {
|
|
34
|
+
const name = toCommandName("Host function collection", collectionKey);
|
|
35
|
+
const collidedCollection = seenCollections.get(name);
|
|
36
|
+
if (collidedCollection !== undefined) {
|
|
37
|
+
throw new Error(`Host function collections "${collidedCollection}" and "${collectionKey}" both resolve to the command name "${name}"`);
|
|
38
|
+
}
|
|
39
|
+
seenCollections.set(name, collectionKey);
|
|
40
|
+
const functions = {};
|
|
41
|
+
const seenFunctions = new Map();
|
|
42
|
+
for (const [functionKey, definition] of Object.entries(collection)) {
|
|
43
|
+
const functionName = toCommandName("Host function", functionKey);
|
|
44
|
+
const collidedFunction = seenFunctions.get(functionName);
|
|
45
|
+
if (collidedFunction !== undefined) {
|
|
46
|
+
throw new Error(`Host functions "${collidedFunction}" and "${functionKey}" in collection "${collectionKey}" both resolve to the command name "${functionName}"`);
|
|
47
|
+
}
|
|
48
|
+
seenFunctions.set(functionName, functionKey);
|
|
49
|
+
functions[functionName] = definition;
|
|
50
|
+
}
|
|
51
|
+
resolved.push({ name, functions });
|
|
52
|
+
}
|
|
53
|
+
return resolved;
|
|
54
|
+
}
|
|
@@ -86,7 +86,7 @@ export declare const nodeRuntimePermissionsSchema: z.ZodObject<{
|
|
|
86
86
|
patterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
87
87
|
}, z.core.$strict>>;
|
|
88
88
|
}, z.core.$strict>]>>;
|
|
89
|
-
|
|
89
|
+
hostFunction: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
90
90
|
allow: "allow";
|
|
91
91
|
deny: "deny";
|
|
92
92
|
}>, z.ZodObject<{
|
|
@@ -67,7 +67,10 @@ const patternRulePermissionsSchema = z
|
|
|
67
67
|
rules: z.array(patternPermissionRuleSchema),
|
|
68
68
|
})
|
|
69
69
|
.strict();
|
|
70
|
-
const fsPermissionsSchema = z.union([
|
|
70
|
+
const fsPermissionsSchema = z.union([
|
|
71
|
+
permissionModeSchema,
|
|
72
|
+
fsRulePermissionsSchema,
|
|
73
|
+
]);
|
|
71
74
|
const patternPermissionsSchema = z.union([
|
|
72
75
|
permissionModeSchema,
|
|
73
76
|
patternRulePermissionsSchema,
|
|
@@ -79,7 +82,7 @@ export const nodeRuntimePermissionsSchema = z
|
|
|
79
82
|
childProcess: patternPermissionsSchema.optional(),
|
|
80
83
|
process: patternPermissionsSchema.optional(),
|
|
81
84
|
env: patternPermissionsSchema.optional(),
|
|
82
|
-
|
|
85
|
+
hostFunction: patternPermissionsSchema.optional(),
|
|
83
86
|
})
|
|
84
87
|
.strict();
|
|
85
88
|
const uint8ArraySchema = z.custom((value) => value instanceof Uint8Array, { message: "Expected Uint8Array" });
|
|
@@ -104,20 +107,18 @@ const jsRuntimeSchema = z
|
|
|
104
107
|
highResolutionTime: z.boolean().optional(),
|
|
105
108
|
})
|
|
106
109
|
.strict();
|
|
107
|
-
const
|
|
110
|
+
const hostFunctionExampleSchema = z
|
|
108
111
|
.object({
|
|
109
112
|
description: z.string(),
|
|
110
113
|
input: z.unknown(),
|
|
111
114
|
})
|
|
112
115
|
.strict();
|
|
113
|
-
const
|
|
116
|
+
const hostFunctionDefinitionSchema = z
|
|
114
117
|
.object({
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
commandAliases: stringArray.optional(),
|
|
120
|
-
handler: z.custom((value) => typeof value === "function", { message: "Expected function" }),
|
|
118
|
+
inputSchema: z.custom((value) => typeof value === "object" && value !== null, { message: "Expected Zod schema object" }),
|
|
119
|
+
timeout: z.number().int().nonnegative().optional(),
|
|
120
|
+
examples: z.array(hostFunctionExampleSchema).optional(),
|
|
121
|
+
execute: z.custom((value) => typeof value === "function", { message: "Expected function" }),
|
|
121
122
|
})
|
|
122
123
|
.strict();
|
|
123
124
|
/**
|
|
@@ -151,10 +152,10 @@ export const nodeRuntimeCreateOptionsSchema = z
|
|
|
151
152
|
.optional(),
|
|
152
153
|
mounts: z.array(hostDirectoryMountSchema).optional(),
|
|
153
154
|
nodeModules: z.union([z.string(), nodeModulesMountSchema]).optional(),
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
.array(z.number().int().min(0).max(65535))
|
|
155
|
+
hostFunctions: z
|
|
156
|
+
.record(z.string(), hostFunctionDefinitionSchema)
|
|
157
157
|
.optional(),
|
|
158
|
+
loopbackExemptPorts: z.array(z.number().int().min(0).max(65535)).optional(),
|
|
158
159
|
jsRuntime: jsRuntimeSchema.optional(),
|
|
159
160
|
})
|
|
160
161
|
.strict();
|