Assivy

Function tools

TypeScript or Python tools that run sandboxed, return values, write files, and log with levels.

A function tool is code the agent can call: look up the order, sync the calendar, transform the payload. You write it in TypeScript or Python, the platform runs it in a sandbox, and the turn records what it did.

Writing one

A function tool has two parts. Inputs is a JSON Schema for the arguments the model supplies. The body is plain statements with no function signature, and input (the arguments) and runtime (the caller) are already in scope.

{
  "type": "object",
  "properties": { "city": { "type": "string", "description": "City name" } },
  "required": ["city"]
}

A tool with no Inputs is called with no arguments.

TypeScript is the default. The body runs inside an async function, so await and return both work:

const res = await fetch(`https://api.example.com/weather?q=${encodeURIComponent(input.city)}`);
if (!res.ok) throw new Error(`Weather service returned ${res.status}`);
const data = await res.json();
return { city: input.city, tempC: data.temp };

Python runs as a script. There's no function to return from, so assign result instead:

rows = [r for r in input["rows"] if r["amount"] > 0]
result = {"count": len(rows), "total": sum(r["amount"] for r in rows)}

A string result reaches the model as is, and anything else is sent as JSON. A thrown error comes back as Function execution failed: … with its message, and the model can read it and react. Bodies saved in the older async (input, runtime) => { … } style still run.

runtime

runtime describes who is calling. It's verified by the platform, holds no secrets, and is never shown to the model as an input.

FieldValue
runtime.user.id, runtime.user.emailThe caller
runtime.user.isAnonymoustrue for anonymous visitors
runtime.user.rolesThe caller's roles
runtime.user.metadataFree-form user metadata, for example from SSO
runtime.tenantIdThe organization

Network

TypeScript bodies get a fetch that goes through the egress proxy. It reaches only the hosts allowed under Outbound access, and a new org allows none. The response has ok, status, headers, text(), and json(). Python bodies have no network.

Files

To return a file, write it to the output directory. It's attached to the reply as a download, and the model is told which files arrived.

  • Python: output_path("report.pdf") gives the path, and OUTPUT_DIR is the directory. read_json is also available.
  • TypeScript: outputPath("report.pdf") and OUTPUT_DIR.

Only top-level files are collected: up to 10 per call, 10 MB each, and 25 MB in total. A turn delivers at most 10 files across all its tools. The sandbox image has the same document libraries run_code uses (Tools).

Limits

LimitValue
Time30 s per call. A failed call is never retried.
Memory512 MB
CPU0.5 core
Processes64
Logs50 log() lines per call, 4 KB each
Output files10 per call, 10 MB each, 25 MB total

Memory, CPU, and process limits are deployment defaults that your operator can change.

Logging from a tool

Call log(message, data?, level?). The level is debug, info, warn, or error, and the default is info. Each line lands in the Tracing Logs tab under the calling trace, tagged [tool:<name>], with data attached as a structured payload:

log('order synced', { orderId: input.orderId, lines: items.length });
log('retrying upstream', undefined, 'warn');
log('payment failed', { reason }, 'error');
log("retrying upstream", level="warn")

Logging is opt-in, and only log() is forwarded. console.log and print output is not. After 50 lines, the rest are dropped with a warning. A line over 4 KB drops its data first, then has its message cut.

Running in the browser

Turn on Run in the user's browser (executeOnClient) and a TypeScript body runs in the chat page instead of the sandbox. Use it to reach things only the page has: navigator.geolocation, the clipboard, the real timezone, or localStorage.

const pos = await new Promise((resolve, reject) =>
  navigator.geolocation.getCurrentPosition(resolve, reject),
);
return { lat: pos.coords.latitude, lon: pos.coords.longitude };
  • It isn't isolated. The code runs with the viewer's session on the page's origin, so the switch lives on the tool, out of chat users' reach.
  • runtime has user and isClient: true, but no tenantId.
  • runtime.host.call('method', args) calls the page that embeds the chat (see Channels). Inside Assivy itself runtime.host is absent.
  • log() and the proxied fetch aren't available. The time limit is still 30 seconds.
  • Python bodies can't run in the browser, so the option is hidden for them.
  • Failures, including a permission the user denied, come back to the model as errors.

What the turn records

Every tool call lands in the trace: what went in, what came back, and what it said along the way. Open the turn in Tracing and expand the tool span to see the full story, or filter the Logs tab to the trace id and read it as a narrative.

The agent's transcript mode controls how much of this the user sees inline. By default the user sees the answer plus tool summaries. Verbose mode spells out every call, and thinking mode adds reasoning when the provider returns it.

On this page