Back to projects
Active Started Jun 2026

Agent Builder Studio

One app for the agent design → run → package lifecycle — a visual canvas authors and runs a governed spec; a deterministic engine packages it. Local-first.

JavaScript Next.js 16 React 19 MLX Ollama

In development (beta). Mid-merge: apps/agent-builder (packaging workbench) and apps/agent-studio (runtime canvas) are being collapsed into a single canonical app, with phased compile + test gates between steps. apps/agent-studio is the primary, launchable app today (npm run dev, port 3030); apps/agent-builder is a retired-UI signpost that still hosts legacy tooling. The whole monorepo is plain JavaScript/.mjs — no TypeScript.

The Problem

Designing an agent, running it against a real model, and packaging it for reuse are usually three disconnected tools — a whiteboard, a script, and a build step — with the truth scattered across all three. The graph you drew drifts from the code you ran, which drifts from the thing you shipped.

Approach

The design bet: the agent spec is the single source of truth; canvas layout, run transcripts, and the generated package are all derived, rebuildable projections that can be regenerated from the spec. A derived layer never becomes the source of truth.

The corollary that shapes the packaging engine: there is no separate code path for “generate a skill” versus “generate a plugin” versus “generate an agent.” Every export — whether from a two-node prototype or a nine-node governed graph — runs through the same deterministic template engine (@tyroneross/agent-pack) and produces the same artifact shape: a self-contained, copyable folder. What changes per-spec is how much validation scaffolding that folder carries and which extra skill files get bundled into it — both decided by rule-based signal detection over the spec text, not by a human picking an output type from a menu. Converting the folder’s skills/skill-bank.json entries into an actual Claude Code skill, a subagent, or a Codex AGENTS.md entry is left to the operator at install time (setup/host-deployment.md documents the mapping); the generator does not emit .claude-plugin/, commands/, hooks/, or MCP-server manifests for the agents it builds. (The one plugin manifest in the repo, apps/agent-builder/plugin/.claude-plugin/plugin.json, packages Agent Builder Studio itself as a Claude/Codex skill for advising on harness design — that’s the tool’s own distribution, not something end-user specs route into.)

Architecture

Two independent flows share one spec contract: run (Studio executes the graph live against a local model) and package (the same graph, or a hand-authored spec, is compiled into an installable folder). Tracing one graph from canvas to shippable package:

