Skip to content

refactor(agentos): drop the host function wrappers, names, and descriptions - #2006

Merged
NathanFlurry merged 1 commit into
mainfrom
host-functions-flat-api
Sep 21, 2026
Merged

NathanFlurry merged 1 commit into
mainfrom
host-functions-flat-api

Conversation

@NathanFlurry

@NathanFlurry NathanFlurry commented Sep 21, 2026

Copy link
Copy Markdown
Member

Host functions are now a plain record of collections. The keys name everything, and a function needs only a schema and a handler:

hostFunctions: {
  store: {
    listOrders: {
      inputSchema: z.object({ customer: z.string() }).describe("List a customer's orders."),
      execute: ({ customer }) => db.orders.findMany({ customer }),
    },
  },
}

Removed from the public API

  • hostFunction() and hostFunctions() wrappers.
  • HostFunctions.name and HostFunctions.description (the collection key names it).
  • The functions: nesting key (functions are the collection's own keys).
  • HostFunction.description (comes from the schema's .describe()).
  • MAX_HOST_FUNCTION_DESCRIPTION_LENGTH and validateHostFunctions.
  • HostFunctions as an interface, replaced by HostFunctionCollection / HostFunctionCollections.
  • HostFunctionDefinition and its commandAliases, which only made sense in NodeRuntime's old one-command-per-function model.

Behavior

  • Keys may be camelCase or kebab-case; both resolve to one kebab-case command name, so store.listOrders is agentos-store list-orders on the CLI and store.listOrders(input) in guest JavaScript. A key that cannot become a command name, or two keys that collide once converted, throw before the VM is created.
  • Descriptions are optional on both sides now. The sidecar keeps the length cap and no longer rejects a registration that omits one; the client no longer duplicates that limit.
  • execute still gets its input typed from its own inputSchema, with no wrapper to hang the inference on. The options types carry a schema type parameter that AgentOs.create(), agentOS(), NodeRuntime.create() and the secure-exec entry points infer from the literal. A collection built outside the call that takes it has nothing to infer from, so its input falls back to any rather than forcing every handler to annotate.
  • Mirrored in the Rust client: HostFunction loses name/description, HostFunctions becomes HostFunctionCollections (a BTreeMap), and the description is read from the JSON Schema.

Breaking change for NodeRuntime guest code

NodeRuntime used to take a flat map and register each function as its own single-callback collection, so the guest invoked it as a top-level command named after the function (add --json ...). It now takes the same two-level collections as AgentOs.create(), and the guest reaches a function as agentos-<collection> <function>. The host function module moves from packages/core down into packages/runtime-core so both layers share one implementation rather than two parallel ones.

agentos list-bindings is intentionally retained as an alias for list-host-functions.

Docs, examples, and both docs bundles are updated to the inline form.

🤖 Generated with Claude Code

@railway-app

railway-app Bot commented Sep 21, 2026

Copy link
Copy Markdown

🚅 Environment agentos-pr-2006 in rivet-frontend has no services deployed.

…ptions

Host functions are now a record of collections. The keys name everything and a
function needs only `inputSchema` and `execute`.

  hostFunctions: {
    store: {
      listOrders: { inputSchema, execute },
    },
  }

Removed from the public API: the `hostFunction()` and `hostFunctions()`
wrappers, `HostFunctions.name`, `HostFunctions.description`, the
`functions:` nesting key, `HostFunction.description`,
`MAX_HOST_FUNCTION_DESCRIPTION_LENGTH`, `validateHostFunctions`, and
`HostFunctionDefinition.commandAliases`.

The description now comes from the input schema's `.describe()`, so it is one
optional field instead of two required ones. The sidecar keeps the length cap
and no longer requires a description at all.

`NodeRuntime` now takes the same shape. The host function module moves down
into runtime-core so both layers share one implementation, which is a breaking
change for guest code: a function is reached as `agentos-<collection>
<function>` rather than as a top-level command named after the function.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@NathanFlurry
NathanFlurry force-pushed the host-functions-flat-api branch from c06de1d to 150b793 Compare September 21, 2026 08:10
@NathanFlurry
NathanFlurry marked this pull request as ready for review September 21, 2026 08:51
@NathanFlurry
NathanFlurry merged commit 4febb6d into main Sep 21, 2026
6 of 9 checks passed
@NathanFlurry
NathanFlurry deleted the host-functions-flat-api branch September 21, 2026 08:51

@the-company-company the-company-company Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 1 high · 🟠 2 medium · 🔵 1 low

Reviewed commit 150b793.

Comment on lines 3199 to 3206
// bundle loading from the projected package dirs.
const localMounts = await resolveCompatLocalMounts(options?.mounts);
if (options?.hostFunctions && options.hostFunctions.length > 0) {
validateHostFunctions(options.hostFunctions);
}
// Keys become command names, so resolve and check them before anything
// else in `create()` allocates a sidecar or a sandbox.
const resolvedHostFunctions = options?.hostFunctions
? resolveHostFunctions(options.hostFunctions)
: [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 High · Sandbox host functions are discarded before VM registration

resolveHostFunctions runs before resolveSandboxOptions, but the latter is what adds the automatic sandbox collection. hostFunctions is then fixed to the earlier result, so AgentOs.create({ sandbox: ... }) mounts the sandbox filesystem but never registers agentos-sandbox or any process callbacks. Resolve the expanded options.hostFunctions after sandbox expansion (while validating caller-provided functions before starting the provider if needed).

Comment on lines +78 to +80
const runCommandResponse = (await hostFunctions.hostFunctions[
"run-command"
].execute({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Medium · Sandbox integration test still calls the removed wrapper shape

createSandboxHostFunctions() now returns the collection directly (runCommand, createProcess, and so on), so hostFunctions.hostFunctions is undefined. This test throws before exercising the sandbox, and the same stale access remains for create/list/kill below. Update these calls to the new camelCase members so the migrated integration test can run.

Comment on lines -17 to -19
hostFunction,
MAX_HOST_FUNCTION_DESCRIPTION_LENGTH,
hostFunctions,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Medium · Removed exports leave a default test file unloadable

The non-nightly packages/core/tests/host-function-command-exec.test.ts still imports both hostFunction and hostFunctions from this entry point and constructs its fixture with them. Removing these exports without migrating that test makes Vitest fail while loading the default core suite, before any tests execute. Convert the fixture to the new keyed object API in this PR.

Comment thread secure-exec/src/index.ts
Comment on lines +77 to +79
export function evaluate<
T = JsonValue,
HOST_FUNCTIONS extends HostFunctionSchemas = HostFunctionSchemas,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Low · Explicit evaluation result types disable host-function input inference

TypeScript does not infer omitted type parameters after any explicit type argument; it uses their defaults. Therefore the common evaluate<MyResult>(..., { hostFunctions: ... }) form fixes HOST_FUNCTIONS to HostFunctionSchemas, and every inline execute(input) falls back to any, defeating the new schema-derived typing. Add a type-level regression test for this call form and reshape the API so the result annotation does not prevent host-function inference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant