Assembly LineDocs

Building Agents

Build and inspect a production-capable agent from Markdown and plugins.

Edit

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 agent

The 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 agent

validate 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 agent

This 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 agent

Later 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"}' --approve

4. 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 agent

The 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: always

There 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 channel

The 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: e2b

Provider 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: slack

The 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: silent

The 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:
  - reviewer

Then 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-run

The 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.md declares 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.

On this page