ultra-agents
v0.1.1
Published
Ultra Autonomous Agents — a self-organizing, emergent multi-agent system that replaces LangGraph's manual graph flow with blackboard architecture, dynamic semantic routing, episodic + procedural memory, metacognition, token budgeting, adversarial resilien
Maintainers
Readme
ultra-agents
A self-organizing multi-agent framework that replaces LangGraph's hand-coded graph flow with blackboard architecture, dynamic semantic routing, episodic + procedural memory, metacognition, token budgeting, adversarial resilience, and asymmetric autonomy.
Why?
LangGraph models agent workflows as manually-defined graphs. The human has to predict every possible path, wire every edge, and hope nothing surprising happens. That doesn't scale to truly autonomous behavior.
This package implements the Ultra Autonomous Agents manifesto:
- Blackboard architecture — agents publish/subscribe to a shared workspace instead of being wired with static edges.
- Dynamic semantic routing — tasks are routed by capability distance, not by hand-coded transitions.
- Episodic memory — past executions are retrievable by similarity (RAG over own logs).
- Procedural memory — successful multi-step flows are compiled into new atomic tools.
- Context compression — when the workspace grows too large, the LLM summarizes it.
- Metacognition engine — an independent observer that can
continue,pause,kill,reset,downgrade_model, orescalate_human. - Formal verification — every task result is validated against its declared JSON Schema.
- Contract-driven agents — agents sign tamper-evident contracts with explicit constraints (forbidden domains, max tokens, trust level).
- Epistemic isolation sandbox — the agent that executes tools is never the same one that interprets results.
- Token budgeting — per-task + per-step caps prevent runaway spending.
- Cost-aware LLM routing — trivial tasks go to a cheap 8B model; only out-of-distribution tasks escalate to the premium model.
- Asymmetric autonomy — confidence threshold dynamically adjusts; the system pauses for human approval when it's out-of-distribution.
Installation
npm install ultra-agentsOr from source:
git clone https://github.com/andreslpxz/ultra-agents.git
cd ultra-agents
npm install
npm run build # produces dist/Quick start
import {
Orchestrator,
Agent,
AgentProfileBuilder,
MockLLMProvider,
type Task,
type TaskResult,
} from 'ultra-agents';
// 1. Define an agent.
class EchoAgent extends Agent {
constructor() {
super(
new AgentProfileBuilder('echo', 'Echo', 'Echo back the task description')
.withCapability({
description: 'Echo back the task description',
tags: ['echo'],
})
.build(),
);
}
async handle(task: Task): Promise<TaskResult> {
return {
taskId: task.id,
agentId: this.id,
success: true,
output: { echoed: task.description },
tokensUsed: 10,
costUsd: 0,
durationMs: 0,
trustLevel: 'trusted',
};
}
}
// 2. Wire up the orchestrator.
const orchestrator = new Orchestrator({
llm: new MockLLMProvider({ tier: 'premium' }),
cheapLlm: new MockLLMProvider({ tier: 'cheap' }),
defaultBudget: 10_000,
maxIterations: 10,
proceduralMemoryEnabled: true,
});
orchestrator.registerAgent(new EchoAgent());
// 3. Run a task.
const result = await orchestrator.run('echo back the task description');
console.log(result.result?.output);
// → { echoed: 'echo back the task description' }See examples/basic-usage.ts for a full multi-agent pipeline.
Architecture
┌─────────────────────────────────────────┐
│ Orchestrator │
│ ┌──────────────────────────────────┐ │
task ──────────► │ │ Blackboard (shared workspace) │ │
│ │ ┌────────┐ ┌────────┐ ┌──────┐ │ │
│ │ │ task │ │ obs │ │ res │ │ │
│ │ └────────┘ └────────┘ └──────┘ │ │
│ └──────────┬───────────────────────┘ │
│ │ │
│ ┌─────────┼───────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Router Memory Safety │
│ (sem.) ├ Episodic ├ Metacog │
│ ├ Semantic ├ Verifier │
│ ├ Procedural ├ Contracts │
│ └ Compressor └ Sandbox │
│ │
│ Economics Autonomy │
│ ├ CostModel ├ Confidence │
│ └ BudgetTracker └ Human-in-Loop │
└─────────────────────────────────────────────┘Subsystem map
| Subsystem | File | Responsibility |
|-----------|------|----------------|
| Blackboard | src/core/blackboard.ts | Shared append-mostly workspace with pub/sub |
| Agent | src/core/agent.ts | Base class; agents implement handle() |
| Orchestrator | src/core/orchestrator.ts | Main loop; ties everything together |
| SemanticRouter | src/routing/semantic-router.ts | Capability-distance routing |
| EpisodicMemory | src/memory/episodic.ts | RAG over past executions |
| SemanticMemory | src/memory/episodic.ts | General facts the system learned |
| ProceduralMemory | src/memory/procedural.ts | Auto-tool fabrication from successful flows |
| ContextCompressor | src/memory/context-compressor.ts | LLM-summarizes old workspace entries |
| MetacognitionEngine | src/safety/metacognition.ts | Independent observer; can kill/reset/downgrade |
| FormalVerifier | src/safety/verifier.ts | JSON Schema + contract audit-trail validation |
| ContractManager | src/safety/contract.ts | Signed constraints between agents |
| Sandbox | src/safety/sandbox.ts | Epistemic isolation for untrusted data |
| CostModel | src/economics/cost-model.ts | Picks cheap/balanced/premium per task |
| BudgetTracker | src/economics/cost-model.ts | Per-task + per-step token caps |
| AutonomyManager | src/autonomy/confidence.ts | Dynamic confidence threshold |
| MockLLMProvider | src/llm/mock.ts | Deterministic provider for tests |
Key concepts
Blackboard architecture
Instead of A → B → C edges, agents publish entries to a shared workspace. Any agent can subscribe and react. There are no static dependencies — agents can be added, removed, or replaced at runtime.
blackboard.append({
taskId: 't1',
kind: 'observation',
severity: 'info',
content: { saw: 'something interesting' },
trustLevel: 'trusted',
});
blackboard.subscribe((entry) => {
console.log('new entry:', entry.kind);
});Dynamic semantic routing
The router embeds the task description and each agent's capability description, then picks the agent with the shortest distance. Hard filters (capability tags) are applied first; ties are broken by estimated cost.
const decision = await router.route(task, llm);
// → { agentId: 'researcher', distance: 0.12, confidence: 0.88, ... }If no agent is close enough (distance > maxDistance), the router returns null and the orchestrator either decomposes the task or escalates to a human.
Episodic + procedural memory
Every completed task is recorded as an episode with its outcome, lesson, and tool-call sequence. The orchestrator retrieves similar past episodes via similarity search and injects them as few-shot examples.
When an episode is successful and used ≥2 tools, the procedural memory fabricates a new atomic tool that wraps the entire chain. Future invocations skip reasoning and just call the fabricated tool.
// After a successful multi-step task:
const procedures = await proceduralMemory.scanAndFabricate(episodicMemory, llm);
// procedures[0] === { id: 'proc_xxx', recipe: [step1, step2, ...], status: 'experimental' }After 3 successful reuses, the procedure is promoted to stable. After 2 failures, it's deprecated.
Metacognition
An independent observer watches every workspace entry. It can decide:
continue— healthypause— no activity for 30skill— budget exhausted, max iterations reached, error spike, deadline missedreset— error spike + high kill rate (workspace is corrupted)downgrade_model— at 75% budget, switch to cheaper LLMescalate_human— confidence too low
const metrics = metacognition.computeMetrics(budget, depth);
const decision = metacognition.decide(metrics, task);
if (decision.action === 'kill') {
// ... terminate the task ...
}Contracts
Before agent B executes a task for agent A, both sign a contract:
const contract = contractManager.sign('orchestrator', 'researcher', 'task_42', {
forbiddenDomains: ['evil.com'],
forbiddenTools: ['dangerous_tool'],
maxTokens: 1000,
minTrustLevel: 'verified',
});
// Agent B executes, recording each step in the audit trail:
contractManager.recordStep(contract.id, {
taskId: 'task_42',
agentId: 'researcher',
kind: 'tool_call',
severity: 'info',
content: { toolId: 'safe_search', input: {}, output: 'ok' },
trustLevel: 'trusted',
});
// Verifier replays the audit trail:
const finalized = contractManager.finalize(contract.id);
const result = verifier.verifyContract(finalized!);
// → { ok: true, errors: [] }Sandbox
Agents that interact with untrusted sources run in a sandbox. The sandbox:
- Wraps tools so their outputs are tagged
untrusteduntil a critic reviews them - Restricts network and filesystem access to a whitelist
- Appends every tool call to the workspace with
trustLevel: 'untrusted'
const sandbox = orchestrator.createSandbox({
networkAllowed: false,
fsWriteAllowed: false,
allowedDomains: ['trusted.com'],
});
const wrapped = sandbox.wrapTool(webSearchTool);
// wrapped.id === 'sandboxed:sandbox_xxx:web_search'Cost-aware LLM routing
The CostModel picks the cheapest LLM that can handle a task:
- Trivial tasks (short prompt, few capabilities, low distance) →
cheaptier (e.g. 8B model) - Complex tasks →
balancedtier - Out-of-distribution tasks (distance > 0.5) →
premiumtier - Budget pressure (≥75%) → downgrade regardless
Asymmetric autonomy
The AutonomyManager computes a confidence score from routing distance, episodic success rate, and schema strictness. If confidence is below the escalation threshold, the system pauses and calls the onHumanApproval callback:
const result = await orchestrator.run('risky task', {
onHumanApproval: async (assessment, task) => {
console.log(autonomy.explainPause(assessment, task));
const userChoice = await askUser(); // 'approve' | 'reject' | 'modify'
return userChoice;
},
});Autonomy levels, in increasing order:
| Level | Behavior |
|-------|----------|
| manual | Every step requires human approval |
| supervised | System runs, pauses at checkpoints |
| auto | Full autonomy, escalates on low confidence |
| auto_aggressive | Never escalates; humans see only post-hoc logs |
API reference
See src/index.ts for the full list of exports. The most important ones:
Orchestrator— main entry pointAgent,AgentProfileBuilder— define agentsBlackboard— workspaceSemanticRouter— routingEpisodicMemory,ProceduralMemory,ContextCompressor— memoryMetacognitionEngine,FormalVerifier,ContractManager,Sandbox— safetyCostModel,BudgetTracker— economicsAutonomyManager— autonomyMockLLMProvider— deterministic LLM for tests
Testing
npm testThe test suite uses Node's built-in test runner (node:test) and covers:
- Blackboard pub/sub, filtering, compression
- Formal verifier (JSON Schema, contracts)
- Contract manager (signing, audit trail, tamper detection)
- Metacognition engine (budget, iterations, loops, deadlines)
- Episodic + procedural memory (recording, retrieval, fabrication, promotion/deprecation)
- Context compressor (threshold, fallback to structural summary)
- Semantic router (capability matching, fallback, top-k)
- Cost model (tier selection, budget pressure, overrides)
- Autonomy manager (escalation, capping, manual mode)
- Budget tracker (reservation, refund, consumption)
- Orchestrator integration (end-to-end runs, episode recording, sandbox)
Current status: 68/68 tests passing.
Plugging in a real LLM
The MockLLMProvider is deterministic but not smart. To use a real LLM, implement the LLMProvider interface:
import type { LLMProvider, LLMResponse, LLMCallOptions } from 'ultra-agents';
class OpenAIProvider implements LLMProvider {
readonly id = 'gpt-4o';
readonly tier = 'premium';
readonly costPer1kInputTokens = 0.005;
readonly costPer1kOutputTokens = 0.015;
readonly avgLatencyMs = 800;
readonly contextWindow = 128_000;
async complete(prompt: string, opts?: LLMCallOptions): Promise<LLMResponse> {
// ... call OpenAI ...
}
async embed(text: string): Promise<number[]> {
// ... call OpenAI embeddings ...
}
}
const orchestrator = new Orchestrator({
llm: new OpenAIProvider(),
// ...
});License
MIT
