darwin-agents 0.7.1 → 0.9.0
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 +62 -0
- package/README.md +62 -1
- package/dist/src/cli/canary.d.ts +13 -0
- package/dist/src/cli/canary.d.ts.map +1 -0
- package/dist/src/cli/canary.js +66 -0
- package/dist/src/cli/canary.js.map +1 -0
- package/dist/src/cli/evolution-flags.d.ts +40 -0
- package/dist/src/cli/evolution-flags.d.ts.map +1 -0
- package/dist/src/cli/evolution-flags.js +106 -0
- package/dist/src/cli/evolution-flags.js.map +1 -0
- package/dist/src/cli/evolve.d.ts +10 -1
- package/dist/src/cli/evolve.d.ts.map +1 -1
- package/dist/src/cli/evolve.js +93 -8
- package/dist/src/cli/evolve.js.map +1 -1
- package/dist/src/cli/index.js +18 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/run.d.ts.map +1 -1
- package/dist/src/cli/run.js +54 -70
- package/dist/src/cli/run.js.map +1 -1
- package/dist/src/evolution/build-loop.d.ts +20 -0
- package/dist/src/evolution/build-loop.d.ts.map +1 -0
- package/dist/src/evolution/build-loop.js +67 -0
- package/dist/src/evolution/build-loop.js.map +1 -0
- package/dist/src/evolution/canary.d.ts +164 -0
- package/dist/src/evolution/canary.d.ts.map +1 -0
- package/dist/src/evolution/canary.js +244 -0
- package/dist/src/evolution/canary.js.map +1 -0
- package/dist/src/evolution/critic-families.d.ts +51 -0
- package/dist/src/evolution/critic-families.d.ts.map +1 -0
- package/dist/src/evolution/critic-families.js +51 -0
- package/dist/src/evolution/critic-families.js.map +1 -0
- package/dist/src/evolution/enabled-state.d.ts +59 -0
- package/dist/src/evolution/enabled-state.d.ts.map +1 -0
- package/dist/src/evolution/enabled-state.js +122 -0
- package/dist/src/evolution/enabled-state.js.map +1 -0
- package/dist/src/evolution/loop.d.ts +46 -0
- package/dist/src/evolution/loop.d.ts.map +1 -1
- package/dist/src/evolution/loop.js +119 -1
- package/dist/src/evolution/loop.js.map +1 -1
- package/dist/src/evolution/parse-score.d.ts +35 -0
- package/dist/src/evolution/parse-score.d.ts.map +1 -0
- package/dist/src/evolution/parse-score.js +115 -0
- package/dist/src/evolution/parse-score.js.map +1 -0
- package/dist/src/evolution/tracker.d.ts +13 -0
- package/dist/src/evolution/tracker.d.ts.map +1 -1
- package/dist/src/evolution/tracker.js +34 -0
- package/dist/src/evolution/tracker.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/memory/postgres-memory.d.ts.map +1 -1
- package/dist/src/memory/postgres-memory.js +1 -0
- package/dist/src/memory/postgres-memory.js.map +1 -1
- package/dist/src/memory/sqlite-memory.d.ts.map +1 -1
- package/dist/src/memory/sqlite-memory.js +1 -0
- package/dist/src/memory/sqlite-memory.js.map +1 -1
- package/dist/src/types.d.ts +45 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/agents/analyst.d.ts +0 -11
- package/dist/agents/analyst.d.ts.map +0 -1
- package/dist/agents/analyst.js +0 -78
- package/dist/agents/analyst.js.map +0 -1
- package/dist/agents/blog-writer.d.ts +0 -13
- package/dist/agents/blog-writer.d.ts.map +0 -1
- package/dist/agents/blog-writer.js +0 -59
- package/dist/agents/blog-writer.js.map +0 -1
- package/dist/agents/critic.d.ts +0 -11
- package/dist/agents/critic.d.ts.map +0 -1
- package/dist/agents/critic.js +0 -57
- package/dist/agents/critic.js.map +0 -1
- package/dist/agents/index.d.ts +0 -15
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/index.js +0 -31
- package/dist/agents/index.js.map +0 -1
- package/dist/agents/investigator-critic.d.ts +0 -10
- package/dist/agents/investigator-critic.d.ts.map +0 -1
- package/dist/agents/investigator-critic.js +0 -78
- package/dist/agents/investigator-critic.js.map +0 -1
- package/dist/agents/investigator.d.ts +0 -13
- package/dist/agents/investigator.d.ts.map +0 -1
- package/dist/agents/investigator.js +0 -105
- package/dist/agents/investigator.js.map +0 -1
- package/dist/agents/marketing.d.ts +0 -13
- package/dist/agents/marketing.d.ts.map +0 -1
- package/dist/agents/marketing.js +0 -59
- package/dist/agents/marketing.js.map +0 -1
- package/dist/agents/researcher.d.ts +0 -11
- package/dist/agents/researcher.d.ts.map +0 -1
- package/dist/agents/researcher.js +0 -68
- package/dist/agents/researcher.js.map +0 -1
- package/dist/agents/writer.d.ts +0 -9
- package/dist/agents/writer.d.ts.map +0 -1
- package/dist/agents/writer.js +0 -47
- package/dist/agents/writer.js.map +0 -1
- package/dist/cli/create.d.ts +0 -11
- package/dist/cli/create.d.ts.map +0 -1
- package/dist/cli/create.js +0 -104
- package/dist/cli/create.js.map +0 -1
- package/dist/cli/evolve.d.ts +0 -13
- package/dist/cli/evolve.d.ts.map +0 -1
- package/dist/cli/evolve.js +0 -69
- package/dist/cli/evolve.js.map +0 -1
- package/dist/cli/index.d.ts +0 -13
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -84
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/init.d.ts +0 -12
- package/dist/cli/init.d.ts.map +0 -1
- package/dist/cli/init.js +0 -68
- package/dist/cli/init.js.map +0 -1
- package/dist/cli/run.d.ts +0 -7
- package/dist/cli/run.d.ts.map +0 -1
- package/dist/cli/run.js +0 -371
- package/dist/cli/run.js.map +0 -1
- package/dist/cli/status.d.ts +0 -7
- package/dist/cli/status.d.ts.map +0 -1
- package/dist/cli/status.js +0 -123
- package/dist/cli/status.js.map +0 -1
- package/dist/core/agent.d.ts +0 -53
- package/dist/core/agent.d.ts.map +0 -1
- package/dist/core/agent.js +0 -172
- package/dist/core/agent.js.map +0 -1
- package/dist/core/runner.d.ts +0 -75
- package/dist/core/runner.d.ts.map +0 -1
- package/dist/core/runner.js +0 -255
- package/dist/core/runner.js.map +0 -1
- package/dist/evolution/loop.d.ts +0 -100
- package/dist/evolution/loop.d.ts.map +0 -1
- package/dist/evolution/loop.js +0 -424
- package/dist/evolution/loop.js.map +0 -1
- package/dist/evolution/multi-critic.d.ts +0 -58
- package/dist/evolution/multi-critic.d.ts.map +0 -1
- package/dist/evolution/multi-critic.js +0 -326
- package/dist/evolution/multi-critic.js.map +0 -1
- package/dist/evolution/notifications.d.ts +0 -32
- package/dist/evolution/notifications.d.ts.map +0 -1
- package/dist/evolution/notifications.js +0 -92
- package/dist/evolution/notifications.js.map +0 -1
- package/dist/evolution/optimizer.d.ts +0 -64
- package/dist/evolution/optimizer.d.ts.map +0 -1
- package/dist/evolution/optimizer.js +0 -223
- package/dist/evolution/optimizer.js.map +0 -1
- package/dist/evolution/patterns.d.ts +0 -63
- package/dist/evolution/patterns.d.ts.map +0 -1
- package/dist/evolution/patterns.js +0 -297
- package/dist/evolution/patterns.js.map +0 -1
- package/dist/evolution/safety.d.ts +0 -76
- package/dist/evolution/safety.d.ts.map +0 -1
- package/dist/evolution/safety.js +0 -182
- package/dist/evolution/safety.js.map +0 -1
- package/dist/evolution/tracker.d.ts +0 -48
- package/dist/evolution/tracker.d.ts.map +0 -1
- package/dist/evolution/tracker.js +0 -163
- package/dist/evolution/tracker.js.map +0 -1
- package/dist/index.d.ts +0 -32
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -35
- package/dist/index.js.map +0 -1
- package/dist/memory/index.d.ts +0 -32
- package/dist/memory/index.d.ts.map +0 -1
- package/dist/memory/index.js +0 -49
- package/dist/memory/index.js.map +0 -1
- package/dist/memory/postgres-memory.d.ts +0 -52
- package/dist/memory/postgres-memory.d.ts.map +0 -1
- package/dist/memory/postgres-memory.js +0 -515
- package/dist/memory/postgres-memory.js.map +0 -1
- package/dist/memory/sqlite-memory.d.ts +0 -36
- package/dist/memory/sqlite-memory.d.ts.map +0 -1
- package/dist/memory/sqlite-memory.js +0 -380
- package/dist/memory/sqlite-memory.js.map +0 -1
- package/dist/providers/anthropic.d.ts +0 -20
- package/dist/providers/anthropic.d.ts.map +0 -1
- package/dist/providers/anthropic.js +0 -82
- package/dist/providers/anthropic.js.map +0 -1
- package/dist/providers/claude-cli.d.ts +0 -35
- package/dist/providers/claude-cli.d.ts.map +0 -1
- package/dist/providers/claude-cli.js +0 -153
- package/dist/providers/claude-cli.js.map +0 -1
- package/dist/providers/index.d.ts +0 -39
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js +0 -58
- package/dist/providers/index.js.map +0 -1
- package/dist/providers/ollama.d.ts +0 -17
- package/dist/providers/ollama.d.ts.map +0 -1
- package/dist/providers/ollama.js +0 -64
- package/dist/providers/ollama.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -19
- package/dist/providers/openai.d.ts.map +0 -1
- package/dist/providers/openai.js +0 -75
- package/dist/providers/openai.js.map +0 -1
- package/dist/providers/types.d.ts +0 -62
- package/dist/providers/types.d.ts.map +0 -1
- package/dist/providers/types.js +0 -9
- package/dist/providers/types.js.map +0 -1
- package/dist/types.d.ts +0 -221
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -19
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate-by-Reproduce drift-detection canary (Phase 2 A5).
|
|
3
|
+
*
|
|
4
|
+
* The A/B safety gate guards prompt *quality* — it catches a new prompt version
|
|
5
|
+
* that scores worse. But an agent can drift WITHOUT its quality score moving: a
|
|
6
|
+
* model update, an MCP tool breaking, or a prompt evolution's side-effect can
|
|
7
|
+
* change *how* the agent gets to the answer (different tools, more turns, more
|
|
8
|
+
* errors) while the final score stays flat. Vadim Nicolai's production write-up
|
|
9
|
+
* captures it: "the agent still sounds fluent, but the trajectory is longer and
|
|
10
|
+
* the tool-calls more repetitive."
|
|
11
|
+
*
|
|
12
|
+
* This module compares an agent's recent execution *trajectories* against a
|
|
13
|
+
* frozen baseline and flags behavioural drift. It is the read side of the
|
|
14
|
+
* trajectory capture shipped in A1 (`ExecutionTrace`).
|
|
15
|
+
*
|
|
16
|
+
* ── Method (grounded in 2026 agent-eval practice) ──
|
|
17
|
+
* Exact-hash equivalence is the wrong tool — LLM runs are non-deterministic, so
|
|
18
|
+
* an exact trajectory hash drifts every single run. Instead we use the same
|
|
19
|
+
* deterministic, tolerance-based metrics the field settled on (LangChain's
|
|
20
|
+
* `agentevals`, Vertex AI trajectory metrics, Galileo):
|
|
21
|
+
*
|
|
22
|
+
* 1. **Unordered tool-set Jaccard** — `|A∩B| / |A∪B|` over tool *names*.
|
|
23
|
+
* Catches new/missing tools regardless of order. Primary signal.
|
|
24
|
+
* 2. **Ordered tool-sequence similarity** — normalised Levenshtein over the
|
|
25
|
+
* tool-name sequence. Catches re-ordering / repetition.
|
|
26
|
+
* 3. **Turn-count ratio** — `candidate.turns / baseline.turns`. A ratio > 1.5
|
|
27
|
+
* is the canonical "agent is grinding" symptom.
|
|
28
|
+
* 4. **Error-rate delta** — error spikes are the most reliable tool-break tell.
|
|
29
|
+
*
|
|
30
|
+
* We deliberately do NOT compare tool *arguments* (LLM-variable → noise) or
|
|
31
|
+
* output length (verbosity swings without drift). We keep zero hard deps: these
|
|
32
|
+
* metrics are ~a dozen lines each, so `agentevals` stays an inspiration, not a
|
|
33
|
+
* dependency — Darwin's whole point is a pure, injectable core.
|
|
34
|
+
*
|
|
35
|
+
* ── Sampling ──
|
|
36
|
+
* A single failing run is noise, not drift. The canary needs a *pattern*:
|
|
37
|
+
* evaluate several same-version runs and alert only when ≥ N of them drift
|
|
38
|
+
* (default 2). Three candidate runs per week is the field's rule of thumb.
|
|
39
|
+
*
|
|
40
|
+
* ── Baseline staleness (the load-bearing footgun) ──
|
|
41
|
+
* A baseline is only meaningful against runs of the SAME prompt version. When a
|
|
42
|
+
* prompt evolves, the new version *should* behave differently — comparing it to
|
|
43
|
+
* the old baseline would fire a false alarm on every intentional improvement.
|
|
44
|
+
* `runCanaryOverExperiments` therefore baselines strictly within the active
|
|
45
|
+
* prompt version: after an evolution the canary reports `insufficient-data`
|
|
46
|
+
* (re-baseline in progress) rather than screaming drift.
|
|
47
|
+
*
|
|
48
|
+
* Everything here is pure and deterministic. Side-effects (loading experiments,
|
|
49
|
+
* printing, alerting) live in the CLI command and in private cron wiring.
|
|
50
|
+
*/
|
|
51
|
+
// ─── Trajectory signature extraction ───────────────────────
|
|
52
|
+
/** The ordered sequence of tool names exactly as they fired. */
|
|
53
|
+
export function toolSequence(trace) {
|
|
54
|
+
// `?? []` is defensive against trajectories deserialised from older DB rows:
|
|
55
|
+
// a NULL JSONB/TEXT column can yield null at runtime despite the non-optional
|
|
56
|
+
// type, and a drift check must degrade gracefully rather than throw.
|
|
57
|
+
return (trace.toolCalls ?? []).map((c) => c.tool);
|
|
58
|
+
}
|
|
59
|
+
/** The set of distinct tool names used (order-independent). */
|
|
60
|
+
export function toolSet(trace) {
|
|
61
|
+
return new Set(toolSequence(trace));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Error rate of a run: (tool-call errors + turn-level errors) normalised by the
|
|
65
|
+
* turn count. Uses `max(1, turnCount)` so a zero-turn trace can't divide by zero.
|
|
66
|
+
*/
|
|
67
|
+
export function errorRate(trace) {
|
|
68
|
+
const toolErrors = (trace.toolCalls ?? []).filter((c) => c.outcome === 'error').length;
|
|
69
|
+
const turnErrors = (trace.errors ?? []).length;
|
|
70
|
+
return (toolErrors + turnErrors) / Math.max(1, trace.turnCount ?? 0);
|
|
71
|
+
}
|
|
72
|
+
// ─── Pure similarity metrics ────────────────────────────────
|
|
73
|
+
/**
|
|
74
|
+
* Jaccard similarity of two string sets: `|A∩B| / |A∪B|`.
|
|
75
|
+
* 1 = identical, 0 = disjoint. Two empty sets are defined as 1 (both used no
|
|
76
|
+
* tools → identical behaviour, not drift).
|
|
77
|
+
*/
|
|
78
|
+
export function jaccard(a, b) {
|
|
79
|
+
if (a.size === 0 && b.size === 0)
|
|
80
|
+
return 1;
|
|
81
|
+
let intersection = 0;
|
|
82
|
+
for (const x of a)
|
|
83
|
+
if (b.has(x))
|
|
84
|
+
intersection++;
|
|
85
|
+
const union = a.size + b.size - intersection;
|
|
86
|
+
return union === 0 ? 1 : intersection / union;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Normalised Levenshtein similarity over two token sequences:
|
|
90
|
+
* `1 - editDistance / max(len)`. 1 = identical order, 0 = fully different.
|
|
91
|
+
* Two empty sequences are defined as 1.
|
|
92
|
+
*/
|
|
93
|
+
export function sequenceSimilarity(a, b) {
|
|
94
|
+
const maxLen = Math.max(a.length, b.length);
|
|
95
|
+
if (maxLen === 0)
|
|
96
|
+
return 1;
|
|
97
|
+
return 1 - levenshtein(a, b) / maxLen;
|
|
98
|
+
}
|
|
99
|
+
/** Iterative Levenshtein edit distance over token arrays. O(n·m) time, O(min) space. */
|
|
100
|
+
function levenshtein(a, b) {
|
|
101
|
+
const m = a.length;
|
|
102
|
+
const n = b.length;
|
|
103
|
+
if (m === 0)
|
|
104
|
+
return n;
|
|
105
|
+
if (n === 0)
|
|
106
|
+
return m;
|
|
107
|
+
let prev = Array.from({ length: n + 1 }, (_, i) => i);
|
|
108
|
+
let curr = new Array(n + 1).fill(0);
|
|
109
|
+
for (let i = 1; i <= m; i++) {
|
|
110
|
+
curr[0] = i;
|
|
111
|
+
for (let j = 1; j <= n; j++) {
|
|
112
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
113
|
+
curr[j] = Math.min(prev[j] + 1, // deletion
|
|
114
|
+
curr[j - 1] + 1, // insertion
|
|
115
|
+
prev[j - 1] + cost);
|
|
116
|
+
}
|
|
117
|
+
[prev, curr] = [curr, prev];
|
|
118
|
+
}
|
|
119
|
+
return prev[n];
|
|
120
|
+
}
|
|
121
|
+
const DEFAULT_THRESHOLDS = {
|
|
122
|
+
minToolJaccard: 0.7,
|
|
123
|
+
minSequenceSimilarity: 0.6,
|
|
124
|
+
maxTurnRatio: 1.5,
|
|
125
|
+
maxErrorRateIncrease: 0.2,
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Compare one candidate trajectory against a baseline across all four
|
|
129
|
+
* dimensions. Pure. Missing thresholds fall back to {@link DEFAULT_THRESHOLDS}.
|
|
130
|
+
*/
|
|
131
|
+
export function compareTrajectory(candidate, baseline, thresholds = {}) {
|
|
132
|
+
const t = { ...DEFAULT_THRESHOLDS, ...thresholds };
|
|
133
|
+
const toolJaccard = jaccard(toolSet(candidate), toolSet(baseline));
|
|
134
|
+
const seqSim = sequenceSimilarity(toolSequence(candidate), toolSequence(baseline));
|
|
135
|
+
const baseTurns = Math.max(1, baseline.turnCount ?? 0);
|
|
136
|
+
const turnRatio = (candidate.turnCount ?? 0) / baseTurns;
|
|
137
|
+
const errorRateDelta = errorRate(candidate) - errorRate(baseline);
|
|
138
|
+
const reasons = [];
|
|
139
|
+
if (toolJaccard < t.minToolJaccard) {
|
|
140
|
+
reasons.push(`tool-set drift (Jaccard ${fmt(toolJaccard)} < ${t.minToolJaccard})`);
|
|
141
|
+
}
|
|
142
|
+
if (seqSim < t.minSequenceSimilarity) {
|
|
143
|
+
reasons.push(`tool-order drift (similarity ${fmt(seqSim)} < ${t.minSequenceSimilarity})`);
|
|
144
|
+
}
|
|
145
|
+
if (turnRatio > t.maxTurnRatio) {
|
|
146
|
+
reasons.push(`turn inflation (ratio ${fmt(turnRatio)} > ${t.maxTurnRatio})`);
|
|
147
|
+
}
|
|
148
|
+
if (errorRateDelta > t.maxErrorRateIncrease) {
|
|
149
|
+
reasons.push(`error-rate spike (+${fmt(errorRateDelta)} > ${t.maxErrorRateIncrease})`);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
toolJaccard,
|
|
153
|
+
sequenceSimilarity: seqSim,
|
|
154
|
+
turnRatio,
|
|
155
|
+
errorRateDelta,
|
|
156
|
+
drifted: reasons.length > 0,
|
|
157
|
+
reasons,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Evaluate a set of candidate trajectories against one baseline. Pure.
|
|
162
|
+
* An empty candidate set yields `total: 0`, `alert: false`.
|
|
163
|
+
*/
|
|
164
|
+
export function evaluateCanary(candidates, baseline, opts = {}) {
|
|
165
|
+
const minDriftedToAlert = clampPositiveInt(opts.minDriftedToAlert, 2);
|
|
166
|
+
const comparisons = candidates.map((c) => compareTrajectory(c, baseline, opts.thresholds));
|
|
167
|
+
const driftedCount = comparisons.filter((c) => c.drifted).length;
|
|
168
|
+
const reasons = Array.from(new Set(comparisons.filter((c) => c.drifted).flatMap((c) => c.reasons)));
|
|
169
|
+
return {
|
|
170
|
+
total: comparisons.length,
|
|
171
|
+
driftedCount,
|
|
172
|
+
alert: driftedCount >= minDriftedToAlert,
|
|
173
|
+
medianToolJaccard: median(comparisons.map((c) => c.toolJaccard)),
|
|
174
|
+
medianTurnRatio: median(comparisons.map((c) => c.turnRatio)),
|
|
175
|
+
comparisons,
|
|
176
|
+
reasons,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Run a canary over an agent's stored experiments. Pure: takes the experiments
|
|
181
|
+
* and the active prompt version, returns a report. The caller supplies the data
|
|
182
|
+
* (e.g. `memory.loadExperiments` + `memory.getActivePrompt`).
|
|
183
|
+
*
|
|
184
|
+
* Baseline policy: among experiments carrying a trajectory for the ACTIVE prompt
|
|
185
|
+
* version, the earliest (by `startedAt`) is the frozen reference; every later
|
|
186
|
+
* same-version run is a candidate. Restricting to the active version is what
|
|
187
|
+
* prevents false alarms right after an intentional prompt evolution.
|
|
188
|
+
*/
|
|
189
|
+
export function runCanaryOverExperiments(agentName, experiments, activeVersion, opts = {}) {
|
|
190
|
+
const minCandidates = clampPositiveInt(opts.minCandidates, 3);
|
|
191
|
+
// Only v1 trajectories are comparable. The ExecutionTrace forward-compat
|
|
192
|
+
// contract (types.ts) requires consumers to skip unknown schema versions
|
|
193
|
+
// rather than mis-score them, so a future v2 trace is treated as not-capturable.
|
|
194
|
+
const withTrace = experiments.filter((e) => e.trajectory != null && e.trajectory.version === 1);
|
|
195
|
+
if (withTrace.length === 0) {
|
|
196
|
+
return report(agentName, activeVersion, null, 0, null, 'no-trajectory', 'No comparable (v1) trajectories captured yet — enable trace capture to run the canary.');
|
|
197
|
+
}
|
|
198
|
+
if (!activeVersion) {
|
|
199
|
+
return report(agentName, activeVersion, null, 0, null, 'insufficient-data', 'No active prompt version resolved for this agent.');
|
|
200
|
+
}
|
|
201
|
+
// Baseline strictly within the active version (staleness guard).
|
|
202
|
+
const sameVersion = withTrace
|
|
203
|
+
.filter((e) => e.promptVersion === activeVersion)
|
|
204
|
+
// Lexicographic order on ISO-8601 strings == chronological, without
|
|
205
|
+
// locale-dependent collation surprises (assumes consistent UTC timestamps).
|
|
206
|
+
.sort((a, b) => (a.startedAt < b.startedAt ? -1 : a.startedAt > b.startedAt ? 1 : 0));
|
|
207
|
+
if (sameVersion.length === 0) {
|
|
208
|
+
return report(agentName, activeVersion, null, 0, null, 'insufficient-data', `Active version ${activeVersion} has no trajectories yet (${withTrace.length} on older versions) — re-baseline in progress.`);
|
|
209
|
+
}
|
|
210
|
+
const [baseline, ...candidates] = sameVersion;
|
|
211
|
+
if (candidates.length < minCandidates) {
|
|
212
|
+
return report(agentName, activeVersion, baseline.id, candidates.length, null, 'insufficient-data', `Only ${candidates.length} candidate run(s) on ${activeVersion}; need ${minCandidates} for a verdict.`);
|
|
213
|
+
}
|
|
214
|
+
const result = evaluateCanary(candidates.map((c) => c.trajectory), baseline.trajectory, opts);
|
|
215
|
+
const status = result.alert ? 'drift' : 'ok';
|
|
216
|
+
const message = result.alert
|
|
217
|
+
? `Behavioural drift on ${agentName}/${activeVersion}: ${result.driftedCount}/${result.total} runs drifted — ${result.reasons.join('; ')}`
|
|
218
|
+
: `Stable: ${result.total} runs on ${activeVersion}, ${result.driftedCount} drifted (median tool-Jaccard ${fmt(result.medianToolJaccard)}).`;
|
|
219
|
+
return report(agentName, activeVersion, baseline.id, candidates.length, result, status, message);
|
|
220
|
+
}
|
|
221
|
+
// ─── Small pure helpers ─────────────────────────────────────
|
|
222
|
+
function report(agentName, promptVersion, baselineId, evaluated, result, status, message) {
|
|
223
|
+
return { agentName, promptVersion, baselineId, evaluated, result, status, message };
|
|
224
|
+
}
|
|
225
|
+
/** Median of a numeric array. Empty array → 0. Does not mutate the input. */
|
|
226
|
+
function median(values) {
|
|
227
|
+
if (values.length === 0)
|
|
228
|
+
return 0;
|
|
229
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
230
|
+
const mid = Math.floor(sorted.length / 2);
|
|
231
|
+
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
232
|
+
}
|
|
233
|
+
/** Coerce to a positive integer, falling back to `fallback` for NaN/≤0/non-finite. */
|
|
234
|
+
function clampPositiveInt(value, fallback) {
|
|
235
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
236
|
+
return fallback;
|
|
237
|
+
const n = Math.floor(value);
|
|
238
|
+
return n > 0 ? n : fallback;
|
|
239
|
+
}
|
|
240
|
+
/** Format a ratio/score to 2 dp for messages. */
|
|
241
|
+
function fmt(n) {
|
|
242
|
+
return n.toFixed(2);
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=canary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canary.js","sourceRoot":"","sources":["../../../src/evolution/canary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-family critic diversity check.
|
|
3
|
+
*
|
|
4
|
+
* LLM-as-judge bias is materially reduced when the critics that score an
|
|
5
|
+
* agent's output come from more than one model *family* (e.g. GPT + Claude):
|
|
6
|
+
* different training pipelines carry different blind spots, so a disagreement
|
|
7
|
+
* between families is a stronger quality signal than agreement within one.
|
|
8
|
+
*
|
|
9
|
+
* Darwin's `resolveCriticProviders` already spreads critics across families
|
|
10
|
+
* WHEN the provider keys are present — but when only one provider is
|
|
11
|
+
* configured, all three critics silently collapse onto a single family and the
|
|
12
|
+
* diversity guarantee is gone with no signal to the operator. This module makes
|
|
13
|
+
* that observable (warn by default) and enforceable (opt-in, env-gated).
|
|
14
|
+
*
|
|
15
|
+
* Important nuance: `claude-cli` and `anthropic-api` are the SAME family (both
|
|
16
|
+
* Anthropic); they differ only in latency. Real family diversity needs a
|
|
17
|
+
* non-Anthropic provider key (OpenAI, Ollama, …). The check therefore keys on
|
|
18
|
+
* the model *family*, never on the provider label.
|
|
19
|
+
*/
|
|
20
|
+
/** Minimal shape needed from a resolved critic provider entry. */
|
|
21
|
+
export interface CriticFamilyInfo {
|
|
22
|
+
/** Model family — e.g. 'anthropic', 'openai', 'ollama'. */
|
|
23
|
+
family: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CrossFamilyCheck {
|
|
26
|
+
/** Distinct, non-empty families across all critics, sorted alphabetically. */
|
|
27
|
+
families: string[];
|
|
28
|
+
/** Number of distinct families. */
|
|
29
|
+
count: number;
|
|
30
|
+
/**
|
|
31
|
+
* True when every critic shares a single family (judge bias unmitigated).
|
|
32
|
+
* A degenerate empty critic map is also reported as single-family — it is
|
|
33
|
+
* not a diversity win.
|
|
34
|
+
*/
|
|
35
|
+
singleFamily: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Count the distinct model families across a resolved critic-provider map.
|
|
39
|
+
* Pure and deterministic. Blank / non-string families are ignored so a
|
|
40
|
+
* mis-populated entry can never inflate the diversity count.
|
|
41
|
+
*/
|
|
42
|
+
export declare function checkCriticFamilies(providers: Record<string, CriticFamilyInfo>): CrossFamilyCheck;
|
|
43
|
+
/**
|
|
44
|
+
* Whether cross-family critics are *required* (hard fail) rather than merely
|
|
45
|
+
* recommended (warn). Gated by `DARWIN_REQUIRE_CROSS_FAMILY` so CI can enforce
|
|
46
|
+
* it without changing the default developer experience.
|
|
47
|
+
*/
|
|
48
|
+
export declare function crossFamilyRequired(env?: NodeJS.ProcessEnv): boolean;
|
|
49
|
+
/** Operator-facing message when the critics collapse onto one family. */
|
|
50
|
+
export declare function singleFamilyHint(check: CrossFamilyCheck): string;
|
|
51
|
+
//# sourceMappingURL=critic-families.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"critic-families.d.ts","sourceRoot":"","sources":["../../../src/evolution/critic-families.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC1C,gBAAgB,CAclB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAGjF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAOhE"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-family critic diversity check.
|
|
3
|
+
*
|
|
4
|
+
* LLM-as-judge bias is materially reduced when the critics that score an
|
|
5
|
+
* agent's output come from more than one model *family* (e.g. GPT + Claude):
|
|
6
|
+
* different training pipelines carry different blind spots, so a disagreement
|
|
7
|
+
* between families is a stronger quality signal than agreement within one.
|
|
8
|
+
*
|
|
9
|
+
* Darwin's `resolveCriticProviders` already spreads critics across families
|
|
10
|
+
* WHEN the provider keys are present — but when only one provider is
|
|
11
|
+
* configured, all three critics silently collapse onto a single family and the
|
|
12
|
+
* diversity guarantee is gone with no signal to the operator. This module makes
|
|
13
|
+
* that observable (warn by default) and enforceable (opt-in, env-gated).
|
|
14
|
+
*
|
|
15
|
+
* Important nuance: `claude-cli` and `anthropic-api` are the SAME family (both
|
|
16
|
+
* Anthropic); they differ only in latency. Real family diversity needs a
|
|
17
|
+
* non-Anthropic provider key (OpenAI, Ollama, …). The check therefore keys on
|
|
18
|
+
* the model *family*, never on the provider label.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Count the distinct model families across a resolved critic-provider map.
|
|
22
|
+
* Pure and deterministic. Blank / non-string families are ignored so a
|
|
23
|
+
* mis-populated entry can never inflate the diversity count.
|
|
24
|
+
*/
|
|
25
|
+
export function checkCriticFamilies(providers) {
|
|
26
|
+
const families = Array.from(new Set(Object.values(providers)
|
|
27
|
+
.map((p) => (typeof p?.family === 'string' ? p.family.trim().toLowerCase() : ''))
|
|
28
|
+
.filter((f) => f.length > 0))).sort();
|
|
29
|
+
return {
|
|
30
|
+
families,
|
|
31
|
+
count: families.length,
|
|
32
|
+
singleFamily: families.length < 2,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Whether cross-family critics are *required* (hard fail) rather than merely
|
|
37
|
+
* recommended (warn). Gated by `DARWIN_REQUIRE_CROSS_FAMILY` so CI can enforce
|
|
38
|
+
* it without changing the default developer experience.
|
|
39
|
+
*/
|
|
40
|
+
export function crossFamilyRequired(env = process.env) {
|
|
41
|
+
const v = env.DARWIN_REQUIRE_CROSS_FAMILY;
|
|
42
|
+
return v === '1' || v === 'true' || v === 'yes';
|
|
43
|
+
}
|
|
44
|
+
/** Operator-facing message when the critics collapse onto one family. */
|
|
45
|
+
export function singleFamilyHint(check) {
|
|
46
|
+
const fam = check.families[0] ?? 'unknown';
|
|
47
|
+
return (`All critics run on one model family (${fam}). ` +
|
|
48
|
+
`Cross-family critique reduces LLM-as-judge bias — set OPENAI_API_KEY ` +
|
|
49
|
+
`(or another non-Anthropic provider key) so a second family scores in parallel.`);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=critic-families.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"critic-families.js","sourceRoot":"","sources":["../../../src/evolution/critic-families.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAqBH;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAA2C;IAE3C,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CACzB,IAAI,GAAG,CACL,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAChF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAC/B,CACF,CAAC,IAAI,EAAE,CAAC;IAET,OAAO;QACL,QAAQ;QACR,KAAK,EAAE,QAAQ,CAAC,MAAM;QACtB,YAAY,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC;KAClC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACtE,MAAM,CAAC,GAAG,GAAG,CAAC,2BAA2B,CAAC;IAC1C,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,KAAK,CAAC;AAClD,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,KAAuB;IACtD,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;IAC3C,OAAO,CACL,wCAAwC,GAAG,KAAK;QAChD,uEAAuE;QACvE,gFAAgF,CACjF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Darwin — Persisted Evolution Enable/Disable State
|
|
3
|
+
*
|
|
4
|
+
* `darwin evolve <agent> --enable|--disable` used to mutate the in-memory
|
|
5
|
+
* agent singleton (`agent.evolution.enabled = true`) and never persist it,
|
|
6
|
+
* so the flag was lost the moment the process exited — and `darwin run`
|
|
7
|
+
* then read the STATIC source default again. This module makes the toggle
|
|
8
|
+
* durable by recording it in {@link DarwinState.evolutionEnabled} (the same
|
|
9
|
+
* JSON state blob every backend already round-trips) and resolving the
|
|
10
|
+
* effective value with the persisted override winning over the static
|
|
11
|
+
* default.
|
|
12
|
+
*/
|
|
13
|
+
import type { AgentDefinition, DarwinState, EvolutionConfig, EvolutionConfigOverride, MemoryProvider } from '../types.js';
|
|
14
|
+
/**
|
|
15
|
+
* The effective "is evolution enabled for this agent" decision.
|
|
16
|
+
*
|
|
17
|
+
* Resolution order:
|
|
18
|
+
* 1. A persisted override in `state.evolutionEnabled[agentName]` (set by
|
|
19
|
+
* `darwin evolve --enable/--disable`) — wins when present.
|
|
20
|
+
* 2. The agent definition's static `evolution.enabled` default.
|
|
21
|
+
* 3. `false` when the agent has no `evolution` block at all.
|
|
22
|
+
*
|
|
23
|
+
* Read defensively: state written before the `evolutionEnabled` field
|
|
24
|
+
* existed simply lacks the key, in which case rule 1 is skipped and the
|
|
25
|
+
* static default applies — so old installs keep their previous behaviour.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveEvolutionEnabled(agent: AgentDefinition, state: Pick<DarwinState, 'evolutionEnabled'>): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Persist the enable/disable override for an agent atomically.
|
|
30
|
+
*
|
|
31
|
+
* Uses {@link MemoryProvider.updateState} so a concurrent writer cannot lose
|
|
32
|
+
* the update, and initialises the `evolutionEnabled` map lazily for state
|
|
33
|
+
* rows that predate the field.
|
|
34
|
+
*/
|
|
35
|
+
export declare function setEvolutionEnabled(memory: MemoryProvider, agentName: string, enabled: boolean): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the effective {@link EvolutionConfig} for an agent: the static
|
|
38
|
+
* definition's `evolution` block with any persisted override
|
|
39
|
+
* ({@link DarwinState.evolutionConfigOverrides}) merged ON TOP, then any
|
|
40
|
+
* in-this-process CLI override merged last (e.g. `darwin run … --gepa`).
|
|
41
|
+
*
|
|
42
|
+
* Only the five advanced flags are overridable; every other field
|
|
43
|
+
* (`enabled`, `metrics`, `minRuns`, …) is taken verbatim from the static
|
|
44
|
+
* config. Returns `undefined` when the agent has no `evolution` block AND no
|
|
45
|
+
* persisted/CLI override would create one — overriding a flag on an agent that
|
|
46
|
+
* never opted into evolution is meaningless.
|
|
47
|
+
*
|
|
48
|
+
* Read defensively: undefined override values are skipped so they never clobber
|
|
49
|
+
* a static default with `undefined`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveEvolutionConfig(agent: AgentDefinition, state: Pick<DarwinState, 'evolutionConfigOverrides'>, cliOverride?: EvolutionConfigOverride): EvolutionConfig | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Persist advanced-config overrides for an agent atomically, merging the given
|
|
54
|
+
* partial into any existing override map (so toggling `--gepa` does not wipe a
|
|
55
|
+
* previously-set `--reflection-model`). Initialises the map lazily for state
|
|
56
|
+
* rows that predate the field.
|
|
57
|
+
*/
|
|
58
|
+
export declare function setEvolutionConfigOverrides(memory: MemoryProvider, agentName: string, partial: EvolutionConfigOverride): Promise<void>;
|
|
59
|
+
//# sourceMappingURL=enabled-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enabled-state.d.ts","sourceRoot":"","sources":["../../../src/evolution/enabled-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,eAAe,EACf,uBAAuB,EACvB,cAAc,EACf,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,GAC3C,OAAO,CAUT;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC,CAQf;AAaD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,0BAA0B,CAAC,EACpD,WAAW,CAAC,EAAE,uBAAuB,GACpC,eAAe,GAAG,SAAS,CA8B7B;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Darwin — Persisted Evolution Enable/Disable State
|
|
3
|
+
*
|
|
4
|
+
* `darwin evolve <agent> --enable|--disable` used to mutate the in-memory
|
|
5
|
+
* agent singleton (`agent.evolution.enabled = true`) and never persist it,
|
|
6
|
+
* so the flag was lost the moment the process exited — and `darwin run`
|
|
7
|
+
* then read the STATIC source default again. This module makes the toggle
|
|
8
|
+
* durable by recording it in {@link DarwinState.evolutionEnabled} (the same
|
|
9
|
+
* JSON state blob every backend already round-trips) and resolving the
|
|
10
|
+
* effective value with the persisted override winning over the static
|
|
11
|
+
* default.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The effective "is evolution enabled for this agent" decision.
|
|
15
|
+
*
|
|
16
|
+
* Resolution order:
|
|
17
|
+
* 1. A persisted override in `state.evolutionEnabled[agentName]` (set by
|
|
18
|
+
* `darwin evolve --enable/--disable`) — wins when present.
|
|
19
|
+
* 2. The agent definition's static `evolution.enabled` default.
|
|
20
|
+
* 3. `false` when the agent has no `evolution` block at all.
|
|
21
|
+
*
|
|
22
|
+
* Read defensively: state written before the `evolutionEnabled` field
|
|
23
|
+
* existed simply lacks the key, in which case rule 1 is skipped and the
|
|
24
|
+
* static default applies — so old installs keep their previous behaviour.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveEvolutionEnabled(agent, state) {
|
|
27
|
+
const persisted = state.evolutionEnabled?.[agent.name];
|
|
28
|
+
if (typeof persisted === 'boolean') {
|
|
29
|
+
// A persisted `true` only takes effect when the agent actually carries an
|
|
30
|
+
// `evolution` config block — the run/critic loop needs an evaluator and
|
|
31
|
+
// metrics to act on. Enabling an agent that has no evolution config is a
|
|
32
|
+
// no-op (returns false), not a crash. A persisted `false` always disables.
|
|
33
|
+
return persisted && agent.evolution !== undefined;
|
|
34
|
+
}
|
|
35
|
+
return agent.evolution?.enabled ?? false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Persist the enable/disable override for an agent atomically.
|
|
39
|
+
*
|
|
40
|
+
* Uses {@link MemoryProvider.updateState} so a concurrent writer cannot lose
|
|
41
|
+
* the update, and initialises the `evolutionEnabled` map lazily for state
|
|
42
|
+
* rows that predate the field.
|
|
43
|
+
*/
|
|
44
|
+
export async function setEvolutionEnabled(memory, agentName, enabled) {
|
|
45
|
+
await memory.updateState((state) => {
|
|
46
|
+
if (!state.evolutionEnabled) {
|
|
47
|
+
state.evolutionEnabled = {};
|
|
48
|
+
}
|
|
49
|
+
state.evolutionEnabled[agentName] = enabled;
|
|
50
|
+
return state;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// ─── Advanced evolution-config overrides (useGepa / useMerge / …) ───
|
|
54
|
+
/** The keys the CLI is allowed to override + persist. */
|
|
55
|
+
const OVERRIDE_KEYS = [
|
|
56
|
+
'useGepa',
|
|
57
|
+
'useMerge',
|
|
58
|
+
'paretoGate',
|
|
59
|
+
'useCoverage',
|
|
60
|
+
'reflectionModel',
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the effective {@link EvolutionConfig} for an agent: the static
|
|
64
|
+
* definition's `evolution` block with any persisted override
|
|
65
|
+
* ({@link DarwinState.evolutionConfigOverrides}) merged ON TOP, then any
|
|
66
|
+
* in-this-process CLI override merged last (e.g. `darwin run … --gepa`).
|
|
67
|
+
*
|
|
68
|
+
* Only the five advanced flags are overridable; every other field
|
|
69
|
+
* (`enabled`, `metrics`, `minRuns`, …) is taken verbatim from the static
|
|
70
|
+
* config. Returns `undefined` when the agent has no `evolution` block AND no
|
|
71
|
+
* persisted/CLI override would create one — overriding a flag on an agent that
|
|
72
|
+
* never opted into evolution is meaningless.
|
|
73
|
+
*
|
|
74
|
+
* Read defensively: undefined override values are skipped so they never clobber
|
|
75
|
+
* a static default with `undefined`.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveEvolutionConfig(agent, state, cliOverride) {
|
|
78
|
+
const persisted = state.evolutionConfigOverrides?.[agent.name];
|
|
79
|
+
const base = agent.evolution;
|
|
80
|
+
if (!base && !persisted && !cliOverride) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
// Start from the static config (or a disabled stub when the only reason we
|
|
84
|
+
// are here is an override — the loop still gates on `enabled`, which stays
|
|
85
|
+
// false unless the static config or the separate enable-toggle turns it on).
|
|
86
|
+
const resolved = base ? { ...base } : { enabled: false };
|
|
87
|
+
for (const layer of [persisted, cliOverride]) {
|
|
88
|
+
if (!layer)
|
|
89
|
+
continue;
|
|
90
|
+
for (const key of OVERRIDE_KEYS) {
|
|
91
|
+
const value = layer[key];
|
|
92
|
+
if (value !== undefined) {
|
|
93
|
+
// Each key is assigned to its own matching field — typed per-key to
|
|
94
|
+
// keep `any` out of the codebase.
|
|
95
|
+
if (key === 'reflectionModel') {
|
|
96
|
+
resolved.reflectionModel = value;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
resolved[key] = value;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return resolved;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Persist advanced-config overrides for an agent atomically, merging the given
|
|
108
|
+
* partial into any existing override map (so toggling `--gepa` does not wipe a
|
|
109
|
+
* previously-set `--reflection-model`). Initialises the map lazily for state
|
|
110
|
+
* rows that predate the field.
|
|
111
|
+
*/
|
|
112
|
+
export async function setEvolutionConfigOverrides(memory, agentName, partial) {
|
|
113
|
+
await memory.updateState((state) => {
|
|
114
|
+
if (!state.evolutionConfigOverrides) {
|
|
115
|
+
state.evolutionConfigOverrides = {};
|
|
116
|
+
}
|
|
117
|
+
const existing = state.evolutionConfigOverrides[agentName] ?? {};
|
|
118
|
+
state.evolutionConfigOverrides[agentName] = { ...existing, ...partial };
|
|
119
|
+
return state;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=enabled-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enabled-state.js","sourceRoot":"","sources":["../../../src/evolution/enabled-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAsB,EACtB,KAA4C;IAE5C,MAAM,SAAS,GAAG,KAAK,CAAC,gBAAgB,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvD,IAAI,OAAO,SAAS,KAAK,SAAS,EAAE,CAAC;QACnC,0EAA0E;QAC1E,wEAAwE;QACxE,yEAAyE;QACzE,2EAA2E;QAC3E,OAAO,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC;IACpD,CAAC;IACD,OAAO,KAAK,CAAC,SAAS,EAAE,OAAO,IAAI,KAAK,CAAC;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAsB,EACtB,SAAiB,EACjB,OAAgB;IAEhB,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,EAAE;QACjC,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC;YAC5B,KAAK,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC9B,CAAC;QACD,KAAK,CAAC,gBAAgB,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC;AAED,uEAAuE;AAEvE,yDAAyD;AACzD,MAAM,aAAa,GAAG;IACpB,SAAS;IACT,UAAU;IACV,YAAY;IACZ,aAAa;IACb,iBAAiB;CACT,CAAC;AAEX;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAsB,EACtB,KAAoD,EACpD,WAAqC;IAErC,MAAM,SAAS,GAAG,KAAK,CAAC,wBAAwB,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC;IAE7B,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,WAAW,EAAE,CAAC;QACxC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,2EAA2E;IAC3E,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,QAAQ,GAAoB,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAE1E,KAAK,MAAM,KAAK,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,oEAAoE;gBACpE,kCAAkC;gBAClC,IAAI,GAAG,KAAK,iBAAiB,EAAE,CAAC;oBAC9B,QAAQ,CAAC,eAAe,GAAG,KAAe,CAAC;gBAC7C,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAgB,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAsB,EACtB,SAAiB,EACjB,OAAgC;IAEhC,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,EAAE;QACjC,IAAI,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAC;YACpC,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC;QACtC,CAAC;QACD,MAAM,QAAQ,GAAG,KAAK,CAAC,wBAAwB,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QACjE,KAAK,CAAC,wBAAwB,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -83,6 +83,34 @@ export declare class DarwinLoop {
|
|
|
83
83
|
* 5. Evolution trigger (if enough data and no active test)
|
|
84
84
|
*/
|
|
85
85
|
afterRun(experiment: DarwinExperiment): Promise<EvolutionResult>;
|
|
86
|
+
/**
|
|
87
|
+
* Manual / on-demand evolution trigger — the engine behind
|
|
88
|
+
* `darwin evolve <agent> --force`.
|
|
89
|
+
*
|
|
90
|
+
* Runs the SAME variant-generation + A/B-start path as the automatic loop's
|
|
91
|
+
* Step 5, but WITHOUT the "enough runs / actionable patterns / data-quality"
|
|
92
|
+
* gates. Use it to deliberately kick off an optimisation from the current
|
|
93
|
+
* best prompt using the experiments collected so far, instead of waiting for
|
|
94
|
+
* the loop to decide on its own.
|
|
95
|
+
*
|
|
96
|
+
* It still refuses the cases where evolution is genuinely impossible or
|
|
97
|
+
* unsafe:
|
|
98
|
+
* - no active prompt seeded yet (nothing to mutate from),
|
|
99
|
+
* - no recorded experiments (the optimizer has nothing to learn from),
|
|
100
|
+
* - an A/B test already running (can't start a second concurrent test).
|
|
101
|
+
*
|
|
102
|
+
* Patterns are still DETECTED (so the change reason and the GEPA/legacy
|
|
103
|
+
* feedback are meaningful) — they are simply not used as a gate.
|
|
104
|
+
*/
|
|
105
|
+
forceEvolve(agentName: string): Promise<EvolutionResult>;
|
|
106
|
+
/**
|
|
107
|
+
* Shared tail of the evolution cycle: generate one challenger prompt (GEPA
|
|
108
|
+
* reflective path when opted in, else the legacy meta-prompt optimizer),
|
|
109
|
+
* persist it as a new version, and start an A/B test against the incumbent.
|
|
110
|
+
* Called by both the gated automatic loop ({@link afterRun}) and the
|
|
111
|
+
* on-demand {@link forceEvolve}. Mutates and returns the passed `result`.
|
|
112
|
+
*/
|
|
113
|
+
private generateAndStartABTest;
|
|
86
114
|
private handleABTest;
|
|
87
115
|
/**
|
|
88
116
|
* Roll back to the last known good prompt version.
|
|
@@ -159,6 +187,24 @@ export declare class DarwinLoop {
|
|
|
159
187
|
* runs {@link runAlignmentGuard} on it.
|
|
160
188
|
*/
|
|
161
189
|
private tryMergeVariant;
|
|
190
|
+
/**
|
|
191
|
+
* v0.7.0 — GEPA Algorithm 2 instance-wise coverage selection.
|
|
192
|
+
*
|
|
193
|
+
* Builds a {@link ScoredVariant} per RUN prompt-version (its averaged
|
|
194
|
+
* objective vector + its per-task-type composite map from {@link
|
|
195
|
+
* ExperimentTracker.getPerKeyScoresByCategory}), then asks the
|
|
196
|
+
* {@link GepaOptimizer#nextGeneration} coverage path to pick the survivor
|
|
197
|
+
* that wins on the MOST DIFFERENT task types. Returns that survivor's prompt
|
|
198
|
+
* text — the version to reflect the next challenger from.
|
|
199
|
+
*
|
|
200
|
+
* Returns `null` (→ caller reflects from the active prompt as before) when no
|
|
201
|
+
* optimizer is wired or fewer than two versions carry per-task-type data —
|
|
202
|
+
* coverage selection is meaningless with a single covered version. This is
|
|
203
|
+
* the ONLINE adaptation: it selects the reflection PARENT among already-run
|
|
204
|
+
* versions by their real per-task-type scores. (Per-challenger coverage of
|
|
205
|
+
* unrun candidates needs the offline scored-pool path and is out of scope.)
|
|
206
|
+
*/
|
|
207
|
+
private selectCoverageParent;
|
|
162
208
|
/**
|
|
163
209
|
* v0.6.0 — Build GEPA {@link ReflectiveFeedback} from the most recent
|
|
164
210
|
* experiments that carry critic feedback. Each becomes a (variantId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../../src/evolution/loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAEV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,cAAc,
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../../src/evolution/loop.ts"],"names":[],"mappings":"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"}
|