@zenera/neo
v1.1.23
Published
A multi-agent runtime for Node.js: agents, models, tools, skills, memory and an append-only trajectory.
Maintainers
Readme
@zenera/neo
A multi-agent runtime for Node.js: agents, models, tools, skills, memory and an append-only trajectory.
Part of ZeneraNeo. This is an open-source side project for experimentation and chore work - not the official Zenera AI Platform. It carries no support or stability promises.
Install
Node.js 24+, ESM only. Runtime dependencies are yaml and zod and nothing
else; the four vendor SDKs are optional peer dependencies, not loaded until
a client for that vendor is first built, so an OpenAI-only application never
pays for the others.
npm i @zenera/neo openai
npm i @zenera/neo @anthropic-ai/sdk
npm i @zenera/neo @google/genai
npm i @zenera/neo @openrouter/sdkUse
There are two ways in, and they meet in the middle: declare the agents in code, or load them from a folder.
In code
An agent is an instruction, a model, some tools, and who it may hand work to. That is the whole list - there is no orchestration layer underneath.
import { AgentRunner, createModel, tool } from '@zenera/neo';
const getWeather = tool<{ city: string }>({
name: 'get_weather',
description: 'Current weather for a city.',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
additionalProperties: false,
},
execute: ({ city }) => ({ city, tempC: 21, sky: 'clear' }),
});
const runner = new AgentRunner({ model: createModel('openai:gpt-5.4-mini') });
const planner = runner.agent({
name: 'planner',
description: 'Turns a request into a concrete day-by-day plan.',
instructions: 'Answer with a plan. Check the weather before you commit to a day.',
tools: [getWeather],
});
const result = await runner.run(planner, 'Two days in Rome, outdoors where possible.');
console.log(result.output);run() returns a handle: await it for the answer, or iterate it for the
stream - token deltas plus a checkpoint for every model call, tool call,
handoff, fork and join.
for await (const ev of runner.run(planner, 'Two days in Rome.')) {
if (ev.type === 'text_delta') process.stdout.write(ev.delta);
}A second agent and a handoff is one more runner.agent({ … }) plus
handoffs: [planner] on the first. Skills, memory scopes and payload stores are
declared once on the runner and referenced by id from each agent allowed to use
them.
From a folder
An agentic project is a folder - prompts, agent wiring, skills and tool selections are Markdown and YAML files, not code buried inside an application. Load one and run it:
import { loadProject } from '@zenera/neo';
const project = await loadProject('./my-project', { tools: [lookupPolicy] });
for await (const ev of project.run('Water damage, policy NM-448127.')) {
// stream events: thinking, text, tool calls, handoffs, usage
}The whole system is an agents.yaml:
default: intake
model: anthropic:claude-sonnet-4-5
skills: agents/skills
agents:
- name: intake
description: Takes the first message and routes the case.
system: agents/prompts/intake.md
tools: [policy_lookup]
handoffs: [adjuster]
- name: adjuster
description: Weighs the written policy against the case and explains the outcome.
system: agents/prompts/adjuster.md
model: openai:gpt-5.4
tools: [files:*, sandbox:*]
skills:
discovery: index # the model loads what the case needs
preload: [house_style] # always on, from turn oneIt is validated strictly at load: an unknown tool, a handoff to nobody, a missing prompt file - each fails immediately, naming the offending key, instead of surfacing three turns into a run as a confused model.
What it gives you
- Agents - an instruction, a model, tools, skills, who it may hand the work to, and how it splits into parallel branches. There is no hidden orchestration layer.
- Models - OpenAI, Anthropic, Google/Vertex and OpenRouter behind one interface, each through its own SDK, plus any OpenAI-compatible endpoint. Named providers and model aliases resolve from config or from the host.
- Tools - plain typed functions, selectable per agent by name, group or wildcard. Workspace file tools and a Podman sandbox ship with the library.
- Skills - instruction bundles discovered and loaded on demand instead of permanently occupying the prompt, optionally owning their own tools.
- Memory - a timed, masked knowledge graph that outlives a run: recall
returns a stitched subgraph rather than a ranked list, remembered files are
mounted at
/memory, and standing preferences go into the system prompt. - Trajectory - an append-only log of everything that happened. What is sent to a provider is derived from it, and compaction covers nodes rather than deleting them, so the full record survives even when older turns have to be pushed out of the context window.
- Inspection -
renderReportHtmlturns any run into one self-contained HTML file: the graph, every request, every token.
Entry points
| Specifier | Contents |
| ----------------------------- | ----------------------------------------- |
| @zenera/neo | agents, models, tools, kernel, inspection |
| @zenera/neo/project | loading a project folder |
| @zenera/neo/skill-providers | skill discovery and loading |
| @zenera/neo/payload-stores | payload persistence |
Documentation
docs/projects.md ·
docs/agents-yaml.md
· and
examples/sdk/ for
eight worked demos, from a single agent to a project loaded entirely from a
folder.
The rest of the family
| Package | What it is |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| @zenera/cli | zen - this runtime on the command line, with a TUI |
| @zenera/faker | zen faker - a mock API from an openapi/swagger document |
| @zenera/rag | zen rag - an API description as a searchable graph, + tools |
@zenera/rag also exports tools you can hand straight to loadProject, so an
agent can search an API it has never read.
License
Early days and moving fast - issues, questions and pull requests are welcome. MIT.
