Assembly LineDocs

Authoring Plugins

Build local or published Agent Plugins v1 capabilities for Assembly Line.

Edit

Assembly Line uses Agent Plugins v1 as its one executable capability package boundary. A plugin may remain checked into one agent or be published without changing shape.

Use a plugin for one cohesive capability or provider role: its tools, external connection, webhook contract, trusted hooks, model registration, channel, or infrastructure implementation. Keep schedules and agent instructions in agent.md, and keep unrelated utilities out of the plugin.

Channels and infrastructure remain explicit selections in agent.md. Their provider packages use the provider contract described at the end of this page; they are not hidden inside the agent's capabilities: list.

Minimal local plugin

Create a conforming checked-in plugin with:

assembly-line plugin init meal-memory ./agent

The resulting shape is:

agent/plugins/meal-memory/
├── plugin.json
└── ai.assemblyline/
    └── index.ts

Every immediate child of root plugins/ is validated and activated automatically. Do not repeat a local plugin ID in agent.md.

plugin.json is the standard Agent Plugins v1 envelope:

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "meal-memory",
  "version": "0.1.0",
  "description": "Durable meal capture and editing.",
  "extensions": {
    "ai.assemblyline": {
      "entry": "./ai.assemblyline/index.ts"
    }
  }
}

Portable plugin components use the standard fixed locations:

  • skills/<name>/SKILL.md for reusable Agent Skills.
  • mcp.json for portable MCP servers and their tool surfaces.
  • ai.assemblyline/ for typed Assembly Line-native capabilities.

Static extension entry

The compiler statically inspects the declared extension entry. It never imports or executes the entry during discovery. Keep it declarative: export an object literal, or pass one to defineAssemblyLinePlugin. Executable implementations live in contained modules referenced by definition.

// plugins/meal-memory/ai.assemblyline/index.ts
import { defineAssemblyLinePlugin } from "@assemblyline-agents/core";

export default defineAssemblyLinePlugin({
  tools: {
    save_meal: {
      definition: "./ai.assemblyline/save-meal.ts",
      description: "Save one meal to durable memory.",
      inputSchema: {
        type: "object",
        properties: {
          meal: { type: "string" },
          eatenAt: { type: "string" }
        },
        required: ["meal", "eatenAt"]
      },
      capability: {
        visibility: "always",
        execution: "direct",
        tags: ["nutrition", "memory"]
      }
    }
  }
});

The implementation keeps the existing typed tool contract:

// plugins/meal-memory/ai.assemblyline/save-meal.ts
import { defineTool } from "@assemblyline-agents/core";

export default defineTool({
  description: "Save one meal to durable memory.",
  inputSchema: {
    type: "object",
    properties: {
      meal: { type: "string" },
      eatenAt: { type: "string" }
    },
    required: ["meal", "eatenAt"]
  },
  async execute(input, ctx) {
    await ctx.step("save-meal", async () => {
      // Perform the durable, idempotent implementation here.
    });
    return { saved: true };
  }
});

The plugin entry key, save_meal, is the model-facing tool name. Internal module names and folders have no discovery semantics.

Native contribution types

The ai.assemblyline entry has one optional root configuration contract and a closed, typed set of contribution sections:

Root keyOwns
configSchemaJSON Schema for agent-owned options under capabilities.<plugin>.config.
toolsNative model-callable tools, schemas, approval, visibility, execution boundary, and environment requirements.
connectionsExternal identities, protocols, credentials, grants, events, and native/MCP/HTTP/SDK/CLI execution.
hooksAfter-persist event observers associated with the plugin capability.
lifecycleTrusted automation prepare and finalize implementations.
compositionBounded conditional setup logic that cannot fit the declarative composition DSL.
contextsCustom prompt/context policies.
instrumentationCustom observability setup.
channelsA custom implementation for an explicitly declared channel.
providersDeploy, state, blob, sandbox, secrets, or channel-media provider declarations.
modelsModel-provider selection, static metadata, and runtime package binding.
channelHooksNamed channel-specific behaviors, such as a first-contact attachment.

Unknown sections and malformed descriptors fail compilation. Source paths must remain inside the plugin. The runtime packages only declared implementations; it does not scan or execute arbitrary plugin files.

Plugin-owned per-agent configuration

A reusable published capability plugin can expose one typed configuration object. The schema is statically inspected and locked with the plugin:

