agent.ts
Compose an Assembly Line agent with static policy and synchronous runtime hooks.
agent.ts is the agent-composition entrypoint. Its defineAgent({...}) object
holds static identity, policy, and limits. Its synchronous setup() function
selects the compiled capabilities available to the next model request.
instructions.md remains required and always trusted. useInstructions()
only appends conditional guidance; it never replaces that permanent identity.
Minimal Example
import { defineAgent, useModel, useTool } from "@assemblyline-agents/core";
export default defineAgent({
id: "minimal-agent",
name: "Minimal Agent",
setup() {
useModel("openai/gpt-5.4-mini");
useTool("echo");
}
});setup() must be synchronous and side-effect free. The runtime preloads run
data and conversation state, validates the result against the compiled catalog
and static policy, and records the complete capability snapshot before using
it. The hook API provides no asynchronous setup phase. Keep network,
filesystem, and other side effects in tools or adapters.
Static Fields
| Field | Meaning |
|---|---|
id, name, description | Stable identity and display metadata. |
maxIterations | Hard positive agent-loop iteration limit. |
maxReasoning | Ceiling for useReasoning(). |
selfImprovement | Permission policy for durable skill authoring. |
dynamicAutomations | Permission policy for runtime-created automations. |
dynamicConnections | Permission and host policy for adopted connections. |
context | Trusted context policy, when a custom context.ts policy is required. |
metadata | Static JSON metadata. |
setup() | Synchronous runtime capability declaration. |
Model, reasoning selection, visible tools, output schema, skills, connections, subagents, sandbox profile, event observers, and conditional instructions do not belong in static fields.
Built-in Hooks
| Hook | Effect |
|---|---|
useRun() | Reads immutable run/message/channel/conversation/metadata/attachment metadata. |
usePersistentState(key, initial) | Reads conversation-scoped JSON control state and returns an async setter. |
useModel(model) | Selects exactly one model. |
useReasoning(level) | Selects effort within maxReasoning. |
useInstructions(text) | Appends trusted instructions in call order. |
useTool(name) | Enables a compiled tool. |
useSkill(name) | Activates a compiled skill. |
useConnection(name) | Selects a compiled connection; grants and approvals still apply. |
useSubagent(name) | Exposes a compiled subagent. |
useSandbox(name) | Selects a compiled sandbox profile; acquisition stays lazy. |
useOutputSchema(schema) | Selects the runtime-enforced final-output contract. |
useEvent(type, handler) | Registers an after-persist observer for this run. |
Set-like hooks deduplicate by compiled name. Repeated useModel(),
useReasoning(), useSandbox(), or useOutputSchema() calls must agree.
Conditional calls are valid:
state identity comes from explicit keys, not hook position.
Capability names, event types, models, reasoning levels, and state keys passed to hooks must be string literals so the compiler can audit them. Custom hooks are ordinary synchronous functions:
function useVerifiedCustomer() {
const [verified] = usePersistentState("customer.verified", false);
useTool("verify_customer");
if (verified) useTool("issue_refund");
return verified;
}There is no hook registry or special directory. Calling a built-in hook outside
setup() (or a function called by it) throws an actionable error.
Conditional Capabilities
import {
defineAgent,
useInstructions,
useModel,
useRun,
useTool
} from "@assemblyline-agents/core";
export default defineAgent({
setup() {
const run = useRun();
if (run.metadata?.tier === "trial") {
useModel("openai/gpt-5.4-mini");
useTool("search_docs");
useInstructions("Do not access billing information.");
return;
}
useModel("openai/gpt-5.4");
useTool("search_docs");
useTool("lookup_account");
}
});Every possible named capability must already exist in the compiled catalog. Hooks can narrow static policy but cannot bypass host restrictions, connection authorization, tool approvals, sandbox policy, or subagent declarations.
Durable State And Re-evaluation
usePersistentState() stores small JSON control values such as workflow
stages. Do not use it for secrets, files, transcripts, or long-form memory.
Each key may contain at most 200 characters. Each value may serialize to at
most 16,384 characters. A conversation snapshot may contain at most 256 keys
and 262,144 serialized characters.
The hook returns the current value and an async setter. Call the setter from a
tool or event handler, never during setup(). Tools can also update the same
state through ctx.agentState:
async execute(input, ctx) {
await saveDiagnosis(input);
await ctx.agentState.set("triage.stage", "report");
return { saved: true };
}The write is atomic, increments the conversation revision, emits an
agent.state_changed event with the key, revision, and either a value hash or
a deletion marker, and marks
the active snapshot dirty. Pass expectedRevision to ctx.agentState writes
when concurrent changes must fail instead of overwriting each other. The
runtime lets the current tool finish, then re-evaluates setup() before the
next model request. Model, prompt, tools, connections, subagents, sandbox, and
output schema change only at that boundary. One run may record at most 50
capability snapshots.
Event Observation
import { defineAgent, useEvent, useModel } from "@assemblyline-agents/core";
export default defineAgent({
setup() {
useModel("openai/gpt-5.4-mini");
useEvent("run.completed", recordCompletion);
}
});The callback runs after its event is durable. Exact event handlers run before
"*" handlers. Failure emits agent.event_handler_failed and does not fail
the run. Re-evaluation replaces registrations instead of accumulating them.
Structured Outputs
Call useOutputSchema(schema) in setup(). The runtime adds the schema to the
trusted prompt, validates the final JSON, performs bounded corrective retries,
and exposes the parsed value as RunAgentResult.output. maxIterations remains
a static hard limit.