Building Agents
Build and inspect a production-capable agent from Markdown and plugins.
An Assembly Line agent is a folder whose canonical declaration is agent.md.
The Markdown body contains permanent instructions; YAML frontmatter selects
runtime policy, infrastructure, channels, published plugins, automations, and
subagents. Root Skills and checked-in local plugins are discovered by
convention.
Commands below use an installed assembly-line binary. In this repository,
run the same commands as pnpm assembly-line <command> after pnpm build.
1. Scaffold the smallest agent
assembly-line init agentThe command creates this source shape:
agent/
agent.md
config.production.ts
skills/
plugins/
sandbox/
subagents/
assets/
evals/The initial runnable agent.md is:
---
name: Assembly Line Agent
model: openrouter/openai/gpt-5.4-mini
---
# Role
You are a concise Assembly Line agent. Use tools when they are available.gateway, state, blob, and sandbox default to local. The scaffold omits
them because it has no exceptions.
The empty typed config.production.ts is where committed non-secret deployment
values go. Local shell values override it; credentials never belong in it.
There is no gateway.ts, agent.ts, root tools/, root hooks/, or root
connections/. Safe framework tools are loaded automatically and are expanded
in the resolved view.
assembly-line validate agent
assembly-line inspect agent --resolved
assembly-line capabilities agentvalidate checks the authored folder. inspect --resolved emits the complete
compiled truth. capabilities evaluates the same setup path used for a run,
without calling a model or performing a side effect.
2. Add instructions and Skills
Keep always-on identity and safety policy in the Markdown body. Put occasional procedures and reference material in root Skills:
agent/
skills/
weekly-review/
SKILL.md
references/Root skills/*/SKILL.md folders are indexed automatically; do not list 30 or
50 skill names in frontmatter. Skill bodies and resources remain lazy until the
runtime selects them. Published and local plugins may also bundle reusable
Skills. Name collisions fail compilation instead of silently overriding one
source.
When a selected plugin Skill uses a compiled path outside the generic
/skills resource tree, the runtime materializes that trusted path alongside
the writable /workspace/.agents/skills/<name>/SKILL.md projection. Plugin
scripts and references can therefore use their compiled relative paths without
expanding the generic resource-projection roots.
3. Add one custom executable capability
When an existing published plugin does not provide the capability, create a cohesive local plugin:
assembly-line plugin init notes agentThis creates plugins/notes/plugin.json and
plugins/notes/ai.assemblyline/index.ts. Local plugins activate by validated
directory presence, so notes must not be repeated under capabilities: in
agent.md.
Add plugins/notes/ai.assemblyline/record-note.ts:
import { approvalRequired, defineTool } from "@assemblyline-agents/core";
export default defineTool({
description: "Record a durable note after explicit approval.",
inputSchema: {
type: "object",
properties: { note: { type: "string" } },
required: ["note"]
},
needsApproval: approvalRequired(
"Recording a note is a durable side effect.",
"idempotent"
),
async execute(input: { note: string }, ctx) {
await ctx.emit("note.recorded", {
note: input.note,
idempotencyKey: ctx.idempotencyKey("record-note")
});
return { recorded: true, note: input.note };
}
});Expose its static authority from plugins/notes/ai.assemblyline/index.ts:
import { defineAssemblyLinePlugin } from "@assemblyline-agents/core";
export default defineAssemblyLinePlugin({
tools: {
record_note: {
definition: "./ai.assemblyline/record-note.ts",
description: "Record a durable note after explicit approval.",
inputSchema: {
type: "object",
properties: { note: { type: "string" } },
required: ["note"]
},
needsApproval: {
mode: "always",
reason: "Recording a note is a durable side effect.",
sideEffect: "idempotent"
}
}
}
});The entry is a statically inspected authority descriptor; the tool module is
the runtime implementation. Compilation never executes plugin source.
TypeScript modules may use their emitted ESM extensions in local imports—for
example, import "./helpers.js" resolves a neighboring helpers.ts or
helpers.tsx when no authored JavaScript file exists.
Editing the initially empty plugin adds executable authority, so review and accept the lock change explicitly:
assembly-line plugin lock agent --confirm-upgrade
assembly-line validate agent
assembly-line explain capabilities.record_note agentLater implementation-only edits can refresh the content lock without
--confirm-upgrade. Adding a tool, MCP server, hook, connection, or another
executable contribution again requires explicit confirmation.
Run the tool and observe its durable approval gate:
assembly-line run agent --tool record_note \
--input '{"note":"Ship Friday"}'
assembly-line run agent --tool record_note \
--input '{"note":"Ship Friday"}' --approve4. Select published plugins
Published capability plugins cover external tools, connections, and reusable Skills. Install and select one by package or official ID:
assembly-line add notion agentThe command installs the package, adds its stable plugin ID under capabilities: in
agent.md, and records its exact version, integrity, portable components, and
Assembly Line authority in plugins.lock. Compilation never downloads an
undeclared package. An upgrade that expands authority requires
--confirm-upgrade.
Normal calls inside an already authorized connection grant do not ask again. The connection still owns credential binding and scopes, while dangerous tool actions retain their compiled approval policy. Narrow exceptions stay under the selected plugin:
capabilities:
notion:
tools:
disable:
- delete_page
approval:
update_page: alwaysThere is no top-level connections: or tools: field. Those concepts remain
fully represented in the resolved manifest and runtime.
5. Select channels and infrastructure explicitly
Channels are not capability selections because they define the agent's public interface. Add and select a reviewed provider explicitly:
assembly-line add slack agent --role channelThe resulting source selection is concise:
channels: [slack]Multiple channels and exceptions use mapping form. One channel becomes the default automatically; with multiple channels, mark exactly one default when outbound delivery needs one:
channels:
slack:
default: true
telegram: {}Infrastructure uses the same short profile rule:
deploy: railway
state: postgres
blob: r2
sandbox: e2bProvider defaults, package provenance, and required environment remain visible
in inspect --resolved. Scheduling is kernel behavior over the selected state
adapter, not a plugin selector. Use the single-key object form
only for an exception. E2B blocks outbound internet by default; opt in with
sandbox: { e2b: { internet: enabled } } only when the agent requires egress.
Photon, Slack, Teams, and Telegram include OpenRouter audio transcription. Set
channels.<name>.audio only to use OpenAI or disable transcription.
Custom immutable sandbox profiles live at sandbox/<name>.yaml. Every valid
profile is compiled for conditional composition; sandbox: chooses the
default.
6. Add automations
Put short, central routines directly in agent.md:
timezone: America/Chicago
automations:
morning-review:
schedule: weekdays at 08:00
skill: weekly-review
delivery: slackThe schedule language is deterministic. Supported forms include daily at 06:30, sunday at 18:00, weekdays at 09:00, every 15 minutes, and
monthly on day 1 at 08:00. { cron: "0 9 1 * *" } is the precise escape
hatch. The compiler emits cron plus timezone; the runtime never parses prose.
Connection webhooks do not implicitly start runs. Subscribe with an authored event automation:
capabilities:
- mirror
automations:
strava-review:
trigger: mirror.source.changed
filter:
provider: strava
skill: weekly-review
delivery: silentThe plugin owns ingress verification and normalized events. The automation owns the filter, target, idempotency, and delivery. Complex prepare/finalize behavior is a named lifecycle contribution from the plugin, not JavaScript embedded in YAML.
7. Add subagents and conditional composition
Declare child names in the parent:
subagents:
- reviewerThen create subagents/reviewer/agent.md. Each child has a complete isolated
MD-first surface: its own models, Skills, plugins, channels, sandbox profiles,
context, output schema, and nested children. Unlisted directories do not become
agents.
Files selected with handoff_artifact are immutable, durable, and addressed as
/files/handoffs/<child-run-id>/<path>. A later root or worker run may read that
path when it belongs to the same canonical conversation, which lets one worker
build on another worker's verified output without copying bytes through model
text. The runtime denies the same path from every other conversation.
Common conditional policy is declarative:
composition:
- when:
principal.roles:
includes: finance
model: openrouter/openai/gpt-5.4
capabilities:
enable: [record_note]
- otherwise: true
model: openrouter/openai/gpt-5.4-mini
capabilities:
disable: [record_note]Arbitrary conditional setup remains available as a named plugin composition contribution. Its output is constrained to the statically compiled model, sandbox, and capability ceilings and is re-evaluated after control-state changes.
8. Test and inspect
Keep deterministic cases under evals/*.json. Every case uses the production
runtime path with isolated state, blob, sandbox, and delivery boundaries.
Before deployment, run:
assembly-line validate agent
assembly-line inspect agent --resolved
assembly-line capabilities agent
assembly-line build agent
assembly-line eval agent
assembly-line deploy agent --dry-runThe resolved view is the audit surface for all framework defaults, tools, schemas, approval policy, connection grants, plugin versions and hashes, channels and routes, normalized automations, adapters, preflight requirements, subagents, mutability ceilings, and provenance.
Design rules
agent.mddeclares intent, policy, routines, channels, and infrastructure.- Root
skills/contains agent-owned procedures and knowledge. - Published capability plugins are selected in
agent.md; local plugins are activated by directory presence. - A local plugin should own one coherent capability, not miscellaneous code.
- Secrets and account bindings stay outside source control.
- Inspect the resolved agent whenever a concise source change could expand authority.
For every accepted value and default, use the Declarative Reference. Continue with Plugins for executable extensions and Runtime and Deployment for production operations.