flowchart TD
    A["Canvas: author nodes + edges<br/>(role: agent/tool/subagent/guardrail/<br/>approval/verifier/orchestrator/executor/eval/memory)"] --> B["localStorage<br/>agent-studio:v7"]
    A --> C["Live run: agent-runtime.mjs<br/>topological DAG, 4-wide per-level parallelism"]
    C --> D["Ollama /api/chat, stream:true<br/>default gpt-oss:20b, per-node override"]
    D --> C
    C --> E["Run transcript + per-node cache<br/>(derived, replayable)"]

    B --> F["spec-export.mjs: projectToSpec()<br/>canvas -> agent-spec/v1"]
    F --> G{"Export depth"}
    G -->|"portable (10 files)"| H["agent.yaml, manifest.json,<br/>system-prompt.md, tools.json, evals/, memory/"]
    G -->|"full package (~30-40 files)"| I["agent-pack: buildAgentArtifacts()<br/>zero LLM calls, all deterministic templating"]

    I --> J["spec-profile.js: inferSpecProfile()<br/>regex/keyword signal detection over spec text<br/>-> skill / personal / team / enterprise"]
    J --> K["Profile sets required contracts:<br/>skill-contract only (skill) up to<br/>agent-registry+lifecycle+observability (enterprise)"]
    I --> L["emitted-capabilities/index.mjs<br/>conditional skill/tool bundler"]
    L -->|doc-heavy spec| M["doc-ingest tool + skill"]
    L -->|write/deploy/credential signal| N["threat-modeler skill"]
    L -->|doc-producing output| O["pyramid-principle skill"]
    K --> P["Package folder: manifest, agent.yaml,<br/>system-prompt.md, tools.json,<br/>skills/skill-bank.json, contracts/*,<br/>runtime adapters, evals/, memory/"]
    M --> P
    N --> P
    O --> P
    P --> Q["/api/artifacts: stage()<br/>.artifacts/package/<slug>/ under workingFolder"]
    Q --> R["promote()<br/>copy to <workingFolder>/promoted/<slug>/"]
    R --> S["Operator installs into a host:<br/>API-key runtime, Claude (skills/subagents),<br/>Codex (AGENTS.md), or hybrid multi-agent"]
  1. Author — the Studio canvas builds a project as nodes (one of eleven canonical roles: agent, tool, subagent, guardrail, approval, verifier, orchestrator, executor, eval, memory, state) and edges, held in browser localStorage (agent-studio:v7); no server-side project database exists.
  2. Runagent-runtime.mjs topologically orders the graph from explicit edges plus declared inputs/outputs tag-matching, detects cycles before any call, and executes each level up to 4 nodes in parallel, streaming NDJSON from Ollama’s /api/chat (stream:true, format:"json", default model gpt-oss:20b, overridable per node or via OLLAMA_MODEL).
  3. Export to specspec-export.mjs#projectToSpec() converts the canvas into agent-spec/v1: canonicalizes each node’s role, drops studio-only fields (canvas x/y, mock outputs, snapshots, pan/zoom — logged in the README’s “stripped fields” list), and fills runtime defaults (Ollama, custom-loop framework, deny-by-default permissions).
  4. Route to depth — the studio offers two export sizes from the same spec: a lightweight 10-file “portable spec” (exportProjectToSpec) or the full ~30-40-file installable package (exportProjectToFullPackage, delegating to @tyroneross/agent-pack#buildAgentArtifacts). Both call the same validateSpec() gate first.
  5. Infer validation profilespec-profile.js#inferSpecProfile() decides how much governance scaffolding the package needs: an explicit validationProfile field wins outright; otherwise regex/keyword detection over the spec’s description, tools, and I/O looks for enterprise signals (production, regulated, hipaa, sox, iam, customer data, etc.) → enterprise, then shared-workflow signals (hosted/hybrid runtime, human-checkpoint nodes, ≥4 agent-like nodes) → team, else → personal. A fourth profile, skill, exists only when explicitly declared and requires just a skill contract, not the full contract set.
  6. Bundle conditional capabilitiesemitted-capabilities/index.mjs runs three independent deterministic detectors over the spec text and, only when triggered, adds extra files: detectDocIngest (document-handling language) bundles a doc-ingest tool + skill and a runtime/doc-ingest.mjs shim; detectRiskSurfaces (write/deploy/credential/destructive language) bundles a skills/threat-modeler.skill.md; detectDocProducer (report/brief-style outputs) bundles a skills/pyramid-principle.skill.md. None of this is model-driven — every trigger is a word-boundary regex match against spec fields.
  7. Emit the packagebuildAgentArtifacts() assembles manifest, agent.yaml, system-prompt.md, tools.json, skills/skill-bank.json (one skill entry per graph node, keyed to that node’s tools/inputs/outputs/permission), the profile-gated contracts/*.yaml files (system-boundary, tool-contracts, flow-topology, guardrails, human-checkpoints, agent-registry, observability, lifecycle — filtered to what the inferred profile requires), runtime adapter guides for seven target frameworks (custom-loop, OpenAI Agents SDK, Claude Code subagents, LangChain DeepAgents, LangGraph, Pydantic AI, NVIDIA NeMo Agent Toolkit), and evals/golden-tasks.json seeded from any node fixtures captured during a live run.
  8. Stage and promote/api/artifacts (backed by @tyroneross/agent-artifacts) writes the package to a git-ignored .artifacts/package/<slug>/ under the project’s working folder, then promote copies it to a standalone <workingFolder>/promoted/<slug>/ outside the app tree — the operator’s install unit.
  9. Install into a hostsetup/host-deployment.md (generated per package) is prose guidance, not code: it tells the operator when to convert a skills/skill-bank.json entry into a Claude Code skill or subagent (“only when isolated context is useful”), how to wire it into Codex via AGENTS.md, or how to run it as a plain API-key runtime — this final artifact-type decision is a human judgment call the generator surfaces but does not automate.

Models — which, where, why:

StageModel / tierWhere it runsWhy there
Studio live runOllama, default gpt-oss:20b (env OLLAMA_MODEL or per-node model override)agent-runtime.mjs, one call per graph node per levelSingle local daemon, no key management, matches the studio’s local-first default; per-node override lets one graph mix model sizes
Chief-of-Staff / shared cascade (@tyroneross/local-llm, used by apps/chief-of-staff and apps/cos)Tiered: parse (MLX Llama-3.2-3B-Instruct-4bit / Ollama llama3.2:3b), mid (MLX Qwen2.5-3B-Instruct-4bit / Ollama qwen3:8b-q4_K_M), synthesis (MLX Qwen2.5-3B-Instruct-4bit / Ollama gemma4:26b)tiers.mjs#resolveCascade(), per node-type routing tableMLX is the local-primary lane (Apple Silicon, 127.0.0.1:8080, OpenAI-compatible), Ollama the local-fallback (localhost:11434); cascade order and cloud eligibility are config, not per-call choices
Cloud fallback (same cascade)Groq (Llama 3.1/3.3), Anthropic (Claude Haiku/Sonnet family), OpenAI (GPT-5 family)Only when local lanes fail or allowCloud policy is on-failure/always, and only when the matching API key env var is presentKey-gated and token-budget-capped (createCloudBudget) so a run degrades to local-only rather than silently spending cloud tokens
Package generation (agent-pack#buildAgentArtifacts)Nonen/a100% deterministic template/string assembly plus regex-based signal detection (spec-profile, emitted-capabilities) — no model call anywhere in the packaging pipeline

Tools & infra — which, why:

  • Storage — browser localStorage for project/canvas state (agent-studio:v7, versioned with migrations); the local filesystem (via Next.js route handlers, path-allowlisted to /Users/, /tmp/, /var/folders/) for staged/promoted package exports. No server database — the app is single-user, local-first by construction.
  • Runtime — Next.js 16 App Router (route handlers under app/api/) + React 19, plain JavaScript/.mjs throughout (no TypeScript).
  • Model runtimes — Ollama (localhost:11434) as the default local backend; MLX (127.0.0.1:8080, OpenAI-compatible mlx_lm.server) as the local-primary lane in the shared cascade package, Apple Silicon only.
  • Monorepo — npm workspaces across six apps (agent-builder, agent-studio, chief-of-staff, cos, meetings, investments) and six packages: packages/agent-spec (schema/validation/YAML/role vocabulary), packages/agent-pack (the deterministic packaging engine described above), packages/agent-artifacts (stage/promote helpers), packages/local-llm (the MLX→Ollama→cloud cascade), packages/tool-spec, packages/builder-tools.
  • Doc parsing@tyroneross/omniparse (local SDK, sibling repo) is the doc-ingest tool bundled into packages when detectDocIngest fires.

How it works

  1. A node is added to the canvas with a role (agent, tool, subagent, guardrail, approval, verifier, orchestrator, executor, eval, memory, or state) and, optionally, declared input/output tags.
  2. Running the graph computes a dependency map from explicit edges plus tag-matching (a node declaring inputs:["scope"] implicitly depends on any node declaring outputs:["scope"]), throws on cycles, then executes level-by-level, up to 4 nodes concurrently per level, streaming each node’s Ollama response and accumulating NDJSON chunks into a parsed JSON output.
  3. Exporting the project runs projectToSpec() to normalize the canvas into agent-spec/v1, then either the 10-file portable path or agent-pack#buildAgentArtifacts() for the full package — the same validateSpec() call gates both.
  4. Inside buildAgentArtifacts(), inferSpecProfile() reads the spec once and returns a profile (skill/personal/team/enterprise) that determines which contracts/*.yaml files are required; resolveEmittedCapabilities() reads the same spec and conditionally attaches doc-ingest, threat-modeler, and/or pyramid-principle skill files.
  5. The generator writes 20-plus core files unconditionally (manifest, agent.yaml, system-prompt.md, tools.json, skills/skill-bank.json with one entry per node, seven framework-adapter guides, evals/golden-tasks.json, memory/ stubs) plus the profile- and capability-gated extras, landing at roughly 30-40 files for a governed package.
  6. /api/artifacts stages the package under the project’s working folder and, on promote, copies it to a standalone folder outside the app tree — the unit an operator hands to a host runtime.
  7. The generated setup/host-deployment.md walks the operator through installing that folder as an API-key runtime, a Claude Code skill/subagent set, a Codex AGENTS.md entry, or a hybrid multi-agent setup — the artifact-type conversion is operator-driven prose guidance, not an automated branch in the generator.

Tech stack

Next.js 16 App Router + React 19, plain JavaScript (.js/.mjs, no TypeScript) across a six-app, six-package npm-workspaces monorepo. Ollama is the default local model runtime for the Studio canvas; MLX (Apple Silicon, OpenAI-compatible local server) is the local-primary lane for the shared local-llm cascade package used by the Chief-of-Staff apps, with Ollama as fallback and Groq/Anthropic/OpenAI as key-gated cloud lanes. The packaging engine (@tyroneross/agent-pack) is a zero-dependency deterministic template compiler — no model calls, no external services. Project state lives in browser localStorage; generated packages land on the local filesystem via path-allowlisted Next.js route handlers. @tyroneross/omniparse (a sibling local SDK) handles document parsing when a spec triggers the doc-ingest capability.

Status

Mid-merge — the two source apps are being collapsed into a single apps/studio, with phased compile + test gates between steps. It consolidates two earlier projects — Agent Builder (the evaluation/packaging workbench) and Agent Studio (the runtime canvas) — plus a standalone Chief-of-Staff product, merged via git subtree so the history is preserved.