opencode-swarm 7.136.1 → 7.136.2
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/.opencode/skills/swarm-pr-feedback/SKILL.md +41 -1
- package/.opencode/skills/swarm-pr-review/SKILL.md +72 -24
- package/README.md +18 -0
- package/dist/background/workspace-snapshot.d.ts +54 -0
- package/dist/cli/{config-doctor-6fphg5xm.js → config-doctor-b67nb84b.js} +2 -2
- package/dist/cli/{curation-policy-m24jbyag.js → curation-policy-kvzhfaj1.js} +2 -2
- package/dist/cli/{curator-llm-factory-w5arq7tr.js → curator-llm-factory-x7kvry9x.js} +17 -14
- package/dist/cli/{curator-zrt5sqj2.js → curator-nrmj8bds.js} +17 -14
- package/dist/cli/{evidence-summary-service-w006jnpg.js → evidence-summary-service-cf8sz2bq.js} +3 -1
- package/dist/cli/{guardrail-explain-462jwhbh.js → guardrail-explain-5kd44rcj.js} +18 -15
- package/dist/cli/{guardrail-log-ax21jd9e.js → guardrail-log-aksrzqdx.js} +3 -3
- package/dist/cli/hashing-mjwn6j4y.js +34 -0
- package/dist/cli/{hive-promoter-2vacnbws.js → hive-promoter-cy21rhnd.js} +17 -14
- package/dist/cli/{index-hzkvbycn.js → index-0755132s.js} +4 -4
- package/dist/cli/{index-9gxp450h.js → index-1kn24ja0.js} +1 -1
- package/dist/cli/{index-h3bweb88.js → index-21115szq.js} +5 -5
- package/dist/cli/{index-g7g4hqh3.js → index-37v2wxqe.js} +1 -1
- package/dist/cli/{index-gf7hqbmz.js → index-7ayaq27q.js} +5 -1
- package/dist/cli/{index-kzc2ygea.js → index-84nz7bhe.js} +1 -1
- package/dist/cli/{index-1sq47n6t.js → index-ad6j0m7k.js} +12 -6
- package/dist/cli/{index-zkddc5ye.js → index-azghvnja.js} +2 -2
- package/dist/cli/{index-rhgctfn3.js → index-ckybsn6p.js} +19 -16
- package/dist/cli/{index-cggqh2dz.js → index-kwwxevne.js} +53 -996
- package/dist/cli/{index-tfa40hwb.js → index-m5fw4mer.js} +19 -2
- package/dist/cli/{index-jwrydqjp.js → index-rw3f73aq.js} +1 -1
- package/dist/cli/{index-dyy3hvk3.js → index-s0rbdp61.js} +2 -2
- package/dist/cli/{index-p1pqqwgp.js → index-s0vahtdm.js} +1 -1
- package/dist/cli/{index-prnjt2jr.js → index-sr2cynyw.js} +4509 -2306
- package/dist/cli/index-wjm896ey.js +1100 -0
- package/dist/cli/{index-f4cmtd89.js → index-wnz282j3.js} +2 -2
- package/dist/cli/{index-y6a7gjtj.js → index-wsdnttf4.js} +228 -81
- package/dist/cli/{index-r9v98d0y.js → index-y0xaq4f3.js} +39 -4
- package/dist/cli/index-y8552snf.js +264 -0
- package/dist/cli/index-z1tm47nj.js +783 -0
- package/dist/cli/index.js +17 -14
- package/dist/cli/{knowledge-escalator-1zt8qy1c.js → knowledge-escalator-ta3xjrdj.js} +3 -3
- package/dist/cli/{knowledge-events-ej3s9tsm.js → knowledge-events-dk6banmz.js} +1 -1
- package/dist/cli/{knowledge-store-ey4cbkp4.js → knowledge-store-h3jz10bv.js} +1 -1
- package/dist/cli/{knowledge-validator-zwmq7s2c.js → knowledge-validator-jsz1dxkr.js} +4 -4
- package/dist/cli/runner-2v413r74.js +21 -0
- package/dist/cli/{scan-cursor-809hf2n1.js → scan-cursor-48gwzh9c.js} +2 -2
- package/dist/cli/{schema-mhd7xqwr.js → schema-f937b9cs.js} +5 -1
- package/dist/cli/{skill-generator-yyc8zd9j.js → skill-generator-4p10ktw1.js} +5 -5
- package/dist/cli/{workspace-snapshot-jmyamqnv.js → workspace-snapshot-h5rzw37b.js} +5 -1
- package/dist/commands/registry.d.ts +72 -0
- package/dist/commands/skill-opt.d.ts +41 -0
- package/dist/config/schema.d.ts +47 -0
- package/dist/hooks/guardrails/file-authority.d.ts +11 -2
- package/dist/hooks/pr-workflow-gate.d.ts +261 -4
- package/dist/hooks/pr-workflow-response-gate.d.ts +27 -9
- package/dist/hooks/write-target-resolver.d.ts +19 -0
- package/dist/index.js +353 -318
- package/dist/services/skill-evaluator.d.ts +17 -0
- package/dist/services/skill-optimizer/activation.d.ts +58 -0
- package/dist/services/skill-optimizer/candidates.d.ts +88 -0
- package/dist/services/skill-optimizer/controller.d.ts +146 -0
- package/dist/services/skill-optimizer/deterministic-seed.d.ts +29 -0
- package/dist/services/skill-optimizer/lifecycle.d.ts +70 -0
- package/dist/services/skill-optimizer/promoted-external-staleness.d.ts +98 -0
- package/dist/services/skill-optimizer/retirement.d.ts +54 -0
- package/dist/services/skill-optimizer/skill-eval-tasks.d.ts +47 -0
- package/dist/services/skill-optimizer/smoke.d.ts +46 -0
- package/dist/services/skill-optimizer/store.d.ts +118 -0
- package/dist/tools/write-pr-review-trigger-eval.d.ts +2 -1
- package/dist/utils/stable-stringify.d.ts +46 -0
- package/evaluation-fixtures/skill-eval/scoring/score-skill-eval.cjs +97 -0
- package/package.json +1 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/evaluation/hashing.ts
|
|
3
|
+
import { createHash } from "crypto";
|
|
4
|
+
import { lstatSync, realpathSync } from "fs";
|
|
5
|
+
import { lstat, opendir, readFile, realpath } from "fs/promises";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
function canonicalize(value, ancestors) {
|
|
8
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
if (typeof value === "number") {
|
|
12
|
+
if (!Number.isFinite(value))
|
|
13
|
+
throw new TypeError("canonical JSON rejects non-finite numbers");
|
|
14
|
+
return Object.is(value, -0) ? 0 : value;
|
|
15
|
+
}
|
|
16
|
+
if (value === undefined)
|
|
17
|
+
return;
|
|
18
|
+
if (typeof value !== "object") {
|
|
19
|
+
throw new TypeError(`canonical JSON cannot encode ${typeof value}`);
|
|
20
|
+
}
|
|
21
|
+
if (ancestors.has(value))
|
|
22
|
+
throw new TypeError("canonical JSON rejects cyclic values");
|
|
23
|
+
ancestors.add(value);
|
|
24
|
+
try {
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
return value.map((entry) => {
|
|
27
|
+
const encoded = canonicalize(entry, ancestors);
|
|
28
|
+
return encoded === undefined ? null : encoded;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const record = value;
|
|
32
|
+
const result = {};
|
|
33
|
+
for (const key of Object.keys(record).sort()) {
|
|
34
|
+
const encoded = canonicalize(record[key], ancestors);
|
|
35
|
+
if (encoded !== undefined)
|
|
36
|
+
result[key] = encoded;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
} finally {
|
|
40
|
+
ancestors.delete(value);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function canonicalJson(value) {
|
|
44
|
+
return JSON.stringify(canonicalize(value, new Set));
|
|
45
|
+
}
|
|
46
|
+
function sha256(value) {
|
|
47
|
+
return createHash("sha256").update(value).digest("hex");
|
|
48
|
+
}
|
|
49
|
+
function canonicalHash(value) {
|
|
50
|
+
return sha256(canonicalJson(value));
|
|
51
|
+
}
|
|
52
|
+
function contentHashWithout(value, keys) {
|
|
53
|
+
const copy = { ...value };
|
|
54
|
+
for (const key of keys)
|
|
55
|
+
delete copy[key];
|
|
56
|
+
return canonicalHash(copy);
|
|
57
|
+
}
|
|
58
|
+
function computeTaskContentHash(task) {
|
|
59
|
+
return contentHashWithout(task, ["contentHash"]);
|
|
60
|
+
}
|
|
61
|
+
async function computeCandidateInputContentHash(projectRoot, candidate) {
|
|
62
|
+
const payload = await hashTree(projectRoot, candidate.payloadPath, createHashBudget({ maxFiles: 5000, maxBytes: 50 * 1024 * 1024 }));
|
|
63
|
+
return canonicalHash({
|
|
64
|
+
candidate: JSON.parse(canonicalJson({ ...candidate, contentHash: undefined })),
|
|
65
|
+
payload
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function createHashBudget(limits) {
|
|
69
|
+
return { entries: 0, bytes: 0, limits };
|
|
70
|
+
}
|
|
71
|
+
function chargeHashEntry(budget) {
|
|
72
|
+
budget.entries++;
|
|
73
|
+
if (budget.entries > budget.limits.maxFiles) {
|
|
74
|
+
throw new Error("evaluation input exceeds the canonical hashing budget");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function closeDirectory(directory) {
|
|
78
|
+
try {
|
|
79
|
+
await directory.close();
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (typeof error !== "object" || error === null || !("code" in error) || error.code !== "ERR_DIR_CLOSED") {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function resolveTaskInputHashLimits(limits) {
|
|
87
|
+
const bounded = {
|
|
88
|
+
maxFiles: limits.maxFiles ?? 5000,
|
|
89
|
+
maxBytes: limits.maxBytes ?? 50 * 1024 * 1024
|
|
90
|
+
};
|
|
91
|
+
if (!Number.isSafeInteger(bounded.maxFiles) || bounded.maxFiles <= 0 || !Number.isSafeInteger(bounded.maxBytes) || bounded.maxBytes <= 0) {
|
|
92
|
+
throw new Error("task input hash limits must be positive safe integers");
|
|
93
|
+
}
|
|
94
|
+
return bounded;
|
|
95
|
+
}
|
|
96
|
+
async function hashTree(projectRoot, relativePath, budget) {
|
|
97
|
+
const target = await resolveContainedExistingPathAsync(projectRoot, relativePath);
|
|
98
|
+
const canonicalRoot = await _internals.realpath(projectRoot);
|
|
99
|
+
const entries = [];
|
|
100
|
+
const visit = async (absolutePath, entryAlreadyCharged = false) => {
|
|
101
|
+
const stat = await _internals.lstat(absolutePath);
|
|
102
|
+
if (stat.isSymbolicLink()) {
|
|
103
|
+
throw new Error(`evaluation input contains a symlink or reparse point: ${relativePath}`);
|
|
104
|
+
}
|
|
105
|
+
if (!entryAlreadyCharged)
|
|
106
|
+
chargeHashEntry(budget);
|
|
107
|
+
const relative2 = path.relative(canonicalRoot, absolutePath).replace(/\\/g, "/");
|
|
108
|
+
if (stat.isDirectory()) {
|
|
109
|
+
entries.push({ path: relative2, type: "directory" });
|
|
110
|
+
const children = [];
|
|
111
|
+
const directory = await _internals.opendir(absolutePath);
|
|
112
|
+
try {
|
|
113
|
+
while (true) {
|
|
114
|
+
const child = await directory.read();
|
|
115
|
+
if (!child)
|
|
116
|
+
break;
|
|
117
|
+
chargeHashEntry(budget);
|
|
118
|
+
children.push(child.name);
|
|
119
|
+
}
|
|
120
|
+
} finally {
|
|
121
|
+
await closeDirectory(directory);
|
|
122
|
+
}
|
|
123
|
+
for (const child of children.sort()) {
|
|
124
|
+
await visit(path.join(absolutePath, child), true);
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!stat.isFile()) {
|
|
129
|
+
throw new Error(`evaluation input must contain only regular files: ${relative2}`);
|
|
130
|
+
}
|
|
131
|
+
budget.bytes += stat.size;
|
|
132
|
+
if (budget.bytes > budget.limits.maxBytes) {
|
|
133
|
+
throw new Error("evaluation input exceeds the canonical hashing budget");
|
|
134
|
+
}
|
|
135
|
+
const content = await _internals.readFile(absolutePath);
|
|
136
|
+
entries.push({
|
|
137
|
+
path: relative2,
|
|
138
|
+
type: "file",
|
|
139
|
+
bytes: content.byteLength,
|
|
140
|
+
sha256: sha256(content)
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
await visit(target);
|
|
144
|
+
return entries;
|
|
145
|
+
}
|
|
146
|
+
async function computeTaskInputContentHash(projectRoot, task, limits = {}) {
|
|
147
|
+
const bounded = resolveTaskInputHashLimits(limits);
|
|
148
|
+
const budget = createHashBudget(bounded);
|
|
149
|
+
const inputTrees = [
|
|
150
|
+
...await hashTree(projectRoot, task.instructionPath, budget),
|
|
151
|
+
...await hashTree(projectRoot, task.environment.path, budget)
|
|
152
|
+
];
|
|
153
|
+
if (task.scorer.kind === "project" && task.scorer.argv[0]) {
|
|
154
|
+
inputTrees.push(...await hashTree(projectRoot, task.scorer.argv[0], budget));
|
|
155
|
+
}
|
|
156
|
+
return canonicalHash({
|
|
157
|
+
task: JSON.parse(canonicalJson({ ...task, contentHash: undefined })),
|
|
158
|
+
inputs: inputTrees.sort((left, right) => `${left.path}\x00${left.type}`.localeCompare(`${right.path}\x00${right.type}`))
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async function computeTaskLineageInputHash(projectRoot, task, limits = {}) {
|
|
162
|
+
const bounded = resolveTaskInputHashLimits(limits);
|
|
163
|
+
const budget = createHashBudget(bounded);
|
|
164
|
+
const normalizedTree = async (relativePath) => {
|
|
165
|
+
const canonicalRoot = await _internals.realpath(projectRoot);
|
|
166
|
+
const target = await resolveContainedExistingPathAsync(projectRoot, relativePath);
|
|
167
|
+
const targetRelative = path.relative(canonicalRoot, target).replace(/\\/g, "/");
|
|
168
|
+
return (await hashTree(projectRoot, relativePath, budget)).map((entry) => ({
|
|
169
|
+
...entry,
|
|
170
|
+
path: path.posix.relative(targetRelative, entry.path) || "."
|
|
171
|
+
}));
|
|
172
|
+
};
|
|
173
|
+
const scorerArgv = [...task.scorer.argv];
|
|
174
|
+
const projectScorer = task.scorer.kind === "project" && scorerArgv[0] ? await normalizedTree(scorerArgv[0]) : undefined;
|
|
175
|
+
if (projectScorer)
|
|
176
|
+
scorerArgv[0] = "<project-scorer>";
|
|
177
|
+
return canonicalHash({
|
|
178
|
+
instruction: await normalizedTree(task.instructionPath),
|
|
179
|
+
environment: {
|
|
180
|
+
kind: task.environment.kind,
|
|
181
|
+
tree: await normalizedTree(task.environment.path)
|
|
182
|
+
},
|
|
183
|
+
scorer: {
|
|
184
|
+
kind: task.scorer.kind,
|
|
185
|
+
argv: scorerArgv,
|
|
186
|
+
timeoutMs: task.scorer.timeoutMs,
|
|
187
|
+
scoreRange: task.scorer.scoreRange,
|
|
188
|
+
projectScorer
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function computeTaskSetContentHash(snapshot) {
|
|
193
|
+
return contentHashWithout(snapshot, ["contentHash", "createdAt"]);
|
|
194
|
+
}
|
|
195
|
+
function computeRunIntegrityHash(run) {
|
|
196
|
+
return contentHashWithout(run, [
|
|
197
|
+
"integrityHash"
|
|
198
|
+
]);
|
|
199
|
+
}
|
|
200
|
+
function computeManifestContentHash(manifest) {
|
|
201
|
+
return contentHashWithout(manifest, ["contentHash"]);
|
|
202
|
+
}
|
|
203
|
+
function isWithin(root, candidate) {
|
|
204
|
+
const relative2 = path.relative(root, candidate);
|
|
205
|
+
return relative2 === "" || !relative2.startsWith("..") && !path.isAbsolute(relative2);
|
|
206
|
+
}
|
|
207
|
+
function resolveContainedExistingPath(root, relativePath) {
|
|
208
|
+
if (path.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
|
|
209
|
+
throw new Error("evaluation path must be a non-NUL relative path");
|
|
210
|
+
}
|
|
211
|
+
const segments = relativePath.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
212
|
+
if (segments.length === 0 || segments.some((segment) => segment === "..")) {
|
|
213
|
+
throw new Error("evaluation path traversal is not allowed");
|
|
214
|
+
}
|
|
215
|
+
const canonicalRoot = realpathSync(root);
|
|
216
|
+
let cursor = canonicalRoot;
|
|
217
|
+
for (const segment of segments) {
|
|
218
|
+
if (segment === ".")
|
|
219
|
+
continue;
|
|
220
|
+
cursor = path.join(cursor, segment);
|
|
221
|
+
const stat = lstatSync(cursor);
|
|
222
|
+
if (stat.isSymbolicLink()) {
|
|
223
|
+
throw new Error(`evaluation path contains a symlink or reparse point: ${relativePath}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const canonicalCandidate = realpathSync(cursor);
|
|
227
|
+
if (!isWithin(canonicalRoot, canonicalCandidate)) {
|
|
228
|
+
throw new Error("evaluation path escapes its admitted root");
|
|
229
|
+
}
|
|
230
|
+
return canonicalCandidate;
|
|
231
|
+
}
|
|
232
|
+
async function resolveContainedExistingPathAsync(root, relativePath) {
|
|
233
|
+
if (path.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
|
|
234
|
+
throw new Error("evaluation path must be a non-NUL relative path");
|
|
235
|
+
}
|
|
236
|
+
const segments = relativePath.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
237
|
+
if (segments.length === 0 || segments.some((segment) => segment === "..")) {
|
|
238
|
+
throw new Error("evaluation path traversal is not allowed");
|
|
239
|
+
}
|
|
240
|
+
const canonicalRoot = await _internals.realpath(root);
|
|
241
|
+
let cursor = canonicalRoot;
|
|
242
|
+
for (const segment of segments) {
|
|
243
|
+
if (segment === ".")
|
|
244
|
+
continue;
|
|
245
|
+
cursor = path.join(cursor, segment);
|
|
246
|
+
const stat = await _internals.lstat(cursor);
|
|
247
|
+
if (stat.isSymbolicLink()) {
|
|
248
|
+
throw new Error(`evaluation path contains a symlink or reparse point: ${relativePath}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const canonicalCandidate = await _internals.realpath(cursor);
|
|
252
|
+
if (!isWithin(canonicalRoot, canonicalCandidate)) {
|
|
253
|
+
throw new Error("evaluation path escapes its admitted root");
|
|
254
|
+
}
|
|
255
|
+
return canonicalCandidate;
|
|
256
|
+
}
|
|
257
|
+
var _internals = {
|
|
258
|
+
lstat,
|
|
259
|
+
opendir,
|
|
260
|
+
readFile,
|
|
261
|
+
realpath
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export { canonicalJson, sha256, canonicalHash, contentHashWithout, computeTaskContentHash, computeCandidateInputContentHash, computeTaskInputContentHash, computeTaskLineageInputHash, computeTaskSetContentHash, computeRunIntegrityHash, computeManifestContentHash, resolveContainedExistingPath, resolveContainedExistingPathAsync, _internals };
|