AgentPrism Workflows
Run dynamic, multi-agent workflow scripts — agent(), parallel(), pipeline() — over real coding agents (Claude Code and OpenAI Codex), with deterministic journaling, resume, token budgets, and git-worktree isolation.
README
AgentPrism Workflows
Run dynamic, multi-agent workflow scripts — agent(), parallel(), pipeline() — over real coding agents (Claude Code and OpenAI Codex), with deterministic journaling, resume, token budgets, and git-worktree isolation.
You author a small JavaScript script (export const meta, then call agent() / parallel() / pipeline()); the engine runs it in a sandboxed realm, fanning each agent() call out to an Agent Client Protocol (ACP) backend. It's available two ways:
- As a TypeScript SDK —
@automatalabs/workflows— embed the runner in your own program. - As a stdio MCP server —
@automatalabs/mcp-server, built on the SDK — expose aworkflowtool to any MCP host (Claude Code, Zed, …).
The
@automatalabs/*packages are published on npm — see Install. Two are user-facing: the@automatalabs/workflowsSDK and the@automatalabs/mcp-serverstdio server.
How it works
One process plays two protocol roles at once: it's an MCP server (or a library) that accepts a workflow script, and an ACP client that drives one or more agent subprocesses to execute each agent() call.
your program ──or── MCP host (Claude Code / Zed / …)
│ runDynamicWorkflow(script) calls tool "workflow"
▼
┌──────────────────────────────────────────────┐
│ AgentPrism orchestrator │
│ • the deterministic engine runs the script │
│ • ACP CLIENT → drives agent servers │
└──────────────────────────────────────────────┘
│ session/new, session/prompt … (ACP, JSON-RPC over stdio)
▼
claude-agent-acp / codex-acp (long-lived, pooled subprocesses)
│ → real Claude / Codex agents, one session per agent() call
The deterministic engine (sandboxed vm realm, parallel/pipeline, journal/resume, token budget, worktree isolation) is independent of how a single agent runs and of how the tool is exposed. See docs/design-notes.md for the full protocol-level design.
Requirements
- Node.js ≥ 22 and pnpm ≥ 10 (see
.nvmrc/packageManager). - A backend agent CLI, authenticated on your machine:
- Claude — via the bundled
@agentclientprotocol/claude-agent-acp; auth from~/.claude/.credentials.jsonorANTHROPIC_API_KEY(the orchestrator inherits your environment). - Codex — via
@automatalabs/codex-acp(+ the@openai/codexbinary, installed as a dependency); auth from~/.codex/auth.json.
- Claude — via the bundled
You only need auth for the backend(s) you actually call.
Install
From npm
pnpm add @automatalabs/workflows # the SDK
# or, to run the MCP server:
pnpm add @automatalabs/mcp-server
From source (for development)
git clone <this-repo> agentprism-workflows
cd agentprism-workflows
pnpm install # installs deps + fetches backend binaries
pnpm build # tsc -b across all packages
Packages
Two packages are user-facing — start with one of these:
| Package | What it is |
|---|---|
@automatalabs/workflows |
The canonical public SDK — a thin facade that runs workflow scripts programmatically over the default ACP backend, and re-exports the engine + backend surface. Start here. |
@automatalabs/mcp-server |
The stdio MCP server (bin: agentprism-workflow) exposing the workflow tool — built on @automatalabs/workflows. |
The other three are internal building blocks, composed by the SDK. You don't depend on them directly: @automatalabs/workflows is the public entry point for everything they export.
| Package | What it is |
|---|---|
@automatalabs/acp-agents |
The ACP client + Claude/Codex backends (the AgentRunner implementation, connection pooling, structured output, permissions, usage). Internal — public entry is @automatalabs/workflows. |
@automatalabs/workflow-engine |
The deterministic engine: the script realm, parallel/pipeline, journal/resume, budgets, worktree isolation. Internal — public entry is @automatalabs/workflows. |
@automatalabs/shared-types |
The AgentRunner seam + shared types the others compose against. Internal — public entry is @automatalabs/workflows. |
Dependency direction: mcp-server → workflows → { workflow-engine, acp-agents, shared-types }. The SDK (workflows) is the single facade that composes the deterministic engine and the ACP backend, which meet only at the AgentRunner seam in shared-types. The engine never names a backend; the agents never know they're inside a workflow.
Quickstart — SDK
Run a workflow script. The default backend is the ACP runner (createAcpRunner()), so this drives real agents and needs backend auth.
import { runDynamicWorkflow } from "@automatalabs/workflows";
const script = `
export const meta = {
name: "repo-scan",
description: "describe a repo as JSON, three ways in parallel",
phases: [{ title: "Fan" }],
};
const SCHEMA = {
type: "object",
additionalProperties: false,
required: ["repo", "fileCount"],
properties: { repo: { type: "string" }, fileCount: { type: "number" } },
};
phase("Fan");
const results = await parallel([
() => agent("Report this repo as JSON {repo, fileCount}.", { label: "a1", schema: SCHEMA }),
() => agent("Report this repo as JSON {repo, fileCount}.", { label: "a2", schema: SCHEMA }),
]);
return results;
`;
const run = await runDynamicWorkflow(script, { args: {} });
console.log(run.status); // "completed" | "paused" | "failed" | "aborted"
console.log(run.result); // [{ repo: "...", fileCount: 123 }, …] — schema-validated objects
console.log(run.tokenUsage, run.runId);
runDynamicWorkflow resolves to a terminal WorkflowRunResult even on pause/fail/abort — read run.status instead of catching. To swap the backend (or stub it in tests), pass your own runner: runDynamicWorkflow(script, { runner }). For lower-level control, use WorkflowManager / runWorkflow (also re-exported from the SDK).
Run a single agent directly
import { createAcpRunner } from "@automatalabs/workflows";
const runner = createAcpRunner();
const data = await runner.run("Summarize this repo as JSON {summary}.", {
schema: {
type: "object", additionalProperties: false,
required: ["summary"], properties: { summary: { type: "string" } },
},
model: "opus", // routes to Claude; e.g. "gpt-5-codex" routes to Codex
cwd: process.cwd(),
});
// data is typed/validated against the schema (a plain object, not text)
await runner.dispose(); // closes pooled backend processes
Quickstart — MCP server
The workflow tool runs a script to completion synchronously, streaming notifications/progress, and returns the structured result.
Register the stdio server in your MCP host's config:
{
"mcpServers": {
"agentprism-workflow": {
"command": "agentprism-workflow",
"env": { "AGENTPRISM_DEFAULT_BACKEND": "claude" }
}
}
}
From a source checkout, point at the built entry instead:
{
"mcpServers": {
"agentprism-workflow": {
"command": "node",
"args": ["/abs/path/to/agentprism-workflows/packages/mcp-server/dist/index.js"]
}
}
}
Tool: workflow — input parameters:
| Param | Type | Notes |
|---|---|---|
script |
string (required) | Raw JS; first statement must be export const meta = { name, description, phases? }; must call agent() at least once. |
args |
any | Exposed to the script as the global args. |
maxAgents |
number | Default 1000. |
concurrency |
number | Clamped to 16 (not rejected). |
agentRetries |
number | Clamped to 3. |
agentTimeoutMs |
number | null | Per-agent timeout; omit for none. |
tokenBudget |
number | null | Hard total-token cap for the run; omit for none. |
resumeFromRunId |
string | Resume a prior run from its persisted journal (resume is explicit). |
The run is synchronous (one tools/call = one full run). Resume after a pause/crash by calling workflow again with resumeFromRunId.
Writing workflow scripts
A script is plain JavaScript whose first statement is the meta literal. Inside it, these globals are available (injected into the run's realm — they are not importable functions; @automatalabs/workflows ships an ambient .d.ts so your editor knows them):
agent(prompt, opts?)— run one subagent. Withopts.schema(a JSON Schema) you get a validated object back; without it, the assistant's text. Other opts:label,phase,model/tier,agentType,isolation,timeoutMs,retries,mcpServers. (Working directory comes from worktreeisolation; tool policy and instructions come from theagentTypedefinition.cwd/toolNames/instructionsare options on the lower-levelcreateAcpRunner().run()API, not script-levelagent()opts.)parallel([fn, …])— run thunks concurrently; barrier (awaits all).pipeline(items, stage1, stage2, …)— stream each item through stages independently (no inter-stage barrier).phase(title),log(msg)— progress grouping + narration.budget— the run's token budget (budget.total,budget.remaining(),budget.spent()).checkpoint(),gate(),verify(),judgePanel(),loopUntilDry(),completenessCheck(),retry(),workflow(),args.
Determinism is enforced (Date.now/Math.random/new Date() are neutered in the realm) so a killed run resumes from its journal with a cache-hit on the unchanged prefix.
Structured output
Pass a JSON Schema as agent({ schema }) and the result is a validated object, not text. Each backend constrains generation natively (Claude via its output-format channel; Codex via a turn-level outputSchema), then the runner validates and re-prompts on mismatch. See docs/design-notes.md §6 for the per-backend mechanics.
Backends & selection
The backend is chosen per agent() call from the model/tier you pass, by provider prefix:
opus,sonnet,haiku,claude…,anthropic/…→ Claude (claude-agent-acp).gpt…,codex…,o3/o4,openai/…→ Codex (codex-acp).- Otherwise the default backend (
AGENTPRISM_DEFAULT_BACKEND, defaultclaude).
One long-lived ACP process per backend is pooled and reused across agent() calls (one spawn + one initialize), with a fresh session per call — so worktree isolation is preserved via each session's cwd.
Configuration
| Env var | Default | Meaning |
|---|---|---|
AGENTPRISM_DEFAULT_BACKEND |
claude |
Backend when the model/tier doesn't imply one (claude | codex). |
AGENTPRISM_ACP_POOL_SIZE |
1 |
Long-lived processes held per backend. |
AGENTPRISM_CLAUDE_ACP_CMD / …_ARGS |
(bundled) | Override the Claude ACP server command/args. |
AGENTPRISM_CODEX_ACP_CMD / …_ARGS / …_BIN |
(bundled) | Override the Codex ACP server command/args/binary. |
Documentation
docs/design-notes.md— the deep protocol-level design: ACP lifecycle, the structured-output crux, model/permission/usage/cancellation mechanics, and the engine lineage.CONTRIBUTING.md— local development, testing (including the gated live-backend e2e), and releasing.- Agent Client Protocol · Model Context Protocol
License
Apache-2.0 — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。