LoopEngineBETA

Docs

Documentation

How LoopEngine works, from scaffold to durable approvals — agents, adapters, permission rules, and abilities.

Tools

Types of tools

Every agent's tools are one of four kinds, merged together into one ToolDefinition[] before a turn runs:

  • Local tools — code under agents/<name>/tools/, exported from that folder's index.ts. The ordinary case: a tool that calls your own API, queries your own database, or wraps any other integration code you actually own — hand-written, or generated by the Web UI's HTTP tool builder below, which produces this exact same shape of file.
  • System tools — core/system-tools/ (readFile, system_ask_user). Merged into every agent unconditionally, the same way systemTools is spread into the tool list regardless of what AgentConfig.tools resolves to — not opt-out-able the normal way.
  • Gateway tools — core/gateway-tools.ts registers external tool gateways (Composio today — Nango, Arcade, Scalekit are meant to slot in later as thin adapters, same shape, once this mechanism is proven) against an agent, no hand-written tools/index.ts required for those specific integrations. core/mcpplug.ts is the MCP-shaped adapter layer underneath it — formerly a standalone package, folded in once no second consumer ever materialized.
  • Agent as a tool — core/agent-as-tool.ts wraps a whole other agent — its own system prompt, tools, permission rules, and ReAct loop — as a single ToolDefinition. The calling agent only ever sees a request in, a final answer out: the wrapped agent's own turns, tool calls, and permission decisions never surface to it. See Discovery & Registry for the subagents/ folder convention built on top of this.

Running tools in parallel: safe: true

core/toollane.ts's ToolLane schedules a batch of already-approved tool calls — everything the model requested in one turn that made it past the gate — into lanes: consecutive calls the batch classifies as safe merge into one shared parallel lane (run via Promise.all); anything else, or a run of unsafe calls, gets its own solo lane, awaited one at a time. Batch order is always preserved across lanes — this only ever decides what runs together, never reorders anything.

Set safe: true on a ToolDefinition for a read-only call with no side effects and no shared mutable state — a lookup, a search — never for anything that mutates something (a refund, an email, a write):

import type { ToolDefinition } from 'loopengine'

export const lookupOrder: ToolDefinition = {
  name: 'lookup_order',
  description: "Look up an order's total and status",
  input_schema: { type: 'object', properties: { orderId: { type: 'string' } }, required: ['orderId'] },
  execute: async (input) => orders[input.orderId as string] ?? { error: 'not found' },
  safe: true,
}

(agents/customer-service/tools/lookup_order.ts's real version — get_shipment_details next to it is safe: true too, so a turn that calls both together runs them concurrently instead of one after the other.) AgentConfig.isSafeTool, if set, takes full precedence over every tool's own safe flag — it's a function of the whole call, not a static per-tool default, so it can vary by agent or by the call's own args; omit it and each tool's own safe flag is looked up by name instead, and omitting both runs every tool solo.

Adding a tool

Four ways, in increasing order of how much loopengine does for you:

  1. Text editor — hand-write a ToolDefinition under agents/<name>/tools/ and export it from that folder's index.ts. Full control, but you own the integration code and its tests. See Configure an Agent's Text editor section.
  2. Web UI, "Create an HTTP tool" — a constrained form in the Tools tab (method, a {field}-templated URL, headers — a header value can reference {{ENV_VAR}}, read from process.env at call time, never persisted as a literal secret — optional JSON body, and a response path to extract). No hand-written code: web/http-tool-admin.ts generates a real, readable .ts file from the spec — the same kind of file a human would've written by hand for this one well-understood shape, never arbitrary code — writes it to agents/<name>/tools/, registers it in that folder's index.ts, and splices the live ToolDefinition straight into the running agent registry, no restart needed. A sidecar <name>.http-tool.json is what lets the tab's Edit button repopulate the form later; hand-editing the generated file directly still works, it just falls out of sync with that sidecar.
  3. Web UI, Gateway Tools tab — pick a connected app (Composio toolkit) and the specific tools you want from a picker, no code and no generated file either. Submits to POST /agents/:name/gateway-tools, which live-connects and resolves real tool descriptions before adding them — right for an integration Composio already covers, rather than one this repo has to describe itself.
  4. loopengine add-ability — installs a whole tool and its skill and permission rules together as one unit, rather than a tool alone. Right when the tool needs an accompanying SKILL.md or actauth.yml rules to be usable safely, not just the tool file itself — see Abilities and Ability System.