export default defineAssemblyLinePlugin({
  configSchema: {
    type: "object",
    properties: {
      dataset: { type: "string" },
      resultLimit: { type: "integer", minimum: 1, maximum: 100 }
    },
    required: ["dataset"],
    additionalProperties: false
  },
  tools: {
    search_meals: {
      definition: "./ai.assemblyline/search-meals.ts",
      factory: "createSearchMealsTool",
      description: "Search the configured meal dataset.",
      inputSchema: { type: "object" },
      optionalConfig: ["MEAL_MEMORY_URL"],
      config: { resultLimit: 20 }
    }
  }
});

The agent supplies only its deviation:

capabilities:
  meal-memory:
    config:
      dataset: household
      resultLimit: 50

The compiler rejects the selection before code generation if it does not match configSchema. Native tool and connection factories receive their contribution defaults merged with the selected plugin config. Hook and lifecycle descriptors may name a factory that receives the selected config; composition handlers receive it as their argument. Static exports, Skills, and portable MCP declarations remain static.

Checked-in local plugins are already agent-specific and activate by presence, so keep their settings in their source declarations. config is for reusable published capability selection. Authored tools may declare only non-secret requiredConfig and optionalConfig; authenticated provider work belongs in a reviewed connection with explicit credential metadata.

Connections

A connection contribution preserves Assembly Line's existing connection runtime. It does not have to become a remote MCP server.

export default {
  connections: {
    meals_api: {
      definition: "./ai.assemblyline/meals-connection.ts",
      provider: "meals",
      protocol: "http",
      transport: "http",
      subject: "user",
      required: true,
      scopes: ["meals:read", "meals:write"],
      requiredConfig: ["MEALS_API_URL"],
      requiredCredentials: ["MEALS_API_TOKEN"],
      optionalConfig: [],
      optionalCredentials: [],
      events: {
        catalog: ["meal.created", "meal.updated"],
        defaultEvents: ["meal.created"]
      }
    }
  }
};

The referenced module exports a validated connection definition or a named factory. Protocols remain mcp, a2a, openapi, http, sdk, cli, or credential; the existing runtime continues to own authorization, account binding, credentials, tool discovery, approval policy, webhook verification, event normalization, and sandbox materialization.

The plugin advertises events. agent.md decides which event starts a run:

automations:
  update-plan:
    trigger: meals_api.meal.updated
    skill: update-nutrition-plan
    delivery: silent

No authored automation means no implicit run.

Hooks and automation lifecycle

Hooks belong in a plugin when they are part of that plugin's named capability. They are not a general dumping ground for agent behavior.

export default {
  hooks: {
    audit: {
      definition: "./ai.assemblyline/audit.ts",
      eventTypes: ["run.completed"]
    }
  },
  lifecycle: {
    daily_context: {
      definition: "./ai.assemblyline/daily-context.ts",
      phases: ["prepare", "finalize"]
    }
  }
};

Reference lifecycle code by its qualified name:

automations:
  daily-review:
    schedule: daily at 06:30
    skill: daily-review
    lifecycle: meal-memory.daily_context

Generic durability, retries, approvals, scheduling, state transitions, and delivery remain framework responsibilities. A plugin hook should be removable with the capability it supports.

Published plugins

A published plugin keeps the same plugin.json and extension layout. Include the runtime files in the package:

{
  "name": "@acme/meal-memory-plugin",
  "version": "1.2.0",
  "type": "module",
  "files": [
    "dist",
    "plugin.json",
    "ai.assemblyline",
    "skills",
    "mcp.json"
  ],
  "peerDependencies": {
    "@assemblyline-agents/core": "^6.0.0"
  }
}

Select the installed package by the stable ID from its plugin.json:

capabilities: [meal-memory]

Use object form for plugin-owned config and contribution-scoped typed exceptions:

capabilities:
  meal-memory:
    config:
      dataset: household
    tools:
      disable: [delete_meal]
      approval:
        save_meal: always

assembly-line add @acme/meal-memory-plugin ./agent installs, selects, and locks the package. Compile never downloads dependencies implicitly. The plugin's configSchema owns accepted per-agent configuration. Environment requirements own credentials and deployment bindings.

Locking and authority review

plugins.lock records exact package versions, integrity, portable components, local content hashes, and the native authority inventory. Lock format version 3 is generated from the compiler's canonical plugin graph; the compiler does not maintain a reader for obsolete lock versions.

