ThinkingRoot Docs
Guides

Root Functions

Deploy sandboxed JavaScript your AI can call — with sealed secrets and an egress-gated fetch.

A Root Function is a small piece of JavaScript that runs inside the engine's V8 isolate. It's how you give your AI custom capabilities: call an external API, transform data, glue services together — without standing up a separate service. The Functions tab lists them, lets you edit the body, deploy, invoke, and view run history.

The author contract

A function body must evaluate to a callable that takes (input, env):

async (input, env) => {
  // input — the argument passed at invoke time
  // env   — your project's secrets, resolved by name
  const r = await fetch("https://api.example.com/things/" + input.id, {
    headers: { Authorization: `Bearer ${env.EXAMPLE_API_KEY}` },
  });
  return { ok: r.ok, thing: await r.json() };
}

The runtime invokes (<body>)(input, env), awaits the result if it's a promise, and returns it as JSON. Execution is bounded by a wall-clock timeout.

Environment (secrets)

env is your project's secrets, resolved by name. The resolution order is process env first, then the local secrets file, so the same env.X works in the cloud (provisioner injects it) and locally (root secrets set X … writes ~/.config/thinkingroot/secrets.toml). Write once, run in both places.

Network: an egress-gated fetch

Functions get a familiar fetch(url, opts) — but every call is checked against your project's egress allowlist first. If a host isn't allowlisted (when an allowlist is set), the call is refused before any network request:

egress blocked: host 'evil.example' is not in this project's TR_OUTBOUND_ALLOWLIST

fetch returns { status, ok, headers, text(), json() }. With no allowlist set (local/desktop dev), all hosts are allowed.

Deploy, invoke, inspect

In the Functions tab: write the body, Deploy, then Invoke with a JSON input and see the result. Past runs (status, output, errors) are listed per function.

# Deploy
curl -X PUT "$GATEWAY/engine/api/v1/ws/$WS/functions" \
  -H "Authorization: Bearer tr_sk_…" -H 'Content-Type: application/json' \
  -d '{ "name": "fetch_thing", "body": "async (input, env) => ({ id: input.id })" }'

# Invoke
curl -X POST "$GATEWAY/engine/api/v1/ws/$WS/functions/fetch_thing/invoke" \
  -H "Authorization: Bearer tr_sk_…" -H 'Content-Type: application/json' \
  -d '{ "id": 42 }'

# Runs
curl "$GATEWAY/engine/api/v1/ws/$WS/functions/fetch_thing/runs" \
  -H "Authorization: Bearer tr_sk_…"
root function deploy fetch_thing --code ./fetch_thing.js
root function invoke fetch_thing --input '{"id":42}'
root function list

Every invocation is recorded (status, output, error) so runs are auditable.

Self-extending agents (JIT)

Root Functions are also the noun the engine's just-in-time capability acquisition produces. When an agent hits a wall, it classifies the gap (knowledge, capability, or impossible) and climbs an acquisition ladder — reuse a tool, load or define a skill, install an MCP server, or author and deploy a Root Function — then retries. Capabilities the agent writes this way persist as ordinary Root Functions you can inspect.

Honest about maturity

The manual Root Function path (deploy → invoke → runs) is built and verified end-to-end. The fully autonomous JIT loop is implemented but not yet proven across many real apps — treat it as capable, not battle-tested.

Build feature

The executor is gated behind the engine's root-functions build feature, which the cloud image enables. On a build without it (e.g. a lean desktop build), deploy/invoke return a clear "feature not enabled" error rather than failing silently.