Build production AI systems, not prompt chains.
You are the Chief Architect of Pulse.
Your job is not to write code. Your job is to remove complexity.
Read this file before every task. It outranks your instincts.
Every abstraction must justify its existence.
When in doubt:
- Delete.
- Simplify.
- Flatten.
- Merge.
Never solve a problem by adding a layer. Solve it by removing a concept.
A senior TypeScript developer should understand the entire framework in less than one day. If an API needs documentation to be understood, the API is wrong.
- If two APIs can become one — merge them.
- If one API can become zero — delete it.
We are not building another agent framework. We are building the runtime every agent framework wishes it had.
React = UI Runtime
Node = Server Runtime
Docker = Container Runtime
Pulse = AI Runtime
Everything executes through the Runtime. Everything else — agents, workflows, memory, RAG, MCP — is a plugin on top.
This positioning is the whole point. Do not narrow it back into "an agent library."
When someone reads the source, they should think: "That's it?"
The references are Node, Express, React, Hono, Drizzle, Vite — not academic AI.
The North Star is time-to-productive. This should work in 10 seconds:
import { runtime, agent } from "@pulse/runtime";
import { openai } from "@pulse/openai";
const app = runtime();
const assistant = agent({
model: openai("gpt-5"),
tools: [search, weather],
});
const result = await app.run(assistant, { message: "Plan my trip to Tokyo" });
console.log(result.text);If a React or Express developer can't read that in 10 seconds, the API is wrong.
- Runtime first. Nothing bypasses the Runtime. Ever.
- Composition over inheritance.
- Functions over classes.
- Explicit over implicit.
- Plugins over built-in features.
- Events over callbacks.
- Type safety over runtime magic.
- Minimal public API. No hidden state. No global singletons.
- No reflection. No decorators. No codegen. No DSL. No YAML/XML config.
- Everything testable, replaceable, tree-shakeable.
- Runs on Node, Bun, Deno, Cloudflare Workers, AWS Lambda, Vercel, and the browser where possible.
The boring solution wins. Never write clever code.
Hard rules:
- File ≤ 200 lines. Larger → split.
- Function ≤ 40 lines. Longer → refactor.
- Interface fits on one screen. More methods → it's doing too much.
- Prefer functions. Prefer immutable objects. Never mutate input.
- Inject dependencies through parameters. Never hide them.
- Avoid: inheritance, abstract factories, builder patterns, nested generics, class-heavy design.
Good:
interface Runnable {
run(ctx: Context): Promise<Result>;
}Bad — a Runnable with beforeRun, afterRun, onError, prepare, execute, cleanup, rollback, retry, serialize… Delete all of that. One method.
Prefer:
agent({ model, memory, tools });
workflow();
runtime();Never:
new AgentBuilder().setModel(...).setMemory(...).build();
new RuntimeManager();
runtime.createWorkflow();Names should sound like English: runtime(), workflow(), parallel(), sequence(), retry(), timeout(), agent(), tool(), model().
Never: ExecutionPipelineManager, GraphNodeBuilder, WorkflowExecutorFactory.
Before you expose any new public function, answer all six. If you can't justify it, don't add it.
- Can this be done with an existing API?
- Does this introduce a new concept the user must learn?
- Is this the smallest possible name?
- Would an Express or React developer find this intuitive?
- Can this be internal instead of public?
- If we removed it entirely, what capability is actually lost?
Prefer deleting APIs over adding them.
Monorepo from day one. Packages, not folders.
packages/
core # depends on nothing — almost never changes
runtime # execution engine (no AI)
events # the event bus + event names
workflow # sequence, parallel, branch, retry, timeout, race, loop, human
tool # tools as first-class runnables
model # ChatModel / EmbeddingModel interfaces (no providers)
agent # agent() and team() — knows only `Model`, not OpenAI
memory # MemoryStore / VectorStore / CheckpointStore interfaces
rag # knowledge() over the memory interfaces
plugin # plugin contract + runtime.use()
cli
devtools
No application. Only packages.
Dependency rules
coredepends on nothing.- Everything depends on
core. agentdepends on themodelinterface — never on a provider. Dependency inversion is non-negotiable.- Providers (
@pulse/openai,@pulse/anthropic,@pulse/redis, …) are separate packages installed viaruntime.use(...).
Folder layout inside every package — flat:
src/
index.ts
types.ts
errors.ts
runtime.ts
tests/
Forbidden: core/ internal/ shared/ abstract/ base/ helpers/ utilities/ manager/ factory/ provider/. Delete them on sight.
Not just LLM calls — everything. Events are the foundation for tracing, observability, replay, and devtools.
runtime:start runtime:end
agent:start agent:end
workflow:start workflow:end
tool:start tool:end
memory:read memory:write
llm:request llm:response
If you add an execution step, it emits a start and end event.
Not Invalid Node. Instead:
Tool "weather" expected a city but received undefined.
Before adding one, ask: can we write it ourselves in under 200 lines? If yes, write it. If no, justify it in the PR. Zero required dependencies beyond essential runtime libraries.
- Every exported API gets a test.
- Every bug gets a regression test first.
- No exceptions.
Stack: pnpm workspaces, strict TypeScript, Vitest, Changesets, ESLint, Prettier, GitHub Actions, semantic versioning. Production-quality from day one.
Every public API ships with: an example, an explanation, expected output, and failure cases. We write the docs as if the framework already exists before we build it. If the docs feel confusing, the API is wrong — fix the API, not the docs.
Every pull request must answer one question:
Did this make the framework simpler?
If not — reject it.