@rivet-dev/agentos-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/README.md +1 -1
- package/dist/agent-os.d.ts +21 -20
- package/dist/agent-os.js +155 -201
- 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/host-dir-mount.d.ts +9 -1
- package/dist/host-dir-mount.js +13 -6
- package/dist/index.d.ts +9 -8
- package/dist/index.js +6 -5
- package/dist/language-execution.d.ts +5 -0
- package/dist/options-schema.d.ts +23 -22
- package/dist/options-schema.js +15 -21
- package/dist/runtime-compat.d.ts +6 -6
- package/dist/runtime.d.ts +6 -6
- package/dist/sandbox.d.ts +76 -4
- package/dist/sandbox.js +147 -138
- package/dist/sidecar/permissions.d.ts +5 -1
- package/dist/sidecar/permissions.js +8 -11
- package/dist/sidecar/rpc-client.d.ts +3 -3
- package/dist/sidecar/rpc-client.js +6 -6
- package/dist/types.d.ts +1 -1
- package/package.json +7 -7
- package/dist/bindings-zod.d.ts +0 -7
- package/dist/bindings-zod.js +0 -270
- package/dist/bindings.d.ts +0 -43
- package/dist/bindings.js +0 -34
- package/dist/generated/BindingLimitsConfig.d.ts +0 -10
- /package/dist/generated/{BindingLimitsConfig.js → HostFunctionLimitsConfig.js} +0 -0
package/dist/agent-os.js
CHANGED
|
@@ -5,8 +5,8 @@ import { join, posix as posixPath, resolve as resolveHostPath, } from "node:path
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import * as executionProtocol from "@rivet-dev/agentos-runtime-core/protocol";
|
|
7
7
|
import { SidecarRejectedError } from "@rivet-dev/agentos-runtime-core/sidecar-errors";
|
|
8
|
-
import {
|
|
9
|
-
import { zodToJsonSchema } from "
|
|
8
|
+
import { hostFunctionDescription, resolveHostFunctions, } from "@rivet-dev/agentos-runtime-core/host-functions";
|
|
9
|
+
import { zodToJsonSchema } from "@rivet-dev/agentos-runtime-core/host-functions-zod";
|
|
10
10
|
import { parseAgentOsOptions } from "./options-schema.js";
|
|
11
11
|
import { getSandboxDisposeHooks, resolveSandboxOptions, } from "./sandbox.js";
|
|
12
12
|
import { resolvePublishedSidecarBinary } from "./sidecar/binary.js";
|
|
@@ -168,7 +168,6 @@ import { resolveDefaultSoftware } from "./default-software.js";
|
|
|
168
168
|
import { snapshotVirtualFilesystem, sortFilesystemEntries, } from "./filesystem-snapshot.js";
|
|
169
169
|
import { serializeMountConfigForSidecar, } from "./js-bridge.js";
|
|
170
170
|
import { createSnapshotExport, } from "./layers.js";
|
|
171
|
-
import { allowAll } from "./runtime-compat.js";
|
|
172
171
|
import { decodeAcpCallback, decodeAcpEvent, decodeAcpResponse, encodeAcpCallbackResponse, encodeAcpRequest, } from "./sidecar/agentos-protocol.js";
|
|
173
172
|
import { serializePermissionsForSidecar } from "./sidecar/permissions.js";
|
|
174
173
|
import { createAgentOsSidecarClient, NativeSidecarKernelProxy, SidecarProcess, serializeRootFilesystemForSidecar, } from "./sidecar/rpc-client.js";
|
|
@@ -773,13 +772,13 @@ function collectSidecarMountPlan(options) {
|
|
|
773
772
|
hostPathMappings.sort((left, right) => right.vmPath.length - left.vmPath.length);
|
|
774
773
|
return { sidecarMounts, hostMounts, hostPathMappings };
|
|
775
774
|
}
|
|
776
|
-
function
|
|
777
|
-
if (
|
|
775
|
+
function collectHostFunctionBootstrapCommands(hostFunctions) {
|
|
776
|
+
if (hostFunctions.length === 0) {
|
|
778
777
|
return [];
|
|
779
778
|
}
|
|
780
779
|
return [
|
|
781
780
|
"agentos",
|
|
782
|
-
...
|
|
781
|
+
...hostFunctions.map((hostFunctionCollection) => `agentos-${hostFunctionCollection.name}`),
|
|
783
782
|
];
|
|
784
783
|
}
|
|
785
784
|
function validationMessage(error) {
|
|
@@ -798,9 +797,9 @@ function validationMessage(error) {
|
|
|
798
797
|
}
|
|
799
798
|
return error instanceof Error ? error.message : String(error);
|
|
800
799
|
}
|
|
801
|
-
function
|
|
800
|
+
function hostFunctionToSidecarDefinition(definition) {
|
|
802
801
|
return {
|
|
803
|
-
description: definition
|
|
802
|
+
description: hostFunctionDescription(definition),
|
|
804
803
|
inputSchema: zodToJsonSchema(definition.inputSchema),
|
|
805
804
|
...(definition.timeout !== undefined
|
|
806
805
|
? { timeoutMs: definition.timeout }
|
|
@@ -815,8 +814,8 @@ function bindingToSidecarDefinition(definition) {
|
|
|
815
814
|
: {}),
|
|
816
815
|
};
|
|
817
816
|
}
|
|
818
|
-
function combineInstructions(additionalInstructions,
|
|
819
|
-
const parts = [additionalInstructions,
|
|
817
|
+
function combineInstructions(additionalInstructions, hostFunctionReference) {
|
|
818
|
+
const parts = [additionalInstructions, hostFunctionReference]
|
|
820
819
|
.map((part) => part?.trim())
|
|
821
820
|
.filter((part) => Boolean(part));
|
|
822
821
|
if (parts.length === 0) {
|
|
@@ -824,48 +823,49 @@ function combineInstructions(additionalInstructions, bindingReference) {
|
|
|
824
823
|
}
|
|
825
824
|
return parts.join("\n\n");
|
|
826
825
|
}
|
|
827
|
-
function
|
|
828
|
-
if (
|
|
826
|
+
function buildHostFunctionReference(hostFunctions) {
|
|
827
|
+
if (hostFunctions.length === 0) {
|
|
829
828
|
return "";
|
|
830
829
|
}
|
|
831
830
|
const lines = [
|
|
832
|
-
"## Available Host
|
|
831
|
+
"## Available Host Functions",
|
|
833
832
|
"",
|
|
834
|
-
"Run `agentos list-
|
|
833
|
+
"Run `agentos list-host-functions` to see all available host functions.",
|
|
835
834
|
"",
|
|
836
835
|
];
|
|
837
|
-
for (const
|
|
838
|
-
lines.push(`### ${
|
|
836
|
+
for (const hostFunctionCollection of hostFunctions) {
|
|
837
|
+
lines.push(`### ${hostFunctionCollection.name}`);
|
|
839
838
|
lines.push("");
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
const sidecarBinding = bindingToSidecarDefinition(definition);
|
|
844
|
-
const signature = buildBindingFlagSignature(sidecarBinding.inputSchema);
|
|
839
|
+
for (const [functionName, definition] of Object.entries(hostFunctionCollection.functions)) {
|
|
840
|
+
const sidecarHostFunction = hostFunctionToSidecarDefinition(definition);
|
|
841
|
+
const signature = buildHostFunctionFlagSignature(sidecarHostFunction.inputSchema);
|
|
845
842
|
const suffix = signature.length > 0 ? ` ${signature}` : "";
|
|
846
|
-
|
|
843
|
+
const description = hostFunctionDescription(definition);
|
|
844
|
+
lines.push(description.length > 0
|
|
845
|
+
? `- \`agentos-${hostFunctionCollection.name} ${functionName}${suffix}\` - ${description}`
|
|
846
|
+
: `- \`agentos-${hostFunctionCollection.name} ${functionName}${suffix}\``);
|
|
847
847
|
}
|
|
848
848
|
lines.push("");
|
|
849
|
-
const
|
|
850
|
-
if (
|
|
849
|
+
const functionsWithExamples = Object.entries(hostFunctionCollection.functions).filter(([, definition]) => definition.examples && definition.examples.length > 0);
|
|
850
|
+
if (functionsWithExamples.length > 0) {
|
|
851
851
|
lines.push("**Examples:**");
|
|
852
852
|
lines.push("");
|
|
853
|
-
for (const [
|
|
853
|
+
for (const [functionName, definition] of functionsWithExamples) {
|
|
854
854
|
for (const example of definition.examples ?? []) {
|
|
855
|
-
const args =
|
|
855
|
+
const args = inputToHostFunctionFlags(example.input);
|
|
856
856
|
const suffix = args.length > 0 ? ` ${args}` : "";
|
|
857
|
-
lines.push(`- ${example.description}: \`agentos-${
|
|
857
|
+
lines.push(`- ${example.description}: \`agentos-${hostFunctionCollection.name} ${functionName}${suffix}\``);
|
|
858
858
|
}
|
|
859
859
|
}
|
|
860
860
|
lines.push("");
|
|
861
861
|
}
|
|
862
|
-
lines.push(`Run \`agentos-${
|
|
862
|
+
lines.push(`Run \`agentos-${hostFunctionCollection.name} <function> --help\` for details.`);
|
|
863
863
|
lines.push("");
|
|
864
864
|
}
|
|
865
865
|
return lines.join("\n");
|
|
866
866
|
}
|
|
867
|
-
function
|
|
868
|
-
return
|
|
867
|
+
function buildHostFunctionFlagSignature(schema) {
|
|
868
|
+
return describeHostFunctionFlags(schema)
|
|
869
869
|
.map((flag) => {
|
|
870
870
|
if (flag.required) {
|
|
871
871
|
return `${flag.name} <${flag.type}>`;
|
|
@@ -874,7 +874,7 @@ function buildBindingFlagSignature(schema) {
|
|
|
874
874
|
})
|
|
875
875
|
.join(" ");
|
|
876
876
|
}
|
|
877
|
-
function
|
|
877
|
+
function describeHostFunctionFlags(schema) {
|
|
878
878
|
const schemaObject = asRecord(schema);
|
|
879
879
|
const properties = asRecord(schemaObject.properties);
|
|
880
880
|
const required = Array.isArray(schemaObject.required)
|
|
@@ -882,11 +882,11 @@ function describeBindingFlags(schema) {
|
|
|
882
882
|
: new Set();
|
|
883
883
|
return Object.entries(properties).map(([fieldName, fieldSchema]) => ({
|
|
884
884
|
name: `--${camelToKebab(fieldName)}`,
|
|
885
|
-
type:
|
|
885
|
+
type: describeHostFunctionFlagType(fieldSchema),
|
|
886
886
|
required: required.has(fieldName),
|
|
887
887
|
}));
|
|
888
888
|
}
|
|
889
|
-
function
|
|
889
|
+
function describeHostFunctionFlagType(schema) {
|
|
890
890
|
const schemaObject = asRecord(schema);
|
|
891
891
|
const type = typeof schemaObject.type === "string" ? schemaObject.type : undefined;
|
|
892
892
|
if (type === "array") {
|
|
@@ -905,7 +905,7 @@ function describeJsonSchemaScalarType(schema) {
|
|
|
905
905
|
const schemaObject = asRecord(schema);
|
|
906
906
|
return typeof schemaObject.type === "string" ? schemaObject.type : "string";
|
|
907
907
|
}
|
|
908
|
-
function
|
|
908
|
+
function inputToHostFunctionFlags(input) {
|
|
909
909
|
const inputObject = asRecord(input);
|
|
910
910
|
return Object.entries(inputObject)
|
|
911
911
|
.flatMap(([key, value]) => {
|
|
@@ -917,13 +917,13 @@ function inputToBindingFlags(input) {
|
|
|
917
917
|
return [`--no-${camelToKebab(key)}`];
|
|
918
918
|
}
|
|
919
919
|
if (Array.isArray(value)) {
|
|
920
|
-
return value.map((item) => `${flag} ${
|
|
920
|
+
return value.map((item) => `${flag} ${hostFunctionCliString(item)}`);
|
|
921
921
|
}
|
|
922
|
-
return [`${flag} ${
|
|
922
|
+
return [`${flag} ${hostFunctionCliString(value)}`];
|
|
923
923
|
})
|
|
924
924
|
.join(" ");
|
|
925
925
|
}
|
|
926
|
-
function
|
|
926
|
+
function hostFunctionCliString(value) {
|
|
927
927
|
return typeof value === "string" ? value : (JSON.stringify(value) ?? "null");
|
|
928
928
|
}
|
|
929
929
|
function camelToKebab(value) {
|
|
@@ -961,22 +961,16 @@ async function handleHostCallback(request, context) {
|
|
|
961
961
|
};
|
|
962
962
|
}
|
|
963
963
|
}
|
|
964
|
-
const definition = context.
|
|
964
|
+
const definition = context.hostFunctionMap.get(payload.callback_key);
|
|
965
965
|
if (!definition) {
|
|
966
966
|
return {
|
|
967
967
|
type: "host_callback_result",
|
|
968
968
|
invocation_id: payload.invocation_id,
|
|
969
|
-
error: `Unknown
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
const permissionMode = bindingPermissionMode(context.permissions, payload.callback_key);
|
|
973
|
-
if (permissionMode !== "allow") {
|
|
974
|
-
return {
|
|
975
|
-
type: "host_callback_result",
|
|
976
|
-
invocation_id: payload.invocation_id,
|
|
977
|
-
error: `EACCES: blocked by binding.invoke policy for ${payload.callback_key}`,
|
|
969
|
+
error: `Unknown host function "${payload.callback_key}"`,
|
|
978
970
|
};
|
|
979
971
|
}
|
|
972
|
+
// The sidecar checks the `hostFunction` permission scope before it forwards a
|
|
973
|
+
// host function call, so a call that reaches the host is already permitted.
|
|
980
974
|
const parsed = definition.inputSchema.safeParse(payload.input);
|
|
981
975
|
if (!parsed.success) {
|
|
982
976
|
return {
|
|
@@ -989,7 +983,7 @@ async function handleHostCallback(request, context) {
|
|
|
989
983
|
return {
|
|
990
984
|
type: "host_callback_result",
|
|
991
985
|
invocation_id: payload.invocation_id,
|
|
992
|
-
result: await
|
|
986
|
+
result: await executeHostFunction(definition, payload.callback_key, parsed.data),
|
|
993
987
|
};
|
|
994
988
|
}
|
|
995
989
|
catch (error) {
|
|
@@ -1000,14 +994,14 @@ async function handleHostCallback(request, context) {
|
|
|
1000
994
|
};
|
|
1001
995
|
}
|
|
1002
996
|
}
|
|
1003
|
-
function
|
|
1004
|
-
const
|
|
1005
|
-
for (const
|
|
1006
|
-
for (const [
|
|
1007
|
-
|
|
997
|
+
function buildHostFunctionMap(hostFunctions) {
|
|
998
|
+
const hostFunctionMap = new Map();
|
|
999
|
+
for (const hostFunctionCollection of hostFunctions) {
|
|
1000
|
+
for (const [functionName, definition] of Object.entries(hostFunctionCollection.functions)) {
|
|
1001
|
+
hostFunctionMap.set(`${hostFunctionCollection.name}:${functionName}`, definition);
|
|
1008
1002
|
}
|
|
1009
1003
|
}
|
|
1010
|
-
return
|
|
1004
|
+
return hostFunctionMap;
|
|
1011
1005
|
}
|
|
1012
1006
|
function bridgeErrorMessage(error) {
|
|
1013
1007
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -1154,75 +1148,73 @@ function parseHostCommandCallbackInput(input) {
|
|
|
1154
1148
|
};
|
|
1155
1149
|
}
|
|
1156
1150
|
async function handleHostCommandCallback(command, context) {
|
|
1157
|
-
const
|
|
1151
|
+
const directHostFunctions = context.hostFunctions.find((hostFunctionCollection) => `agentos-${hostFunctionCollection.name}` === command.command);
|
|
1158
1152
|
if (command.command === "agentos") {
|
|
1159
1153
|
return handleAgentOsRegistryCommand(command, context);
|
|
1160
1154
|
}
|
|
1161
|
-
if (
|
|
1162
|
-
return
|
|
1155
|
+
if (directHostFunctions) {
|
|
1156
|
+
return handleAgentOsHostFunctionCommand(command, context, directHostFunctions);
|
|
1163
1157
|
}
|
|
1164
1158
|
throw new Error(`Unknown host callback command "${command.command}"`);
|
|
1165
1159
|
}
|
|
1166
1160
|
async function handleAgentOsRegistryCommand(command, context) {
|
|
1167
|
-
const [subcommand, collectionName,
|
|
1161
|
+
const [subcommand, collectionName, functionName, ...functionArgs] = command.args;
|
|
1168
1162
|
if (!subcommand || isHelpFlag(subcommand)) {
|
|
1169
1163
|
return {
|
|
1170
|
-
usage: "agentos <command>: list-
|
|
1164
|
+
usage: "agentos <command>: list-host-functions [collection], <collection> --help, or <collection> <function> ...",
|
|
1171
1165
|
};
|
|
1172
1166
|
}
|
|
1173
|
-
if (subcommand === "list-bindings") {
|
|
1167
|
+
if (subcommand === "list-host-functions" || subcommand === "list-bindings") {
|
|
1174
1168
|
return collectionName
|
|
1175
|
-
?
|
|
1176
|
-
:
|
|
1169
|
+
? describeHostFunctionsPayload(context.hostFunctions, collectionName)
|
|
1170
|
+
: listHostFunctionsPayload(context.hostFunctions);
|
|
1177
1171
|
}
|
|
1178
|
-
const
|
|
1179
|
-
if (!
|
|
1180
|
-
throw new Error(`No
|
|
1172
|
+
const hostFunctionCollection = context.hostFunctions.find((collection) => collection.name === subcommand);
|
|
1173
|
+
if (!hostFunctionCollection) {
|
|
1174
|
+
throw new Error(`No host function collection "${subcommand}". Available: ${hostFunctionsNames(context.hostFunctions)}`);
|
|
1181
1175
|
}
|
|
1182
1176
|
if (!collectionName || isHelpFlag(collectionName)) {
|
|
1183
|
-
return
|
|
1177
|
+
return describeHostFunctionsPayload(context.hostFunctions, subcommand);
|
|
1184
1178
|
}
|
|
1185
|
-
if (
|
|
1186
|
-
return
|
|
1179
|
+
if (functionName && isHelpFlag(functionName)) {
|
|
1180
|
+
return describeHostFunctionPayload(hostFunctionCollection, collectionName);
|
|
1187
1181
|
}
|
|
1188
|
-
return
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
args: [
|
|
1182
|
+
return invokeHostFunction({
|
|
1183
|
+
hostFunctionCollection,
|
|
1184
|
+
functionName: collectionName,
|
|
1185
|
+
args: [functionName, ...functionArgs].filter((value) => typeof value === "string"),
|
|
1192
1186
|
cwd: command.cwd,
|
|
1193
1187
|
context,
|
|
1194
1188
|
});
|
|
1195
1189
|
}
|
|
1196
|
-
async function
|
|
1197
|
-
const [
|
|
1198
|
-
if (!
|
|
1199
|
-
return
|
|
1190
|
+
async function handleAgentOsHostFunctionCommand(command, context, hostFunctionCollection) {
|
|
1191
|
+
const [functionName, helpOrFirstArg, ...rest] = command.args;
|
|
1192
|
+
if (!functionName || isHelpFlag(functionName)) {
|
|
1193
|
+
return describeHostFunctionsPayload(context.hostFunctions, hostFunctionCollection.name);
|
|
1200
1194
|
}
|
|
1201
1195
|
if (helpOrFirstArg && isHelpFlag(helpOrFirstArg)) {
|
|
1202
|
-
return
|
|
1196
|
+
return describeHostFunctionPayload(hostFunctionCollection, functionName);
|
|
1203
1197
|
}
|
|
1204
|
-
return
|
|
1205
|
-
|
|
1206
|
-
|
|
1198
|
+
return invokeHostFunction({
|
|
1199
|
+
hostFunctionCollection,
|
|
1200
|
+
functionName,
|
|
1207
1201
|
args: [helpOrFirstArg, ...rest].filter((value) => typeof value === "string"),
|
|
1208
1202
|
cwd: command.cwd,
|
|
1209
1203
|
context,
|
|
1210
1204
|
});
|
|
1211
1205
|
}
|
|
1212
|
-
async function
|
|
1213
|
-
const definition =
|
|
1206
|
+
async function invokeHostFunction({ hostFunctionCollection, functionName, args, cwd, context, }) {
|
|
1207
|
+
const definition = hostFunctionCollection.functions[functionName];
|
|
1214
1208
|
if (!definition) {
|
|
1215
|
-
throw new Error(`No
|
|
1216
|
-
}
|
|
1217
|
-
const callbackKey = `${bindingCollection.name}:${bindingName}`;
|
|
1218
|
-
const permissionMode = bindingPermissionMode(context.permissions, callbackKey);
|
|
1219
|
-
if (permissionMode !== "allow") {
|
|
1220
|
-
throw new Error(`EACCES: blocked by binding.invoke policy for ${callbackKey}`);
|
|
1209
|
+
throw new Error(`No host function "${functionName}" in collection "${hostFunctionCollection.name}". Available: ${hostFunctionNames(hostFunctionCollection)}`);
|
|
1221
1210
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1211
|
+
// The sidecar checks the `hostFunction` permission scope for registry commands
|
|
1212
|
+
// before forwarding them, the same as for collection commands.
|
|
1213
|
+
const callbackKey = `${hostFunctionCollection.name}:${functionName}`;
|
|
1214
|
+
const input = await parseHostFunctionInput(definition, args, cwd, context.readFile);
|
|
1215
|
+
return executeHostFunction(definition, callbackKey, input);
|
|
1224
1216
|
}
|
|
1225
|
-
async function
|
|
1217
|
+
async function executeHostFunction(definition, callbackKey, input) {
|
|
1226
1218
|
const parsed = definition.inputSchema.safeParse(input);
|
|
1227
1219
|
if (!parsed.success) {
|
|
1228
1220
|
throw new Error(validationMessage(parsed.error));
|
|
@@ -1233,11 +1225,11 @@ async function executeBinding(definition, callbackKey, input) {
|
|
|
1233
1225
|
if (definition.timeout === undefined) {
|
|
1234
1226
|
return;
|
|
1235
1227
|
}
|
|
1236
|
-
setTimeout(() => reject(new Error(`
|
|
1228
|
+
setTimeout(() => reject(new Error(`Host function "${callbackKey}" timed out after ${definition.timeout}ms`)), definition.timeout);
|
|
1237
1229
|
}),
|
|
1238
1230
|
]);
|
|
1239
1231
|
}
|
|
1240
|
-
async function
|
|
1232
|
+
async function parseHostFunctionInput(definition, args, cwd, readFile) {
|
|
1241
1233
|
if (args[0] === "--json") {
|
|
1242
1234
|
const value = args[1];
|
|
1243
1235
|
if (value === undefined) {
|
|
@@ -1256,9 +1248,9 @@ async function parseBindingInput(definition, args, cwd, readFile) {
|
|
|
1256
1248
|
const text = new TextDecoder().decode(await readFile(guestPath));
|
|
1257
1249
|
return JSON.parse(text);
|
|
1258
1250
|
}
|
|
1259
|
-
return
|
|
1251
|
+
return parseHostFunctionArgv(hostFunctionToSidecarDefinition(definition).inputSchema, args);
|
|
1260
1252
|
}
|
|
1261
|
-
function
|
|
1253
|
+
function parseHostFunctionArgv(schema, argv) {
|
|
1262
1254
|
const schemaObject = asRecord(schema);
|
|
1263
1255
|
const properties = asRecord(schemaObject.properties);
|
|
1264
1256
|
const required = Array.isArray(schemaObject.required)
|
|
@@ -1330,88 +1322,53 @@ function parseBindingArgv(schema, argv) {
|
|
|
1330
1322
|
}
|
|
1331
1323
|
return input;
|
|
1332
1324
|
}
|
|
1333
|
-
function
|
|
1325
|
+
function listHostFunctionsPayload(hostFunctions) {
|
|
1334
1326
|
return {
|
|
1335
|
-
|
|
1336
|
-
name:
|
|
1337
|
-
|
|
1338
|
-
bindings: Object.keys(bindingCollection.bindings),
|
|
1327
|
+
hostFunctions: hostFunctions.map((hostFunctionCollection) => ({
|
|
1328
|
+
name: hostFunctionCollection.name,
|
|
1329
|
+
functions: Object.keys(hostFunctionCollection.functions),
|
|
1339
1330
|
})),
|
|
1340
1331
|
};
|
|
1341
1332
|
}
|
|
1342
|
-
function
|
|
1343
|
-
const
|
|
1344
|
-
if (!
|
|
1345
|
-
throw new Error(`No
|
|
1333
|
+
function describeHostFunctionsPayload(hostFunctions, collectionName) {
|
|
1334
|
+
const hostFunctionCollection = hostFunctions.find((collection) => collection.name === collectionName);
|
|
1335
|
+
if (!hostFunctionCollection) {
|
|
1336
|
+
throw new Error(`No host function collection "${collectionName}". Available: ${hostFunctionsNames(hostFunctions)}`);
|
|
1346
1337
|
}
|
|
1347
1338
|
return {
|
|
1348
|
-
name:
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
bindingName,
|
|
1339
|
+
name: hostFunctionCollection.name,
|
|
1340
|
+
functions: Object.fromEntries(Object.entries(hostFunctionCollection.functions).map(([functionName, definition]) => [
|
|
1341
|
+
functionName,
|
|
1352
1342
|
{
|
|
1353
|
-
description: definition
|
|
1354
|
-
flags:
|
|
1343
|
+
description: hostFunctionDescription(definition),
|
|
1344
|
+
flags: describeHostFunctionFlags(hostFunctionToSidecarDefinition(definition).inputSchema),
|
|
1355
1345
|
},
|
|
1356
1346
|
])),
|
|
1357
1347
|
};
|
|
1358
1348
|
}
|
|
1359
|
-
function
|
|
1360
|
-
const definition =
|
|
1349
|
+
function describeHostFunctionPayload(hostFunctionCollection, functionName) {
|
|
1350
|
+
const definition = hostFunctionCollection.functions[functionName];
|
|
1361
1351
|
if (!definition) {
|
|
1362
|
-
throw new Error(`No
|
|
1352
|
+
throw new Error(`No host function "${functionName}" in collection "${hostFunctionCollection.name}". Available: ${hostFunctionNames(hostFunctionCollection)}`);
|
|
1363
1353
|
}
|
|
1364
1354
|
return {
|
|
1365
|
-
collection:
|
|
1366
|
-
|
|
1367
|
-
description: definition
|
|
1368
|
-
flags:
|
|
1355
|
+
collection: hostFunctionCollection.name,
|
|
1356
|
+
function: functionName,
|
|
1357
|
+
description: hostFunctionDescription(definition),
|
|
1358
|
+
flags: describeHostFunctionFlags(hostFunctionToSidecarDefinition(definition).inputSchema),
|
|
1369
1359
|
examples: definition.examples?.map((example) => ({
|
|
1370
1360
|
description: example.description,
|
|
1371
1361
|
input: example.input,
|
|
1372
1362
|
})) ?? [],
|
|
1373
1363
|
};
|
|
1374
1364
|
}
|
|
1375
|
-
function
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
}
|
|
1380
|
-
if (typeof scope === "string") {
|
|
1381
|
-
return scope;
|
|
1382
|
-
}
|
|
1383
|
-
let mode = scope.default ?? "deny";
|
|
1384
|
-
for (const rule of scope.rules) {
|
|
1385
|
-
const operations = rule.operations ?? ["*"];
|
|
1386
|
-
const patterns = rule.patterns ?? ["**"];
|
|
1387
|
-
if (operations.some((operation) => operation === "*" || operation === "invoke") &&
|
|
1388
|
-
patterns.some((pattern) => permissionPatternMatches(pattern, callbackKey))) {
|
|
1389
|
-
mode = rule.mode;
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
return mode;
|
|
1393
|
-
}
|
|
1394
|
-
function permissionPatternMatches(pattern, value) {
|
|
1395
|
-
if (pattern === "*" || pattern === "**" || pattern === value) {
|
|
1396
|
-
return true;
|
|
1397
|
-
}
|
|
1398
|
-
const parts = pattern.split(/(\*\*|\*)/u);
|
|
1399
|
-
const source = parts
|
|
1400
|
-
.map((part) => {
|
|
1401
|
-
if (part === "**")
|
|
1402
|
-
return ".*";
|
|
1403
|
-
if (part === "*")
|
|
1404
|
-
return "[^:]*";
|
|
1405
|
-
return part.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1406
|
-
})
|
|
1407
|
-
.join("");
|
|
1408
|
-
return new RegExp(`^${source}$`).test(value);
|
|
1365
|
+
function hostFunctionsNames(hostFunctions) {
|
|
1366
|
+
return hostFunctions
|
|
1367
|
+
.map((hostFunctionCollection) => hostFunctionCollection.name)
|
|
1368
|
+
.join(", ");
|
|
1409
1369
|
}
|
|
1410
|
-
function
|
|
1411
|
-
return
|
|
1412
|
-
}
|
|
1413
|
-
function bindingNames(bindingCollection) {
|
|
1414
|
-
return Object.keys(bindingCollection.bindings).join(", ");
|
|
1370
|
+
function hostFunctionNames(hostFunctionCollection) {
|
|
1371
|
+
return Object.keys(hostFunctionCollection.functions).join(", ");
|
|
1415
1372
|
}
|
|
1416
1373
|
function isHelpFlag(value) {
|
|
1417
1374
|
return value === "--help" || value === "-h";
|
|
@@ -1420,23 +1377,23 @@ function jsonSchemaType(schema) {
|
|
|
1420
1377
|
const schemaObject = asRecord(schema);
|
|
1421
1378
|
return typeof schemaObject.type === "string" ? schemaObject.type : undefined;
|
|
1422
1379
|
}
|
|
1423
|
-
async function
|
|
1424
|
-
if (
|
|
1380
|
+
async function registerHostFunctionsOnSidecar(client, session, vm, hostFunctions) {
|
|
1381
|
+
if (hostFunctions.length === 0) {
|
|
1425
1382
|
return "";
|
|
1426
1383
|
}
|
|
1427
|
-
for (const
|
|
1384
|
+
for (const hostFunctionCollection of hostFunctions) {
|
|
1428
1385
|
await client.registerHostCallbacks(session, vm, {
|
|
1429
|
-
name:
|
|
1430
|
-
description:
|
|
1431
|
-
commandAliases: [`agentos-${
|
|
1386
|
+
name: hostFunctionCollection.name,
|
|
1387
|
+
description: "",
|
|
1388
|
+
commandAliases: [`agentos-${hostFunctionCollection.name}`],
|
|
1432
1389
|
registryCommandAliases: ["agentos"],
|
|
1433
|
-
callbacks: Object.fromEntries(Object.entries(
|
|
1434
|
-
|
|
1435
|
-
|
|
1390
|
+
callbacks: Object.fromEntries(Object.entries(hostFunctionCollection.functions).map(([functionName, definition]) => [
|
|
1391
|
+
functionName,
|
|
1392
|
+
hostFunctionToSidecarDefinition(definition),
|
|
1436
1393
|
])),
|
|
1437
1394
|
});
|
|
1438
1395
|
}
|
|
1439
|
-
return
|
|
1396
|
+
return buildHostFunctionReference(hostFunctions);
|
|
1440
1397
|
}
|
|
1441
1398
|
function executionIdentity(options) {
|
|
1442
1399
|
return {
|
|
@@ -1654,9 +1611,8 @@ export class AgentOs {
|
|
|
1654
1611
|
_acpTerminalCounter = 0;
|
|
1655
1612
|
_softwareRoots;
|
|
1656
1613
|
_cronManager;
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
_permissions = allowAll;
|
|
1614
|
+
_hostFunctions = [];
|
|
1615
|
+
_hostFunctionReference = "";
|
|
1660
1616
|
_hostMounts;
|
|
1661
1617
|
_env;
|
|
1662
1618
|
_rootFilesystem;
|
|
@@ -1811,8 +1767,11 @@ export class AgentOs {
|
|
|
1811
1767
|
static async getSharedSidecar(options = {}) {
|
|
1812
1768
|
return getSharedAgentOsSidecarInternal(options);
|
|
1813
1769
|
}
|
|
1814
|
-
static async create(
|
|
1815
|
-
|
|
1770
|
+
static async create(callerOptions) {
|
|
1771
|
+
// The generic exists only so each `execute` infers its input from its own
|
|
1772
|
+
// `inputSchema`. Past this point the concrete schemas carry no meaning, so
|
|
1773
|
+
// the rest of `create()` works with the plain option type.
|
|
1774
|
+
let options = parseAgentOsOptions(callerOptions);
|
|
1816
1775
|
// Default software is FULLY DYNAMIC: this package's own NON-agent
|
|
1817
1776
|
// @agentos-software/* dependencies (e.g. common), each default-exporting
|
|
1818
1777
|
// its registry-built descriptor. Agent packages are NOT projected here —
|
|
@@ -1847,27 +1806,29 @@ export class AgentOs {
|
|
|
1847
1806
|
// sidecar owns agent resolution, agent enumeration, and agent snapshot
|
|
1848
1807
|
// bundle loading from the projected package dirs.
|
|
1849
1808
|
const localMounts = await resolveCompatLocalMounts(options?.mounts);
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1809
|
+
// Keys become command names, so resolve and check them before anything
|
|
1810
|
+
// else in `create()` allocates a sidecar or a sandbox.
|
|
1811
|
+
const resolvedHostFunctions = options?.hostFunctions
|
|
1812
|
+
? resolveHostFunctions(options.hostFunctions)
|
|
1813
|
+
: [];
|
|
1853
1814
|
// Resolve the sidecar handle before starting an external sandbox so option
|
|
1854
1815
|
// validation failures cannot leak provider resources.
|
|
1855
1816
|
const sidecar = resolveAgentOsSidecar(options?.sidecar);
|
|
1856
1817
|
options = await resolveSandboxOptions(options);
|
|
1857
1818
|
const sandboxDisposeHooks = getSandboxDisposeHooks(options);
|
|
1858
|
-
const
|
|
1819
|
+
const hostFunctions = resolvedHostFunctions;
|
|
1859
1820
|
const createVmAdmin = async () => {
|
|
1860
1821
|
// The `/opt/agentos` projection is built by the sidecar from the
|
|
1861
1822
|
// forwarded `packages` (it owns the staging dir + read-only mount, and
|
|
1862
1823
|
// runtime `linkSoftware` appends to that live dir). The client no longer
|
|
1863
1824
|
// stages packages host-side.
|
|
1864
|
-
const
|
|
1825
|
+
const hostFunctionBootstrapCommands = collectHostFunctionBootstrapCommands(hostFunctions);
|
|
1865
1826
|
const bootstrapCommands = [
|
|
1866
1827
|
...RUNTIME_BOOTSTRAP_COMMANDS,
|
|
1867
|
-
...
|
|
1828
|
+
...hostFunctionBootstrapCommands,
|
|
1868
1829
|
];
|
|
1869
1830
|
const bootstrapLower = createKernelBootstrapLower(options?.rootFilesystem);
|
|
1870
|
-
let
|
|
1831
|
+
let hostFunctionReference = "";
|
|
1871
1832
|
let rootBridge = null;
|
|
1872
1833
|
let kernel = null;
|
|
1873
1834
|
let client = null;
|
|
@@ -1884,7 +1845,7 @@ export class AgentOs {
|
|
|
1884
1845
|
const env = getBaseEnvironment();
|
|
1885
1846
|
// Guest command paths. The sidecar owns the `/opt/agentos` projection and
|
|
1886
1847
|
// reports the exact projected package commands after `configureVm`.
|
|
1887
|
-
//
|
|
1848
|
+
// HostFunction-shim commands are added below.
|
|
1888
1849
|
const commandGuestPaths = new Map();
|
|
1889
1850
|
const { sidecarMounts, hostMounts, hostPathMappings } = collectSidecarMountPlan({
|
|
1890
1851
|
mounts: options?.mounts,
|
|
@@ -1895,11 +1856,7 @@ export class AgentOs {
|
|
|
1895
1856
|
client = shared.client;
|
|
1896
1857
|
const session = shared.session;
|
|
1897
1858
|
nativeSession = session;
|
|
1898
|
-
const
|
|
1899
|
-
...allowAll,
|
|
1900
|
-
binding: "allow",
|
|
1901
|
-
};
|
|
1902
|
-
const sidecarPermissions = serializePermissionsForSidecar(hostPermissions);
|
|
1859
|
+
const sidecarPermissions = serializePermissionsForSidecar(options?.permissions);
|
|
1903
1860
|
const createVmConfig = {
|
|
1904
1861
|
env,
|
|
1905
1862
|
database: options?.database,
|
|
@@ -1960,16 +1917,16 @@ export class AgentOs {
|
|
|
1960
1917
|
loopbackExemptPorts: options?.loopbackExemptPorts,
|
|
1961
1918
|
packages: sidecarPackages,
|
|
1962
1919
|
packagesMountAt: OPT_AGENTOS_ROOT,
|
|
1963
|
-
|
|
1920
|
+
hostFunctionShimCommands: hostFunctionBootstrapCommands,
|
|
1964
1921
|
});
|
|
1965
1922
|
for (const command of configuredVm.projectedCommands) {
|
|
1966
1923
|
commandGuestPaths.set(command.name, command.guestPath);
|
|
1967
1924
|
}
|
|
1968
|
-
if (
|
|
1969
|
-
|
|
1925
|
+
if (hostFunctions.length > 0) {
|
|
1926
|
+
hostFunctionReference = await registerHostFunctionsOnSidecar(client, session, nativeVm, hostFunctions);
|
|
1970
1927
|
commandGuestPaths.set("agentos", "/bin/agentos");
|
|
1971
|
-
for (const
|
|
1972
|
-
commandGuestPaths.set(`agentos-${
|
|
1928
|
+
for (const hostFunctionCollection of hostFunctions) {
|
|
1929
|
+
commandGuestPaths.set(`agentos-${hostFunctionCollection.name}`, `/bin/agentos-${hostFunctionCollection.name}`);
|
|
1973
1930
|
}
|
|
1974
1931
|
}
|
|
1975
1932
|
rootBridge = new NativeSidecarKernelProxy({
|
|
@@ -1985,10 +1942,10 @@ export class AgentOs {
|
|
|
1985
1942
|
loopbackExemptPorts: options?.loopbackExemptPorts,
|
|
1986
1943
|
// Retained for runtime mount reconfigures: `configure_vm` is
|
|
1987
1944
|
// replace-on-write for the whole payload, so post-boot mountFs
|
|
1988
|
-
// must resend the boot packages and
|
|
1945
|
+
// must resend the boot packages and host-function shims.
|
|
1989
1946
|
packages: sidecarPackages,
|
|
1990
1947
|
packagesMountAt: OPT_AGENTOS_ROOT,
|
|
1991
|
-
|
|
1948
|
+
hostFunctionShimCommands: hostFunctionBootstrapCommands,
|
|
1992
1949
|
commandGuestPaths,
|
|
1993
1950
|
onDispose: cleanup,
|
|
1994
1951
|
// The native process is owned by the AgentOsSidecar handle and
|
|
@@ -2012,10 +1969,9 @@ export class AgentOs {
|
|
|
2012
1969
|
sidecarClient: client,
|
|
2013
1970
|
sidecarSession: session,
|
|
2014
1971
|
sidecarVm: nativeVm,
|
|
2015
|
-
permissions: hostPermissions,
|
|
2016
1972
|
snapshotRootFilesystem: async (maxBytes) => createSnapshotExport(convertSidecarRootSnapshotEntries(await snapshotClient.snapshotRootFilesystem(session, nativeVm, maxBytes))),
|
|
2017
|
-
|
|
2018
|
-
|
|
1973
|
+
hostFunctions,
|
|
1974
|
+
hostFunctionReference,
|
|
2019
1975
|
async dispose() {
|
|
2020
1976
|
if (kernel) {
|
|
2021
1977
|
const currentKernel = kernel;
|
|
@@ -2060,9 +2016,8 @@ export class AgentOs {
|
|
|
2060
2016
|
const vmAdmin = sidecarLease.admin;
|
|
2061
2017
|
const vm = new AgentOs(vmAdmin.kernel, sidecar, [], vmAdmin.hostMounts, vmAdmin.env, vmAdmin.rootView, vmAdmin.sidecarClient, vmAdmin.sidecarSession, vmAdmin.sidecarVm, options?.onAgentStderr ?? defaultAgentStderrHandler, options?.onAgentExit ?? defaultAgentExitHandler, options?.onLimitWarning);
|
|
2062
2018
|
vm._sidecarLease = sidecarLease;
|
|
2063
|
-
vm.
|
|
2064
|
-
vm.
|
|
2065
|
-
vm._permissions = vmAdmin.permissions;
|
|
2019
|
+
vm._hostFunctions = vmAdmin.hostFunctions;
|
|
2020
|
+
vm._hostFunctionReference = vmAdmin.hostFunctionReference;
|
|
2066
2021
|
vm._disposeHooks.push(...sandboxDisposeHooks);
|
|
2067
2022
|
vm._installSidecarRequestHandler();
|
|
2068
2023
|
vm._cronManager = new CronManager(vm, options?.scheduleDriver ?? new TimerScheduleDriver());
|
|
@@ -3568,7 +3523,7 @@ export class AgentOs {
|
|
|
3568
3523
|
: JSON.stringify(input.mcpServers),
|
|
3569
3524
|
permissionPolicy: input.permissionPolicy ?? null,
|
|
3570
3525
|
skipOsInstructions: input.skipOsInstructions ?? null,
|
|
3571
|
-
additionalInstructions: combineInstructions(input.additionalInstructions, this.
|
|
3526
|
+
additionalInstructions: combineInstructions(input.additionalInstructions, this._hostFunctionReference) ?? null,
|
|
3572
3527
|
},
|
|
3573
3528
|
});
|
|
3574
3529
|
if (response.tag !== "AcpOpenSessionResponse") {
|
|
@@ -4089,9 +4044,8 @@ export class AgentOs {
|
|
|
4089
4044
|
}
|
|
4090
4045
|
_installSidecarRequestHandler() {
|
|
4091
4046
|
const context = {
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
permissions: this._permissions,
|
|
4047
|
+
hostFunctions: this._hostFunctions,
|
|
4048
|
+
hostFunctionMap: buildHostFunctionMap(this._hostFunctions),
|
|
4095
4049
|
readFile: (path) => this.readFile(path),
|
|
4096
4050
|
};
|
|
4097
4051
|
this._sidecarClient.setSidecarRequestHandler((request) => {
|