BorgIQ Agent Builder
Design autonomous AI in BorgIQ. Pair with borgiq-builder (hub) for wiring, and with borgiq-json-schema-builder whenever a tool input needs a real contract.
Mental model
BorgIQ has three execution modes for AI work plus one endpoint pattern:
- AiActor — a single LLM call. Stateless. Returns text, structured output, or tool definitions — but does not execute tools in a loop. One round trip, done.
- AiAgentActor — an autonomous coding agent (pi) running in checkpointed serverless segments. Has a private workspace with built-in
read/write/edit/bash/grep/find/lstools (plus an opt-indenotool for running code, viaenableDenoTool) plus connected BorgIQ actors as tools. Sessions continue viasessionId(7-day sliding TTL); workspace goes in viavolumeZipFileand comes out asoutputZipFile. Two output ports: Done (final result + zips) and Status (assistant turns + tool results). - AgentHarnessActor — Claude Code running in an isolated sandbox VM (E2B or Daytona) with a full machine, MCP servers, background processes, and session persistence via
sessionId. Inbound messages are queued FIFO with mutex. Returns workspace + session zips on completion. - McpServerActor — not an agent. An MCP endpoint that exposes its child tool actors so external clients (Claude Desktop, Cursor, custom agents) can call BorgIQ actors via the Model Context Protocol.
Legacy note: flows built before mid-2026 may contain
DeprecatedAiAgent— the old orchestrator-loop agent (no filesystem, options liketemperature/maxTokens/messages) that used to own theAiAgentActortype name. It still runs but must not be used for new work. Seereferences/deprecated-ai-agent.md.
"Which one do I use" matrix
| User wants | Use | Why |
|---|---|---|
| Text generation, summarization, classification | AiActor | One LLM call, no loop overhead |
| Structured JSON output | AiActor with outputSchema |
Deterministic, see borgiq-json-schema-builder |
| Tool definitions without execution (single round) | AiActor | LLM lists tools, you invoke them yourself |
| Multi-step research / orchestration / "agent figures it out" | AiAgentActor | Autonomous loop, BorgIQ actors as tools |
| File/data processing: unzip, transform, script, re-zip | AiAgentActor | Built-in filesystem + bash on a private workspace |
| Code generation that must also run the code | AiAgentActor | bash executes against the workspace |
| Resumable session across invocations | AiAgentActor (or AgentHarnessActor) with stable sessionId |
Checkpointed sessions, 7-day sliding TTL |
| Claude Code skills / slash commands / plugins | AgentHarnessActor | The harness runs Claude Code itself |
| Remote MCP server, or a BorgIQ MCP Server Actor, as agent tools | AiAgentActor or AgentHarnessActor | mcpServers array: type: http / type: borgiq on both |
| stdio (subprocess) MCP server as agent tools | AgentHarnessActor | type: stdio runs in the sandbox; AiAgentActor has no subprocess host |
| Background processes (dev server, daemon) | AgentHarnessActor | Nothing survives an AiAgentActor segment boundary |
| Heavy environments (big installs/builds, >10 GB, PTY) | AgentHarnessActor | Full sandbox VM |
| Expose BorgIQ tools to Claude Desktop / Cursor / external agents | McpServerActor | MCP endpoint, external client drives tool calls |
| Multi-agent systems | AiAgentActor with CallFlowActor tools | Sub-agents as callable sub-flows |
Key decisions — AiAgentActor
- System vs user prompt.
systemPromptdeclares role, conventions, and the wired-tool inventory (list each tool'smsgVar). Thepromptfield holds the task wired frominputs. Don't mix — role definition stays stable; task changes per invocation. - Session strategy. Leave
sessionIdblank for one-shot runs (auto-generated, returned on the done port). Use a stable ID when the agent should resume its workspace + conversation later. After the 7-day sliding TTL, the same ID starts fresh. - Runtime sizing. The workspace is capped at 20% of the runtime's ephemeral storage — recommend a runtime with ≥ 4 GB ephemeral for real file work. The runtime timeout only sets the segment length; total time is governed by
timeoutInMinutes(default 30). - Built-in tool filtering.
disallowedTools: [write, edit, bash]makes a read-only analyst.allowedToolsfor a strict allow-list. Built-in names (read/write/edit/bash/grep/find/ls) are reserved — wired tool actors must not use them asmsgVar;denojoins them only whenenableDenoTool: true. Notebashcannot spawn programs (no python/node/git); setenableDenoTool: truewhen the agent needs to run code it writes — and if you do, don't also excludedenoviaallowedTools/disallowedTools, which is now rejected as a contradiction. - Network lists.
allowNet/allowNetList/denyNetListgovern the whole tool runtime, bash included —curlis one of bash's built-ins and is subject to the same allowlist, so withallowNetoff it reports "command not found". Default isallowNet: false. - Done vs Status ports. Done fires once with
result+ zips +meta.endReason. Status streams assistant turns and tool results. Wire Done to the next task actor; wire Status to a UI/logging actor for real-time visibility.resultis optional — branch onsuccess/meta.endReason. - Sub-agents via CallFlowActor. Wrap complex sub-tasks in their own AiAgentActor inside a callable sub-flow; expose it as a tool. Use
${{aiInput}}in the callable'spayload(notinputs). Keeps the parent agent's tool surface flat. - Idempotent bash side effects. Bash is at-least-once across segment retries — external calls made from bash may repeat. Put exactly-once API work in wired tool actors, not bash + curl.
Key decisions — AgentHarnessActor
- E2B vs Daytona. E2B has full internet by default — good for research and external APIs. Daytona is isolated by default — good for sensitive code. Tune both with
allowNet/allowNetList/denyNetList. sessionIdstrategy. Leave blank for one-shot runs (auto-generated). Use a stable ID (user-123-research) when the agent should resume context. Concurrent calls with the samesessionIdqueue FIFO under a mutex.volumeZipFile. Upstream DenoActor builds a zip with CLAUDE.md, skills, input data; harness extracts to~/workspace/. SetworkingDirectory: my-projecttocdbefore the run.- MCP servers. Add to the
mcpServersarray when the agent needs external tools (type: http, e.g. Linear/GitHub), an MCP Server Actor elsewhere in BorgIQ (type: borgiq, no auth — the agent's session is already scoped to the servers listed), or a subprocess server (type: stdio, AgentHarnessActor only). BorgIQ tool actors auto-inject separately; MCP servers are additive. - Env vars vs credentials. Never hardcode secrets in prompts. Pattern:
env: { GITHUB_TOKEN: ${{ credentials.github.token }} }. - Return zips. Set
returnOutputZipFile: trueif downstream actors need workspace files. SetreturnClaudeSessionDataFile: trueto archive the conversation. Both false for fast, fire-and-forget runs.
Producing skill directories for an agent (file-handle export pattern)
An AgentHarnessActor receives its context as a volumeZipFile — a BorgIQ file handle that the harness extracts to ~/workspace/. When the directory's contents (a skill bundle, CLAUDE.md, reference docs, input data) live in Collections rather than as a static asset, don't hand-assemble the zip inline at every call site. Instead:
- Expose a callable actor/flow that reconstructs the directory and emits a BorgIQ file handle. Build a CallableTriggerActor sub-flow (or a DenoActor) that reads the relevant Collection items, writes them into the right directory layout (
skills/<name>/SKILL.md,CLAUDE.md, …), zips it, and returns the file handle viastashFile/ CallableResponseActor. Upstream flows call it and pass the handle straight intovolumeZipFile. - Treat this as a packaging/export endpoint, not as normal CRUD. Its job is to materialize a directory tree from stored state and produce a file artifact — it is not a getItem/putItem accessor. Keep it separate from the CRUD endpoints that mutate the underlying Collection: those own the data; this one only renders a snapshot into a file handle. (Endpoint-first split: the export route and the CRUD routes are different triggers — see the hub's Universal Trigger vs Webhook Trigger matrix.)
This keeps the directory definition in one reusable place: any agent or flow that needs the bundle calls the export actor instead of duplicating zip-assembly logic. If the Collections backing it must exist before first use, provision them with a migration runner — see collection-migrations.md.
Anti-patterns
- Using AiActor when you need a loop. A single LLM call doesn't execute tools — it just lists them. If the customer wants "the AI figures out which API to call and then uses the result to call another one," that's AiAgentActor.
- Using AgentHarnessActor for plain file work. Unzipping, scripting, and editing files doesn't need a sandbox VM — AiAgentActor's built-in tools do it with far less latency and cost. Reserve the harness for Claude Code features, stdio MCP servers, and daemons.
- Wiring file-system tool actors into AiAgentActor. The agent already has
read/write/edit/bashagainst its workspace — don't rebuild them as HTTP/Deno tools, and never name a wired tool after a built-in (validation rejects it). - Wiring tool inputs through
optionsinstead of the agent boundary. Tools must be listed inaiAgentToolActorIds; tool actors consume${{aiInput}}in theirinputs. Trying to route data via the parent'soptionsbreaks the loop. - Ignoring the Done/Status port distinction. Wiring only Done and expecting real-time progress means you'll see nothing until completion. Wire Status for in-flight visibility.
sessionIdfootguns. NosessionId= every run is isolated. SamesessionIdacross concurrent calls = serialized execution. AsessionIdpast its TTL silently starts fresh. Pick the strategy on purpose — and persist the returnedsessionIdif a later flowrun must continue the session.- MCP scope confusion.
McpServerActorexposes BorgIQ tools outward. To consume tools from a BorgIQ agent, use itsmcpServersarray —type: httpfor an external server,type: borgiqto point at an McpServerActor (which is how an agent reuses one internally, rather than calling its public endpoint with a token). - Leaking secrets via tool schemas. Tool input schemas are visible to the agent; description and title fields can accidentally include secret hints. Audit them before deploy.
References
| File | What's inside |
|---|---|
references/ai-actor.md |
Single LLM call: text generation, structured output, tool definitions. When NOT to use it. |
references/ai-agent-actor.md |
Serverless coding agent: built-in filesystem/bash tools, BorgIQ actor tools, sessions, Done/Status ports, runtime sizing. |
references/deprecated-ai-agent.md |
Legacy orchestrator-loop agent (DeprecatedAiAgent) — read/debug existing flows only. |
references/ai-agent-api-guide.md |
Programmatic agent workflow API: creation, editing, execution, flowrun monitoring. |
references/agent-harness-actor.md |
Sandboxed Claude Code: full VM, session persistence, MCP, network controls. |
references/mcp-server-actor.md |
Expose BorgIQ tools as MCP endpoint, PAT auth, JSON-RPC protocol. |
references/message-processor-actor.md |
issueCallbackToken / waitForCallbackToken for human-in-the-loop agent flows. |
When to hand off to other spokes
| Customer ask | Hand off to |
|---|---|
| "Design the schema for this tool's input" | borgiq-json-schema-builder |
| "Pause for human approval mid-agent" | borgiq-form-builder (InterfaceActor with callback token) |
| "Render the agent's findings in a custom UI" | borgiq-react-app-builder |
| Edges, msgVars, deploy, debug, CommentActor | Hub: borgiq-builder |