darwin-langgraph 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +119 -0
- package/LICENSE +21 -0
- package/README.md +276 -0
- package/dist/create-darwin-node.d.ts +151 -0
- package/dist/create-darwin-node.d.ts.map +1 -0
- package/dist/create-darwin-node.js +121 -0
- package/dist/create-darwin-node.js.map +1 -0
- package/dist/darwin-annotation.d.ts +80 -0
- package/dist/darwin-annotation.d.ts.map +1 -0
- package/dist/darwin-annotation.js +92 -0
- package/dist/darwin-annotation.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +23 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/dist/with-darwin-evolution.d.ts +92 -0
- package/dist/with-darwin-evolution.d.ts.map +1 -0
- package/dist/with-darwin-evolution.js +256 -0
- package/dist/with-darwin-evolution.js.map +1 -0
- package/package.json +79 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `darwin-langgraph` are documented here.
|
|
4
|
+
The project adheres to [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [0.1.0-alpha.1] — 2026-05-24
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Surface 1: `createDarwinNode(agent, opts?)`** — wraps a Darwin
|
|
11
|
+
`AgentDefinition` as a LangGraph `NodeAction`. Reads the task from state,
|
|
12
|
+
runs the agent via `darwin-agents.runAgent()`, writes `output` and (opt-in)
|
|
13
|
+
the captured `ExecutionTrace` back to state.
|
|
14
|
+
- **Surface 2: `darwinAnnotation(extra?)`** — `Annotation.Root` helper with
|
|
15
|
+
three pre-defined channels (`task`, `output`, `darwinTrajectory`).
|
|
16
|
+
Composable via `extra` for user channels.
|
|
17
|
+
- **Surface 3: `withDarwinEvolution(graph, opts)`** — wraps a compiled
|
|
18
|
+
`StateGraph`'s `invoke` / `stream` methods with a fire-and-forget post-run
|
|
19
|
+
hook that forwards each node's trajectory to a user-supplied callback,
|
|
20
|
+
enabling Darwin's closed-loop A/B evolution without changing graph code.
|
|
21
|
+
- **Custom errors:** `DarwinNodeError`, `DarwinEvolutionHookError` — subclass
|
|
22
|
+
`Error` for `instanceof` checks.
|
|
23
|
+
- **Types:** Re-export of `AgentDefinition`, `ExecutionTrace`, `RunResult`,
|
|
24
|
+
`MemoryProvider`, `TraceToolCall`, `TraceTokenUsage`, `TraceTurnError`
|
|
25
|
+
from `darwin-agents` for ergonomic single-import.
|
|
26
|
+
|
|
27
|
+
### Compatibility
|
|
28
|
+
|
|
29
|
+
| `darwin-langgraph` | `darwin-agents` | `@langchain/langgraph` |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `0.1.0-alpha.x` | `^0.5.0-alpha.1` | `^1.3.0` |
|
|
32
|
+
|
|
33
|
+
Both upstream packages are declared as **peer-dependencies** — the consumer
|
|
34
|
+
controls the installed versions and the adapter never pins them.
|
|
35
|
+
|
|
36
|
+
### Notes
|
|
37
|
+
|
|
38
|
+
- Released under the `alpha` npm dist-tag in parallel with
|
|
39
|
+
`darwin-agents@0.5.0-alpha.1` (the first release that ships
|
|
40
|
+
`ExecutionTrace` capture). Default `npm install darwin-langgraph`
|
|
41
|
+
refuses to resolve until `0.1.0` final ships — explicit opt-in via
|
|
42
|
+
`npm install darwin-langgraph@alpha`.
|
|
43
|
+
- The adapter never touches `ANTHROPIC_API_KEY`. If you run Darwin on a
|
|
44
|
+
Claude Max subscription via the Claude Code CLI, set `delete
|
|
45
|
+
process.env.ANTHROPIC_API_KEY` in your own bootstrap.
|
|
46
|
+
- **Peer-dep semver caveat:** `darwin-agents: "^0.5.0-alpha.1"` follows
|
|
47
|
+
npm's prerelease semver rules — `0.5.0-alpha.N` and `0.5.0` final
|
|
48
|
+
satisfy it, but `0.5.1-alpha.0` does NOT. A patch release of this
|
|
49
|
+
adapter will be required when `darwin-agents` bumps past `0.5.x`.
|
|
50
|
+
|
|
51
|
+
### R1 + R2 Code-Review Loop Pre-Publish
|
|
52
|
+
|
|
53
|
+
The 3-Agent code-review loop (Critic + Analyst + Research, in parallel)
|
|
54
|
+
ran twice before this release. R1 surfaced 8 findings, all fixed
|
|
55
|
+
in-place. R2 surfaced 1 HIGH must-fix + 4 LOW deferrals, the must-fix
|
|
56
|
+
landed in this release. The 9 R1 + 4 R2 regression tests live in
|
|
57
|
+
[`tests/r1-fixes.test.ts`](./tests/r1-fixes.test.ts).
|
|
58
|
+
|
|
59
|
+
**R2 MUST-FIX (S1185):**
|
|
60
|
+
|
|
61
|
+
- **HIGH (R2 Research 1):** the R1 hand-rolled `isGraphInterrupt`
|
|
62
|
+
duck-type missed `NodeInterrupt` (a `GraphInterrupt` subclass that
|
|
63
|
+
is the ACTUAL error thrown by `interrupt()` inside a node) AND broke
|
|
64
|
+
under bundler minification when `keep_classnames` was off. Fix:
|
|
65
|
+
import `isGraphInterrupt` directly from `@langchain/langgraph`'s
|
|
66
|
+
main entry (a stable public export that uses LangGraph's internal
|
|
67
|
+
`unminifiable_name` static-getter pattern). 12 LOC of hand-rolled
|
|
68
|
+
helper deleted, 1 line of named import added. Tests now use real
|
|
69
|
+
`GraphInterrupt` + `NodeInterrupt` instances.
|
|
70
|
+
- **LOW (R2 Critic 3):** added a comment block in `wrappedStream`
|
|
71
|
+
explaining the `activeInvokeMarkers.size > 0` ordering invariant
|
|
72
|
+
so future maintainers don't misread the race-free pattern.
|
|
73
|
+
- **LOW (R2 Analyst):** fixed stale README design-note line that still
|
|
74
|
+
said "counter" instead of "`Set<symbol>`".
|
|
75
|
+
|
|
76
|
+
**R1-Fixes (8 findings):**
|
|
77
|
+
|
|
78
|
+
1. **CRITICAL (Critic 1):** `withDarwinEvolution` shared-counter race —
|
|
79
|
+
replaced `let invokesInFlight = 0` with `Set<symbol>` so concurrent
|
|
80
|
+
`Promise.all([graph.invoke, graph.invoke])` calls fire `onTrajectory`
|
|
81
|
+
exactly once each instead of double-firing. Regression test in
|
|
82
|
+
`tests/r1-fixes.test.ts`.
|
|
83
|
+
2. **HIGH (Critic 2):** `stream({ streamMode: "updates" })` silently
|
|
84
|
+
skipped hook — adapter now `console.warn`s once when streamMode is
|
|
85
|
+
not `"values"`, surfacing the contract gap to users.
|
|
86
|
+
3. **HIGH (Research 5):** `runAgent`-throws of `GraphInterrupt`
|
|
87
|
+
(LangGraph HITL signal) were getting wrapped in `DarwinNodeError`,
|
|
88
|
+
breaking the resume protocol. `createDarwinNode` now duck-types the
|
|
89
|
+
`name` / `constructor.name` field and re-throws untouched.
|
|
90
|
+
4. **MEDIUM (Critic 4):** `Object.freeze({...stateObj})` is shallow.
|
|
91
|
+
JSDoc on `DarwinTrajectoryEvent.finalState` now says so explicitly
|
|
92
|
+
and points to "treat nested values as immutable by convention."
|
|
93
|
+
5. **MEDIUM (Critic 6):** Added explicit test for
|
|
94
|
+
`result.experiment.trajectory === null` (not just `undefined`).
|
|
95
|
+
6. **MEDIUM (Critic 7):** Added `verify:version-sync` script that the
|
|
96
|
+
`prepublishOnly` step runs — refuses to publish when `package.json`
|
|
97
|
+
and `src/index.ts` `VERSION` constant drift.
|
|
98
|
+
7. **MEDIUM (Critic 8, Analyst 4):** Example 02 + 03 now warn about
|
|
99
|
+
`taskKey` shadowing and `MemorySaver` being dev-only.
|
|
100
|
+
8. **LOW (Research 6):** `peerDependenciesMeta` stanza added
|
|
101
|
+
(both peers marked `optional: false` explicitly).
|
|
102
|
+
|
|
103
|
+
### V0.2 Roadmap (deferred from R1 review)
|
|
104
|
+
|
|
105
|
+
- **`DarwinCallbackHandler` migration** (Research 2): drop the
|
|
106
|
+
`invoke`/`stream` monkey-patch in `withDarwinEvolution` in favour of
|
|
107
|
+
LangChain's `BaseCallbackHandler` registered via `graph.invoke(input,
|
|
108
|
+
{ callbacks: [...] })`. Outlives any LangGraph-internal API shuffle.
|
|
109
|
+
- **`toOtelAttributes(trajectory)` helper** (Research 3): map Darwin's
|
|
110
|
+
`ExecutionTrace` to OpenTelemetry GenAI Semantic Conventions
|
|
111
|
+
(`gen_ai.usage.input_tokens`, `gen_ai.agent.name`, etc.) so Langfuse /
|
|
112
|
+
Braintrust / Datadog integrate out-of-the-box.
|
|
113
|
+
- **`darwinMessagesAnnotation()`** (Research 7): variant that merges
|
|
114
|
+
Darwin's three channels with LangGraph's `MessagesAnnotation.spec` so
|
|
115
|
+
mixing `createReactAgent` and `createDarwinNode` in one graph works
|
|
116
|
+
without channel-name conflicts.
|
|
117
|
+
- **`onResult` vs `onTrajectory` README paragraph** (Analyst 2):
|
|
118
|
+
explicit "when to pick which" guidance. Currently covered in JSDoc
|
|
119
|
+
only.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 StudioMeyer (Matthias Meyer)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# darwin-langgraph
|
|
2
|
+
|
|
3
|
+
> **LangGraph.js adapter for [`darwin-agents`](https://www.npmjs.com/package/darwin-agents).**
|
|
4
|
+
> Wrap self-evolving Darwin agents as `StateGraph` nodes — with zero hard deps.
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/darwin-langgraph)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
`darwin-langgraph` is a thin, peer-dependency-only bridge between two
|
|
10
|
+
independent libraries:
|
|
11
|
+
|
|
12
|
+
- **[`darwin-agents`](https://www.npmjs.com/package/darwin-agents)** — Darwin
|
|
13
|
+
is an agent framework where prompts evolve themselves via A/B testing,
|
|
14
|
+
multi-model critics, safety gates, and (since `v0.5.0-alpha.1`) full
|
|
15
|
+
execution-trace capture for GEPA-style reflective optimizers.
|
|
16
|
+
- **[`@langchain/langgraph`](https://www.npmjs.com/package/@langchain/langgraph)** —
|
|
17
|
+
LangGraph is the state-graph orchestration runtime used by Uber,
|
|
18
|
+
LinkedIn, Klarna, Replit, and others to build multi-step, multi-agent
|
|
19
|
+
workflows with durable execution and human-in-the-loop.
|
|
20
|
+
|
|
21
|
+
If you use **only Darwin**, you install nothing extra — Darwin itself has
|
|
22
|
+
zero LangChain dependency. If you use **only LangGraph**, the same.
|
|
23
|
+
If you want to combine the two — let LangGraph orchestrate, let Darwin
|
|
24
|
+
evolve — install this adapter.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install darwin-langgraph@alpha @langchain/langgraph darwin-agents@alpha
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Both `@langchain/langgraph` and `darwin-agents` are declared as
|
|
33
|
+
**peer-dependencies** — you control the installed versions, the adapter
|
|
34
|
+
never pins them.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { StateGraph } from "@langchain/langgraph";
|
|
40
|
+
import { defineAgent } from "darwin-agents";
|
|
41
|
+
import {
|
|
42
|
+
createDarwinNode,
|
|
43
|
+
darwinAnnotation,
|
|
44
|
+
withDarwinEvolution,
|
|
45
|
+
} from "darwin-langgraph";
|
|
46
|
+
|
|
47
|
+
const researcher = defineAgent({
|
|
48
|
+
name: "researcher",
|
|
49
|
+
role: "Topic Researcher",
|
|
50
|
+
description: "Five bullets on a topic.",
|
|
51
|
+
systemPrompt: "Return exactly 5 bullet points.",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const graph = withDarwinEvolution(
|
|
55
|
+
new StateGraph(darwinAnnotation())
|
|
56
|
+
.addNode("research", createDarwinNode(researcher))
|
|
57
|
+
.addEdge("__start__", "research")
|
|
58
|
+
.compile(),
|
|
59
|
+
{
|
|
60
|
+
nodeMap: { research: "researcher" },
|
|
61
|
+
onTrajectory: (event) => {
|
|
62
|
+
console.log(
|
|
63
|
+
`${event.nodeName} ran in ${event.trajectory.turnCount} turns ` +
|
|
64
|
+
`with ${event.trajectory.toolCalls.length} tool calls.`,
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const result = await graph.invoke({ task: "What is GEPA?" });
|
|
71
|
+
console.log(result.output);
|
|
72
|
+
console.log("trajectory:", result.darwinTrajectory);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Three surfaces
|
|
76
|
+
|
|
77
|
+
### 1. `createDarwinNode(agent, opts?)`
|
|
78
|
+
|
|
79
|
+
Wraps a Darwin `AgentDefinition` as a LangGraph node action. The returned
|
|
80
|
+
function:
|
|
81
|
+
|
|
82
|
+
1. Reads the task from `state[opts.taskKey ?? "task"]`.
|
|
83
|
+
2. Calls `runAgent(agent, task, opts.runOptions)` from `darwin-agents`.
|
|
84
|
+
3. Writes the agent's output to `state[opts.outputKey ?? "output"]`.
|
|
85
|
+
4. Optionally writes the captured `ExecutionTrace` to
|
|
86
|
+
`state[opts.trajectoryKey ?? "darwinTrajectory"]`.
|
|
87
|
+
|
|
88
|
+
| Option | Type | Default | Description |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| `taskKey` | `string` | `"task"` | State property to read the task from. |
|
|
91
|
+
| `outputKey` | `string` | `"output"` | State property the output is written to. |
|
|
92
|
+
| `trajectoryKey` | `string` | `"darwinTrajectory"` | State property the trace is written to. |
|
|
93
|
+
| `runOptions` | object | `{}` | Passthrough for `model`, `maxTurns`, `timeout`, `cwd`, `autonomous`, `config`. |
|
|
94
|
+
| `captureTrace` | `boolean` | `true` | Set `false` to skip trajectory propagation. |
|
|
95
|
+
| `onResult` | function | — | Fire-and-forget side-effect after each run. Errors are swallowed. |
|
|
96
|
+
|
|
97
|
+
### 2. `darwinAnnotation(extra?)`
|
|
98
|
+
|
|
99
|
+
Returns an `Annotation.Root` with three pre-defined channels
|
|
100
|
+
(`task` / `output` / `darwinTrajectory`) plus any extra channels you pass
|
|
101
|
+
in. Composable:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
const State = darwinAnnotation({
|
|
105
|
+
reviewNotes: Annotation<string[]>({
|
|
106
|
+
reducer: (a, b) => a.concat(b),
|
|
107
|
+
default: () => [],
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Power-users can spread `getDarwinChannelSpec()` directly into their own
|
|
113
|
+
`Annotation.Root({...})` when the helper doesn't fit.
|
|
114
|
+
|
|
115
|
+
### 3. `withDarwinEvolution(graph, opts)`
|
|
116
|
+
|
|
117
|
+
Wraps a compiled `StateGraph` with a post-run hook that fires `onTrajectory`
|
|
118
|
+
for each node listed in `nodeMap` after every `invoke` / `stream` run.
|
|
119
|
+
Errors inside the hook are swallowed — the graph result is always
|
|
120
|
+
returned unchanged.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
withDarwinEvolution(graph, {
|
|
124
|
+
nodeMap: {
|
|
125
|
+
research: "researcher",
|
|
126
|
+
critique: { agentName: "critic", trajectoryKey: "critiqueTrace" },
|
|
127
|
+
},
|
|
128
|
+
onTrajectory: (event) => {
|
|
129
|
+
// event.nodeName, event.agentName, event.trajectory, event.finalState
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Custom errors
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { DarwinNodeError, DarwinEvolutionHookError } from "darwin-langgraph";
|
|
138
|
+
|
|
139
|
+
try { await graph.invoke({ task: "..." }); }
|
|
140
|
+
catch (err) {
|
|
141
|
+
if (err instanceof DarwinNodeError) {
|
|
142
|
+
console.error(`Node "${err.agentName}" failed:`, err.cause);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
> **GraphInterrupt is re-thrown un-wrapped.** When a downstream node
|
|
148
|
+
> calls LangGraph's `interrupt()` for human-in-the-loop, the resulting
|
|
149
|
+
> `GraphInterrupt` is surfaced through `createDarwinNode` un-touched so
|
|
150
|
+
> the resume protocol (`Command({ resume: ... })`) works. Only non-HITL
|
|
151
|
+
> errors get wrapped in `DarwinNodeError`.
|
|
152
|
+
|
|
153
|
+
### `onResult` vs `onTrajectory` — when to pick which
|
|
154
|
+
|
|
155
|
+
Both hooks fire after a Darwin agent finishes, but at different layers:
|
|
156
|
+
|
|
157
|
+
- **`onResult`** (per-node, on `createDarwinNode`) receives the **full
|
|
158
|
+
raw `RunResult`** — including `experiment`, `evolution`, `reportPath`,
|
|
159
|
+
and the raw output. Use it for **per-agent side-effects** that don't
|
|
160
|
+
care about graph structure (e.g. writing to a database, logging the
|
|
161
|
+
full result).
|
|
162
|
+
- **`onTrajectory`** (graph-wide, on `withDarwinEvolution`) receives a
|
|
163
|
+
**normalised `DarwinTrajectoryEvent`** with `nodeName`, `agentName`,
|
|
164
|
+
trajectory, and a frozen final-state snapshot. Use it for
|
|
165
|
+
**cross-node observability** that needs to know which LangGraph node
|
|
166
|
+
produced which trace (cost tracking, A/B telemetry, evolution loops).
|
|
167
|
+
|
|
168
|
+
If you only need per-agent side-effects, use `onResult`. If you need
|
|
169
|
+
node-aware telemetry, use `onTrajectory`. They are **not redundant** —
|
|
170
|
+
combining both is fine, just be aware they fire at slightly different
|
|
171
|
+
points (node-completion vs graph-completion).
|
|
172
|
+
|
|
173
|
+
### `streamMode` and the trajectory hook
|
|
174
|
+
|
|
175
|
+
`withDarwinEvolution` wraps both `invoke()` and `stream()`. The hook
|
|
176
|
+
reliably fires when:
|
|
177
|
+
|
|
178
|
+
- You call `graph.invoke({...})` — always works. Internally
|
|
179
|
+
`streamMode: "values"`.
|
|
180
|
+
- You call `graph.stream({...})` or `graph.stream({...}, { streamMode:
|
|
181
|
+
"values" })` — works.
|
|
182
|
+
- You include `"values"` in a multi-mode array
|
|
183
|
+
(`streamMode: ["values", "updates"]`) — works.
|
|
184
|
+
|
|
185
|
+
It does **not** fire on `streamMode: "updates"` / `"messages"` /
|
|
186
|
+
`"debug"` (without `"values"`) — the last chunk in those modes is a
|
|
187
|
+
node-update dict, not the final state, so the trajectory key lookup
|
|
188
|
+
silently misses. The adapter logs a `console.warn` (once per process)
|
|
189
|
+
the first time it detects a non-`values` stream so you don't silently
|
|
190
|
+
lose telemetry. V0.2 will migrate to the LangChain `CallbackHandler`
|
|
191
|
+
mechanism which fires regardless of stream mode.
|
|
192
|
+
|
|
193
|
+
## Examples
|
|
194
|
+
|
|
195
|
+
The [`examples/`](./examples/) directory ships three runnable scripts:
|
|
196
|
+
|
|
197
|
+
- [`01-basic-node.ts`](./examples/01-basic-node.ts) — single Darwin node in a 1-edge graph.
|
|
198
|
+
- [`02-multi-agent-research.ts`](./examples/02-multi-agent-research.ts) — researcher → critic → writer pipeline with per-node trajectories.
|
|
199
|
+
- [`03-hitl-with-darwin.ts`](./examples/03-hitl-with-darwin.ts) — LangGraph `interrupt()` before a Darwin agent runs, with `Command({ resume: ... })` to continue.
|
|
200
|
+
|
|
201
|
+
## Compatibility matrix
|
|
202
|
+
|
|
203
|
+
| `darwin-langgraph` | `darwin-agents` | `@langchain/langgraph` | Status |
|
|
204
|
+
|---|---|---|---|
|
|
205
|
+
| `0.1.0-alpha.x` | `^0.5.0-alpha.1` | `^1.3.0` | alpha (this release) |
|
|
206
|
+
|
|
207
|
+
The peer-dep range `darwin-agents: "^0.5.0-alpha.1"` follows npm's
|
|
208
|
+
prerelease semver rules — `0.5.0-alpha.N` and `0.5.0` final satisfy it,
|
|
209
|
+
but `0.5.1-alpha.0` does NOT. A patch release of this adapter will be
|
|
210
|
+
required when `darwin-agents` bumps past `0.5.x`.
|
|
211
|
+
|
|
212
|
+
## Known limitations (will be addressed in v0.2)
|
|
213
|
+
|
|
214
|
+
- **`withDarwinEvolution` monkey-patches `invoke`/`stream`** — V0.2
|
|
215
|
+
will migrate to a `DarwinCallbackHandler` you pass via
|
|
216
|
+
`graph.invoke(input, { callbacks: [...] })`, which is the canonical
|
|
217
|
+
LangChain pattern (matches Langfuse, Braintrust, LangSmith handlers).
|
|
218
|
+
- **No `gen_ai.*` OTEL attribute helper** — V0.2 will ship
|
|
219
|
+
`toOtelAttributes(trajectory)` so traces forward to Langfuse /
|
|
220
|
+
Braintrust / Datadog with OpenTelemetry GenAI Semantic Conventions.
|
|
221
|
+
- **No bundled `messages` channel** — V0.2 will add
|
|
222
|
+
`darwinMessagesAnnotation()` for graphs that mix `createReactAgent`
|
|
223
|
+
with `createDarwinNode`. Until then, you can pass
|
|
224
|
+
`darwinAnnotation({ ...MessagesAnnotation.spec })` manually.
|
|
225
|
+
|
|
226
|
+
The adapter releases follow `darwin-agents` major bumps. When
|
|
227
|
+
`@langchain/langgraph` 2.x lands, the adapter ships a new major within
|
|
228
|
+
the same minor of the underlying Darwin release.
|
|
229
|
+
|
|
230
|
+
## Subscription / API key handling
|
|
231
|
+
|
|
232
|
+
The adapter NEVER touches `ANTHROPIC_API_KEY`. If you run Darwin on a
|
|
233
|
+
Claude Max subscription via the Claude Code CLI, your bootstrap must
|
|
234
|
+
`delete process.env.ANTHROPIC_API_KEY` BEFORE invoking the graph —
|
|
235
|
+
otherwise Darwin's `ClaudeCliProvider` will hit the metered API and you
|
|
236
|
+
pay per request instead of the flat subscription.
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
// bootstrap.ts
|
|
240
|
+
import "dotenv/config";
|
|
241
|
+
delete process.env.ANTHROPIC_API_KEY; // enforce Max-Plan subscription
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Design notes
|
|
245
|
+
|
|
246
|
+
- **Zero runtime deps.** Both `darwin-agents` and `@langchain/langgraph`
|
|
247
|
+
are peer-dependencies. The adapter itself is a couple of hundred lines
|
|
248
|
+
of TypeScript that re-exports a handful of types.
|
|
249
|
+
- **`invoke()` calls `stream()` internally.** LangGraph v1.x implements
|
|
250
|
+
`invoke` on top of `stream`. The adapter tracks in-flight invokes
|
|
251
|
+
with a per-call `Symbol` marker stored in a `Set` so the trajectory
|
|
252
|
+
hook fires exactly once per logical run, even under concurrent
|
|
253
|
+
`Promise.all([graph.invoke(...), graph.invoke(...)])`.
|
|
254
|
+
- **`GraphInterrupt` re-throw.** The adapter imports `isGraphInterrupt`
|
|
255
|
+
directly from `@langchain/langgraph` so LangGraph's HITL exceptions
|
|
256
|
+
(both `GraphInterrupt` and `NodeInterrupt`) propagate untouched —
|
|
257
|
+
`Command({ resume })` works as expected.
|
|
258
|
+
- **Hook errors never propagate.** Both `opts.onResult` (on
|
|
259
|
+
`createDarwinNode`) and `opts.onTrajectory` (on `withDarwinEvolution`)
|
|
260
|
+
use a fire-and-forget pattern with one-shot `console.warn` for the
|
|
261
|
+
first swallowed throw per process.
|
|
262
|
+
- **No memory provider construction.** Memory backends (Postgres,
|
|
263
|
+
SQLite) are resolved by `darwin-agents` itself — set
|
|
264
|
+
`DARWIN_POSTGRES_URL` / `DARWIN_SQLITE_PATH` env vars or pass
|
|
265
|
+
`runOptions.config.dataDir`.
|
|
266
|
+
|
|
267
|
+
## Versioning
|
|
268
|
+
|
|
269
|
+
Released under the `alpha` npm dist-tag in parallel with
|
|
270
|
+
`darwin-agents@0.5.0-alpha.1`. Default `npm install darwin-langgraph`
|
|
271
|
+
will NOT resolve until `0.1.0` final ships. Explicit opt-in via
|
|
272
|
+
`npm install darwin-langgraph@alpha`.
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
MIT © 2026 StudioMeyer
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surface 1 — `createDarwinNode(agent, opts?)`.
|
|
3
|
+
*
|
|
4
|
+
* Wraps a Darwin `AgentDefinition` as a LangGraph node action. The
|
|
5
|
+
* returned function:
|
|
6
|
+
* 1. Reads the task from `state[opts.taskKey ?? "task"]`.
|
|
7
|
+
* 2. Invokes `runAgent(agent, task, { memory })` from `darwin-agents`.
|
|
8
|
+
* 3. Writes the agent output to `state[opts.outputKey ?? "output"]`.
|
|
9
|
+
* 4. Optionally writes the captured `ExecutionTrace` to
|
|
10
|
+
* `state[opts.trajectoryKey ?? "darwinTrajectory"]` so downstream
|
|
11
|
+
* nodes (or the evolution hook) can read it.
|
|
12
|
+
*
|
|
13
|
+
* Design notes (S1185):
|
|
14
|
+
* - **Non-generic return type.** The adapter returns `(state, config?) =>
|
|
15
|
+
* Promise<Record<string, unknown>>`. LangGraph accepts any function
|
|
16
|
+
* with the shape `(state, config?) => Partial<State>` as a NodeAction;
|
|
17
|
+
* leaving the return type generic-free keeps the public API trivial
|
|
18
|
+
* to consume from JS and TS alike. Strong typing is recovered via
|
|
19
|
+
* `darwinAnnotation()` on the StateGraph side.
|
|
20
|
+
* - **`runAgent` errors are wrapped** in `DarwinNodeError` so consumers
|
|
21
|
+
* can `instanceof`-check adapter failures vs upstream failures.
|
|
22
|
+
* - **`opts.onResult` is fire-and-forget swallowed.** A user callback
|
|
23
|
+
* must NEVER break the graph; we await it, but suppress any throw
|
|
24
|
+
* and surface it via `console.warn` once per Node. Mirrors the
|
|
25
|
+
* `darwinPostRun` no-throw guarantee from `agents/lib/darwin-hook.ts`.
|
|
26
|
+
* - **No `ANTHROPIC_API_KEY` manipulation.** `runAgent` makes its own
|
|
27
|
+
* subprocess/provider decisions; consumers running on Claude Max
|
|
28
|
+
* subscriptions must `delete process.env.ANTHROPIC_API_KEY` in
|
|
29
|
+
* their own bootstrap (documented in README).
|
|
30
|
+
*/
|
|
31
|
+
import type { AgentDefinition, DarwinConfig, RunResult } from "darwin-agents";
|
|
32
|
+
/**
|
|
33
|
+
* LangGraph HITL detector (R1 Research Finding 5, refined by R2 Research
|
|
34
|
+
* Finding 1, S1185).
|
|
35
|
+
*
|
|
36
|
+
* When `interrupt()` is called inside a downstream node during a
|
|
37
|
+
* `runAgent` run, LangGraph throws a typed error to surface the HITL
|
|
38
|
+
* pause. The adapter MUST re-throw that error untouched — wrapping it
|
|
39
|
+
* in `DarwinNodeError` would swallow the interrupt and break the
|
|
40
|
+
* `Command({ resume })` protocol.
|
|
41
|
+
*
|
|
42
|
+
* Initially (R1) we hand-rolled a `name === "GraphInterrupt"` duck-type.
|
|
43
|
+
* R2 surfaced two issues with that approach:
|
|
44
|
+
* 1. Production minifiers (esbuild/Terser/SWC) rewrite class.name
|
|
45
|
+
* and constructor.name unless `keep_classnames` is set — breaking
|
|
46
|
+
* the constructor-name fallback for bundled consumers.
|
|
47
|
+
* 2. The real-world case throws `NodeInterrupt` (a `GraphInterrupt`
|
|
48
|
+
* subclass), NOT raw `GraphInterrupt`. The hand-rolled string
|
|
49
|
+
* check missed it.
|
|
50
|
+
*
|
|
51
|
+
* Fix: import `isGraphInterrupt` from `@langchain/langgraph`'s main
|
|
52
|
+
* entry — it is a stable PUBLIC export and uses LangGraph's internal
|
|
53
|
+
* `unminifiable_name` static-getter pattern that survives minification
|
|
54
|
+
* AND covers both `GraphInterrupt` and `NodeInterrupt`. The previous
|
|
55
|
+
* concern about "internal-ish module paths" was wrong — it lives in the
|
|
56
|
+
* top-level barrel.
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* Mirror of `darwin-agents` `RunOptions` — narrower because the adapter
|
|
60
|
+
* sets `taskType` and `promptVersion` implicitly. Exposed as `runOptions`
|
|
61
|
+
* on {@link CreateDarwinNodeOptions} so consumers can forward
|
|
62
|
+
* `model` / `maxTurns` / `timeout` / `autonomous` flags to `runAgent`
|
|
63
|
+
* without re-importing `darwin-agents`.
|
|
64
|
+
*
|
|
65
|
+
* If the upstream `RunOptions` shape changes in a future `darwin-agents`
|
|
66
|
+
* release, the build will break here on purpose — peer-dep semver
|
|
67
|
+
* guarantees additive-only changes inside the alpha range, but real
|
|
68
|
+
* drift happens (see `nex_learn fcfc928f`).
|
|
69
|
+
*/
|
|
70
|
+
export interface DarwinRunOptionsPassthrough {
|
|
71
|
+
model?: string;
|
|
72
|
+
maxTurns?: number;
|
|
73
|
+
taskType?: string;
|
|
74
|
+
config?: DarwinConfig;
|
|
75
|
+
promptVersion?: string;
|
|
76
|
+
cwd?: string;
|
|
77
|
+
timeout?: number;
|
|
78
|
+
autonomous?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Options accepted by {@link createDarwinNode}.
|
|
82
|
+
*
|
|
83
|
+
* All keys are optional. Defaults match the channel names returned by
|
|
84
|
+
* {@link darwinAnnotation} so the most common case (`createDarwinNode(myAgent)`
|
|
85
|
+
* inside `darwinAnnotation()`) needs zero configuration.
|
|
86
|
+
*/
|
|
87
|
+
export interface CreateDarwinNodeOptions {
|
|
88
|
+
/** State property to read the task from. Default: `"task"`. */
|
|
89
|
+
taskKey?: string;
|
|
90
|
+
/** State property to write the agent output to. Default: `"output"`. */
|
|
91
|
+
outputKey?: string;
|
|
92
|
+
/** State property to write the captured trajectory to. Default: `"darwinTrajectory"`. */
|
|
93
|
+
trajectoryKey?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Passthrough options forwarded to `darwin-agents.runAgent()`. Use this
|
|
96
|
+
* to override `model`, `maxTurns`, `timeout`, `cwd`, `autonomous`, or to
|
|
97
|
+
* provide a custom `DarwinConfig` (data dir, MCP servers, etc.).
|
|
98
|
+
*
|
|
99
|
+
* Memory resolution lives inside `darwin-agents` itself — set
|
|
100
|
+
* `DARWIN_POSTGRES_URL` / `DARWIN_SQLITE_PATH` env vars or pass a
|
|
101
|
+
* `config.dataDir` here. The adapter never instantiates a memory
|
|
102
|
+
* provider on its own.
|
|
103
|
+
*/
|
|
104
|
+
runOptions?: DarwinRunOptionsPassthrough;
|
|
105
|
+
/**
|
|
106
|
+
* Persist the captured `ExecutionTrace` in graph state. Default `true`.
|
|
107
|
+
* Set `false` to skip trajectory propagation when memory pressure is a
|
|
108
|
+
* concern (long-running streams) or when the consumer reads
|
|
109
|
+
* trajectories from Darwin's own memory backend.
|
|
110
|
+
*/
|
|
111
|
+
captureTrace?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Fire-and-forget side-effect after every successful run. Receives the
|
|
114
|
+
* full `RunResult`. **Errors are swallowed** — never let a callback
|
|
115
|
+
* break the LangGraph node.
|
|
116
|
+
*/
|
|
117
|
+
onResult?: (result: RunResult) => void | Promise<void>;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Type alias for the function returned by {@link createDarwinNode}.
|
|
121
|
+
* Matches the loose `NodeAction` contract from `@langchain/langgraph`:
|
|
122
|
+
* `(state, config?) => Promise<Partial<State>>`.
|
|
123
|
+
*/
|
|
124
|
+
export type DarwinNodeFn = (state: Record<string, unknown>, config?: unknown) => Promise<Record<string, unknown>>;
|
|
125
|
+
/**
|
|
126
|
+
* Build a LangGraph node from a Darwin agent.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* import { StateGraph } from "@langchain/langgraph";
|
|
131
|
+
* import { createDarwinNode, darwinAnnotation } from "darwin-langgraph";
|
|
132
|
+
* import { defineAgent } from "darwin-agents";
|
|
133
|
+
*
|
|
134
|
+
* const researcher = defineAgent({
|
|
135
|
+
* name: "researcher",
|
|
136
|
+
* role: "Topic Researcher",
|
|
137
|
+
* description: "Researches a topic and returns 5 bullet points.",
|
|
138
|
+
* systemPrompt: "Return exactly 5 bullets.",
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* const graph = new StateGraph(darwinAnnotation())
|
|
142
|
+
* .addNode("research", createDarwinNode(researcher))
|
|
143
|
+
* .addEdge("__start__", "research")
|
|
144
|
+
* .compile();
|
|
145
|
+
*
|
|
146
|
+
* const result = await graph.invoke({ task: "What is GEPA?" });
|
|
147
|
+
* console.log(result.output);
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export declare function createDarwinNode(agent: AgentDefinition, opts?: CreateDarwinNodeOptions): DarwinNodeFn;
|
|
151
|
+
//# sourceMappingURL=create-darwin-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-darwin-node.d.ts","sourceRoot":"","sources":["../src/create-darwin-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,SAAS,EACV,MAAM,eAAe,CAAC;AAIvB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,CAAC,EAAE,OAAO,KACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EACtB,IAAI,GAAE,uBAA4B,GACjC,YAAY,CAiFd"}
|