This document describes how Hugind executes JavaScript agents and what the JS runtime exposes to agent code.
An agent runs in the JavaScript runtime when its entry_point is a .js file.
If the entry point is a .wasm file, the WASM runtime is used instead.
JavaScript modules are loaded with a local-only resolver:
- Only relative imports are allowed (
./or../). - Imports must resolve inside the agent root directory.
- Only
.jsmodules are allowed.
Imports that violate these rules raise a runtime error.
The entry module is evaluated, then Hugind looks for a default export:
- If a default export function exists, it is called with a single argument containing initial data.
- If the default export returns a Promise, Hugind waits for it to resolve.
- If no default export exists, the result is
null.
The returned value is converted to JSON and used as the agent's output.
Hugind provides the initial arguments in two ways:
- Default export parameter (preferred):
export default async function (input) { ... }
- Global helper:
get_args_json()returns the JSON string for the initial input.get_args()is an alias forget_args_json().
Input shape:
input.args: CLI args passed after--.input.meta.session: resolved backend session metadata.input.meta.env: environment values declared inagent.yamlenv:section. Only declared env names are passed through, and required vars fail the run if missing from the host environment.
Agents can return output in two ways:
- Return a value from the default export (or a resolved Promise).
- Call
set_result(value)to explicitly set the output.
If both are used, set_result takes precedence once it is observed.
These globals are installed at runtime:
Writes to stdout.
Writes to stdout without appending a newline.
Writes to stderr (CLI mode) or emits an agent.progress event (stdio mode).
Use this for agent progress/diagnostic output that should not appear in the
agent's final result.
In stdio/MCP mode, eprint messages are delivered as agent_event events
with type: "agent.progress" so the UI can display real-time progress.
Returns the current Hugind runtime version.
Writes a prompt, then reads a line from stdin.
Performs an HTTP GET request and returns the response body as text.
Network access is gated by the agent's permissions.network:
allowmust betrue.- If
allowed_domainsorallowed_ipsis non-empty, the host must match. - If
block_private_networksis true, private/loopback IPs are blocked. timeoutandmax_response_bytesare enforced.- Redirects are followed up to 5 times.
Sends a chat completion request to the configured backend and returns the
assistant response text. The backend is resolved from the agent manifest.
For plain string prompts, response_format defaults to
{ "type": "json_object" }. For object request bodies, no response_format
is injected.
Calls the chat completion endpoint with streaming enabled and returns the
full response text. For plain string prompts, response_format defaults to
{ "type": "json_object" }. For object request bodies, no response_format
is injected.
If the input is an object, you may provide on_token (or onToken) callback
function. It will be called for each streamed delta, letting the agent decide
whether to print.
Executes a shell command using permissions.shell.
Important: This function is async. It returns a Promise, not a string.
At module top level, you cannot call run_command() and use the result
directly (top-level await is not supported). Inside tool execute callbacks,
use async function and await:
// Correct — inside a tool execute callback
execute: async function(args_json) {
var result = await run_command("ls");
return result;
}
// Wrong — at module top level
var result = run_command("ls"); // result is a Promise, not a stringShell permissions enforced:
allowmust betrue.whitelistis enforced if present.blacklistblocks if present.timeout,max_output,env_clear, andworking_dirare applied.
runCommand(cmd) is an alias for run_command(cmd).
Executes a command directly without a shell, bypassing shell escaping issues. Arguments are passed exactly as provided.
allowmust betrue.whitelistis enforced if present.blacklistblocks if present.timeout,max_output,env_clear, andworking_dirare applied.
Returns a JSON string for an array of tool descriptors
(name, description, input_schema, server).
Calls an MCP tool by name and returns the tool result as a JSON string.
Tool naming:
- When multiple MCP servers are configured, use
server:tool. - With a single MCP server, unqualified
toolis accepted.
Host filesystem calls are gated by permissions.filesystem and
runtime_fs_mode (same semantics as the WASM runtime).
Available methods:
fs.cwd() -> stringfs.exists(path: string) -> boolfs.is_file(path: string) -> boolfs.is_dir(path: string) -> boolfs.realpath(path: string) -> stringfs.read_text(path: string) -> stringfs.read_bytes(path: string) -> number[](0..255)fs.write_text(path: string, data: string) -> voidfs.write_bytes(path: string, data: string | number[]) -> voidfs.append_text(path: string, data: string) -> voidfs.list_dir(path: string) -> string(JSON array of entry names)fs.mkdir(path: string, recursive: bool) -> voidfs.remove(path: string, recursive: bool) -> voidfs.rename(src: string, dst: string) -> voidfs.copy(src: string, dst: string) -> voidfs.stat(path: string) -> string(JSON stat object)
When mode: agentic is set in agent.yaml, these additional globals are available:
Registers a tool for the agentic loop. The LLM can invoke registered tools
via <tool_call> tags in its response.
register_tool({
name: "read_file",
description: "Read a file's contents",
parameters: {
type: "object",
properties: { path: { type: "string" } },
required: ["path"]
},
execute: (args_json) => {
var args = JSON.parse(args_json);
return fs.read_text(args.path);
}
});The execute function receives a JSON string of arguments and should return
a string result. Async functions (returning Promises) are supported — the
runtime awaits them automatically.
Sets the system prompt for the agentic loop. Called during agent setup.
Overrides the maximum number of LLM round-trips (default: 10, or from YAML).
When running in a team (workflow or hugind agent team), these globals are
available for inter-agent communication:
Write a value to shared memory under the agent's namespace.
Read a value by fully-qualified key (e.g. "architect/spec"). Returns JSON string or "null".
Returns all shared memory entries as a JSON object.
Returns a markdown summary grouped by agent.
Send a point-to-point message to another agent.
Broadcast a message to all team members.
Returns unread messages as a JSON array of {from, to, content} objects.
Dynamically spawn a new task (only available in workflow execution with task queue):
tasks.spawn(JSON.stringify({
title: "Fix auth bug",
description: "Auth returns 500",
assignee: "developer",
depends_on: []
}));If the JS runtime throws an exception, Hugind prints the exception and stack trace (when available) and the agent run fails.
- JS execution is sandboxed by module resolution rules, but JS code can still access the provided globals. Capabilities are intentionally narrow.
- Only
.jsmodules are supported; other extensions are rejected. - Networking supports only GET via
net.fetch.