The Agent Handle

Everything in this section is built on one call.

import { Axon } from "@axon/core"

const { axon: barry } = await Axon("../barry")

Axon() resolves an agent, boots it, and hands you a complete handle to that instance. Complete is the operative word: anything you can do to a running agent, you can do through this handle. That is what makes the layer above it — pipelines, pools, state machines, whatever you build — a thing you can write rather than a thing we have to provide.

What the handle can do

await barry.request(input)       // run to completion → { text, entries }
barry.stream(input)              // live entries as they arrive
await barry.prompt(name, vars)   // render one of this agent's prompts
await barry.tools.ns.fn(args)    // call one of this agent's tools
barry.session.id                 // this instance's session identity
barry.session.entries            // everything it has done
barry.on(type, handler)          // observe what it emits
await barry.stim(stimulus)       // send a stimulus directly
await barry.update(blueprint)    // hot-reload it
await barry.shutdown()           // tear it down

This is the same handle an agent-scoped script has as its axon global. Nothing is withheld because you constructed it yourself.

The three that matter most for managing many agents are the last three: session.id gives each instance an identity you can correlate against, shutdown() gives you lifetime control, and update() lets you change an agent without restarting it.

Naming the handle

Axon() returns the runtime; axon is the handle on it. Destructure and rename on the way out, so a script with three agents reads clearly:

const { axon: barry } = await Axon("../barry")
const { axon: dave }  = await Axon("../dave")

The names are yours. They are what this program calls these agents, not anything the agents know about themselves.

Instances are independent

Two handles share nothing:

await barry.prompt("review")   // ../barry/src/prompts/review.vue
await dave.prompt("review")    // ../dave's — a different file entirely

Separate tools, separate policy, separate session, separate capsule, separate memory. Results cross between them only when you put them in a prompt. See The Boundary.

One handle is one conversation

Consecutive calls on the same handle share full context:

await barry.request("we're refactoring the auth module")
await barry.request("start with the token parser")   // barry remembers

There is no sub-context to address inside an instance — no threads, no named conversations. Isolation comes from booting another instance, which gives you a real boundary rather than a partition inside one.

To reset an agent's context, shut it down and boot it again:

await barry.shutdown()
const { axon: fresh } = await Axon("../barry")

Booting is cheap enough to do often

A request-response agent is a small runtime. Booting several — a dozen, more — is ordinary, and running a pool of them is a reasonable thing to build.

Two things to keep in mind:

Continuous-mode agents are not small. An agent running a live cognitive loop on a timestep is doing work whether or not you are talking to it. Run few of those, not many.

Booting the same agent twice runs its module setup twice. If an agent declares a module that opens an external connection — a Discord gateway, a webhook subscription — two instances mean two connections. Fine for most modules, wrong for some. Prefer one instance per agent unless you have a reason.

// one runtime, called twice — shares context
const { axon: barry } = await Axon("../barry")
await barry.request("first task")
await barry.request("second task")

The current agent

Called with no argument, Axon() walks up from the script and boots the agent folder it finds:

const { axon } = await Axon()

Useful when a script lives in an agent repo but wants boot to be explicit — work that runs before an agent exists, then boots one:

const branch = await gitBranch()
if (branch === "main") process.exit(0)   // nothing booted, nothing to clean up

const { axon } = await Axon()
await axon.request(`review the changes on ${branch}`)

Inside src/scripts/, where the agent is already running, this returns that running instance rather than booting a second one. The call means "the agent for this context" in both places; only the cost differs.


Next: Global Scripts — the file that holds all this.