opencode-swarm 7.136.0 → 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.
Files changed (68) hide show
  1. package/.opencode/skills/swarm-pr-feedback/SKILL.md +41 -1
  2. package/.opencode/skills/swarm-pr-review/SKILL.md +83 -24
  3. package/README.md +18 -0
  4. package/dist/background/candidate-contract.d.ts +13 -0
  5. package/dist/background/workspace-snapshot.d.ts +54 -0
  6. package/dist/cli/{config-doctor-q0krbxwv.js → config-doctor-b67nb84b.js} +2 -2
  7. package/dist/cli/{curation-policy-v6ywe8qd.js → curation-policy-kvzhfaj1.js} +2 -2
  8. package/dist/cli/{curator-llm-factory-mhwje8rf.js → curator-llm-factory-x7kvry9x.js} +17 -14
  9. package/dist/cli/{curator-tqedvpy1.js → curator-nrmj8bds.js} +17 -14
  10. package/dist/cli/{evidence-summary-service-w006jnpg.js → evidence-summary-service-cf8sz2bq.js} +3 -1
  11. package/dist/cli/{guardrail-explain-m0m74hvr.js → guardrail-explain-5kd44rcj.js} +18 -15
  12. package/dist/cli/{guardrail-log-5g8x6qc6.js → guardrail-log-aksrzqdx.js} +3 -3
  13. package/dist/cli/hashing-mjwn6j4y.js +34 -0
  14. package/dist/cli/{hive-promoter-s804newd.js → hive-promoter-cy21rhnd.js} +17 -14
  15. package/dist/cli/{index-n8j0cnp1.js → index-0755132s.js} +4 -4
  16. package/dist/cli/{index-1kjg590p.js → index-1kn24ja0.js} +1 -1
  17. package/dist/cli/{index-j08trvny.js → index-21115szq.js} +5 -5
  18. package/dist/cli/{index-2cv1j3rf.js → index-37v2wxqe.js} +1 -1
  19. package/dist/cli/{index-cfz5750k.js → index-7ayaq27q.js} +5 -1
  20. package/dist/cli/{index-9bk16zkv.js → index-84nz7bhe.js} +1 -1
  21. package/dist/cli/{index-bwfzq91q.js → index-ad6j0m7k.js} +12 -6
  22. package/dist/cli/{index-qh9466j3.js → index-azghvnja.js} +2 -2
  23. package/dist/cli/{index-cxq1t7j1.js → index-ckybsn6p.js} +19 -16
  24. package/dist/cli/{index-cggqh2dz.js → index-kwwxevne.js} +53 -996
  25. package/dist/cli/{index-nw9cn84t.js → index-m5fw4mer.js} +19 -2
  26. package/dist/cli/{index-2w4tsmva.js → index-rw3f73aq.js} +1 -1
  27. package/dist/cli/{index-wpqypdge.js → index-s0rbdp61.js} +2 -2
  28. package/dist/cli/{index-bbejqagj.js → index-s0vahtdm.js} +1 -1
  29. package/dist/cli/{index-81dkzncr.js → index-sr2cynyw.js} +4535 -2325
  30. package/dist/cli/index-wjm896ey.js +1100 -0
  31. package/dist/cli/{index-w58bmwyq.js → index-wnz282j3.js} +2 -2
  32. package/dist/cli/{index-y6a7gjtj.js → index-wsdnttf4.js} +228 -81
  33. package/dist/cli/{index-m1hew17b.js → index-y0xaq4f3.js} +39 -4
  34. package/dist/cli/index-y8552snf.js +264 -0
  35. package/dist/cli/index-z1tm47nj.js +783 -0
  36. package/dist/cli/index.js +17 -14
  37. package/dist/cli/{knowledge-escalator-f1m2xp5x.js → knowledge-escalator-ta3xjrdj.js} +3 -3
  38. package/dist/cli/{knowledge-events-rksgem6f.js → knowledge-events-dk6banmz.js} +1 -1
  39. package/dist/cli/{knowledge-store-t6d6fr8b.js → knowledge-store-h3jz10bv.js} +1 -1
  40. package/dist/cli/{knowledge-validator-5ggzb3j1.js → knowledge-validator-jsz1dxkr.js} +4 -4
  41. package/dist/cli/runner-2v413r74.js +21 -0
  42. package/dist/cli/{scan-cursor-18fnwr58.js → scan-cursor-48gwzh9c.js} +2 -2
  43. package/dist/cli/{schema-jw15d8bt.js → schema-f937b9cs.js} +5 -1
  44. package/dist/cli/{skill-generator-3n5y902z.js → skill-generator-4p10ktw1.js} +5 -5
  45. package/dist/cli/{workspace-snapshot-jmyamqnv.js → workspace-snapshot-h5rzw37b.js} +5 -1
  46. package/dist/commands/registry.d.ts +72 -0
  47. package/dist/commands/skill-opt.d.ts +41 -0
  48. package/dist/config/schema.d.ts +47 -0
  49. package/dist/hooks/guardrails/file-authority.d.ts +11 -2
  50. package/dist/hooks/pr-workflow-gate.d.ts +261 -4
  51. package/dist/hooks/pr-workflow-response-gate.d.ts +27 -9
  52. package/dist/hooks/write-target-resolver.d.ts +19 -0
  53. package/dist/index.js +355 -320
  54. package/dist/services/skill-evaluator.d.ts +17 -0
  55. package/dist/services/skill-optimizer/activation.d.ts +58 -0
  56. package/dist/services/skill-optimizer/candidates.d.ts +88 -0
  57. package/dist/services/skill-optimizer/controller.d.ts +146 -0
  58. package/dist/services/skill-optimizer/deterministic-seed.d.ts +29 -0
  59. package/dist/services/skill-optimizer/lifecycle.d.ts +70 -0
  60. package/dist/services/skill-optimizer/promoted-external-staleness.d.ts +98 -0
  61. package/dist/services/skill-optimizer/retirement.d.ts +54 -0
  62. package/dist/services/skill-optimizer/skill-eval-tasks.d.ts +47 -0
  63. package/dist/services/skill-optimizer/smoke.d.ts +46 -0
  64. package/dist/services/skill-optimizer/store.d.ts +118 -0
  65. package/dist/tools/write-pr-review-trigger-eval.d.ts +2 -1
  66. package/dist/utils/stable-stringify.d.ts +46 -0
  67. package/evaluation-fixtures/skill-eval/scoring/score-skill-eval.cjs +97 -0
  68. 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 };