
Research
/Security News
Mini Shai-Hulud Campaign Hits Red Hat Cloud Services npm Packages
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.
darwin-langgraph
Advanced tools
LangGraph.js adapter for darwin-agents — wrap self-evolving Darwin agents as StateGraph nodes with zero hard deps.
LangGraph.js adapter for
darwin-agents. Wrap self-evolving Darwin agents asStateGraphnodes — with zero hard deps.
darwin-langgraph is a thin, peer-dependency-only bridge between two
independent libraries:
darwin-agents — Darwin
is an agent framework where prompts evolve themselves via A/B testing,
multi-model critics, safety gates, and (since v0.5.0-alpha.1) full
execution-trace capture for GEPA-style reflective optimizers.@langchain/langgraph —
LangGraph is the state-graph orchestration runtime used by Uber,
LinkedIn, Klarna, Replit, and others to build multi-step, multi-agent
workflows with durable execution and human-in-the-loop.If you use only Darwin, you install nothing extra — Darwin itself has zero LangChain dependency. If you use only LangGraph, the same. If you want to combine the two — let LangGraph orchestrate, let Darwin evolve — install this adapter.
npm install darwin-langgraph@alpha @langchain/langgraph darwin-agents@alpha
Both @langchain/langgraph and darwin-agents are declared as
peer-dependencies — you control the installed versions, the adapter
never pins them.
import { StateGraph } from "@langchain/langgraph";
import { defineAgent } from "darwin-agents";
import {
createDarwinNode,
darwinAnnotation,
withDarwinEvolution,
} from "darwin-langgraph";
const researcher = defineAgent({
name: "researcher",
role: "Topic Researcher",
description: "Five bullets on a topic.",
systemPrompt: "Return exactly 5 bullet points.",
});
const graph = withDarwinEvolution(
new StateGraph(darwinAnnotation())
.addNode("research", createDarwinNode(researcher))
.addEdge("__start__", "research")
.compile(),
{
nodeMap: { research: "researcher" },
onTrajectory: (event) => {
console.log(
`${event.nodeName} ran in ${event.trajectory.turnCount} turns ` +
`with ${event.trajectory.toolCalls.length} tool calls.`,
);
},
},
);
const result = await graph.invoke({ task: "What is GEPA?" });
console.log(result.output);
console.log("trajectory:", result.darwinTrajectory);
createDarwinNode(agent, opts?)Wraps a Darwin AgentDefinition as a LangGraph node action. The returned
function:
state[opts.taskKey ?? "task"].runAgent(agent, task, opts.runOptions) from darwin-agents.state[opts.outputKey ?? "output"].ExecutionTrace to
state[opts.trajectoryKey ?? "darwinTrajectory"].| Option | Type | Default | Description |
|---|---|---|---|
taskKey | string | "task" | State property to read the task from. |
outputKey | string | "output" | State property the output is written to. |
trajectoryKey | string | "darwinTrajectory" | State property the trace is written to. |
runOptions | object | {} | Passthrough for model, maxTurns, timeout, cwd, autonomous, config. |
captureTrace | boolean | true | Set false to skip trajectory propagation. |
onResult | function | — | Fire-and-forget side-effect after each run. Errors are swallowed. |
darwinAnnotation(extra?)Returns an Annotation.Root with three pre-defined channels
(task / output / darwinTrajectory) plus any extra channels you pass
in. Composable:
const State = darwinAnnotation({
reviewNotes: Annotation<string[]>({
reducer: (a, b) => a.concat(b),
default: () => [],
}),
});
Power-users can spread getDarwinChannelSpec() directly into their own
Annotation.Root({...}) when the helper doesn't fit.
withDarwinEvolution(graph, opts)Wraps a compiled StateGraph with a post-run hook that fires onTrajectory
for each node listed in nodeMap after every invoke / stream run.
Errors inside the hook are swallowed — the graph result is always
returned unchanged.
withDarwinEvolution(graph, {
nodeMap: {
research: "researcher",
critique: { agentName: "critic", trajectoryKey: "critiqueTrace" },
},
onTrajectory: (event) => {
// event.nodeName, event.agentName, event.trajectory, event.finalState
},
});
import { DarwinNodeError, DarwinEvolutionHookError } from "darwin-langgraph";
try { await graph.invoke({ task: "..." }); }
catch (err) {
if (err instanceof DarwinNodeError) {
console.error(`Node "${err.agentName}" failed:`, err.cause);
}
}
GraphInterrupt is re-thrown un-wrapped. When a downstream node calls LangGraph's
interrupt()for human-in-the-loop, the resultingGraphInterruptis surfaced throughcreateDarwinNodeun-touched so the resume protocol (Command({ resume: ... })) works. Only non-HITL errors get wrapped inDarwinNodeError.
onResult vs onTrajectory — when to pick whichBoth hooks fire after a Darwin agent finishes, but at different layers:
onResult (per-node, on createDarwinNode) receives the full
raw RunResult — including experiment, evolution, reportPath,
and the raw output. Use it for per-agent side-effects that don't
care about graph structure (e.g. writing to a database, logging the
full result).onTrajectory (graph-wide, on withDarwinEvolution) receives a
normalised DarwinTrajectoryEvent with nodeName, agentName,
trajectory, and a frozen final-state snapshot. Use it for
cross-node observability that needs to know which LangGraph node
produced which trace (cost tracking, A/B telemetry, evolution loops).If you only need per-agent side-effects, use onResult. If you need
node-aware telemetry, use onTrajectory. They are not redundant —
combining both is fine, just be aware they fire at slightly different
points (node-completion vs graph-completion).
streamMode and the trajectory hookwithDarwinEvolution wraps both invoke() and stream(). The hook
reliably fires when:
graph.invoke({...}) — always works. Internally
streamMode: "values".graph.stream({...}) or graph.stream({...}, { streamMode: "values" }) — works."values" in a multi-mode array
(streamMode: ["values", "updates"]) — works.It does not fire on streamMode: "updates" / "messages" /
"debug" (without "values") — the last chunk in those modes is a
node-update dict, not the final state, so the trajectory key lookup
silently misses. The adapter logs a console.warn (once per process)
the first time it detects a non-values stream so you don't silently
lose telemetry. V0.2 will migrate to the LangChain CallbackHandler
mechanism which fires regardless of stream mode.
The examples/ directory ships three runnable scripts:
01-basic-node.ts — single Darwin node in a 1-edge graph.02-multi-agent-research.ts — researcher → critic → writer pipeline with per-node trajectories.03-hitl-with-darwin.ts — LangGraph interrupt() before a Darwin agent runs, with Command({ resume: ... }) to continue.darwin-langgraph | darwin-agents | @langchain/langgraph | Status |
|---|---|---|---|
0.1.0-alpha.x | ^0.5.0-alpha.1 | ^1.3.0 | alpha (this release) |
The peer-dep range darwin-agents: "^0.5.0-alpha.1" follows npm's
prerelease semver rules — 0.5.0-alpha.N and 0.5.0 final satisfy it,
but 0.5.1-alpha.0 does NOT. A patch release of this adapter will be
required when darwin-agents bumps past 0.5.x.
withDarwinEvolution monkey-patches invoke/stream — V0.2
will migrate to a DarwinCallbackHandler you pass via
graph.invoke(input, { callbacks: [...] }), which is the canonical
LangChain pattern (matches Langfuse, Braintrust, LangSmith handlers).gen_ai.* OTEL attribute helper — V0.2 will ship
toOtelAttributes(trajectory) so traces forward to Langfuse /
Braintrust / Datadog with OpenTelemetry GenAI Semantic Conventions.messages channel — V0.2 will add
darwinMessagesAnnotation() for graphs that mix createReactAgent
with createDarwinNode. Until then, you can pass
darwinAnnotation({ ...MessagesAnnotation.spec }) manually.The adapter releases follow darwin-agents major bumps. When
@langchain/langgraph 2.x lands, the adapter ships a new major within
the same minor of the underlying Darwin release.
The adapter NEVER touches ANTHROPIC_API_KEY. If you run Darwin on a
Claude Max subscription via the Claude Code CLI, your bootstrap must
delete process.env.ANTHROPIC_API_KEY BEFORE invoking the graph —
otherwise Darwin's ClaudeCliProvider will hit the metered API and you
pay per request instead of the flat subscription.
// bootstrap.ts
import "dotenv/config";
delete process.env.ANTHROPIC_API_KEY; // enforce Max-Plan subscription
darwin-agents and @langchain/langgraph
are peer-dependencies. The adapter itself is a couple of hundred lines
of TypeScript that re-exports a handful of types.invoke() calls stream() internally. LangGraph v1.x implements
invoke on top of stream. The adapter tracks in-flight invokes
with a per-call Symbol marker stored in a Set so the trajectory
hook fires exactly once per logical run, even under concurrent
Promise.all([graph.invoke(...), graph.invoke(...)]).GraphInterrupt re-throw. The adapter imports isGraphInterrupt
directly from @langchain/langgraph so LangGraph's HITL exceptions
(both GraphInterrupt and NodeInterrupt) propagate untouched —
Command({ resume }) works as expected.opts.onResult (on
createDarwinNode) and opts.onTrajectory (on withDarwinEvolution)
use a fire-and-forget pattern with one-shot console.warn for the
first swallowed throw per process.darwin-agents itself — set
DARWIN_POSTGRES_URL / DARWIN_SQLITE_PATH env vars or pass
runOptions.config.dataDir.Released under the alpha npm dist-tag in parallel with
darwin-agents@0.5.0-alpha.1. Default npm install darwin-langgraph
will NOT resolve until 0.1.0 final ships. Explicit opt-in via
npm install darwin-langgraph@alpha.
MIT © 2026 StudioMeyer
FAQs
LangGraph.js adapter for darwin-agents — wrap self-evolving Darwin agents as StateGraph nodes with zero hard deps.
We found that darwin-langgraph demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.

Research
/Security News
The North Korean malware loader hides in a Packagist-listed package and its GitHub branch to fetch and execute remote code in a likely Contagious Interview-style lure.

Security News
The Rust project is moving toward formal rules on LLM use in contributions after months of internal debate over maintainer burden, code quality, and contributor experience.