The own-the-loop 5-phase cognition runtime: synchronous fakes-first core plus an additive async real path, behind swappable
CognitionFace/AsyncCognitionFacecontracts.
Status: M2 · part of the Vollko cube platform.
Language: TypeScript (Bun/Node) · deps: cube-spine only.
The loop is backend-agnostic. The synchronous LoopEngine + FakeBackend is the
fakes-first CI contract — it runs offline, deterministically, with no API keys. The
async path (AsyncLoopEngine + AsyncModelBackend) is additive: it mirrors the same
5-phase shape (Review → Hypothesis → Investigate → Verify → Synthesize) but awaits a
real LLM over HTTP. The sync core is never patched — the async engine is a separate
class that delegates to an AsyncModelBackend. The real model backend lives in the
sibling cube-runtime-anthropic
package; this package defines only the interfaces.
Budget caps and pattern-based loop detection are enforced in code; each phase emits a
cognition.* envelope on the cube-spine bus. Multimodal inputs (GenerateArgs.images)
are part of the contract; text-only backends silently ignore the field.
Dev: bun install, bun test, bun run typecheck (tsc --noEmit).
This package depends on its sibling cube-spine
via a file:../cube-spine path dependency. Clone it alongside this repo (same parent
directory) before bun install — a standalone clone will not install or build.
import { Bus } from "cube-spine";
import { LoopEngine, FakeBackend, ToolRegistry } from "cube-runtime";
const bus = new Bus();
bus.subscribe("*", (e) => console.log(e.topic));
const tools = new ToolRegistry().register("lookup", (args) => ({ found: args.id }));
const engine = new LoopEngine({
backend: new FakeBackend({ synthesize: { text: "final answer" } }),
bus,
who: "did:web:agent",
tools,
});
const result = engine.run({ task: "summarize the incident" });
console.log(result.output); // "final answer"
console.log(result.phases); // ["review", "hypothesis", "investigate", "verify", "synthesize"]A real LLM call is async — HTTP cannot resolve synchronously. AsyncLoopEngine is the
additive path that mirrors the same 5-phase loop but awaits an AsyncModelBackend. The
sync LoopEngine remains the fakes-first contract that the platform's tests rest on; this
is purely additive.
import { Bus } from "cube-spine";
import { AsyncLoopEngine, FakeAsyncBackend } from "cube-runtime";
// In production swap FakeAsyncBackend for the cube-runtime-anthropic adapter.
const engine = new AsyncLoopEngine({
backend: new FakeAsyncBackend({ synthesize: { text: "done" } }),
bus: new Bus(),
who: "did:web:agent",
});
const result = await engine.run({ task: "analyze the outage" });
console.log(result.output); // "done"Pass base64-encoded images via GenerateArgs.images. Text-only backends ignore the field.
import { FakeBackend, LoopEngine } from "cube-runtime";
import type { ImageContent } from "cube-runtime";
const image: ImageContent = {
mediaType: "image/png",
dataBase64: "<base64-bytes>",
};
// Images are forwarded through GenerateArgs inside the engine;
// a multimodal backend reads them, a text-only backend skips them.Everything is exported from the package root (cube-runtime).
LoopEngine,PHASES, typeLoopEngineArgs— the own-the-loop 5-phase engine (run(args): CognitionResult).BudgetMeter— hard step/token caps enforced in code; halts the loop when exhausted.LoopGuard— pattern-based loop detection; halts when the same tool-call signature repeats.ProgressJournal— rolling structured context fed into each phase; not a full transcript replay.
AsyncLoopEngine, typeAsyncLoopEngineArgs— mirrorsLoopEnginebut awaits anAsyncModelBackend; implementsAsyncCognitionFace.FakeAsyncBackend, typeFakeScript— deterministicAsyncModelBackendfor tests/offline use of the async path.NoOpAsyncExecutor— default async executor (no durability); implementsAsyncDurableExecutor.
NoOpExecutor— default sync executor (no durability); implementsDurableExecutor.NoOpAsyncExecutor— default async executor (no durability); implementsAsyncDurableExecutor.- type
CognitionFace— sync contract:run(args: RunArgs): CognitionResult. - type
AsyncCognitionFace— async contract:run(args: RunArgs): Promise<CognitionResult>. - type
ModelBackend— sync:generate(args: GenerateArgs): ModelResponse. - type
AsyncModelBackend— async:generate(args: GenerateArgs): Promise<ModelResponse>. The real implementation lives incube-runtime-anthropic. - type
DurableExecutor— sync:run<T>(name, fn): T. - type
AsyncDurableExecutor— async:run<T>(name, fn): Promise<T>. - type
GenerateArgs—{ phase, prompt, context, reasoningEffort?, images? }. - type
ImageContent—{ mediaType: string, dataBase64: string }— base64-encoded image for multimodal backends; text-only backends ignore it. - types
ModelResponse,ToolCall,CognitionResult,RunArgs.
FakeBackend, typeFakeScript— deterministic sync backend for tests/offline runs.FakeAsyncBackend, typeFakeScript— deterministic async backend (CI contract for the async path).FileExecutor— durable sync executor: memoizes each step to a JSON file so a crashed run can resume.
ToolRegistry, typesTool,ToolOutcome— the registry the loop calls in the Investigate phase.registerWithRisk,manifestFor,listManifests, typesRiskGate,RiskLevel,ToolManifest— risk-aware tool registration; high-risk tools can be gated at call time.
schedule, typesExecutionStep,WorkItem— order work by mutation safety: mutating items run serially, read-only items are batched in parallel; causal order is preserved.
ModelRouter, typesAgentRole,ModelCatalogEntry— pure role →ModelBackendrouter.ModelCatalog,describeBackend,enforceGateway,expandModelConfig, typesCustomModelConfig,GatewayPolicy— bring-your-own-key config (env-expanded, never logged), an org gateway allowlist, and a cost-aware model catalog.
planFromOutput,readOnlyTools,requireApproval, typesApprovalGate,Plan,ToolPredicate— read-only analysis emits a structured plan; a single approval gate sits between the plan and any write.
SessionStore, typesCreateOpts,SessionFilter,SessionRecord— continue, fork, and tag a unit of work, with JSON persistence and an injectable clock.
Part of the Vollko cube platform — a polyrepo of composable, zero-dependency building blocks for AI-native organizations. See reference-constellation for the whole stack running end-to-end; packages depend on each other via file:../, so clone the siblings alongside this one.
Apache-2.0 © 2026 Vlad Bordei bordeivlad@gmail.com · https://github.com/p-vbordei