One of the key benefits of using Agention is that pipelines are typed end to end. Every agent, and every custom node you write, implements the same interface:

interface GraphNode<TInput, TOutput> {
  name?: string;
  nodeType?: GraphNodeType;
  execute(input: TInput): Promise<TOutput>;
}

Agents already implement GraphNode, so they drop straight into a pipeline with nothing extra to configure.

Chaining Agents

AgentGraph.sequential() chains nodes together, passing each one's output to the next:

import { AgentGraph } from "@agentionai/agents";
import { ClaudeAgent } from "@agentionai/agents/claude";

const researcher = new ClaudeAgent({
  id: "researcher",
  name: "Researcher",
  description: "Research the topic and list key facts.",
  apiKey: process.env.ANTHROPIC_API_KEY,
  model: "claude-sonnet-4-5",
});

const writer = new ClaudeAgent({
  id: "writer",
  name: "Writer",
  description: "Write a blog post from the research provided.",
  apiKey: process.env.ANTHROPIC_API_KEY,
  model: "claude-sonnet-4-5",
});

const chain = AgentGraph.sequential(researcher, writer);
const result = await chain.execute("Artificial intelligence in healthcare");

Custom Nodes Keep Their Types

Because the interface is generic, a plain function can join a pipeline right next to an LLM agent, and TypeScript still checks that the shapes line up:

import { GraphNode } from "@agentionai/agents";

const dataFetcher: GraphNode<string, object> = {
  name: "data-fetcher",
  nodeType: "custom",
  async execute(url: string): Promise<object> {
    const response = await fetch(url);
    return response.json();
  },
};

const pipeline = AgentGraph.pipeline(dataFetcher, analyzer);

If dataFetcher's output type doesn't match what analyzer expects as input, that's a compile error, not a runtime surprise.

Composing Without Losing Types

Executors are nodes themselves, so you can nest them: run a parallel research phase, then feed all of it into a sequential synthesis phase.

const researchPhase = AgentGraph.parallel({}, webSearcher, documentAnalyzer, expertConsult);
const synthesisPhase = AgentGraph.sequential(summarizer, factChecker);

const pipeline = AgentGraph.pipeline(researchPhase, synthesisPhase);

Observability Comes Free

Because every stage is a typed node, wrapping the whole pipeline in a metrics collector is one line:

import { createMetricsCollector } from "@agentionai/agents";

const metrics = createMetricsCollector();
const pipeline = AgentGraph.pipeline(researcher, writer).withMetrics(metrics);

await pipeline.execute("Input");
console.log(metrics.getAggregateMetrics());

Conclusion

Type safety here isn't a layer bolted on top. It's the same GraphNode interface all the way down, from a single agent to a tree of parallel and sequential stages. Your IDE can autocomplete and your compiler can catch mismatches, even several stages deep.