@hmharness/evolution 0.14.5 → 0.14.9
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/dist/bench.d.ts.v2bak.v2bak +44 -0
- package/dist/bench.js.v2bak.v2bak +156 -0
- package/dist/candidates.d.ts.v2bak.v2bak +114 -0
- package/dist/candidates.js.v2bak.v2bak +232 -0
- package/dist/dataset.d.ts.v2bak.v2bak +64 -0
- package/dist/dataset.js.v2bak.v2bak +184 -0
- package/dist/evolve.d.ts +14 -0
- package/dist/evolve.d.ts.v2bak.v2bak +98 -0
- package/dist/evolve.js +36 -15
- package/dist/evolve.js.v2bak.v2bak +573 -0
- package/dist/impact.d.ts.v2bak.v2bak +77 -0
- package/dist/impact.js.v2bak.v2bak +214 -0
- package/dist/index.d.ts.v2bak.v2bak +16 -0
- package/dist/index.js.v2bak.v2bak +16 -0
- package/dist/insights.d.ts.v2bak.v2bak +18 -0
- package/dist/insights.js.v2bak.v2bak +80 -0
- package/dist/knowledge.d.ts.v2bak.v2bak +15 -0
- package/dist/knowledge.js.v2bak.v2bak +145 -0
- package/dist/labels.d.ts.v2bak.v2bak +20 -0
- package/dist/labels.js.v2bak.v2bak +57 -0
- package/dist/memory.d.ts.v2bak.v2bak +32 -0
- package/dist/memory.js.v2bak.v2bak +233 -0
- package/dist/patches.d.ts.v2bak.v2bak +83 -0
- package/dist/patches.js +6 -2
- package/dist/patches.js.v2bak.v2bak +253 -0
- package/dist/radar.d.ts.v2bak.v2bak +3 -0
- package/dist/radar.js.v2bak.v2bak +40 -0
- package/dist/ranker.d.ts.v2bak.v2bak +44 -0
- package/dist/ranker.js.v2bak.v2bak +55 -0
- package/dist/readiness.d.ts.v2bak.v2bak +15 -0
- package/dist/readiness.js +14 -2
- package/dist/readiness.js.v2bak.v2bak +167 -0
- package/dist/skillpayload.d.ts.v2bak.v2bak +1 -0
- package/dist/skillpayload.js.v2bak +28 -0
- package/dist/skillpayload.js.v2bak.v2bak.v2bak +28 -0
- package/dist/skills.d.ts.v2bak.v2bak +54 -0
- package/dist/skills.js.v2bak.v2bak +321 -0
- package/dist/workflows.d.ts.v2bak.v2bak +28 -0
- package/dist/workflows.js.v2bak.v2bak +84 -0
- package/package.json +1 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const RANK_WEIGHTS = {
|
|
2
|
+
relevance: 0.30,
|
|
3
|
+
dependency: 0.20,
|
|
4
|
+
recency: 0.15,
|
|
5
|
+
importance: 0.15,
|
|
6
|
+
similarity: 0.10,
|
|
7
|
+
tokenCost: -0.10,
|
|
8
|
+
};
|
|
9
|
+
const clamp01 = (n) => Number.isFinite(n) ? Math.max(0, Math.min(1, n)) : 0;
|
|
10
|
+
/** Rank candidates: fixed weights, token cost normalized to the batch max,
|
|
11
|
+
* ties broken by cheaper-first. Returns a NEW sorted array. */
|
|
12
|
+
export function rankContext(candidates) {
|
|
13
|
+
const maxCost = Math.max(1, ...candidates.map((c) => c.tokenCost));
|
|
14
|
+
return candidates
|
|
15
|
+
.map((c) => {
|
|
16
|
+
const n = {
|
|
17
|
+
relevance: clamp01(c.relevance),
|
|
18
|
+
dependency: clamp01(c.dependency),
|
|
19
|
+
recency: clamp01(c.recency),
|
|
20
|
+
importance: clamp01(c.importance),
|
|
21
|
+
similarity: clamp01(c.similarity),
|
|
22
|
+
tokenCost: clamp01(c.tokenCost / maxCost),
|
|
23
|
+
};
|
|
24
|
+
const score = RANK_WEIGHTS.relevance * n.relevance +
|
|
25
|
+
RANK_WEIGHTS.dependency * n.dependency +
|
|
26
|
+
RANK_WEIGHTS.recency * n.recency +
|
|
27
|
+
RANK_WEIGHTS.importance * n.importance +
|
|
28
|
+
RANK_WEIGHTS.similarity * n.similarity +
|
|
29
|
+
RANK_WEIGHTS.tokenCost * n.tokenCost;
|
|
30
|
+
return { ...c, score: Math.round(score * 1000) / 1000 };
|
|
31
|
+
})
|
|
32
|
+
.sort((a, b) => b.score - a.score || a.tokenCost - b.tokenCost);
|
|
33
|
+
}
|
|
34
|
+
/** Pick the top-K candidates under a token budget (greedy, ranked order). */
|
|
35
|
+
export function packContext(candidates, budgetTokens) {
|
|
36
|
+
const ranked = rankContext(candidates);
|
|
37
|
+
const out = [];
|
|
38
|
+
let used = 0;
|
|
39
|
+
for (const c of ranked) {
|
|
40
|
+
if (used + c.tokenCost > budgetTokens)
|
|
41
|
+
continue;
|
|
42
|
+
out.push(c);
|
|
43
|
+
used += c.tokenCost;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
export function classifyMemory(entry) {
|
|
48
|
+
if (/^\(distilled\)/.test(entry) || /api|sdk|版本|参数/.test(entry.slice(0, 80)))
|
|
49
|
+
return 'semantic';
|
|
50
|
+
if (/design|架构|决策|why|adr/i.test(entry.slice(0, 80)))
|
|
51
|
+
return 'project';
|
|
52
|
+
if (/self-note|工具|失败|重试|how to|怎么/.test(entry.slice(0, 80)))
|
|
53
|
+
return 'procedural';
|
|
54
|
+
return 'episodic';
|
|
55
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ReadinessCondition {
|
|
2
|
+
id: string;
|
|
3
|
+
met: boolean;
|
|
4
|
+
current: number | string | boolean;
|
|
5
|
+
threshold: number | string;
|
|
6
|
+
evidence: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ReadinessReport {
|
|
9
|
+
verdict: 'rl-eligible' | 'optimize-first';
|
|
10
|
+
conditions: ReadinessCondition[];
|
|
11
|
+
/** when optimize-first: which lever to pull first (weakest condition) */
|
|
12
|
+
recommendedLever?: 'data-collection' | 'bench-expansion' | 'reward-calibration' | 'eval-suite-stabilization' | 'version-provenance';
|
|
13
|
+
at: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function rlReadiness(home: string): Promise<ReadinessReport>;
|
package/dist/readiness.js
CHANGED
|
@@ -107,8 +107,20 @@ export async function rlReadiness(home) {
|
|
|
107
107
|
workflows: false,
|
|
108
108
|
};
|
|
109
109
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
// skills layout has BOTH shapes in the wild: legacy flat `skills/<name>.md`
|
|
111
|
+
// and current `skills/active/<name>/` directories (plus `skills/drafts/*.md`).
|
|
112
|
+
// A top-level-only .md check silently fails every modern install.
|
|
113
|
+
const entries = await readdir(join(home, 'skills'), { withFileTypes: true });
|
|
114
|
+
const activeDir = entries.find((e) => e.isDirectory() && e.name === 'active');
|
|
115
|
+
const draftsDir = entries.find((e) => e.isDirectory() && e.name === 'drafts');
|
|
116
|
+
const flatMd = entries.some((e) => e.isFile() && e.name.endsWith('.md'));
|
|
117
|
+
let active = 0;
|
|
118
|
+
if (activeDir)
|
|
119
|
+
active = (await readdir(join(home, 'skills', 'active'))).length;
|
|
120
|
+
let draftMd = 0;
|
|
121
|
+
if (draftsDir)
|
|
122
|
+
draftMd = (await readdir(join(home, 'skills', 'drafts'))).filter((f) => f.endsWith('.md')).length;
|
|
123
|
+
provenance.skills = flatMd || active > 0 || draftMd > 0;
|
|
112
124
|
}
|
|
113
125
|
catch { /* none */ }
|
|
114
126
|
try {
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hmharness/evolution - readiness (V2 M11: RL 前置条件检查器)
|
|
3
|
+
* The blueprint's gate: RL is allowed ONLY when all six conditions hold.
|
|
4
|
+
* Every condition is measured from real on-disk evidence - nothing is
|
|
5
|
+
* estimated, nothing can be waved through. When the gate is closed the
|
|
6
|
+
* report says exactly which optimization to do instead. See ADR-0005.
|
|
7
|
+
*/
|
|
8
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { listCases } from "./bench.js";
|
|
11
|
+
const QUALITY_MIN_REWARD = 0.5;
|
|
12
|
+
async function countRunTrajectories(home) {
|
|
13
|
+
const root = join(home, 'runs');
|
|
14
|
+
let ids = [];
|
|
15
|
+
try {
|
|
16
|
+
ids = (await readdir(root)).filter((d) => !d.startsWith('.'));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return { total: 0, highQuality: 0 };
|
|
20
|
+
}
|
|
21
|
+
let high = 0;
|
|
22
|
+
for (const id of ids) {
|
|
23
|
+
try {
|
|
24
|
+
const s = JSON.parse(await readFile(join(root, id, 'summary.json'), 'utf8'));
|
|
25
|
+
const outcome = typeof s.outcome === 'string' ? s.outcome : (s.outcome?.success ? 'ok' : String(s.outcome?.reason ?? ''));
|
|
26
|
+
const uses = Number(s.toolUses ?? s.metrics?.toolUses ?? 0);
|
|
27
|
+
const fails = Number(s.toolFailures ?? s.metrics?.toolFailures ?? 0);
|
|
28
|
+
const failRate = uses > 0 ? fails / uses : 0;
|
|
29
|
+
const reward = outcome === 'ok' ? 1 - Math.min(0.6, Math.round(failRate * 10) / 10) : 0.3;
|
|
30
|
+
if (outcome && (s.turns ?? s.metrics?.turns ?? 0) >= 1 && reward >= QUALITY_MIN_REWARD)
|
|
31
|
+
high++;
|
|
32
|
+
}
|
|
33
|
+
catch { /* torn */ }
|
|
34
|
+
}
|
|
35
|
+
return { total: ids.length, highQuality: high };
|
|
36
|
+
}
|
|
37
|
+
export async function rlReadiness(home) {
|
|
38
|
+
const conditions = [];
|
|
39
|
+
// 1. >= 1000 high-quality trajectories
|
|
40
|
+
const runs = await countRunTrajectories(home);
|
|
41
|
+
conditions.push({
|
|
42
|
+
id: 'high-quality-trajectories',
|
|
43
|
+
met: runs.highQuality >= 1000,
|
|
44
|
+
current: runs.highQuality,
|
|
45
|
+
threshold: 1000,
|
|
46
|
+
evidence: `runs/ scan: ${runs.total} trajectories, ${runs.highQuality} with outcome + turns>=1 + reward>=${QUALITY_MIN_REWARD}`,
|
|
47
|
+
});
|
|
48
|
+
// 2. >= 100 stable benchmark tasks
|
|
49
|
+
let cases = 0;
|
|
50
|
+
try {
|
|
51
|
+
cases = (await listCases(home)).length;
|
|
52
|
+
}
|
|
53
|
+
catch { /* none */ }
|
|
54
|
+
conditions.push({
|
|
55
|
+
id: 'stable-benchmark-tasks',
|
|
56
|
+
met: cases >= 100,
|
|
57
|
+
current: cases,
|
|
58
|
+
threshold: 100,
|
|
59
|
+
evidence: `bench/cases/ scan: ${cases} cases (holdout included; stability tracked by bench history)`,
|
|
60
|
+
});
|
|
61
|
+
// 3. reward vs human judgment correlation VERIFIED - no human-labeling
|
|
62
|
+
// channel exists yet, so this is honestly not met (never a guess)
|
|
63
|
+
let humanLabels = 0;
|
|
64
|
+
try {
|
|
65
|
+
const f = await stat(join(home, 'evolution', 'reward-human-labels.jsonl'));
|
|
66
|
+
if (f.isFile())
|
|
67
|
+
humanLabels = (await readFile(join(home, 'evolution', 'reward-human-labels.jsonl'), 'utf8')).split('\n').filter(Boolean).length;
|
|
68
|
+
}
|
|
69
|
+
catch { /* absent */ }
|
|
70
|
+
conditions.push({
|
|
71
|
+
id: 'reward-human-correlation',
|
|
72
|
+
met: humanLabels >= 100,
|
|
73
|
+
current: humanLabels,
|
|
74
|
+
threshold: 100,
|
|
75
|
+
evidence: 'evolution/reward-human-labels.jsonl (human-scored samples; dataset label field reserves the slot)',
|
|
76
|
+
});
|
|
77
|
+
// 4. evaluation regression suite STABLE - last two bench records, no regression
|
|
78
|
+
let suiteStable = false;
|
|
79
|
+
let suiteEvidence = 'no bench history records (evolution/benches/ empty)';
|
|
80
|
+
try {
|
|
81
|
+
const dir = join(home, 'evolution', 'benches');
|
|
82
|
+
const files = (await readdir(dir)).filter((f) => f.endsWith('.json')).sort();
|
|
83
|
+
if (files.length >= 2) {
|
|
84
|
+
const last = JSON.parse(await readFile(join(dir, files[files.length - 1]), 'utf8'));
|
|
85
|
+
const prev = JSON.parse(await readFile(join(dir, files[files.length - 2]), 'utf8'));
|
|
86
|
+
if (typeof last.passRate === 'number' && typeof prev.passRate === 'number') {
|
|
87
|
+
suiteStable = last.passRate >= prev.passRate - 0.05;
|
|
88
|
+
suiteEvidence = `last two bench passRates: ${(prev.passRate * 100).toFixed(0)}% -> ${(last.passRate * 100).toFixed(0)}% (tolerance -5%)`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else if (files.length === 1) {
|
|
92
|
+
suiteEvidence = 'only one bench record - stability needs at least two';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch { /* absent */ }
|
|
96
|
+
conditions.push({
|
|
97
|
+
id: 'eval-regression-stable',
|
|
98
|
+
met: suiteStable,
|
|
99
|
+
current: suiteStable,
|
|
100
|
+
threshold: 'no >5% passRate regression across last two bench runs',
|
|
101
|
+
evidence: suiteEvidence,
|
|
102
|
+
});
|
|
103
|
+
// 5. model/skill/workflow version provenance
|
|
104
|
+
const provenance = {
|
|
105
|
+
model: true, // every run's summary carries model; rollout session_meta pins it
|
|
106
|
+
skills: false,
|
|
107
|
+
workflows: false,
|
|
108
|
+
};
|
|
109
|
+
try {
|
|
110
|
+
// skills layout has BOTH shapes in the wild: legacy flat `skills/<name>.md`
|
|
111
|
+
// and current `skills/active/<name>/` directories (plus `skills/drafts/*.md`).
|
|
112
|
+
// A top-level-only .md check silently fails every modern install.
|
|
113
|
+
const entries = await readdir(join(home, 'skills'), { withFileTypes: true });
|
|
114
|
+
const activeDir = entries.find((e) => e.isDirectory() && e.name === 'active');
|
|
115
|
+
const draftsDir = entries.find((e) => e.isDirectory() && e.name === 'drafts');
|
|
116
|
+
const flatMd = entries.some((e) => e.isFile() && e.name.endsWith('.md'));
|
|
117
|
+
let active = 0;
|
|
118
|
+
if (activeDir)
|
|
119
|
+
active = (await readdir(join(home, 'skills', 'active'))).length;
|
|
120
|
+
let draftMd = 0;
|
|
121
|
+
if (draftsDir)
|
|
122
|
+
draftMd = (await readdir(join(home, 'skills', 'drafts'))).filter((f) => f.endsWith('.md')).length;
|
|
123
|
+
provenance.skills = flatMd || active > 0 || draftMd > 0;
|
|
124
|
+
}
|
|
125
|
+
catch { /* none */ }
|
|
126
|
+
try {
|
|
127
|
+
const wf = await readdir(join(home, 'evolution', 'workflows'));
|
|
128
|
+
provenance.workflows = wf.length > 0;
|
|
129
|
+
}
|
|
130
|
+
catch { /* none */ }
|
|
131
|
+
conditions.push({
|
|
132
|
+
id: 'version-provenance',
|
|
133
|
+
met: provenance.skills && provenance.workflows,
|
|
134
|
+
current: provenance.skills && provenance.workflows,
|
|
135
|
+
threshold: 'skills + workflows under version control (model provenance ships with every run)',
|
|
136
|
+
evidence: `skills dir: ${provenance.skills ? 'versioned .md files' : 'empty'}; workflows dir: ${provenance.workflows ? 'present' : 'empty'}`,
|
|
137
|
+
});
|
|
138
|
+
// 6. offline evaluation + holdout set exist
|
|
139
|
+
let holdout = 0;
|
|
140
|
+
try {
|
|
141
|
+
holdout = (await listCases(home)).filter((c) => c.holdout).length;
|
|
142
|
+
}
|
|
143
|
+
catch { /* none */ }
|
|
144
|
+
conditions.push({
|
|
145
|
+
id: 'offline-eval-holdout',
|
|
146
|
+
met: holdout > 0 && cases > 0,
|
|
147
|
+
current: holdout,
|
|
148
|
+
threshold: '>0 holdout cases in a non-empty bench suite',
|
|
149
|
+
evidence: `bench/cases/: ${cases} total, ${holdout} holdout`,
|
|
150
|
+
});
|
|
151
|
+
const allMet = conditions.every((c) => c.met);
|
|
152
|
+
const weakest = conditions.find((c) => !c.met);
|
|
153
|
+
const leverMap = {
|
|
154
|
+
'high-quality-trajectories': 'data-collection',
|
|
155
|
+
'stable-benchmark-tasks': 'bench-expansion',
|
|
156
|
+
'reward-human-correlation': 'reward-calibration',
|
|
157
|
+
'eval-regression-stable': 'eval-suite-stabilization',
|
|
158
|
+
'version-provenance': 'version-provenance',
|
|
159
|
+
'offline-eval-holdout': 'bench-expansion',
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
verdict: allMet ? 'rl-eligible' : 'optimize-first',
|
|
163
|
+
conditions,
|
|
164
|
+
...(allMet ? {} : { recommendedLever: weakest ? leverMap[weakest.id] : undefined }),
|
|
165
|
+
at: new Date().toISOString(),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadSkillPayload(home: string, payload: string): Promise<string>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a treatment-arm skills prompt for a skill-target candidate: the
|
|
3
|
+
* payload names the skill; the arm must inject the skill's CONTENT (draft
|
|
4
|
+
* first, promoted second), never the bare name string. (Day-49 SELFFEED:
|
|
5
|
+
* the first real experiment injected "+34 tokens" of name - it measured the
|
|
6
|
+
* instrument, not the skill.)
|
|
7
|
+
*/
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
export async function loadSkillPayload(home, payload) {
|
|
11
|
+
const name = payload.trim();
|
|
12
|
+
if (!name)
|
|
13
|
+
return '';
|
|
14
|
+
const candidates = [
|
|
15
|
+
join(home, 'skills', 'drafts', `${name}.md`),
|
|
16
|
+
join(home, 'skills', `${name}.md`),
|
|
17
|
+
];
|
|
18
|
+
for (const f of candidates) {
|
|
19
|
+
try {
|
|
20
|
+
const md = await readFile(f, 'utf8');
|
|
21
|
+
if (md.trim())
|
|
22
|
+
return md.trim().slice(0, 4000);
|
|
23
|
+
}
|
|
24
|
+
catch { /* next */ }
|
|
25
|
+
}
|
|
26
|
+
// no file found: fall back to the raw string so the arm still runs
|
|
27
|
+
return name;
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a treatment-arm skills prompt for a skill-target candidate: the
|
|
3
|
+
* payload names the skill; the arm must inject the skill's CONTENT (draft
|
|
4
|
+
* first, promoted second), never the bare name string. (Day-49 SELFFEED:
|
|
5
|
+
* the first real experiment injected "+34 tokens" of name - it measured the
|
|
6
|
+
* instrument, not the skill.)
|
|
7
|
+
*/
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
export async function loadSkillPayload(home, payload) {
|
|
11
|
+
const name = payload.trim();
|
|
12
|
+
if (!name)
|
|
13
|
+
return '';
|
|
14
|
+
const candidates = [
|
|
15
|
+
join(home, 'skills', 'drafts', `${name}.md`),
|
|
16
|
+
join(home, 'skills', `${name}.md`),
|
|
17
|
+
];
|
|
18
|
+
for (const f of candidates) {
|
|
19
|
+
try {
|
|
20
|
+
const md = await readFile(f, 'utf8');
|
|
21
|
+
if (md.trim())
|
|
22
|
+
return md.trim().slice(0, 4000);
|
|
23
|
+
}
|
|
24
|
+
catch { /* next */ }
|
|
25
|
+
}
|
|
26
|
+
// no file found: fall back to the raw string so the arm still runs
|
|
27
|
+
return name;
|
|
28
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type SkillState = 'draft' | 'canary' | 'active' | 'archived';
|
|
2
|
+
export interface SkillEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
file: string;
|
|
6
|
+
state: SkillState;
|
|
7
|
+
}
|
|
8
|
+
/** Skills the user pinned: evolution never moves, merges or decays them.
|
|
9
|
+
* Marked by a .pin file next to SKILL.md (cheap, visible, git-friendly). */
|
|
10
|
+
export declare function pinSkill(home: string, name: string): Promise<boolean>;
|
|
11
|
+
export declare function unpinSkill(home: string, name: string): Promise<boolean>;
|
|
12
|
+
export declare function isPinned(file: string): Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Install skills from a git URL or a local directory (`hmh skills add <src>`).
|
|
15
|
+
* Handles the three common repo layouts: SKILL.md at the root (single skill),
|
|
16
|
+
* skills/<name>/SKILL.md (multi-skill pack, e.g. greensock/gsap-skills), and
|
|
17
|
+
* <name>/SKILL.md one level down. Existing skill names are never overwritten.
|
|
18
|
+
*/
|
|
19
|
+
export declare function installSkills(src: string, home: string): Promise<{
|
|
20
|
+
installed: string[];
|
|
21
|
+
skipped: string[];
|
|
22
|
+
}>;
|
|
23
|
+
export declare function listSkills(home: string): Promise<SkillEntry[]>;
|
|
24
|
+
export declare function listDrafts(home: string): Promise<SkillEntry[]>;
|
|
25
|
+
/** Canary-state skills: promoted through the bench gates but still under
|
|
26
|
+
* impact evaluation - injected into a sample of sessions, watermarked. */
|
|
27
|
+
export declare function listCanary(home: string): Promise<SkillEntry[]>;
|
|
28
|
+
export declare function skillsToPrompt(entries: SkillEntry[]): string;
|
|
29
|
+
/** Write (or overwrite) a draft; drafts are cheap and reversible by deletion. */
|
|
30
|
+
export declare function writeDraft(home: string, name: string, skillMd: string): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Promote a draft. Default target is `canary` (P0: bench-passing skills
|
|
33
|
+
* earn a canary slot first; full activation happens through the impact
|
|
34
|
+
* loop, not on the gate alone). `{canary: false}` promotes straight to
|
|
35
|
+
* active (used by the impact loop once evidence clears, and by `hmh skills
|
|
36
|
+
* promote`). If a same-name skill exists at the destination it is archived
|
|
37
|
+
* first (timestamped snapshot) so promotion is always reversible.
|
|
38
|
+
*/
|
|
39
|
+
export declare function promoteSkill(home: string, name: string, opts?: {
|
|
40
|
+
canary?: boolean;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
file: string;
|
|
43
|
+
archivedPrevious: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
/** Graduate a canary skill to full active (the impact loop's decision). */
|
|
46
|
+
export declare function promoteCanary(home: string, name: string): Promise<boolean>;
|
|
47
|
+
/** Retire a canary skill back to draft (impact loop's rejection path) -
|
|
48
|
+
* nothing is ever deleted (append-only red line). */
|
|
49
|
+
export declare function retireCanary(home: string, name: string): Promise<boolean>;
|
|
50
|
+
/** Restore the most recent archived snapshot of a skill back to active. */
|
|
51
|
+
export declare function rollbackSkill(home: string, name: string): Promise<boolean>;
|
|
52
|
+
/** Demote an active skill back to draft without deleting anything. */
|
|
53
|
+
export declare function unpromoteSkill(home: string, name: string): Promise<boolean>;
|
|
54
|
+
export declare function deleteDraft(home: string, name: string): Promise<boolean>;
|