> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentium.in/llms.txt
> Use this file to discover all available pages before exploring further.

# External Agents

> Serve agents written in any framework — LangGraph, Claude Agent SDK, or plain code — through Agentium's runtime.

# External Agents

Agentium's runtime (registry, Express/Socket.IO gateways, queue workers, observability) is **framework-agnostic**. Anything that satisfies the `ServableAgent` contract can be registered and served — the first-party `Agent` class is just one implementation.

## The ServableAgent contract

```typescript theme={null}
interface ServableAgent {
  readonly kind: "agent";
  readonly name: string;
  run(input: MessageContent, opts?: RunOpts): Promise<RunOutput>;
  stream(input: MessageContent, opts?: RunOpts): AsyncIterable<StreamChunk>;

  // Optional — each field lights up extra runtime features when present:
  readonly eventBus?: EventBus;          // tracing, metrics, approval streams
  readonly modelId?: string;
  readonly providerId?: string;          // provider-specific API key routing
  readonly tools?: ReadonlyArray<{ name: string; description?: string }>;
  readonly instructions?: unknown;       // swagger docs, agent cards
  readonly hasStructuredOutput?: boolean;
  readonly structuredOutputSchema?: unknown;
  readonly memory?: unknown;             // enables the corrections endpoint
  readonly approvalManager?: unknown;    // enables HITL approval endpoints
  readonly checkpointManager?: unknown;  // enables checkpoint endpoints
}
```

## defineExternalAgent()

The easiest way to wrap external logic. It normalizes your return value into a full `RunOutput`, provides a default `stream()` fallback, wires up an `EventBus` (so observability works out of the box), and auto-registers in the global registry.

```typescript theme={null}
import { defineExternalAgent } from "@agentium/core";

const researcher = defineExternalAgent({
  name: "researcher",
  run: async (input) => {
    // Wrap a LangGraph graph, a Claude Agent SDK call, or any custom code
    const result = await graph.invoke({ messages: [String(input)] });
    return result.messages.at(-1).content;   // plain string is fine
  },
});

// Immediately servable:
//   POST /agents/researcher/run
//   POST /agents/researcher/stream
```

### Returning rich output

Return a partial `RunOutput` to include usage, tool calls, or structured data — missing fields are filled with defaults:

```typescript theme={null}
const agent = defineExternalAgent({
  name: "extractor",
  run: async (input) => ({
    text: extracted.summary,
    structured: extracted,
    usage: { promptTokens: 800, completionTokens: 200, totalTokens: 1000 },
  }),
});
```

### Native streaming

By default, `stream()` runs to completion and yields the text as a single chunk. Provide your own generator for true streaming:

```typescript theme={null}
const agent = defineExternalAgent({
  name: "streamer",
  run: async (input) => fullResponse(input),
  stream: async function* (input) {
    for await (const token of llm.streamTokens(input)) {
      yield { type: "text", text: token };
    }
    yield { type: "finish", finishReason: "stop" };
  },
});
```

### Configuration

| Property                 | Type                                                     | Required | Default                 | What it controls                                  |
| ------------------------ | -------------------------------------------------------- | -------- | ----------------------- | ------------------------------------------------- |
| `name`                   | `string`                                                 | **Yes**  | —                       | Registry key and route path (`/agents/:name/...`) |
| `run`                    | `(input, opts) => Promise<string \| Partial<RunOutput>>` | **Yes**  | —                       | The agent's logic                                 |
| `stream`                 | `(input, opts) => AsyncIterable<StreamChunk>`            | No       | run-then-yield fallback | Native streaming                                  |
| `instructions`           | `string`                                                 | No       | —                       | Shown in registry descriptions and swagger        |
| `modelId` / `providerId` | `string`                                                 | No       | —                       | Metadata for discovery cards and API key routing  |
| `register`               | `boolean`                                                | No       | `true`                  | Auto-register in the global registry              |
| `eventBus`               | `EventBus`                                               | No       | fresh instance          | Bring your own bus for shared observability       |

## Observability for free

`defineExternalAgent` emits the standard run lifecycle events (`run.start`, `run.complete`, `run.error`) on its event bus, so [Tracer, MetricsExporter, and StructuredLogger](/observability/overview) work unchanged:

```typescript theme={null}
import { instrumentBus } from "@agentium/observability";

const agent = defineExternalAgent({ name: "researcher", run });
instrumentBus(agent.eventBus!, { tracing: true, metrics: true });
```

## Registry names are labels (v2.3.2+)

Registering an agent with an existing name **replaces** the previous entry (last-write-wins) — names are labels, not unique keys. This means the same agent definition can be constructed repeatedly (loops, factories, concurrent requests) without `Duplicate agent name` errors. Gateways resolve a name to the most recently registered instance.

```typescript theme={null}
// Safe to call per-request — no name collision errors
function classify(email: string) {
  const agent = new Agent({ name: "email-classifier", model: getModel(), ... });
  return agent.run(email);
}
```

Pass `register: false` (on `AgentConfig` or `ExternalAgentConfig`) for ephemeral agents that should skip the registry entirely.

## Cross-references

* [Express Transport](/transport/express) — the routes every served agent gets
* [Socket.IO Gateway](/transport/socketio) — realtime serving
* [Observability](/observability/overview) — event-bus-driven tracing and metrics
