Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

darwin-langgraph

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

darwin-langgraph

LangGraph.js adapter for darwin-agents — wrap self-evolving Darwin agents as StateGraph nodes with zero hard deps.

latest
Source
npmnpm
Version
0.1.0-alpha.1
Version published
Maintainers
1
Created
Source

darwin-langgraph

LangGraph.js adapter for darwin-agents. Wrap self-evolving Darwin agents as StateGraph nodes — with zero hard deps.

npm version license: MIT

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.

Install

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.

Quick start

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);

Three surfaces

1. createDarwinNode(agent, opts?)

Wraps a Darwin AgentDefinition as a LangGraph node action. The returned function:

  • Reads the task from state[opts.taskKey ?? "task"].
  • Calls runAgent(agent, task, opts.runOptions) from darwin-agents.
  • Writes the agent's output to state[opts.outputKey ?? "output"].
  • Optionally writes the captured ExecutionTrace to state[opts.trajectoryKey ?? "darwinTrajectory"].
OptionTypeDefaultDescription
taskKeystring"task"State property to read the task from.
outputKeystring"output"State property the output is written to.
trajectoryKeystring"darwinTrajectory"State property the trace is written to.
runOptionsobject{}Passthrough for model, maxTurns, timeout, cwd, autonomous, config.
captureTracebooleantrueSet false to skip trajectory propagation.
onResultfunctionFire-and-forget side-effect after each run. Errors are swallowed.

2. 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.

3. 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
  },
});

Custom errors

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 resulting GraphInterrupt is surfaced through createDarwinNode un-touched so the resume protocol (Command({ resume: ... })) works. Only non-HITL errors get wrapped in DarwinNodeError.

onResult vs onTrajectory — when to pick which

Both 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 hook

withDarwinEvolution wraps both invoke() and stream(). The hook reliably fires when:

  • You call graph.invoke({...}) — always works. Internally streamMode: "values".
  • You call graph.stream({...}) or graph.stream({...}, { streamMode: "values" }) — works.
  • You include "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.

Examples

The examples/ directory ships three runnable scripts:

Compatibility matrix

darwin-langgraphdarwin-agents@langchain/langgraphStatus
0.1.0-alpha.x^0.5.0-alpha.1^1.3.0alpha (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.

Known limitations (will be addressed in v0.2)

  • 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).
  • No gen_ai.* OTEL attribute helper — V0.2 will ship toOtelAttributes(trajectory) so traces forward to Langfuse / Braintrust / Datadog with OpenTelemetry GenAI Semantic Conventions.
  • No bundled 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.

Subscription / API key handling

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

Design notes

  • Zero runtime deps. Both 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.
  • Hook errors never propagate. Both 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.
  • No memory provider construction. Memory backends (Postgres, SQLite) are resolved by darwin-agents itself — set DARWIN_POSTGRES_URL / DARWIN_SQLITE_PATH env vars or pass runOptions.config.dataDir.

Versioning

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.

License

MIT © 2026 StudioMeyer

Keywords

langgraph

FAQs

Package last updated on 24 May 2026

Did you know?

Socket

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.

Install

Related posts