Skip to main content
Write agent callbacks as small synchronous calculations over explicit inputs. Custom tools, evaluators, exact-diff selectors, judge selectors and pass rules use the same isolated QuickJS environment for normal and filesystem agents.

Supported code

Use strings, finite numbers, booleans, arrays, plain objects, regular expressions, loops, JSON operations and ordinary Math calculations. Import local TypeScript helpers and captured constants from the source project:
math.ts
tools.ts
The compiler bundles these files. It allows imports of @eigenpal/automations, Zod and captured relative source modules. Zod defines schema contracts; unsupported parsing effects still fail schema compilation. Return JSON-compatible data. Convert internal collections to arrays/plain objects before returning. Functions, symbols, bigint, cycles and non-finite numbers are unsupported at the boundary. Tools return objects matching outputSchema; evaluators return scores, selectors return JSON data, and pass rules return booleans.

Unavailable APIs

Callbacks do not receive:
  • Node built-ins such as node:fs, child_process, Buffer or process.env.
  • Network APIs such as fetch, database clients or host credentials.
  • Arbitrary npm runtime imports, dynamic import() or require().
  • Browser globals such as window or document, or host timers such as setTimeout.
  • A platform client/context for calling another agent, reading a resource file directly, invoking built-in resource tools as JavaScript functions or spawning a process.
Async functions, generators and returned Promises are unsupported. Do not write an asynchronous tool even when its return type matches the desired data:

Filesystem scripts versus TypeScript callbacks

.withFilesystem() gives the agent sandbox-native read/write/shell tools. Through those tools, it can execute scripts that use installed Python/npm packages and workspace files. Those scripts run inside the filesystem sandbox. It does not move createTool.execute or evaluation callbacks into that sandbox or grant them extra imports. There is no second execute mode for Python or shell scripts. For a file-heavy task, give the filesystem agent instructions and a skill directory with its helper scripts. For structured calculations, use a custom TypeScript tool. The resource tools also work for normal agents. EigenPal implements them as bounded platform operations. Your custom callback cannot invoke them through an injected context.

Invocation and state

EigenPal evaluates declarations in an isolated compiler, then retains the callback bundle for execution. It does not proactively invoke callback bodies with sample arguments during compilation. If your own module calls a helper at top level, that code executes while loading the declaration; keep module initialization portable. Each callback invocation gets a fresh QuickJS context. A captured helper can use local constants, but module variables are not persistent storage between calls. Use explicit inputs; do not mutate evaluation inputs or rely on object identity with values outside the context. The runtime reuses the same QuickJS engine as workflow script steps, with agent-specific input/output contracts and limits. It does not expose arbitrary workflow-step configuration through the agent SDK. Declaration compilation disables wall-clock time and random sources. Runtime callbacks are not guaranteed deterministic. Avoid Date and Math.random when you need reproducible decisions; pass timestamps or other values as explicit input.

Execution limits

Custom tools and evaluation callbacks currently have at most:
  • 2 seconds per invocation and 64 MiB of QuickJS memory.
  • A 512 KiB stack.
  • 128 KiB for tool results and evaluation outputs.
  • 256 KiB for evaluation arguments.
Deployment script limits can be tighter. An agent’s remaining time can shorten tool execution further. These are platform limits, not per-callback SDK options. A deployment can disable script execution; custom callbacks then fail rather than running on the application host.

Catch mistakes before release

The example project uses a strict authoring configuration that omits Node/browser globals:
tsconfig.json
Run tsc --noEmit. Callback parameters, selected output fields and registered score names infer from your schemas and chain. lib and types help the IDE flag process, fetch and window; they do not prevent explicit imports from adding other ambient types. Keep Bun/Node verification scripts in a separate TypeScript project, as the example does. Run eigenpal agents validate from the agent package for platform compilation. Unsupported imports identify their project-relative source file and category. Async/generator declarations receive synchronous-callback guidance. Static checks cannot prove arbitrary JavaScript pure, terminating or correct for all inputs. A synchronous untyped helper could return a Promise or access a global indirectly. Runtime checks reject returned Promises, invalid scores, schema failures and exceeded limits. Arbitrary exception text remains private; you receive a sanitized failure. Test tools and evaluations on representative inputs before release.