After editing a checked-in plugin, run:

assembly-line plugin lock ./agent

The command prints the capability and authority diff and records the developer's explicit local source change. A published package upgrade that changes Skills, MCP servers, tools, connections, hooks, or another executable contribution fails closed until the diff is reviewed and the command is rerun with --confirm-upgrade.

Ordinary runtime calls then follow the compiled grant and approval policy; a user is not asked to approve the plugin repeatedly.

Model providers

A model provider uses the same Agent Plugins v1 package boundary. Its static entry binds a provider prefix to a complete contract and runtime package:

module.exports = {
  models: {
    nebula: {
      definition: "@acme/assembly-line-nebula",
      provider: "nebula",
      defaultModel: "nebula/standard",
      contract: "./ai.assemblyline/models/nebula.json"
    }
  }
};

The runtime module exports assemblyLineModelProvider with the matching metadata and a create function returning a Pi provider. Optional hooks own dynamic metadata discovery, payload transformation, normalized response accounting, and semantic-memory embeddings. Authentication and deployment requirements belong in the static model contract. Pi stays provider-neutral.

The compiler rejects disagreement between the locked static contract and the runtime registration. Selecting model: nebula/standard requires no framework registry edit; assembly-line add @acme/assembly-line-nebula agent --role model installs, locks, and writes the plugin's declared default model.

Infrastructure and channel providers

Deploy, state, blob, sandbox, scheduler, secrets, and channels are explicit agent.md fields. Channel-scoped audio processors are selected only through channels.<name>.audio. A package that supplies an infrastructure or channel implementation is an Agent Plugin whose runtime module exports assemblyLineProvider:

import type { ProviderModule } from "@assemblyline-agents/core";

export const assemblyLineProvider: ProviderModule = {
  providers: [{
    metadata: {
      kind: "acme-state",
      role: "state",
      packageName: "@acme/assembly-line-state",
      stability: "supported",
      requiredConfig: ["ACME_DATABASE_URL"],
      optionalConfig: [],
      requiredCredentials: [],
      optionalCredentials: [],
      capabilities: ["durable-state"]
    },
    create(ctx) {
      return createAcmeState(ctx.env.ACME_DATABASE_URL!, ctx.options);
    }
  }]
};

Provider metadata flows into preflight, deployment planning, artifact dependencies, and runtime construction. An env entry in a deploy provider's conditional deployment.requirements or deployment.warnings must declare classification: "config" or classification: "credential". The compiler uses the active conditional credential entries to scope the selected deploy capability before reading any value; do not duplicate them in an unconditional credential list. The same package declares its authoring contract in the statically inspected Agent Plugin entry:

export default {
  providers: {
    state: {
      definition: "@acme/assembly-line-state",
      name: "acme-state",
      role: "state",
      kind: "acme-state",
      contract: {
        kind: "acme-state",
        role: "state",
        packageName: "@acme/assembly-line-state",
        stability: "supported",
        requiredConfig: ["ACME_DATABASE_URL"],
        optionalConfig: [],
        requiredCredentials: [],
        optionalCredentials: [],
        capabilities: ["durable-state"]
      },
      configSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          poolSize: { type: "integer", minimum: 1 }
        }
      }
    }
  }
};

assemblyLineProvider owns runtime construction. The statically inspected Agent Plugin contribution owns the compile-time contract: selection name, role, kind, complete provider metadata, defaults, package binding, and option schema. The contract must match the runtime registration; official conformance tests reject drift. Compilation never imports executable provider code to discover declarative authority.

The MD-first field grammar is closed, but the provider set is extensible. Adding state: acme-state requires installing and locking this plugin; it does not require a compiler registry edit. Agents cannot inject packageName, configuration/credential declarations, capabilities, runtime, or a generic options envelope.

Testing

At minimum, test that:

  • plugin.json validates against Agent Plugins v1.
  • The entry can be statically inspected without executing top-level code.
  • Every declared definition is contained and ships in the package.
  • Plugin configuration, tool and connection schemas, environment requirements, approval policy, events, and authority match the documented surface.
  • plugins.lock changes when content or authority changes.
  • A fixture agent.md compiles, packages, loads, and invokes each executable contribution through the real runtime path.
  • Credentials and account bindings never enter source, lockfiles, or model context.

The official package conformance and plugin runtime tests in this repository are the reference suite.

On this page