apps/agent-forge) is this SDK’s companion visual dashboard:
build an agent’s graph on a canvas, run it, watch it execute in a live
debug console, chat with it, and author sandboxed pre/post hooks - all
reading and writing the exact same AgentSpec YAML that lousho dev and
lousho build use. It’s launched with one command, lousho studio, and
ships as part of this SDK’s npm package (LOU-S).
This doc covers: installation, the lousho studio quickstart, a
first-agent walkthrough, hook authoring, time travel (replaying a run from
a past step), and how settings/secrets/deploy wiring works (and doesn’t
yet).
Installation
Agent Forge ships inside@lousho/build-ai-agent itself - there’s no
separate package to install:
lousho studio (below) runs a pre-built copy of the app; you
don’t need apps/agent-forge’s own source or its devDependencies (Vite,
tsx, etc.) to use it.
If you’re working inside this SDK’s own monorepo instead (contributing to
Agent Forge itself), see Dev mode below.
Quickstart
http://127.0.0.1:4750). Open it in a browser - you’ll see the canvas,
the left rail (your saved agents + a node/hook palette), the Inspector
(right), and a bottom drawer with Chat/Logs/Trace/Output/Settings tabs.
Useful flags:
.lousho/ in the directory you
ran lousho studio from (agent YAML files under .lousho/agents/,
checkpoints and approvals alongside them) - the same .lousho/ layout
lousho dev/lousho build use.
Production vs. dev mode
lousho studio has two modes, and picks the right one automatically:
- Production (the default once Agent Forge has been built): a single
Express server serves both the REST/WebSocket API and the pre-built
client (React app) as static files, on one port. This is what runs when
you
npm installthe published package - there’s no separate Vite dev server and no TypeScript loader involved. - Dev (falls back to this if the build hasn’t been run yet, i.e. inside
a source checkout of this monorepo): the API server runs straight off
its TypeScript source via
tsx, alongside a real Vite dev server (with hot-module reload) that proxies API requests to it. Two processes, two ports internally, one command.
--prod/--dev force one or the other; run without either flag and
lousho studio auto-detects based on whether apps/agent-forge/dist-server
exists.
First-agent walkthrough
- Create an agent. In the left rail’s “Agents” tab, click New,
give it a name, and pick a starting template - Blank graph (a single
LLM node using the built-in
mockprovider) is the fastest way to try things out without any API keys. Agent Forge creates the agent and opens it on the canvas. - Look at the graph. A blank agent is one
llmnode. Drag more nodes in from the left rail’s palette (Trigger, LLM step, Tool call, Approval gate, Response / output) and connect them by dragging between their handles. Click a node to edit its settings (prompt, provider/model, tools, breakpoints) in the Inspector on the right. - Run it. Click Run in the top bar. With the
mockprovider there’s nothing to configure - it returns deterministic canned output, which is exactly the point for trying the rest of the UI without needing a real LLM API key. The status pill in the top bar tracks the run (running→idle/error/awaiting approval). - Watch it in the debug console. Open the bottom drawer’s Logs
tab for a live, filterable log feed of the run (trigger/llm/tool/
sandbox/checkpoint/approval events), or Trace for a span waterfall.
Turn on Debug in the top bar (or set a breakpoint on a node in the
Inspector) to pause the run at LLM/tool boundaries and step through it
with the debug bar’s Step/Continue controls; expand “Live
message array” there to inspect the in-flight message list. Output
shows the full
ExecutionResult(messages, tool calls, usage, steps) as a collapsible JSON tree once the run finishes or pauses. - Chat with it. The Chat tab is a separate conversational
transport (
POST /agents/:id/message, streamed back over the same WebSocket as everything else) - send it a message and it replies in the thread, independent of the graph “Run” button above. Each agent keeps its own chat history (+ New chatstarts a fresh session; old ones stay browsable). - Approve a paused tool call. Add a Tool call node, open it in the Inspector, and pick a tool - some tools (or an agent’s own policy) can require human approval before running. When a run or chat hits one, an inline approval card appears (in the top bar for a graph run, or as a message bubble in the Chat thread) showing the tool name and its arguments. Click Approve to let it proceed or Reject to abort that call; the run resumes automatically either way.
Hooks
Hooks (LOU-Q) are small, sandboxed functions that run immediately before or after a tool call or an LLMgenerate - the same mechanism the SDK core
exposes as HookRegistry/AgentHook (see
API Overview), but authored visually and attached to a
specific canvas node.
To attach one:
- Select an
llmortoolnode on the canvas (hooks only apply to these - they’re the two pointsAgentExecutoractually calls out to). - Open the Inspector’s Hooks section. Drag a Pre-hook or
Post-hook entry from the left rail’s palette onto the node (or drop
it directly in the Hooks section) to attach a new one; you’ll get a
choice of starter templates (
redact-pii,rate-limit,audit-log,inject-context) as an editable starting point. - Click the hook’s chip to select it, then edit its code in the CodeMirror
editor. The code is the body of an async function invoked as
hook(ctx)inside a sandboxed subprocess (server/hookSandbox.ts) - never in the browser or the API server process itself:- For a tool-call hook,
ctxis{ toolName, args, result?, error? }. A pre-hook can mutatectx.args; a post-hook can inspect/mutatectx.result/ctx.error. Returnctx. - For an LLM-generate hook,
ctxis{ messages, model }. Mutate/push ontoctx.messagesand returnctx. - Throwing aborts the step - a rate-limit hook, for example, throws to block the tool call outright rather than letting it run.
- For a tool-call hook,
- Toggle the switch on a hook’s chip to enable/disable it without removing it, or use its x to remove it entirely. Only enabled hooks are compiled into the run.
redact-pii
starter template) -
spec.policy.hooks in the agent’s YAML;
the server compiles enabled hooks into a real HookRegistry for the run
(server/compileHooks.ts), routed through the same SandboxAdapter a
sandboxed tool uses.
Time travel
Agent Forge keeps every run’s checkpoints, not just the latest: its file store (server/checkpointStore.ts) keeps the same bounded history as the
SDK’s LocalStorageCheckpointStore (the newest 50 saves per run, under
.lousho/agents/<id>/checkpoint-history/; see
Checkpoint history). From
that history you can fork a run at any step, change what happened there,
and replay it next to the original, with
AgentExecutor.fork() and
compareTrajectories() doing the work.
The History tab
- Run the agent (Topbar Run, or a Chat message).
- Open the bottom drawer’s History tab. It lists the run’s steps: step number, status, the model’s finish reason, the tool calls made, and the tokens and cost of the step’s model call when known.
- Click Edit and replay from here on a step. Choose Append a user message and type one, or pick one of the step’s tool calls to edit its result (prefilled with the recorded result; text that parses as JSON is sent as JSON).
- Click Fork and replay. The run is forked at that step with your
edit and started as a new run,
<id>.fork-<n>. - The fork opens next to the original as a side-by-side trajectory: each model turn of both runs (text, tool calls and results), the first turn where they differ highlighted as diverged, and the drift entries (tool order, arguments, step count, finish reason) listed below. It refreshes when the fork finishes.
Routes
A run id is the run’s checkpoint session id: the agent id for the agent’s own runs,<id>.fork-<n> for a fork of run <id>. The runtime control
server has three routes for it (the History tab uses them):
The flow, for an agent
weather that has run once:
Settings, secrets and deploy wiring
The bottom drawer has a Settings tab with three parts: provider API keys (OpenAI and Anthropic, stored encrypted under.lousho/ and never shown
again), named settings profiles (provider, deploy adapter, hook timeout,
OpenTelemetry toggle; kept in .lousho/settings.json, which holds no
secrets), and a Deploy section that picks an adapter (node-server,
docker or cloudflare-worker) and runs lousho build for the selected
agent. Other providers still read their environment variables, the same way
lousho dev/lousho build do (see Configuration), and
the mock provider needs no credentials.
Dev mode
If you’re working inside this SDK’s own monorepo (contributing to Agent Forge itself, not just using it),lousho studio falls back to dev mode
automatically as long as apps/agent-forge/dist-server hasn’t been built
yet:
tsx) and a real Vite
dev server with hot-module reload for apps/agent-forge/src/**, as two
sibling processes. To build the production bundle used by everyone else
(and to verify what actually ships):
npm run build:studio also runs automatically as part of the root
prepublishOnly script, so a published npm publish can never ship a
stale or unbuilt studio.
E2E smoke test
apps/agent-forge/e2e/studio.spec.ts is a headless Playwright test that
drives the real, built lousho studio (the same dist-server/index.cjs
production server, not a dev-mode Vite server) through a browser: it
creates an agent from the “Support bot” template, retargets its tool node
at a server-local demo-approval tool (always needsApproval: true -
see server/buildAgent.ts’s doc comment for why this exists, since none
of the SDK’s spec-resolvable built-in tools require approval), sends it a
chat message that triggers the mock provider’s tool-call heuristic, waits
for the run to pause on the approval gate, approves it via the Chat tab’s
inline approval card, and asserts the run completes. Run it with:
build:studio), then runs Playwright
against it.